// 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));
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
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')
# 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)
#####################
# 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:
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:
# 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
01:
00:
3cfb87c46c0b4575ca8175e0ed1c0ae9dae79db78df86997c4847b9f20b27718:
-
-0000000000000000000000000000000000000000000000000000000000000000:
-00:
-00:
-0000000000000000000000000000000000000000000000000000000000000000: