背景
國密,是國家商用密碼的簡稱,由國家密碼管理局制定算法標準,同時也制定了大量的產品及接口規範以及應用場景。 linux
隨着近年來外部的國際貿易衝突和技術封鎖,內部互聯網的快速發展,IoT 領域的崛起,以及金融領域的變革愈演愈烈。擺脫對國外技術和產品的過分依賴,建設行業網絡安全環境,加強我國行業信息系統的安全可信顯得尤其必要和迫切。git
密碼算法是保障信息安全的核心技術,尤爲是最關鍵的銀行業核心領域長期以來都是沿用 3DES、SHA-一、RSA 等國際通用的密碼算法體系及相關標準。 web
2010 年末,國家密碼管理局公佈了我國自主研製的「橢圓曲線公鑰密碼算法」(SM2 算法)。爲保障重要經濟系統密碼應用安全,國家密碼管理局於 2011 年發佈了《關於作好公鑰密碼算法升級工做的通知》,明確要求「自 2011 年 3 月 1 日起,在建和擬建公鑰密碼基礎設施電子認證系統和密鑰管理系統應使用國密算法。自 2011 年 7 月 1 日起,投入運行並使用公鑰密碼的信息系統,應使用 SM2 算法。」算法
自 2012 年以來,國家密碼管理局以《中華人民共和國密碼行業標準》的方式,陸續公佈了 SM2/SM3/SM4 等密碼算法標準及其應用規範。其中「SM」表明「商密」,即用於商用的、不涉及國家祕密的密碼技術。安全
這其中值得咱們關注的主要是如下公開的算法:網絡
- SM2:基於橢圓曲線密碼(ECC)的公鑰密碼算法標準,提供數字簽名,密鑰交換,公鑰加密,用於替換 RSA/ECDSA/ECDH 等國際算法
- SM3:消息摘要算法,哈希結果爲 256 位,用於替換 MD5/SHA1/SHA256 等國際算法
- SM4:對稱加密算法,密鑰長度和分組長度均爲 128 位,主要用於無線局域網標準,用於替換 DES/AES 等算法
- 國密證書:這裏的國密證書指的是使用國密算法(SM2-with-SM3)的標準 X509 格式證書,證書使用 SM3 做爲哈希算法,使用 SM2 做爲數字簽名算法
- 國密 SSL:採用國密算法,符合國密標準的安全傳輸協議,也就是 SSL/TLS 協議的國密版本
SM2 進階 Linux 內核之路
目前 Linux 內核已經較好的支持了 SM3 和 SM4 算法,這得益於無線局域網標準的普遍使用。但 SM2 算法和國密證書遲遲沒有獲得支持,也就沒法基於國密來創建全棧可信和內核中的完整性驗證,所以在內核中支持這一套體系也變得迫切起來。整個規劃是:在內核中支持 SM2 算法和國密證書,在內部業務率先應用起來後,最終推動到社區。架構
整個流程下來,諸多不順,權且記錄下來。框架
第一回
有了規劃,接下來就是考慮如何實施。但凡密碼學算法,都會先考慮是否能夠從 openssl 作移植。幸運的是,openssl 對 SM2/3/4 支持得很是好,橢圓曲線算法的實現久經考驗,很是成熟,並且最新版本也完整支持了國密證書。不幸的是,openssl 的各個模塊之間耦合度很高,要實現 SM2 和國密證書,須要移植 openssl 架構和基礎設施代碼,包括 BIGNUM、ECC、X509 等。這個工做量無疑是巨大的,即使實現了,這種方式也很難被社區接受,再三考慮權衡後,果斷放棄了這條"捷徑"。函數
第二回
發現了一個使人驚喜的事實:內核中已經有了一個橢圓算法的基礎實現(crypto/ecc.c),何不嘗試基於這個算法來作呢?因而參照 SM2 規範開始編碼,但結果有點遺憾,這個橢圓算法在 SM2 上竟然失效了,連最基本的點乘結果都是錯的!納尼?劇本不該該是這樣的。因而發郵件諮詢該算法的一個資深開發者,很快就獲得了下面的回覆(感嘆天下仍是好心人多): 工具
Shamir's trick algo is probably generic, but it's ecc_point_double_jacobian()
that is curve specific.Algorithms are chosen that are fit curves I (and previous coders) used.
You need to check their properties carefully if you going to use them.Some variants of used algos, that may fit other curves, are in referenced
papers (in comments).
總結緣由:這個算法是通過高度優化的算法,是精確適配過 NIST 和 ECRDSA 橢圓曲線參數的,並不必定適合國內的 SM2 曲線參數,看來這條路是走不通了......
......哭泣中,別理我。
......擦乾眼淚,當作敗,人生豪邁,不過是從頭再來......
第三回
通過反覆探索,發現內核中 RSA 算法是基於一個 mpi(高精度整數)庫實現的,這個庫來源於 libgcrypt(這是知名隱私保護軟件 GnuPG 的底層算法實現)。內核中已經實現了一個早期版本的 mpi,當時就是爲了實現 RSA 引入的。如今的 libgcrypt 已經有了完整的橢圓曲線基礎算法,因而抱着試試看的心態基於 libgcrypt 測試 SM2 算法曲線,蒼天保佑,此次的驚喜沒有變成驚嚇,實驗結果與 SM2 規範一致。
第四回
libgcrypt 中的 ECC 算法是個通用的算法,實現耦合度低。因而有了一個想法,能夠嘗試先在 libgcrypt 中實現 SM2,小試牛刀以後再把這一套東西所有移植到內核,進一步推動到社區,看起來這也是能被社區接受的方式。有了計劃後,索性擺個安逸的造型,莊嚴肅穆地將雙手放在鍵盤上,讓思惟隨着手指天然流淌,接下來的開發調試就比較順利了,很快便有了公鑰算法的四件套:加密,解密,簽名,驗籤。一氣呵成把這些實現提交到了 libgcrypt 社區,通過兩輪的審覈再修改以後,最終 SM2 算法做爲 ECC 的一個子算法被社區接受,這裏要感謝 libgcrypt 的維護者之一的 NIIBE Yutaka,耐心友好,對中國傳統文化也很瞭解。總體過程比較順利,表過不提。附上相關提交:
http://git.gnupg.org/cgi-bin/gitweb.cgi?p=libgcrypt.git;a=search;s=Tianjia+Zhang;st=author
第五回
趁熱打鐵,因爲內核中的 lib/mpi 庫是一個較老的版本而且是爲 RSA 服務的,相對於 libgcrypt 中的 mpi,是一個閹割的版本,須要移植缺失的函數以及 ECC 算法,這沒什麼技術難度,卻也是一個精細活,工做量也不小。在實踐中,把實現 SM2 的過程當中所缺失的東西都移植過來,很快便有了相應的 SM2 算法和國密證書的實現。再通過幾輪打磨,並作了充分的測試後,就有了最初的這組補丁: https://lkml.org/lkml/2020/2/16/43
第六回
中國古語曰過,一氣呵成,再而衰,三而竭,事情的進展再次遇到阻礙。Linux 內核比不得專用的密碼學庫,對於這麼一個不怎麼知名的算法,社區並無表現出什麼興趣,甚至鮮有人問津,最終以沒有實際應用場景而被拒絕。事情固然不能就這麼結束,考慮到代碼量大,維護者審覈意願低,果斷裁剪掉了 SM2 的加解密和簽名,只保留了支持國密證書必要的驗籤功能,後來陸陸續續又作了一些小修小補,同時給 IMA 的上游作了國密算法的加強以便將國密功能在 IMA 場景的應用做爲實際案例。同時,隨着跟相關開發者和維護者不斷的軟(si)磨(chan)硬(lan)泡(da),不斷地發送新的補丁,最後甚至都摸透了維護者習慣性的回覆時間和補丁的合入規律,持續地緩慢推動 SM2 進入社區的步伐。這期間沒有波瀾壯闊的故事,也沒有狗血的劇情,balabalabala......,省略while (1) {...}
循環。
第七回
年過中秋月過半, 歷時半年多時間,SM2 早已不是那個最熟悉的娃,不知不覺補丁也更新到了 v7 版本。中秋月圓之夜向來都是有大事要發生的。是夜,一個蓋世英雄,頭頂鍋蓋,腰纏海帶,腳踩七彩祥雲飛過來了,SM2 終於等到了它的至尊寶。言歸正傳,這個版本的補丁最終被社區接受,目前已經合併到了 Linux 主線的 5.10-rc1:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/log/?qt=author&q=Tianjia+Zhang
如不出意外會在 5.10 內核版本中正式發佈。
libgcrypt 全面支持國密算法
後來有幸在某個機緣巧合之下,在 libgcrypt 中實現了 SM4。做爲國密開發過程的一個附屬產物,目前 libgcrypt 已經全面支持了國密算法 SM2/3/4,這些實現都會在下一個版本 1.9.0 正式發佈。其中 SM3 由相關同事在 2017 年開發。
ima-evm-utils 支持 SM2-with-SM3 國密簽名
內核已經支持了 SM2 和國密證書,做爲 IMA 完整性簽名的用戶態工具,ima-evm-utils 對國密的支持固然不能落下,附上相關的提交:
已知問題
當下 SM2 還有一些問題須要注意:
- SM2 X509 證書中沒有爲 SM2 公鑰算法定義獨立的 OID 標識,目前是識別 OID_id_ecPublicKey 標識默認看成 SM2
- SM2 規範沒有爲推薦的橢圓曲線參數定義 OID 標識,這也致使 SM2 算法僅有一個默認的橢圓曲線參數
- SM2 規範中對消息簽名時,除了要計算消息自身的 SM3,同時 SM2 橢圓曲線參數和公鑰都要參與到 SM3 的計算中來,SM2 私鑰簽名是對最終的哈希結果作簽名,這一規範定義是有點另類,這是與國際通用算法的一個主要差別:
正常狀況下,X509 證書解析與算法都被實現爲獨立的模塊。正是因爲 SM2 的這個規範會致使實現上的強耦合:X509 證書驗證時須要計算證書中 tbs 的 SM3 哈希,這個哈希一樣須要橢圓曲線參數以及公鑰數據,而這些數據是一次完整 SM2 驗簽過程當中的臨時數據,目前的內核中並無提供這樣的回調機制(固然這是 SM2 的特殊狀況),這就把 X509 證書解析與 SM2 算法強綁到了一塊兒,無法解耦。這也致使了應用該功能的一點限制,SM2 只能支持內建編譯(Y),而不支持編譯成模塊(M),讓 X509 在編譯時就知道已經支持 SM2,才能正常驗籤國密證書。從實現上看,也有一些動態加載 SM2 模塊獲取獲取函數指針的方法,相比於框架層的支持,都不是很友好。 - IMA 簽名在計算文件哈希的時候,內核是直接計算文件哈希的,這塊並無針對是否使用 SM2 簽名而作特殊處理(指上圖中的增長 Za 值)。當下內核作的 IMA 國密簽名驗證的支持,同時也支持了 ima-evm-utils 的國密 sm2+sm3 簽名(依賴最新的 openssl),目前這塊都是直接計算文件哈希,沒有加 Za 值,這也是當下最優的方案。須要說明的一點是,Za 是國密簽名以及驗籤裏要求的,只是簽名驗籤的流程裏須要,可是國密這個流程跟目前主流的算法是相悖的,若是要支持,內核和 ima-evm-utils 工具都須要較大修改,內核要涉及到架構修改,社區也不肯意接受,因此目前就按主流方式支持了 sm2+sm3 的 IMA 簽名。
總而言之,言而總之兩條:
- 要支持國密證書驗證,SM2 要麼不編譯,要麼必須內建編譯,不支持編譯成模塊。固然了,SM2 做爲一個非對稱算法,只簽名一個哈希或者基於國密的 IMA 驗證,並無這個限制。
- IMA 簽名工具 ima-evm-utils 以及內核計算文件 SM3 哈希所用的國密算法沒有加 Za,這個是與規範的一點差別。
致謝
感謝相關同窗在開發過程當中所作的測試工做,技術支持以及在 openssl 所作的大量的國密支持工做
感謝社區的熱心開發者,NIIBE Yutaka,Gilad Ben-Yossef,Vitaly Chikunov,Jussi Kivilinna,Herbert Xu
(題圖:wallpapercave)