]> git.codecow.com Git - Monocypher.git/commitdiff
Fixed Elligator reverse map comments
authorLoup Vaillant <loup@loup-vaillant.fr>
Sun, 20 Mar 2022 20:29:07 +0000 (21:29 +0100)
committerLoup Vaillant <loup@loup-vaillant.fr>
Sun, 20 Mar 2022 20:29:07 +0000 (21:29 +0100)
Also took the opportunity to fix the Python inverse map when the point
is zero (it used to fail, now it returns the all zero representative).

src/monocypher.c
tests/gen/elligator-inverse.py
tests/gen/elligator.py
tests/gen/vectors/elligator_inv

index a58a691e0b3102e9b7e675ed03fc14095f1021df..1dd8f2eb24b1f8a625911908d4af7ea395d6fa8d 100644 (file)
@@ -2680,43 +2680,36 @@ void crypto_hidden_to_curve(uint8_t curve[32], const uint8_t hidden[32])
 // Let sq = -non_square * u * (u+A)
 // if sq is not a square, or u = -A, there is no mapping
 // Assuming there is a mapping:
-//   if v is positive: r = sqrt(-(u+A) / u)
-//   if v is negative: r = sqrt(-u / (u+A))
+//    if v is positive: r = sqrt(-u     / (non_square * (u+A)))
+//    if v is negative: r = sqrt(-(u+A) / (non_square * u    ))
 //
 // We compute isr = invsqrt(-non_square * u * (u+A))
-// if it wasn't a non-zero square, abort.
+// if it wasn't a square, abort.
 // else, isr = sqrt(-1 / (non_square * u * (u+A))
 //
-// This causes us to abort if u is zero, even though we shouldn't. This
-// never happens in practice, because (i) a random point in the curve has
-// a negligible chance of being zero, and (ii) scalar multiplication with
-// a trimmed scalar *never* yields zero.
+// If v is positive, we return isr * u:
+//   isr * u = sqrt(-1 / (non_square * u * (u+A)) * u
+//   isr * u = sqrt(-u / (non_square * (u+A))
 //
-// Since:
+// If v is negative, we return isr * (u+A):
 //   isr * (u+A) = sqrt(-1     / (non_square * u * (u+A)) * (u+A)
