diff --git a/src/per_curve/eddsa.tmpl.c b/src/per_curve/eddsa.tmpl.c new file mode 100644 index 0000000..0a5c398 --- /dev/null +++ b/src/per_curve/eddsa.tmpl.c @@ -0,0 +1,205 @@ +/** + * @cond internal + * @brief EdDSA routines. + */ + +#include "decaf.h" +#include "decaf/shake.h" +#include "word.h" +#include + +#define API_NAME "$(c_ns)" +#define API_NS(_id) $(c_ns)_##_id + +static void clamp( + uint8_t secret_scalar_ser[$(C_NS)_EDDSA_PRIVATE_BYTES] +) { + /* Blarg */ + secret_scalar_ser[0] &= -$(cofactor); + uint8_t hibit = (1<<$(gf_bits % 8))>>1; + secret_scalar_ser[$(C_NS)_EDDSA_PRIVATE_BYTES - 1] &= -hibit; + secret_scalar_ser[$(C_NS)_EDDSA_PRIVATE_BYTES - 1] |= hibit; + if (hibit == 0) secret_scalar_ser[$(C_NS)_EDDSA_PRIVATE_BYTES - 2] |= 0x80; +} + +void API_NS(eddsa_derive_public_key) ( + uint8_t pubkey[$(C_NS)_EDDSA_PUBLIC_BYTES], + const uint8_t privkey[$(C_NS)_EDDSA_PRIVATE_BYTES] +) { + /* only this much used for keygen */ + uint8_t secret_scalar_ser[$(C_NS)_EDDSA_PRIVATE_BYTES]; + + shake256_hash( + secret_scalar_ser, + sizeof(secret_scalar_ser), + privkey, + $(C_NS)_EDDSA_PRIVATE_BYTES + ); + clamp(secret_scalar_ser); + + API_NS(scalar_t) secret_scalar; + API_NS(scalar_decode_long)(secret_scalar, secret_scalar_ser, sizeof(secret_scalar_ser)); + /* TODO: write documentation for why (due to isogenies) this needs to be quartered */ + API_NS(scalar_sub)(secret_scalar,API_NS(scalar_zero),secret_scalar); + API_NS(scalar_halve)(secret_scalar,secret_scalar); + API_NS(scalar_halve)(secret_scalar,secret_scalar); + + API_NS(point_t) p; + API_NS(precomputed_scalarmul)(p,API_NS(precomputed_base),secret_scalar); + + API_NS(point_encode_like_eddsa)(pubkey, p); + + /* Cleanup */ + API_NS(scalar_destroy)(secret_scalar); + API_NS(point_destroy)(p); + decaf_bzero(secret_scalar_ser, sizeof(secret_scalar_ser)); +} + +static const char *domS = "SigEd448"; + +void API_NS(eddsa_sign) ( + uint8_t signature[$(C_NS)_EDDSA_SIGNATURE_BYTES], + const uint8_t privkey[$(C_NS)_EDDSA_PRIVATE_BYTES], + const uint8_t pubkey[$(C_NS)_EDDSA_PUBLIC_BYTES], + const uint8_t *context, + uint8_t context_len, + const uint8_t *message, + size_t message_len, + uint8_t prehashed +) { + /* FIXME: of course, need a different hash for Curve25519 */ + + API_NS(scalar_t) secret_scalar; + shake256_ctx_t shake; + const uint8_t dom[2] = {1+word_is_zero(prehashed), context_len}; + { + /* Schedule the secret key */ + struct { + uint8_t secret_scalar_ser[$(C_NS)_EDDSA_PRIVATE_BYTES]; + uint8_t seed[$(C_NS)_EDDSA_PRIVATE_BYTES]; + } __attribute__((packed)) expanded; + shake256_hash( + (uint8_t *)&expanded, + sizeof(expanded), + privkey, + $(C_NS)_EDDSA_PRIVATE_BYTES + ); + clamp(expanded.secret_scalar_ser); + API_NS(scalar_decode_long)(secret_scalar, expanded.secret_scalar_ser, sizeof(expanded.secret_scalar_ser)); + + /* Hash to create the nonce */ + shake256_init(shake); + shake256_update(shake,(const unsigned char *)domS, strlen(domS)); + shake256_update(shake,dom,2); + shake256_update(shake,context,context_len); + shake256_update(shake,expanded.seed,sizeof(expanded.seed)); + shake256_update(shake,message,message_len); + decaf_bzero(&expanded, sizeof(expanded)); + } + + /* Decode the nonce */ + API_NS(scalar_t) nonce_scalar; + { + uint8_t nonce[2*$(C_NS)_EDDSA_PRIVATE_BYTES]; + shake256_final(shake,nonce,sizeof(nonce)); + API_NS(scalar_decode_long)(nonce_scalar, nonce, sizeof(nonce)); + decaf_bzero(nonce, sizeof(nonce)); + } + + uint8_t nonce_point[$(C_NS)_EDDSA_PUBLIC_BYTES] = {0}; + { + /* Scalarmul to create the nonce-point */ + API_NS(scalar_t) nonce_scalar_2; + API_NS(scalar_halve)(nonce_scalar_2, nonce_scalar); + API_NS(scalar_halve)(nonce_scalar_2, nonce_scalar_2); + API_NS(scalar_sub)(nonce_scalar_2,API_NS(scalar_zero),nonce_scalar_2); + API_NS(point_t) p; + API_NS(precomputed_scalarmul)(p,API_NS(precomputed_base),nonce_scalar_2); + API_NS(point_encode_like_eddsa)(nonce_point, p); + API_NS(point_destroy)(p); + API_NS(scalar_destroy)(nonce_scalar_2); + } + + API_NS(scalar_t) challenge_scalar; + { + /* Compute the challenge */ + shake256_init(shake); + shake256_update(shake,(const unsigned char *)domS, strlen(domS)); + shake256_update(shake,dom,2); + shake256_update(shake,context,context_len); + shake256_update(shake,nonce_point,sizeof(nonce_point)); + shake256_update(shake,pubkey,$(C_NS)_EDDSA_PUBLIC_BYTES); + shake256_update(shake,message,message_len); + uint8_t challenge[2*$(C_NS)_EDDSA_PRIVATE_BYTES]; + shake256_final(shake,challenge,sizeof(challenge)); + shake256_destroy(shake); + API_NS(scalar_decode_long)(challenge_scalar,challenge,sizeof(challenge)); + decaf_bzero(challenge,sizeof(challenge)); + } + + API_NS(scalar_mul)(challenge_scalar,challenge_scalar,secret_scalar); + API_NS(scalar_add)(challenge_scalar,challenge_scalar,nonce_scalar); + + decaf_bzero(signature,$(C_NS)_EDDSA_SIGNATURE_BYTES); + memcpy(signature,nonce_point,sizeof(nonce_point)); + API_NS(scalar_encode)(&signature[$(C_NS)_EDDSA_PUBLIC_BYTES],challenge_scalar); + + API_NS(scalar_destroy)(secret_scalar); + API_NS(scalar_destroy)(nonce_scalar); + API_NS(scalar_destroy)(challenge_scalar); +} + + +decaf_error_t API_NS(eddsa_verify) ( + const uint8_t signature[$(C_NS)_EDDSA_SIGNATURE_BYTES], + const uint8_t pubkey[$(C_NS)_EDDSA_PUBLIC_BYTES], + const uint8_t *context, + uint8_t context_len, + const uint8_t *message, + size_t message_len, + uint8_t prehashed +) { + API_NS(point_t) pk_point, r_point; + decaf_error_t error = API_NS(point_decode_like_eddsa)(pk_point,pubkey); + if (DECAF_SUCCESS != error) { return error; } + + error = API_NS(point_decode_like_eddsa)(r_point,signature); + if (DECAF_SUCCESS != error) { return error; } + + API_NS(scalar_t) challenge_scalar; + { + /* Compute the challenge */ + shake256_ctx_t shake; + const uint8_t dom[2] = {1+word_is_zero(prehashed), context_len}; + shake256_init(shake); + shake256_update(shake,(const unsigned char *)domS, strlen(domS)); + shake256_update(shake,dom,2); + shake256_update(shake,context,context_len); + shake256_update(shake,signature,$(C_NS)_EDDSA_PUBLIC_BYTES); + shake256_update(shake,pubkey,$(C_NS)_EDDSA_PUBLIC_BYTES); + shake256_update(shake,message,message_len); + uint8_t challenge[2*$(C_NS)_EDDSA_PRIVATE_BYTES]; + shake256_final(shake,challenge,sizeof(challenge)); + shake256_destroy(shake); + API_NS(scalar_decode_long)(challenge_scalar,challenge,sizeof(challenge)); + decaf_bzero(challenge,sizeof(challenge)); + } + API_NS(scalar_sub)(challenge_scalar, API_NS(scalar_zero), challenge_scalar); + + API_NS(scalar_t) response_scalar; + API_NS(scalar_decode_long)( + response_scalar, + &signature[$(C_NS)_EDDSA_PUBLIC_BYTES], + $(C_NS)_EDDSA_PRIVATE_BYTES + ); + API_NS(scalar_sub)(response_scalar, API_NS(scalar_zero), response_scalar); /* TODO because nega-base point */ + + /* pk_point = -c(x(P)) + (cx + k)G = kG */ + API_NS(base_double_scalarmul_non_secret)( + pk_point, + response_scalar, + pk_point, + challenge_scalar + ); + return decaf_succeed_if(API_NS(point_eq(pk_point,r_point))); +}