diff --git a/basicswap/contrib/ed25519_fast.py b/basicswap/contrib/ed25519_fast.py deleted file mode 100644 index 6e94238..0000000 --- a/basicswap/contrib/ed25519_fast.py +++ /dev/null @@ -1,356 +0,0 @@ -# ed25519.py - Optimized version of the reference implementation of Ed25519 -# -# Written in 2011? by Daniel J. Bernstein -# 2013 by Donald Stufft -# 2013 by Alex Gaynor -# 2013 by Greg Price -# -# To the extent possible under law, the author(s) have dedicated all copyright -# and related and neighboring rights to this software to the public domain -# worldwide. This software is distributed without any warranty. -# -# You should have received a copy of the CC0 Public Domain Dedication along -# with this software. If not, see -# . - -""" -NB: This code is not safe for use with secret keys or secret data. -The only safe use of this code is for verifying signatures on public messages. - -Functions for computing the public key of a secret key and for signing -a message are included, namely publickey_unsafe and signature_unsafe, -for testing purposes only. - -The root of the problem is that Python's long-integer arithmetic is -not designed for use in cryptography. Specifically, it may take more -or less time to execute an operation depending on the values of the -inputs, and its memory access patterns may also depend on the inputs. -This opens it to timing and cache side-channel attacks which can -disclose data to an attacker. We rely on Python's long-integer -arithmetic, so we cannot handle secrets without risking their disclosure. -""" - -import hashlib -import operator -import sys - - -__version__ = "1.0.dev0" - - -# Useful for very coarse version differentiation. -PY3 = sys.version_info[0] == 3 - -if PY3: - indexbytes = operator.getitem - intlist2bytes = bytes - int2byte = operator.methodcaller("to_bytes", 1, "big") -else: - int2byte = chr - range = xrange - - def indexbytes(buf, i): - return ord(buf[i]) - - def intlist2bytes(l): - return b"".join(chr(c) for c in l) - - -b = 256 -q = 2 ** 255 - 19 -l = 2 ** 252 + 27742317777372353535851937790883648493 - - -def H(m): - return hashlib.sha512(m).digest() - - -def pow2(x, p): - """== pow(x, 2**p, q)""" - while p > 0: - x = x * x % q - p -= 1 - return x - - -def inv(z): - """$= z^{-1} \mod q$, for z != 0""" - # Adapted from curve25519_athlon.c in djb's Curve25519. - z2 = z * z % q # 2 - z9 = pow2(z2, 2) * z % q # 9 - z11 = z9 * z2 % q # 11 - z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0 - z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0 - z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ... - z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % q - z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % q - z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % q - z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % q - z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0 - return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2 - - -d = -121665 * inv(121666) % q -I = pow(2, (q - 1) // 4, q) - - -def xrecover(y, sign=0): - xx = (y * y - 1) * inv(d * y * y + 1) - x = pow(xx, (q + 3) // 8, q) - - if (x * x - xx) % q != 0: - x = (x * I) % q - - if x % 2 != sign: - x = q-x - - return x - - -By = 4 * inv(5) -Bx = xrecover(By) -B = (Bx % q, By % q, 1, (Bx * By) % q) -ident = (0, 1, 1, 0) - - -def edwards_add(P, Q): - # This is formula sequence 'addition-add-2008-hwcd-3' from - # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - (x1, y1, z1, t1) = P - (x2, y2, z2, t2) = Q - - a = (y1-x1)*(y2-x2) % q - b = (y1+x1)*(y2+x2) % q - c = t1*2*d*t2 % q - dd = z1*2*z2 % q - e = b - a - f = dd - c - g = dd + c - h = b + a - x3 = e*f - y3 = g*h - t3 = e*h - z3 = f*g - - return (x3 % q, y3 % q, z3 % q, t3 % q) - - -def edwards_sub(P, Q): - # This is formula sequence 'addition-add-2008-hwcd-3' from - # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - (x1, y1, z1, t1) = P - (x2, y2, z2, t2) = Q - - # https://eprint.iacr.org/2008/522.pdf - # The negative of (X:Y:Z)is (−X:Y:Z) - #x2 = q-x2 - """ - doesn't work - x2 = q-x2 - t2 = (x2*y2) % q - """ - - zi = inv(z2) - x2 = q-((x2 * zi) % q) - y2 = (y2 * zi) % q - z2 = 1 - t2 = (x2*y2) % q - - - a = (y1-x1)*(y2-x2) % q - b = (y1+x1)*(y2+x2) % q - c = t1*2*d*t2 % q - dd = z1*2*z2 % q - e = b - a - f = dd - c - g = dd + c - h = b + a - x3 = e*f - y3 = g*h - t3 = e*h - z3 = f*g - - return (x3 % q, y3 % q, z3 % q, t3 % q) - - -def edwards_double(P): - # This is formula sequence 'dbl-2008-hwcd' from - # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html - (x1, y1, z1, t1) = P - - a = x1*x1 % q - b = y1*y1 % q - c = 2*z1*z1 % q - # dd = -a - e = ((x1+y1)*(x1+y1) - a - b) % q - g = -a + b # dd + b - f = g - c - h = -a - b # dd - b - x3 = e*f - y3 = g*h - t3 = e*h - z3 = f*g - - return (x3 % q, y3 % q, z3 % q, t3 % q) - - -def scalarmult(P, e): - if e == 0: - return ident - Q = scalarmult(P, e // 2) - Q = edwards_double(Q) - if e & 1: - Q = edwards_add(Q, P) - return Q - - -# Bpow[i] == scalarmult(B, 2**i) -Bpow = [] - - -def make_Bpow(): - P = B - for i in range(253): - Bpow.append(P) - P = edwards_double(P) -make_Bpow() - - -def scalarmult_B(e): - """ - Implements scalarmult(B, e) more efficiently. - """ - # scalarmult(B, l) is the identity - e = e % l - P = ident - for i in range(253): - if e & 1: - P = edwards_add(P, Bpow[i]) - e = e // 2 - assert e == 0, e - return P - - -def encodeint(y): - bits = [(y >> i) & 1 for i in range(b)] - return b''.join([ - int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) - for i in range(b//8) - ]) - - -def encodepoint(P): - (x, y, z, t) = P - zi = inv(z) - x = (x * zi) % q - y = (y * zi) % q - bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] - return b''.join([ - int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) - for i in range(b // 8) - ]) - - -def bit(h, i): - return (indexbytes(h, i // 8) >> (i % 8)) & 1 - - -def publickey_unsafe(sk): - """ - Not safe to use with secret keys or secret data. - - See module docstring. This function should be used for testing only. - """ - h = H(sk) - a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) - A = scalarmult_B(a) - return encodepoint(A) - - -def Hint(m): - h = H(m) - return sum(2 ** i * bit(h, i) for i in range(2 * b)) - - -def signature_unsafe(m, sk, pk): - """ - Not safe to use with secret keys or secret data. - - See module docstring. This function should be used for testing only. - """ - h = H(sk) - a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) - r = Hint( - intlist2bytes([indexbytes(h, j) for j in range(b // 8, b // 4)]) + m - ) - R = scalarmult_B(r) - S = (r + Hint(encodepoint(R) + pk + m) * a) % l - return encodepoint(R) + encodeint(S) - - -def isoncurve(P): - (x, y, z, t) = P - return (z % q != 0 and - x*y % q == z*t % q and - (y*y - x*x - z*z - d*t*t) % q == 0) - - -def decodeint(s): - return sum(2 ** i * bit(s, i) for i in range(0, b)) - - -def decodepoint(s): - y = sum(2 ** i * bit(s, i) for i in range(0, b - 1)) - x = xrecover(y) - if x & 1 != bit(s, b-1): - x = q - x - P = (x, y, 1, (x*y) % q) - if not isoncurve(P): - raise ValueError("decoding point that is not on curve") - return P - - -class SignatureMismatch(Exception): - pass - - -def checkvalid(s, m, pk): - """ - Not safe to use when any argument is secret. - - See module docstring. This function should be used only for - verifying public signatures of public messages. - """ - if len(s) != b // 4: - raise ValueError("signature length is wrong") - - if len(pk) != b // 8: - raise ValueError("public-key length is wrong") - - R = decodepoint(s[:b // 8]) - A = decodepoint(pk) - S = decodeint(s[b // 8:b // 4]) - h = Hint(encodepoint(R) + pk + m) - - (x1, y1, z1, t1) = P = scalarmult_B(S) - (x2, y2, z2, t2) = Q = edwards_add(R, scalarmult(A, h)) - - if (not isoncurve(P) or not isoncurve(Q) or - (x1*z2 - x2*z1) % q != 0 or (y1*z2 - y2*z1) % q != 0): - raise SignatureMismatch("signature does not pass verification") - - -def is_identity(P): - return True if P[0] == 0 else False - - -def edwards_negated(P): - (x, y, z, t) = P - - zi = inv(z) - x = q - ((x * zi) % q) - y = (y * zi) % q - z = 1 - t = (x * y) % q - - return (x, y, z, t) diff --git a/basicswap/contrib/ellipticcurve.py b/basicswap/contrib/ellipticcurve.py deleted file mode 100644 index 8a58166..0000000 --- a/basicswap/contrib/ellipticcurve.py +++ /dev/null @@ -1,486 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# -# Implementation of elliptic curves, for cryptographic applications. -# -# This module doesn't provide any way to choose a random elliptic -# curve, nor to verify that an elliptic curve was chosen randomly, -# because one can simply use NIST's standard curves. -# -# Notes from X9.62-1998 (draft): -# Nomenclature: -# - Q is a public key. -# The "Elliptic Curve Domain Parameters" include: -# - q is the "field size", which in our case equals p. -# - p is a big prime. -# - G is a point of prime order (5.1.1.1). -# - n is the order of G (5.1.1.1). -# Public-key validation (5.2.2): -# - Verify that Q is not the point at infinity. -# - Verify that X_Q and Y_Q are in [0,p-1]. -# - Verify that Q is on the curve. -# - Verify that nQ is the point at infinity. -# Signature generation (5.3): -# - Pick random k from [1,n-1]. -# Signature checking (5.4.2): -# - Verify that r and s are in [1,n-1]. -# -# Version of 2008.11.25. -# -# Revision history: -# 2005.12.31 - Initial version. -# 2008.11.25 - Change CurveFp.is_on to contains_point. -# -# Written in 2005 by Peter Pearson and placed in the public domain. - -def inverse_mod(a, m): - """Inverse of a mod m.""" - - if a < 0 or m <= a: - a = a % m - - # From Ferguson and Schneier, roughly: - - c, d = a, m - uc, vc, ud, vd = 1, 0, 0, 1 - while c != 0: - q, c, d = divmod(d, c) + (c,) - uc, vc, ud, vd = ud - q * uc, vd - q * vc, uc, vc - - # At this point, d is the GCD, and ud*a+vd*m = d. - # If d == 1, this means that ud is a inverse. - - assert d == 1 - if ud > 0: - return ud - else: - return ud + m - - -def modular_sqrt(a, p): - # from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/ - """ Find a quadratic residue (mod p) of 'a'. p - must be an odd prime. - - Solve the congruence of the form: - x^2 = a (mod p) - And returns x. Note that p - x is also a root. - - 0 is returned is no square root exists for - these a and p. - - The Tonelli-Shanks algorithm is used (except - for some simple cases in which the solution - is known from an identity). This algorithm - runs in polynomial time (unless the - generalized Riemann hypothesis is false). - """ - # Simple cases - # - if legendre_symbol(a, p) != 1: - return 0 - elif a == 0: - return 0 - elif p == 2: - return p - elif p % 4 == 3: - return pow(a, (p + 1) // 4, p) - - # Partition p-1 to s * 2^e for an odd s (i.e. - # reduce all the powers of 2 from p-1) - # - s = p - 1 - e = 0 - while s % 2 == 0: - s /= 2 - e += 1 - - # Find some 'n' with a legendre symbol n|p = -1. - # Shouldn't take long. - # - n = 2 - while legendre_symbol(n, p) != -1: - n += 1 - - # Here be dragons! - # Read the paper "Square roots from 1; 24, 51, - # 10 to Dan Shanks" by Ezra Brown for more - # information - # - - # x is a guess of the square root that gets better - # with each iteration. - # b is the "fudge factor" - by how much we're off - # with the guess. The invariant x^2 = ab (mod p) - # is maintained throughout the loop. - # g is used for successive powers of n to update - # both a and b - # r is the exponent - decreases with each update - # - x = pow(a, (s + 1) // 2, p) - b = pow(a, s, p) - g = pow(n, s, p) - r = e - - while True: - t = b - m = 0 - for m in range(r): - if t == 1: - break - t = pow(t, 2, p) - - if m == 0: - return x - - gs = pow(g, 2 ** (r - m - 1), p) - g = (gs * gs) % p - x = (x * gs) % p - b = (b * g) % p - r = m - - -def legendre_symbol(a, p): - """ Compute the Legendre symbol a|p using - Euler's criterion. p is a prime, a is - relatively prime to p (if p divides - a, then a|p = 0) - - Returns 1 if a has a square root modulo - p, -1 otherwise. - """ - ls = pow(a, (p - 1) // 2, p) - return -1 if ls == p - 1 else ls - - -def jacobi_symbol(n, k): - """Compute the Jacobi symbol of n modulo k - - See http://en.wikipedia.org/wiki/Jacobi_symbol - - For our application k is always prime, so this is the same as the Legendre symbol.""" - assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k" - n %= k - t = 0 - while n != 0: - while n & 1 == 0: - n >>= 1 - r = k & 7 - t ^= (r == 3 or r == 5) - n, k = k, n - t ^= (n & k & 3 == 3) - n = n % k - if k == 1: - return -1 if t else 1 - return 0 - - -class CurveFp(object): - """Elliptic Curve over the field of integers modulo a prime.""" - def __init__(self, p, a, b): - """The curve of points satisfying y^2 = x^3 + a*x + b (mod p).""" - self.__p = p - self.__a = a - self.__b = b - - def p(self): - return self.__p - - def a(self): - return self.__a - - def b(self): - return self.__b - - def contains_point(self, x, y): - """Is the point (x,y) on this curve?""" - return (y * y - (x * x * x + self.__a * x + self.__b)) % self.__p == 0 - - -class Point(object): - """ A point on an elliptic curve. Altering x and y is forbidding, - but they can be read by the x() and y() methods.""" - def __init__(self, curve, x, y, order=None): - """curve, x, y, order; order (optional) is the order of this point.""" - self.__curve = curve - self.__x = x - self.__y = y - self.__order = order - # self.curve is allowed to be None only for INFINITY: - if self.__curve: - assert self.__curve.contains_point(x, y) - if order: - assert self * order == INFINITY - - def __eq__(self, other): - """Return 1 if the points are identical, 0 otherwise.""" - if self.__curve == other.__curve \ - and self.__x == other.__x \ - and self.__y == other.__y: - return 1 - else: - return 0 - - def __add__(self, other): - """Add one point to another point.""" - - # X9.62 B.3: - if other == INFINITY: - return self - if self == INFINITY: - return other - assert self.__curve == other.__curve - if self.__x == other.__x: - if (self.__y + other.__y) % self.__curve.p() == 0: - return INFINITY - else: - return self.double() - - p = self.__curve.p() - - l = ((other.__y - self.__y) * inverse_mod(other.__x - self.__x, p)) % p - - x3 = (l * l - self.__x - other.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def __sub__(self, other): - #The inverse of a point P=(xP,yP) is its reflexion across the x-axis : P′=(xP,−yP). - #If you want to compute Q−P, just replace yP by −yP in the usual formula for point addition. - - # X9.62 B.3: - if other == INFINITY: - return self - if self == INFINITY: - return other - assert self.__curve == other.__curve - - p = self.__curve.p() - #opi = inverse_mod(other.__y, p) - opi = -other.__y % p - #print(opi) - #print(-other.__y % p) - - if self.__x == other.__x: - if (self.__y + opi) % self.__curve.p() == 0: - return INFINITY - else: - return self.double - - l = ((opi - self.__y) * inverse_mod(other.__x - self.__x, p)) % p - - x3 = (l * l - self.__x - other.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def __mul__(self, e): - if self.__order: - e %= self.__order - if e == 0 or self == INFINITY: - return INFINITY - result, q = INFINITY, self - while e: - if e & 1: - result += q - e, q = e >> 1, q.double() - return result - - """ - def __mul__(self, other): - #Multiply a point by an integer. - - def leftmost_bit( x ): - assert x > 0 - result = 1 - while result <= x: result = 2 * result - return result // 2 - - e = other - if self.__order: e = e % self.__order - if e == 0: return INFINITY - if self == INFINITY: return INFINITY - assert e > 0 - - # From X9.62 D.3.2: - - e3 = 3 * e - negative_self = Point( self.__curve, self.__x, -self.__y, self.__order ) - i = leftmost_bit( e3 ) // 2 - result = self - # print "Multiplying %s by %d (e3 = %d):" % ( self, other, e3 ) - while i > 1: - result = result.double() - if ( e3 & i ) != 0 and ( e & i ) == 0: result = result + self - if ( e3 & i ) == 0 and ( e & i ) != 0: result = result + negative_self - # print ". . . i = %d, result = %s" % ( i, result ) - i = i // 2 - - return result - """ - - def __rmul__(self, other): - """Multiply a point by an integer.""" - - return self * other - - def __str__(self): - if self == INFINITY: - return "infinity" - return "(%d, %d)" % (self.__x, self.__y) - - def inverse(self): - return Point(self.__curve, self.__x, -self.__y % self.__curve.p()) - - def double(self): - """Return a new point that is twice the old.""" - - if self == INFINITY: - return INFINITY - - # X9.62 B.3: - - p = self.__curve.p() - a = self.__curve.a() - - l = ((3 * self.__x * self.__x + a) * inverse_mod(2 * self.__y, p)) % p - - x3 = (l * l - 2 * self.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p - - return Point(self.__curve, x3, y3) - - def x(self): - return self.__x - - def y(self): - return self.__y - - def pair(self): - return (self.__x, self.__y) - - def curve(self): - return self.__curve - - def order(self): - return self.__order - - -# This one point is the Point At Infinity for all purposes: -INFINITY = Point(None, None, None) - - -def __main__(): - - class FailedTest(Exception): - pass - - def test_add(c, x1, y1, x2, y2, x3, y3): - """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - p1 = Point(c, x1, y1) - p2 = Point(c, x2, y2) - p3 = p1 + p2 - print("%s + %s = %s" % (p1, p2, p3)) - if p3.x() != x3 or p3.y() != y3: - raise FailedTest("Failure: should give (%d,%d)." % (x3, y3)) - else: - print(" Good.") - - def test_double(c, x1, y1, x3, y3): - """We expect that on curve c, 2*(x1,y1) = (x3, y3).""" - p1 = Point(c, x1, y1) - p3 = p1.double() - print("%s doubled = %s" % (p1, p3)) - if p3.x() != x3 or p3.y() != y3: - raise FailedTest("Failure: should give (%d,%d)." % (x3, y3)) - else: - print(" Good.") - - def test_double_infinity(c): - """We expect that on curve c, 2*INFINITY = INFINITY.""" - p1 = INFINITY - p3 = p1.double() - print("%s doubled = %s" % (p1, p3)) - if p3.x() != INFINITY.x() or p3.y() != INFINITY.y(): - raise FailedTest("Failure: should give (%d,%d)." % (INFINITY.x(), INFINITY.y())) - else: - print(" Good.") - - def test_multiply(c, x1, y1, m, x3, y3): - """We expect that on curve c, m*(x1,y1) = (x3,y3).""" - p1 = Point(c, x1, y1) - p3 = p1 * m - print("%s * %d = %s" % (p1, m, p3)) - if p3.x() != x3 or p3.y() != y3: - raise FailedTest("Failure: should give (%d,%d)." % (x3, y3)) - else: - print(" Good.") - - # A few tests from X9.62 B.3: - - c = CurveFp(23, 1, 1) - test_add(c, 3, 10, 9, 7, 17, 20) - test_double(c, 3, 10, 7, 12) - test_add(c, 3, 10, 3, 10, 7, 12) # (Should just invoke double.) - test_multiply(c, 3, 10, 2, 7, 12) - - test_double_infinity(c) - - # From X9.62 I.1 (p. 96): - - g = Point(c, 13, 7, 7) - - check = INFINITY - for i in range(7 + 1): - p = (i % 7) * g - print("%s * %d = %s, expected %s . . ." % (g, i, p, check)) - if p == check: - print(" Good.") - else: - raise FailedTest("Bad.") - check = check + g - - # NIST Curve P-192: - p = 6277101735386680763835789423207666416083908700390324961279 - r = 6277101735386680763835789423176059013767194773182842284081 - #s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L - c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 - b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 - Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 - Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 - - c192 = CurveFp(p, -3, b) - p192 = Point(c192, Gx, Gy, r) - - # Checking against some sample computations presented - # in X9.62: - - d = 651056770906015076056810763456358567190100156695615665659 - Q = d * p192 - if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5: - raise FailedTest("p192 * d came out wrong.") - else: - print("p192 * d came out right.") - - k = 6140507067065001063065065565667405560006161556565665656654 - R = k * p192 - if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ - or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: - raise FailedTest("k * p192 came out wrong.") - else: - print("k * p192 came out right.") - - u1 = 2563697409189434185194736134579731015366492496392189760599 - u2 = 6266643813348617967186477710235785849136406323338782220568 - temp = u1 * p192 + u2 * Q - if temp.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \ - or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835: - raise FailedTest("u1 * p192 + u2 * Q came out wrong.") - else: - print("u1 * p192 + u2 * Q came out right.") - - -if __name__ == "__main__": - __main__() diff --git a/basicswap/contrib/key.py b/basicswap/contrib/key.py deleted file mode 100644 index 912c0ca..0000000 --- a/basicswap/contrib/key.py +++ /dev/null @@ -1,386 +0,0 @@ -# Copyright (c) 2019 Pieter Wuille -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test-only secp256k1 elliptic curve implementation - -WARNING: This code is slow, uses bad randomness, does not properly protect -keys, and is trivially vulnerable to side channel attacks. Do not use for -anything but tests.""" -import random - -def modinv(a, n): - """Compute the modular inverse of a modulo n - - See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers. - """ - t1, t2 = 0, 1 - r1, r2 = n, a - while r2 != 0: - q = r1 // r2 - t1, t2 = t2, t1 - q * t2 - r1, r2 = r2, r1 - q * r2 - if r1 > 1: - return None - if t1 < 0: - t1 += n - return t1 - -def jacobi_symbol(n, k): - """Compute the Jacobi symbol of n modulo k - - See http://en.wikipedia.org/wiki/Jacobi_symbol - - For our application k is always prime, so this is the same as the Legendre symbol.""" - assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k" - n %= k - t = 0 - while n != 0: - while n & 1 == 0: - n >>= 1 - r = k & 7 - t ^= (r == 3 or r == 5) - n, k = k, n - t ^= (n & k & 3 == 3) - n = n % k - if k == 1: - return -1 if t else 1 - return 0 - -def modsqrt(a, p): - """Compute the square root of a modulo p when p % 4 = 3. - - The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm - - Limiting this function to only work for p % 4 = 3 means we don't need to - iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd - is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4) - - secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4. - """ - if p % 4 != 3: - raise NotImplementedError("modsqrt only implemented for p % 4 = 3") - sqrt = pow(a, (p + 1)//4, p) - if pow(sqrt, 2, p) == a % p: - return sqrt - return None - -class EllipticCurve: - def __init__(self, p, a, b): - """Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p).""" - self.p = p - self.a = a % p - self.b = b % p - - def affine(self, p1): - """Convert a Jacobian point tuple p1 to affine form, or None if at infinity. - - An affine point is represented as the Jacobian (x, y, 1)""" - x1, y1, z1 = p1 - if z1 == 0: - return None - inv = modinv(z1, self.p) - inv_2 = (inv**2) % self.p - inv_3 = (inv_2 * inv) % self.p - return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1) - - def negate(self, p1): - """Negate a Jacobian point tuple p1.""" - x1, y1, z1 = p1 - return (x1, (self.p - y1) % self.p, z1) - - def on_curve(self, p1): - """Determine whether a Jacobian tuple p is on the curve (and not infinity)""" - x1, y1, z1 = p1 - z2 = pow(z1, 2, self.p) - z4 = pow(z2, 2, self.p) - return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0 - - def is_x_coord(self, x): - """Test whether x is a valid X coordinate on the curve.""" - x_3 = pow(x, 3, self.p) - return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1 - - def lift_x(self, x): - """Given an X coordinate on the curve, return a corresponding affine point.""" - x_3 = pow(x, 3, self.p) - v = x_3 + self.a * x + self.b - y = modsqrt(v, self.p) - if y is None: - return None - return (x, y, 1) - - def double(self, p1): - """Double a Jacobian tuple p1 - - See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling""" - x1, y1, z1 = p1 - if z1 == 0: - return (0, 1, 0) - y1_2 = (y1**2) % self.p - y1_4 = (y1_2**2) % self.p - x1_2 = (x1**2) % self.p - s = (4*x1*y1_2) % self.p - m = 3*x1_2 - if self.a: - m += self.a * pow(z1, 4, self.p) - m = m % self.p - x2 = (m**2 - 2*s) % self.p - y2 = (m*(s - x2) - 8*y1_4) % self.p - z2 = (2*y1*z1) % self.p - return (x2, y2, z2) - - def add_mixed(self, p1, p2): - """Add a Jacobian tuple p1 and an affine tuple p2 - - See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)""" - x1, y1, z1 = p1 - x2, y2, z2 = p2 - assert(z2 == 1) - # Adding to the point at infinity is a no-op - if z1 == 0: - return p2 - z1_2 = (z1**2) % self.p - z1_3 = (z1_2 * z1) % self.p - u2 = (x2 * z1_2) % self.p - s2 = (y2 * z1_3) % self.p - if x1 == u2: - if (y1 != s2): - # p1 and p2 are inverses. Return the point at infinity. - return (0, 1, 0) - # p1 == p2. The formulas below fail when the two points are equal. - return self.double(p1) - h = u2 - x1 - r = s2 - y1 - h_2 = (h**2) % self.p - h_3 = (h_2 * h) % self.p - u1_h_2 = (x1 * h_2) % self.p - x3 = (r**2 - h_3 - 2*u1_h_2) % self.p - y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p - z3 = (h*z1) % self.p - return (x3, y3, z3) - - def add(self, p1, p2): - """Add two Jacobian tuples p1 and p2 - - See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition""" - x1, y1, z1 = p1 - x2, y2, z2 = p2 - # Adding the point at infinity is a no-op - if z1 == 0: - return p2 - if z2 == 0: - return p1 - # Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1 - if z1 == 1: - return self.add_mixed(p2, p1) - if z2 == 1: - return self.add_mixed(p1, p2) - z1_2 = (z1**2) % self.p - z1_3 = (z1_2 * z1) % self.p - z2_2 = (z2**2) % self.p - z2_3 = (z2_2 * z2) % self.p - u1 = (x1 * z2_2) % self.p - u2 = (x2 * z1_2) % self.p - s1 = (y1 * z2_3) % self.p - s2 = (y2 * z1_3) % self.p - if u1 == u2: - if (s1 != s2): - # p1 and p2 are inverses. Return the point at infinity. - return (0, 1, 0) - # p1 == p2. The formulas below fail when the two points are equal. - return self.double(p1) - h = u2 - u1 - r = s2 - s1 - h_2 = (h**2) % self.p - h_3 = (h_2 * h) % self.p - u1_h_2 = (u1 * h_2) % self.p - x3 = (r**2 - h_3 - 2*u1_h_2) % self.p - y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p - z3 = (h*z1*z2) % self.p - return (x3, y3, z3) - - def mul(self, ps): - """Compute a (multi) point multiplication - - ps is a list of (Jacobian tuple, scalar) pairs. - """ - r = (0, 1, 0) - for i in range(255, -1, -1): - r = self.double(r) - for (p, n) in ps: - if ((n >> i) & 1): - r = self.add(r, p) - return r - -SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7) -SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1) -SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 -SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2 - -class ECPubKey(): - """A secp256k1 public key""" - - def __init__(self): - """Construct an uninitialized public key""" - self.valid = False - - def set(self, data): - """Construct a public key from a serialization in compressed or uncompressed format""" - if (len(data) == 65 and data[0] == 0x04): - p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1) - self.valid = SECP256K1.on_curve(p) - if self.valid: - self.p = p - self.compressed = False - elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)): - x = int.from_bytes(data[1:33], 'big') - if SECP256K1.is_x_coord(x): - p = SECP256K1.lift_x(x) - # if the oddness of the y co-ord isn't correct, find the other - # valid y - if (p[1] & 1) != (data[0] & 1): - p = SECP256K1.negate(p) - self.p = p - self.valid = True - self.compressed = True - else: - self.valid = False - else: - self.valid = False - - @property - def is_compressed(self): - return self.compressed - - @property - def is_valid(self): - return self.valid - - def get_bytes(self): - assert(self.valid) - p = SECP256K1.affine(self.p) - if p is None: - return None - if self.compressed: - return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big') - else: - return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big') - - def verify_ecdsa(self, sig, msg, low_s=True): - """Verify a strictly DER-encoded ECDSA signature against this pubkey. - - See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the - ECDSA verifier algorithm""" - assert(self.valid) - - # Extract r and s from the DER formatted signature. Return false for - # any DER encoding errors. - if (sig[1] + 2 != len(sig)): - return False - if (len(sig) < 4): - return False - if (sig[0] != 0x30): - return False - if (sig[2] != 0x02): - return False - rlen = sig[3] - if (len(sig) < 6 + rlen): - return False - if rlen < 1 or rlen > 33: - return False - if sig[4] >= 0x80: - return False - if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)): - return False - r = int.from_bytes(sig[4:4+rlen], 'big') - if (sig[4+rlen] != 0x02): - return False - slen = sig[5+rlen] - if slen < 1 or slen > 33: - return False - if (len(sig) != 6 + rlen + slen): - return False - if sig[6+rlen] >= 0x80: - return False - if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)): - return False - s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big') - - # Verify that r and s are within the group order - if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER: - return False - if low_s and s >= SECP256K1_ORDER_HALF: - return False - z = int.from_bytes(msg, 'big') - - # Run verifier algorithm on r, s - w = modinv(s, SECP256K1_ORDER) - u1 = z*w % SECP256K1_ORDER - u2 = r*w % SECP256K1_ORDER - R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)])) - if R is None or R[0] != r: - return False - return True - -class ECKey(): - """A secp256k1 private key""" - - def __init__(self): - self.valid = False - - def set(self, secret, compressed): - """Construct a private key object with given 32-byte secret and compressed flag.""" - assert(len(secret) == 32) - secret = int.from_bytes(secret, 'big') - self.valid = (secret > 0 and secret < SECP256K1_ORDER) - if self.valid: - self.secret = secret - self.compressed = compressed - - def generate(self, compressed=True): - """Generate a random private key (compressed or uncompressed).""" - self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed) - - def get_bytes(self): - """Retrieve the 32-byte representation of this key.""" - assert(self.valid) - return self.secret.to_bytes(32, 'big') - - @property - def is_valid(self): - return self.valid - - @property - def is_compressed(self): - return self.compressed - - def get_pubkey(self): - """Compute an ECPubKey object for this secret key.""" - assert(self.valid) - ret = ECPubKey() - p = SECP256K1.mul([(SECP256K1_G, self.secret)]) - ret.p = p - ret.valid = True - ret.compressed = self.compressed - return ret - - def sign_ecdsa(self, msg, low_s=True): - """Construct a DER-encoded ECDSA signature with this key. - - See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the - ECDSA signer algorithm.""" - assert(self.valid) - z = int.from_bytes(msg, 'big') - # Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation) - k = random.randrange(1, SECP256K1_ORDER) - R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)])) - r = R[0] % SECP256K1_ORDER - s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER - if low_s and s > SECP256K1_ORDER_HALF: - s = SECP256K1_ORDER - s - # Represent in DER format. The byte representations of r and s have - # length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33 - # bytes). - rb = r.to_bytes((r.bit_length() + 8) // 8, 'big') - sb = s.to_bytes((s.bit_length() + 8) // 8, 'big') - return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb diff --git a/basicswap/contrib/test_framework/key.py b/basicswap/contrib/test_framework/key.py deleted file mode 100644 index 55e2de1..0000000 --- a/basicswap/contrib/test_framework/key.py +++ /dev/null @@ -1,393 +0,0 @@ -# Copyright (c) 2019 Pieter Wuille -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test-only secp256k1 elliptic curve implementation - -WARNING: This code is slow, uses bad randomness, does not properly protect -keys, and is trivially vulnerable to side channel attacks. Do not use for -anything but tests.""" -import random - -def modinv(a, n): - """Compute the modular inverse of a modulo n - - See https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm#Modular_integers. - """ - t1, t2 = 0, 1 - r1, r2 = n, a - while r2 != 0: - q = r1 // r2 - t1, t2 = t2, t1 - q * t2 - r1, r2 = r2, r1 - q * r2 - if r1 > 1: - return None - if t1 < 0: - t1 += n - return t1 - -def jacobi_symbol(n, k): - """Compute the Jacobi symbol of n modulo k - - See http://en.wikipedia.org/wiki/Jacobi_symbol - - For our application k is always prime, so this is the same as the Legendre symbol.""" - assert k > 0 and k & 1, "jacobi symbol is only defined for positive odd k" - n %= k - t = 0 - while n != 0: - while n & 1 == 0: - n >>= 1 - r = k & 7 - t ^= (r == 3 or r == 5) - n, k = k, n - t ^= (n & k & 3 == 3) - n = n % k - if k == 1: - return -1 if t else 1 - return 0 - -def modsqrt(a, p): - """Compute the square root of a modulo p when p % 4 = 3. - - The Tonelli-Shanks algorithm can be used. See https://en.wikipedia.org/wiki/Tonelli-Shanks_algorithm - - Limiting this function to only work for p % 4 = 3 means we don't need to - iterate through the loop. The highest n such that p - 1 = 2^n Q with Q odd - is n = 1. Therefore Q = (p-1)/2 and sqrt = a^((Q+1)/2) = a^((p+1)/4) - - secp256k1's is defined over field of size 2**256 - 2**32 - 977, which is 3 mod 4. - """ - if p % 4 != 3: - raise NotImplementedError("modsqrt only implemented for p % 4 = 3") - sqrt = pow(a, (p + 1)//4, p) - if pow(sqrt, 2, p) == a % p: - return sqrt - return None - -class EllipticCurve: - def __init__(self, p, a, b): - """Initialize elliptic curve y^2 = x^3 + a*x + b over GF(p).""" - self.p = p - self.a = a % p - self.b = b % p - - def affine(self, p1): - """Convert a Jacobian point tuple p1 to affine form, or None if at infinity. - - An affine point is represented as the Jacobian (x, y, 1)""" - x1, y1, z1 = p1 - if z1 == 0: - return None - inv = modinv(z1, self.p) - inv_2 = (inv**2) % self.p - inv_3 = (inv_2 * inv) % self.p - return ((inv_2 * x1) % self.p, (inv_3 * y1) % self.p, 1) - - def negate(self, p1): - """Negate a Jacobian point tuple p1.""" - x1, y1, z1 = p1 - return (x1, (self.p - y1) % self.p, z1) - - def on_curve(self, p1): - """Determine whether a Jacobian tuple p is on the curve (and not infinity)""" - x1, y1, z1 = p1 - z2 = pow(z1, 2, self.p) - z4 = pow(z2, 2, self.p) - return z1 != 0 and (pow(x1, 3, self.p) + self.a * x1 * z4 + self.b * z2 * z4 - pow(y1, 2, self.p)) % self.p == 0 - - def is_x_coord(self, x): - """Test whether x is a valid X coordinate on the curve.""" - x_3 = pow(x, 3, self.p) - return jacobi_symbol(x_3 + self.a * x + self.b, self.p) != -1 - - def lift_x(self, x): - """Given an X coordinate on the curve, return a corresponding affine point.""" - x_3 = pow(x, 3, self.p) - v = x_3 + self.a * x + self.b - y = modsqrt(v, self.p) - if y is None: - return None - return (x, y, 1) - - def double(self, p1): - """Double a Jacobian tuple p1 - - See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Doubling""" - x1, y1, z1 = p1 - if z1 == 0: - return (0, 1, 0) - y1_2 = (y1**2) % self.p - y1_4 = (y1_2**2) % self.p - x1_2 = (x1**2) % self.p - s = (4*x1*y1_2) % self.p - m = 3*x1_2 - if self.a: - m += self.a * pow(z1, 4, self.p) - m = m % self.p - x2 = (m**2 - 2*s) % self.p - y2 = (m*(s - x2) - 8*y1_4) % self.p - z2 = (2*y1*z1) % self.p - return (x2, y2, z2) - - def add_mixed(self, p1, p2): - """Add a Jacobian tuple p1 and an affine tuple p2 - - See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition (with affine point)""" - x1, y1, z1 = p1 - x2, y2, z2 = p2 - assert(z2 == 1) - # Adding to the point at infinity is a no-op - if z1 == 0: - return p2 - z1_2 = (z1**2) % self.p - z1_3 = (z1_2 * z1) % self.p - u2 = (x2 * z1_2) % self.p - s2 = (y2 * z1_3) % self.p - if x1 == u2: - if (y1 != s2): - # p1 and p2 are inverses. Return the point at infinity. - return (0, 1, 0) - # p1 == p2. The formulas below fail when the two points are equal. - return self.double(p1) - h = u2 - x1 - r = s2 - y1 - h_2 = (h**2) % self.p - h_3 = (h_2 * h) % self.p - u1_h_2 = (x1 * h_2) % self.p - x3 = (r**2 - h_3 - 2*u1_h_2) % self.p - y3 = (r*(u1_h_2 - x3) - y1*h_3) % self.p - z3 = (h*z1) % self.p - return (x3, y3, z3) - - def add(self, p1, p2): - """Add two Jacobian tuples p1 and p2 - - See https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates - Point Addition""" - x1, y1, z1 = p1 - x2, y2, z2 = p2 - # Adding the point at infinity is a no-op - if z1 == 0: - return p2 - if z2 == 0: - return p1 - # Adding an Affine to a Jacobian is more efficient since we save field multiplications and squarings when z = 1 - if z1 == 1: - return self.add_mixed(p2, p1) - if z2 == 1: - return self.add_mixed(p1, p2) - z1_2 = (z1**2) % self.p - z1_3 = (z1_2 * z1) % self.p - z2_2 = (z2**2) % self.p - z2_3 = (z2_2 * z2) % self.p - u1 = (x1 * z2_2) % self.p - u2 = (x2 * z1_2) % self.p - s1 = (y1 * z2_3) % self.p - s2 = (y2 * z1_3) % self.p - if u1 == u2: - if (s1 != s2): - # p1 and p2 are inverses. Return the point at infinity. - return (0, 1, 0) - # p1 == p2. The formulas below fail when the two points are equal. - return self.double(p1) - h = u2 - u1 - r = s2 - s1 - h_2 = (h**2) % self.p - h_3 = (h_2 * h) % self.p - u1_h_2 = (u1 * h_2) % self.p - x3 = (r**2 - h_3 - 2*u1_h_2) % self.p - y3 = (r*(u1_h_2 - x3) - s1*h_3) % self.p - z3 = (h*z1*z2) % self.p - return (x3, y3, z3) - - def mul(self, ps): - """Compute a (multi) point multiplication - - ps is a list of (Jacobian tuple, scalar) pairs. - """ - r = (0, 1, 0) - for i in range(255, -1, -1): - r = self.double(r) - for (p, n) in ps: - if ((n >> i) & 1): - r = self.add(r, p) - return r - -SECP256K1 = EllipticCurve(2**256 - 2**32 - 977, 0, 7) -SECP256K1_G = (0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, 1) -SECP256K1_ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 -SECP256K1_ORDER_HALF = SECP256K1_ORDER // 2 - -class ECPubKey(): - """A secp256k1 public key""" - - def __init__(self): - """Construct an uninitialized public key""" - self.valid = False - - def set_int(self, x, y): - p = (x, y, 1) - self.valid = SECP256K1.on_curve(p) - if self.valid: - self.p = p - self.compressed = False - - def set(self, data): - """Construct a public key from a serialization in compressed or uncompressed format""" - if (len(data) == 65 and data[0] == 0x04): - p = (int.from_bytes(data[1:33], 'big'), int.from_bytes(data[33:65], 'big'), 1) - self.valid = SECP256K1.on_curve(p) - if self.valid: - self.p = p - self.compressed = False - elif (len(data) == 33 and (data[0] == 0x02 or data[0] == 0x03)): - x = int.from_bytes(data[1:33], 'big') - if SECP256K1.is_x_coord(x): - p = SECP256K1.lift_x(x) - # if the oddness of the y co-ord isn't correct, find the other - # valid y - if (p[1] & 1) != (data[0] & 1): - p = SECP256K1.negate(p) - self.p = p - self.valid = True - self.compressed = True - else: - self.valid = False - else: - self.valid = False - - @property - def is_compressed(self): - return self.compressed - - @property - def is_valid(self): - return self.valid - - def get_bytes(self): - assert(self.valid) - p = SECP256K1.affine(self.p) - if p is None: - return None - if self.compressed: - return bytes([0x02 + (p[1] & 1)]) + p[0].to_bytes(32, 'big') - else: - return bytes([0x04]) + p[0].to_bytes(32, 'big') + p[1].to_bytes(32, 'big') - - def verify_ecdsa(self, sig, msg, low_s=True): - """Verify a strictly DER-encoded ECDSA signature against this pubkey. - - See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the - ECDSA verifier algorithm""" - assert(self.valid) - - # Extract r and s from the DER formatted signature. Return false for - # any DER encoding errors. - if (sig[1] + 2 != len(sig)): - return False - if (len(sig) < 4): - return False - if (sig[0] != 0x30): - return False - if (sig[2] != 0x02): - return False - rlen = sig[3] - if (len(sig) < 6 + rlen): - return False - if rlen < 1 or rlen > 33: - return False - if sig[4] >= 0x80: - return False - if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)): - return False - r = int.from_bytes(sig[4:4+rlen], 'big') - if (sig[4+rlen] != 0x02): - return False - slen = sig[5+rlen] - if slen < 1 or slen > 33: - return False - if (len(sig) != 6 + rlen + slen): - return False - if sig[6+rlen] >= 0x80: - return False - if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)): - return False - s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big') - - # Verify that r and s are within the group order - if r < 1 or s < 1 or r >= SECP256K1_ORDER or s >= SECP256K1_ORDER: - return False - if low_s and s >= SECP256K1_ORDER_HALF: - return False - z = int.from_bytes(msg, 'big') - - # Run verifier algorithm on r, s - w = modinv(s, SECP256K1_ORDER) - u1 = z*w % SECP256K1_ORDER - u2 = r*w % SECP256K1_ORDER - R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, u1), (self.p, u2)])) - if R is None or R[0] != r: - return False - return True - -class ECKey(): - """A secp256k1 private key""" - - def __init__(self): - self.valid = False - - def set(self, secret, compressed): - """Construct a private key object with given 32-byte secret and compressed flag.""" - assert(len(secret) == 32) - secret = int.from_bytes(secret, 'big') - self.valid = (secret > 0 and secret < SECP256K1_ORDER) - if self.valid: - self.secret = secret - self.compressed = compressed - - def generate(self, compressed=True): - """Generate a random private key (compressed or uncompressed).""" - self.set(random.randrange(1, SECP256K1_ORDER).to_bytes(32, 'big'), compressed) - - def get_bytes(self): - """Retrieve the 32-byte representation of this key.""" - assert(self.valid) - return self.secret.to_bytes(32, 'big') - - @property - def is_valid(self): - return self.valid - - @property - def is_compressed(self): - return self.compressed - - def get_pubkey(self): - """Compute an ECPubKey object for this secret key.""" - assert(self.valid) - ret = ECPubKey() - p = SECP256K1.mul([(SECP256K1_G, self.secret)]) - ret.p = p - ret.valid = True - ret.compressed = self.compressed - return ret - - def sign_ecdsa(self, msg, low_s=True): - """Construct a DER-encoded ECDSA signature with this key. - - See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the - ECDSA signer algorithm.""" - assert(self.valid) - z = int.from_bytes(msg, 'big') - # Note: no RFC6979, but a simple random nonce (some tests rely on distinct transactions for the same operation) - k = random.randrange(1, SECP256K1_ORDER) - R = SECP256K1.affine(SECP256K1.mul([(SECP256K1_G, k)])) - r = R[0] % SECP256K1_ORDER - s = (modinv(k, SECP256K1_ORDER) * (z + self.secret * r)) % SECP256K1_ORDER - if low_s and s > SECP256K1_ORDER_HALF: - s = SECP256K1_ORDER - s - # Represent in DER format. The byte representations of r and s have - # length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33 - # bytes). - rb = r.to_bytes((r.bit_length() + 8) // 8, 'big') - sb = s.to_bytes((s.bit_length() + 8) // 8, 'big') - return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb diff --git a/basicswap/ed25519_fast_util.py b/basicswap/ed25519_fast_util.py deleted file mode 100644 index d9b2d01..0000000 --- a/basicswap/ed25519_fast_util.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- - -import secrets -import hashlib -import basicswap.contrib.ed25519_fast as edf - - -def get_secret(): - return 9 + secrets.randbelow(edf.l - 9) - - -def encodepoint(P): - zi = edf.inv(P[2]) - x = (P[0] * zi) % edf.q - y = (P[1] * zi) % edf.q - y += (x & 1) << 255 - return y.to_bytes(32, byteorder="little") - - -def hashToEd25519(bytes_in): - hashed = hashlib.sha256(bytes_in).digest() - for i in range(1000): - h255 = bytearray(hashed) - x_sign = 0 if h255[31] & 0x80 == 0 else 1 - h255[31] &= 0x7F # Clear top bit - y = int.from_bytes(h255, byteorder="little") - x = edf.xrecover(y, x_sign) - if x == 0 and y == 1: # Skip infinity point - continue - - P = [x, y, 1, (x * y) % edf.q] - # Keep trying until the point is in the correct subgroup - if edf.isoncurve(P) and edf.is_identity(edf.scalarmult(P, edf.l)): - return P - hashed = hashlib.sha256(hashed).digest() - raise ValueError("hashToEd25519 failed") diff --git a/basicswap/interface/btc.py b/basicswap/interface/btc.py index 8cc25cc..57211df 100644 --- a/basicswap/interface/btc.py +++ b/basicswap/interface/btc.py @@ -30,10 +30,6 @@ from basicswap.util import ( i2b, i2h, ) -from basicswap.util.ecc import ( - pointToCPK, - CPKToPoint, -) from basicswap.util.extkey import ExtKeyPair from basicswap.util.script import ( SerialiseNumCompact, @@ -744,18 +740,12 @@ class BTCInterface(Secp256k1Interface): wif_prefix = self.chainparams_network()["key_prefix"] return toWIF(wif_prefix, key_bytes) - def encodePubkey(self, pk: bytes) -> bytes: - return pointToCPK(pk) - def encodeSegwitAddress(self, key_hash: bytes) -> str: return segwit_addr.encode(self.chainparams_network()["hrp"], 0, key_hash) def decodeSegwitAddress(self, addr: str) -> bytes: return bytes(segwit_addr.decode(self.chainparams_network()["hrp"], addr)[1]) - def decodePubkey(self, pke): - return CPKToPoint(pke) - def decodeKey(self, k: str) -> bytes: return decodeWif(k) @@ -790,17 +780,16 @@ class BTCInterface(Secp256k1Interface): return funded_tx def genScriptLockRefundTxScript(self, Kal, Kaf, csv_val) -> CScript: - - Kal_enc = Kal if len(Kal) == 33 else self.encodePubkey(Kal) - Kaf_enc = Kaf if len(Kaf) == 33 else self.encodePubkey(Kaf) + assert len(Kal) == 33 + assert len(Kaf) == 33 # fmt: off return CScript([ CScriptOp(OP_IF), - 2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG), + 2, Kal, Kaf, 2, CScriptOp(OP_CHECKMULTISIG), CScriptOp(OP_ELSE), csv_val, CScriptOp(OP_CHECKSEQUENCEVERIFY), CScriptOp(OP_DROP), - Kaf_enc, CScriptOp(OP_CHECKSIG), + Kaf, CScriptOp(OP_CHECKSIG), CScriptOp(OP_ENDIF)]) # fmt: on diff --git a/basicswap/interface/dcr/dcr.py b/basicswap/interface/dcr/dcr.py index 7aabc51..b6ea07f 100644 --- a/basicswap/interface/dcr/dcr.py +++ b/basicswap/interface/dcr/dcr.py @@ -1087,22 +1087,21 @@ class DCRInterface(Secp256k1Interface): return self.fundTx(tx_bytes, feerate) def genScriptLockRefundTxScript(self, Kal, Kaf, csv_val) -> bytes: - - Kal_enc = Kal if len(Kal) == 33 else self.encodePubkey(Kal) - Kaf_enc = Kaf if len(Kaf) == 33 else self.encodePubkey(Kaf) + assert len(Kal) == 33 + assert len(Kaf) == 33 script = bytearray() script += bytes((OP_IF,)) push_script_data(script, bytes((2,))) - push_script_data(script, Kal_enc) - push_script_data(script, Kaf_enc) + push_script_data(script, Kal) + push_script_data(script, Kaf) push_script_data(script, bytes((2,))) script += bytes((OP_CHECKMULTISIG,)) script += bytes((OP_ELSE,)) script += CScriptNum.encode(CScriptNum(csv_val)) script += bytes((OP_CHECKSEQUENCEVERIFY,)) script += bytes((OP_DROP,)) - push_script_data(script, Kaf_enc) + push_script_data(script, Kaf) script += bytes((OP_CHECKSIG,)) script += bytes((OP_ENDIF,)) diff --git a/basicswap/interface/xmr.py b/basicswap/interface/xmr.py index 2a8664a..2a98133 100644 --- a/basicswap/interface/xmr.py +++ b/basicswap/interface/xmr.py @@ -8,10 +8,9 @@ import logging import os +import secrets import time -import basicswap.contrib.ed25519_fast as edf -import basicswap.ed25519_fast_util as edu import basicswap.util_xmr as xmr_util from coincurve.ed25519 import ( ed25519_add, @@ -36,6 +35,9 @@ from basicswap.chainparams import XMR_COIN, Coins from basicswap.interface.base import CoinInterface +ed25519_l = 2**252 + 27742317777372353535851937790883648493 + + class XMRInterface(CoinInterface): @staticmethod def curve_type(): @@ -400,10 +402,7 @@ class XMRInterface(CoinInterface): def getNewRandomKey(self) -> bytes: # Note: Returned bytes are in big endian order - return i2b(edu.get_secret()) - - def pubkey(self, key: bytes) -> bytes: - return edf.scalarmult_B(key) + return i2b(9 + secrets.randbelow(ed25519_l - 9)) def encodeKey(self, vk: bytes) -> str: return vk[::-1].hex() @@ -411,12 +410,6 @@ class XMRInterface(CoinInterface): def decodeKey(self, k_hex: str) -> bytes: return bytes.fromhex(k_hex)[::-1] - def encodePubkey(self, pk: bytes) -> str: - return edu.encodepoint(pk) - - def decodePubkey(self, pke): - return edf.decodepoint(pke) - def getPubkey(self, privkey): return ed25519_get_pubkey(privkey) @@ -427,7 +420,7 @@ class XMRInterface(CoinInterface): def verifyKey(self, k: int) -> bool: i = b2i(k) - return i < edf.l and i > 8 + return i < ed25519_l and i > 8 def verifyPubkey(self, pubkey_bytes): # Calls ed25519_decode_check_point() in secp256k1 diff --git a/basicswap/protocols/xmr_swap_1.py b/basicswap/protocols/xmr_swap_1.py index b6eaa8b..733aea9 100644 --- a/basicswap/protocols/xmr_swap_1.py +++ b/basicswap/protocols/xmr_swap_1.py @@ -215,10 +215,10 @@ class XmrSwapInterface(ProtocolInterface): if hasattr(ci, "genScriptLockTxScript") and callable(ci.genScriptLockTxScript): return ci.genScriptLockTxScript(ci, Kal, Kaf, **kwargs) - Kal_enc = Kal if len(Kal) == 33 else ci.encodePubkey(Kal) - Kaf_enc = Kaf if len(Kaf) == 33 else ci.encodePubkey(Kaf) + assert len(Kal) == 33 + assert len(Kaf) == 33 - return CScript([2, Kal_enc, Kaf_enc, 2, CScriptOp(OP_CHECKMULTISIG)]) + return CScript([2, Kal, Kaf, 2, CScriptOp(OP_CHECKMULTISIG)]) def getFundedInitiateTxTemplate(self, ci, amount: int, sub_fee: bool) -> bytes: addr_to = self.getMockAddrTo(ci) diff --git a/basicswap/util/ecc.py b/basicswap/util/ecc.py index fa72d62..177a60c 100644 --- a/basicswap/util/ecc.py +++ b/basicswap/util/ecc.py @@ -2,10 +2,8 @@ # -*- coding: utf-8 -*- import os -import hashlib import secrets -from basicswap.contrib.ellipticcurve import CurveFp, Point, INFINITY, jacobi_symbol from . import i2b @@ -29,19 +27,6 @@ ep = ECCParameters( ) -curve_secp256k1 = CurveFp(ep.p, ep.a, ep.b) -G = Point(curve_secp256k1, ep.Gx, ep.Gy, ep.o) -SECP256K1_ORDER_HALF = ep.o // 2 - - -def ToDER(P) -> bytes: - return ( - bytes((4,)) - + int(P.x()).to_bytes(32, byteorder="big") - + int(P.y()).to_bytes(32, byteorder="big") - ) - - def getSecretBytes() -> bytes: i = 1 + secrets.randbelow(ep.o - 1) return i2b(i) @@ -67,145 +52,3 @@ def getInsecureInt() -> int: s_test = int.from_bytes(s, byteorder="big") if s_test > 1 and s_test < ep.o: return s_test - - -def powMod(x, y, z) -> int: - # Calculate (x ** y) % z efficiently. - number = 1 - while y: - if y & 1: - number = number * x % z - y >>= 1 # y //= 2 - - x = x * x % z - return number - - -def ExpandPoint(xb, sign): - x = int.from_bytes(xb, byteorder="big") - a = (powMod(x, 3, ep.p) + 7) % ep.p - y = powMod(a, (ep.p + 1) // 4, ep.p) - - if sign: - y = ep.p - y - return Point(curve_secp256k1, x, y, ep.o) - - -def CPKToPoint(cpk): - y_parity = cpk[0] - 2 - - x = int.from_bytes(cpk[1:], byteorder="big") - a = (powMod(x, 3, ep.p) + 7) % ep.p - y = powMod(a, (ep.p + 1) // 4, ep.p) - - if y % 2 != y_parity: - y = ep.p - y - - return Point(curve_secp256k1, x, y, ep.o) - - -def pointToCPK2(point, ind=0x09): - # The function is_square(x), where x is an integer, returns whether or not x is a quadratic residue modulo p. Since p is prime, it is equivalent to the Legendre symbol (x / p) = x(p-1)/2 mod p being equal to 1[8]. - ind = bytes((ind ^ (1 if jacobi_symbol(point.y(), ep.p) == 1 else 0),)) - return ind + point.x().to_bytes(32, byteorder="big") - - -def pointToCPK(point): - - y = point.y().to_bytes(32, byteorder="big") - ind = bytes((0x03,)) if y[31] % 2 else bytes((0x02,)) - - cpk = ind + point.x().to_bytes(32, byteorder="big") - return cpk - - -def secretToCPK(secret): - secretInt = ( - secret if isinstance(secret, int) else int.from_bytes(secret, byteorder="big") - ) - - R = G * secretInt - - Y = R.y().to_bytes(32, byteorder="big") - ind = bytes((0x03,)) if Y[31] % 2 else bytes((0x02,)) - - pubkey = ind + R.x().to_bytes(32, byteorder="big") - - return pubkey - - -def getKeypair(): - secretBytes = getSecretBytes() - return secretBytes, secretToCPK(secretBytes) - - -def hashToCurve(pubkey): - - xBytes = hashlib.sha256(pubkey).digest() - x = int.from_bytes(xBytes, byteorder="big") - - for k in range(0, 100): - # get matching y element for point - y_parity = 0 # always pick 0, - a = (powMod(x, 3, ep.p) + 7) % ep.p - y = powMod(a, (ep.p + 1) // 4, ep.p) - - # print("before parity %x" % (y)) - if y % 2 != y_parity: - y = ep.p - y - - # If x is always mod P, can R ever not be on the curve? - try: - R = Point(curve_secp256k1, x, y, ep.o) - except Exception: - x = (x + 1) % ep.p # % P? - continue - - if ( - R == INFINITY or R * ep.o != INFINITY - ): # is R * O != INFINITY check necessary? Validation of Elliptic Curve Public Keys says no if cofactor = 1 - x = (x + 1) % ep.p # % P? - continue - return R - - raise ValueError("hashToCurve failed for 100 tries") - - -def hash256(inb): - return hashlib.sha256(inb).digest() - - -def testEccUtils(): - print("testEccUtils()") - - G_enc = ToDER(G) - assert ( - G_enc.hex() - == "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" - ) - - G_enc = pointToCPK(G) - assert ( - G_enc.hex() - == "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" - ) - G_dec = CPKToPoint(G_enc) - assert G_dec == G - - G_enc = pointToCPK2(G) - assert ( - G_enc.hex() - == "0879be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798" - ) - - H = hashToCurve(ToDER(G)) - assert ( - pointToCPK(H).hex() - == "0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" - ) - - print("Passed.") - - -if __name__ == "__main__": - testEccUtils() diff --git a/tests/basicswap/extended/test_dash.py b/tests/basicswap/extended/test_dash.py index 1ba2f58..72f3b98 100644 --- a/tests/basicswap/extended/test_dash.py +++ b/tests/basicswap/extended/test_dash.py @@ -22,6 +22,8 @@ import threading import time import unittest +from coincurve.keys import PrivateKey + import basicswap.config as cfg from basicswap.basicswap import ( BasicSwap, @@ -40,9 +42,6 @@ from basicswap.basicswap_util import ( from basicswap.util.address import ( toWIF, ) -from basicswap.contrib.key import ( - ECKey, -) from basicswap.http_server import ( HttpThread, ) @@ -291,10 +290,9 @@ class Test(unittest.TestCase): def setUpClass(cls): super(Test, cls).setUpClass() - eckey = ECKey() - eckey.generate() - cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes()) - cls.network_pubkey = eckey.get_pubkey().get_bytes().hex() + k = PrivateKey() + cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret) + cls.network_pubkey = k.public_key.format().hex() if os.path.isdir(cfg.TEST_DATADIRS): logging.info("Removing " + cfg.TEST_DATADIRS) diff --git a/tests/basicswap/extended/test_network.py b/tests/basicswap/extended/test_network.py index d1a6334..5642e5e 100644 --- a/tests/basicswap/extended/test_network.py +++ b/tests/basicswap/extended/test_network.py @@ -17,6 +17,8 @@ import time import traceback import unittest +from coincurve.keys import PrivateKey + import basicswap.config as cfg from basicswap.basicswap import ( BasicSwap, @@ -33,9 +35,6 @@ from basicswap.util.address import ( from basicswap.rpc import ( callrpc, ) -from basicswap.contrib.key import ( - ECKey, -) from basicswap.http_server import ( HttpThread, ) @@ -312,10 +311,9 @@ class Test(unittest.TestCase): ) logging.info("Preparing swap clients.") - eckey = ECKey() - eckey.generate() - cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes()) - cls.network_pubkey = eckey.get_pubkey().get_bytes().hex() + k = PrivateKey() + cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret) + cls.network_pubkey = k.public_key.format().hex() for i in range(NUM_NODES): prepare_swapclient_dir(TEST_DIR, i, cls.network_key, cls.network_pubkey) diff --git a/tests/basicswap/extended/test_pivx.py b/tests/basicswap/extended/test_pivx.py index e12b918..cd87c82 100644 --- a/tests/basicswap/extended/test_pivx.py +++ b/tests/basicswap/extended/test_pivx.py @@ -22,6 +22,8 @@ import threading import time import unittest +from coincurve.keys import PrivateKey + import basicswap.config as cfg from basicswap.basicswap import ( BasicSwap, @@ -40,9 +42,6 @@ from basicswap.basicswap_util import ( from basicswap.util.address import ( toWIF, ) -from basicswap.contrib.key import ( - ECKey, -) from basicswap.http_server import ( HttpThread, ) @@ -296,10 +295,9 @@ class Test(unittest.TestCase): def setUpClass(cls): super(Test, cls).setUpClass() - eckey = ECKey() - eckey.generate() - cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes()) - cls.network_pubkey = eckey.get_pubkey().get_bytes().hex() + k = PrivateKey() + cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret) + cls.network_pubkey = k.public_key.format().hex() if os.path.isdir(cfg.TEST_DATADIRS): logging.info("Removing " + cfg.TEST_DATADIRS) diff --git a/tests/basicswap/test_other.py b/tests/basicswap/test_other.py index c8f57cd..1dfc7b5 100644 --- a/tests/basicswap/test_other.py +++ b/tests/basicswap/test_other.py @@ -13,9 +13,6 @@ import secrets import threading import unittest -import basicswap.contrib.ed25519_fast as edf -import basicswap.ed25519_fast_util as edu - from coincurve.ed25519 import ed25519_get_pubkey from coincurve.ecdsaotves import ( ecdsaotves_enc_sign, @@ -27,7 +24,7 @@ from coincurve.keys import PrivateKey from basicswap.contrib.mnemonic import Mnemonic from basicswap.db import create_db_, DBMethods, KnownIdentity -from basicswap.util import i2b, h2b +from basicswap.util import h2b from basicswap.util.address import decodeAddress from basicswap.util.crypto import ripemd160, hash160, blake256 from basicswap.util.extkey import ExtKeyPair @@ -191,12 +188,13 @@ class Test(unittest.TestCase): assert "Too many decimal places" in str(e) def test_ed25519(self): - privkey = edu.get_secret() - pubkey = edu.encodepoint(edf.scalarmult_B(privkey)) - - privkey_bytes = i2b(privkey) - pubkey_test = ed25519_get_pubkey(privkey_bytes) - assert pubkey == pubkey_test + privkey = bytes.fromhex( + "0b4c6e34c21b910f92c7985a8093de526f5f8677a112a8c672d1098139b70e0f" + ) + pubkey = ed25519_get_pubkey(privkey) + assert pubkey == bytes.fromhex( + "5c26c518fb698e91a5858c33e9075488c55c235f391162fe9e6cbd4f694f80aa" + ) def test_ecdsa_otves(self): coin_settings = {"rpcport": 0, "rpcauth": "none"} @@ -591,15 +589,15 @@ class Test(unittest.TestCase): assert decode_varint(b) == (i, expect_length) def test_base58(self): - kv = edu.get_secret() - Kv = edu.encodepoint(edf.scalarmult_B(kv)) - ks = edu.get_secret() - Ks = edu.encodepoint(edf.scalarmult_B(ks)) + k = bytes.fromhex( + "0b4c6e34c21b910f92c7985a8093de526f5f8677a112a8c672d1098139b70e0f" + ) + K = ed25519_get_pubkey(k) - addr = xmr_encode_address(Kv, Ks) + addr = xmr_encode_address(K, K) assert addr.startswith("4") - addr = xmr_encode_address(Kv, Ks, 4146) + addr = xmr_encode_address(K, K, 4146) assert addr.startswith("Wo") def test_blake256(self): diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index fae1c38..206bb2f 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -20,6 +20,8 @@ import unittest from copy import deepcopy +from coincurve.keys import PrivateKey + import basicswap.config as cfg from basicswap.db import ( Concepts, @@ -48,9 +50,6 @@ from basicswap.rpc_xmr import ( from basicswap.interface.xmr import ( XMR_COIN, ) -from basicswap.contrib.key import ( - ECKey, -) from basicswap.http_server import ( HttpThread, ) @@ -342,9 +341,8 @@ class BaseTest(unittest.TestCase): @classmethod def getRandomPubkey(cls): - eckey = ECKey() - eckey.generate() - return eckey.get_pubkey().get_bytes() + k = PrivateKey() + return k.public_key.format() @classmethod def setUpClass(cls): @@ -652,10 +650,9 @@ class BaseTest(unittest.TestCase): logging.info("Preparing swap clients.") if not cls.restore_instance: - eckey = ECKey() - eckey.generate() - cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, eckey.get_bytes()) - cls.network_pubkey = eckey.get_pubkey().get_bytes().hex() + k = PrivateKey() + cls.network_key = toWIF(PREFIX_SECRET_KEY_REGTEST, k.secret) + cls.network_pubkey = k.public_key.format().hex() for i in range(NUM_NODES): start_nodes = set()