]> git.codecow.com Git - Monocypher.git/commitdiff
Added scalar multiplication by inverse
authorLoup Vaillant <loup@loup-vaillant.fr>
Sun, 22 Mar 2020 00:07:01 +0000 (01:07 +0100)
committerLoup Vaillant <loup@loup-vaillant.fr>
Sun, 22 Mar 2020 00:07:01 +0000 (01:07 +0100)
src/monocypher.c
src/monocypher.h
tests/speed/speed.c
tests/test.c

index 8b57f262c6311f64f1a75de118d100624d6b1ae0..36ecd57c455c6574748dc620da682612de7a27a6 100644 (file)
@@ -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 ///
 ////////////////////////////////
index a9986bb5f792c994f547f79e96f8aae63257ff8e..c90cd100f689b8719314d4a86938493bb3a21c30 100644 (file)
@@ -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
index e24eac300576fa5b37ea6d3ce10ea4bf244eeb37..a626efd033ccc73a4e7d104eeb85b37e7c0c4ee8 100644 (file)
@@ -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;
 }
index fd6d92b0b017fbf6f525cb87e614f9b81d6e71de..de2ffd267b459e557e0eb8457dc2eae5992d03d2 100644 (file)
@@ -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;
 }