一
实验三:素性测试
摘要
在现代密码体系中,大素数的判定和寻找起着很关键的作用。因此素性测试也是一个很重要的话题,本文通过介绍三种基本的素数判定测试——Fermat素数测试、 Solovay-Strassen素数测试、Miller-Rabin素数测试三者的原理以及代码实现,最后比较各个方法的优缺点。
关键词:素数测试 算法 概率
Abstract
In modern cryptosystems, the determination and search of large prime numbers play a key role. Therefore, primality testing is also a very important topic. This paper introduces the principles and code implementation of three basic prime number determination tests - Fermat prime number test, Solovay Strassen prime number test, Miller Rabin prime number test, and finally compares the advantages.
## Fermat 测试 **原理:** 基于费马小定理的素数判定 若$m$为素数,则对于任意的$a,(a,m) = 1$有 $$a^{m-1}\equiv 1(mod \ m)$$ **算法流程:** 1.随计选取$a\in[1,m-2]$ 2.计算$(a,m)$,如果两者不互质,则跳出,判断为合数。 3.计算$a^{m-1}mod\ m$结果为$1$,$p$可能为素数,否则,$p$一定为合数
算法概率
素数的分布是不规律的,但是可以用\(\frac{1}{2}\)近似考虑。
重复上述过程\(k\)次,如果每次得到\(m\)为素数,则\(m\)为素数的概率为\(1-\frac{1}{2}^k\),但是一些合数(被称为卡迈克尔数,一种强伪素数),可以通过费马小定理的检测。安全性较低。
算法实现
使用NTL中的RandomBnd生成一定范围内的随机数,值得注意的是,随机数可能为0
Powermod计算\(a^{m-1}mod\ m\)
GCD计算\((a,m)\)
代码
void Fermat(ZZ p) {
ZZ a, phi;
phi = p - 1;
bool flag = true;
for (int i = 1; i <= 100; ++i) {
RandomBnd(a, p - 1); if (a == 0) continue;
if (GCD(a, p) != ZZ(1)) {
flag = false;
break;
}
if (PowerMod(a, phi, p) != ZZ(1)) {
flag = false;
break;
}
}
if (flag) printf("It's a prime\n");
else printf("It's not a prime\n");
return;
}
Sage
mayp = input()
print("Fermat try...")
def Ksm(a, b, p):
ret = 1
while b != 0:
if (b % 2 == 1):
ret = ret * a
ret %= p
a = a * a % p
b = b >> 1
return ret
# try 100 times
flag = 1 # is a prime
for i in range(100):
a = random_prime(2 ^ 100)
while GCD(a, mayp) != 1:
a = random_prime(2 ^ 100)
if (Ksm(a, mayp - 1, mayp) % mayp) == 1:
continue
flag = 0
if flag == 1:
print("we think %d is a prime." % mayp)
else:
print("%d is not a prime." % mayp)
Solovay-Strassen算法
算法原理
基本原理是基于二次剩余的算法
即
算法流程:
1.随机选取\(a \in [1,m-1]\)
2.计算\(a^{\frac{m-1}{2}}\),判断是否同余于\(1,-1\).若同余,进行下一步,不同余,说明\(m\)为合数
3.求得\((\dfrac{a}{m})\),判断是否相等,若相等,则重复上述操作,不相等,说明\(m\)为合数
算法实现
在这里,可以利用NTL中\(Jacobi\)可以代替求取勒让德符号求得二次剩余的值
Powermod计算\(a^{m-1}mod\ m\)
GCD计算\((a,m)\)
代码
void Solovay_Strassen(ZZ p) {
ZZ a, m;
m = (p - 1) / 2;
bool flag = true;
for (int i = 1; i <= 100; ++i) {
RandomBnd(a, p - 1); if (a == 0) continue;
if (GCD(a, p) != ZZ(1)) { flag = false; break; }
ZZ tmp = PowerMod(a, m, p);
if (tmp != ZZ(1) && tmp != p - 1) { flag = false; break; }
if (tmp != (Jacobi(a, p) + p) % p) { flag = false; break; }
}
if (flag) printf("It's a prime\n");
else printf("It's not a prime\n");
return;
}
Sage
print("Solovay-Strassen try...")
# kronecker(p, q)
# try 100 times
flag = 1
for i in range(100):
a = random_prime(2 ^ 100)
while GCD(a, mayp) != 1:
a = random_prime(2 ^ 100)
if Ksm(a, (int)(mayp - 1) >> 1, mayp) == (kronecker(a, mayp) % mayp):
continue
flag = 0
if flag == 1:
print("we think %d is a prime." % mayp)
else:
print("%d is not a prime." % mayp)
Miller-Rabin算法
算法原理:
该算法基于费马小定理和一个素数特性
素数特性:当\(p\)为素数且\(p > 2\)时,不存在\(1(mod p)\)的"非平凡平方根"
非平凡平方根:\(1^2(mod p)\)和\((-1)^2(mod p)\)总是得到\(1\),这两个数即为"平凡平方根"
又有费马小定理\(a^{p-1} \equiv 1(mod \ p)\)
而除\(2\)以外,p都为质素数,故\(p - 1\)可以被拆成\(2^{t}d\)
故$$a{2d}\equiv 1(mod p)$$
对于上述结论,可以得到该算法的核心。
算法流程:
1.对上述式子取平方根,结果为\(1\),重复1.,结果为\(-1\),判断终止,\(m\)可能为素数,也可能是合数。其他结果,\(m\)一定为合数。
判断到\(2^d\)终止。
算法概率:一次误判的概率为\(\frac{1}{4}\)
代码
void Miller_Rabin(ZZ p) {
ZZ a , t = (ZZ)0, m, tmp;
tmp = p - 1;
while (tmp % 2 == 0) {
tmp = tmp / 2;
t++;
}
bool flag = true;
for (int i = 1; i <= 100; ++i) {
RandomBnd(a, p); if (a == 0) continue;
tmp = p - 1;
for (int j = 0; j < t; ++j){
tmp = tmp / 2;
ZZ ans = PowerMod(a, tmp, p);
if (ans == p - 1) break;
if (ans != 1) {
flag = false;
break;
}
}
if (!flag) break;
}
flag ? printf("It's a prime\n") : printf("It's not a prime\n");
return;
}
Sage
print("Miller-Rabin try ...")
flag = 1
s = 0
d = mayp - 1
while d % 2 == 0:
s += 1
d //= 2
# try 100 times
for i in range(2):
a = random_prime(2 ^ 100)
flaga = 1
if Ksm(a, d, mayp) == 1:
flaga = 0
else:
for r in range(s):
if Ksm(a, ((2 ^ r) * d), mayp) == (mayp - 1):
flaga = 0
if flaga == 1:
print("we found that %d is a witness that %d is not a prime" % (a, mayp))
flag = 0
break
if flag == 1:
print("we think %d is a prime." % mayp)
else:
print("%d is not a prime." % mayp)
结果分析
| 素数判断方法| 优势 | 缺点 |单轮误判概率|
| :-------- | --------😐 :--: | |
| Fermat| 原理简单,容易实现 | 概率性算法,鲁棒性差|\(\frac{1}{2}\) |
| Solovay-Strassen | 算法简单,且应用广泛| 概率性算法 | \(\frac{1}{2}\) |
| Miller-Rabin | 简单,“精度”可调,实用性强,极为广泛 | 概率性算法 | \(\frac{1}{4}\) |
值得注意的是:
这三个算法的时间复杂度都为\(O(klog^3n)\),k为判定次数,n为待测数。
由实验样例可知,前三个为合数,后三个为素数,但是Fermat素数判定,却将前两个样例判断为素数,说明其安全性不高,且容易被卡迈克尔数(强伪素数)攻击。由此可见,Solovay-Strassen算法和Miller-Rabin 算法明显优于Fermat素性判断,这两个算法安全性较高。
且当Miller-Rabin 算法迭代次数到40+次,错误概率为\(1.8*10^{-24}\)足够低了,再提高准确度也无意义了。
实际上,Miller-Rabin 素性检验算法是目前最适合被用来构建密码安全体系的素数生成模块的算法。
参考资料:
[1]许斌,张艳硕,吕正宏.常见素性检验算法的比较分析[J].北京电子科技学院学报,2021,29(04):25-37.
[2] 王小云,王明强,孟宪萌.公钥密码学的数学基础[M] 北京:科学出版社 2013.1
[3] NTL 电子文档[EB/OL] https://libntl.org/doc/tour.html/
[4] Sage电子文档[EB/OL] https://doc.sagemath.org/pdf/en/a_tour_of_sage/