diff --git a/include/decaf.h b/include/decaf.h index 23353bf..1d803a6 100644 --- a/include/decaf.h +++ b/include/decaf.h @@ -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 diff --git a/src/decaf.c b/src/decaf.c index b4ca128..a29d56c 100644 --- a/src/decaf.c +++ b/src/decaf.c @@ -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; +} diff --git a/test/test_pointops.c b/test/test_pointops.c index caff3c1..8f302a2 100644 --- a/test/test_pointops.c +++ b/test/test_pointops.c @@ -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;