From b1c6de630923bc9cd74b60094fe73c0e9ccf411d Mon Sep 17 00:00:00 2001 From: Michael Hamburg Date: Mon, 22 Feb 2016 13:39:56 -0800 Subject: [PATCH] EdDSA 448 seems to be working. Needs more testing, code moved around. EdDSA 255 not working yet; needs SHA512 --- src/per_curve/decaf.tmpl.c | 85 +++++++++++++++++++++++++++++++----- src/per_curve/decaf.tmpl.h | 42 +++++++++++++++++- src/per_curve/decaf.tmpl.hxx | 69 ++++++++++++++++++++++++++--- test/test_decaf.cxx | 37 ++++++++++++++++ 4 files changed, 212 insertions(+), 21 deletions(-) diff --git a/src/per_curve/decaf.tmpl.c b/src/per_curve/decaf.tmpl.c index aae2cd2..65fa107 100644 --- a/src/per_curve/decaf.tmpl.c +++ b/src/per_curve/decaf.tmpl.c @@ -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], diff --git a/src/per_curve/decaf.tmpl.h b/src/per_curve/decaf.tmpl.h index a91499c..e8c14ba 100644 --- a/src/per_curve/decaf.tmpl.h +++ b/src/per_curve/decaf.tmpl.h @@ -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 diff --git a/src/per_curve/decaf.tmpl.hxx b/src/per_curve/decaf.tmpl.hxx index 048f97e..e020bbc 100644 --- a/src/per_curve/decaf.tmpl.hxx +++ b/src/per_curve/decaf.tmpl.hxx @@ -287,7 +287,7 @@ public: */ inline explicit Point(const FixedBlock &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 &buffer, decaf_bool_t allow_identity=DECAF_TRUE + inline decaf_error_t WARN_UNUSED decode ( + const FixedBlock &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 &priv ) { @@ -691,6 +714,38 @@ public: ); return out; } + + static inline decaf_error_t WARN_UNUSED verify_noexcept ( + const FixedBlock &sig, + const FixedBlock &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 &sig, + const FixedBlock &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) */ diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index e4b481d..ce11da3 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -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 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();