@@ -1067,18 +1067,17 @@ void API_NS(point_encode_like_eddsa) ( | |||
gf u; | |||
gf_sqr ( x, p->x ); | |||
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_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)); | |||
} | |||
#endif | |||
@@ -1090,7 +1089,7 @@ void API_NS(point_encode_like_eddsa) ( | |||
/* Encode */ | |||
enc[$(C_NS)_EDDSA_PRIVATE_BYTES-1] = 0; | |||
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(y,sizeof(y)); | |||
@@ -1098,6 +1097,68 @@ void API_NS(point_encode_like_eddsa) ( | |||
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) ( | |||
uint8_t out[X_PUBLIC_BYTES], | |||
const uint8_t base[X_PUBLIC_BYTES], | |||
@@ -429,7 +429,7 @@ void $(c_ns)_eddsa_derive_public_key ( | |||
/** | |||
* @brief EdDSA signing. | |||
* | |||
* @param [out] The signature. | |||
* @param [out] signature The signature. | |||
* @param [in] privkey The private key. | |||
* @param [in] pubkey The public key. | |||
* @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, | |||
size_t message_len, | |||
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. | |||
* | |||
* @param [out] enc The encoded 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 ( | |||
uint8_t enc[$(C_NS)_EDDSA_PUBLIC_BYTES], | |||
const $(c_ns)_point_t p | |||
) 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. | |||
* 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) | |||
throw(CryptoException) { | |||
if (DECAF_SUCCESS != decode(*this,buffer,allow_identity)) { | |||
if (DECAF_SUCCESS != decode(buffer,allow_identity)) { | |||
throw CryptoException(); | |||
} | |||
} | |||
@@ -300,10 +300,34 @@ public: | |||
* @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. | |||
*/ | |||
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 { | |||
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: | |||
/** The size of a public key */ | |||
static const size_t PUBLIC_BYTES = $(C_NS)_EDDSA_PUBLIC_BYTES; | |||
@@ -661,7 +685,6 @@ public: | |||
/** The size of a private key */ | |||
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 ( | |||
const FixedBlock<PRIVATE_BYTES> &priv | |||
) { | |||
@@ -691,6 +714,38 @@ public: | |||
); | |||
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) */ | |||
@@ -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"); | |||
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() { | |||
printf("Testing %s:\n",Group::name()); | |||
test_arithmetic(); | |||
test_elligator(); | |||
test_ec(); | |||
test_eddsa(); | |||
test_cfrg_crypto(); | |||
test_cfrg_vectors(); | |||
test_crypto(); | |||