| @@ -1580,6 +1580,122 @@ decaf_error_t API_NS(direct_scalarmul) ( | |||||
| return succ; | 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 | * @cond internal | ||||
| * Control for variable-time scalar multiply algorithms. | * 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 decaf_word_t API_NS(MONTGOMERY_FACTOR) = 0; | ||||
| const API_NS(point_t) API_NS(point_base); | const API_NS(point_t) API_NS(point_base); | ||||
| const uint8_t API_NS(x_base_point)[X_PUBLIC_BYTES] = {0}; | |||||
| struct niels_s; | struct niels_s; | ||||
| const gf_s *API_NS(precomputed_wnaf_as_fe); | const gf_s *API_NS(precomputed_wnaf_as_fe); | ||||
| @@ -170,6 +171,19 @@ int main(int argc, char **argv) { | |||||
| w *= w*plo + 2; | w *= w*plo + 2; | ||||
| } | } | ||||
| printf("const decaf_word_t API_NS(MONTGOMERY_FACTOR) = (decaf_word_t)0x%016llxull;\n\n", w); | 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; | return 0; | ||||
| } | } | ||||
| @@ -49,6 +49,15 @@ def ceil_log2(x): | |||||
| for field,data in field_data.iteritems(): | for field,data in field_data.iteritems(): | ||||
| if "gf_bits" not in data: | if "gf_bits" not in data: | ||||
| data["gf_bits"] = ceil_log2(data["modulus"]) | 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 curve,data in curve_data.iteritems(): | ||||
| for key in field_data[data["field"]]: | for key in field_data[data["field"]]: | ||||
| @@ -66,7 +75,7 @@ for curve,data in curve_data.iteritems(): | |||||
| data["bits"] = ceil_log2(data["modulus"]) | data["bits"] = ceil_log2(data["modulus"]) | ||||
| if "ser_bytes" not in data: | 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: | if "scalar_ser_bytes" not in data: | ||||
| data["scalar_ser_bytes"] = (data["scalar_bits"]-1)//8 + 1 | 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. */ | /** Number of bytes in a serialized scalar. */ | ||||
| #define %(C_NS)s_SCALAR_BYTES %(scalar_ser_bytes)d | #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 */ | /** Twisted Edwards extended homogeneous coordinates */ | ||||
| typedef struct %(c_ns)s_point_s { | typedef struct %(c_ns)s_point_s { | ||||
| /** @cond internal */ | /** @cond internal */ | ||||
| @@ -346,6 +352,39 @@ decaf_error_t %(c_ns)s_direct_scalarmul ( | |||||
| decaf_bool_t short_circuit | decaf_bool_t short_circuit | ||||
| ) API_VIS NONNULL3 WARN_UNUSED NOINLINE; | ) 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. | * @brief Precompute a table for fast scalar multiplication. | ||||
| * Some implementations do not include precomputed points; for | * Some implementations do not include precomputed points; for | ||||
| @@ -136,7 +136,7 @@ public: | |||||
| * Decode from correct-length little-endian byte sequence. | * Decode from correct-length little-endian byte sequence. | ||||
| * @return DECAF_FAILURE if the scalar is greater than or equal to the group order q. | * @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 | Scalar &sc, const FixedBlock<SER_BYTES> buffer | ||||
| ) NOEXCEPT { | ) NOEXCEPT { | ||||
| return %(c_ns)s_scalar_decode(sc.s,buffer.data()); | 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 | /** Invert with Fermat's Little Theorem (slow!). If *this == 0, set r=0 | ||||
| * and return DECAF_FAILURE. */ | * and return DECAF_FAILURE. */ | ||||
| inline decaf_error_t __attribute__((warn_unused_result)) | |||||
| inline decaf_error_t WARN_UNUSED | |||||
| inverse_noexcept(Scalar &r) const NOEXCEPT { | inverse_noexcept(Scalar &r) const NOEXCEPT { | ||||
| return %(c_ns)s_scalar_invert(r.s,s); | 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, | * @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 __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 | Point &p, const FixedBlock<SER_BYTES> &buffer, decaf_bool_t allow_identity=DECAF_TRUE | ||||
| ) NOEXCEPT { | ) NOEXCEPT { | ||||
| return %(c_ns)s_point_decode(p.p,buffer.data(),allow_identity); | return %(c_ns)s_point_decode(p.p,buffer.data(),allow_identity); | ||||
| @@ -565,6 +565,64 @@ public: | |||||
| /** @endcond */ | /** @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 */ | }; /* struct %(cxx_ns)s */ | ||||
| /** @cond internal */ | /** @cond internal */ | ||||
| @@ -14,7 +14,7 @@ f_field_h = gen_file( | |||||
| #define __DECAF_%(gf_shortname)s_GF_DEFINED__ 1 | #define __DECAF_%(gf_shortname)s_GF_DEFINED__ 1 | ||||
| #define NLIMBS (%(gf_impl_bits)d/sizeof(word_t)/8) | #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 { | typedef struct gf_%(gf_shortname)s_s { | ||||
| word_t limb[NLIMBS]; | word_t limb[NLIMBS]; | ||||
| } __attribute__((aligned(32))) gf_%(gf_shortname)s_s, gf_%(gf_shortname)s_t[1]; | } __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_serialize gf_%(gf_shortname)s_serialize | ||||
| #define gf_deserialize gf_%(gf_shortname)s_deserialize | #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 SQRT_MINUS_ONE P%(gf_shortname)s_SQRT_MINUS_ONE /* might not be defined */ | ||||
| #define INLINE_UNUSED __inline__ __attribute__((unused,always_inline)) | #define INLINE_UNUSED __inline__ __attribute__((unused,always_inline)) | ||||
| @@ -41,6 +41,12 @@ private: | |||||
| keccak_prng_t sp; | keccak_prng_t sp; | ||||
| public: | 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) */ | /** Exception thrown when The RNG fails (to seed itself) */ | ||||
| class RngException : public std::exception { | class RngException : public std::exception { | ||||
| private: | private: | ||||
| @@ -54,14 +60,14 @@ public: | |||||
| }; | }; | ||||
| /** Initialize, deterministically by default, from block */ | /** 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 */ | /** 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) { | 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)) { | if (!decaf_successful(ret)) { | ||||
| throw RngException(errno, "Couldn't load from file"); | throw RngException(errno, "Couldn't load from file"); | ||||
| } | } | ||||
| @@ -288,10 +288,21 @@ static void spake2ee( | |||||
| server.respec(STROBE_KEYED_128); | 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() { | static void macro() { | ||||
| printf("\nMacro-benchmarks for %s:\n", Group::name()); | 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); | PrivateKey<Group> s1((NOINIT())), s2(rng); | ||||
| PublicKey<Group> p1((NOINIT())), p2(s2); | PublicKey<Group> p1((NOINIT())), p2(s2); | ||||
| @@ -317,8 +328,8 @@ static void macro() { | |||||
| } | } | ||||
| printf("\nProtocol benchmarks:\n"); | 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")); | SecureBuffer hashedPassword(Block("hello world")); | ||||
| for (Benchmark b("Spake2ee c+s",0.1); b.iter(); ) { | for (Benchmark b("Spake2ee c+s",0.1); b.iter(); ) { | ||||
| spake2ee(clientRng, serverRng, hashedPassword,false); | spake2ee(clientRng, serverRng, hashedPassword,false); | ||||
| @@ -343,7 +354,7 @@ static void macro() { | |||||
| } | } | ||||
| static void micro() { | static void micro() { | ||||
| SpongeRng rng(Block("per-curve-benchmarks")); | |||||
| SpongeRng rng(Block("per-curve-benchmarks"),SpongeRng::DETERMINISTIC); | |||||
| Precomputed pBase; | Precomputed pBase; | ||||
| Point p,q; | Point p,q; | ||||
| Scalar s(1),t(2); | Scalar s(1),t(2); | ||||
| @@ -382,7 +393,7 @@ int main(int argc, char **argv) { | |||||
| if (argc >= 2 && !strcmp(argv[1], "--micro")) | if (argc >= 2 && !strcmp(argv[1], "--micro")) | ||||
| micro = true; | micro = true; | ||||
| SpongeRng rng(Block("micro-benchmarks")); | |||||
| SpongeRng rng(Block("micro-benchmarks"),SpongeRng::DETERMINISTIC); | |||||
| if (micro) { | if (micro) { | ||||
| printf("\nMicro-benchmarks:\n"); | printf("\nMicro-benchmarks:\n"); | ||||
| SHAKE<128> shake1; | SHAKE<128> shake1; | ||||
| @@ -415,7 +426,6 @@ int main(int argc, char **argv) { | |||||
| Benches<Ed448Goldilocks>::micro(); | Benches<Ed448Goldilocks>::micro(); | ||||
| } | } | ||||
| Benches<IsoEd25519>::macro(); | Benches<IsoEd25519>::macro(); | ||||
| Benches<Ed448Goldilocks>::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 */ | /* Specify the same value as you did when compiling decaf_crypto.c */ | ||||
| #ifndef DECAF_CRYPTO_SHARED_SECRET_SHORT_CIRUIT | #ifndef DECAF_CRYPTO_SHARED_SECRET_SHORT_CIRUIT | ||||
| #define DECAF_CRYPTO_SHARED_SECRET_SHORT_CIRUIT DECAF_FALSE | #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::Scalar Scalar; | ||||
| typedef typename Group::Point Point; | typedef typename Group::Point Point; | ||||
| typedef typename Group::DhLadder DhLadder; | |||||
| typedef typename Group::Precomputed Precomputed; | typedef typename Group::Precomputed Precomputed; | ||||
| static void print(const char *name, const Scalar &x) { | static void print(const char *name, const Scalar &x) { | ||||
| @@ -128,7 +129,7 @@ static bool point_check( | |||||
| } | } | ||||
| static void test_arithmetic() { | static void test_arithmetic() { | ||||
| SpongeRng rng(Block("test_arithmetic")); | |||||
| SpongeRng rng(Block("test_arithmetic"),SpongeRng::DETERMINISTIC); | |||||
| Test test("Arithmetic"); | Test test("Arithmetic"); | ||||
| Scalar x(0),y(0),z(0); | Scalar x(0),y(0),z(0); | ||||
| @@ -169,7 +170,7 @@ static void test_arithmetic() { | |||||
| } | } | ||||
| static void test_elligator() { | static void test_elligator() { | ||||
| SpongeRng rng(Block("test_elligator")); | |||||
| SpongeRng rng(Block("test_elligator"),SpongeRng::DETERMINISTIC); | |||||
| Test test("Elligator"); | Test test("Elligator"); | ||||
| const int NHINTS = Group::REMOVED_COFACTOR * 2; | const int NHINTS = Group::REMOVED_COFACTOR * 2; | ||||
| @@ -265,7 +266,7 @@ static void test_elligator() { | |||||
| } | } | ||||
| static void test_ec() { | static void test_ec() { | ||||
| SpongeRng rng(Block("test_ec")); | |||||
| SpongeRng rng(Block("test_ec"),SpongeRng::DETERMINISTIC); | |||||
| Test test("EC"); | Test test("EC"); | ||||
| @@ -327,7 +328,7 @@ static void test_ec() { | |||||
| static void test_crypto() { | static void test_crypto() { | ||||
| Test test("Sample 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++) { | for (int i=0; i<NTESTS && test.passing_now; i++) { | ||||
| PrivateKey<Group> priv1(rng), priv2(rng); | PrivateKey<Group> priv1(rng), priv2(rng); | ||||
| @@ -340,14 +341,123 @@ static void test_crypto() { | |||||
| SecureBuffer s1(priv1.sharedSecret(pub2,32,true)); | SecureBuffer s1(priv1.sharedSecret(pub2,32,true)); | ||||
| SecureBuffer s2(priv2.sharedSecret(pub1,32,false)); | SecureBuffer s2(priv2.sharedSecret(pub1,32,false)); | ||||
| if (memcmp(s1.data(),s2.data(),s1.size())) { | |||||
| if (!memeq(s1,s2)) { | |||||
| test.fail(); | 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) { | int main(int argc, char **argv) { | ||||
| (void) argc; (void) argv; | (void) argc; (void) argv; | ||||
| @@ -356,6 +466,8 @@ int main(int argc, char **argv) { | |||||
| Tests<IsoEd25519>::test_arithmetic(); | Tests<IsoEd25519>::test_arithmetic(); | ||||
| Tests<IsoEd25519>::test_elligator(); | Tests<IsoEd25519>::test_elligator(); | ||||
| Tests<IsoEd25519>::test_ec(); | Tests<IsoEd25519>::test_ec(); | ||||
| Tests<IsoEd25519>::test_cfrg_crypto(); | |||||
| Tests<IsoEd25519>::test_cfrg_vectors(); | |||||
| Tests<IsoEd25519>::test_crypto(); | Tests<IsoEd25519>::test_crypto(); | ||||
| printf("\n"); | printf("\n"); | ||||
| @@ -363,6 +475,8 @@ int main(int argc, char **argv) { | |||||
| Tests<Ed448Goldilocks>::test_arithmetic(); | Tests<Ed448Goldilocks>::test_arithmetic(); | ||||
| Tests<Ed448Goldilocks>::test_elligator(); | Tests<Ed448Goldilocks>::test_elligator(); | ||||
| Tests<Ed448Goldilocks>::test_ec(); | Tests<Ed448Goldilocks>::test_ec(); | ||||
| Tests<Ed448Goldilocks>::test_cfrg_crypto(); | |||||
| Tests<Ed448Goldilocks>::test_cfrg_vectors(); | |||||
| Tests<Ed448Goldilocks>::test_crypto(); | Tests<Ed448Goldilocks>::test_crypto(); | ||||
| if (passing) printf("Passed all tests.\n"); | if (passing) printf("Passed all tests.\n"); | ||||