| @@ -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; | |||