diff --git a/tests/api/test_ed25519.c b/tests/api/test_ed25519.c index 2df8d41270..f2f1e4fcc1 100644 --- a/tests/api/test_ed25519.c +++ b/tests/api/test_ed25519.c @@ -725,3 +725,160 @@ int test_wc_Ed25519KeyToDer_oneasymkey_version(void) return EXPECT_RESULT(); } +/* + * Test wc_ed25519_verify_msg() in non-blocking mode. + * Uses RFC 8032 test vectors 1-3 (1-, 1-, and 2-byte messages). + * Each verify is driven in a do/while loop until it returns 0 (done) + * or a real error, mirroring how an embedded application would yield + * between steps. A corrupted signature is also checked for rejection. + */ +int test_wc_ed25519_verify_msg_nonblock(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_VERIFY) && \ + defined(WC_ED25519_NONBLOCK) + /* RFC 8032 test vectors 1-3 */ + static const byte pKey1[] = { + 0xd7,0x5a,0x98,0x01,0x82,0xb1,0x0a,0xb7, + 0xd5,0x4b,0xfe,0xd3,0xc9,0x64,0x07,0x3a, + 0x0e,0xe1,0x72,0xf3,0xda,0xa6,0x23,0x25, + 0xaf,0x02,0x1a,0x68,0xf7,0x07,0x51,0x1a + }; + static const byte pKey2[] = { + 0x3d,0x40,0x17,0xc3,0xe8,0x43,0x89,0x5a, + 0x92,0xb7,0x0a,0xa7,0x4d,0x1b,0x7e,0xbc, + 0x9c,0x98,0x2c,0xcf,0x2e,0xc4,0x96,0x8c, + 0xc0,0xcd,0x55,0xf1,0x2a,0xf4,0x66,0x0c + }; + static const byte pKey3[] = { + 0xfc,0x51,0xcd,0x8e,0x62,0x18,0xa1,0xa3, + 0x8d,0xa4,0x7e,0xd0,0x02,0x30,0xf0,0x58, + 0x08,0x16,0xed,0x13,0xba,0x33,0x03,0xac, + 0x5d,0xeb,0x91,0x15,0x48,0x90,0x80,0x25 + }; + static const byte sig1[] = { + 0xe5,0x56,0x43,0x00,0xc3,0x60,0xac,0x72, + 0x90,0x86,0xe2,0xcc,0x80,0x6e,0x82,0x8a, + 0x84,0x87,0x7f,0x1e,0xb8,0xe5,0xd9,0x74, + 0xd8,0x73,0xe0,0x65,0x22,0x49,0x01,0x55, + 0x5f,0xb8,0x82,0x15,0x90,0xa3,0x3b,0xac, + 0xc6,0x1e,0x39,0x70,0x1c,0xf9,0xb4,0x6b, + 0xd2,0x5b,0xf5,0xf0,0x59,0x5b,0xbe,0x24, + 0x65,0x51,0x41,0x43,0x8e,0x7a,0x10,0x0b + }; + static const byte sig2[] = { + 0x92,0xa0,0x09,0xa9,0xf0,0xd4,0xca,0xb8, + 0x72,0x0e,0x82,0x0b,0x5f,0x64,0x25,0x40, + 0xa2,0xb2,0x7b,0x54,0x16,0x50,0x3f,0x8f, + 0xb3,0x76,0x22,0x23,0xeb,0xdb,0x69,0xda, + 0x08,0x5a,0xc1,0xe4,0x3e,0x15,0x99,0x6e, + 0x45,0x8f,0x36,0x13,0xd0,0xf1,0x1d,0x8c, + 0x38,0x7b,0x2e,0xae,0xb4,0x30,0x2a,0xee, + 0xb0,0x0d,0x29,0x16,0x12,0xbb,0x0c,0x00 + }; + static const byte sig3[] = { + 0x62,0x91,0xd6,0x57,0xde,0xec,0x24,0x02, + 0x48,0x27,0xe6,0x9c,0x3a,0xbe,0x01,0xa3, + 0x0c,0xe5,0x48,0xa2,0x84,0x74,0x3a,0x44, + 0x5e,0x36,0x80,0xd7,0xdb,0x5a,0xc3,0xac, + 0x18,0xff,0x9b,0x53,0x8d,0x16,0xf2,0x90, + 0xae,0x67,0xf7,0x60,0x98,0x4d,0xc6,0x59, + 0x4a,0x7c,0x15,0xe9,0x71,0x6e,0xd2,0x8d, + 0xc0,0x27,0xbe,0xce,0xea,0x1e,0xc4,0x0a + }; + static const byte msg1[] = { 0x00 }; /* Workaround since C-lang doesn't allow zero length array */ + static const byte msg2[] = { 0x72 }; + static const byte msg3[] = { 0xAF, 0x82 }; + + static const byte* pKeys[] = { pKey1, pKey2, pKey3 }; + static const byte* sigs[] = { sig1, sig2, sig3 }; + static const byte* msgs[] = { msg1, msg2, msg3 }; + static const word32 msgSz[] = { 0, sizeof(msg2), sizeof(msg3) }; + + ed25519_key key; + ed25519_nb_ctx_t nb_ctx; + byte bad_sig[ED25519_SIG_SIZE]; + byte bad_msg[2]; + int verify; + int ret; + int i; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&nb_ctx, 0, sizeof(nb_ctx)); + + ExpectIntEQ(wc_ed25519_init(&key), 0); + ExpectIntEQ(wc_ed25519_set_nonblock(&key, &nb_ctx), 0); + + for (i = 0; i < 3; i++) { + ExpectIntEQ(wc_ed25519_import_public(pKeys[i], ED25519_KEY_SIZE, &key), + 0); + + /* non-blocking verify good signature */ + verify = 0; + do { + ret = wc_ed25519_verify_msg(sigs[i], ED25519_SIG_SIZE, + msgs[i], msgSz[i], &verify, &key); + } while (ret == MP_WOULDBLOCK); + ExpectIntEQ(ret, 0); + ExpectIntEQ(verify, 1); + + /* verify corrupted last byte of signature - must fail */ + XMEMCPY(bad_sig, sigs[i], ED25519_SIG_SIZE); + bad_sig[ED25519_SIG_SIZE - 1] = bad_sig[ED25519_SIG_SIZE - 1] + 1; + verify = 0; + do { + ret = wc_ed25519_verify_msg(bad_sig, ED25519_SIG_SIZE, + msgs[i], msgSz[i], &verify, &key); + } while (ret == MP_WOULDBLOCK); + ExpectIntNE(ret, 0); + ExpectIntEQ(verify, 0); + + /* verify corrupted first byte of signature - must fail */ + XMEMCPY(bad_sig, sigs[i], ED25519_SIG_SIZE); + bad_sig[0] = bad_sig[0] + 1; + verify = 0; + do { + ret = wc_ed25519_verify_msg(bad_sig, ED25519_SIG_SIZE, + msgs[i], msgSz[i], &verify, &key); + } while (ret == MP_WOULDBLOCK); + ExpectIntNE(ret, 0); + ExpectIntEQ(verify, 0); + } + + /* tampered message with valid signature must fail (pKey3/sig3 still loaded) */ + XMEMCPY(bad_msg, msg3, sizeof(msg3)); + bad_msg[0] ^= 0x01; + verify = 0; + do { + ret = wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE, + bad_msg, sizeof(msg3), &verify, &key); + } while (ret == MP_WOULDBLOCK); + ExpectIntNE(ret, 0); + ExpectIntEQ(verify, 0); + + /* bad args */ + ExpectIntEQ(wc_ed25519_verify_msg(NULL, ED25519_SIG_SIZE, + msg3, sizeof(msg3), &verify, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE, + NULL, sizeof(msg3), &verify, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE, + msg3, sizeof(msg3), NULL, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE, + msg3, sizeof(msg3), &verify, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE - 1, + msg3, sizeof(msg3), &verify, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE + 1, + msg3, sizeof(msg3), &verify, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* verify falls back to blocking after disabling non-block mode */ + wc_ed25519_set_nonblock(&key, NULL); + verify = 0; + ExpectIntEQ(wc_ed25519_verify_msg(sig3, ED25519_SIG_SIZE, + msg3, sizeof(msg3), &verify, &key), 0); + ExpectIntEQ(verify, 1); + + wc_ed25519_free(&key); +#endif + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_ed25519.h b/tests/api/test_ed25519.h index 4ac3693f66..f62c5aab5c 100644 --- a/tests/api/test_ed25519.h +++ b/tests/api/test_ed25519.h @@ -37,20 +37,22 @@ int test_wc_Ed25519PublicKeyToDer(void); int test_wc_Ed25519KeyToDer(void); int test_wc_Ed25519PrivateKeyToDer(void); int test_wc_Ed25519KeyToDer_oneasymkey_version(void); +int test_wc_ed25519_verify_msg_nonblock(void); -#define TEST_ED25519_DECLS \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_key), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_init), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg_pubonly_fails), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_public), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_private_key), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_export), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_size), \ - TEST_DECL_GROUP("ed25519", test_wc_ed25519_exportKey), \ - TEST_DECL_GROUP("ed25519", test_wc_Ed25519PublicKeyToDer), \ - TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer), \ - TEST_DECL_GROUP("ed25519", test_wc_Ed25519PrivateKeyToDer), \ - TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer_oneasymkey_version) +#define TEST_ED25519_DECLS \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_key), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_init), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_sign_msg_pubonly_fails), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_public), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_import_private_key), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_export), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_size), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_exportKey), \ + TEST_DECL_GROUP("ed25519", test_wc_Ed25519PublicKeyToDer), \ + TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer), \ + TEST_DECL_GROUP("ed25519", test_wc_Ed25519PrivateKeyToDer), \ + TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer_oneasymkey_version), \ + TEST_DECL_GROUP("ed25519", test_wc_ed25519_verify_msg_nonblock) #endif /* WOLFCRYPT_TEST_ED25519_H */ diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index de12e87b83..e46357844e 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -205,6 +205,215 @@ static int ed25519_hash(ed25519_key* key, const byte* in, word32 inLen, return ret; } +#if defined(WC_ED25519_NONBLOCK) && defined(ED25519_SMALL) + +/* Forward declarations for static helpers defined later in this file. */ +#ifdef HAVE_ED25519_VERIFY +static int ed25519_verify_msg_init_with_sha(const byte* sig, word32 sigLen, + ed25519_key* key, wc_Sha512 *sha, byte type, const byte* context, + byte contextLen); +static int ed25519_verify_msg_update_with_sha(const byte* msgSegment, + word32 msgSegmentLen, ed25519_key* key, wc_Sha512 *sha); +#endif + + +/* + * Perform one bit-step of a scalar multiplication in extended twisted Edwards + * coordinates (double-and-add, constant-time select). + * + * ctx->r : accumulator (must be initialized to ed25519_neutral before call 0) + * ctx->pt : base point for this multiplication + * ctx->i : current bit index, caller must set to 255 before first call + * scalar : 32-byte reduced scalar + * + * Returns MP_WOULDBLOCK while bits remain (ctx->i >= 0 after decrement), + * or 0 when done (ctx->i fell below 0). + */ +static int ed25519_smult_nb_step(ed25519_nb_ctx_t* ctx, const byte* scalar) +{ + word32 n; + for (n = 0; n < ED25519_NB_STEPS_PER_YIELD && ctx->i >= 0; n++) { + const byte bit = (scalar[ctx->i >> 3] >> (ctx->i & 7)) & 1; + ge_p3 s; + + ed25519_double(&ctx->r, &ctx->r); + ed25519_add(&s, &ctx->r, &ctx->pt); + + fe_select(ctx->r.X, ctx->r.X, s.X, bit); + fe_select(ctx->r.Y, ctx->r.Y, s.Y, bit); + fe_select(ctx->r.Z, ctx->r.Z, s.Z, bit); + fe_select(ctx->r.T, ctx->r.T, s.T, bit); + + ctx->i--; + } + return (ctx->i >= 0) ? MP_WOULDBLOCK : 0; +} + +int wc_ed25519_set_nonblock(ed25519_key* key, ed25519_nb_ctx_t* ctx) +{ + if (key == NULL) + return BAD_FUNC_ARG; + + /* If replacing a context, clear the old one first. */ + if (key->nb_ctx != NULL && key->nb_ctx != ctx) { + ForceZero(key->nb_ctx, sizeof(ed25519_nb_ctx_t)); + } + if (ctx != NULL) { + XMEMSET(ctx, 0, sizeof(ed25519_nb_ctx_t)); + } + key->nb_ctx = ctx; + return 0; +} + +/* Group order in little-endian (same value as ed25519_order in ge_low_mem.c). */ +static const byte ed25519_order[ED25519_KEY_SIZE] = { + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 +}; + +/* ----------------------------------------------------------------------- + * Non-blocking verify (state 0 = setup, state 1 = SB smult, state 2 = hA smult) + * ----------------------------------------------------------------------- */ +#ifdef HAVE_ED25519_VERIFY + +static int ed25519_verify_nb(const byte* sig, word32 sigLen, const byte* msg, + word32 msgLen, int* res, ed25519_key* key, + byte type, const byte* context, byte contextLen) +{ + ed25519_nb_ctx_t* nb_ctx = key->nb_ctx; + int ret = 0; + + switch (nb_ctx->state) { + case 0: { + /* ---- Setup phase (synchronous) ---- */ + int j; +#ifdef WOLFSSL_ED25519_PERSISTENT_SHA + wc_Sha512* sha = &key->sha; +#else + WC_DECLARE_VAR(sha, wc_Sha512, 1, key->heap); + WC_ALLOC_VAR_EX(sha, wc_Sha512, 1, key->heap, DYNAMIC_TYPE_HASHES, + ret = MEMORY_E; break); + ret = ed25519_hash_init(key, sha); + if (ret != 0) { + WC_FREE_VAR_EX(sha, key->heap, DYNAMIC_TYPE_HASHES); + break; + } +#endif + + /* init + update (domain, sig_R, pub) + update (msg) */ + ret = ed25519_verify_msg_init_with_sha(sig, sigLen, key, sha, + type, context, contextLen); + if (ret == 0) + ret = ed25519_verify_msg_update_with_sha(msg, msgLen, key, sha); + + /* S range check: S must be < group order */ + if (ret == 0) { + if (sigLen != ED25519_SIG_SIZE) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + for (j = (int)sizeof(ed25519_order) - 1; j >= 0; j--) { + if (sig[ED25519_SIG_SIZE / 2 + j] > ed25519_order[j]) { + ret = BAD_FUNC_ARG; + break; + } + if (sig[ED25519_SIG_SIZE / 2 + j] < ed25519_order[j]) + break; + } + if (ret == 0 && j == -1) /* S == order */ + ret = BAD_FUNC_ARG; + } + + /* Decompress and negate public key -> neg_A */ + if (ret == 0) + ret = ge_frombytes_negate_vartime(&nb_ctx->neg_A, key->p); + + /* hash_final -> h */ + if (ret == 0) + ret = ed25519_hash_final(key, sha, nb_ctx->h); + +#ifndef WOLFSSL_ED25519_PERSISTENT_SHA + ed25519_hash_free(key, sha); + WC_FREE_VAR_EX(sha, key->heap, DYNAMIC_TYPE_HASHES); +#endif + if (ret != 0) + break; + + sc_reduce(nb_ctx->h); + + /* Save sig_S (32 bytes) for first scalar mult */ + XMEMCPY(nb_ctx->sig_S, sig + (ED25519_SIG_SIZE / 2), ED25519_KEY_SIZE); + + /* Init first smult: B * sig_S */ + XMEMCPY(&nb_ctx->r, &ed25519_neutral, sizeof(ge_p3)); + XMEMCPY(&nb_ctx->pt, &ed25519_base, sizeof(ge_p3)); + nb_ctx->i = 255; + nb_ctx->state = 1; + ret = MP_WOULDBLOCK; + break; + } + + case 1: { + /* ---- First scalar mult: ge_scalarmult_base(sig_S) = SB ---- */ + ret = ed25519_smult_nb_step(nb_ctx, nb_ctx->sig_S); + if (ret == MP_WOULDBLOCK) + break; + + /* Save SB, then init second smult: neg_A * h */ + XMEMCPY(&nb_ctx->SB, &nb_ctx->r, sizeof(ge_p3)); + XMEMCPY(&nb_ctx->r, &ed25519_neutral, sizeof(ge_p3)); + XMEMCPY(&nb_ctx->pt, &nb_ctx->neg_A, sizeof(ge_p3)); + nb_ctx->i = 255; + nb_ctx->state = 2; + ret = MP_WOULDBLOCK; + break; + } + + case 2: { + /* ---- Second scalar mult: neg_A * h = hA ---- */ + ret = ed25519_smult_nb_step(nb_ctx, nb_ctx->h); + if (ret == MP_WOULDBLOCK) + break; + + /* SB + hA = SB - H(R,A,M)*A */ + { + ge_p3 sum; + ALIGN16 byte rcheck[ED25519_KEY_SIZE]; + + ed25519_add(&sum, &nb_ctx->SB, &nb_ctx->r); + ge_p3_tobytes(rcheck, &sum); + + ret = ConstantCompare(rcheck, sig, ED25519_SIG_SIZE / 2); + if (ret != 0) + ret = SIG_VERIFY_E; +#ifdef WOLFSSL_CHECK_VER_FAULTS + if (ret == 0 && + ConstantCompare(rcheck, sig, ED25519_SIG_SIZE / 2) != 0) + ret = SIG_VERIFY_E; +#endif + if (ret == 0) + *res = 1; + } + break; + } + + default: + ret = BAD_STATE_E; + break; + } + + if (ret != MP_WOULDBLOCK) { + ForceZero(nb_ctx, sizeof(ed25519_nb_ctx_t)); + } + return ret; +} +#endif /* HAVE_ED25519_VERIFY */ + +#endif /* WC_ED25519_NONBLOCK && ED25519_SMALL */ + #ifdef HAVE_ED25519_MAKE_KEY #if FIPS_VERSION3_GE(6,0,0) /* Performs a Pairwise Consistency Test on an Ed25519 key pair. @@ -753,12 +962,6 @@ static int ed25519_verify_msg_update_with_sha(const byte* msgSegment, } /* ed25519 order in little endian. */ -static const byte ed25519_order[] = { - 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, - 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 -}; /* sig is array of bytes containing the signature @@ -899,6 +1102,18 @@ int wc_ed25519_verify_msg_ex(const byte* sig, word32 sigLen, const byte* msg, byte type, const byte* context, byte contextLen) { int ret; +#if defined(WC_ED25519_NONBLOCK) && defined(ED25519_SMALL) + if (key != NULL && key->nb_ctx != NULL) { + if (sig == NULL || msg == NULL || res == NULL || + (context == NULL && contextLen != 0)) { + return BAD_FUNC_ARG; + } + if ((type == Ed25519ph) && (msgLen != WC_SHA512_DIGEST_SIZE)) + return BAD_LENGTH_E; + return ed25519_verify_nb(sig, sigLen, msg, msgLen, res, key, + type, context, contextLen); + } +#endif #ifdef WOLFSSL_SE050 (void)type; (void)context; diff --git a/wolfssl/wolfcrypt/ed25519.h b/wolfssl/wolfcrypt/ed25519.h index 120c210a92..1770fc36db 100644 --- a/wolfssl/wolfcrypt/ed25519.h +++ b/wolfssl/wolfcrypt/ed25519.h @@ -41,6 +41,13 @@ #include #endif +#ifdef WC_ED25519_NONBLOCK + #ifndef ED25519_SMALL + #error "WC_ED25519_NONBLOCK requires ED25519_SMALL" + #endif + #include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -80,6 +87,37 @@ enum { WC_ED25519_FLAG_DEC_SIGN = 0x01 }; +/* Non-blocking context for Ed25519 verify operations. + * Requires WC_ED25519_NONBLOCK and ED25519_SMALL. + * Tracks state across multiple calls to wc_ed25519_verify_msg() (and + * variants) so the caller can yield between steps of the scalar + * multiplications and resume by calling the same function again with + * identical arguments. Returns MP_WOULDBLOCK while in progress, 0 on + * success, <0 on error. The context is zeroed automatically on any + * non-pending return. + */ +#ifdef WC_ED25519_NONBLOCK +/* Number of scalar-multiply bit-steps processed per wc_ed25519_verify_msg() + * call before returning MP_WOULDBLOCK. Override in user_settings.h to tune + * yield granularity: higher values reduce call overhead at the cost of longer + * blocking intervals. Default 1 preserves the original one-bit-per-call + * behaviour. */ +#ifndef ED25519_NB_STEPS_PER_YIELD + #define ED25519_NB_STEPS_PER_YIELD 1 +#endif + +typedef struct ed25519_nb_ctx_t { + int state; /* operation state machine */ + int i; /* bit index for scalar mult (starts 255, ends at -1) */ + ge_p3 r; /* scalar mult accumulator */ + ge_p3 pt; /* current base point for scalar mult */ + ge_p3 neg_A; /* negated public key point */ + ge_p3 SB; /* saved result of first scalar mult (SB) */ + ALIGN16 byte sig_S[ED25519_KEY_SIZE]; /* copy of sig[32..63] */ + ALIGN16 byte h[WC_SHA512_DIGEST_SIZE]; /* reduced H(R,A,M) */ +} ed25519_nb_ctx_t; +#endif /* WC_ED25519_NONBLOCK */ + /* An ED25519 Key */ struct ed25519_key { ALIGN16 byte p[ED25519_PUB_KEY_SIZE]; /* compressed public key */ @@ -108,6 +146,9 @@ struct ed25519_key { #ifdef WOLFSSL_ED25519_PERSISTENT_SHA wc_Sha512 sha; #endif +#ifdef WC_ED25519_NONBLOCK + ed25519_nb_ctx_t* nb_ctx; +#endif }; #ifndef WC_ED25519KEY_TYPE_DEFINED @@ -181,6 +222,24 @@ WOLFSSL_API int wc_ed25519_init_ex(ed25519_key* key, void* heap, int devId); WOLFSSL_API void wc_ed25519_free(ed25519_key* key); + +#ifdef WC_ED25519_NONBLOCK +/*! + \brief Enable non-blocking support for Ed25519 verify operations on a key. + When enabled, wc_ed25519_verify_msg() and its variants return + MP_WOULDBLOCK during each step of the two scalar multiplications, + allowing the caller to yield and resume by calling the same + function again with identical arguments. + Requires WC_ED25519_NONBLOCK and ED25519_SMALL. + + \param key Pointer to ed25519_key to configure + \param ctx Pointer to ed25519_nb_ctx_t context, or NULL to disable + + \return 0 on success, BAD_FUNC_ARG if key is NULL +*/ +WOLFSSL_API +int wc_ed25519_set_nonblock(ed25519_key* key, ed25519_nb_ctx_t* ctx); +#endif /* WC_ED25519_NONBLOCK */ #ifndef WC_NO_CONSTRUCTORS WOLFSSL_API ed25519_key* wc_ed25519_new(void* heap, int devId, int *result_code); diff --git a/wolfssl/wolfcrypt/ge_operations.h b/wolfssl/wolfcrypt/ge_operations.h index 0a18a7ebba..81f53b334d 100644 --- a/wolfssl/wolfcrypt/ge_operations.h +++ b/wolfssl/wolfcrypt/ge_operations.h @@ -99,6 +99,13 @@ WOLFSSL_LOCAL void ge_p3_tobytes(unsigned char *s,const ge_p3 *h); #endif +#ifdef ED25519_SMALL +WOLFSSL_LOCAL extern const ge_p3 ed25519_base; +WOLFSSL_LOCAL extern const ge_p3 ed25519_neutral; +WOLFSSL_LOCAL void ed25519_add(ge_p3 *r, const ge_p3 *a, const ge_p3 *b); +WOLFSSL_LOCAL void ed25519_double(ge_p3 *r, const ge_p3 *a); +#endif /* ED25519_SMALL */ + #ifndef ED25519_SMALL typedef struct { ge X;