(ECDSA3).pygithub网站
import math import secrets
椭圆曲线公式和定义一个点
# Parameters for the curve function y^2 = x^3 + Ax + B # In this case, secp256k1 is of the equation: y^2 = x^3 + 7 A = 0 B = 7 # base point of the algorithm base_X = 55066263022277343669578718895168534326250603453777594175500187360389116729240 base_Y = 32670510020758816978083085130507043184471273380659243275938904335757337482424 base_POINT = (base_X, base_Y)
曲线素数,和基点在曲线上的阶(阶指的是基点在重复加法下产生的点的数量)
# The proven prime P = 115792089237316195423570985008687907853269984665640564039457584007908834671663 # The order of the base point on the curve (number of points the base point generates under repeated addition) N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
欧几里得算法,来解决jx + ky = 1中的x和y
j (int): Any integer where j <= k. k (int): Any integer. (gcd, x, y): gcd is the greatest common divisor, x and y as the solution to jx + ky = 1
def extended_euclidean_algorithm(j, k): if j == k: return (j, 1, 0) else: i = 0 j_array = [j] k_array = [k] q_array = [] r_array = [] prev_r_is_zero = False while not (prev_r_is_zero): q_array.append(k_array[i]//j_array[i]) r_array.append(k_array[i]%j_array[i]) k_array.append(j_array[i]) j_array.append(r_array[i]) i += 1 if r_array[i-1] == 0: prev_r_is_zero = True i -= 1 gcd = j_array[i] # "extended" part of the algorithm, when the algorithm iterates backwards x_array = [1] y_array = [0] i -= 1 total_steps = i while i >= 0: y_array.append(x_array[total_steps-i]) x_array.append(y_array[total_steps-i] - q_array[i]*x_array[total_steps-i]) i -= 1 return (gcd, x_array[-1], y_array[-1]) print(extended_euclidean_algorithm(28, 161)) print(extended_euclidean_algorithm(14, 24))
此处输出的为最大公约数,x,y。满足jx+ky=1.
286+161(-1)=1
给出j,n,使用扩展的欧几里得算法来寻找j mod n的逆
def mod_inverse(j, n): (gcd, x, y) = extended_euclidean_algorithm(j, n) if gcd == 1: return x%n else: return -1 print(mod_inverse(14, 25)) print(mod_inverse(345, 1234)) print(mod_inverse(3, 37)) print(mod_inverse(20, 5))
求的模逆的值
如果想要更好的了解,必须了解椭圆曲线的基本原理。
参考椭圆曲线公钥加密理论
在椭圆曲线上添加两个不同的点,使用两点的斜率,去寻找与图形相交的第三点,然后翻转第三点并找到与x轴对称的点,即为p+q的和。
def elliptic_add(p, q): if p == 0 and q == 0: return 0 elif p == 0: return q elif q == 0: return p else: # Swap p and q if px > qx. if p[0] > q[0]: temp = p p = q q = temp r = [] slope = (q[1] - p[1])*mod_inverse(q[0] - p[0], P) % P r.append((slope**2 - p[0] - q[0]) % P) r.append((slope*(p[0] - r[0]) - p[1]) % P) return (r[0], r[1]) print(elliptic_add(0,0)) print(elliptic_add((1,60),0)) print(elliptic_add(0,(15,7))) print(elliptic_add((1,60),(15,7)))
p+q的和
#将椭圆曲线上的一个点添加到自身。自加p+p
def elliptic_double(p): r = [] slope = (3*p[0]**2 + A)*mod_inverse(2*p[1], P) % P r.append((slope**2 - 2*p[0])%P) r.append((slope*(p[0] - r[0]) - p[1])%P) return (r[0], r[1]) print(elliptic_double((1,5))) print(elliptic_double(base_POINT))
#在椭圆曲线上用一个给定的点p进行标量乘法。在这个实现中,将包括 是一个Python的双加法的实现。
def elliptic_multiply(s, p): n = p r = 0 # 0 representing a point at infinity s_binary = bin(s)[2:] # convert s to binary and remove the "0b" in the beginning s_length = len(s_binary) for i in reversed(range(s_length)): if s_binary[i] == '1': r = elliptic_add(r, n) n = elliptic_double(n) return r #前两个输出判断是否相等 # Assert that 2P = P + P print(elliptic_multiply(2, base_POINT) == elliptic_double(base_POINT)) # Assert that 4P = 3P + 1P print(elliptic_multiply(4, base_POINT) == elliptic_add(elliptic_multiply(3, base_POINT), elliptic_multiply(1, base_POINT))) print(elliptic_add(elliptic_multiply(1, base_POINT), elliptic_multiply(6, base_POINT))) print(elliptic_multiply(7, base_POINT)) print(elliptic_double(elliptic_multiply(5, base_POINT)))
#以上是定义椭圆曲线的计算方法
接下来产生公私钥
import secrets
#返回的为随机的256位整数值(16进制的32个字节) def generate_private_key(): return int(secrets.token_hex(32), 16) #返回一个由公钥生成的私钥,其公钥是安全的,因为他的计算方法为通过将生成器的点 "乘以 "一个巨大的整数(私钥)在一个巨大的领域。产生的点 (将被压缩并作为公钥返回)将经过许多椭圆加法的处理 以至于不可能猜到乘数(也就是私钥)。 def generate_public_key(private_key): return elliptic_multiply(private_key, base_POINT) #返回一个压缩的公钥:取公钥的x值并加上 y值的奇偶校验位开始。 #判断密钥y的奇偶性 if key[1] % 2 == 0: parity = '02' else: parity = '03' return parity + hex(key[0])[2:]#hex() 用于将十进制数字转换成十六进制;key[0]为密钥的x值,前两位为校验数字,后面取从第3位开始。 #Python中的十六进制值包含一个 "0x "前缀。[2:]去掉了这个前缀 def generate_key_pair(print_flag=True): private_key = generate_private_key() public_key = generate_public_key(private_key) #进行了十六进制转换。 if (print_flag): print("Private Key: " + str(private_key)) print("Private Key (hex): " + str(hex(private_key))[2:]) print("Public Key: " + str(public_key[0]) + str(public_key[1])) print("Public Key (hex): " + "04" + hex(public_key[0])[2:] + hex(public_key[1])[2:]) # Bitcoin adds a "04" prefix to indicate that this is an uncompressed public key. print("Public Key (hex and compressed): " + compress_public_key(public_key)) return (private_key, generate_public_key(private_key)) (sample_private_key, sample_public_key) = generate_key_pair(True)
扩展:A[ : 2]:表示索引 0至1行;A[ :, 2]:表示所有行的第3列。
接下来,比特币用SHA-256对其信息内容进行双重加密
from hashlib import sha256 def double_hash(message): """伪代码 Args: message (str): A message to be hashed Return: A SHA-256 double-hashed message in decimal format, 256 bits long. """ hashed_message = sha256(message.encode('utf-8')).hexdigest() hashed_message = sha256(hashed_message.encode('utf-8')).hexdigest()#双重哈希对消息进行加密 return int(hashed_message, 16) print(double_hash("I hate COVID"))
对消息求哈希得到的结果:
扩展:
hash.digest()
返回摘要,作为二进制数据字符串值
hash.hexdigest()
返回摘要,作为十六进制数据字符串值
注意:要清楚对消息签名和对消息验证的整个流程
接下来,对消息进行签名
其中,
private_key (int): 发送者的私钥。
message (str): 一个包含交易信息的消息。
一个以元组形式存在的签名(rx, s),其中rx是一个随机点的x坐标,s是签名本身。
def sign(private_key, message):#私钥对消息签名 hashed_message = double_hash(message)#消息为上个函数计算的双重哈希的消息 # A secure random number for the signature k = secrets.randbelow(P)#产生一个0到P的随机数,其中randbelow()为secrets包的内置函数 random_point = elliptic_multiply(k, base_POINT)#随机数*基点 # only the x-value is needed, as the y can always be generated using the curve equation y^2 = x^3 + 7 rx = random_point[0] % N#随机的点有x,y的坐标,但是只取x:random_point[0],模为N signature_proof = mod_inverse(k, N) * (hashed_message + rx*private_key) % N#用私钥进行签名 return (rx, signature_proof)#以元组形式存在的签名 message = "Alice sends to Bob 12 Bitcoins" signature = sign(sample_private_key, message) print("Message: " + message) print("Signature: ", end="") print(signature)
接下来,验证签名。验证签名是否和交易时的私钥一致。通过恢复签名函数中使用的随机点完成,查看得到的签名rx是否一致
def verify(public_key, message, signature): (rx, s) = signature#为签名的形式 hashed_message = double_hash(message)#双重签名得到的哈希消息 inverse_s = mod_inverse(s, N)#对s求逆 # Solve for the random point a = elliptic_multiply(hashed_message * inverse_s % N, base_POINT)#elliptic_multiply()为乘法 b = elliptic_multiply(rx * inverse_s % N, public_key)#公钥验证 recovered_random_point = elliptic_add(a, b) # Check that the recovered random point matches the actual random point return recovered_random_point[0] == rx#相等则表示验证通过 print(verify(sample_public_key, message, signature))
说输出消息,消息的签名,消息验证的结果
接下来,3个测试
#接下来用于测试被篡改信息的签名和验证功能。
#被篡改的信息测试。确保消息的内容是它们所要表达的。
def tampered_message_tests(): #3条消息 message_pairs = [ ["Chau sends Professor Nguyen 2 Bitcoins", "Chau sends Professor Nguyen 500 Bitcoins"], ["Hernan sends Chau 3 Bitcoins", "Hernan sends Chau 50 Bitcoins"], ["Owen sends Felix 50 Bitcoins", "Owen sends Felix 600 Bitcoins"] ] #打印每条消息 for i in range(len(message_pairs)): print('----------------------- Tampered Message Case ' + str(i+1) + '----------------------') print() #产生公私钥 #成对的列表。第一个元素是原始信息。第二个是被篡改的信息。 (priv_key, pub_key) = generate_key_pair(True) (message, tampered_message) = message_pairs[i] print() print("Original Message: " + message) print("Tampered Message: " + tampered_message) print() #私钥签名 signature = sign(priv_key, message) print("Signature: ", end="") print(signature) print() #公钥验证原始签名、篡改的签名 print("Original Message Verification: ", end="") print(verify(pub_key, message, signature)) print("Tampered Message Verification: ", end="") print(verify(pub_key, tampered_message, signature)) print() tampered_message_tests()
接下来,测试当错误的公钥被用来 "解锁 "信息时会发生什么。
def wrong_public_key_test(): print('----------------------- Wrong Public Key Case ----------------------') print() #正确的公私钥和错误的公私钥 (priv_key, pub_key) = generate_key_pair(False) (wrong_priv_key, wrong_pub_key) = generate_key_pair(False) #原始公钥和错误公钥04开头,取公钥的x值的索引为2和之后的元素+y值的索引为2和之后的元素 print("Original Public Key: " + "04" + (hex(pub_key[0])[2:] + hex(pub_key[1])[2:])) print("Wrong Public Key: " + "04" + (hex(wrong_pub_key[0])[2:] + hex(wrong_pub_key[1])[2:])) message = "Satoshi sends 500 Bitcoins to Chau" print("Message: " + message) #用正确的私钥签名 signature = sign(priv_key, message) print("Signature: ", end="") print(signature) print() #输出正确的的公钥签名的消息的验证结果 print("With Correct Public Key: ", end="") print(verify(pub_key, message, signature)) #输出错误的公钥签名的消息的验证结果 print("With Wrong Public Key: ", end="") print(verify(wrong_pub_key, message, signature)) wrong_public_key_test()
接下来,测试如果一个拥有不同私钥的人试图签署同一消息会发生什么。
def wrong_private_key_test(): print('----------------------- Wrong Private Key Case ----------------------') print() (priv_key, pub_key) = generate_key_pair(False) (wrong_priv_key, wrong_pub_key) = generate_key_pair(False) message = "LeBron sends 5 Bitcoins to Dwight" #与错误公钥不同的是,这里有错误的签名 signature = sign(priv_key, message) wrong_signature = sign(wrong_priv_key, message) # Different signer! print("Original User's Signature: ", end="") print(signature) print("Another User's Signature: ", end="") print(wrong_signature) print() print("Original Signature Verification: ", end="") print(verify(pub_key, message, signature)) print("Wrong Signature Verification: ", end="") print(verify(pub_key, message, wrong_signature)) wrong_private_key_test()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)