| @@ -0,0 +1,205 @@ | |||||
| /** | |||||
| * @cond internal | |||||
| * @brief EdDSA routines. | |||||
| */ | |||||
| #include "decaf.h" | |||||
| #include "decaf/shake.h" | |||||
| #include "word.h" | |||||
| #include <string.h> | |||||
| #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))); | |||||
| } | |||||