@@ -22,6 +22,13 @@ | |||
#include <stdint.h> | |||
/* 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))) | |||
typedef uint64_t decaf_word_t, decaf_bool_t; | |||
/* TODO: prefix all these operations and factor to support multiple curves. */ | |||
@@ -34,6 +41,9 @@ typedef uint64_t decaf_word_t, decaf_bool_t; | |||
/** Number of bytes in a serialized point. One less bit than you'd think. */ | |||
#define DECAF_SER_BYTES ((DECAF_FIELD_BITS+6)/8) | |||
/** Number of bytes in a serialized scalar. Two less bits than you'd think. */ | |||
#define DECAF_SCALAR_BYTES ((DECAF_FIELD_BITS+5)/8) | |||
/** Twisted Edwards (-1,d-1) extended homogeneous coordinates */ | |||
typedef struct decaf_point_s { | |||
decaf_word_t x[DECAF_LIMBS],y[DECAF_LIMBS],z[DECAF_LIMBS],t[DECAF_LIMBS]; | |||
@@ -50,25 +60,58 @@ static const decaf_bool_t DECAF_SUCCESS = -(decaf_bool_t)1 /*DECAF_TRUE*/, | |||
DECAF_FAILURE = 0 /*DECAF_FALSE*/; | |||
/** The identity point on the curve. */ | |||
const decaf_point_t decaf_identity; | |||
const decaf_point_t decaf_identity API_VIS; | |||
/** The prime p, for debugging purposes. | |||
* FIXME: prevent this scalar from actually being used for non-debugging purposes? | |||
*/ | |||
const decaf_scalar_t decaf_scalar_p API_VIS; | |||
/** A scalar equal to 1. */ | |||
const decaf_scalar_t decaf_scalar_one API_VIS; | |||
/** A scalar equal to 0. */ | |||
const decaf_scalar_t decaf_scalar_zero API_VIS; | |||
/** An arbitrarily chosen base point on the curve. TODO: define */ | |||
const decaf_point_t decaf_basepoint; | |||
const decaf_point_t decaf_basepoint API_VIS; | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
/* 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))) | |||
// TODO: ser, deser, inv?. | |||
// FIXME: scalar math is untested, and therefore probably wrong. | |||
/** | |||
* @brief Read a scalar from wire format or from bytes. | |||
* | |||
* Return DECAF_SUCCESS if the scalar was in reduced form. This | |||
* function is not WARN_UNUSED because eg challenges in signatures | |||
* may need to be longer. | |||
* | |||
* TODO: create a decode long function. | |||
* | |||
* @param [in] ser Serialized form of a scalar. | |||
* @param [out] out Deserialized form. | |||
*/ | |||
decaf_bool_t decaf_decode_scalar( | |||
decaf_scalar_t s, | |||
const unsigned char ser[DECAF_SER_BYTES] | |||
) API_VIS NONNULL2; | |||
/** | |||
* @brief Serialize a scalar to wire format. | |||
* | |||
* @param [out] ser Serialized form of a scalar. | |||
* @param [in] s Deserialized scalar. | |||
*/ | |||
void decaf_encode_scalar( | |||
unsigned char ser[DECAF_SER_BYTES], | |||
const decaf_scalar_t s | |||
) API_VIS NONNULL2; | |||
/** | |||
* @brief Add two scalars. The scalars may use the same memory. | |||
* @param [in] a One scalar. | |||
@@ -81,6 +124,18 @@ void decaf_add_scalars ( | |||
const decaf_scalar_t b | |||
) API_VIS NONNULL3; | |||
/** | |||
* @brief Compare two scalars. | |||
* @param [in] a One scalar. | |||
* @param [in] b Another scalar. | |||
* @retval DECAF_TRUE The scalars are equal. | |||
* @retval DECAF_FALSE The scalars are not equal. | |||
*/ | |||
decaf_bool_t decaf_eq_scalars ( | |||
const decaf_scalar_t a, | |||
const decaf_scalar_t b | |||
) API_VIS WARN_UNUSED NONNULL2; | |||
/** | |||
* @brief Subtract two scalars. The scalars may use the same memory. | |||
* @param [in] a One scalar. | |||
@@ -215,7 +215,7 @@ sv decaf_subx( | |||
} | |||
} | |||
static const decaf_scalar_t DECAF_SCALAR_P = {{{ | |||
const decaf_scalar_t decaf_scalar_p = {{{ | |||
0x2378c292ab5844f3ull, | |||
0x216cc2728dc58f55ull, | |||
0xc44edb49aed63690ull, | |||
@@ -224,7 +224,9 @@ static const decaf_scalar_t DECAF_SCALAR_P = {{{ | |||
0xffffffffffffffffull, | |||
0x3fffffffffffffffull | |||
// TODO 32-bit clean | |||
}}}, DECAF_SCALAR_R2 = {{{ | |||
}}}, decaf_scalar_one = {{{1}}}, decaf_scalar_zero = {{{0}}}; | |||
static const decaf_scalar_t decaf_scalar_r2 = {{{ | |||
0xe3539257049b9b60ull, | |||
0x7af32c4bc1b195d9ull, | |||
0x0d66de2388ea1859ull, | |||
@@ -235,7 +237,7 @@ static const decaf_scalar_t DECAF_SCALAR_P = {{{ | |||
// TODO 32-bit clean | |||
}}}; | |||
static const decaf_word_t DECAF_MONTGOMERY_FACTOR = 0xfc42bbf0516e743b; | |||
static const decaf_word_t DECAF_MONTGOMERY_FACTOR = 0x3bd440fae918bc5ull; | |||
sv decaf_montmul ( | |||
decaf_scalar_t out, | |||
@@ -254,7 +256,7 @@ sv decaf_montmul ( | |||
decaf_dword_t chain = 0; | |||
for (j=0; j<DECAF_SCALAR_LIMBS; j++) { | |||
chain += (decaf_dword_t)mand*mier[j] + accum[j]; | |||
chain += ((decaf_dword_t)mand)*mier[j] + accum[j]; | |||
accum[j] = chain; | |||
chain >>= WBITS; | |||
} | |||
@@ -282,8 +284,8 @@ void decaf_mul_scalars ( | |||
const decaf_scalar_t a, | |||
const decaf_scalar_t b | |||
) { | |||
decaf_montmul(out,a,b,DECAF_SCALAR_P,DECAF_MONTGOMERY_FACTOR); | |||
decaf_montmul(out,out,DECAF_SCALAR_R2,DECAF_SCALAR_P,DECAF_MONTGOMERY_FACTOR); | |||
decaf_montmul(out,a,b,decaf_scalar_p,DECAF_MONTGOMERY_FACTOR); | |||
decaf_montmul(out,out,decaf_scalar_r2,decaf_scalar_p,DECAF_MONTGOMERY_FACTOR); | |||
} | |||
void decaf_sub_scalars ( | |||
@@ -291,7 +293,7 @@ void decaf_sub_scalars ( | |||
const decaf_scalar_t a, | |||
const decaf_scalar_t b | |||
) { | |||
decaf_subx(out, a->limb, b, DECAF_SCALAR_P, 0); | |||
decaf_subx(out, a->limb, b, decaf_scalar_p, 0); | |||
} | |||
void decaf_add_scalars ( | |||
@@ -306,7 +308,19 @@ void decaf_add_scalars ( | |||
out->limb[i] = chain; | |||
chain >>= WBITS; | |||
} | |||
decaf_subx(out, out->limb, b, DECAF_SCALAR_P, chain); | |||
decaf_subx(out, out->limb, decaf_scalar_p, decaf_scalar_p, chain); | |||
} | |||
decaf_bool_t decaf_eq_scalars ( | |||
const decaf_scalar_t a, | |||
const decaf_scalar_t b | |||
) { | |||
decaf_word_t diff = 0; | |||
unsigned int i; | |||
for (i=0; i<DECAF_SCALAR_LIMBS; i++) { | |||
diff |= a->limb[i] ^ b->limb[i]; | |||
} | |||
return (((decaf_dword_t)diff)-1)>>WBITS; | |||
} | |||
/* *** API begins here *** */ | |||
@@ -451,6 +465,41 @@ void decaf_copy ( | |||
gf_cpy(a->t, b->t); | |||
} | |||
decaf_bool_t decaf_decode_scalar( | |||
decaf_scalar_t s, | |||
const unsigned char ser[DECAF_SER_BYTES] | |||
) { | |||
unsigned int i,j,k=0; | |||
for (i=0; i<DECAF_SCALAR_LIMBS; i++) { | |||
decaf_word_t out = 0; | |||
for (j=0; j<sizeof(decaf_word_t); j++,k++) { | |||
out |= ((decaf_word_t)ser[k])<<(8*j); | |||
} | |||
s->limb[i] = out; | |||
} | |||
decaf_sdword_t accum = 0; | |||
for (i=0; i<DECAF_SCALAR_LIMBS; i++) { | |||
accum = (accum + s->limb[i] - decaf_scalar_p->limb[i]) >> WBITS; | |||
} | |||
//decaf_mul_scalars(s,s,decaf_scalar_one); /* ham-handed reduce */ | |||
return accum; | |||
} | |||
void decaf_encode_scalar( | |||
unsigned char ser[DECAF_SER_BYTES], | |||
const decaf_scalar_t s | |||
) { | |||
unsigned int i,j,k=0; | |||
for (i=0; i<DECAF_SCALAR_LIMBS; i++) { | |||
for (j=0; j<sizeof(decaf_word_t); j++,k++) { | |||
ser[k] = s->limb[i] >> (8*j); | |||
} | |||
} | |||
} | |||
void decaf_scalarmul ( | |||
decaf_point_t a, | |||
const decaf_point_t b, | |||
@@ -266,6 +266,16 @@ int main(int argc, char **argv) { | |||
when = now() - when; | |||
printf("barrett mac: %5.1fns\n", when * 1e9 / i); | |||
decaf_scalar_t asc,bsc,csc; | |||
memset(asc,0,sizeof(asc)); | |||
memset(bsc,0,sizeof(bsc)); | |||
when = now(); | |||
for (i=0; i<nbase*10; i++) { | |||
decaf_mul_scalars(csc,asc,bsc); | |||
} | |||
when = now() - when; | |||
printf("decaf mulsc: %5.1fns\n", when * 1e9 / i); | |||
memset(&ext,0,sizeof(ext)); | |||
memset(&niels,0,sizeof(niels)); /* avoid assertions in p521 even though this isn't a valid ext or niels */ | |||
@@ -1,10 +1,24 @@ | |||
#include "field.h" | |||
#include "test.h" | |||
#include "decaf.h" | |||
#include <gmp.h> | |||
#include <string.h> | |||
#include <stdio.h> | |||
mpz_t mp_field; | |||
mpz_t mp_scalar_field; | |||
void decaf_scalar_print ( | |||
const char *descr, | |||
const decaf_scalar_t scalar | |||
) { | |||
int j; | |||
printf("%s = 0x", descr); | |||
for (j=DECAF_SCALAR_LIMBS-1; j>=0; j--) { | |||
printf(PRIxWORDfull, scalar->limb[j]); | |||
} | |||
printf("\n"); | |||
} | |||
static mask_t mpz_to_field ( | |||
field_a_t out, | |||
@@ -20,6 +34,78 @@ static mask_t mpz_to_field ( | |||
return succ; | |||
} | |||
static mask_t mpz_to_scalar ( | |||
decaf_scalar_t out, | |||
const mpz_t in | |||
) { | |||
uint8_t ser[DECAF_SCALAR_BYTES]; | |||
mpz_t modded; | |||
memset(ser,0,sizeof(ser)); | |||
mpz_init(modded); | |||
mpz_mod(modded, in, mp_scalar_field); | |||
mpz_export(ser, NULL, -1, 1, -1, 0, modded); | |||
mask_t succ = decaf_decode_scalar(out, ser); | |||
return succ; | |||
} | |||
static mask_t scalar_assert_eq_gmp( | |||
const char *descr, | |||
const decaf_scalar_t a, | |||
const decaf_scalar_t b, | |||
const decaf_scalar_t x, | |||
const mpz_t ma, | |||
const mpz_t mb, | |||
const mpz_t y | |||
) { | |||
uint8_t xser[FIELD_BYTES], yser[FIELD_BYTES]; | |||
mpz_t modded; | |||
memset(yser,0,sizeof(yser)); | |||
decaf_encode_scalar(xser, x); | |||
mpz_init(modded); | |||
mpz_mod(modded, y, mp_scalar_field); | |||
mpz_export(yser, NULL, -1, 1, -1, 0, modded); | |||
if (memcmp(xser,yser,FIELD_BYTES)) { | |||
youfail(); | |||
printf(" Failed arithmetic test %s\n", descr); | |||
decaf_scalar_print(" a", a); | |||
decaf_scalar_print(" b", b); | |||
decaf_scalar_print(" decaf", x); | |||
// printf(" gmpa = 0x"); | |||
int j; | |||
// mpz_export(yser, NULL, -1, 1, -1, 0, ma); | |||
// for (j=FIELD_BYTES-1; j>=0; j--) { | |||
// printf("%02x", yser[j]); | |||
// } | |||
// printf("\n"); | |||
// printf(" gmpb = 0x"); | |||
// | |||
// | |||
// mpz_export(yser, NULL, -1, 1, -1, 0, mb); | |||
// for (j=FIELD_BYTES-1; j>=0; j--) { | |||
// printf("%02x", yser[j]); | |||
// } | |||
// printf("\n"); | |||
(void)ma; (void)mb; | |||
printf(" gmpy = 0x"); | |||
mpz_export(yser, NULL, -1, 1, -1, 0, modded); | |||
for (j=FIELD_BYTES-1; j>=0; j--) { | |||
printf("%02x", yser[j]); | |||
} | |||
printf("\n"); | |||
return MASK_FAILURE; | |||
} | |||
mpz_clear(modded); | |||
return MASK_SUCCESS; | |||
} | |||
static inline int BRANCH_ON_CONSTANT(int x) { | |||
__asm__ ("" : "+r"(x)); | |||
return x; | |||
@@ -127,6 +213,34 @@ static mask_t test_add_sub_RAW ( | |||
return succ; | |||
} | |||
static mask_t test_scalar ( | |||
const mpz_t x, | |||
const mpz_t y | |||
) { | |||
decaf_scalar_t xx,yy,tt; | |||
mpz_t t; | |||
mask_t succ = MASK_SUCCESS; | |||
succ = mpz_to_scalar(xx,x); | |||
succ &= mpz_to_scalar(yy,y); | |||
mpz_init(t); | |||
decaf_add_scalars(tt,xx,yy); | |||
mpz_add(t,x,y); | |||
succ &= scalar_assert_eq_gmp("scalar add",xx,yy,tt,x,y,t); | |||
decaf_sub_scalars(tt,xx,yy); | |||
mpz_sub(t,x,y); | |||
succ &= scalar_assert_eq_gmp("scalar sub",xx,yy,tt,x,y,t); | |||
decaf_mul_scalars(tt,xx,yy); | |||
mpz_mul(t,x,y); | |||
succ &= scalar_assert_eq_gmp("scalar mul",xx,yy,tt,x,y,t); | |||
mpz_clear(t); | |||
return succ; | |||
} | |||
static mask_t test_mul_sqr ( | |||
const mpz_t x, | |||
const mpz_t y, | |||
@@ -208,6 +322,8 @@ int test_arithmetic (void) { | |||
mpz_init(mp_field); | |||
mpz_import(mp_field, FIELD_BYTES, -1, 1, -1, 0, FIELD_MODULUS); | |||
mpz_import(mp_scalar_field, DECAF_SCALAR_LIMBS, -1, sizeof(decaf_word_t), -1, 0, decaf_scalar_p); | |||
mpz_t x,y; | |||
mpz_init(x); | |||
mpz_init(y); | |||
@@ -234,6 +350,7 @@ int test_arithmetic (void) { | |||
succ &= test_add_sub_RAW(x,y,word); | |||
succ &= test_mul_sqr(x,y,word); | |||
succ &= test_scalar(x,y); | |||
if (j < 1000) | |||
succ &= test_isr(x); | |||