ds-1
This commit is contained in:
5440
ds/25-1/1/Custom+CUDA+Kernels+in+Python+with+Numba.ipynb
Normal file
5440
ds/25-1/1/Custom+CUDA+Kernels+in+Python+with+Numba.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
2073
ds/25-1/1/Effective+Memory+Use.ipynb
Normal file
2073
ds/25-1/1/Effective+Memory+Use.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
2071
ds/25-1/1/Introduction+to+CUDA+Python+with+Numba.ipynb
Normal file
2071
ds/25-1/1/Introduction+to+CUDA+Python+with+Numba.ipynb
Normal file
File diff suppressed because one or more lines are too long
160
ds/25-1/1e/secp256k1.py
Normal file
160
ds/25-1/1e/secp256k1.py
Normal file
@ -0,0 +1,160 @@
|
||||
|
||||
# secp256k1_pure_python.py
|
||||
# Pure Python scalar->public (affine x,y) for secp256k1
|
||||
# No external libs required.
|
||||
|
||||
# Curve parameters for secp256k1
|
||||
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
||||
A = 0 # a = 0 for secp256k1
|
||||
B = 7
|
||||
# Base point G
|
||||
Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
|
||||
Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
|
||||
# Order of base point
|
||||
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
||||
|
||||
# ---------- Field helpers ----------
|
||||
def inv_mod(x, p=P):
|
||||
"""Multiplicative inverse modulo p (Python built-in pow with -1 not supported)"""
|
||||
return pow(x, p - 2, p)
|
||||
|
||||
# ---------- Jacobian coordinates helpers ----------
|
||||
# Represent point as tuple (X, Y, Z) meaning affine (X/Z^2, Y/Z^3).
|
||||
INFINITY = (0, 1, 0) # Z == 0 means point at infinity in these helpers
|
||||
|
||||
def is_infinity(Pj):
|
||||
return Pj[2] == 0
|
||||
|
||||
def to_jacobian(x, y):
|
||||
return (x % P, y % P, 1)
|
||||
|
||||
def from_jacobian(Pj):
|
||||
X, Y, Z = Pj
|
||||
if Z == 0:
|
||||
return None # infinity
|
||||
Z_inv = inv_mod(Z)
|
||||
Z_inv2 = (Z_inv * Z_inv) % P
|
||||
Z_inv3 = (Z_inv2 * Z_inv) % P
|
||||
x = (X * Z_inv2) % P
|
||||
y = (Y * Z_inv3) % P
|
||||
return (x, y)
|
||||
|
||||
# Point doubling in Jacobian coords
|
||||
def jacobian_double(Pj):
|
||||
X1, Y1, Z1 = Pj
|
||||
if Z1 == 0 or Y1 == 0:
|
||||
return INFINITY
|
||||
# Formula for a = 0
|
||||
S = (4 * X1 * pow(Y1, 2, P)) % P
|
||||
M = (3 * pow(X1, 2, P)) % P # since a=0
|
||||
X3 = (pow(M, 2, P) - 2 * S) % P
|
||||
Y3 = (M * (S - X3) - 8 * pow(Y1, 4, P)) % P
|
||||
Z3 = (2 * Y1 * Z1) % P
|
||||
return (X3, Y3, Z3)
|
||||
|
||||
# Mixed addition: add Jacobian Pj and affine Q=(x2,y2)
|
||||
def jacobian_add_affine(Pj, Qx, Qy):
|
||||
X1, Y1, Z1 = Pj
|
||||
if Z1 == 0:
|
||||
return to_jacobian(Qx, Qy)
|
||||
if Y1 == 0 and X1 == 0 and Z1 == 0:
|
||||
return to_jacobian(Qx, Qy)
|
||||
|
||||
Z1z = (Z1 * Z1) % P
|
||||
U2 = (Qx * Z1z) % P
|
||||
S2 = (Qy * Z1z * Z1) % P # Qy * Z1^3
|
||||
|
||||
H = (U2 - X1) % P
|
||||
r = (S2 - Y1) % P
|
||||
|
||||
if H == 0:
|
||||
if r == 0:
|
||||
return jacobian_double(Pj)
|
||||
else:
|
||||
return INFINITY
|
||||
|
||||
HH = (H * H) % P
|
||||
HHH = (H * HH) % P
|
||||
V = (X1 * HH) % P
|
||||
|
||||
X3 = (r * r - HHH - 2 * V) % P
|
||||
Y3 = (r * (V - X3) - Y1 * HHH) % P
|
||||
Z3 = (Z1 * HH) % P
|
||||
return (X3, Y3, Z3)
|
||||
|
||||
# ---------- Scalar multiplication ----------
|
||||
def scalar_mult(k, Px=Gx, Py=Gy):
|
||||
"""
|
||||
Multiply base point (Px,Py) by scalar k.
|
||||
Returns affine (x,y) or None for infinity.
|
||||
k should be integer >=0.
|
||||
"""
|
||||
if k % N == 0:
|
||||
return None
|
||||
if k < 0:
|
||||
# use -P
|
||||
return scalar_mult(-k, Px, (-Py) % P)
|
||||
|
||||
# Ensure scalar reduced mod N and >0
|
||||
k = k % N
|
||||
if k == 0:
|
||||
return None
|
||||
|
||||
# Initialize result as infinity (Jacobian)
|
||||
R = INFINITY
|
||||
# Take base point as affine (we'll use mixed add)
|
||||
Px = Px % P
|
||||
Py = Py % P
|
||||
|
||||
# Left-to-right binary method
|
||||
bits = k.bit_length()
|
||||
for i in range(bits - 1, -1, -1):
|
||||
# R = 2*R
|
||||
R = jacobian_double(R)
|
||||
if (k >> i) & 1:
|
||||
# R = R + P
|
||||
R = jacobian_add_affine(R, Px, Py)
|
||||
# Convert R to affine
|
||||
return from_jacobian(R)
|
||||
|
||||
# ---------- Utility outputs ----------
|
||||
def pubkey_bytes_compressed(x, y):
|
||||
prefix = b'\x02' if (y % 2 == 0) else b'\x03'
|
||||
xb = x.to_bytes(32, 'big')
|
||||
return prefix + xb
|
||||
|
||||
def pubkey_bytes_uncompressed(x, y):
|
||||
return b'\x04' + x.to_bytes(32, 'big') + y.to_bytes(32, 'big')
|
||||
|
||||
# ---------- Public function as user asked ----------
|
||||
def priv_to_pub(priv_int):
|
||||
"""
|
||||
Input: priv_int (Python int) — private key (assumed 0 < priv < N but function reduces)
|
||||
Output: (x, y) tuple of Python ints (affine coordinates). Returns None if point at infinity (shouldn't for valid keys).
|
||||
"""
|
||||
if not isinstance(priv_int, int):
|
||||
raise TypeError("priv must be int")
|
||||
k = priv_int % N
|
||||
if k == 0:
|
||||
raise ValueError("private key is zero modulo curve order")
|
||||
pt = scalar_mult(k, Gx, Gy)
|
||||
if pt is None:
|
||||
raise RuntimeError("Result is point at infinity (shouldn't happen for valid priv)")
|
||||
return pt
|
||||
|
||||
# ---------- Example / quick test ----------
|
||||
if __name__ == "__main__":
|
||||
# Test vector: priv = 1 -> pub = G
|
||||
priv = 1
|
||||
x, y = priv_to_pub(priv)
|
||||
assert x == Gx and y == Gy
|
||||
print("priv=1 -> pub == G : OK")
|
||||
|
||||
# Test with a random private (example)
|
||||
import secrets
|
||||
priv = secrets.randbelow(N - 1) + 1
|
||||
x, y = priv_to_pub(priv)
|
||||
print("priv (hex):", hex(priv))
|
||||
print("pub x:", hex(x))
|
||||
print("pub y:", hex(y))
|
||||
print("compressed pubkey:", pubkey_bytes_compressed(x, y).hex())
|
||||
Reference in New Issue
Block a user