@@ -54,6 +54,7 @@ extern "C" { | |||
/* Goldilocks' build flags default to hidden and stripping executables. */ | |||
#define API_VIS __attribute__((visibility("default"))) | |||
#define WARN_UNUSED __attribute__((warn_unused_result)) | |||
#define NONNULL1 __attribute__((nonnull(1))) | |||
#define NONNULL2 __attribute__((nonnull(1,2))) | |||
#define NONNULL3 __attribute__((nonnull(1,2,3))) | |||
@@ -158,6 +159,31 @@ void decaf_scalarmul ( | |||
unsigned int scalar_words | |||
) API_VIS NONNULL3; | |||
/** | |||
* @brief Test that a point is valid, for debugging purposes. | |||
* | |||
* @param [in] point The number to test. | |||
* @retval DECAF_TRUE The point is valid. | |||
* @retval DECAF_FALSE The point is invalid. | |||
*/ | |||
decaf_bool_t decaf_valid ( | |||
const decaf_point_t toTest | |||
) API_VIS WARN_UNUSED NONNULL1; | |||
/** | |||
* @brief Elligator-like hash to curve. | |||
* | |||
* May be up to 4:1 on [0,(p-1)/2] | |||
* // TODO: check that it isn't more. | |||
* | |||
* @param [in] ser A serialized point. | |||
* @param [out] pt The hashed input | |||
*/ | |||
void decaf_nonuniform_map_to_curve ( | |||
decaf_point_t pt, | |||
const unsigned char ser[DECAF_SER_BYTES] | |||
) API_VIS NONNULL2; | |||
#undef API_VIS | |||
#undef WARN_UNUSED | |||
#undef NONNULL2 | |||
@@ -371,3 +371,91 @@ decaf_bool_t decaf_eq ( const decaf_point_t p, const decaf_point_t q ) { | |||
gf_mul ( b, q->y, p->x ); | |||
return gf_eq(a,b); | |||
} | |||
static const int QUADRATIC_NONRESIDUE = -1; | |||
void decaf_nonuniform_map_to_curve ( | |||
decaf_point_t p, | |||
const unsigned char ser[DECAF_SER_BYTES] | |||
) { | |||
/* | |||
sage: XXD = (u*r^2 + 1) * (d - u*r^2) * (1 - u*d*r^2) / (d+1) | |||
sage: factor(XX / (1/XXD)) | |||
(u*r^2 - d)^2 | |||
sage: factor((ey-1)/(ey+1)/(1/d * 1/XXD)) | |||
(u*d*r^2 - 1)^2 | |||
sage: factor(XX2 / (u*r^2/XXD)) | |||
(u*d*r^2 - 1)^2 | |||
sage: factor((ey2-1)/(ey2+1)/(1/d * u*r^2/XXD)) | |||
(u*r^2 - d)^2 | |||
*/ | |||
gf r,urr,a,b,c,dee,e,ur2_d,udr2_1; | |||
(void)gf_deser(r,ser); | |||
gf_canon(r); // just in case | |||
gf_sqr(a,r); | |||
gf_mlw(urr,a,QUADRATIC_NONRESIDUE); // urr = u*r^2 | |||
gf_mlw(dee,ONE,EDWARDS_D); | |||
gf_add(a,urr,ONE); | |||
gf_sub(ur2_d,dee,urr); // ur2_d = -(ur^2-d) | |||
gf_mul(c,a,ur2_d); | |||
gf_mlw(b,urr,-EDWARDS_D); | |||
gf_add(udr2_1,b,ONE); // udr2_1 = -(udr^2-1) | |||
gf_mul(a,c,udr2_1); | |||
gf_mlw(c,a,EDWARDS_D+1); // c = (u*r^2 + 1) * (d - u*r^2) * (1 - u*d*r^2) * (d+1) | |||
gf_isqrt(b,c); // FIELD: if 5 mod 8, multiply result by u. | |||
gf_sqr(a,b); | |||
gf_mul(e,a,c); | |||
mask_t square = gf_eq(e,ONE); | |||
gf_mul(a,b,r); | |||
cond_sel(b,a,b,square); | |||
cond_neg(b,hibit(b)); | |||
gf_mlw(a,b,EDWARDS_D+1); | |||
/* Here: a = sqrt( (d+1) / (ur^2?) * (u*r^2 + 1) * (d - u*r^2) * (1 - u*d*r^2)) */ | |||
cond_swap(ur2_d,udr2_1,~square); | |||
gf_mul(e,ur2_d,a); | |||
gf_mul(b,udr2_1,a); | |||
gf_sqr(c,b); | |||
/* Here: | |||
* ed_x = 2e/(1-e^2) | |||
* c = * (ed_y-1)/(ed_y+1) | |||
* | |||
* Special cases: | |||
* e^2 = 1: impossible for cofactor-4 curves (would isogenize to order-4 point) | |||
* e = 0 <-> also c = 0: maps to (0,1), which is fine. | |||
*/ | |||
gf_sqr(a,e); | |||
gf_sub(a,ONE,a); | |||
gf_add(e,e,e); | |||
gf_add(b,dee,c); | |||
gf_sub(c,dee,c); | |||
gf_mul(p->x,e,c); | |||
gf_mul(p->z,a,c); | |||
gf_mul(p->y,b,a); | |||
gf_mul(p->t,b,e); | |||
} | |||
decaf_bool_t decaf_valid ( | |||
const decaf_point_t p | |||
) { | |||
gf a,b,c; | |||
gf_mul(a,p->x,p->y); | |||
gf_mul(b,p->z,p->t); | |||
mask_t out = gf_eq(a,b); | |||
gf_sqr(a,p->x); | |||
gf_sqr(b,p->y); | |||
gf_sub(a,b,a); | |||
gf_sqr(b,p->t); | |||
gf_mlw(c,b,1-EDWARDS_D); | |||
gf_sqr(b,p->z); | |||
gf_sub(b,b,c); | |||
out &= gf_eq(a,b); | |||
return out; | |||
} |
@@ -363,13 +363,19 @@ int test_decaf_evil (void) { | |||
mask_t succ_dec = decaf_decode(pt_dec2, ser_de, -1); | |||
field_serialize(ser_ed, out_ed); | |||
decaf_point_t p; | |||
decaf_nonuniform_map_to_curve (p,random_input); | |||
mask_t succ_nur = decaf_valid(p); | |||
if ((care_should && should != s_m) | |||
|| ~s_base || s_e != s_te || s_m != s_te || s_ed != s_te | |||
|| (s_te && ~field_eq(out_e,out_m)) | |||
|| (s_ed && ~field_eq(out_e,out_ed)) | |||
|| memcmp(ser_de, ser_ed, 56) | |||
|| (s_e & ~succ_dec) | |||
|| (s_e & ~decaf_eq(pt_dec, pt_dec2)) | |||
|| (s_e & ~decaf_eq(pt_dec, pt_dec2) | |||
|| (s_e & ~decaf_valid(pt_dec)) | |||
|| ~succ_nur) | |||
) { | |||
youfail(); | |||
field_print(" base", base); | |||
@@ -377,8 +383,9 @@ int test_decaf_evil (void) { | |||
field_print(" oute", out_e); | |||
field_print(" outE", out_ed); | |||
field_print(" outm", out_m); | |||
printf(" succ: m=%d, e=%d, t=%d, b=%d, T=%d, D=%d, should=%d[%d]\n", | |||
printf(" succ: m=%d, e=%d, t=%d, b=%d, T=%d, D=%d, nur=%d, should=%d[%d]\n", | |||
-(int)s_m,-(int)s_e,-(int)s_te,-(int)s_base,-(int)s_ed,-(int)succ_dec, | |||
-(int)succ_nur, | |||
-(int)should,-(int)care_should | |||
); | |||
ret = -1; | |||
@@ -538,6 +545,7 @@ int test_decaf (void) { | |||
printf(" Fail: only %d successes in decaf_deser\n", hits); | |||
return -1; | |||
} else if (fails) { | |||
printf(" %d fails\n", fails); | |||
return -1; | |||
} else { | |||
return 0; | |||