@@ -1067,18 +1067,17 @@ void API_NS(point_encode_like_eddsa) ( | |||||
gf u; | gf u; | ||||
gf_sqr ( x, p->x ); | gf_sqr ( x, p->x ); | ||||
gf_sqr ( t, p->y ); | gf_sqr ( t, p->y ); | ||||
gf_add( u, x, t ); // x^2 + y^2 | |||||
gf_add( u, x, t ); | |||||
gf_add( z, p->y, p->x ); | gf_add( z, p->y, p->x ); | ||||
gf_sqr ( y, z); // (x+y)^2 | |||||
gf_sub ( y, y, u ); // 2xy | |||||
gf_sub ( z, t, x ); // y^2 - x^2 | |||||
gf_sqr ( x, p->z ); // z^2 | |||||
gf_add ( t, x, x); // 2z^2 | |||||
gf_sub ( t, t, z); // 2z^2 - y^2 + x^2 | |||||
gf_mul ( x, t, y ); // (2z^2 - y^2 + x^2)(2xy) | |||||
gf_mul ( y, z, u ); // (y^2 - x^2)(x^2 + y^2) | |||||
gf_mul ( z, u, t ); // (x^2 + y^2)(2z^2 - y^2 + x^2) | |||||
gf_sqr ( y, z); | |||||
gf_sub ( y, y, u ); | |||||
gf_sub ( z, t, x ); | |||||
gf_sqr ( x, p->z ); | |||||
gf_add ( t, x, x); | |||||
gf_sub ( t, t, z); | |||||
gf_mul ( x, t, y ); | |||||
gf_mul ( y, z, u ); | |||||
gf_mul ( z, u, t ); | |||||
decaf_bzero(t,sizeof(u)); | decaf_bzero(t,sizeof(u)); | ||||
} | } | ||||
#endif | #endif | ||||
@@ -1090,7 +1089,7 @@ void API_NS(point_encode_like_eddsa) ( | |||||
/* Encode */ | /* Encode */ | ||||
enc[$(C_NS)_EDDSA_PRIVATE_BYTES-1] = 0; | enc[$(C_NS)_EDDSA_PRIVATE_BYTES-1] = 0; | ||||
gf_serialize(enc, x, 1); | gf_serialize(enc, x, 1); | ||||
enc[$(C_NS)_EDDSA_PRIVATE_BYTES-1] |= 0x80 &~ gf_lobit(t); | |||||
enc[$(C_NS)_EDDSA_PRIVATE_BYTES-1] |= 0x80 & gf_lobit(t); | |||||
decaf_bzero(x,sizeof(x)); | decaf_bzero(x,sizeof(x)); | ||||
decaf_bzero(y,sizeof(y)); | decaf_bzero(y,sizeof(y)); | ||||
@@ -1098,6 +1097,68 @@ void API_NS(point_encode_like_eddsa) ( | |||||
decaf_bzero(t,sizeof(t)); | decaf_bzero(t,sizeof(t)); | ||||
} | } | ||||
decaf_error_t API_NS(point_decode_like_eddsa) ( | |||||
point_t p, | |||||
const uint8_t enc[$(C_NS)_EDDSA_PUBLIC_BYTES] | |||||
) { | |||||
uint8_t enc2[$(C_NS)_EDDSA_PUBLIC_BYTES]; | |||||
memcpy(enc2,enc,sizeof(enc2)); | |||||
mask_t low = ~word_is_zero(enc2[$(C_NS)_EDDSA_PRIVATE_BYTES-1] & 0x80); | |||||
enc2[$(C_NS)_EDDSA_PRIVATE_BYTES-1] &= ~0x80; | |||||
mask_t succ = DECAF_TRUE; | |||||
#if $(gf_bits % 8) == 0 | |||||
succ = word_is_zero(enc2[$(C_NS)_EDDSA_PRIVATE_BYTES-1]); | |||||
#endif | |||||
succ &= gf_deserialize(p->y, enc2, 1); | |||||
gf_sqr(p->z,p->y); | |||||
gf_mulw(p->t,p->z,EDWARDS_D); | |||||
gf_sub(p->z,ONE,p->z); /* 1-y^2 */ | |||||
gf_sub(p->t,ONE,p->t); /* 1-dy^2 */ | |||||
gf_mul(p->x,p->z,p->t); | |||||
succ &= gf_isr(p->t,p->x); /* 1/sqrt((1-y^2)(1-dy^2)) */ | |||||
gf_mul(p->x,p->t,p->z); /* sqrt((1-y^2) / (1-dy^2)) */ | |||||
gf_cond_neg(p->x,gf_lobit(p->x)^low); | |||||
gf_copy(p->z,ONE); | |||||
#if IMAGINE_TWIST | |||||
{ | |||||
gf_mul(p->t,p->x,SQRT_MINUS_ONE); | |||||
gf_copy(p->x,p->t); | |||||
point_double_internal(p,p,0); | |||||
} | |||||
#else | |||||
{ | |||||
/* 4-isogeny */ | |||||
gf a, b, c, d; | |||||
gf_sqr ( c, p->x ); | |||||
gf_sqr ( a, p->y ); | |||||
gf_add ( d, c, a ); | |||||
gf_add ( p->t, p->y, p->x ); | |||||
gf_sqr ( b, p->t ); | |||||
gf_sub ( b, b, d ); | |||||
gf_sub ( p->t, a, c ); | |||||
gf_sqr ( p->x, p->z ); | |||||
gf_add ( p->z, p->x, p->x ); | |||||
gf_sub ( a, p->z, d ); | |||||
gf_mul ( p->x, a, b ); | |||||
gf_mul ( p->z, p->t, a ); | |||||
gf_mul ( p->y, p->t, d ); | |||||
gf_mul ( p->t, b, d ); | |||||
decaf_bzero(a,sizeof(a)); | |||||
decaf_bzero(b,sizeof(b)); | |||||
decaf_bzero(c,sizeof(c)); | |||||
decaf_bzero(d,sizeof(d)); | |||||
} | |||||
#endif | |||||
decaf_bzero(enc2,sizeof(enc2)); | |||||
assert(API_NS(point_valid)(p) || ~succ); | |||||
return decaf_succeed_if(succ); | |||||
} | |||||
decaf_error_t API_NS(x_direct_scalarmul) ( | decaf_error_t API_NS(x_direct_scalarmul) ( | ||||
uint8_t out[X_PUBLIC_BYTES], | uint8_t out[X_PUBLIC_BYTES], | ||||
const uint8_t base[X_PUBLIC_BYTES], | const uint8_t base[X_PUBLIC_BYTES], | ||||
@@ -429,7 +429,7 @@ void $(c_ns)_eddsa_derive_public_key ( | |||||
/** | /** | ||||
* @brief EdDSA signing. | * @brief EdDSA signing. | ||||
* | * | ||||
* @param [out] The signature. | |||||
* @param [out] signature The signature. | |||||
* @param [in] privkey The private key. | * @param [in] privkey The private key. | ||||
* @param [in] pubkey The public key. | * @param [in] pubkey The public key. | ||||
* @param [in] context A "context" for this signature of up to 255 bytes. | * @param [in] context A "context" for this signature of up to 255 bytes. | ||||
@@ -447,19 +447,57 @@ void $(c_ns)_eddsa_sign ( | |||||
const uint8_t *message, | const uint8_t *message, | ||||
size_t message_len, | size_t message_len, | ||||
uint8_t prehashed | uint8_t prehashed | ||||
) API_VIS NONNULL NOINLINE; | |||||
) API_VIS __attribute__((nonnull(1,2,3))) NOINLINE; | |||||
/** | |||||
* @brief EdDSA signature verification. | |||||
* | |||||
* Uses the standard (i.e. less-strict) verification formula. | |||||
* | |||||
* @param [in] signature The signature. | |||||
* @param [in] pubkey The public key. | |||||
* @param [in] context A "context" for this signature of up to 255 bytes. | |||||
* @param [in] context_len Length of the context. | |||||
* @param [in] message The message to verify. | |||||
* @param [in] message_len The length of the message. | |||||
* @param [in] prehashed Nonzero if the message is actually the hash of something you want to verify. | |||||
*/ | |||||
decaf_error_t $(c_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_VIS __attribute__((nonnull(1,2))) NOINLINE; | |||||
/** | /** | ||||
* @brief EdDSA point encoding. | * @brief EdDSA point encoding. | ||||
* | * | ||||
* @param [out] enc The encoded point. | * @param [out] enc The encoded point. | ||||
* @param [in] p The point. | * @param [in] p The point. | ||||
* | |||||
* FIXME: encode and decode aren't inverses of each other: they | |||||
* multiply by a factor. Rename to reflect this once the base | |||||
* point doctrine is worked out. | |||||
*/ | */ | ||||
void $(c_ns)_point_encode_like_eddsa ( | void $(c_ns)_point_encode_like_eddsa ( | ||||
uint8_t enc[$(C_NS)_EDDSA_PUBLIC_BYTES], | uint8_t enc[$(C_NS)_EDDSA_PUBLIC_BYTES], | ||||
const $(c_ns)_point_t p | const $(c_ns)_point_t p | ||||
) API_VIS NONNULL NOINLINE; | ) API_VIS NONNULL NOINLINE; | ||||
/** | |||||
* @brief EdDSA point encoding. | |||||
* | |||||
* @param [out] enc The encoded point. | |||||
* @param [in] p The point. | |||||
*/ | |||||
decaf_error_t $(c_ns)_point_decode_like_eddsa ( | |||||
$(c_ns)_point_t p, | |||||
const uint8_t enc[$(C_NS)_EDDSA_PUBLIC_BYTES] | |||||
) API_VIS NONNULL NOINLINE; | |||||
/** | /** | ||||
* @brief Precompute a table for fast scalar multiplication. | * @brief Precompute a table for fast scalar multiplication. | ||||
* Some implementations do not include precomputed points; for | * Some implementations do not include precomputed points; for | ||||
@@ -287,7 +287,7 @@ public: | |||||
*/ | */ | ||||
inline explicit Point(const FixedBlock<SER_BYTES> &buffer, decaf_bool_t allow_identity=DECAF_TRUE) | inline explicit Point(const FixedBlock<SER_BYTES> &buffer, decaf_bool_t allow_identity=DECAF_TRUE) | ||||
throw(CryptoException) { | throw(CryptoException) { | ||||
if (DECAF_SUCCESS != decode(*this,buffer,allow_identity)) { | |||||
if (DECAF_SUCCESS != decode(buffer,allow_identity)) { | |||||
throw CryptoException(); | throw CryptoException(); | ||||
} | } | ||||
} | } | ||||
@@ -300,10 +300,34 @@ public: | |||||
* @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, | * @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point, | ||||
* or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. | * or was the identity and allow_identity was DECAF_FALSE. Contents of the buffer are undefined. | ||||
*/ | */ | ||||
static inline decaf_error_t WARN_UNUSED decode ( | |||||
Point &p, const FixedBlock<SER_BYTES> &buffer, decaf_bool_t allow_identity=DECAF_TRUE | |||||
inline decaf_error_t WARN_UNUSED decode ( | |||||
const FixedBlock<SER_BYTES> &buffer, decaf_bool_t allow_identity=DECAF_TRUE | |||||
) NOEXCEPT { | |||||
return $(c_ns)_point_decode(p,buffer.data(),allow_identity); | |||||
} | |||||
/** | |||||
* Initialize from C++ fixed-length byte string, like EdDSA. | |||||
* The all-zero string maps to the identity. | |||||
* | |||||
* @retval DECAF_SUCCESS the string was successfully decoded. | |||||
* @return DECAF_FAILURE the string was the wrong length, or wasn't the encoding of a point. | |||||
* Contents of the point are undefined. | |||||
* TODO: rename to noexcept? | |||||
*/ | |||||
inline decaf_error_t WARN_UNUSED decode_like_eddsa ( | |||||
const FixedBlock<$(C_NS)_EDDSA_PUBLIC_BYTES> &buffer | |||||
) NOEXCEPT { | ) NOEXCEPT { | ||||
return $(c_ns)_point_decode(p.p,buffer.data(),allow_identity); | |||||
return $(c_ns)_point_decode_like_eddsa(p,buffer.data()); | |||||
} | |||||
/** | |||||
* Encode like EdDSA. FIXME: and multiply by the cofactor... | |||||
*/ | |||||
inline SecureBuffer encode_like_eddsa() const { | |||||
SecureBuffer ret($(C_NS)_EDDSA_PUBLIC_BYTES); | |||||
$(c_ns)_point_encode_like_eddsa(ret.data(),p); | |||||
return ret; | |||||
} | } | ||||
/** | /** | ||||
@@ -649,8 +673,8 @@ public: | |||||
} | } | ||||
}; | }; | ||||
struct EdDSA { | |||||
struct EdDSA { /* TODO: make into a utility class. Possibly move to another include file. */ | |||||
public: | public: | ||||
/** The size of a public key */ | /** The size of a public key */ | ||||
static const size_t PUBLIC_BYTES = $(C_NS)_EDDSA_PUBLIC_BYTES; | static const size_t PUBLIC_BYTES = $(C_NS)_EDDSA_PUBLIC_BYTES; | ||||
@@ -661,7 +685,6 @@ public: | |||||
/** The size of a private key */ | /** The size of a private key */ | ||||
static const size_t SIGNATURE_BYTES = $(C_NS)_EDDSA_SIGNATURE_BYTES; | static const size_t SIGNATURE_BYTES = $(C_NS)_EDDSA_SIGNATURE_BYTES; | ||||
/* TODO: make into a nice class. Change name. Possibly move to another include file. */ | |||||
static inline SecureBuffer generate_key ( | static inline SecureBuffer generate_key ( | ||||
const FixedBlock<PRIVATE_BYTES> &priv | const FixedBlock<PRIVATE_BYTES> &priv | ||||
) { | ) { | ||||
@@ -691,6 +714,38 @@ public: | |||||
); | ); | ||||
return out; | return out; | ||||
} | } | ||||
static inline decaf_error_t WARN_UNUSED verify_noexcept ( | |||||
const FixedBlock<SIGNATURE_BYTES> &sig, | |||||
const FixedBlock<PUBLIC_BYTES> &pub, | |||||
const Block &context, | |||||
const Block &message, | |||||
bool prehashed = false | |||||
) { | |||||
if (context.size() > 255) return DECAF_FAILURE; | |||||
return $(c_ns)_eddsa_verify ( | |||||
sig.data(), | |||||
pub.data(), | |||||
context.data(), | |||||
context.size(), | |||||
message.data(), | |||||
message.size(), | |||||
prehashed | |||||
); | |||||
} | |||||
static inline void verify ( | |||||
const FixedBlock<SIGNATURE_BYTES> &sig, | |||||
const FixedBlock<PUBLIC_BYTES> &pub, | |||||
const Block &context, | |||||
const Block &message, | |||||
bool prehashed = false | |||||
) throw(LengthException,CryptoException) { | |||||
if (context.size() > 255) { throw LengthException(); } | |||||
if (DECAF_SUCCESS != verify_noexcept( sig, pub, context, message, prehashed )) { | |||||
throw CryptoException(); | |||||
} | |||||
} | |||||
}; | }; | ||||
}; /* struct $(cxx_ns) */ | }; /* struct $(cxx_ns) */ | ||||
@@ -394,6 +394,16 @@ static void test_ec() { | |||||
); | ); | ||||
point_check(test,p,q,r,x,0,Point(x.direct_scalarmul(p.serialize())),x*p,"direct mul"); | point_check(test,p,q,r,x,0,Point(x.direct_scalarmul(p.serialize())),x*p,"direct mul"); | ||||
q=p; | |||||
for (int j=1; j<Group::REMOVED_COFACTOR; j<<=1) q = q.times_two(); | |||||
decaf_error_t error = r.decode_like_eddsa(p.encode_like_eddsa()); | |||||
if (error != DECAF_SUCCESS) { | |||||
test.fail(); | |||||
printf(" Decode like EdDSA failed."); | |||||
} | |||||
point_check(test,-q,q,r,0,0,q,r,"Encode like EdDSA round-trip"); | |||||
} | } | ||||
} | } | ||||
@@ -515,12 +525,39 @@ static void test_cfrg_vectors() { | |||||
} | } | ||||
} | } | ||||
static void test_eddsa() { | |||||
Test test("EdDSA"); | |||||
SpongeRng rng(Block("test_cfrg_crypto"),SpongeRng::DETERMINISTIC); | |||||
for (int i=0; i<NTESTS && test.passing_now; i++) { | |||||
FixedArrayBuffer<EdDSA::PRIVATE_BYTES> priv(rng); | |||||
SecureBuffer pub = EdDSA::generate_key(priv); | |||||
SecureBuffer message(i); | |||||
rng.read(message); | |||||
SecureBuffer context(i%256); | |||||
rng.read(message); | |||||
SecureBuffer sig = EdDSA::sign(priv,pub,context,message,i%2); | |||||
try { | |||||
EdDSA::verify(sig,pub,context,message,i%2); | |||||
} catch(CryptoException) { | |||||
test.fail(); | |||||
printf(" Signature validation failed on sig %d\n", i); | |||||
} | |||||
} | |||||
} | |||||
static void run() { | static void run() { | ||||
printf("Testing %s:\n",Group::name()); | printf("Testing %s:\n",Group::name()); | ||||
test_arithmetic(); | test_arithmetic(); | ||||
test_elligator(); | test_elligator(); | ||||
test_ec(); | test_ec(); | ||||
test_eddsa(); | |||||
test_cfrg_crypto(); | test_cfrg_crypto(); | ||||
test_cfrg_vectors(); | test_cfrg_vectors(); | ||||
test_crypto(); | test_crypto(); | ||||