Commit 650356c5 authored by Michael Hamburg's avatar Michael Hamburg

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
parent 2104923b
......@@ -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
}
}
......
......@@ -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;
/**
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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<SER_BYTES; i++) {
uint64_t tmp = carry + p[i] * factor + x[i];
/* tmp <= 2^32-1 + (2^32-1)*(2^8-1) + (2^8-1) = 2^40-1 */
x[i] = tmp; carry = tmp>>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]);
......
......@@ -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::INVERT_ELLIGATOR_WHICH_BITS;
SecureBuffer *alts[NHINTS];
bool successes[NHINTS];
SecureBuffer *alts2[NHINTS];
bool successes2[NHINTS];
for (int i=0; i<NTESTS/10 && test.passing_now; i++) {
for (int i=0; i<NTESTS/10 && (i<10 || test.passing_now); i++) {
size_t len = (i % (2*Point::HASH_BYTES + 3));
SecureBuffer b1(len);
if (i!=Point::HASH_BYTES) rng.read(b1); /* special test case */
if (len >= 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");
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment