@@ -248,34 +248,47 @@ deserialize_montgomery_decaf ( | |||||
const field_a_t s | const field_a_t s | ||||
) { | ) { | ||||
field_copy ( a->s0, s ); | field_copy ( a->s0, s ); | ||||
field_copy ( a->xa, s ); | |||||
field_sqr ( a->xa, s ); | |||||
field_set_ui ( a->za, 1 ); | field_set_ui ( a->za, 1 ); | ||||
field_set_ui ( a->xd, 1 ); | field_set_ui ( a->xd, 1 ); | ||||
field_set_ui ( a->zd, 0 ); | field_set_ui ( a->zd, 0 ); | ||||
} | } | ||||
mask_t | |||||
serialize_montgomery_decaf ( | |||||
field_a_t b, | |||||
const montgomery_aux_a_t a, | |||||
const field_a_t sbz | |||||
) { | |||||
field_a_t L0, L1; | |||||
field_isr(L0,a->zd); | |||||
field_sqr(L1,L0); | |||||
field_mul(b,a->xd,L1); | |||||
(void)sbz; | |||||
return 0; // Fail, because this routine isn't done yet. | |||||
} | |||||
void | void | ||||
montgomery_aux_step ( | montgomery_aux_step ( | ||||
struct montgomery_aux_t* a | struct montgomery_aux_t* a | ||||
) { | ) { | ||||
ANALYZE_THIS_ROUTINE_CAREFULLY; | ANALYZE_THIS_ROUTINE_CAREFULLY; | ||||
field_add_nr ( a->xs, a->xa, a->za ); | |||||
field_subx_nr ( a->zs, a->xa, a->za ); | |||||
field_add_nr ( a->xa, a->xd, a->zd ); | |||||
field_subx_nr ( a->za, a->xd, a->zd ); | |||||
field_mul ( a->xd, a->xa, a->zs ); | |||||
field_mul ( a->zd, a->xs, a->za ); | |||||
field_add_nr ( a->xs, a->xd, a->zd ); | |||||
field_subx_nr ( a->zd, a->xd, a->zd ); | |||||
field_mul ( a->zs, a->zd, a->s0 ); | |||||
field_sqr ( a->zd, a->xa ); | |||||
field_sqr ( a->xa, a->za ); | |||||
field_subx_nr ( a->za, a->zd, a->xa ); | |||||
IF32( field_weak_reduce( a->za ) ); | |||||
field_mul ( a->xd, a->xa, a->zd ); | |||||
field_mulw_scc_wr ( a->zd, a->xa, 1-EDWARDS_D ); | |||||
field_add_nr ( a->xa, a->za, a->zd ); | |||||
field_mul ( a->zd, a->xa, a->za ); | |||||
field_add_nr ( a->xs, a->xa, a->za ); | |||||
field_subx_nr ( a->zs, a->xa, a->za ); | |||||
field_add_nr ( a->xa, a->xd, a->zd ); | |||||
field_subx_nr ( a->za, a->xd, a->zd ); | |||||
field_mul ( a->xd, a->xa, a->zs ); | |||||
field_mul ( a->zd, a->xs, a->za ); | |||||
field_add_nr ( a->xs, a->xd, a->zd ); | |||||
field_subx_nr ( a->zd, a->xd, a->zd ); | |||||
field_mul ( a->zs, a->zd, a->s0 ); | |||||
field_sqr ( a->zd, a->xa ); | |||||
field_sqr ( a->xa, a->za ); | |||||
field_subx_nr ( a->za, a->zd, a->xa ); | |||||
field_mul ( a->xd, a->xa, a->zd ); | |||||
field_mulw_scc_wr ( a->zd, a->za, 1-EDWARDS_D ); | |||||
field_add_nr ( a->xa, a->xa, a->zd ); | |||||
field_mul ( a->zd, a->xa, a->za ); | |||||
field_sqr ( a->xa, a->xs ); | field_sqr ( a->xa, a->xs ); | ||||
field_sqr ( a->za, a->zs ); | field_sqr ( a->za, a->zs ); | ||||
} | } | ||||
@@ -294,6 +294,13 @@ serialize_montgomery ( | |||||
const montgomery_a_t a, | const montgomery_a_t a, | ||||
const field_a_t sbz | const field_a_t sbz | ||||
); | ); | ||||
mask_t | |||||
serialize_montgomery_decaf ( | |||||
field_a_t b, | |||||
const montgomery_aux_a_t a, | |||||
const field_a_t sbz | |||||
); | |||||
void | void | ||||
deserialize_montgomery_decaf ( | deserialize_montgomery_decaf ( | ||||
@@ -97,6 +97,40 @@ montgomery_ladder ( | |||||
unsigned int n_extra_doubles | unsigned int n_extra_doubles | ||||
) __attribute__((warn_unused_result)); | ) __attribute__((warn_unused_result)); | ||||
/** | |||||
* Full Montgomery aux ladder in decaf format. | |||||
* | |||||
* Out = [2^n_extra_doubles * scalar] * in, where | |||||
* scalar is little-endian and has length $nbits$ bits. | |||||
* | |||||
* This function (once it's done; TODO) will always reject points | |||||
* on the twist. | |||||
* | |||||
* This function takes constant time with respect to $*in$ | |||||
* and $*scalar$, but not of course with respect to nbits or | |||||
* n_extra_doubles. | |||||
* | |||||
* @param [out] out The output point. | |||||
* @param [in] in The base point. | |||||
* @param [in] scalar The scalar's little-endian representation. | |||||
* @param [in] nbits The number of bits in the scalar. Note that | |||||
* unlike in Curve25519, we do not require the top bit to be set. | |||||
* @param [in] n_extra_doubles The number of extra doubles to do at | |||||
* the end. | |||||
* | |||||
* @retval MASK_SUCCESS The operation was successful. | |||||
* @retval MASK_FAILURE The input point was invalid, or the output | |||||
* would be the identity or the point of order 2. | |||||
*/ | |||||
mask_t | |||||
montgomery_ladder_decaf ( | |||||
field_a_t out, | |||||
const field_a_t in, | |||||
const word_t *scalar, | |||||
unsigned int nbits, | |||||
unsigned int n_extra_doubles | |||||
) __attribute__((warn_unused_result)); | |||||
/** | /** | ||||
* Scalar multiply a twisted Edwards-form point. | * Scalar multiply a twisted Edwards-form point. | ||||
* | * | ||||
@@ -48,6 +48,41 @@ montgomery_ladder ( | |||||
return serialize_montgomery(out, mont, in); | return serialize_montgomery(out, mont, in); | ||||
} | } | ||||
mask_t | |||||
montgomery_ladder_decaf ( | |||||
field_a_t out, | |||||
const field_a_t in, | |||||
const word_t *scalar, | |||||
unsigned int nbits, | |||||
unsigned int n_extra_doubles | |||||
) { | |||||
montgomery_aux_a_t mont; | |||||
deserialize_montgomery_decaf(mont, in); | |||||
int i,j,n=(nbits-1)%WORD_BITS; | |||||
mask_t pflip = 0; | |||||
for (j=(nbits+WORD_BITS-1)/WORD_BITS-1; j>=0; j--) { | |||||
word_t w = scalar[j]; | |||||
for (i=n; i>=0; i--) { | |||||
mask_t flip = -((w>>i)&1); | |||||
constant_time_cond_swap(mont->xa,mont->xd,sizeof(mont->xd),flip^pflip); | |||||
constant_time_cond_swap(mont->za,mont->zd,sizeof(mont->xd),flip^pflip); | |||||
montgomery_aux_step(mont); | |||||
pflip = flip; | |||||
} | |||||
n = WORD_BITS-1; | |||||
} | |||||
constant_time_cond_swap(mont->xa,mont->xd,sizeof(mont->xd),pflip); | |||||
constant_time_cond_swap(mont->za,mont->zd,sizeof(mont->xd),pflip); | |||||
assert(n_extra_doubles < INT_MAX); | |||||
for (j=0; j<(int)n_extra_doubles; j++) { | |||||
montgomery_aux_step(mont); | |||||
} | |||||
return serialize_montgomery_decaf(out, mont, in); | |||||
} | |||||
static __inline__ void | static __inline__ void | ||||
__attribute__((unused,always_inline)) | __attribute__((unused,always_inline)) | ||||
constant_time_lookup_tw_pniels ( | constant_time_lookup_tw_pniels ( | ||||
@@ -325,6 +325,13 @@ int main(int argc, char **argv) { | |||||
} | } | ||||
when = now() - when; | when = now() - when; | ||||
printf("full ladder: %5.1fµs\n", when * 1e6 / i); | printf("full ladder: %5.1fµs\n", when * 1e6 / i); | ||||
when = now(); | |||||
for (i=0; i<nbase/10; i++) { | |||||
ignore_result(montgomery_ladder_decaf(a,b,sk,FIELD_BITS,0)); | |||||
} | |||||
when = now() - when; | |||||
printf("decafladder: %5.1fµs\n", when * 1e6 / i); | |||||
when = now(); | when = now(); | ||||
for (i=0; i<nbase/10; i++) { | for (i=0; i<nbase/10; i++) { | ||||
@@ -3,6 +3,7 @@ | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include "ec_point.h" | #include "ec_point.h" | ||||
#include "scalarmul.h" | |||||
#include "magic.h" | #include "magic.h" | ||||
#include "field.h" | #include "field.h" | ||||
#include "crandom.h" | #include "crandom.h" | ||||
@@ -261,22 +262,32 @@ int test_decaf (void) { | |||||
int i, hits = 0, fails = 0; | int i, hits = 0, fails = 0; | ||||
for (i=0; i<1000; i++) { | for (i=0; i<1000; i++) { | ||||
uint8_t ser[FIELD_BYTES]; | uint8_t ser[FIELD_BYTES]; | ||||
crandom_generate(&crand, ser, sizeof(ser)); | |||||
#if (FIELD_BITS % 8) | |||||
ser[FIELD_BYTES-1] &= (1<<(FIELD_BITS%8)) - 1; | |||||
#endif | |||||
ser[0] &= ~1; | |||||
int j; | |||||
mask_t succ = 0; | |||||
for (j=0; j<128 && !succ; j++) { | |||||
crandom_generate(&crand, ser, sizeof(ser)); | |||||
#if (FIELD_BITS % 8) | |||||
ser[FIELD_BYTES-1] &= (1<<(FIELD_BITS%8)) - 1; | |||||
#endif | |||||
ser[0] &= ~1; | |||||
mask_t succ = field_deserialize(serf, ser); | |||||
succ = field_deserialize(serf, ser); | |||||
if (!succ) { | |||||
youfail(); | |||||
printf(" Unlikely: fail at field_deserialize\n"); | |||||
return -1; | |||||
} | |||||
succ &= decaf_deserialize_affine(&base, serf, 0); | |||||
} | |||||
if (!succ) { | if (!succ) { | ||||
youfail(); | youfail(); | ||||
printf(" Unlikely: fail at field_deserialize\n"); | |||||
printf("Unlikely: fail 128 desers\n"); | |||||
return -1; | return -1; | ||||
} | } | ||||
succ &= decaf_deserialize_affine(&base, serf, 0); | |||||
if (!succ) continue; | |||||
hits++; | hits++; | ||||
field_a_t serf2; | field_a_t serf2; | ||||
struct extensible_t ext; | struct extensible_t ext; | ||||
@@ -351,10 +362,18 @@ int test_decaf (void) { | |||||
fails ++; | fails ++; | ||||
} | } | ||||
word_t scalar = i; | |||||
mask_t res = montgomery_ladder_decaf(serf2,serf,&scalar,i,0); | |||||
// youfail(); | |||||
// printf("Decaf Montgomery ladder i=%d res=%d\n", i, (int)res); | |||||
// field_print(" s", serf); | |||||
// field_print(" o", serf2); | |||||
// printf("\n"); | |||||
(void)res; | |||||
} | } | ||||
if (hits < 350) { | |||||
if (hits < 1000) { | |||||
youfail(); | youfail(); | ||||
printf(" Unlikely: only %d successes in decaf_deser\n", hits); | |||||
printf(" Fail: only %d successes in decaf_deser\n", hits); | |||||
return -1; | return -1; | ||||
} else if (fails) { | } else if (fails) { | ||||
return -1; | return -1; | ||||