DLP离散对数
基础知识
简单了解几个数论的概念会更好的理解离散对数问题。
欧拉定理:对于任意的$a,m$只要$gcd(a,m)=1$,那么就有$a^{\phi(n)}\equiv 1 ~mod(~m)$
模指数的阶:与m互质的整数a,我们记满足$a^{n}\equiv1~(mod~m)$的最小正整数n为a模m的阶
推论:对于任意的$a,m$只要$gcd(a,m)=1$,$n$为a模m的阶,那么就有$n|\phi(n)$
原根:若存在与m互质的整数$g$,并且$g$模$m$的阶为$phi(n)$,那么我们称模m有原根,并称$g$为模$m$的一个原根。实际上就是阶等于$\phi(n)$的时候原根存在
离散对数
- 了解了指数、原根和阶的概念,其实还可以发现一个规律:设a模m的阶为$n$,那么根据同余的指数运算性质可以发现,$a^x\equiv b~mod(~m)$,这个指数运算得到的模结果其实发生循环,b的取值范围其实就为$b\in{a^0~mod~(m),…,a^{n-1}~mod~(m)}$,模的指数运算的结果存在周期性,其周期为$n$。
- 这里的$a$,在抽象代数的群论中有一个称呼即$生成元$,也称为本原元。设G是一个群(可以理解为一个集合,该集合里面的元素可以满足特定的运算,例如集合:${a^0~mod~(m),…,a^{n-1}~mod~(m)}$),如果集合中存在一个元素$a\in G$,通过对g进行重复的群运算,可以得到G中的每个元素,那么g就被称为G的生成元。
- 由上面的指数、原根和阶的定义,就可以引入一个离散对数的问题,已知模的幂
y、底数g、模数p,要我们求出指数x使得下面式子成立:- 求解这个问题通用解法只能是从
0~n-1(n为g模p下的阶)遍历,判断是否$g^k\equiv y~(mod~p)$ - 当模数
p比较小的时候,并且n也较小的时候,求得x算是比较容易的 - 但是当模数
p很大、下式的阶n也很大或者下式存在原根,那要遍历的范围就相当的大(这就导致了在有限的时间无法求得x,这就是著名的离散对数问题)
- 求解这个问题通用解法只能是从
$$
y\equiv g^x~(mod~p)\Leftrightarrow x=log_g(y)~mod~(p)
$$
- 离散对数和大整数分解问题类似,都是不能够在有限的时间内得到答案。基于大整数分解问题得到了著名的
RSA加密算法,而基于离散对数问题密码学家也提出了对应的加密算法如Diffie-Hellman密钥交换协议(简称DH密钥交换协议)、ElGamal加密算法、DSA数字签名算法、ECDLP椭圆曲线上的离散对数问题。
求解离散对数的方法
暴力破解法
暴力破解法是最一般的求解离散对数的算法,就是1遍历到n-1,计算每个模指数的值,直到找到一个指数使其满足$y \equiv g^x$才退出循环(由于计算阶还需要时间,这里一般n都是直接用模数的欧拉函数)
1 | def discrete_log(y,g,p): |
BSGS算法
-
BSGS算法本质上也是暴力破解,但是BSGS是经过算法优化后再进行遍历操作,时间复杂度会低一些,但是该算法的思想有用上
meet-in-the-middle即中间相遇攻击的思想。其推导过程如下:-
对于下面的式子,我们可以得到$yg^{b}\equiv g^{Ka}~mod(~p)$,其中$q\in{1,2,…,K}$,$a\in{1,2,3,…,\left\lceil \frac{p}{K}\right\rceil}$,这样$Ka-b\in{0,1,…,p-1}$,同样可以达到从
0~p-1的遍历效果。 -
接下来先选取一个
K(K取$\sqrt{p}$时间复杂度达到最小),遍历计算$y*g^{b}~mod(~p)$的结果,并且存储计算结果及其对应的b。 -
存储得到
b后,再遍历a的取值计算$g^{Ka}~mod(~p)$,并判断计算结果与前面计算的$yg^{b}$的其中一个相等(这里就会出现查找的步骤,使用哈希表查找应该是最快的),如果找到相等的数,那么也就找到了这个式子的指数x -
最后计算$x=Ka-b$,即可得到结果。
-
注意:BSGS有俩中构造方式一种就是
Ka-b还有一种是Ka+b这俩中形式,但是貌似Ka+b是论文原始的构造方法
-
$$
y \equiv g^x~(mod~p)\
\iff y \equiv g^{Ka-b} ~(mod~p)\
\iff y \equiv g^{Ka}g^{-b}~mod(~p)\
\iff yg^{b} \equiv g^{Ka}~mod(~p)
$$
- 下面是
BSGS的伪代码:
1 | Input: g,y,p |
- Python代码如下:
1 | def bsgs(p,g,y): |
Pohlig-Hellman算法
Pohlig-Hellman算法是经典用于加速离散对数求解的算法,只适用于一种模数比较特殊的情况即模数p要满足b-光滑数,该算法的本质其实就是将大整数下的离散对数问题,转换为若干个小模数下的离散对数问题,并且使用中国剩余定理求解
B-Smooth number(B光滑数):一个正整数它的所有质因数都不大于B,则该正整数就被称为B光滑数,而这个数B并不确定,但是它通常取决于算法的需要和计算资源。
Pollard λ算法
Pollard’s Rho算法
sagemath相关函数
sage中discrete_log就是利用Pohlig-Hellman算法和BSGS算法实现的。如果使用sage,只需要调用discrete_log这个函数即可。- 详细说明一下
discrete_log的参数,记要求的相关高次同余式如下:$base ^x\equiv a~mod(~n)$- 参数
a:就是模幂的结果 - 参数
base:作为底数 - 参数
ord:群的阶,如果知道群的阶就可以填入,可以加快计算 - 参数
bounds:指数的搜索范围,即在给定区间内寻找离散对数,可以加快搜索,格式(lower_bound,upper_bound)例如:(100,1000) - 参数
operation:群操作类型,默认为乘法 - 参数
identity:单位元,通常是函数内部自动处理,若自定义群结构就像ECC那样,可以手动设置 - 参数
inverse:取逆函数,默认为None,也可以传一个函数用于计算逆元 - 参数
op:自定义操作函数可选 - 参数
algorithm:默认是bsgs也就是默认使用bsgs算法求离散对数,还可以有rho选项(使用pollard_rho算法求离散对数),lambda选项(λ 算法Pollard lambda,又叫“kangaroo”方法),automatic选项Sage自动判断哪种算法更优
- 参数
1 | discrete_log( |
discrete_log的使用:
1 | b = Mod(2,37) # 先使用Mod(n,m)创建一个底数为n,模数为m的对象,返回的是整数n,n作为Z/mZ中的元素 |
discrete_log源码如下:
1 | def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, inverse=None, op=None, algorithm='bsgs'): |
离散对数加密
ElGamal加密算法
ElGamal加密算法是一种基于离散对数难题的公钥加密算法,由Taher ElGamal于1985年提出。是非对称加密算法。
ElGamal提出了一个基于$(Z^{}_p,·)$(即模数p为素数)上离散对数问题的公钥密码体制.设p是一个素数,使得$(Z^{}_p,·)$上的离散对数问题是最难处理的
-
ElGamal加密算法如下:-
首先选取一个大素数
p,令生成元为g -
选取明文空间$0<m<p$范围中的整数
-
而密文也是$0<c_1,c_2<p$范围内的整数。
-
选取密钥
K有四个元素,其中三个元素作为公钥,一个元素作为私钥,密钥满足关系$K\in{(p,g,a,h)|h\equiv g^a mod~p}$,其中$(p,g,h)$是公钥,$a$是私钥 -
加密函数如下:
- $c_1=g^k~mod~p$
- $c_2=m*h^k~mod~p$
- $e_K(m,k)=(c_1,c_2)$
-
解密函数:$d_K(c_1,c_2) = c_2(c_1^{a})^{-1}~mod~p$
-
-
解密推导过程:$c_2(c_1^{a})^{-1}=m*h^{k}((g^k)^a)^{-1} = mh^{k}((g^a)^k)^{-1}=mh^k(h^k)^{-1}=m$
注:ElGamal密码体制中,加密运算是随机的,因为密文既依赖于明文m,也依赖于加密者所选择的随机数k,所以对于同一个明文,会有p-1个可能的密文,这个加密体制其实是概率加密
Diffie-Hellman密钥交换协议
-
Diffie-Hellman密钥交换协议简称DH密钥交换 -
DH密钥交换也是基于离散对数问题的。设$p$是一个素数,使得$(Z^*_p,·)$上的离散对数问题是难处理的,另g为生成元
-
密钥交换者A:
- 随机选择一个整数$x_A,0≤x_A≤p-2$
- 计算$y_A=g^{x_A}~mod~p$,并且将$y_A$发送给B
-
密钥交换者B:
- 随机选择一个整数$x_B,0≤x_B≤p-2$
- 计算$y_B=g^{x_B}~mod~p$,并且将$y_B$发送给A
-
双方得到对方发送的值后进行计算,交换者A计算:$k_A=y_B^{x_A}~mod~p$;交换者B计算$k_B=y_A^{x_B}~mod~p$
-
这样就可以得到:$k_A=y_B^{x_A}=(g^{x_B})^{x_A}=(g^{x_A})^{x_B}=y_A^{x_B}=k_B$

