| @@ -1580,6 +1580,122 @@ decaf_error_t API_NS(direct_scalarmul) ( | |||
| return succ; | |||
| } | |||
| decaf_error_t API_NS(x_direct_scalarmul) ( | |||
| uint8_t out[X_PUBLIC_BYTES], | |||
| const uint8_t base[X_PUBLIC_BYTES], | |||
| const uint8_t scalar[X_PRIVATE_BYTES] | |||
| ) { | |||
| gf x1, x2, z2, x3, z3, t1, t2; | |||
| ignore_result(gf_deserialize(x1,base)); | |||
| gf_copy(x2,ONE); | |||
| gf_copy(z2,ZERO); | |||
| gf_copy(x3,x1); | |||
| gf_copy(z3,ONE); | |||
| int t; | |||
| mask_t swap = 0; | |||
| for (t = X_PRIVATE_BITS-1; t>=0; t--) { | |||
| uint8_t sb = scalar[t/8]; | |||
| /* Scalar conditioning */ | |||
| if (t/8==0) sb &= -(uint8_t)COFACTOR; | |||
| else if (t == X_PRIVATE_BITS-1) sb = -1; | |||
| mask_t k_t = (sb>>(t%8)) & 1; | |||
| k_t = -k_t; /* set to all 0s or all 1s */ | |||
| swap ^= k_t; | |||
| cond_swap(x2,x3,swap); | |||
| cond_swap(z2,z3,swap); | |||
| swap = k_t; | |||
| gf_add(t1,x2,z2); /* A = x2 + z2 */ | |||
| gf_sub(t2,x2,z2); /* B = x2 - z2 */ | |||
| gf_sub(z2,x3,z3); /* D = x3 - z3 */ | |||
| gf_mul(x2,t1,z2); /* DA */ | |||
| gf_add(z2,z3,x3); /* C = x3 + z3 */ | |||
| gf_mul(x3,t2,z2); /* CB */ | |||
| gf_sub(z3,x2,x3); /* DA-CB */ | |||
| gf_sqr(z2,z3); /* (DA-CB)^2 */ | |||
| gf_mul(z3,x1,z2); /* z3 = x1(DA-CB)^2 */ | |||
| gf_add(z2,x2,x3); /* (DA+CB) */ | |||
| gf_sqr(x3,z2); /* x3 = (DA+CB)^2 */ | |||
| gf_sqr(z2,t1); /* AA = A^2 */ | |||
| gf_sqr(t1,t2); /* BB = B^2 */ | |||
| gf_mul(x2,z2,t1); /* x2 = AA*BB */ | |||
| gf_sub(t2,z2,t1); /* E = AA-BB */ | |||
| gf_mulw_sgn(t1,t2,-EDWARDS_D); /* E*-d = a24*E */ | |||
| gf_add(t1,t1,z2); /* AA + a24*E */ | |||
| gf_mul(z2,t2,t1); /* z2 = E(AA+a24*E) */ | |||
| } | |||
| /* Finish */ | |||
| cond_swap(x2,x3,swap); | |||
| cond_swap(z2,z3,swap); | |||
| gf_invert(z2,z2); | |||
| gf_mul(x1,x2,z2); | |||
| gf_serialize(out,x1); | |||
| mask_t nz = ~gf_eq(x1,ZERO); | |||
| decaf_bzero(x1,sizeof(x1)); | |||
| decaf_bzero(x2,sizeof(x2)); | |||
| decaf_bzero(z2,sizeof(z2)); | |||
| decaf_bzero(x3,sizeof(x3)); | |||
| decaf_bzero(z3,sizeof(z3)); | |||
| decaf_bzero(t1,sizeof(t1)); | |||
| decaf_bzero(t2,sizeof(t2)); | |||
| return decaf_succeed_if(mask_to_bool(nz)); | |||
| } | |||
| void API_NS(x_base_scalarmul) ( | |||
| uint8_t out[X_PUBLIC_BYTES], | |||
| const uint8_t scalar[X_PRIVATE_BYTES] | |||
| ) { | |||
| /* Scalar conditioning */ | |||
| uint8_t scalar2[X_PRIVATE_BYTES]; | |||
| memcpy(scalar2,scalar,sizeof(scalar2)); | |||
| scalar2[0] &= -(uint8_t)COFACTOR; | |||
| scalar2[X_PRIVATE_BYTES-1] &= ~(-1<<((X_PRIVATE_BITS+7)%8)); | |||
| scalar2[X_PRIVATE_BYTES-1] |= 1<<((X_PRIVATE_BITS+7)%8); | |||
| scalar_t the_scalar; | |||
| API_NS(scalar_decode_long)(the_scalar,scalar2,sizeof(scalar2)); | |||
| /* We're gonna isogenize by 2, so divide by 2. | |||
| * | |||
| * Why by 2, even though it's a 4-isogeny? | |||
| * | |||
| * The isogeny map looks like | |||
| * Montgomery <-2-> Jacobi <-2-> Edwards | |||
| * | |||
| * Since the Jacobi base point is the PREimage of the iso to | |||
| * the Montgomery curve, and we're going | |||
| * Jacobi -> Edwards -> Jacobi -> Montgomery, | |||
| * we pick up only a factor of 2 over Jacobi -> Montgomery. | |||
| */ | |||
| sc_halve(the_scalar,the_scalar,sc_p); | |||
| point_t p; | |||
| API_NS(precomputed_scalarmul)(p,API_NS(precomputed_base),the_scalar); | |||
| /* Isogenize to Montgomery curve */ | |||
| gf_invert(p->t,p->x); /* 1/x */ | |||
| gf_mul(p->z,p->t,p->y); /* y/x */ | |||
| gf_sqr(p->y,p->z); /* (y/x)^2 */ | |||
| #if IMAGINE_TWIST | |||
| gf_sub(p->y,ZERO,p->y); | |||
| #endif | |||
| gf_serialize(out,p->y); | |||
| decaf_bzero(scalar2,sizeof(scalar2)); | |||
| API_NS(scalar_destroy)(the_scalar); | |||
| API_NS(point_destroy)(p); | |||
| } | |||
| /** | |||
| * @cond internal | |||
| * Control for variable-time scalar multiply algorithms. | |||
| @@ -28,6 +28,7 @@ const API_NS(scalar_t) API_NS(sc_r2) = {{{0}}}; | |||
| const decaf_word_t API_NS(MONTGOMERY_FACTOR) = 0; | |||
| const API_NS(point_t) API_NS(point_base); | |||
| const uint8_t API_NS(x_base_point)[X_PUBLIC_BYTES] = {0}; | |||
| struct niels_s; | |||
| const gf_s *API_NS(precomputed_wnaf_as_fe); | |||
| @@ -170,6 +171,19 @@ int main(int argc, char **argv) { | |||
| w *= w*plo + 2; | |||
| } | |||
| printf("const decaf_word_t API_NS(MONTGOMERY_FACTOR) = (decaf_word_t)0x%016llxull;\n\n", w); | |||
| /* Generate the Montgomery ladder version of the base point */ | |||
| gf base1,base2; | |||
| ret = gf_deserialize(base1,base_point_ser_for_pregen); | |||
| if (ret != DECAF_SUCCESS) return 1; | |||
| gf_sqr(base2,base1); | |||
| uint8_t x_ser[X_PUBLIC_BYTES] = {0}; | |||
| gf_serialize(x_ser, base2); | |||
| printf("const uint8_t API_NS(x_base_point)[%d] = {", X_PUBLIC_BYTES); | |||
| for (i=0; i<X_PUBLIC_BYTES; i++) { | |||
| printf("%s%s%d",i?",":"",(i%32==0)?"\n ":"",x_ser[i]); | |||
| } | |||
| printf("\n};\n"); | |||
| return 0; | |||
| } | |||
| @@ -49,6 +49,15 @@ def ceil_log2(x): | |||
| for field,data in field_data.iteritems(): | |||
| if "gf_bits" not in data: | |||
| data["gf_bits"] = ceil_log2(data["modulus"]) | |||
| if "x_pub_bytes" not in data: | |||
| data["x_pub_bytes"] = (data["gf_bits"]-1)//8 + 1 | |||
| if "x_priv_bytes" not in data: | |||
| data["x_priv_bytes"] = (data["gf_bits"]-1)//8 + 1 | |||
| if "x_priv_bits" not in data: | |||
| data["x_priv_bits"] = ceil_log2(data["modulus"]*0.99) | |||
| for curve,data in curve_data.iteritems(): | |||
| for key in field_data[data["field"]]: | |||
| @@ -66,7 +75,7 @@ for curve,data in curve_data.iteritems(): | |||
| data["bits"] = ceil_log2(data["modulus"]) | |||
| if "ser_bytes" not in data: | |||
| data["ser_bytes"] = (data["bits"]-1)//8 + 1 | |||
| data["ser_bytes"] = (data["bits"]-2)//8 + 1 | |||
| if "scalar_ser_bytes" not in data: | |||
| data["scalar_ser_bytes"] = (data["scalar_bits"]-1)//8 + 1 | |||
| @@ -35,6 +35,12 @@ typedef struct gf_%(gf_shortname)s_s { | |||
| /** Number of bytes in a serialized scalar. */ | |||
| #define %(C_NS)s_SCALAR_BYTES %(scalar_ser_bytes)d | |||
| /** Number of bytes in an x%(gf_shortname)s public key */ | |||
| #define X%(gf_shortname)s_PUBLIC_BYTES %(x_pub_bytes)d | |||
| /** Number of bytes in an x%(gf_shortname)s private key */ | |||
| #define X%(gf_shortname)s_PRIVATE_BYTES %(x_priv_bytes)d | |||
| /** Twisted Edwards extended homogeneous coordinates */ | |||
| typedef struct %(c_ns)s_point_s { | |||
| /** @cond internal */ | |||
| @@ -346,6 +352,39 @@ decaf_error_t %(c_ns)s_direct_scalarmul ( | |||
| decaf_bool_t short_circuit | |||
| ) API_VIS NONNULL3 WARN_UNUSED NOINLINE; | |||
| /** | |||
| * @brief RFC 7748 Diffie-Hellman scalarmul. This function uses a different | |||
| * (non-Decaf) encoding. | |||
| * | |||
| * @param [out] scaled The scaled point base*scalar | |||
| * @param [in] base The point to be scaled. | |||
| * @param [in] scalar The scalar to multiply by. | |||
| * | |||
| * @retval DECAF_SUCCESS The scalarmul succeeded. | |||
| * @retval DECAF_FAILURE The scalarmul didn't succeed, because the base | |||
| * point is in a small subgroup. | |||
| */ | |||
| decaf_error_t %(c_ns)s_x_direct_scalarmul ( /* TODO: rename? */ | |||
| uint8_t out[X%(gf_shortname)s_PUBLIC_BYTES], | |||
| const uint8_t base[X%(gf_shortname)s_PUBLIC_BYTES], | |||
| const uint8_t scalar[X%(gf_shortname)s_PRIVATE_BYTES] | |||
| ) API_VIS NONNULL3 WARN_UNUSED NOINLINE; | |||
| /** The base point for X%(gf_shortname)s Diffie-Hellman */ | |||
| extern const uint8_t %(c_ns)s_x_base_point[X%(gf_shortname)s_PUBLIC_BYTES] API_VIS; | |||
| /** | |||
| * @brief RFC 7748 Diffie-Hellman base point scalarmul. This function uses | |||
| * a different (non-Decaf) encoding. | |||
| * | |||
| * @param [out] scaled The scaled point base*scalar | |||
| * @param [in] scalar The scalar to multiply by. | |||
| */ | |||
| void %(c_ns)s_x_base_scalarmul ( | |||
| uint8_t out[X%(gf_shortname)s_PUBLIC_BYTES], | |||
| const uint8_t scalar[X%(gf_shortname)s_PRIVATE_BYTES] | |||
| ) API_VIS NONNULL2 NOINLINE; | |||
| /** | |||
| * @brief Precompute a table for fast scalar multiplication. | |||
| * Some implementations do not include precomputed points; for | |||
| @@ -136,7 +136,7 @@ public: | |||
| * Decode from correct-length little-endian byte sequence. | |||
| * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. | |||
| */ | |||
| static inline decaf_error_t __attribute__((warn_unused_result)) decode ( | |||
| static inline decaf_error_t WARN_UNUSED decode ( | |||
| Scalar &sc, const FixedBlock<SER_BYTES> buffer | |||
| ) NOEXCEPT { | |||
| return %(c_ns)s_scalar_decode(sc.s,buffer.data()); | |||
| @@ -175,7 +175,7 @@ public: | |||
| /** Invert with Fermat's Little Theorem (slow!). If *this == 0, set r=0 | |||
| * and return DECAF_FAILURE. */ | |||
| inline decaf_error_t __attribute__((warn_unused_result)) | |||
| inline decaf_error_t WARN_UNUSED | |||
| inverse_noexcept(Scalar &r) const NOEXCEPT { | |||
| return %(c_ns)s_scalar_invert(r.s,s); | |||
| } | |||
| @@ -276,7 +276,7 @@ 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 __attribute__((warn_unused_result)) decode ( | |||
| static inline decaf_error_t WARN_UNUSED decode ( | |||
| Point &p, const FixedBlock<SER_BYTES> &buffer, decaf_bool_t allow_identity=DECAF_TRUE | |||
| ) NOEXCEPT { | |||
| return %(c_ns)s_point_decode(p.p,buffer.data(),allow_identity); | |||
| @@ -565,6 +565,64 @@ public: | |||
| /** @endcond */ | |||
| }; | |||
| struct DhLadder { | |||
| public: | |||
| /** Bytes in an X%(gf_shortname)s public key. */ | |||
| static const size_t PUBLIC_BYTES = X%(gf_shortname)s_PUBLIC_BYTES; | |||
| /** Bytes in an X%(gf_shortname)s private key. */ | |||
| static const size_t PRIVATE_BYTES = X%(gf_shortname)s_PRIVATE_BYTES; | |||
| /** Base point for a scalar multiplication. */ | |||
| static const FixedBlock<PUBLIC_BYTES> base_point() NOEXCEPT { | |||
| return FixedBlock<PUBLIC_BYTES>(%(c_ns)s_x_base_point); | |||
| } | |||
| /** Generate and return a shared secret with public key. */ | |||
| static inline SecureBuffer shared_secret( | |||
| const FixedBlock<PUBLIC_BYTES> &pk, | |||
| const FixedBlock<PRIVATE_BYTES> &scalar | |||
| ) throw(std::bad_alloc,CryptoException) { | |||
| SecureBuffer out(PUBLIC_BYTES); | |||
| if (DECAF_SUCCESS != %(c_ns)s_x_direct_scalarmul(out.data(), pk.data(), scalar.data())) { | |||
| throw CryptoException(); | |||
| } | |||
| return out; | |||
| } | |||
| /** Generate and return a shared secret with public key, noexcept version. */ | |||
| static inline decaf_error_t WARN_UNUSED | |||
| shared_secret_noexcept ( | |||
| FixedBuffer<PUBLIC_BYTES> &out, | |||
| const FixedBlock<PUBLIC_BYTES> &pk, | |||
| const FixedBlock<PRIVATE_BYTES> &scalar | |||
| ) NOEXCEPT { | |||
| return %(c_ns)s_x_direct_scalarmul(out.data(), pk.data(), scalar.data()); | |||
| } | |||
| /** Generate and return a public key; equivalent to shared_secret(base_point(),scalar) | |||
| * but possibly faster. | |||
| */ | |||
| static inline SecureBuffer generate_key( | |||
| const FixedBlock<PRIVATE_BYTES> &scalar | |||
| ) throw(std::bad_alloc) { | |||
| SecureBuffer out(PUBLIC_BYTES); | |||
| %(c_ns)s_x_base_scalarmul(out.data(), scalar.data()); | |||
| return out; | |||
| } | |||
| /** Generate and return a public key into a fixed buffer; | |||
| * equivalent to shared_secret(base_point(),scalar) but possibly faster. | |||
| */ | |||
| static inline void | |||
| generate_key_noexcept ( | |||
| FixedBuffer<PUBLIC_BYTES> &out, | |||
| const FixedBlock<PRIVATE_BYTES> &scalar | |||
| ) NOEXCEPT { | |||
| %(c_ns)s_x_base_scalarmul(out.data(), scalar.data()); | |||
| } | |||
| }; | |||
| }; /* struct %(cxx_ns)s */ | |||
| /** @cond internal */ | |||
| @@ -14,7 +14,7 @@ f_field_h = gen_file( | |||
| #define __DECAF_%(gf_shortname)s_GF_DEFINED__ 1 | |||
| #define NLIMBS (%(gf_impl_bits)d/sizeof(word_t)/8) | |||
| #define SER_BYTES ((%(gf_bits)d-1)/8 + 1) | |||
| #define SER_BYTES ((%(gf_bits)d-1)/8 + 1) /* MAGIC: depends on if high bit known to be clear (eg p521) */ | |||
| typedef struct gf_%(gf_shortname)s_s { | |||
| word_t limb[NLIMBS]; | |||
| } __attribute__((aligned(32))) gf_%(gf_shortname)s_s, gf_%(gf_shortname)s_t[1]; | |||
| @@ -42,6 +42,11 @@ typedef struct gf_%(gf_shortname)s_s { | |||
| #define gf_serialize gf_%(gf_shortname)s_serialize | |||
| #define gf_deserialize gf_%(gf_shortname)s_deserialize | |||
| /* RFC 7748 support */ | |||
| #define X_PUBLIC_BYTES %(x_pub_bytes)d | |||
| #define X_PRIVATE_BYTES %(x_priv_bytes)d | |||
| #define X_PRIVATE_BITS %(x_priv_bits)d | |||
| #define SQRT_MINUS_ONE P%(gf_shortname)s_SQRT_MINUS_ONE /* might not be defined */ | |||
| #define INLINE_UNUSED __inline__ __attribute__((unused,always_inline)) | |||
| @@ -41,6 +41,12 @@ private: | |||
| keccak_prng_t sp; | |||
| public: | |||
| /** Deterministic flag. | |||
| * The idea is that DETERMINISTIC is used for testing or for lockstep computations, | |||
| * and NONDETERMINISTIC is used in production. | |||
| */ | |||
| enum Deterministic { RANDOM = 0, DETERMINISTIC = 1 }; | |||
| /** Exception thrown when The RNG fails (to seed itself) */ | |||
| class RngException : public std::exception { | |||
| private: | |||
| @@ -54,14 +60,14 @@ public: | |||
| }; | |||
| /** Initialize, deterministically by default, from block */ | |||
| inline SpongeRng( const Block &in, bool deterministic = true ) { | |||
| spongerng_init_from_buffer(sp,in.data(),in.size(),deterministic); | |||
| inline SpongeRng( const Block &in, Deterministic det ) { | |||
| spongerng_init_from_buffer(sp,in.data(),in.size(),(int)det); | |||
| } | |||
| /** Initialize, non-deterministically by default, from C/C++ filename */ | |||
| inline SpongeRng( const std::string &in = "/dev/urandom", size_t len = 32, bool deterministic = false ) | |||
| inline SpongeRng( const std::string &in = "/dev/urandom", size_t len = 32, Deterministic det = RANDOM ) | |||
| throw(RngException) { | |||
| decaf_error_t ret = spongerng_init_from_file(sp,in.c_str(),len,deterministic); | |||
| decaf_error_t ret = spongerng_init_from_file(sp,in.c_str(),len,det); | |||
| if (!decaf_successful(ret)) { | |||
| throw RngException(errno, "Couldn't load from file"); | |||
| } | |||
| @@ -288,10 +288,21 @@ static void spake2ee( | |||
| server.respec(STROBE_KEYED_128); | |||
| } | |||
| static void cfrg() { | |||
| SpongeRng rng(Block("bench_cfrg_crypto"),SpongeRng::DETERMINISTIC); | |||
| FixedArrayBuffer<Group::DhLadder::PUBLIC_BYTES> base(rng); | |||
| FixedArrayBuffer<Group::DhLadder::PRIVATE_BYTES> s1(rng); | |||
| for (Benchmark b("RFC 7748 keygen"); b.iter(); ) { Group::DhLadder::generate_key(s1); } | |||
| for (Benchmark b("RFC 7748 shared secret"); b.iter(); ) { Group::DhLadder::shared_secret(base,s1); } | |||
| } | |||
| static void macro() { | |||
| printf("\nMacro-benchmarks for %s:\n", Group::name()); | |||
| printf("Crypto benchmarks:\n"); | |||
| SpongeRng rng(Block("macro rng seed")); | |||
| printf("CFRG crypto benchmarks:\n"); | |||
| cfrg(); | |||
| printf("\nSample crypto benchmarks:\n"); | |||
| SpongeRng rng(Block("macro rng seed"),SpongeRng::DETERMINISTIC); | |||
| PrivateKey<Group> s1((NOINIT())), s2(rng); | |||
| PublicKey<Group> p1((NOINIT())), p2(s2); | |||
| @@ -317,8 +328,8 @@ static void macro() { | |||
| } | |||
| printf("\nProtocol benchmarks:\n"); | |||
| SpongeRng clientRng(Block("client rng seed")); | |||
| SpongeRng serverRng(Block("server rng seed")); | |||
| SpongeRng clientRng(Block("client rng seed"),SpongeRng::DETERMINISTIC); | |||
| SpongeRng serverRng(Block("server rng seed"),SpongeRng::DETERMINISTIC); | |||
| SecureBuffer hashedPassword(Block("hello world")); | |||
| for (Benchmark b("Spake2ee c+s",0.1); b.iter(); ) { | |||
| spake2ee(clientRng, serverRng, hashedPassword,false); | |||
| @@ -343,7 +354,7 @@ static void macro() { | |||
| } | |||
| static void micro() { | |||
| SpongeRng rng(Block("per-curve-benchmarks")); | |||
| SpongeRng rng(Block("per-curve-benchmarks"),SpongeRng::DETERMINISTIC); | |||
| Precomputed pBase; | |||
| Point p,q; | |||
| Scalar s(1),t(2); | |||
| @@ -382,7 +393,7 @@ int main(int argc, char **argv) { | |||
| if (argc >= 2 && !strcmp(argv[1], "--micro")) | |||
| micro = true; | |||
| SpongeRng rng(Block("micro-benchmarks")); | |||
| SpongeRng rng(Block("micro-benchmarks"),SpongeRng::DETERMINISTIC); | |||
| if (micro) { | |||
| printf("\nMicro-benchmarks:\n"); | |||
| SHAKE<128> shake1; | |||
| @@ -415,7 +426,6 @@ int main(int argc, char **argv) { | |||
| Benches<Ed448Goldilocks>::micro(); | |||
| } | |||
| Benches<IsoEd25519>::macro(); | |||
| Benches<Ed448Goldilocks>::macro(); | |||
| @@ -92,6 +92,10 @@ static void test_ec() { | |||
| } | |||
| } | |||
| /* TODO: test x25519/x448 */ | |||
| /* FUTURE: test ed25519/ed448 */ | |||
| /* Specify the same value as you did when compiling decaf_crypto.c */ | |||
| #ifndef DECAF_CRYPTO_SHARED_SECRET_SHORT_CIRUIT | |||
| #define DECAF_CRYPTO_SHARED_SECRET_SHORT_CIRUIT DECAF_FALSE | |||
| @@ -47,6 +47,7 @@ template<typename Group> struct Tests { | |||
| typedef typename Group::Scalar Scalar; | |||
| typedef typename Group::Point Point; | |||
| typedef typename Group::DhLadder DhLadder; | |||
| typedef typename Group::Precomputed Precomputed; | |||
| static void print(const char *name, const Scalar &x) { | |||
| @@ -128,7 +129,7 @@ static bool point_check( | |||
| } | |||
| static void test_arithmetic() { | |||
| SpongeRng rng(Block("test_arithmetic")); | |||
| SpongeRng rng(Block("test_arithmetic"),SpongeRng::DETERMINISTIC); | |||
| Test test("Arithmetic"); | |||
| Scalar x(0),y(0),z(0); | |||
| @@ -169,7 +170,7 @@ static void test_arithmetic() { | |||
| } | |||
| static void test_elligator() { | |||
| SpongeRng rng(Block("test_elligator")); | |||
| SpongeRng rng(Block("test_elligator"),SpongeRng::DETERMINISTIC); | |||
| Test test("Elligator"); | |||
| const int NHINTS = Group::REMOVED_COFACTOR * 2; | |||
| @@ -265,7 +266,7 @@ static void test_elligator() { | |||
| } | |||
| static void test_ec() { | |||
| SpongeRng rng(Block("test_ec")); | |||
| SpongeRng rng(Block("test_ec"),SpongeRng::DETERMINISTIC); | |||
| Test test("EC"); | |||
| @@ -327,7 +328,7 @@ static void test_ec() { | |||
| static void test_crypto() { | |||
| Test test("Sample crypto"); | |||
| SpongeRng rng(Block("test_decaf_crypto")); | |||
| SpongeRng rng(Block("test_decaf_crypto"),SpongeRng::DETERMINISTIC); | |||
| for (int i=0; i<NTESTS && test.passing_now; i++) { | |||
| PrivateKey<Group> priv1(rng), priv2(rng); | |||
| @@ -340,14 +341,123 @@ static void test_crypto() { | |||
| SecureBuffer s1(priv1.sharedSecret(pub2,32,true)); | |||
| SecureBuffer s2(priv2.sharedSecret(pub1,32,false)); | |||
| if (memcmp(s1.data(),s2.data(),s1.size())) { | |||
| if (!memeq(s1,s2)) { | |||
| test.fail(); | |||
| printf(" Shared secrets disagree."); | |||
| printf(" Shared secrets disagree on iteration %d.\n",i); | |||
| } | |||
| } | |||
| } | |||
| }; /* template<GroupId GROUP> */ | |||
| static const uint8_t rfc7748_1[DhLadder::PUBLIC_BYTES]; | |||
| static const uint8_t rfc7748_1000[DhLadder::PUBLIC_BYTES]; | |||
| static const uint8_t rfc7748_1000000[DhLadder::PUBLIC_BYTES]; | |||
| static void test_cfrg_crypto() { | |||
| Test test("CFRG crypto"); | |||
| SpongeRng rng(Block("test_cfrg_crypto"),SpongeRng::DETERMINISTIC); | |||
| for (int i=0; i<NTESTS && test.passing_now; i++) { | |||
| FixedArrayBuffer<DhLadder::PUBLIC_BYTES> base(rng); | |||
| FixedArrayBuffer<DhLadder::PRIVATE_BYTES> s1(rng), s2(rng); | |||
| SecureBuffer p1 = DhLadder::shared_secret(base,s1); | |||
| SecureBuffer p2 = DhLadder::shared_secret(base,s2); | |||
| SecureBuffer ss1 = DhLadder::shared_secret(p2,s1); | |||
| SecureBuffer ss2 = DhLadder::shared_secret(p1,s2); | |||
| if (!memeq(ss1,ss2)) { | |||
| test.fail(); | |||
| printf(" Shared secrets disagree on iteration %d.\n",i); | |||
| } | |||
| if (!memeq( | |||
| DhLadder::shared_secret(DhLadder::base_point(),s1), | |||
| DhLadder::generate_key(s1) | |||
| )) { | |||
| test.fail(); | |||
| printf(" Generated keys disagree on iteration %d.\n",i); | |||
| } | |||
| } | |||
| } | |||
| static void test_cfrg_vectors() { | |||
| Test test("CFRG test vectors"); | |||
| SecureBuffer k = DhLadder::base_point(); | |||
| SecureBuffer u = DhLadder::base_point(); | |||
| int the_ntests = (NTESTS < 1000000) ? 1000 : 1000000; | |||
| for (int i=0; i<the_ntests && test.passing_now; i++) { | |||
| SecureBuffer n = DhLadder::shared_secret(u,k); | |||
| u = k; k = n; | |||
| if (i==1-1) { | |||
| if (!memeq(k,SecureBuffer(FixedBlock<DhLadder::PUBLIC_BYTES>(rfc7748_1)))) { | |||
| test.fail(); | |||
| printf(" Test vectors disagree at 1."); | |||
| } | |||
| } else if (i==1000-1) { | |||
| if (!memeq(k,SecureBuffer(FixedBlock<DhLadder::PUBLIC_BYTES>(rfc7748_1000)))) { | |||
| test.fail(); | |||
| printf(" Test vectors disagree at 1000."); | |||
| } | |||
| } else if (i==1000000-1) { | |||
| if (!memeq(k,SecureBuffer(FixedBlock<DhLadder::PUBLIC_BYTES>(rfc7748_1000000)))) { | |||
| test.fail(); | |||
| printf(" Test vectors disagree at 1000000."); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }; /* template<GroupId GROUP> struct Tests */ | |||
| template<> const uint8_t Tests<IsoEd25519>::rfc7748_1[32] = { | |||
| 0x42,0x2c,0x8e,0x7a,0x62,0x27,0xd7,0xbc, | |||
| 0xa1,0x35,0x0b,0x3e,0x2b,0xb7,0x27,0x9f, | |||
| 0x78,0x97,0xb8,0x7b,0xb6,0x85,0x4b,0x78, | |||
| 0x3c,0x60,0xe8,0x03,0x11,0xae,0x30,0x79 | |||
| }; | |||
| template<> const uint8_t Tests<IsoEd25519>::rfc7748_1000[32] = { | |||
| 0x68,0x4c,0xf5,0x9b,0xa8,0x33,0x09,0x55, | |||
| 0x28,0x00,0xef,0x56,0x6f,0x2f,0x4d,0x3c, | |||
| 0x1c,0x38,0x87,0xc4,0x93,0x60,0xe3,0x87, | |||
| 0x5f,0x2e,0xb9,0x4d,0x99,0x53,0x2c,0x51 | |||
| }; | |||
| template<> const uint8_t Tests<IsoEd25519>::rfc7748_1000000[32] = { | |||
| 0x7c,0x39,0x11,0xe0,0xab,0x25,0x86,0xfd, | |||
| 0x86,0x44,0x97,0x29,0x7e,0x57,0x5e,0x6f, | |||
| 0x3b,0xc6,0x01,0xc0,0x88,0x3c,0x30,0xdf, | |||
| 0x5f,0x4d,0xd2,0xd2,0x4f,0x66,0x54,0x24 | |||
| }; | |||
| template<> const uint8_t Tests<Ed448Goldilocks>::rfc7748_1[56] = { | |||
| 0x3f,0x48,0x2c,0x8a,0x9f,0x19,0xb0,0x1e, | |||
| 0x6c,0x46,0xee,0x97,0x11,0xd9,0xdc,0x14, | |||
| 0xfd,0x4b,0xf6,0x7a,0xf3,0x07,0x65,0xc2, | |||
| 0xae,0x2b,0x84,0x6a,0x4d,0x23,0xa8,0xcd, | |||
| 0x0d,0xb8,0x97,0x08,0x62,0x39,0x49,0x2c, | |||
| 0xaf,0x35,0x0b,0x51,0xf8,0x33,0x86,0x8b, | |||
| 0x9b,0xc2,0xb3,0xbc,0xa9,0xcf,0x41,0x13 | |||
| }; | |||
| template<> const uint8_t Tests<Ed448Goldilocks>::rfc7748_1000[56] = { | |||
| 0xaa,0x3b,0x47,0x49,0xd5,0x5b,0x9d,0xaf, | |||
| 0x1e,0x5b,0x00,0x28,0x88,0x26,0xc4,0x67, | |||
| 0x27,0x4c,0xe3,0xeb,0xbd,0xd5,0xc1,0x7b, | |||
| 0x97,0x5e,0x09,0xd4,0xaf,0x6c,0x67,0xcf, | |||
| 0x10,0xd0,0x87,0x20,0x2d,0xb8,0x82,0x86, | |||
| 0xe2,0xb7,0x9f,0xce,0xea,0x3e,0xc3,0x53, | |||
| 0xef,0x54,0xfa,0xa2,0x6e,0x21,0x9f,0x38 | |||
| }; | |||
| template<> const uint8_t Tests<Ed448Goldilocks>::rfc7748_1000000[56] = { | |||
| 0x07,0x7f,0x45,0x36,0x81,0xca,0xca,0x36, | |||
| 0x93,0x19,0x84,0x20,0xbb,0xe5,0x15,0xca, | |||
| 0xe0,0x00,0x24,0x72,0x51,0x9b,0x3e,0x67, | |||
| 0x66,0x1a,0x7e,0x89,0xca,0xb9,0x46,0x95, | |||
| 0xc8,0xf4,0xbc,0xd6,0x6e,0x61,0xb9,0xb9, | |||
| 0xc9,0x46,0xda,0x8d,0x52,0x4d,0xe3,0xd6, | |||
| 0x9b,0xd9,0xd9,0xd6,0x6b,0x99,0x7e,0x37 | |||
| }; | |||
| int main(int argc, char **argv) { | |||
| (void) argc; (void) argv; | |||
| @@ -356,6 +466,8 @@ int main(int argc, char **argv) { | |||
| Tests<IsoEd25519>::test_arithmetic(); | |||
| Tests<IsoEd25519>::test_elligator(); | |||
| Tests<IsoEd25519>::test_ec(); | |||
| Tests<IsoEd25519>::test_cfrg_crypto(); | |||
| Tests<IsoEd25519>::test_cfrg_vectors(); | |||
| Tests<IsoEd25519>::test_crypto(); | |||
| printf("\n"); | |||
| @@ -363,6 +475,8 @@ int main(int argc, char **argv) { | |||
| Tests<Ed448Goldilocks>::test_arithmetic(); | |||
| Tests<Ed448Goldilocks>::test_elligator(); | |||
| Tests<Ed448Goldilocks>::test_ec(); | |||
| Tests<Ed448Goldilocks>::test_cfrg_crypto(); | |||
| Tests<Ed448Goldilocks>::test_cfrg_vectors(); | |||
| Tests<Ed448Goldilocks>::test_crypto(); | |||
| if (passing) printf("Passed all tests.\n"); | |||