From 650356c5f510fccde21c503d21a2e367dd95c191 Mon Sep 17 00:00:00 2001 From: Michael Hamburg Date: Sun, 7 Feb 2016 19:52:36 -0800 Subject: [PATCH] elligator overflow bits. Before, invert_elligator would invert to a gf, which wouldnt be a uniformly random string because, eg, curve25519 gfs only have 255 bits out of 256. Now add a random multiple of p. This still wont work for future curves that have a field size of 1 mod 8, because those curves use elligator with no high bit set, but its a start --- src/gen_headers/curve_data.py | 6 ++-- src/per_curve/decaf.tmpl.h | 20 +++++++---- src/per_curve/decaf.tmpl.hxx | 26 +++++++++++--- src/per_curve/decaf_gen_tables.tmpl.c | 2 +- src/per_curve/elligator.tmpl.c | 52 ++++++++++++++++++++++----- test/test_decaf.cxx | 9 ++--- 6 files changed, 86 insertions(+), 29 deletions(-) diff --git a/src/gen_headers/curve_data.py b/src/gen_headers/curve_data.py index 73a7bc4..0aaea64 100644 --- a/src/gen_headers/curve_data.py +++ b/src/gen_headers/curve_data.py @@ -8,13 +8,15 @@ field_data = { "gf_desc" : "2^255 - 19", "gf_shortname" : "25519", "gf_impl_bits" : 320, - "gf_lit_limb_bits" : 51 + "gf_lit_limb_bits" : 51, + "elligator_onto" : 0 }, "p448" : { "gf_desc" : "2^448 - 2^224 - 1", "gf_shortname" : "448", "gf_impl_bits" : 512, - "gf_lit_limb_bits" : 56 + "gf_lit_limb_bits" : 56, + "elligator_onto" : 0 } } diff --git a/src/per_curve/decaf.tmpl.h b/src/per_curve/decaf.tmpl.h index 5106b69..48f6329 100644 --- a/src/per_curve/decaf.tmpl.h +++ b/src/per_curve/decaf.tmpl.h @@ -26,9 +26,17 @@ typedef struct gf_$(gf_shortname)_s { /** Number of bytes in a serialized point. */ #define $(C_NS)_SER_BYTES $((gf_bits-2)/8 + 1) +/** Number of bytes in an elligated point. For now set the same as SER_BYTES + * but could be different for other curves. + */ +#define $(C_NS)_HASH_BYTES $((gf_bits-2)/8 + 1) + /** Number of bytes in a serialized scalar. */ #define $(C_NS)_SCALAR_BYTES $((scalar_bits-1)/8 + 1) +/** Number of bits in the "which" field of an elligator inverse */ +#define $(C_NS)_INVERT_ELLIGATOR_WHICH_BITS $(ceil_log2(cofactor) + 7 + elligator_onto - ((gf_bits-2) % 8)) + /** Number of bytes in an x$(gf_shortname) public key */ #define X$(gf_shortname)_PUBLIC_BYTES $((gf_bits-1)/8 + 1) @@ -594,7 +602,7 @@ void $(c_ns)_point_debugging_pscale ( void $(c_ns)_point_from_hash_nonuniform ( $(c_ns)_point_t pt, - const unsigned char hashed_data[$(C_NS)_SER_BYTES] + const unsigned char hashed_data[$(C_NS)_HASH_BYTES] ) API_VIS NONNULL NOINLINE; /** @@ -607,7 +615,7 @@ $(c_ns)_point_from_hash_nonuniform ( */ void $(c_ns)_point_from_hash_uniform ( $(c_ns)_point_t pt, - const unsigned char hashed_data[2*$(C_NS)_SER_BYTES] + const unsigned char hashed_data[2*$(C_NS)_HASH_BYTES] ) API_VIS NONNULL NOINLINE; /** @@ -630,9 +638,9 @@ void $(c_ns)_point_from_hash_uniform ( */ decaf_error_t $(c_ns)_invert_elligator_nonuniform ( - unsigned char recovered_hash[$(C_NS)_SER_BYTES], + unsigned char recovered_hash[$(C_NS)_HASH_BYTES], const $(c_ns)_point_t pt, - uint16_t which + uint32_t which ) API_VIS NONNULL NOINLINE WARN_UNUSED; /** @@ -655,9 +663,9 @@ $(c_ns)_invert_elligator_nonuniform ( */ decaf_error_t $(c_ns)_invert_elligator_uniform ( - unsigned char recovered_hash[2*$(C_NS)_SER_BYTES], + unsigned char recovered_hash[2*$(C_NS)_HASH_BYTES], const $(c_ns)_point_t pt, - uint16_t which + uint32_t which ) API_VIS NONNULL NOINLINE WARN_UNUSED; /** diff --git a/src/per_curve/decaf.tmpl.hxx b/src/per_curve/decaf.tmpl.hxx index d0aa0e3..176fb7b 100644 --- a/src/per_curve/decaf.tmpl.hxx +++ b/src/per_curve/decaf.tmpl.hxx @@ -228,10 +228,24 @@ public: static const size_t SER_BYTES = $(C_NS)_SER_BYTES; /** Bytes required for hash */ - static const size_t HASH_BYTES = SER_BYTES; + static const size_t HASH_BYTES = $(C_NS)_HASH_BYTES; - /** Size of a stegged element */ + /** + * Size of a stegged element. + * + * FUTURE: You can use HASH_BYTES * 3/2 (or more likely much less, eg HASH_BYTES + 8) + * with a random oracle hash function, by hash-expanding everything past the first + * HASH_BYTES of the element. However, since the internal C invert_elligator is not + * tied to a hash function, I didn't want to tie the C++ wrapper to a hash function + * either. But it might be a good idea to do this in the future, either with STROBE + * or something else. + * + * Then again, calling invert_elligator at all is super niche, so maybe who cares? + */ static const size_t STEG_BYTES = HASH_BYTES * 2; + + /** Number of bits in invert_elligator which are actually used. */ + static const unsigned int INVERT_ELLIGATOR_WHICH_BITS = $(C_NS)_INVERT_ELLIGATOR_WHICH_BITS; /** The c-level object. */ Wrapped p; @@ -441,7 +455,7 @@ public: * or leave buf unmodified and return DECAF_FAILURE. */ inline decaf_error_t invert_elligator ( - Buffer buf, uint16_t hint + Buffer buf, uint32_t hint ) const NOEXCEPT { unsigned char buf2[2*HASH_BYTES]; memset(buf2,0,sizeof(buf2)); @@ -468,8 +482,10 @@ public: SecureBuffer out(STEG_BYTES); decaf_error_t done; do { - rng.read(Buffer(out).slice(HASH_BYTES-1,STEG_BYTES-HASH_BYTES+1)); - done = invert_elligator(out, out[HASH_BYTES-1]); + rng.read(Buffer(out).slice(HASH_BYTES-4,STEG_BYTES-HASH_BYTES+1)); + uint32_t hint = 0; + for (int i=0; i<4; i++) { hint |= uint32_t(out[HASH_BYTES-4+i])<<(8*i); } + done = invert_elligator(out, hint); } while (!decaf_successful(done)); return out; } diff --git a/src/per_curve/decaf_gen_tables.tmpl.c b/src/per_curve/decaf_gen_tables.tmpl.c index 46a031c..4817782 100644 --- a/src/per_curve/decaf_gen_tables.tmpl.c +++ b/src/per_curve/decaf_gen_tables.tmpl.c @@ -25,7 +25,7 @@ void API_NS(precompute_wnafs) ( struct niels_s *out, const API_NS(point_t) base ); -static void field_print(const gf f) { /* UNIFY */ +static void field_print(const gf f) { unsigned char ser[X_SER_BYTES]; gf_serialize(ser,f,1); int b=0, i, comma=0; diff --git a/src/per_curve/elligator.tmpl.c b/src/per_curve/elligator.tmpl.c index ee30c3a..1642c87 100644 --- a/src/per_curve/elligator.tmpl.c +++ b/src/per_curve/elligator.tmpl.c @@ -89,20 +89,49 @@ void API_NS(point_from_hash_uniform) ( API_NS(point_add)(pt,pt,pt2); } +/* Elligator_onto: + * Make elligator-inverse onto at the cost of roughly halving the success probability. + * Currently no effect for curves with field size 1 bit mod 8 (where the top bit + * is chopped off). FUTURE MAGIC: automatic at least for brainpool-style curves; support + * log p == 1 mod 8 brainpool curves maybe? + */ +#define MAX(A,B) (((A)>(B)) ? (A) : (B)) +#define PKP_MASK ((1<<(MAX(8*SER_BYTES + $(elligator_onto) - $(gf_bits),0)))-1) +#if PKP_MASK != 0 +static UNUSED mask_t plus_k_p ( + uint8_t x[SER_BYTES], + uint32_t factor_ +) { + uint32_t carry = 0; + uint64_t factor = factor_; + const uint8_t p[SER_BYTES] = { $(ser(modulus,8)) }; + for (unsigned int i=0; i>8; + } + return word_is_zero(carry); +} +#endif + decaf_error_t API_NS(invert_elligator_nonuniform) ( unsigned char recovered_hash[SER_BYTES], const point_t p, - uint16_t hint_ + uint32_t hint_ ) { mask_t hint = hint_; mask_t sgn_s = -(hint & 1), sgn_t_over_s = -(hint>>1 & 1), - sgn_r0 = -(hint>>2 & 1), /* FIXME: but it's SER_BYTES ... */ + sgn_r0 = -(hint>>2 & 1), sgn_ed_T = -(hint>>3 & 1); gf a, b, c, d; API_NS(deisogenize)(a,c,p,sgn_s,sgn_t_over_s,sgn_ed_T); +#if $(gf_bits) == 8*SER_BYTES + 1 /* p521. */ + sgn_r0 = 0; +#endif + /* ok, a = s; c = -t/s */ gf_mul(b,c,a); gf_sub(b,ONE,b); /* t+1 */ @@ -127,12 +156,19 @@ API_NS(invert_elligator_nonuniform) ( gf_cond_neg(b, sgn_r0^gf_hibit(b)); succ &= ~(gf_eq(b,ZERO) & sgn_r0); -#if COFACTOR == 8 - succ &= ~(is_identity & sgn_ed_T); /* NB: there are no preimages of rotated identity. */ -#endif + #if COFACTOR == 8 + succ &= ~(is_identity & sgn_ed_T); /* NB: there are no preimages of rotated identity. */ + #endif - gf_serialize(recovered_hash,b,1); /* FIXME: ,0 */ - /* TODO: deal with overflow flag */ + #if $(gf_bits) == 8*SER_BYTES + 1 /* p521 */ + gf_serialize(recovered_hash,b,0); + #else + gf_serialize(recovered_hash,b,1); + #if PKP_MASK != 0 + /* Add a multiple of p to make the result either almost-onto or completely onto. */ + succ &= plus_k_p(recovered_hash, (hint >> ((COFACTOR==8)?4:3)) & PKP_MASK); + #endif + #endif return decaf_succeed_if(mask_to_bool(succ)); } @@ -140,7 +176,7 @@ decaf_error_t API_NS(invert_elligator_uniform) ( unsigned char partial_hash[2*SER_BYTES], const point_t p, - uint16_t hint + uint32_t hint ) { point_t pt2; API_NS(point_from_hash_nonuniform)(pt2,&partial_hash[SER_BYTES]); diff --git a/test/test_decaf.cxx b/test/test_decaf.cxx index 96bf2b4..5ccaa76 100644 --- a/test/test_decaf.cxx +++ b/test/test_decaf.cxx @@ -201,17 +201,16 @@ static void test_elligator() { SpongeRng rng(Block("test_elligator"),SpongeRng::DETERMINISTIC); Test test("Elligator"); - const int NHINTS = Group::REMOVED_COFACTOR * 2; + const int NHINTS = 1<= Point::HASH_BYTES) b1[Point::HASH_BYTES-1] &= 0x7F; // FIXME MAGIC /* Pathological cases */ if (i==1) b1[0] = 1; @@ -293,10 +292,6 @@ static void test_elligator() { Point t(rng); point_check(test,t,t,t,0,0,t,Point::from_hash(t.steg_encode(rng)),"steg round-trip"); - - - - } }