์๋ช , ๊ฒ์ฆ ์๊ณ ๋ฆฌ์ฆ์ ์์์ผ๋ ํ์ํ ํด๋์ค๋ค์ ์์ฑํด๋ณด์.
Signature
class Signature:
def __init__(self, r, s):
self.r = r
self.s = s
def __repr__(self):
return 'Signature({:x},{:x})'.format(self.r, self.s)
- ์๋ช ์ ๊ฒฝ์ฐ ์๋ช ํ๋ ์ฌ๋์ด ๋ฃ์ด์ ์ฃผ๋ ๊ฐ์ด๋ค.
Verify
- ํ์ ๊ณก์ ์์ ์๋ ์ ์ ๋ํ๋ด๋
S256Point
์ ๊ฒ์ฆ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ค.
class S256Point(Point): # ํ์ ๊ณก์ ์ ์ ์์
...
def verify(self, z, sig):
s_inv = pow(sig.s, N - 2, N) # <1>
u = z * s_inv % N # <2>
v = sig.r * s_inv % N # <3>
total = u * G + v * self # <4>
return total.x.num == sig.r # <5>
- ํ๋ฅด๋ง์ ์๊ณต์์ ํตํด inverse๋ฅผ 0์ ํฌํจํ ์ ์ ์ง์๋ก ๋ณ๊ฒฝํ๋ค.
- u, v๋ฅผ ๊ตฌํ๋ค.
- u, v ๋๋ค ์์์ธ N์ผ๋ก ๋ณ๊ฒฝํด์ฃผ์ด์ผ ํ๋ค.
- ์ข๋ณ์ ๊ณ์ฐํ๋ค. ()
- ๊ฒฐ๊ณผ๊ฐ๊ณผ ์ ๋น๊ตํ๋ค.
PrivateKey
- ์๋ช
์ ์์ฑํ
PrivateKey
ํด๋์ค๋ฅผ ๋ง๋ค์. - ๋น๋ฐํค๋ฅผ ๋ณด๊ดํ๋ ํด๋์ค๋ฅผ ํ๋ ๋ง๋ค์ด์ฃผ๊ณ , ๊ฑฐ๊ธฐ์ ์๋ช ์ ์์ฑํ์ฌ ๊ฒ์ฆ์์๊ฒ ์ ๋ฌํ์.
- ๊ฒ์ฆ์๋ Public Key์ ํด๋นํ๋ ํ์์์ ์ (
S256Point
)์ ์๋ ๋ฉ์๋์ธverify
๋ก ๊ทธ ์๋ช ์ ๋๊ฒจ์ฃผ๋ฉด ๋์ด๋ค.
class PrivateKey:
def __init__(self, secret):
self.secret = secret
self.point = secret * G # ๊ณต๊ฐํค๋ฅผ ๊ณ์ฐ ํ ๋ณด๊ดํ๋ค.
def hex(self):
return '{:x}'.format(self.secret).zfill(64)
def sign(self, z):
k = self.deterministic_k(z) # ๊ทธ๋ฅ ๋ฌด์์๋ก ๊ฒฐ์ ํ๋ฉด ์๋๋ค. k๊ฐ ๋คํค๋ฉด e๋ ํธ๋ฆฐ๋ค. ์๋ช
๋ง๋ค k๋ ๋ฌ๋ผ์ผํ๋ค. ์๋๋ฉด ํธ๋ฆฐ๋ค.
r = (k * G).x.num
k_inv = pow(k, N - 2, N)
s = (z + r * self.secret) * k_inv % N
if s > N / 2: # ๊ฐ๋ณ์ฑ ๋ฌธ์ ๋ก N/x๋ณด๋ค ์์ s๊ฐ์ ์ฌ์ฉํ๋ค.
s = N - s
return Signature(r, s)
def deterministic_k(self, z): # ์๋ช
๋ง๋ค ๋ค๋ฅธ k๊ฐ์ ๋ช
์์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ ์ฝ๋
k = b'\x00' * 32
v = b'\x01' * 32
if z > N:
z -= N
z_bytes = z.to_bytes(32, 'big')
secret_bytes = self.secret.to_bytes(32, 'big')
s256 = hashlib.sha256
k = hmac.new(k, v + b'\x00' + secret_bytes + z_bytes, s256).digest()
v = hmac.new(k, v, s256).digest()
k = hmac.new(k, v + b'\x01' + secret_bytes + z_bytes, s256).digest()
v = hmac.new(k, v, s256).digest()
while True:
v = hmac.new(k, v, s256).digest()
candidate = int.from_bytes(v, 'big')
if candidate >= 1 and candidate < N:
return candidate # <2>
k = hmac.new(k, v + b'\x00', s256).digest()
v = hmac.new(k, v, s256).digest()
๊ฐ๋ณ์ฑ ๋ฌธ์
- ์์์ s๊ฐ N/2๋ณด๋ค ์์ s๋ฅผ ์๋ช ์ ํฌํจํ๋ค.
- ์ ๊ตณ์ด ์ด๋ฌ๋ ๊ฑธ๊น?
- ์ฐ๋ฆฌ๊ฐ ์๋ช ์ ๋ง๋ ๋ค๋ ๊ฒ์ ์ด๋ ํ ์ด์ฐ ๋ก๊ทธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ณ์๋ฅผ ์ค๋ค๋ ๊ฒ๊ณผ ๊ฐ๋ค.
- ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ด์ฐ ๋ก๊ทธ ๋ฌธ์ ๋ ๊ทผ๋ณธ์ ์ผ๋ก ํ์ ๊ณก์ ์์ ์๊ฒ ๋๋ค.
- ํ์ ๊ณก์ ์ ํน์ง์ผ๋ก๋ x์ถ ๋์นญ์ด ์๋๋ฐ, ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๊ฐ์ x๊ฐ์ ๋ํด ๊ณก์ ์์ ์๋ ์ ์ 2๊ฐ๊ฐ ๋๊ฒ ๋๋ค.
- ์ด๋ ์๋ช ์ผ๋ก ๊ฐ์ ์์ฑํ ๋๋ ๊ฐ์ ํน์ฑ์ ๋๋ค.
- ์ฆ, ์ ๋ํด ์ด์ฐ ๋ก๊ทธ ๋ฌธ์ ๋ฅผ ๋ง์กฑํ๋ ํด๋ , ๋ก ๋๊ฐ๋ผ๋ ๊ฒ์ด๋ค. ๋๊ฐ์ ์ ํจํ ์๋ช ์ด ์๋ ๊ฒ.
- ์ด๋ ๊ฒ 2๊ฐ๊ฐ ๋์ด ๋ฐ์ํ๋ ๋ฌธ์ (์ด๊ฑด ์์ง ๋ชจ๋ฅด๊ฒ ๋ค.)๋ฅผ ๋ง๊ธฐ ์ํด ์์ s๋ฅผ ์ฌ์ฉํ๋ค.
Code
FieldElement
class FieldElement:
def __init__(self, num, prime):
if num >= prime or num < 0:
error = 'Num {} not in field range 0 to {}'.format(
num, prime - 1)
raise ValueError(error)
self.num = num
self.prime = prime
def __repr__(self):
return 'FieldElement_{}({})'.format(self.prime, self.num)
def __eq__(self, other):
if other is None:
return False
return self.num == other.num and self.prime == other.prime
def __ne__(self, other):
# this should be the inverse of the == operator
return not (self == other)
def __add__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot add two numbers in different Fields')
# self.num and other.num are the actual values
# self.prime is what we need to mod against
num = (self.num + other.num) % self.prime
# We return an element of the same class
return self.__class__(num, self.prime)
def __sub__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot subtract two numbers in different Fields')
# self.num and other.num are the actual values
# self.prime is what we need to mod against
num = (self.num - other.num) % self.prime
# We return an element of the same class
return self.__class__(num, self.prime)
def __mul__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot multiply two numbers in different Fields')
# self.num and other.num are the actual values
# self.prime is what we need to mod against
num = (self.num * other.num) % self.prime
# We return an element of the same class
return self.__class__(num, self.prime)
def __pow__(self, exponent):
n = exponent % (self.prime - 1)
num = pow(self.num, n, self.prime)
return self.__class__(num, self.prime)
def __truediv__(self, other):
if self.prime != other.prime:
raise TypeError('Cannot divide two numbers in different Fields')
# self.num and other.num are the actual values
# self.prime is what we need to mod against
# use fermat's little theorem:
# self.num**(p-1) % p == 1
# this means:
# 1/n == pow(n, p-2, p)
num = (self.num * pow(other.num, self.prime - 2, self.prime)) % self.prime
# We return an element of the same class
return self.__class__(num, self.prime)
def __rmul__(self, coefficient):
num = (self.num * coefficient) % self.prime
return self.__class__(num=num, prime=self.prime)
Point
class Point:
def __init__(self, x, y, a, b):
self.a = a
self.b = b
self.x = x
self.y = y
if self.x is None and self.y is None:
return
if self.y**2 != self.x**3 + a * x + b:
raise ValueError('({}, {}) is not on the curve'.format(x, y))
def __eq__(self, other):
return self.x == other.x and self.y == other.y \
and self.a == other.a and self.b == other.b
def __ne__(self, other):
# this should be the inverse of the == operator
return not (self == other)
def __repr__(self):
if self.x is None:
return 'Point(infinity)'
elif isinstance(self.x, FieldElement):
return 'Point({},{})_{}_{} FieldElement({})'.format(
self.x.num, self.y.num, self.a.num, self.b.num, self.x.prime)
else:
return 'Point({},{})_{}_{}'.format(self.x, self.y, self.a, self.b)
def __add__(self, other):
if self.a != other.a or self.b != other.b:
raise TypeError('Points {}, {} are not on the same curve'.format(self, other))
# Case 0.0: self is the point at infinity, return other
if self.x is None:
return other
# Case 0.1: other is the point at infinity, return self
if other.x is None:
return self
# Case 1: self.x == other.x, self.y != other.y
# Result is point at infinity
if self.x == other.x and self.y != other.y:
return self.__class__(None, None, self.a, self.b)
# Case 2: self.x โ other.x
# Formula (x3,y3)==(x1,y1)+(x2,y2)
# s=(y2-y1)/(x2-x1)
# x3=s**2-x1-x2
# y3=s*(x1-x3)-y1
if self.x != other.x:
s = (other.y - self.y) / (other.x - self.x)
x = s**2 - self.x - other.x
y = s * (self.x - x) - self.y
return self.__class__(x, y, self.a, self.b)
# Case 4: if we are tangent to the vertical line,
# we return the point at infinity
# note instead of figuring out what 0 is for each type
# we just use 0 * self.x
if self == other and self.y == 0 * self.x:
return self.__class__(None, None, self.a, self.b)
# Case 3: self == other
# Formula (x3,y3)=(x1,y1)+(x1,y1)
# s=(3*x1**2+a)/(2*y1)
# x3=s**2-2*x1
# y3=s*(x1-x3)-y1
if self == other:
s = (3 * self.x**2 + self.a) / (2 * self.y)
x = s**2 - 2 * self.x
y = s * (self.x - x) - self.y
return self.__class__(x, y, self.a, self.b)
def __rmul__(self, coefficient):
coef = coefficient
current = self # <1>
result = self.__class__(None, None, self.a, self.b) # <2>
while coef:
if coef & 1: # <3>
result += current
current += current # <4>
coef >>= 1 # <5>
return result
Constants For Bitcoin
A = 0
B = 7
P = 2**256 - 2**32 - 977
N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
S256Field
class S256Field(FieldElement): # ์ ํ์ฒด ์์
def __init__(self, num, prime=None):
super().__init__(num=num, prime=P) # ์ด๊ธฐํ์ ์ ์ญ ๋ณ์๋ก ๋น ์ ธ์๋ P๋ฅผ ๋ฃ์ด๋ฒ๋ฆผ
def __repr__(self):
return '{:x}'.format(self.num).zfill(64) # ์ถ๋ ฅ์ ๋น์๋ฆฌ๋ฅผ 0์ผ๋ก ์ฑ์ฐ๋๋ก ํจ
class S256Point(Point): # ํ์ ๊ณก์ ์ ์ ์์
def __init__(self, x, y, a=None, b=None):
a, b = S256Field(A), S256Field(B) # a, b๋ฅผ ์ ์ญ๋ณ์๋ก ๋์ฒด
if type(x) == int:
super().__init__(x=S256Field(x), y=S256Field(y), a=a, b=b)
else:
super().__init__(x=x, y=y, a=a, b=b) # ๋ฌดํ ์์ ์ผ๋ก ์ด๊ธฐํํ๋ ๊ฒฝ์ฐ (x == None)์ ์ํด ๋ถ๊ธฐ
def __repr__(self):
if self.x is None:
return 'S256Point(infinity)'
else:
return 'S256Point({}, {})'.format(self.x, self.y)
def __rmul__(self, coefficient):
coef = coefficient % N # nG๊ฐ 0์ด๋, ์ ํ์ํ๊ตฐ์์์ ๋๋จธ์ง๋ก ๋๋์ด ์ฒ๋ฆฌํด๋ ๋ฌด๋ฐฉํ๋ค.
return super().__rmul__(coef)
def verify(self, z, sig):
s_inv = pow(sig.s, N - 2, N) # <1>
u = z * s_inv % N # <2>
v = sig.r * s_inv % N # <3>
total = u * G + v * self # <4>
return total.x.num == sig.r # <5>
Constants For Bitcoin Signature
G = S256Point(
0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
Signature
class Signature:
def __init__(self, r, s):
self.r = r
self.s = s
def __repr__(self):
return 'Signature({:x},{:x})'.format(self.r, self.s)
PrivateKey
class PrivateKey:
def __init__(self, secret):
self.secret = secret
self.point = secret * G # <1>
def hex(self):
return '{:x}'.format(self.secret).zfill(64)
def sign(self, z):
k = self.deterministic_k(z) # <1>
r = (k * G).x.num
k_inv = pow(k, N - 2, N)
s = (z + r * self.secret) * k_inv % N
if s > N / 2:
s = N - s
return Signature(r, s)
def deterministic_k(self, z):
k = b'\x00' * 32
v = b'\x01' * 32
if z > N:
z -= N
z_bytes = z.to_bytes(32, 'big')
secret_bytes = self.secret.to_bytes(32, 'big')
s256 = hashlib.sha256
k = hmac.new(k, v + b'\x00' + secret_bytes + z_bytes, s256).digest()
v = hmac.new(k, v, s256).digest()
k = hmac.new(k, v + b'\x01' + secret_bytes + z_bytes, s256).digest()
v = hmac.new(k, v, s256).digest()
while True:
v = hmac.new(k, v, s256).digest()
candidate = int.from_bytes(v, 'big')
if candidate >= 1 and candidate < N:
return candidate # <2>
k = hmac.new(k, v + b'\x00', s256).digest()
v = hmac.new(k, v, s256).digest()