212 lines
5.0 KiB
Python
212 lines
5.0 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
import os
|
|
import hashlib
|
|
import secrets
|
|
|
|
from basicswap.contrib.ellipticcurve import CurveFp, Point, INFINITY, jacobi_symbol
|
|
from . import i2b
|
|
|
|
|
|
class ECCParameters:
|
|
def __init__(self, p, a, b, Gx, Gy, o):
|
|
self.p = p
|
|
self.a = a
|
|
self.b = b
|
|
self.Gx = Gx
|
|
self.Gy = Gy
|
|
self.o = o
|
|
|
|
|
|
ep = ECCParameters(
|
|
p=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
|
|
a=0x0,
|
|
b=0x7,
|
|
Gx=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
|
|
Gy=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
|
|
o=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
|
|
)
|
|
|
|
|
|
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)
|
|
|
|
|
|
def getSecretInt() -> int:
|
|
return 1 + secrets.randbelow(ep.o - 1)
|
|
|
|
|
|
def getInsecureBytes() -> bytes:
|
|
while True:
|
|
s = os.urandom(32)
|
|
|
|
s_test = int.from_bytes(s, byteorder="big")
|
|
if s_test > 1 and s_test < ep.o:
|
|
return s
|
|
|
|
|
|
def getInsecureInt() -> int:
|
|
while True:
|
|
s = os.urandom(32)
|
|
|
|
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()
|