文章目录
《区块链编程》第三章
椭圆曲线数字签名算法
名称解释
一定配合着书看。
如下的名词解释不足以解释椭圆曲线数字签名算法的细节。 具体内容还要看书中的详细解释。
- R : R是椭圆曲线上的点,R点有两个,一个是签名者创建的R点, 一个是验证者计算得到的R点
- r : 小r是R点的横坐标
- z : z是签名信息的哈希
- s : s是通过计算得到的,与z,r,e,k有关。
======================================
- G : G点是椭圆曲线密码学的起点,是可以自由选择的
- e : eG = P; 在这个式子中,e是私钥,是一个256位数字,P是公钥,是椭圆曲线上的点。
- k : kG = R, k是一个256位的随机数,k是私钥拥有者(签名者)选定的随机数,
签名的过程:
- 签名者 已知z,e
- 随机选择k
- 计算 R = kG,得到x轴坐标r
- 计算出s(由z、r、e、k计算得到)
- 得到数字签名 (r,s)
验证签名的过程:
- 验证者接收(r,s)、z与公钥
- 计算出R点
- 验证R点的x轴坐标与r值是否相等。相等则签名是有效的
椭圆曲线密码学
练习1
p44
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 14:28:13
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 15:55:45
# 注意: ecc内包含FieldElement类。
# 这里不再重复给出
from ecc import FieldElement
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
def onCurve(x, y):
return y ** 2 == x**3 + a * x + b
if __name__ == '__main__':
print(onCurve(FieldElement(192, prime), FieldElement(105, prime)))
print(onCurve(FieldElement(17, prime), FieldElement(56, prime)))
print(onCurve(FieldElement(200, prime), FieldElement(119, prime)))
print(onCurve(FieldElement(1, prime), FieldElement(193, prime)))
print(onCurve(FieldElement(42, prime), FieldElement(99, prime)))
测试
True
True
False
True
False
[Finished in 303ms]
练习2
p49
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 15:57:16
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 16:12:16
# 注意: ecc内包含FieldElement类与Point类。
# 第一章和第二章中已给出,这里不再重复给出
from ecc import FieldElement, Point
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
x1, y1, x2, y2 = FieldElement(170, prime), FieldElement(142, prime), FieldElement(60, prime), FieldElement(139, prime)
x3, y3, x4, y4 = FieldElement(47, prime), FieldElement(71, prime), FieldElement(17, prime), FieldElement(56, prime)
x5, y5, x6, y6 = FieldElement(143, prime), FieldElement(98, prime), FieldElement(76, prime), FieldElement(66, prime)
p1, p2 = Point(x1, y1, a, b), Point(x2, y2, a, b)
p3, p4 = Point(x3, y3, a, b), Point(x4, y4, a, b)
p5, p6 = Point(x5, y5, a, b), Point(x6, y6, a, b)
print(p1 + p2)
print(p3 + p4)
print(p5 + p6)
测试
Point(FieldElement_223(220), FieldElement_223(181))_FieldElement_223(0)_FieldElement_223(7)
Point(FieldElement_223(215), FieldElement_223(68))_FieldElement_223(0)_FieldElement_223(7)
Point(FieldElement_223(47), FieldElement_223(71))_FieldElement_223(0)_FieldElement_223(7)
[Finished in 330ms]
练习3
p49
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 16:17:26
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 16:25:22
# 注意: ecc内包含FieldElement类与Point类。
# 第一章和第二章中已给出,这里不再重复给出
from ecc import FieldElement, Point
from unittest import TestCase
from unittest import TestSuite, TextTestRunner
class ECCTest(TestCase):
def test_add(self):
# tests the following additions on curve y^2=x^3-7 over F_223:
# (192,105) + (17,56)
# (47,71) + (117,141)
# (143,98) + (76,66)
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
additions = (
# (x1, y1, x2, y2, x3, y3)
(192, 105, 17, 56, 170, 142),
(47, 71, 117, 141, 60, 139),
(143, 98, 76, 66, 47, 71),
)
for x1_raw, y1_raw, x2_raw, y2_raw, x3_raw, y3_raw in additions:
x1 = FieldElement(x1_raw, prime)
y1 = FieldElement(y1_raw, prime)
p1 = Point(x1, y1, a, b)
x2 = FieldElement(x2_raw, prime)
y2 = FieldElement(y2_raw, prime)
p2 = Point(x2, y2, a, b)
x3 = FieldElement(x3_raw, prime)
y3 = FieldElement(y3_raw, prime)
p3 = Point(x3, y3, a, b)
self.assertEqual(p1 + p2, p3)
def run(test):
suite = TestSuite()
suite.addTest(test)
TextTestRunner().run(suite)
if __name__ == '__main__':
run(ECCTest("test_add"))
测试
.
----------------------------------------------------------------------
Ran 1 test in 0.000s
OK
[Finished in 309ms]
练习4
p51
第二个点,书中写错了, 应该是(143,98)
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 17:00:43
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 17:06:08
# 注意: ecc内包含FieldElement类和Point类。
# 第一章和第二章中已给出,这里不再重复给出
from ecc import FieldElement, Point
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
x1 = FieldElement(192, prime)
y1 = FieldElement(105, prime)
x2 = FieldElement(143, prime)
y2 = FieldElement(98, prime)
x3 = FieldElement(47, prime)
y3 = FieldElement(71, prime)
p1 = Point(x1, y1, a, b)
p2 = Point(x2, y2, a, b)
p3 = Point(x3, y3, a, b)
print(p1 + p1)
print(p2 + p2)
print(p3 + p3)
print(p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3)
print(p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3 + p3)
运行结果
Point(49,71)_0_7 FieldElement(223)
Point(64,168)_0_7 FieldElement(223)
Point(36,111)_0_7 FieldElement(223)
Point(116,55)_0_7 FieldElement(223)
Point(infinity)
[Finished in 313ms]
练习5
p56
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 17:14:38
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 17:17:50
# 注意: ecc内包含FieldElement类和Point类。
# 第一章和第二章中已给出,这里不再重复给出
from ecc import FieldElement, Point
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
x1 = FieldElement(15, prime)
y1 = FieldElement(86, prime)
p1 = Point(x1, y1, a, b)
inf = Point(None, None, a, b)
count = 1
product = p1
while product != inf:
product += p1
count += 1
print(count)
运行结果
7
[Finished in 320ms]
练习6
p66
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 17:22:58
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 18:15:33
from ecc import S256Point, N, G
point = S256Point(
0x887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c,
0x61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34)
z = 0xec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60
r = 0xac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395
s = 0x68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4
u = z * pow(s, N - 2, N) % N
v = r * pow(s, N - 2, N) % N
print((u * G + v * point).x.num == r)
z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d
r = 0xeff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c
s = 0xc7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6
u = z * pow(s, N - 2, N) % N
v = r * pow(s, N - 2, N) % N
print((u * G + v * point).x.num == r)
运行结果
True
True
[Finished in 691ms]
练习7
p68
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2021-12-31 18:19:04
# @Last Modified by: 从化北
# @Last Modified time: 2021-12-31 18:27:22
from ecc import S256Point, G, N
from helper import hash256
e = 12345
z = int.from_bytes(hash256('Programming Bitcoin!'.encode("utf-8")), 'big')
k = 1234567890
r = (k * G).x.num
k_inv = pow(k, N - 2, N)
s = (z + r * e) * k_inv % N
print(e * G)
print(hex(z))
print(hex(r))
print(hex(s))
运行结果
S256Point(f01d6b9018ab421dd410404cb869072065522bf85734008f105cf385a023a80f, 0eba29d0f0c5408ed681984dc525982abefccd9f7ff01dd26da4999cf3f6a295)
0x969f6056aa26f7d2795fd013fe88868d09c9f6aed96965016e1936ae47060d48
0x2b698a0f0a4041b77e63488ad48c23e8e8838dd1fb7520408b121697b782ef22
0x1dbc63bfef4416705e602a7b564161167076d8b20990a0f26f316cff2cb0bc1a
[Finished in 325ms]
本章中涉及的ecc与helper
书中给的ecc
from random import randint
from unittest import TestCase
import hashlib
import hmac
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)
class FieldElementTest(TestCase):
def test_ne(self):
a = FieldElement(2, 31)
b = FieldElement(2, 31)
c = FieldElement(15, 31)
self.assertEqual(a, b)
self.assertTrue(a != c)
self.assertFalse(a != b)
def test_add(self):
a = FieldElement(2, 31)
b = FieldElement(15, 31)
self.assertEqual(a + b, FieldElement(17, 31))
a = FieldElement(17, 31)
b = FieldElement(21, 31)
self.assertEqual(a + b, FieldElement(7, 31))
def test_sub(self):
a = FieldElement(29, 31)
b = FieldElement(4, 31)
self.assertEqual(a - b, FieldElement(25, 31))
a = FieldElement(15, 31)
b = FieldElement(30, 31)
self.assertEqual(a - b, FieldElement(16, 31))
def test_mul(self):
a = FieldElement(24, 31)
b = FieldElement(19, 31)
self.assertEqual(a * b, FieldElement(22, 31))
def test_rmul(self):
a = FieldElement(24, 31)
b = 2
self.assertEqual(b * a, a + a)
def test_pow(self):
a = FieldElement(17, 31)
self.assertEqual(a**3, FieldElement(15, 31))
a = FieldElement(5, 31)
b = FieldElement(18, 31)
self.assertEqual(a**5 * b, FieldElement(16, 31))
def test_div(self):
a = FieldElement(3, 31)
b = FieldElement(24, 31)
self.assertEqual(a / b, FieldElement(4, 31))
a = FieldElement(17, 31)
self.assertEqual(a**-3, FieldElement(29, 31))
a = FieldElement(4, 31)
b = FieldElement(11, 31)
self.assertEqual(a**-4 * b, FieldElement(13, 31))
# tag::source1[]
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))
# end::source1[]
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)
# tag::source3[]
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
# end::source3[]
class PointTest(TestCase):
def test_ne(self):
a = Point(x=3, y=-7, a=5, b=7)
b = Point(x=18, y=77, a=5, b=7)
self.assertTrue(a != b)
self.assertFalse(a != a)
def test_on_curve(self):
with self.assertRaises(ValueError):
Point(x=-2, y=4, a=5, b=7)
# these should not raise an error
Point(x=3, y=-7, a=5, b=7)
Point(x=18, y=77, a=5, b=7)
def test_add0(self):
a = Point(x=None, y=None, a=5, b=7)
b = Point(x=2, y=5, a=5, b=7)
c = Point(x=2, y=-5, a=5, b=7)
self.assertEqual(a + b, b)
self.assertEqual(b + a, b)
self.assertEqual(b + c, a)
def test_add1(self):
a = Point(x=3, y=7, a=5, b=7)
b = Point(x=-1, y=-1, a=5, b=7)
self.assertEqual(a + b, Point(x=2, y=-5, a=5, b=7))
def test_add2(self):
a = Point(x=-1, y=1, a=5, b=7)
self.assertEqual(a + a, Point(x=18, y=-77, a=5, b=7))
# tag::source2[]
class ECCTest(TestCase):
def test_on_curve(self):
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
valid_points = ((192, 105), (17, 56), (1, 193))
invalid_points = ((200, 119), (42, 99))
for x_raw, y_raw in valid_points:
x = FieldElement(x_raw, prime)
y = FieldElement(y_raw, prime)
Point(x, y, a, b) # <1>
for x_raw, y_raw in invalid_points:
x = FieldElement(x_raw, prime)
y = FieldElement(y_raw, prime)
with self.assertRaises(ValueError):
Point(x, y, a, b) # <1>
# end::source2[]
def test_add(self):
# tests the following additions on curve y^2=x^3-7 over F_223:
# (192,105) + (17,56)
# (47,71) + (117,141)
# (143,98) + (76,66)
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
additions = (
# (x1, y1, x2, y2, x3, y3)
(192, 105, 17, 56, 170, 142),
(47, 71, 117, 141, 60, 139),
(143, 98, 76, 66, 47, 71),
)
# loop over additions
# initialize x's and y's as FieldElements
# create p1, p2 and p3 as Points
# check p1+p2==p3
raise NotImplementedError
def test_rmul(self):
# tests the following scalar multiplications
# 2*(192,105)
# 2*(143,98)
# 2*(47,71)
# 4*(47,71)
# 8*(47,71)
# 21*(47,71)
prime = 223
a = FieldElement(0, prime)
b = FieldElement(7, prime)
multiplications = (
# (coefficient, x1, y1, x2, y2)
(2, 192, 105, 49, 71),
(2, 143, 98, 64, 168),
(2, 47, 71, 36, 111),
(4, 47, 71, 194, 51),
(8, 47, 71, 116, 55),
(21, 47, 71, None, None),
)
# iterate over the multiplications
for s, x1_raw, y1_raw, x2_raw, y2_raw in multiplications:
x1 = FieldElement(x1_raw, prime)
y1 = FieldElement(y1_raw, prime)
p1 = Point(x1, y1, a, b)
# initialize the second point based on whether it's the point at infinity
if x2_raw is None:
p2 = Point(None, None, a, b)
else:
x2 = FieldElement(x2_raw, prime)
y2 = FieldElement(y2_raw, prime)
p2 = Point(x2, y2, a, b)
# check that the product is equal to the expected point
self.assertEqual(s * p1, p2)
# tag::source6[]
A = 0
B = 7
# end::source6[]
# tag::source4[]
P = 2**256 - 2**32 - 977
# end::source4[]
# tag::source9[]
N = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141
# end::source9[]
# tag::source5[]
class S256Field(FieldElement):
def __init__(self, num, prime=None):
super().__init__(num=num, prime=P)
def __repr__(self):
return '{:x}'.format(self.num).zfill(64)
# end::source5[]
# tag::source7[]
class S256Point(Point):
def __init__(self, x, y, a=None, b=None):
a, b = S256Field(A), S256Field(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) # <1>
# end::source7[]
def __repr__(self):
if self.x is None:
return 'S256Point(infinity)'
else:
return 'S256Point({}, {})'.format(self.x, self.y)
# tag::source8[]
def __rmul__(self, coefficient):
coef = coefficient % N # <1>
return super().__rmul__(coef)
# end::source8[]
# tag::source12[]
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>
# end::source12[]
# tag::source10[]
# starting point
G = S256Point(
0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
# end::source10[]
class S256Test(TestCase):
def test_order(self):
point = N * G
self.assertIsNone(point.x)
def test_pubpoint(self):
# write a test that tests the public point for the following
points = (
# secret, x, y
(7, 0x5cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc, 0x6aebca40ba255960a3178d6d861a54dba813d0b813fde7b5a5082628087264da),
(1485, 0xc982196a7466fbbbb0e27a940b6af926c1a74d5ad07128c82824a11b5398afda, 0x7a91f9eae64438afb9ce6448a1c133db2d8fb9254e4546b6f001637d50901f55),
(2**128, 0x8f68b9d2f63b5f339239c1ad981f162ee88c5678723ea3351b7b444c9ec4c0da, 0x662a9f2dba063986de1d90c2b6be215dbbea2cfe95510bfdf23cbf79501fff82),
(2**240 + 2**31, 0x9577ff57c8234558f293df502ca4f09cbc65a6572c842b39b366f21717945116, 0x10b49c67fa9365ad7b90dab070be339a1daf9052373ec30ffae4f72d5e66d053),
)
# iterate over points
for secret, x, y in points:
# initialize the secp256k1 point (S256Point)
point = S256Point(x, y)
# check that the secret*G is the same as the point
self.assertEqual(secret * G, point)
def test_verify(self):
point = S256Point(
0x887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c,
0x61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34)
z = 0xec208baa0fc1c19f708a9ca96fdeff3ac3f230bb4a7ba4aede4942ad003c0f60
r = 0xac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a395
s = 0x68342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4
self.assertTrue(point.verify(z, Signature(r, s)))
z = 0x7c076ff316692a3d7eb3c3bb0f8b1488cf72e1afcd929e29307032997a838a3d
r = 0xeff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c
s = 0xc7207fee197d27c618aea621406f6bf5ef6fca38681d82b2f06fddbdce6feab6
self.assertTrue(point.verify(z, Signature(r, s)))
# tag::source11[]
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)
# end::source11[]
# tag::source13[]
class PrivateKey:
def __init__(self, secret):
self.secret = secret
self.point = secret * G # <1>
def hex(self):
return '{:x}'.format(self.secret).zfill(64)
# end::source13[]
# tag::source14[]
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()
# end::source14[]
class PrivateKeyTest(TestCase):
def test_sign(self):
pk = PrivateKey(randint(0, N))
z = randint(0, 2**256)
sig = pk.sign(z)
self.assertTrue(pk.point.verify(z, sig))
书中给的helper
# -*- coding: utf-8 -*-
from unittest import TestSuite, TextTestRunner
import hashlib
def run(test):
suite = TestSuite()
suite.addTest(test)
TextTestRunner().run(suite)
def hash256(s):
'''two rounds of sha256'''
return hashlib.sha256(hashlib.sha256(s).digest()).digest()