|
|
@@ -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))); |
|
|
|
} |