提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
代码
因为现在pyunit_prime库很难通过pip下载,而在网上找的教程自己手动下载又出现各种bug,于是直接编写了函数代码进行功能的实现。
import random
from typing import Optional
from typing import List
# from pyunit_prime import get_large_prime_length # 随机生成指定长度大素数
# from pyunit_prime import is_prime # 判断素数
# from pyunit_prime import prime_range # 输出指定区间素数
import math
p = 0
q = 0
def is_prime(n: int, k: int = 5) -> bool:
"""
使用Miller-Rabin素性检验算法判断一个数n是否可能是素数。
参数:
n (int): 待检验的正整数
k (int): 重复检验次数,默认为5,以保证错误率极低
返回:
bool: 如果n可能是素数,返回True;否则返回False
"""
if n <= 1 or (n % 2 == 0 and n != 2):
return False
if n <= 3: # 新增条件判断,避免n - 2小于等于1
return True
def witness(a: int) -> bool:
d = n - 1
s = 0
while d % 2 == 0:
d //= 2
s += 1
x = pow(a, d, n)
if x == 1 or x == n - 1:
return False
for _ in range(s - 1):
x = pow(x, 2, n)
if x == n - 1:
return False
return True
for _ in range(k):
a = random.randint(2, n - 2)
if witness(a):
return False
return True
def get_large_prime_length(length: int) -> int:
"""
生成一个指定二进制长度的大素数。
参数:
length (int): 素数期望的二进制位数
返回:
int: 找到的大素数
"""
def is_sufficiently_large_prime(num: int) -> bool:
return num > (1 << length - 1) and is_prime(num)
lower_bound = 1 << length
upper_bound = (1 << length) + (1 << (length - 1)) # 增加一半长度以提高找到素数的概率
while True:
candidate = random.randint(lower_bound, upper_bound)
if is_sufficiently_large_prime(candidate):
return candidate
def prime_range(start: int, end: int) -> List[int]:
"""
生成指定范围内所有素数的列表。
参数:
start (int): 范围起始值(含)
end (int): 范围结束值(不含)
返回:
List[int]: 范围内的素数列表
"""
primes = []
for num in range(start, end):
if is_prime(num):
primes.append(num)
return primes
def primeFactorization(length): # 分解质因数
global p, q
q = get_large_prime_length(length)
while True:
d = random.randint(2, 10000)
if d % 2 == 0:
p = q * d + 1
if is_prime(p) == True:
break
else:
continue
else:
continue
primeList = prime_range(2, int(math.sqrt(d)))
result = [[0, 0] for i in range(len(primeList))]
for i in range(len(primeList)):
result[i][0] = primeList[i]
while d % primeList[i] == 0:
result[i][1] += 1
d = d // primeList[i]
if d != 1:
result.append([d, 1])
result.append([q, 1])
return result
def quickPower(a, b, c): # 快速幂
result = 1
while b > 0:
if b % 2 == 1:
result = result * a % c
a = a * a % c
b >>= 1
return result
def getGenerator(result): # get g
generator = random.randint(1, 1000)
while True:
if quickPower(generator, q, p) != 1:
generator += 1
else:
for i in range(len(result)):
if quickPower(generator, int((p - 1) / result[i][0]), p) == 1:
break
if i != len(result) - 1:
generator += 1
else:
break
return generator
def getSecretKey(): # get SK,x
x = random.randint(1, q)
return x
def getPublicKey(g, x): # get PK,h
h = quickPower(g, x, p)
return h
def treatMSG(msg): # 处理消息msg为整数
newmsg = ''
for i in msg:
newmsg += str(ord(i))
return int(newmsg)
def ChameleonHash(PK, g, m, r): # 变色龙哈希
CH = quickPower(g, m, p) * quickPower(PK, r, p) % p
return CH
def exgcd(a, b): # 扩展欧几里得
if b == 0:
return 1, 0, a
else:
x, y, gcd = exgcd(b, a % b)
x, y = y, (x - (a // b) * y)
return x, y, gcd
def Forge(SK, m1, r1, m2): # 求r'
x, y, gcd = exgcd(SK, q)
result = x * (m1 - m2 + SK * r1) % q
return result
if __name__ == "__main__":
print('calculating...')
print('')
length = 100 # 随机大素数长度
result = primeFactorization(length)
g = getGenerator(result)
SK = getSecretKey()
PK = getPublicKey(g, SK)
msg1 = 'i sent first message' # 消息1
msg2 = 'second message' # 消息2
newmsg1 = treatMSG(msg1)
newmsg2 = treatMSG(msg2)
rand1 = random.randint(1, q) # r
print('q=', q)
print('p=', p)
print('g=', g)
print('SK=', SK)
print('PK=', PK)
print('')
print('msg1=', msg1)
print('rand1=', rand1)
CH = ChameleonHash(PK, g, newmsg1, rand1)
print('CH=', CH)
print('-----------------')
print("使用Forge函数伪造第msg2的随机数rand2,以使得两个变色龙哈希相等")
print('msg2=', msg2)
rand2 = Forge(SK, newmsg1, rand1, newmsg2)
print('rand2=', rand2)
newCH = ChameleonHash(PK, g, newmsg2, rand2)
print('newCH=', newCH)
print("程序演示了如何利用变色龙哈希的特性,即对于同一公钥和生成元,不同的消息可以有相同的哈希值,前提是知道消息之间的关系和私钥。")
总结
将上述代码运行即可得到演示结果。
代码借鉴:https://github.com/Zipzapztc/ChameleonHash/blob/master/ChameleonHash.py