原理
有限域GF(p)上的椭圆曲线y² = x³ + ax + b,若P(Xp, Yp), Q(Xq, Yq),且P≠-Q,则R(Xr,Yr) = P+Q 由如下规则确定:
Xr = (λ² - Xp - Xq) mod p
Yr = (λ(Xp - Xr) - Yp) mod p
其中λ = (Yq - Yp)/(Xq - Xp) mod p(若P≠Q), λ = (3Xp² + a)/2Yp mod p(若P=Q)
因此,有限域GF(23)上的椭圆曲线y² ≡ x³ + x + 1 (mod 23),假设以(0,1)为G点,计算2G、3G、4G...xG等等,方法如下:
计算2G:
λ = (3x0² + 1)/2x1 mod 23 = (1/2) mod 23
= 12
Xr = (12² - 0 - 0) mod 23 = 6
Yr = (12(0 - 6) - 1) mod 23 = 19
即2G为点(6,19)
Python 实现
# 获取私钥
```
def random_key():
# Gotta be secure after that java.SecureRandom fiasco...
#random.randrange(1, curve.n)
entropy = os.urandom(32) \
+ str(random.randrange(2**256)) \
+ str(int(time.time() * 1000000))
return sha256(entropy)
```
def privkey_to_pubkey(privkey):
f = get_privkey_format(privkey)
privkey = decode_privkey(privkey, f)
if privkey == 0 or privkey >= N:
raise Exception("Invalid privkey")
if f in ['bin', 'bin_compressed', 'hex', 'hex_compressed', 'decimal']:
return encode_pubkey(fast_multiply(G, privkey), f)
else:
return encode_pubkey(fast_multiply(G, privkey), f.replace('wif', 'hex'))
#获取私钥的数据格式
def get_privkey_format(priv):
if isinstance(priv, (int, long)): return 'decimal'
elif len(priv) == 32: return 'bin'
elif len(priv) == 33: return 'bin_compressed'
elif len(priv) == 64: return 'hex'
elif len(priv) == 66: return 'hex_compressed'
else:
bin_p = b58check_to_bin(priv)
if len(bin_p) == 32: return 'wif'
elif len(bin_p) == 33: return 'wif_compressed'
else: raise Exception("WIF does not represent privkey")
def decode(string, base):
base = int(base)
code_string = get_code_string(base)
result = 0
if base == 16:
string = string.lower()
while len(string) > 0:
result *= base
result += code_string.find(string[0])
string = string[1:]
return result
def decode_privkey(priv,formt=None):
if not formt: formt = get_privkey_format(priv)
if formt == 'decimal': return priv
elif formt == 'bin': return decode(priv, 256)
elif formt == 'bin_compressed': return decode(priv[:32], 256)
elif formt == 'hex': return decode(priv, 16)
elif formt == 'hex_compressed': return decode(priv[:64], 16)
else:
bin_p = b58check_to_bin(priv)
if len(bin_p) == 32: return decode(bin_p, 256)
elif len(bin_p) == 33: return decode(bin_p[:32], 256)
else: raise Exception("WIF does not represent privkey")
def jordan_add(a, b):
if jordan_isinf(a):
return b
if jordan_isinf(b):
return a
if (a[0][0] * b[0][1] - b[0][0] * a[0][1]) % P == 0:
if (a[1][0] * b[1][1] - b[1][0] * a[1][1]) % P == 0:
return jordan_double(a)
else:
return ((0, 1), (0, 1))
xdiff = subcoords(b[0], a[0])
ydiff = subcoords(b[1], a[1])
m = mulcoords(ydiff, invcoords(xdiff))
x = subcoords(subcoords(mulcoords(m, m), a[0]), b[0])
y = subcoords(mulcoords(m, subcoords(a[0], x)), a[1])
return (x, y)
def jordan_double(a):
if jordan_isinf(a):
return ((0, 1), (0, 1))
num = addcoords(mul_by_const(mulcoords(a[0], a[0]), 3), (A, 1))
den = mul_by_const(a[1], 2)
m = mulcoords(num, invcoords(den))
x = subcoords(mulcoords(m, m), mul_by_const(a[0], 2))
y = subcoords(mulcoords(m, subcoords(a[0], x)), a[1])
return (x, y)
# G, privkey
#13,6,3,1
def jordan_multiply(a, n):
if jordan_isinf(a) or n == 0:
return ((0, 0), (0, 0))
if n == 1:
return a
if n < 0 or n >= N:
return jordan_multiply(a, n % N)
if (n % 2) == 0:
return jordan_double(jordan_multiply(a, n/2))
if (n % 2) == 1:
return jordan_add(jordan_double(jordan_multiply(a, n/2)), a)
def to_jordan(p):
return ((p[0], 1), (p[1], 1))
def from_jordan(p):
return (p[0][0] * inv(p[0][1], P) % P, p[1][0] * inv(p[1][1], P) % P)
#G, privkey
def fast_multiply(a, n):
return from_jordan(jordan_multiply(to_jordan(a), n))
def fast_add(a, b):
return from_jordan(jordan_add(to_jordan(a), to_jordan(b)))
# Elliptic curve Jordan form functions
# P = (m, n, p, q) where m/n = x, p/q = y
def isinf(p):
return p[0] == 0 and p[1] == 0
def jordan_isinf(p):
return p[0][0] == 0 and p[1][0] == 0
def mulcoords(c1, c2):
return (c1[0] * c2[0] % P, c1[1] * c2[1] % P)
def mul_by_const(c, v):
return (c[0] * v % P, c[1])
def addcoords(c1, c2):
return ((c1[0] * c2[1] + c2[0] * c1[1]) % P, c1[1] * c2[1] % P)
def subcoords(c1, c2):
return ((c1[0] * c2[1] - c2[0] * c1[1]) % P, c1[1] * c2[1] % P)
def invcoords(c):
return (c[1], c[0])
# Extended Euclidean Algorithm
#p[0][1],p[1][1],p
#a%n = a- a/n * n
#x1=y2; y1=x2-(a/b)*y2;
def inv(a, n):
lm, hm = 1, 0
low, high = a % n, n
while low > 1:
r = high/low
nm, new = hm-lm*r, high-low*r
lm, low, hm, high = nm, new, lm, low
return lm % n