-//   isr * (u+A) = sqrt(-(u+A) / (non_square * u * (u+A))
-// and:
-//   isr = u = sqrt(-1 / (non_square * u * (u+A)) * u
-//   isr = u = sqrt(-u / (non_square * u * (u+A))
-// Therefore:
-//   if v is positive: r = isr * (u+A)
-//   if v is negative: r = isr * u
+//   isr * (u+A) = sqrt(-(u+A) / (non_square * u)
 int crypto_curve_to_hidden(u8 hidden[32], const u8 public_key[32], u8 tweak)
 {
     fe t1, t2, t3;
-    fe_frombytes(t1, public_key, 1);
+    fe_frombytes(t1, public_key, 1); // t1 = u
 
-    fe_add(t2, t1, A);
+    fe_add(t2, t1, A);               // t2 = u + A
     fe_mul(t3, t1, t2);
     fe_mul_small(t3, t3, -2);
-    int is_square = invsqrt(t3, t3);
+    int is_square = invsqrt(t3, t3); // t3 = sqrt(-1 / non_square * u * (u+A))
     if (is_square) {
         // The only variable time bit.  This ultimately reveals how many
         // tries it took us to find a representable key.
         // This does not affect security as long as we try keys at random.
 
-        fe_ccopy    (t1, t2, tweak & 1);
-        fe_mul      (t3, t1, t3);
+        fe_ccopy    (t1, t2, tweak & 1); // multiply by u if v is positive,
+        fe_mul      (t3, t1, t3);        // multiply by u+A otherwise
         fe_mul_small(t1, t3, 2);
         fe_neg      (t2, t3);
         fe_ccopy    (t3, t2, fe_isodd(t1));
index d8c0cd05641a02537adfe5f6652851e695a1fb0c..f607ee4eb6980468fafb74cd89789127597f7b88 100755 (executable)
@@ -55,6 +55,7 @@
 from elligator import can_curve_to_hash
 from elligator import curve_to_hash
 from elligator import fast_curve_to_hash
+from elligator import fe
 from elligator import hash_to_curve
 from elligator import print_raw
 
@@ -63,16 +64,20 @@ from elligator_scalarmult import scalarmult
 from random import randrange
 from random import seed
 
-def private_to_curve_and_hash(scalar, tweak):
-    cofactor      = scalar % 8
+def redundant_curve_to_hash(u, tweak):
     v_is_negative = tweak % 2 == 1;
-    msb           = (tweak // 2**6) * 2**254
-    u             = scalarmult(scalar, cofactor)
     r1 = None
     if can_curve_to_hash(u):
         r1 = curve_to_hash(u, v_is_negative)
     r2 = fast_curve_to_hash(u, v_is_negative)
     if r1 != r2: raise ValueError('Incoherent hash_to_curve')
+    return r1
+
+def private_to_curve_and_hash(scalar, tweak):
+    cofactor      = scalar % 8
+    msb           = (tweak // 2**6) * 2**254
+    u             = scalarmult(scalar, cofactor)
+    r1 = redundant_curve_to_hash(u, tweak)
     if r1 is None:
         return u, None
     if r1.val > 2**254: raise ValueError('Representative too big')
@@ -84,6 +89,14 @@ def private_to_curve_and_hash(scalar, tweak):
 # not matter here since these are just tests.
 seed(12345)
 
+# Zero
+for tweak in range(2):
+    print_raw(0)
+    print(format(tweak, '02x') + ":")
+    print('00:') # Success
+    redundant_curve_to_hash(fe(0), tweak).print()
+    print()
+
 # All possible failures
 for cofactor in range(8):
     tweak = randrange(0, 256)
index 50924355aedbbb35851ae471228b04fb18edf373..b1362bd15df07d4a87bdd1d4175359c93db29be7 100644 (file)
@@ -188,9 +188,9 @@ def curve_to_hash(u, v_is_negative):
 #####################
 
 # Inverse square root.
+# Returns (0               , True ) if x is zero.
 # Returns (sqrt(1/x)       , True ) if x is non-zero square.
 # Returns (sqrt(sqrt(-1)/x), False) if x is not a square.
-# Returns (0               , False) if x is zero.
 # We do not guarantee the sign of the square root.
 #
 # Notes:
@@ -247,7 +247,7 @@ def invsqrt(x):
     quartic = x * isr**2
     if quartic == fe(-1) or quartic == -sqrtm1:
         isr = isr * sqrtm1
-    is_square = quartic == fe(1) or quartic == fe(-1)
+    is_square = quartic == fe(1) or quartic == fe(-1) or x == fe(0)
     return isr, is_square
 
 # From the paper:
@@ -344,27 +344,20 @@ def fast_hash_to_curve(r):
 # Let sq = -non_square * u * (u+A)
 # if sq is not a square, or u = -A, there is no mapping
 # Assuming there is a mapping:
-#   if v is positive: r = sqrt(-(u+A) / u)
-#   if v is negative: r = sqrt(-u / (u+A))
+#    if v is positive: r = sqrt(-u     / (non_square * (u+A)))
+#    if v is negative: r = sqrt(-(u+A) / (non_square * u    ))
 #
 # We compute isr = invsqrt(-non_square * u * (u+A))
-# if it wasn't a non-zero square, abort.
+# if it wasn't a square, abort.
 # else, isr = sqrt(-1 / (non_square * u * (u+A))
 #
-# This causes us to abort if u is zero, even though we shouldn't. This
-# never happens in practice, because (i) a random point in the curve has
-# a negligible chance of being zero, and (ii) scalar multiplication with
-# a trimmed scalar *never* yields zero.
+# If v is positive, we return isr * u:
+#   isr * u = sqrt(-1 / (non_square * u * (u+A)) * u
+#   isr * u = sqrt(-u / (non_square * (u+A))
 #
-# Since:
+# If v is negative, we return isr * (u+A):
 #   isr * (u+A) = sqrt(-1     / (non_square * u * (u+A)) * (u+A)
-#   isr * (u+A) = sqrt(-(u+A) / (non_square * u * (u+A))
-# and:
-#   isr = u = sqrt(-1 / (non_square * u * (u+A)) * u
-#   isr = u = sqrt(-u / (non_square * u * (u+A))
-# Therefore:
-#   if v is positive: r = isr * (u+A)
-#   if v is negative: r = isr * u
+#   isr * (u+A) = sqrt(-(u+A) / (non_square * u)
 def fast_curve_to_hash(u, v_is_negative):
     t = u + A
     r = -non_square * u * t
index 1948f9742c98a775998bff658b0bbedc83690b1b..05238a50ed5a863720604cf682274b48eef23387 100644 (file)
@@ -12,8 +12,3 @@ ff:
 01:
 00:
 3cfb87c46c0b4575ca8175e0ed1c0ae9dae79db78df86997c4847b9f20b27718:
-
-0000000000000000000000000000000000000000000000000000000000000000:
-00:
-00:
-0000000000000000000000000000000000000000000000000000000000000000: