《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金鑰交換的安全所依賴的困難問題叫做離散對數問題,下面我們來看一下什麼是離散對數問題。
首先,考慮這樣一個問題,
\[2 = 3^x \mod 5 \]是不是很快就能得出答案?
但是如果我們將式子中的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等式定義的曲線:
\[y^2 = x^3 + ax+b \mod p \]其中
\[a = -3 \\ b = 41058363725152142129326129780047268409114441015993725554835256314039 467401291\\ p = 2^{256}-2^{224}+2^{192}+2^{96}-1 \]曲線所定義的群的階為:
\[n = 11579208921035624876269744694940757352999695522413576034242225906106 8512044369 \]生成元(基點)
\[G = (484395612939064517590525852527979142027629495260417479958440807170 82404635286, 3613425095674979579858512791958788195661110667298501507187 7198253568414405109) \]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曲線,定義如下:
\[y^2 = x^3 + 486662x^2+x \mod p, p = 2^{255}-19 \]它所定義的群的階
\[n = 2^{255}+27742317777372353535851937790883648493 \]基點
\[G = (9, 14781619447589544791020593568409986887264606134616475288964881837 755586237401) \]使用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”指出幾種常見的實現均為執行該檢查。