From: Loup Vaillant Date: Sun, 22 Mar 2020 00:07:01 +0000 (+0100) Subject: Added scalar multiplication by inverse X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=f781b303429c1cde31900994f10f1b51129e99e2;p=Monocypher.git Added scalar multiplication by inverse --- diff --git a/src/monocypher.c b/src/monocypher.c index 8b57f26..36ecd57 100644 --- a/src/monocypher.c +++ b/src/monocypher.c @@ -1382,21 +1382,12 @@ static int scalar_bit(const u8 s[32], int i) { /////////////// /// X-25519 /// Taken from SUPERCOP's ref10 implementation. /////////////// - -void crypto_x25519(u8 raw_shared_secret[32], - const u8 your_secret_key [32], - const u8 their_public_key [32]) +static void scalarmult(u8 q[32], const u8 scalar[32], const u8 p[32], + size_t nb_bits) { // computes the scalar product fe x1; - fe_frombytes(x1, their_public_key); - - // restrict the possible scalar values - u8 e[32]; - FOR (i, 0, 32) { - e[i] = your_secret_key[i]; - } - trim_scalar(e); + fe_frombytes(x1, p); // computes the actual scalar product (the result is in x2 and z2) fe x2, z2, x3, z3, t0, t1; @@ -1406,9 +1397,9 @@ void crypto_x25519(u8 raw_shared_secret[32], fe_1(x2); fe_0(z2); // "zero" point fe_copy(x3, x1); fe_1(z3); // "one" point int swap = 0; - for (int pos = 254; pos >= 0; --pos) { + for (int pos = nb_bits-1; pos >= 0; --pos) { // constant time conditional swap before ladder step - int b = scalar_bit(e, pos); + int b = scalar_bit(scalar, pos); swap ^= b; // xor trick avoids swapping at the end of the loop fe_cswap(x2, x3, swap); fe_cswap(z2, z3, swap); @@ -1431,12 +1422,26 @@ void crypto_x25519(u8 raw_shared_secret[32], // normalises the coordinates: x == X / Z fe_invert(z2, z2); fe_mul(x2, x2, z2); - fe_tobytes(raw_shared_secret, x2); + fe_tobytes(q, x2); + + WIPE_BUFFER(x1); + WIPE_BUFFER(x2); WIPE_BUFFER(z2); WIPE_BUFFER(t0); + WIPE_BUFFER(x3); WIPE_BUFFER(z3); WIPE_BUFFER(t1); +} + +void crypto_x25519(u8 raw_shared_secret[32], + const u8 your_secret_key [32], + const u8 their_public_key [32]) +{ + // restrict the possible scalar values + u8 e[32]; + FOR (i, 0, 32) { + e[i] = your_secret_key[i]; + } + trim_scalar(e); + scalarmult(raw_shared_secret, e, their_public_key, 255); - WIPE_BUFFER(x1); WIPE_BUFFER(e ); - WIPE_BUFFER(x2); WIPE_BUFFER(z2); - WIPE_BUFFER(x3); WIPE_BUFFER(z3); - WIPE_BUFFER(t0); WIPE_BUFFER(t1); + WIPE_BUFFER(e); } void crypto_x25519_public_key(u8 public_key[32], @@ -1450,10 +1455,12 @@ void crypto_x25519_public_key(u8 public_key[32], /// Ed25519 /// /////////////// -static const i64 L[32] = { 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, - 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; +static const u8 L[32] = { + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 +}; // r = x mod L (little-endian) static void modL(u8 *r, i64 x[64]) @@ -2490,6 +2497,53 @@ void crypto_key_exchange(u8 shared_key[32], crypto_hchacha20(shared_key, shared_key, zero); } +////////////////////// +/// Scalar divison /// +////////////////////// +void crypto_x25519_inverse(u8 blind_salt [32], + const u8 private_key[32], + const u8 curve_point[32]) +{ + static const u8 Lm2[32] = { // L - 2 + 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, + 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + }; + u8 scalar[32]; + FOR (i, 0, 32) { + scalar[i] = private_key[i]; + } + trim_scalar(scalar); + u8 inverse[32] = {1}; + for (int i = 252; i >= 0; i--) { + mul_add(inverse, inverse, inverse, zero); + if (scalar_bit(Lm2, i)) { + mul_add(inverse, inverse, scalar, zero); + } + } + // Clear the cofactor of inverse: + // inverse <- inverse * (3*L + 1) + // The order of the curve being 8*L, we can simplify down to + // inverse <- inverse + L * ((inverse*3) % 8) + // + // Before the operation, inverse < L. + // After the operation, inverse < 8*L. + // Therefore, inverse fits in 256 bits (32 bytes) + // + u32 mod8 = (inverse[0] * 3) & 7; + u32 carry = 0; + FOR (i , 0, 32) { + carry = carry + inverse[i] + L[i] * mod8; + inverse[i] = (u8)carry; + carry >>= 8; + } + // Recall that 8*L < 2^256. However it is also very close to + // 2^255. If we spaned the ladder over 255 bits, random tests + // wouldn't catch the of-by-one error. + scalarmult(blind_salt, inverse, curve_point, 256); +} + //////////////////////////////// /// Authenticated encryption /// //////////////////////////////// diff --git a/src/monocypher.h b/src/monocypher.h index a9986bb..c90cd10 100644 --- a/src/monocypher.h +++ b/src/monocypher.h @@ -332,4 +332,10 @@ void crypto_x25519(uint8_t raw_shared_secret[32], const uint8_t your_secret_key [32], const uint8_t their_public_key [32]); +// scalar division +// --------------- +void crypto_x25519_inverse(uint8_t blind_salt [32], + const uint8_t private_key[32], + const uint8_t curve_point[32]); + #endif // MONOCYPHER_H diff --git a/tests/speed/speed.c b/tests/speed/speed.c index e24eac3..a626efd 100644 --- a/tests/speed/speed.c +++ b/tests/speed/speed.c @@ -173,6 +173,17 @@ static u64 edDSA_check(void) TIMING_END; } +static u64 x25519_inverse(void) +{ + u8 in [32] = {9}; + u8 out[32] = {9}; + + TIMING_START { + crypto_x25519_inverse(out, out, in); + } + TIMING_END; +} + int main() { print("Chacha20 ",chacha20() *MUL ,"megabytes per second"); @@ -184,6 +195,7 @@ int main() print("x25519 ",x25519() ,"exchanges per second"); print("EdDSA(sign) ",edDSA_sign() ,"signatures per second"); print("EdDSA(check) ",edDSA_check() ,"checks per second"); + print("x25519 inverse ",x25519_inverse() ,"scalar inv per second"); printf("\n"); return 0; } diff --git a/tests/test.c b/tests/test.c index fd6d92b..de2ffd2 100644 --- a/tests/test.c +++ b/tests/test.c @@ -977,6 +977,44 @@ static int p_elligator_key_pair_overlap() return status; } +static int p_x25519_inverse() +{ + int status = 0; + const u8 base [32] = {9}; + // check round trip + FOR (i, 0, 50) { + RANDOM_INPUT(sk, 32); + u8 pk [32]; + u8 blind[32]; + crypto_x25519_public_key(pk, sk); + crypto_x25519_inverse(blind, sk, pk); + status |= memcmp(blind, base, 32); + } + + // check cofactor clearing + // (Multiplying by a low order point yields zero + u8 low_order[4][32] = { + {0}, {1}, + {0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, + 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, + 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, + 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57,}, + {0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, + 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, + 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, + 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00,}, + }; + u8 zero[32] = {0}; + FOR (i, 0, 32) { + u8 blind[32]; + RANDOM_INPUT(sk, 32); + crypto_x25519_inverse(blind, sk, low_order[i%4]); + status |= memcmp(blind, zero, 32); + } + printf("%s: x25519_inverse\n", status != 0 ? "FAILED" : "OK"); + return status; +} + #define TEST(name, nb_inputs) vector_test(name, #name, nb_inputs, \ nb_##name##_vectors, \ name##_vectors, \ @@ -1046,6 +1084,7 @@ int main(int argc, char *argv[]) status |= p_elligator_x25519(); status |= p_elligator_key_pair(); status |= p_elligator_key_pair_overlap(); + status |= p_x25519_inverse(); printf("\n%s\n\n", status != 0 ? "SOME TESTS FAILED" : "All tests OK!"); return status; }