在公钥加密体系中,除了基于大整数分解问题的RSA加密体制,另一类重要的加密体制是基于离散对数的难解性,如ECC椭圆曲线加密、Diffie-Hellman算法、ElGamal算法等。为了解决离散对数问题,本文首先介绍一些近世代数的基本知识,之后介绍几类基于离散对数的加密体制,最后列出几道CTF中的相关赛题。
设G是非空集合,若在G内定义一种代数运算⨀,且满足下列4个条件,则称G(对运算⨀)构成一个群:
其中运算 ⨀ ⨀ ⨀可以是通常的乘法或者是加法。若 ⨀ ⨀ ⨀为乘法,则称 G G G为乘法群,单位元记为 1 1 1。若 ⨀ ⨀ ⨀为加法,则称 G G G为加法群,单位元记为 0 0 0。
一般情况下,记: a ⨀ a ⨀ … ⨀ a ⏟ k 个 \underbrace{a⨀a⨀…⨀a}_{k个} k个 a⨀a⨀…⨀a= a a ak
群 G G G所含元素的个数,称为该群的阶。 若群 G G G含有有限个元素,则称 G G G为有限群,否则,为无限群。
若对群 G G G中任何 a , b a,b a,b ∈ \in ∈ G G G,有 a ⨀ b = b ⨀ a a⨀b = b⨀a a⨀b=b⨀a,则称 G G G为交换群或 A b e l Abel Abel群。
定义:设 ( G , ⋅ ) (G,· ) (G,⋅)是一个群,如果群 G G G中存在一个元素 α \alpha α,使得对群 G G G任意元素 b b b都存在一个整数 i i i,使得 b = b= b= a a ai则我们称 G G G是一个循环群。元素 α \alpha α是 G G G的一个生成元。
整数的加法群是一个无限循环群,它由1生成。在这种情况下,幂被解释为用加法合成的,因此 n n n是1的n次幂。
例:(Z6, ⨁ \bigoplus ⨁)是循环群,其中Z6={0,1,2,3,4,5}, ⨁ \bigoplus ⨁为模6加法,其中生成元为1或5。
生成元的含义可以理解为:1或5的加法,可以实现群Z6内所有的元素。
5 mod 6 = 5,35 mod 6 = 5
10 mod 6 = 4,40 mod 6 = 4,
15 mod 6 = 3,45 mod 6 = 3
20 mod 6 = 2,50 mod 6 = 2
25 mod 6 = 1,55 mod 6= 1
30 mod 6 = 0,60 mod 6= 0
所以通过生成元5的模6加法,可以得到群内的所有元素,实现循环群。而2,3,4不能作为生成元,是因为这些元素的模6加法并不能得到群内的所有元素,而且并不是连续循环的数。
乘法循环群也是同样的道理。
有限循环群的生成元还具有以下性质:
(元素的阶):循环群 ( G , ⋅ ) (G,·) (G,⋅), α \alpha α为 G G G的一个生成元, 1 1 1为 G G G的单位元, G G G的阶为 n n n,则: a a an = 1 =1 =1
若集合 R R R上定义了两种二元运算: + + +(加法)和x(乘法),且满足下列四个条件,则称 R R R 对这两种运算构成了一个环,记为 ( R , + , x ) (R,+,x) (R,+,x):
本质上说,环就是一个集合,我们可以在其上进行加法、减法 [ a − b = a + ( − b ) ] [a - b = a + ( - b ) ] [a−b=a+(−b)]和乘法而不脱离该集合。
例:实数上所有n阶方阵的集合关于加法和乘法构成一个环。
定义:环如果还满足乘法的交换律,即对于 R R R中任意元素 a 、 b a、b a、b,有 a b = b a ab=ba ab=ba成立,则被称为是交换环
例:偶整数集合(包括正数、负数和0)记为 S S S,在普通加法和乘法运算下是交换环;实数上所有n阶方阵的集合关于加法和乘法则不构成一个交换环。
定义:整环是满足以下公理的交换环:
例:普通加法和乘法运算下的整数集合(包括正数、负数和0)是一个整环。
定义:设 F F F是一个交换环,若 F F F中的所有的非零元素对乘法都存在逆元,则 F F F称为一个域。如果一个域所包含的元素是有限的则称此域是有限域,否则称为无限域。有限域中所含元素的个数称为有限域 ( R , + , x ) (R,+,x) (R,+,x)的阶。
本质上说,域就是一个集合,我们可以在其上进行加法、减法、乘法和除法而不脱离该集合。
除法又按以下规则来定义: a / b = a ( a/b=a( a/b=a( b b b-1 ) ) )。
例:有理数集合、实数集合以及复数集合都是域。而所有整数的集合并不是一个域,因为并不是集合中所有的元素都有乘法逆元;实际上,整数集合中只有元素 1 1 1和 − 1 -1 −1有乘法逆元。
群、环、域的公理总结:
定义1: 有限域又常称为 G a l o i s Galois Galois域,并以 G F ( q ) GF(q) GF(q)或 F q F~q~ F q 表示,其中 q q q表示有限域的阶。
定义2: 设 F 1 、 F 2 F~1~、F~2~ F 1 、F 2 是两个域,称 F 1 F~1~ F 1 到 F 2 F~2~ F 2 的一个可逆映射 σ \sigma σ为一个同构(映射),如果 σ \sigma σ是保持运算的映射,即对任意的 a , b a,b a,b ∈ \in ∈ F 1 F~1~ F 1 ,有: σ \sigma σ(a + b) = σ \sigma σ(a) + σ \sigma σ(b), σ \sigma σ(a · b)= σ \sigma σ(a) · σ \sigma σ(b)
定理3: 设 F F F是有限域,则有:
定义: 设 F F F是一个域,多项式f(x)=anxn+···+a1x+a0,其中 a i a_i ai ∈ \in ∈ F F F, n n n ∈ \in ∈ N N N。
若 a n ≠ 0 a_n\not=0 an=0,称 n n n为该多项式的次数,称为首项系数。
对于域 F F F上 x x x的多项式的全体组成的集合记为 F [ x ] F[x] F[x]。多项式 a ( x ) a(x) a(x) 的次数记为 d e g ( a ( x ) ) deg(a(x)) deg(a(x))
设存在多项式 f ( x ) f(x) f(x)与 g ( x ) g(x) g(x),满足:
容易验证 F [ x ] F[x] F[x]对这样定义的多项式加法与乘法构成一个交换环,称为多项式交换环。
不可约多项式: 设 f ( x ) f(x) f(x)是 F [ x ] F[x] F[x]上的一个次数大于零的多项式,如果它不能分解成两个低次数的多项式的乘积,则称 f [ x ] f[x] f[x]是 F F F上的不可约多项式。
Z p Z~p~ Z p 上的离散对数问题是指对于循环群 Z p Z~p~ Z p (p是一个素数), α \alpha α是群 Z p Z~p~ Z p 的生成元,对于任意的 c c c ∈ \in ∈ Z p Z~p~ Z p 寻找唯一 的整数 d d d ( 0 (0 (0 ≤ \leq ≤ d d d ≤ \leq ≤ p − 1 ) p-1) p−1)满足: C ≡ a d ( m o d p ) C≡a^d(modp) C≡ad(modp)
我们把整数 d d d记为 l o g log logα c c c,并称之为离散对数。
背景: ElGamal是建立在解有限乘法群上的离散对数问题的困难性基础上的一种公钥密码体制。
算法描述:
计算离散对数的算法:
背景: Diffie-Hellman算法由Whitfield Diffie 和 Martin Hellman 提出,该算法的安全性也是基于一般有限域上的离散对数问题的难解性。该算法的目的是使两个用户能安全地交换密钥,以便在后续的通信中用该密钥对消息加密。该算法本身只限于进行密钥交换。
题目链接buu平台
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random
n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)
# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
加密过程为:c = pow(m, bytes_to_long(flag), n)
即已知c, m, n求离散对数:bytes_to_long(flag) = log(mmodn) (c mod n)
题解:
m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499
n = 2 ** 512
import sympy
from Crypto.Util.number import *
flag=sympy.discrete_log(n,c,m)
print(long_to_bytes(flag))