《Real-world Cryptography》读书笔记1 密钥交换
提到密钥交换,必须要提到的就是这两位,
2015年图灵奖得主,Diffie(右)和Hellman(左),二人在
一文中提出了密钥交换、非对称加密、数字签名等概念,可以说是公钥密码学的开山鼻祖,更是提出了第一个密钥交换方案,Deffi-Hellman(DH) Key Exchange。
前面几章介绍了哈希函数、对称加密、授权加密等概念,Alice和Bob似乎可以愉快地继续通信而不用担心被传信兵偷窥或者篡改了,但是这有一个前提,就是Alice和Bob有过一次面对面的解出,二人商议好了用于加密的私钥。人家是国王和公主,所以见一次面很容易,但是我们平时上网,总不能访问一个网站之前先去见服务器一面,要解决这一问题,用到的技术就是这一章的内容,密钥交换,本章共介绍两种密钥交换技术,分别是DH密钥交换和椭圆曲线(ECDH)密钥交换。
1 数论基础
第一章中对密钥交换做了非常形象的比喻,
实际应用中我们往往是通过某种计算困难的数学问题来达到图形重叠后难以区分的效果的,所以在介绍具体的方案之前,我们先介绍理解该方案所需要的数学基础。
群(Group)
群,定义如下
- A set of elements
- A binary operation (like + or ×) defined on these elements
同时,一个群应当有如下四个性质
例如,全体整数就构成一个加法群,单位元为0,每个元素的逆元为相反数;全体有理数就构成一个乘法群,单位元为1,每个元素的逆元为倒数;全体自然数构不成群,因为元素没有逆元。
DH中使用的是乘法群。
素数
素数是密码学中最重要的一类数,大量的密码方案都依赖于素数的性质,素数是什么大家应该都知道,输出指定范围内的素数应该是大一编程课的经典题目了,不过这里还是重申一下其定义:
A prime number is a number that can only be divided by 1 or by itself.
我们下面要讲到的DH密钥交换协议建立在一个正整数1,2...,p-1构成的群上,其中p是素数,素数到底有哪些妙用,会在后面几章里陆续地揭开。
模运算
上面说到DH密钥交换协议建立在在最大值为p-1的群上,这时候就有一个问题,不是说群的第一个性质就是封闭性吗?那我随便找两个大数一乘不就比你的p-1大,还谈何封闭?
这就引出了接下来的概念,模算数(模加、模乘、模逆)
模:$ 7 \equiv 2 \mod 5$
模加:\(4+3 \equiv 2 \mod 5\)
模乘:\(4 \times 3 \equiv 2 \mod 5\)
模逆:\(2 \times 3 \equiv 1 \mod 5\),所以\(2^{-1} \equiv 1 \mod 5\)
这样一来就容易理解了,DH使用的群上的二元操作是模运算。
虽然这样,但还是有问题,为什么这个群里不包含0呢,还有就是为什么p一定需要是素数。第一,0这个元素在以1为单位元的乘法群中没有逆元,所以不能包含进来;第二,p是素数保证了这个群里的每个元素都有逆元(如果一个数和p互素,那该数一定存在模p的逆元)
生成元(generator)
在介绍生成元之前我们先介绍一个与之相关的概念,子群(subgroup):子群首先也是一个群,自然就满足群的定义和性质,此外,子群是群的一个子集。例如全体整数构成的加法群就是全体有理数构成的加法群的子群。如果该子群内的元素都是由一个生成元(generator)或者基(base)通过不断地同自身进行模乘生成而来,我们就称这是一个生成子群(cyclic subgroup)。
例如以4为生成元就构成了一个元素为1和4的子群。
2 离散对数问题和DH密钥交换
说了这么多之后,我们进入本章的正题。
离散对数问题(DLP)
前面说过,公钥密码算法通常建立在计算困难的问题之上,DH密钥交换的安全所依赖的困难问题叫做离散对数问题,下面我们来看一下什么是离散对数问题。
首先,考虑这样一个问题,
是不是很快就能得出答案?
但是如果我们将式子中的5换成另一个更大的模数,
这时还能继续一眼看出\(x\)的解吗?这种给出\(3^x\)求解\(x\)问题,我们就称之为离散对数问题。需要注意的是,离散对数问题已经被证明是计算困难问题,也就是说,在适当的参数设置下,不存在多项式时间内的解决离散对数问题的算法。还有一点,我们这里说的计算困难指的都是经典计算下,不考虑量子计算的问题。
DH key exchange
这样一来,我们已经学习了DH密钥交换协议的所需的全部基础知识,接下来就是正菜了,DH密钥交换的算法描述如下:
首先我们双方协商得到一个大素数\(p\)和生成元\(g\),这是公开的信息,每个参与这个系统的人都知道的参数;随后我们(每个参与者)选择一个随机数\(x\)作为自己的私钥,相应地,以\(g^x\)作为自己的公钥,由于离散对数问题的计算困难性,攻击者无法从公钥中恢复出私钥的信息,假设某个参与者的公钥是\(g^a\),而我想和他建立通信并且我的私钥是\(d\),这样一来我就可以使用它的公钥和我的私钥生成共享密钥\(g^{ab}\),对方亦然。
DH in real-world
我们前面说双方要提前协商好\(p\)和\(g\),那具体实现中是怎么选择这两个值的呢?
首先其基本原则是
- 模数\(p\)越大越安全,目前较为通用的长度是2048-bit
- 生成元的模幂要易于计算
可以看到,两则要求都比较宽泛,所以就衍生出了各种各样的标准以及各种各样的攻击,目前最佳的标准选择为RFC 7919(https://www.rfc-editor.org/info/
rfc7919),它使用一个2048bit长的模数p
和生成元2(可直接使用移位操作进行幂运算)。
到最后,我们来看一下DH都有哪些缺点:
- 不能抵抗主动攻击
- 密钥大,如2048bit,在一些空间受限的场景下(如IOT设备)不适用
- 标准繁多,实现混乱,安全隐患大
这些缺点导致了实际中的DH使用较少,我们会在后面给出弥补这些缺点的替代方案。
3 椭圆曲线密钥交换(ECDH)
上一部分介绍的DH密钥交换是建立在一个以大素数p为模数的群上,那在其他的群上能不能也使用相似的方法构造一个密钥交换协议呢?答案是肯定的,并且能够建立比DH性能更好、占用空间更小的密钥交换协议,就是这一部分要讲的,椭圆曲线密钥交换。
什么是椭圆曲线?
首先,第一个问题,什么是椭圆曲线?
顾名思义,椭圆曲线就是由坐标轴上的一系列点构成的曲线,这些点的横纵坐标满足某些要求,
其中\(a_1, ...a_6\)表示某些常量,经过发展,今天多数情况下使用的曲线为下面这种简化形式,
一条简化形式的曲线大概长这个样子
其中P、Q为曲线上的两个点。实际上,一条椭圆曲线上的点同样构成一个群,也就是说,这些点也有一个二元运算,我们一般称之为点加。
点加的计算方式很简单,过P、Q两点做一条直线,这条直线同椭圆曲线交于另外一点,过这个交点做一条垂直于x轴的垂线,同椭圆曲线交于一个新的点,这个新的点就是P+Q。如果某个点要同自身相加,即倍点操作,同点加相似,唯一不同的地方在于最初的连线变为过该点做一切线,
但在某些情况下,两个点的连线并不能和曲线交于另一点,如下图
这时,我们一般将二者相加的结果定义为一个无穷远点(point at infinity),记为\(O\),类比于有理数中的相反数相加得到0,这个无穷远点也有和0相似的性质,如任一点同其相加结果不变:\(P+O = P\)。
椭圆曲线离散对数问题(ECDLP)
这样一来,对椭圆曲线已经有了一个大致的了解,那我们来看一下它同DH还差了哪些东西
- 椭圆曲线是一系列点的集合,并且这些点上定义了一个二元操作;椭圆曲线上的二元操作满足封闭性、结合律,存在单位元\(O\),每个点存在逆元(关于x轴对称的点),这样一来就定义了一个加法群
- 模数,需要一个模数我们才能保证群中只有有限个点
- 困难问题,需要有一个类似于离散对数问题的困难问题来保障安全性,类似于DH中计算生成元模幂的方式,可能也需要一个生成元
所以首先我们要选一个模数,选好模数后,实际使用的椭圆曲线看起来应该是右边这个样子
这个困难问题我们一般称作椭圆曲线离散对数问题(ECDLP),它被认为是困难问题,描述如下:
我们定义一个基点\(G\),即前文中所提到的生成元,对\(G\)进行累加(加法群的累加,类似于前文中乘法群的幂),有\(P = G+G+...+G(x次)=[x]G\),椭圆曲线离散对数问题即给出基点\(G\)和累加的结果\(P\),求解进行的加法的次数,即\(x\)。实际中,我们通常称\(x\)为标量(scalar),乘这一过程为标量乘,对应DH中的模幂运算。
ECDH
这样一来,无需多言,ECDH也就顺理成章了,
ECDH比之于DH的优势主要在于参数小,只需256比特的私钥即可提供128比特的安全强度,DH提供同样的安全强度则需要至少2048比特的私钥长度;同时,ECDH在标准制定、安全风险方面也有着一些优势。
ECDH in real-world
对于ECDH的参数,一般以曲线为准,一旦选择某一条曲线,其余参数也就都确定了。其中,最常见的两个标准曲线分别为NIST提出的P-256以及非NIST提出的Curve25519。
NIST FIPS 186-4, “Digital Signature Standard,”是NIST在2000年提出的签名标准,在这个标准中包含了15条曲线,其中P-256是如今被使用最为广泛的曲线(在另一个2010年提出的标准Standards for Efficient Cryptography (SEC) 2, v2被称作secp256r1),它是由简化版本的Weierstrass等式定义的曲线:
其中
曲线所定义的群的阶为:
生成元(基点)
P-256提供了128bit的安全性,此外,同一标准内的P-521提供了256bit的安全性
题外话:什么叫128比特的安全性或者说安全强度?
对称算法评估要求目前不存在比遍历复杂度更低的攻击算法。所以对称算法安全强度基本上就是密钥长度。公钥算法依赖于某个困难问题,比如大整数分解、离散对数问题。可能已经有很多优于暴力搜索的技术,比如筛法。这个时候大整数分解就是个亚指数时间复杂度问题,到底多复杂显然不能用密钥长度去度量。按照当前提出的技术可以估计他的复杂度,估出来的差不多就是安全强度。或者说,安全强度大概就是依据当前的最优的算法,我们使用什么水平的算力能够恢复出私钥。这也解释了为什么256比特的ECDH提供的安全强度就同2096比特的DH密钥相当。
虽然P-256仍是今天使用最多的椭圆曲线参数,但它的安全性不能得到保证;以Daniel J. Bernstein为主的许多研究者发现多个NIST标准化的曲线藏有只有NSA知道的后门(potentially be part of a weaker class of curves)。
当前P-256虽然还未被发现安全后门,但其各项参数都是由一个未声明选择理由的参数作为种子生成的
NIST至今没有给出选择它的原因。因此,选择不带有政治因素,参数选择全部公开的曲线是更佳的选择,比如,Daniel J. Bernstein提出的Curve25519,它被包含在2016年发布的标准RFC 7748, “Elliptic Curves for Security,”中,提供了128bit的安全性,此外还存在一个Curve448的版本提供了224比特的安全性。
Curve25519使用了一条Montgomery曲线,定义如下:
它所定义的群的阶
基点
使用Curve25519的ECDH也被叫做X25519,是更推荐使用的密钥交换协议。
相较于DH,ECDH有着巨大的优势,密钥尺寸小、不存在强力的攻击、曲线也较好的被标准化了,所以在能使用ECDH的情况下请务必不要使用DH。
4 小子群攻击(Small Subgroup Attack)
前文中提到了ECDH比DH被更好的标准化了,这一点是非常重要的,所以使用DH意味着有可能使用的是已经被攻破的标准(如RFC5114)、过于宽松的协议、甚至是使用了已经被攻破了的DH群等等。
如果必须要使用DH,请务必遵循最新的标准。安全的DH标准都使用了形如\(p = 2q+1\)的安全素数作为模数,其中\(q\)也是一个素数,这样的模数下,该群的阶为\(p-1=2q\),并且根据拉格朗日定理,它只存在两个子群,他们的阶分别为\(2\)和\(q\),这样性质避免了出现这一节的主角,小子群攻击。
就以刚才说过的阶为2的小子群为例,如果使用的群存在这样一个小子群,攻击者就可以选择一个\(-1\)作为公钥发给Alice,Alice收到这个伪造的公钥后,使用私钥计算\(-1^x\),随后攻击者通过观察得到的共享密钥是1还是-1即可判断Alice的私钥是奇数还是偶数,也就是知道了Alice的私钥的最低位。好的标准里只有这样一个小子群,攻击者难以威胁私钥安全,但如果使用了不安全的参数,就可能存在许多小子群,攻击者进而可以恢复出更多有关私钥的信息,甚至恢复出完整的私钥。
杜绝这一攻击其实非常简单,只需要检查一下收到的公钥是不是在我们的目标群上即可,但不幸的是,2016年一项研究检查了20个DH实现无一进行了这一种检查(see “Measuring small subgroup attacks against Diffie-Hellman” from Valenta et al.)。
读者可能已经注意到了,我们前面提到的椭圆曲线的参数如P-256是素数阶的,不存在子群,那是不是就能彻底杜绝小子群攻击了呢,答案是否定的。
2000年,Biehl等人发现即便是在素数阶的椭圆曲线群尚小子群攻击仍是可能的,它们将这一攻击称作invalid curve attack,无效曲线攻击(弱曲线攻击)。
还是以P-256为例,它的曲线是\(y^2 = x^3+ax+b\),但是在实现标量乘时其实是不考虑\(b\)的,于是攻击者就能够找到一个\(b\)值,得到另一条曲线存在多个小子群进而实现攻击。同样,也可以检查一下收到的公钥是不是在原来的曲线上进而避免这种攻击,同样的不幸,2015年“Practical Invalid Curve Attacks on TLS-ECDH”指出几种常见的实现均为执行该检查。