Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 219 additions & 0 deletions wolfcrypt/src/ed25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,213 @@ 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

/* Symbols from ge_low_mem.c, compiled only when ED25519_SMALL is set. */
extern const ge_p3 ed25519_base;
extern const ge_p3 ed25519_neutral;
extern void ed25519_add(ge_p3 *r, const ge_p3 *a, const ge_p3 *b);
extern void ed25519_double(ge_p3 *r, const ge_p3 *a);

/*
* 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)
{
if (ctx->i >= 0) {
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;
}

/* -----------------------------------------------------------------------
* Non-blocking verify (state 0 = setup, state 1 = SB smult, state 2 = hA smult)
* ----------------------------------------------------------------------- */
#ifdef HAVE_ED25519_VERIFY
/* Group order in little-endian (same value as ed25519_order in ge_low_mem.c). */
static const byte ed25519_order_nb[32] = {
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
};

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_nb) - 1; j >= 0; j--) {
if (sig[ED25519_SIG_SIZE / 2 + j] > ed25519_order_nb[j]) {
ret = BAD_FUNC_ARG;
break;
}
if (sig[ED25519_SIG_SIZE / 2 + j] < ed25519_order_nb[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;
else
*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.
Expand Down Expand Up @@ -899,6 +1106,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;
Expand Down
50 changes: 50 additions & 0 deletions wolfssl/wolfcrypt/ed25519.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@
#include <wolfssl/wolfcrypt/async.h>
#endif

#ifdef WC_ED25519_NONBLOCK
#ifndef ED25519_SMALL
#error "WC_ED25519_NONBLOCK requires ED25519_SMALL"
#endif
#include <wolfssl/wolfcrypt/ge_operations.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -80,6 +87,28 @@ 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
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 */
Expand Down Expand Up @@ -108,6 +137,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
Expand Down Expand Up @@ -181,6 +213,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);
Expand Down
Loading