- 由于离散对数的困难性问题,这就导致了攻击者知道$Y_A$或者$Y_B$的其中一个得到$K$是非常困难的。
- 并且这里又引出一个新的困难性问题CDH问题:给定$g^{x_A}$,$g^{x_B}$,计算$g^{x_Ax_B}$也是很困难的。
相关题目
题目1_MMACTF 2015_Alicegame
-
题目链接如下:[MMACTF 2015]Alicegame | NSSCTF
-
题目附件如下:
1 | # -*- coding: utf-8 -*- |
- 通过看代码,其实发现该代码实现的是一个
ElGamal加密算法,并且会先让用户输入10组明文m和随机数r,并将加密后的c1,c2发送给用户,最后程序会才会对flag进行加密,并且选用随机数r。 - 先回顾一下
ElGamal加密算法:- 选取一个素数
p,生成元g,再生成一个x(作为私钥),计算$h\equiv g^x~mod(~p)$,将g、p、h作为公钥,x作为私钥。 - 选择随机数
r和要加密的明文m,计算$c_1 \equiv g^r ~mod(~p)$,以及对密文进行加密$c2 \equiv m*h^r~mod(~p)$ - 然后将$c_1、c2$发送出去
- 解密就是这样:$(c_1^x)^{-1}*c2 \equiv g^{-rx}mh^{r}\equiv g^{-rx}mg^{xr}\equiv m~mod(~p)$,又
m<p所以这样一定能求出m。
- 选取一个素数
- 接下来从本地的已知数据入手:
$$
\begin{array}{l}
c_{10} \equiv g^{r_0} ~mod(~p)\
c_{20} \equiv m_0h^{r_0}~mod(~p)\
…\
c_{19} \equiv g^{r_9}~mod(~p)\
c_{29} \equiv m_9h^{r_9}~mod(~p)\
\end{array}
$$

