譯|王祖熙 (花名:金九 )
螞蟻集團開發工程師
負責國產化密碼庫 Tongsuo 的開發和維護
專注於密碼學、高性能網絡、網絡安全等領域
本文 9658 字 閲讀 20 分鐘
本文翻譯 OpenSSL 官網文檔:https://www.openssl.org/docs/OpenSSL300Design.html
Tongsuo-8.4.0 是基於 OpenSSL-3.0.3 開發,所以本文對 Tongsuo 開發者同樣適用,內容豐富,值得一讀!
由於文章篇幅較長,今天帶來的是 《FIPS模塊》 部分內容,已發佈文章《介紹、術語與架構》、《Core 和 Provider 設計》可看發表記錄。後續內容將隨每週推送完整發布,請持續關注銅鎖!
FIPS 模塊
這是一個經過 FIPS 140-2 驗證的加密模塊,它是一個只包含經過 FIPS 驗證/批准的加密算法的 Provider,非 FIPS 算法將由默認 Provider(而不是 FIPS 模塊)提供。
該模塊是可以動態加載的,不支持靜態鏈接。
FIPS 模塊本身不會有 "FIPS 模式",可以使用 FIPS Provider 的 OpenSSL 將具有與 FIPS-Module-2.0.0 兼容的"模式"概念。
FIPS 模塊版本編號
版本將為 FIPS-Module-3.0。
任何後續的修訂版本將以與先前發佈類似的方式進行標記,例如 3.0.x。
對於更改通知或重新驗證,FIPS 模塊的版本號將更新以匹配當前的 OpenSSL 庫版本。
檢測 FIPS 邊界內的變更
為了進行驗證,我們需要檢測是否有任何相關的源代碼發生了變化。
可以使用一個腳本來對 C 源代碼進行標記化處理,就像 C 預處理器一樣,但還要教會它忽略源代碼中的某些部分:
- 系統的
#include指令。 - 在 FIPS 模式下被條件排除的代碼(如下文中所述的條件代碼)。
(提醒:C 預處理器可以將所有非換行空白字符合並,並在每個標記之間留下標準的單個空格,對於此目的,註釋被視為空白字符)
標記化處理的結果可以通過校驗和進行處理,該校驗和存儲在與源代碼文件相對應的文件中,並最終進行版本控制。
該過程大致如下(並非完全相同,這只是一個代碼示例,用於展示整個過程):
for f in $(FIPS_SOURCES); do
perl ./util/fips-tokenize $f | openssl sha256 -r
done | openssl sha256 -hex -out fips.checksum
還會有一些機制來提醒我們有關變化的信息,以便我們可以採取適當的措施。例如:
git diff --quiet fips.checksum || \
(git rev-parse HEAD > fips.commit; scream)
關於 scream 的具體操作尚待確定。
更新 fips.checksum 應該作為正常的 make update 的一部分進行,這是通常用於更改和檢查版本控制文件的方法,OpenSSL 的持續集成已經運行了此命令,以確保沒有遺漏任何內容,並且如果有內容被更改,將中斷構建過程,運行make update也是正常的 OpenSSL 發佈流程的一部分。
如何對已簽名校驗和的更改做出反應
儘管發生了變更,但我們倉庫中的校驗和更改本身並沒有什麼大不了的,它只是提醒我們需要額外關注 FIPS 源代碼。
有兩種可能的情況:
- 當即將發佈新版本,並且 fips.checksum 不再包含上一個經過驗證的源代碼的校驗和時,將 FIPS 源代碼發送給實驗室,並開始更新驗證流程。
- 在發佈新版本的同時,fips.checksum 不再包含上一個經過驗證的源代碼的校驗和時,將 FIPS 源代碼(包括 diff 文件和變更列表)發送給實驗室,並啓動相應的更新驗證流程。
已驗證的校驗和列表將在其他地方列出(稍後確定具體位置)。
編譯
對於每個 FIPS Provider 的源文件,我們計算該文件的校驗和,並將其與 fips.checksum 中收集的校驗和進行比對,如果存在不匹配,將拒絕編譯。
FIPS 模式
FIPS 模塊僅包含經過 FIPS 驗證的密碼算法,任何 FIPS 模式的“切換邏輯”將位於 FIPS 模塊邊界之外 - 這將由“fips”屬性處理。
與 FIPS 模式相關的條件代碼在單獨的部分中討論。
以下的 FIPS API 將繼續可供應用程序使用(為了保持一致性,使用了與 1.1.1 版本中相同的名稱):
int FIPS_mode_set(int on)
確保當前全局屬性設置中設置了“fips=yes”(當 on != 0 時),或者未設置“fips”(當 on == 0 時)。這還將嘗試使用屬性“fips=yes”獲取 HMAC-SHA256 算法,並確保它成功返回。
int FIPS_mode(void)
如果當前的全局屬性字符串包含屬性“fips=yes”(或“fips”),則返回1,否則返回0。
我們可以檢查當前是否有提供 FIPS 算法的 Provider 可用,並稍微以不同的方式處理。
int FIPS_self_test(void)
如果 FIPS_mode() 返回true,則運行 KATs。
完整性測試將不在此處涵蓋,如果我們決定提供它,它將是一個單獨的函數。
成功時返回1,失敗或沒有 OpenSSL FIPS Provider 時返回0。
注意:這些函數只能在 OpenSSL FIPS Provider 的上下文中運行,而不能在其他任何 FIPS Provider的上下文中運行。這些是過時的遺留接口,應使用EVP_set_default_alg_properties()函數進行非遺留配置。
角色和身份認證
有兩個隱含的角色 - 密碼官(CO)和用户。這兩個角色都支持相同的服務,唯一的區別是 CO 負責安裝軟件,該模塊不應支持用户身份驗證(對於1級而言不是必需的),所有這些都可以在安全策略中解釋,而無需編寫具體的代碼。
有限狀態模型(FIPS 140-2第4.4節)
需要定義一個狀態機。
我們將需要以下狀態:
- 自檢狀態 - 初始化、運行、自檢、錯誤、關閉(可能還包括後觸發狀態)
- 錯誤狀態 - 如果自檢失敗,模塊應返回該操作的錯誤。可以嘗試清除錯誤並重復操作,如果失敗仍然存在,模塊應進入錯誤狀態,這可以是一個硬錯誤狀態,其中所有加密操作都失敗,或者是一個功能降級狀態,其中失敗的組件僅在使用時返回錯誤。
觸發自檢失敗的方式包括:
-
- 連續測試(生成密鑰對的成對測試(簽名/驗證)和從熵源比較測試中驗證輸入到 DRBG 的隨機數不相同)。
- DRBG 健康測試 - 這可以始終在 RNG 中引發錯誤(而不是設置全局錯誤狀態)。
- 安裝、啓動或按需的 POST 完整性測試失敗。
- 啓動或按需的 POST KAT 失敗。
將提供一個內部 API 來設置上述情況的失敗狀態。
狀態機
在狀態機中未顯示的狀態以虛線表示。進入和離開錯誤狀態的邊緣以虛線表示,以表明不希望遍歷這些邊緣。
| 狀態模型由這些狀態組成:1. 電源關閉(Power Off):FIPS 模塊未加載到應用程序中,共享庫不在內存中。
- 電源開啓(Power On):FIPS 模塊已被應用程序加載,並且共享庫在內存中。默認入口點構造函數將被啓動。
- 初始化(Initialisation):調用OSSL_provider_init。
- 完整性檢查(Integrity Check)(POST 完整性):模塊對自身進行校驗,並驗證其是否被惡意更改。(在 FIPS 提供程序的 OSSL_provider_init() 期間運行)。
- 自檢(Self Test)(POST KAT):FIPS 模塊在安裝期間執行其自檢,或者在通過 API 調用進行按需自檢。
- 運行(Running):FIPS 模塊處於正常運行狀態,可以使用所有 API,並進行連續測試。
- 錯誤(Error):FIPS 模塊進入錯誤狀態,調用所有加密 API 將返回錯誤。
- 關閉(Shutdown):FIPS 模塊正在被終止並從使用應用程序中卸載。
狀態之間的邊緣關係如下:
- 從電源關閉(Power Off)到電源開啓(Power On):此轉換由操作系統在將共享庫加載到應用程序時執行。
- 從電源開啓(Power On)到初始化(Initialisation):當調用共享庫的構造函數時發生此轉換。
- 從電源開啓(Power On)到關閉(Shutdown):如果無法調用構造函數或構造函數失敗,將觸發此轉換。
- 從初始化(Initialisation)到完整性檢查(Integrity Check):此轉換在初始化代碼完成後發生,計算模塊完整性校驗和,並與預期值進行比較。
- 從初始化(Initialisation)到錯誤(Error):如果初始化代碼在啓動自測之前遇到錯誤,將觸發此轉換。
- 從完整性檢查(Integrity Check)到運行(Running):對於所有啓動過程中完整性檢查成功的情況,發生此轉換。
- 從完整性檢查(Integrity Check)到自測(Self Test):在安裝過程中,如果完整性檢查成功,發生此轉換。
- 從完整性檢查(Integrity Check)到錯誤(Error):如果完整性檢查失敗,將觸發此轉換。
- 從運行(Running)到關閉(Shutdown):當 FIPS 模塊最終化時發生此轉換。
- 從運行(Running)到錯誤(Error):如果連續測試中的任何一個失敗,將觸發此轉換。
- 從運行(Running)到自測(Self Test):應用程序手動啓動自測時觸發此轉換。不重新運行完整性檢查。
- 從自測(Self Test)到運行(Running):自測通過時發生此轉換。
- 從自測(Self Test)到錯誤(Error):如果自測失敗,將觸發此轉換。
- 從關閉(Shutdown)到電源關閉(Power Off):當 FIPS 模塊從應用程序的內存中卸載時發生此轉換。
- 從錯誤(Error)到關閉(Shutdown):當 FIPS 模塊最終化時發生此轉換。
如果可能的話,我們應該儘量在運行狀態下注冊算法,任何進入運行狀態的轉換都應該允許註冊/緩存密碼算法,而任何進入錯誤或關閉狀態的轉換都應該清除 libcrypto 中的所有緩存算法,通過採用這種方法,我們可以避免在所有密碼工廠函數中檢查狀態,這樣可以避免為自我測試(手動啓動時)提供特殊情況訪問權限,同時阻止外部調用者的訪問。
服務
FIPS模塊提供以下服務。
- 顯示狀態。如果“運行”狀態處於活動狀態,則返回1,否則返回0。
- 密碼服務,如HMAC、SHS、加密。請參閲附錄3-算法。
- 自檢(按需執行)- libcrypto 中提供了一個名為
FIPS_self_test()的公共 API來訪問此方法。所使用的方法必須與初始化時觸發的方法相同,安全策略將指明只有在沒有其他密碼服務運行時才能訪問此方法。 - 密鑰清零。請參閲 CSP/Key 清零。
這些服務僅在運行狀態下運行,在任何其他狀態下嘗試訪問服務將返回錯誤,如果自檢失敗,則任何嘗試訪問任何服務的操作都應返回錯誤。
自測試
自測試包括上電自測試(POST)和運行時測試(例如,確保熵不會重複作為 RNG 的輸入)。
POST 包括模塊完整性檢查(每次運行使用 FIPS 的應用程序時運行)以及算法 KAT(可以在安裝時運行一次)。
POST 測試在調用 FIPS 模塊的OSSL_provider_init()入口點時運行。
為了按照正確的順序實現完整性測試和 KAT,模塊需要訪問以下數據項:
- 庫的路徑;
- 庫內容的 HMAC-SHA256(或包含該值的文件的路徑);
- 指示庫已安裝並通過 KAT 的指示器;
- 該指示器的 HMAC-SHA256。
這些值將成為可以通過OSSL_PROVIDER對象和相關的OSSL_PARAM getter獲取的參數的一部分。將使用一個“更安全”的獲取值函數來獲取這些值,該函數不會展開環境變量等。此外,還需要傳遞用於訪問和返回庫內容的函數(可能是基於 BIO 的,通過將 Core 傳遞給模塊的調度表中的少量 BIO 函數來實現),以便模塊可以生成自己的庫摘要。
新的 OpenSSL“fips” 應用程序將提供安裝(運行 KAT 並輸出配置文件數據)和檢查(檢查配置文件中的值是否有效)的功能。
模塊的默認入口點(DEP),即 Linux 庫中的 “.init” 函數,將設置一個模塊變量(可能是狀態變量),在OSSL_provider_init()中將檢查此變量,並且如果設置了(通常會設置),則驗證文件中的值。這個兩步過程滿足了 FIPS 要求,即 DEP 確保測試運行,但允許我們在正常運行時初始化模塊的其餘部分時實現這些測試。
作為構建過程的一部分,必須將 FIPS 模塊的完整性校驗和保存到文件中,這可以通過腳本完成,它只是使用已知固定密鑰對整個 FIPS 模塊文件進行 HMAC_SHA256 運算的結果,如果庫已簽名,則必須在應用簽名之後計算校驗和。
至少具有112位的固定密鑰將嵌入到 FIPS 模塊中,用於所有 HMAC 完整性操作,這個密鑰也將提供給外部構建腳本。
出於測試目的,即使其中一個或多個測試失敗,所有活動的 POST 測試也會運行。
完整性校驗和的位置
完整性校驗和將在安裝過程中保存到一個單獨的文件中,默認情況下,該文件將與 FIPS 模塊本身位於相同的位置,但可以配置為位於不同的位置。
已知答案測試
KAT 的目的是對密碼模塊進行健康檢查,以識別在電源週期之間的模塊的嚴重故障或變化,而不是驗證實現是否正確。
FIPS 140-2 IG 的規則指定了需要測試每個已支持的算法(不是每個模式),如果一個算法作為另一個測試的組成部分進行了測試,則不需要單獨的測試,以下是需要進行測試的算法列表:
- 加密/解密算法
-
- AES_128_GCM2
- TDES_CBC
- 哈希算法
-
- SHA1
- SHA256 在其他地方已經要求測試
- SHA512
- SHA3-256
- 簽名/驗證測試
-
- DSA_2048
- RSA_SHA256(使用PKCS #1 v1.5填充)
- ECDSA P256
- 任何支持的 DRBG 機制的 DRBG 健康測試
-
- CTR(AES_128_CTR)
- HASH - SHA256
- HMAC - SHA256
- 派生測試(計算Z)
-
- ECDSA P256
- ECDH
- KDF(密鑰派生函數)
- KBKDF(用於 TLS 的 HKDF)
注意:完整性測試使用 HMAC-SHA-256,因此不需要單獨進行 HMAC 測試。
接口訪問
為了方便修改和更改運行的自測試,自測試應該是數據驅動的,POST 測試在註冊任何方法之前運行,但方法表仍然可以間接使用,仍然需要較低級別的 API 來設置密鑰(參數、公鑰/私鑰),密鑰加載代碼應該在一個單獨的函數中進行隔離。
需要一個初始化方法來為高級函數設置所需的依賴項,例如在執行基本調用之前可能需要調用 set_cpuid。
應為摘要、密碼、簽名、DRBG、KDF、HMAC 提供不同類型的自測試 API。
傳遞給這些測試的參數是 KAT 數據。
安全強度
SP 800-131A rev2 在某些日期之後禁止使用特定的算法和密鑰長度,這些項目與安全強度相關聯。
允許具有至少112位安全強度的算法。
對於簽名驗證,為了遺留目的,允許使用安全強度至少為80且低於112的算法。
這兩個值可以在FIPS模塊中定義和執行,也可以在安全策略文檔中更簡單地處理。
可以通過公共API定義這些最小值,並允許設置它們。
還應添加目標安全強度的概念,該值將在密鑰生成算法中使用,這些算法通過其標準指定了目標安全強度參數。
SP800-56A & 56B
這些標準包含密鑰協商協議,為了測試這些協議,加密模塊需要包含以下低級原語。
- 計算密鑰方法 - 這些已經存在。(例如
DH_compute_key())。 - 密鑰生成 - (目前缺少 RSA FIPS 186-4 密鑰生成)。
- 密鑰驗證 - (大部分已經實現)。
FIPS 186-4 RSA 密鑰生成
- 已經編寫了 RSA 密鑰生成的初始代碼(https://github.com/openssl/openssl/pull/6652);
尚未完成的工作是將其整合到 FIPS 模塊中,OpenSSL FIPS Provider 將具有強制密鑰大小限制的邏輯;
- 對於 RSA、DSA 和 ECDSA 密鑰對生成,需要進行成對一致性測試(條件自檢)。由於在密鑰生成過程中無法確定密鑰的用途,FIPS 140-2 IG 規定相同的成對測試可以用於簽名和加密兩種模式;
- 不允許使用 1024 位的 RSA 密鑰生成;
- 密鑰生成算法具有目標安全強度的概念。例如,RSA 的密鑰生成代碼需要進行以下檢查:
if (target_strength < 112
|| target_strength > 256
|| BN_security_bits(nbits) < target_strength)
return 0;
DH 密鑰生成
- DH 密鑰生成 - 可能需要將其拆分為符合標準步驟的形式。目前它是一個相當複雜用時也用於驗證的整體函數。
密鑰驗證
- RSA SP 800-56B 密鑰驗證 - 公鑰、私鑰和密鑰對的檢查已添加到 PR#6652,符合標準的要求;
- 需要檢查 DH 密鑰驗證是否符合標準;
- EC 密鑰驗證符合標準要求;
- AES-XTS 模式需要進行扭曲密鑰檢查。
對於KAS DH參數,支持兩種類型:
- 已批准的安全素數羣如下:
(其中 g=2,q=(p-1)/2,priv=[1, q-1],pub=[2, p-2])
TLS:(ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192)
IKE:(modp-2048, modp-3072, modp-4096, modp-6144, modp-8192)
只有上述安全素數可以進行驗證-其他任何素數都應該失敗。
安全素數可用於至少112位的安全強度,可能需要進行 FIPS 特定的檢查以驗證羣組。
- FIPS 186-4 參數集僅用於向後兼容性,安全強度僅為112位。羣組為 FB (2048, 224) 和 FC (2048, 256)。這需要保存種子和計數器以進行驗證。
如果需要同時支持兩種類型,則需要不同的密鑰驗證代碼。
現有的DH_Check()需要進行 FIPS 特定的檢查來驗證批准的類型。
密鑰生成對於兩者來説是相同的(安全強度和私鑰的最大位長度是輸入)。
DSA 在 FIPS 186-4 中定義為 “FFC”,DSA 密鑰生成/密鑰驗證可以重新設計,以更好地匹配標準步驟,這將有助於密鑰驗證,並且如果需要,可以在 DH 情況下進行重用。
GCM IV 生成
對於 FIPS 模塊,AES GCM 有與唯一密鑰/IV 對相關的要求,即:
- 加密時密鑰/IV 對必須是唯一的;
- IV 必須在 FIPS 邊界內生成;
- 對於 TLS,IV 的計數部分必須由模塊設置,模塊必須確保當計數用盡時返回錯誤;
- 對於給定的密鑰(對於任何 IV 長度),認證加密函數的總調用次數必須小於
;
- 模塊斷電不應導致 IV 的重複使用。
IV 生成將使用隨機構造方法(來自SP 800-38D),其中包括一個自由字段(將為 NULL)和一個隨機字段,隨機字段將使用一個能提供至少96位熵強度的 DRBG,該 DRBG 需要由模塊進行種子生成。
現有代碼需要修改,以便在init()階段未設置 IV 時生成 IV,然後可以使用do_cipher()方法在需要時生成 IV。
int aes_gcm_cipher()
{
....
/* old code just returned -1 if iv_set was zero */
if (!gctx->iv_set) {
if (ctx->encrypt) {
if (!aes_gcm_iv_generate(gctx, 0))
return -1;
} else {
return -1;
}
}
}
}
生成代碼如下所示:
#define AES_GCM_IV_GENERATE(gctx, offset) \
if (!gctx->iv_set) { \
int sz = gctx->ivlen - offset; \
if (sz <= 0) \
return -1; \
/* Must be at least 96 bits */ \
if (gctx->ivlen < 12) \
return -1; \
/* Use DRBG to generate random iv */ \
if (RAND_bytes(gctx->iv + offset, sz) <= 0) \
return -1; \
gctx->iv_set = 1; \
}
生成的 IV 可以通過EVP_CIPHER_CTX_iv()方法獲取,因此不需要使用 ctrl id。
理想情況下,在 FIPS 模式下嘗試設置 GCM IV 參數將導致錯誤。實際上,可能仍然有一些應用程序需要設置 IV,因此建議將其指定為安全策略項。
安全策略還需要説明以下內容:(參見 FIPS 140-2 IG A.5)
- 當電源斷開然後恢復時,應建立一個新的用於 AES GCM 加密的密鑰;
- 使用相同密鑰的總調用次數必須小於
;
- 場景1:IV 生成符合 TLS 協議;
- 場景2:使用 NIST SP 800-38D(第8.2.2節)的 IV 生成方法。
CSP/Key 清零
當不再需要臨界安全參數(CSP)時,我們必須將其全部設置為零,這可能會在不同的上下文中發生:
- 臨時的 CSP 副本可能是棧或堆分配的,並且將在其使用範圍內的相關函數中被清零;
- 一些 CSP 將與 OpenSSL 對象(如 EVP_PKEY 或 EVP_CIPHER_CTX)關聯,具有與之相關聯的生命週期,在這種情況下,當這些對象被釋放時,CSP 將在該點被清零。在某些情況下,對象可能會被複用(例如,EVP_CIPHER_CTX 可以用於多個加密操作),在這種情況下,對象中仍存在的任何 CSP 將在其重新初始化為新操作時被清零。
- 一些 CSP(例如內部 DRBG 狀態)可能會在加載 OpenSSL FIPS 模塊的整個時間內存在,在這種情況下,狀態將封裝在 OpenSSL 對象中。所有 OpenSSL Provider(包括 FIPS 模塊 Provider)都具有註冊“卸載”函數的能力,該函數在關閉 OpenSSL 時(或因其他原因卸載模塊)時將被調用,該卸載函數將釋放(因此將清零)包含 CSP 的對象。
- 根據 FIPS 140-2 IG 4.7 的規定:僅用於執行 FIPS 140-2 第4.9.1節上電測試的密碼模塊使用的密碼密鑰不被視為 CSP,因此不需要滿足 FIPS 140-2 第4.7.6節的清零要求。
OpenSSL FIPS 模塊將包含其自己的標準OPENSSL_cleanse()函數的副本來執行清零操作,這是使用特定於平台的彙編語言實現的。
DRBG
在舊的 FIPS 模塊中存在以下 API,可能需要重新添加:
- FIPS_drbg_health_check:按需運行 DRBG KAT 測試,我們需要使其可用。
- FIPS_drbg_set_check_interval:設置運行 DRBG KAT 測試之間的間隔(生成調用的次數)。這似乎不是必需的,這些測試在上電時運行,但後續不需要運行,但這個調用對於故障測試很有用。
推導函數
根據 FIPS 140-2 IG 14.5 中的第2點要求,CTR DRBG 將無條件支持派生函數,如果禁用派生函數,當前的代碼在重新生成種子時會出現問題。此外,如果沒有派生函數,需要從實驗室獲得額外的理由支持。
測試要求
- 在
uninstantiate()函數中需要證明內部狀態已被清零; - 故障測試需要一個函數,使 DRBG 始終產生相同的輸出。
其他需要考慮的事項
除了下面描述的熵之外,還需要考慮以下幾點:
- 應考慮實施 NIST SP 800-90C 10.1.2 中的熵擴展;
- 需要更好的 DRBG 選擇機制,以在可用的 DRBG 之間進行選擇;
- 支持預測抵抗,即在請求時嘗試從我們的來源收集更多熵。
- 我們需要弄清楚 DRBG 層將是什麼樣子,代碼的很大一部分需要放在 FIPS 模塊內。目前,此代碼訪問 EVP 功能,而這些功能可能不會在模塊內部公開。例如,
drbg_ctr_init()從 NID 解析 EVP_CIPHER,然後設置一個 EVP_CIPHER_CTX。
熵
對於所有平台,操作系統將提供熵。對於某些平台,還可以使用內置的硬件隨機數生成器,但這將引入額外的驗證需求。
對於類 UNIX 系統,將使用系統調用getrandom、getentropy或隨機設備/dev/random作為熵源,優先考慮使用系統調用。可以替代/dev/random的其他強隨機設備包括:/dev/srandom和/dev/hwrng。請注意,/dev/urandom、/dev/prandom、/dev/wrandom和/dev/arandom在沒有額外的證明的情況下不能用於 FIPS 操作。
在 Windows 上,將使用BCryptGenRandom或CryptGenRandom作為熵源。
在 VMS 上,將使用各種系統狀態信息作為熵源。請注意,這將需要證明和分析以證明源的質量。
對於 iOS,將使用 SecRandomCopyBytes 生成具有密碼學安全性的隨機字節。
FIPS 僅允許將一個熵源歸因於模塊,因此 FIPS 模塊將完全依賴於前述的操作系統源,不會使用其他來源,例如 egd、硬件設備等。
完成熵解決方案的工作
需要將 DRBG 健康檢測添加到隨機框架中,以檢查輸入到 DRBG 的種子材料,檢查的目的是確保沒有連續的兩個種子材料塊是相同的,該檢查在所有熵源被合併在一起後進行,如果檢查失敗,則 DRBG 的種子重新生成操作將永遠失敗。我們可以定義使用的塊大小為64位,這是在意外接收到重複塊的概率()和從操作系統中獲取過多熵量之間的平衡(因為第一個塊會被丟棄),其他明顯可用的塊大小包括128位和256位。
使用後,初始數據塊必須被清零和丟棄。
GCM的初始化向量(IV)
FIPS 140-2 IG A.5 的最新更新指出,如果模塊聲稱為 GCM 生成隨機 IV,則需要提供證明,我們需要證明模塊能夠從操作系統獲取所需的96位熵,如果使用阻塞調用操作系統的隨機性源,並且至少使用這麼多熵素材作為 DRBG 的種子材料,那麼這應該不是無法解決的問題。
FIPS 模塊邊界
一旦進入 FIPS 模塊提供的算法,在任何其他的加密操作中我們必須仍然保持在 FIPS 模塊內部。根據 FIPS 規則,允許一個 FIPS 模塊使用另一個 FIPS 模塊。然而,在3.0設計中,為了簡化起見,我們假設不允許這樣做。例如,EVP_DigestSign*實現同時使用簽名算法和摘要算法,我們不允許其中一個算法來自 FIPS 模塊,另一個來自其他 Provider。
所有 Provider 在初始化時都被分配一個唯一的OSSL_PROVIDER對象,當 FIPS 模塊被要求使用某個算法時,它會驗證該算法的實現OSSL_PROVIDER對象是否與自己的OSSL_PROVIDER對象相同 (即傳遞給OSSL_provider_init的對象),例如,考慮使用 RSA 和 SHA256 的EVP_DigestSign*的情況,兩個算法都會通過 Core 在 FIPS 模塊外部進行查找,RSA 簽名算法是第一個入口點,"init" 調用將被傳遞給要使用的 SHA256 算法的引用,FIPS 模塊的實現將檢查與其被要求使用的 SHA256 實現關聯的OSSL_PROVIDER對象是否也在 FIPS 模塊邊界內,如果不是,則 "init" 操作將失敗。下面的圖示從 FIPS 模塊的角度説明了這個操作。
請注意,在 FIPS 模塊內部,我們使用了EVP的概念(如EVP_MD_CTX、EVP_PKEY_CTX等)來實現這一點,這些是 libcrypto 中 EVP 實現的副本,FIPS 模塊沒有與 libcrypto 進行鏈接,這是為了確保完整的操作都在 FIPS 模塊的邊界內進行,而不調用外部的代碼。
ASN.1 代碼
ASN.1 DER (Distinguished Encoding Rules) 用於:
- 序列化密鑰和參數
- 序列化由兩個值 r 和 s 組成的 DSA 和 ECDSA 簽名
- 編碼放置在 RSA PKCS#1 填充中的簽名摘要 OBJECT IDENTIFIER(OID)
- 序列化 X.509 證書和證書撤銷列表(CRL)
- 其他 PDU,如 PKCS #7/CMS、OCSP、PKCS #12 等。
FIPS 模塊不會包含 ASN.1 DER 編碼器/解析器的副本,也不會要求任何 Provider 對由 OpenSSL 實現的算法執行 ASN.1 序列化/反序列化。
所有 ASN.1 序列化/反序列化操作將在 libcrypto 中執行,複合值的密鑰、參數和簽名結構將作為項數組越過 Core/Provider 邊界,使用 附錄 2 - 參數傳遞 中定義的公共數據結構進行傳遞。
用於 RSA PKCS#1 填充的編碼摘要 OID 將預先生成(與舊 FIPS 模塊使用 SHA_DATA 宏相同)或根據需要使用簡單函數生成,這個函數僅為 PKCS #1 填充支持的小型摘要集合生成編碼的 OID,這些摘要 OID 在“OID 樹”下的一個公共節點下,驗證填充時將獲取預期摘要的編碼 OID,並將其字節與填充中的字節進行比較;不需要進行 DER 解析/解碼。