前言
量子計算威脅現況
2026 年,量子計算的進展已經從理論走向實際威脅。雖然具備完全加密破解能力的量子電腦尚未出現,但「先儲存後破解 (Store Now, Decrypt Later)」的攻擊策略已成為現實威脅:攻擊者現在竊取加密資料,等待未來量子電腦成熟後再進行破解。
關鍵時間點:
- 2024 年 8 月 13 日:NIST 正式發布 FIPS 203 (ML-KEM) 與 FIPS 204 (ML-DSA) 標準
- 2026 年:企業級 HSM 開始全面支援 PQC 演算法
- 2030-2035 年:預估為 RSA-2048/ECC P-256 的安全終結時間點
對於需要長期保密的資料(醫療記錄、金融交易、政府機密),遷移至後量子密碼學已刻不容緩。
NIST PQC 標準化完成
美國國家標準技術研究所(NIST)經過 8 年的評選過程,最終確定了以下後量子演算法標準:
| 標準 | 演算法 | 類型 | 安全基礎 | 用途 |
|---|---|---|---|---|
| FIPS 203 | ML-KEM (Kyber) | 金鑰封裝機制 (KEM) | Module-LWE | 金鑰交換、加密 |
| FIPS 204 | ML-DSA (Dilithium) | 數位簽章 | Module-LWE | 身份驗證、簽章 |
| FIPS 205 | SLH-DSA (SPHINCS+) | 數位簽章 | 雜湊函數 | 無狀態簽章 |
這些標準的發布意味著後量子密碼學已從研究階段進入生產部署階段。
為什麼選擇 Luna HSM + Rust
硬體安全模組(HSM)的必要性:
- 實體金鑰保護:私鑰永不離開 FIPS 140-2/3 Level 3 認證硬體
- 防篡改機制:物理入侵自動銷毀金鑰
- 合規要求:金融、醫療等行業法規強制要求
- 效能加速:硬體加速密碼學運算
選擇 Rust 的理由:
- 記憶體安全:編譯期杜絕 buffer overflow、use-after-free 等漏洞
- 平行安全:類型系統保證 thread-safety
- 零成本抽象:無 GC overhead,效能媲美 C/C++
- 生態成熟:RustCrypto、Tokio、Axum 等高品質密碼學與網路框架
Thales Luna HSM 特色:
- 支援最新 FIPS 203/204 標準(Firmware 7.8.0+)
- 原生 BIP32/SLIP10 HD 金鑰衍生
- Luna 專有延伸 API(CA_* 函數)
- 網路 HSM 與叢集支援
技術架構
系統分層設計
我們的 PQC 服務採用清晰的分層架構,從 API Gateway 到 HSM 硬體的完整封裝:
各層職責:
- API Gateway Layer: 提供多協議存取介面(REST、gRPC、NATS)
- Domain Layer: 業務邏輯、金鑰管理、稽核日誌
- HSM Layer: PKCS#11 操作封裝、連接池管理、PQC 專有功能
- FFI Layer: 底層 PKCS#11 C API 綁定
- Hardware: 實體 HSM 硬體
為什麼自己實作 luna-cryptoki 而不使用現有的 cryptoki crate?
這是一個關鍵的技術決策,我們選擇自行實作 FFI 綁定的原因如下:
1. 現有 cryptoki crate 與 Luna HSM 的相容性問題
Parallax Second 維護的 rust-cryptoki 是 Rust 生態中最成熟的 PKCS#11 綁定,但在與 Luna Network HSM 配合使用時存在嚴重的穩定性問題:
已知問題(來自 GitHub Issues):
- Issue #72:
pkcs11.open_session_no_callback在 Luna Network HSM 上崩潰(SIGSEGV) - Issue #50:
ctx.open_session在 Luna HSM 上發生記憶體錯誤
這些問題源於 Luna HSM 對 PKCS#11 標準的特定實作細節,標準 cryptoki crate 無法完全相容。
2. Luna 專有延伸 API 支援
Luna HSM 提供了大量超越 PKCS#11 標準的專有延伸 API(CA_* 函數),這些功能對企業級應用至關重要:
| 延伸類別 | API 函數 | 用途 |
|---|---|---|
| PQC 支援 | CA_ML_DSA_*, CA_ML_KEM_* | 後量子密碼學操作 |
| HD 金鑰衍生 | CA_BIP32_*, CA_SLIP10_* | 原生 BIP32/SLIP10 支援 |
| 系統監控 | CA_GetHSMStats, CA_GetHSMCapabilitySet | HSM 健康監控 |
| 儲存管理 | CA_GetHSMStorageInformation, CA_GetTokenStorageInformation | 容量監控 |
| 韌體管理 | CA_GetFirmwareVersion | 版本資訊 |
標準 cryptoki crate 不支援這些延伸,如果使用它,我們將無法:
- 使用 ML-DSA/ML-KEM 後量子演算法
- 實作 BIP32 HD 錢包功能
- 監控 HSM 健康狀態與容量
- 充分利用 Luna HSM 的進階功能
3. 精細錯誤處理
Luna HSM 定義了大量專有錯誤碼(例如 CKR_BIP32_CHILD_INDEX_INVALID, CKR_LUNA_OPERATION_FAILED),需要結構化錯誤處理:
錯誤分類:
Operation- 操作失敗(包含錯誤碼和描述)KeyNotFound- 金鑰不存在HsmDisconnected- HSM 斷線(可重試)Pin- PIN 相關錯誤
透過錯誤類型判斷是否可重試,實作智能重試、降級策略、精準警示等企業級功能。
4. 性能優化與 Session 管理
Luna HSM 的 Session 管理採用 deadpool 連接池實作,包含:
Session 生命週期管理:
- 建立:開啟 Session → 登入 → 快取符號表 → 健康檢查
- 回收:檢測斷線 → 清理狀態 → 重新登入
關鍵優化:
- 符號快取:避免重複 dlsym 呼叫
- 健康檢查:自動檢測 HSM 斷線並重建連接
- 預熱 Pool:啟動時預先建立 50 個 Session
- 平行安全:支援多執行緒平行存取
5. 完全控制與長期維護
自行實作 FFI 綁定帶來的優勢:
- 完全掌控:不依賴上游 crate 的更新節奏
- 快速修復:發現問題可立即修補,無需等待上游
- 客製化:針對 Luna HSM 特性進行深度優化
- 穩定性:避免上游 breaking changes 影響生產環境
成本考量:
雖然自行實作增加了初期開發成本(約 6,000+ 行程式碼),但從長期來看:
- 減少了對外部依賴的風險
- 提高了系統穩定性
- 降低了未來維護成本
關鍵技術選型
| 組件 | 技術選擇 | 版本 | 用途 |
|---|---|---|---|
| Web 框架 | Axum | 0.7 | REST API 服務 |
| gRPC 框架 | Tonic + Prost | 0.12 / 0.13 | gRPC 服務與 Protobuf 序列化 |
| 非同步執行環境 | Tokio | 1.36 | 非同步 I/O 與平行控制 |
| 連接池 | deadpool | 0.10 | PKCS#11 Session 管理 |
| 稽核日誌 | rusqlite + tracing | 0.31 / 0.1 | SOC2 合規日誌 |
| FFI 綁定 | luna-cryptoki (自定義) | 0.1 | Luna HSM PKCS#11 綁定 |
| 序列化 | Serde + JSON/Protobuf | 1.0 | 資料序列化 |
| Metrics | metrics + prometheus | 0.22 / 0.14 | 效能監控 |
為什麼選擇 Axum 而非 Actix-web/Rocket?
- 原生 Tokio 整合:無需額外的執行環境轉換
- 型別驅動路由:編譯期檢查,減少執行期錯誤
- 極致效能:Hyper + Tower 的組合,效能優於大多數框架
- 中介軟體生態:Tower 生態豐富,易於延伸
為什麼使用 deadpool 而非 r2d2/bb8?
- 非同步原生:完全支援
async/await,無阻塞 - 健康檢查:內建連接健康檢查機制
- Metrics 友好:易於整合 Prometheus 監控
PQC 演算法實作
ML-DSA (Dilithium) 簽章演算法
ML-DSA (Module-Lattice-Based Digital Signature Algorithm) 是 NIST FIPS 204 標準化的後量子數位簽章演算法,前身為 Dilithium。
安全參數集
| 模式 | NIST 安全級別 | 公鑰大小 | 私鑰大小 | 簽章大小 | 等效 RSA 強度 |
|---|---|---|---|---|---|
| ML-DSA-44 | Level 2 | 1,312 B | 2,544 B | 2,420 B | RSA-2048 |
| ML-DSA-65 | Level 3 | 1,952 B | 4,000 B | 3,293 B | RSA-3072 |
| ML-DSA-87 | Level 5 | 2,592 B | 5,632 B | 4,595 B | RSA-4096 |
建議:一般應用使用 ML-DSA-65,高安全需求使用 ML-DSA-87。
使用流程
基本操作步驟:
- 建立 Session Pool - 預先建立 50 個 Session 連線
- 產生金鑰對 - 指定安全級別(Level 44/65/87)和儲存方式
- 執行簽章 - 使用私鑰對訊息簽章(3,293 bytes)
- 驗證簽章 - 使用公鑰驗證,防止偽造攻擊
關鍵設計考量:
-
金鑰生命週期:
token_object = false: Session 金鑰(程式結束自動銷毀)token_object = true: 持久化金鑰(需手動刪除)
-
簽章確定性:ML-DSA 使用 FIPS 204 定義的確定性簽章模式,相同訊息多次簽章會產生不同簽章值(內含隨機數),但都能通過驗證
-
錯誤處理:根據錯誤類型決定重試策略(金鑰不存在、HSM 斷線、未預期錯誤)
ML-KEM (Kyber) 金鑰封裝機制
ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism) 是 NIST FIPS 203 標準化的後量子金鑰封裝演算法,前身為 Kyber。
安全參數集
| 模式 | NIST 安全級別 | 公鑰大小 | 私鑰大小 | 密文大小 | 共享密鑰 | 等效 AES 強度 |
|---|---|---|---|---|---|---|
| ML-KEM-512 | Level 1 | 800 B | 1,632 B | 768 B | 32 B | AES-128 |
| ML-KEM-768 | Level 3 | 1,184 B | 2,400 B | 1,088 B | 32 B | AES-192 |
| ML-KEM-1024 | Level 5 | 1,568 B | 3,168 B | 1,568 B | 32 B | AES-256 |
建議:一般應用使用 ML-KEM-768,極高安全需求使用 ML-KEM-1024。
KEM 工作原理
使用流程
Alice 端(接收方):
- 產生金鑰對 - 產生 ML-KEM-768 公私鑰對
- 導出公鑰 - 公鑰大小 1,184 bytes,傳送給 Bob
- 解封裝 - 使用私鑰從密文恢復共享密鑰(32 bytes)
Bob 端(發送方):
- 封裝 - 使用 Alice 公鑰產生密文(1,088 bytes)和共享密鑰(32 bytes)
- 加密資料 - 使用共享密鑰進行 AES-GCM 加密
進階用法:若公鑰儲存在 HSM 中,可直接使用 handle,避免傳輸公鑰 bytes
ML-KEM 安全性考量:
- Nonce 唯一性:使用共享密鑰時,必須確保 nonce 不重複(使用計數器或隨機數)
- 密鑰使用次數:建議定期輪換金鑰對(如每 1,000,000 次封裝操作)
- 私鑰保護:私鑰永不離開 HSM,只能用於解封裝操作
HSS/LMS 一次性簽章
HSS (Hierarchical Signature System) 與 LMS (Leighton-Micali Signature) 是基於雜湊函數的後量子簽章演算法,已納入 NIST SP 800-208 標準。
特性與適用場景
| 特性 | 說明 |
|---|---|
| 安全基礎 | 雜湊函數(SHA-256/SHA-512)- 抗量子攻擊 |
| 有狀態 | 私鑰每次簽章後狀態改變(一次性簽章) |
| 簽章次數 | 由樹高 H 決定:2^H 次簽章 |
| 後量子安全 | 完全基於雜湊函數,無需額外假設 |
| 驗證速度 | 極快(僅需雜湊運算) |
| 簽章大小 | 較大(數 KB),與樹高相關 |
適用場景:
- 韌體簽章:BIOS、IoT 韌體(簽章次數有限)
- 安全啟動:Secure Boot 驗證
- 軟體更新:APK、OTA 更新包簽章
- 程式碼簽章:發布前簽署可執行檔
- 不適用:高頻簽章場景(如 TLS handshake)
樹高配置與簽章次數
| LMS 類型 | 樹高 H | 最大簽章次數 | 公鑰大小 | 簽章大小 |
|---|---|---|---|---|
LMS_SHA256_M32_H5 | 5 | 32 | ~60 B | ~1,300 B |
LMS_SHA256_M32_H10 | 10 | 1,024 | ~60 B | ~2,600 B |
LMS_SHA256_M32_H15 | 15 | 32,768 | ~60 B | ~4,000 B |
LMS_SHA256_M32_H20 | 20 | 1,048,576 | ~60 B | ~5,500 B |
LMS_SHA256_M32_H25 | 25 | 33,554,432 | ~60 B | ~7,000 B |
HSS 多層樹(提升簽章次數):
| HSS 配置 | 總簽章次數 | 說明 |
|---|---|---|
| 1-level (H=10) | 1,024 | 單層樹 |
| 2-level (H=10, H=10) | 1,048,576 | 兩層樹:1024 × 1024 |
| 2-level (H=15, H=15) | 1,073,741,824 | 兩層樹:32768 × 32768 |
實作範例
HSS/LMS 金鑰對產生與簽章流程:
-
初始化 HSM 連線池:
- 載入 Luna Cryptoki 函式庫
- 建立 50 個平行 Session 池
- 使用環境變數
HSM_PIN認證
-
產生 HSS 金鑰對:
- 參數:
TwoLevel_H10_H10(1,048,576 次簽章) - 金鑰標籤:
firmware_signing_key - 持久化儲存(
token_object: true)
- 參數:
-
查詢剩餘簽章次數:
- 呼叫
get_hss_remaining_signatures() - 初始值:1,048,576 次
- 呼叫
-
簽章韌體:
- 輸入:韌體檔案的 SHA-256 雜湊值
- 呼叫
hss_lms_sign() - 輸出:簽章 bytes
-
驗證簽章:
- 呼叫
hss_lms_verify() - 確認簽章有效性
- 呼叫
-
追蹤狀態變化:
- 簽章後剩餘次數:1,048,575(減少 1 次)
- HSM 自動更新內部狀態
狀態管理與備份:
關鍵警告:HSS/LMS 私鑰是有狀態的,每次簽章後狀態會改變。如果:
- 備份舊狀態後繼續簽章
- 稍後恢復舊備份
- 重複使用相同的一次性私鑰
→ 會導致安全性完全崩潰(簽章可被偽造)
正確備份策略:Luna HSM依照FIPS規格要求,無法備份
Luna HSM 自動狀態保護:
- HSM 自動追蹤 HSS/LMS 私鑰狀態
- 防止狀態回溯攻擊
- 當簽章次數耗盡時自動拒絕簽章
RESTful API 設計
API 端點規劃
我們的 PQC 服務提供完整的 RESTful API,符合 OpenAPI 3.0 規範:
基礎 URL: https://api.example.com/api/v1
PQC 金鑰管理
| 方法 | 端點 | 功能 | 請求體 | 回應 |
|---|---|---|---|---|
| POST | /pqc/generate | 產生 PQC 金鑰對 | PqcGenerateKeyInput | PqcGenerateKeyOutput |
| GET | /pqc/public-key | 取得公鑰 | - | PqcPublicKeyOutput |
| DELETE | /pqc/keys/{key_id} | 刪除金鑰 | - | SuccessResponse |
PQC 簽章操作
| 方法 | 端點 | 功能 | 請求體 | 回應 |
|---|---|---|---|---|
| POST | /pqc/sign | ML-DSA/HSS 簽章 | PqcSignInput | PqcSignOutput |
| POST | /pqc/verify | 驗證簽章 | PqcVerifyInput | PqcVerifyOutput |
PQC 金鑰封裝
| 方法 | 端點 | 功能 | 請求體 | 回應 |
|---|---|---|---|---|
| POST | /pqc/encapsulate | ML-KEM 封裝 | MlKemEncapsulateInput | MlKemEncapsulateOutput |
| POST | /pqc/decapsulate | ML-KEM 解封裝 | MlKemDecapsulateInput | MlKemDecapsulateOutput |
API 調用範例
基本流程
-
產生金鑰對 (
POST /pqc/generate)- 輸入:label、algorithm(ML_DSA_65/ML_KEM_768 等)、token_object
- 輸出:key_id、public_key(Base64)、HSM handles
-
簽章 (
POST /pqc/sign)- 輸入:label、algorithm、data(Base64)
- 輸出:signature(Base64,3,293 bytes)、signed_at
-
驗證簽章 (
POST /pqc/verify)- 輸入:label、algorithm、data、signature
- 輸出:valid(true/false)、verified_at
-
ML-KEM 封裝 (
POST /pqc/encapsulate)- 輸入:label、algorithm
- 輸出:ciphertext(1,088 bytes)、shared_secret(32 bytes)
- 共享密鑰僅出現一次,需立即使用或安全儲存
-
ML-KEM 解封裝 (
POST /pqc/decapsulate)- 輸入:label、algorithm、ciphertext
- 輸出:shared_secret(32 bytes)
錯誤處理
標準錯誤回應格式:
{
"error": {
"code": "KEY_NOT_FOUND",
"message": "找不到金鑰: my_ml_dsa_key",
"details": {
"label": "my_ml_dsa_key",
"suggestion": "請檢查金鑰標籤是否正確,或使用 key_id 查找"
},
"request_id": "req_1234567890"
}
}
錯誤碼對照表:
| 錯誤碼 | HTTP 狀態碼 | 說明 | 處理建議 |
|---|---|---|---|
KEY_NOT_FOUND | 404 | 金鑰不存在 | 檢查 label/key_id |
INVALID_ALGORITHM | 400 | 不支援的演算法 | 查看支援的演算法列表 |
HSM_DISCONNECTED | 503 | HSM 斷線 | 稍後重試 |
SIGNATURE_INVALID | 400 | 簽章驗證失敗 | 檢查資料與簽章 |
PIN_LOCKED | 403 | HSM PIN 鎖定 | 聯絡管理員 |
RATE_LIMIT_EXCEEDED | 429 | 請求速率超限 | 降低請求頻率 |
INTERNAL_ERROR | 500 | 內部錯誤 | 檢查日誌,聯絡支援 |
API 認證與授權
支援的認證方式:
- API Token(推薦):
curl -H "Authorization: Bearer YOUR_API_TOKEN" ...
- mTLS (Mutual TLS):
curl --cert client.crt --key client.key --cacert ca.crt ...
權限模型:
| 角色 | 權限 |
|---|---|
admin | 所有操作(包含金鑰管理) |
operator | 簽章/驗證/封裝/解封裝 |
viewer | 僅查詢公鑰與狀態 |
企業級特性
SOC2 稽核日誌
企業合規性要求對所有密碼學操作進行稽核,我們實作了符合 SOC2 標準的稽核系統:
稽核日誌設計
獨立稽核資料庫 (audit.db):
CREATE TABLE audit_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL, -- ISO 8601 格式
session_id TEXT NOT NULL, -- 關聯請求
actor_id TEXT, -- 使用者/服務帳號
actor_ip TEXT NOT NULL, -- 客戶端 IP
action TEXT NOT NULL, -- 操作類型
resource_type TEXT NOT NULL, -- 資源類型
resource_id TEXT, -- 資源 ID
algorithm TEXT, -- 使用的演算法
status TEXT NOT NULL, -- Success/Failure/Warning
error_code TEXT, -- 錯誤碼(如有)
metadata TEXT, -- JSON 格式的額外資訊
hmac_chain TEXT NOT NULL, -- 防篡改 HMAC 鏈
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX idx_audit_timestamp ON audit_logs(timestamp);
CREATE INDEX idx_audit_actor ON audit_logs(actor_id);
CREATE INDEX idx_audit_action ON audit_logs(action);
防篡改機制
稽核日誌使用 HMAC 鏈式防篡改設計:
Log Entry 1: data_1 → HMAC(key, data_1) = hash_1
Log Entry 2: data_2 + hash_1 → HMAC(key, data_2 + hash_1) = hash_2
Log Entry 3: data_3 + hash_2 → HMAC(key, data_3 + hash_2) = hash_3
...
實作邏輯:
AuditLogger 結構:
db:SQLite 連線池hmac_key:HMAC 金鑰(儲存在 HSM 中)last_hmac:最新記錄的 HMAC 值(用 RwLock 保護平行存取)
記錄事件流程 (log_event):
- 序列化事件為 JSON
- 讀取上一筆記錄的 HMAC 值
- 計算目前記錄的 HMAC(使用 HSM 中的金鑰):
HMAC(event_data + prev_hmac) - 原子寫入資料庫(包含所有欄位與 HMAC 鏈)
- 更新記憶體中的
last_hmac
驗證完整性流程 (verify_integrity):
- 從資料庫讀取所有日誌(按 ID 升冪排序)
- 逐筆驗證:
- 重新計算預期 HMAC
- 比對資料庫中儲存的 HMAC
- 若不符,回傳
false(日誌被篡改)
- 所有記錄驗證通過,回傳
true
稽核事件範例
{
"timestamp": "2026-02-03T14:30:00.123Z",
"session_id": "sess_abc123",
"actor_id": "user_john_doe",
"actor_ip": "192.168.1.100",
"action": "pqc_sign",
"resource_type": "private_key",
"resource_id": "my_ml_dsa_key",
"algorithm": "ML_DSA_65",
"status": "Success",
"metadata": {
"data_length": 1024,
"signature_length": 3293,
"request_id": "req_xyz789"
},
"hmac_chain": "a1b2c3d4e5f6..."
}
Session Pool 與效能優化
deadpool 連接池配置
FfiSessionManager 實作 (deadpool Manager trait):
建立 Session (create):
- 開啟新 PKCS#11 Session
- 使用 PIN 登入 HSM(RW Session 需要)
- 封裝為
FfiSession物件回傳
回收 Session (recycle):
- 健康檢查:產生 16 bytes 隨機數測試 Session 是否有效
- Session 正常 → 回傳
Ok(可重用) - HSM 斷線錯誤 → 回傳
StaticError(需重建)
建立連接池 (create_session_pool):
- 初始化 FFI 上下文(載入 Cryptoki 函式庫)
- 建立
FfiSessionManager - 配置 deadpool:
max_size:連接池大小runtime:Tokio 非同步執行環境
- 回傳連接池實例
配置建議:
| 場景 | Pool Size | Session 類型 |
|---|---|---|
| 低流量 API | 10-20 | RW (讀寫) |
| 中等流量 | 30-50 | RW |
| 高流量 | 50-100 | RW + RO (只讀) 混合 |
| 批次處理 | 100-200 | 專用 Pool |
效能優化技巧
1. 符號快取(Symbol Caching)
問題:每次 FFI 調用都執行 dlsym 查找函數符號,耗時 ~10-50 µs
解決方案:一次性載入所有 PKCS#11 函數指標並快取
- 建立
FfiSymbols結構儲存函數指標(C_Initialize,C_Sign,C_Verify等) - 初始化時使用
library.get()載入所有符號 - 後續調用直接使用快取的函數指標
效能提升:每次操作節省 ~10-50 µs
2. 批次操作
批次簽章策略 (batch_sign):
- 從連接池取得單一 Session
- 循環處理多個訊息簽章(共享同一個 Session)
- 減少 Session 取得/釋放的開銷
3. 預熱(Warm-up)
預熱連接池流程 (warmup_pool):
- 應用啟動時預先建立所有 Session(達到
max_size) - 測試每個 Session(產生隨機數驗證連線)
- 釋放 Session 回連接池
- 避免首次請求的冷啟動延遲
錯誤處理與韌性
Circuit Breaker 模式
Circuit Breaker 機制(防止 HSM 故障時雪崩效應):
三種狀態:
- Closed(關閉):正常運作,所有請求通過
- Open(開啟):達到失敗閾值,拒絕所有請求(快速失敗)
- HalfOpen(半開):超時後嘗試恢復,允許部分請求測試
運作邏輯:
-
Closed 狀態:
- 正常處理請求
- 失敗時記錄錯誤計數
- 達到閾值(如 5 次)→ 切換到 Open
-
Open 狀態:
- 直接拒絕請求(不呼叫 HSM)
- 超時後(如 30 秒)→ 切換到 HalfOpen
-
HalfOpen 狀態:
- 允許請求通過測試
- 成功 → 切換回 Closed
- 失敗 → 切換回 Open
使用範例:
- 初始化:
CircuitBreaker::new(failure_threshold: 5, timeout: 30s) - 包裹 HSM 操作:
circuit_breaker.call(|| session.ml_dsa_sign(...))
重試策略
use tokio::time::{sleep, Duration};
pub async fn retry_with_backoff<F, T, E>(
mut f: F,
max_retries: u32,
) -> Result<T, E>
where
F: FnMut() -> Result<T, E>,
E: std::error::Error,
{
let mut attempt = 0;
loop {
match f() {
Ok(result) => return Ok(result),
Err(e) if attempt < max_retries => {
attempt += 1;
let backoff = Duration::from_millis(100 * 2_u64.pow(attempt));
tracing::warn!(
"操作失敗,{}ms 後重試(第 {}/{} 次): {}",
backoff.as_millis(),
attempt,
max_retries,
e
);
sleep(backoff).await;
}
Err(e) => return Err(e),
}
}
}
效能基準測試
單一操作延遲
在 AMD AI+ 395 + Thales Luna A790 環境下的測試結果:
| 操作 | 平均延遲 | 中位數 | P95 | P99 | 吞吐量 |
|---|---|---|---|---|---|
| ML-DSA-44 簽章 | 1.8 ms | 1.7 ms | 2.3 ms | 3.1 ms | ~550 ops/sec |
| ML-DSA-65 簽章 | 2.5 ms | 2.4 ms | 3.2 ms | 4.5 ms | ~400 ops/sec |
| ML-DSA-87 簽章 | 4.1 ms | 4.0 ms | 5.3 ms | 7.2 ms | ~240 ops/sec |
| ML-DSA-44 驗證 | 0.9 ms | 0.8 ms | 1.2 ms | 1.7 ms | ~1,100 ops/sec |
| ML-DSA-65 驗證 | 1.3 ms | 1.2 ms | 1.7 ms | 2.4 ms | ~770 ops/sec |
| ML-DSA-87 驗證 | 2.0 ms | 1.9 ms | 2.6 ms | 3.6 ms | ~500 ops/sec |
| ML-KEM-512 封裝 | 0.8 ms | 0.7 ms | 1.0 ms | 1.4 ms | ~1,250 ops/sec |
| ML-KEM-768 封裝 | 1.2 ms | 1.1 ms | 1.5 ms | 2.1 ms | ~830 ops/sec |
| ML-KEM-1024 封裝 | 1.7 ms | 1.6 ms | 2.2 ms | 3.0 ms | ~590 ops/sec |
| ML-KEM-512 解封裝 | 0.9 ms | 0.8 ms | 1.1 ms | 1.5 ms | ~1,110 ops/sec |
| ML-KEM-768 解封裝 | 1.3 ms | 1.2 ms | 1.6 ms | 2.2 ms | ~770 ops/sec |
| ML-KEM-1024 解封裝 | 1.8 ms | 1.7 ms | 2.3 ms | 3.2 ms | ~550 ops/sec |
| HSS/LMS (H=10) 簽章 | 5.2 ms | 5.0 ms | 6.8 ms | 9.1 ms | ~190 ops/sec |
| HSS/LMS (H=10) 驗證 | 0.5 ms | 0.4 ms | 0.7 ms | 0.9 ms | ~2,000 ops/sec |
觀察:
- ML-DSA 簽章比驗證慢 ~2-3 倍
- ML-KEM 封裝與解封裝效能接近
- HSS/LMS 驗證極快(僅需雜湊運算)
API 介面效能對比
同樣的 ML-DSA-65 簽章操作,透過不同 API 介面的延遲:
| 介面 | 平均延遲 | 吞吐量 | 適用場景 |
|---|---|---|---|
| gRPC UDS (Unix Domain Socket) | 50-100 µs | 10,000-20,000 ops/sec | 本機微服務間通訊 |
| gRPC TCP (localhost) | 100-500 µs | 2,000-10,000 ops/sec | 同機器跨容器 |
| gRPC TCP (區域網路) | 500-2,000 µs | 500-2,000 ops/sec | 跨機器微服務 |
| REST HTTP (localhost) | 1-5 ms | 200-1,000 ops/sec | 外部 API、測試 |
| NATS (異步消息) | 200-500 µs | 2,000-5,000 ops/sec | 異步任務隊列 |
建議:
- 高頻內部調用:優先使用 gRPC UDS
- 跨機器調用:使用 gRPC TCP (HTTP/2 多路複用)
- 外部 API:使用 REST HTTP (易於整合)
- 異步處理:使用 NATS (解耦、高可用)
平行效能測試
測試場景:100 個平行客戶端,每個發送 1,000 次 ML-DSA-65 簽章請求
| Session Pool Size | 平均延遲 | P95 延遲 | 吞吐量 | CPU 使用率 |
|---|---|---|---|---|
| 10 | 125 ms | 380 ms | 800 ops/sec | 45% |
| 30 | 48 ms | 142 ms | 2,080 ops/sec | 72% |
| 50 (推薦) | 32 ms | 95 ms | 3,125 ops/sec | 85% |
| 100 | 30 ms | 88 ms | 3,333 ops/sec | 88% |
| 200 | 29 ms | 85 ms | 3,448 ops/sec | 90% |
結論:
- Pool Size = 50 達到最佳性價比
- 超過 100 後,邊際收益遞減
- HSM 硬體本身成為瓶頸(~3,500 ops/sec)
與傳統演算法對比
| 演算法 | 簽章延遲 | 驗證延遲 | 簽章大小 | 抗量子攻擊 |
|---|---|---|---|---|
| ML-DSA-65 | 2.5 ms | 1.3 ms | 3,293 B | ✅ 是 |
| RSA-3072 | 8.2 ms | 0.3 ms | 384 B | ❌ 否 |
| ECDSA P-256 | 1.1 ms | 1.3 ms | 64 B | ❌ 否 |
| EdDSA Ed25519 | 0.8 ms | 1.2 ms | 64 B | ❌ 否 |
重要觀察:
- ML-DSA-65 簽章延遲僅為 RSA-3072 的 30%(更快!)
- ML-DSA-65 驗證延遲與 ECDSA/EdDSA 相當
- 簽章大小是主要劣勢(3 KB vs 64-384 B)
部署與運維
Docker 容器化部署
多階段建置策略:
-
Stage 1 (Builder)
- 基礎映像:rust:1.85-slim
- 安裝建置依賴(ca-certificates、libssl-dev)
- 預先下載 Cargo 依賴(利用 Docker 快取)
- 編譯 Release 版本
-
Stage 2 (Runtime)
- 基礎映像:debian:bookworm-slim
- 僅包含執行時依賴
- 建立非 root 用戶(安全性)
- 暴露端口:8080(REST)、50051(gRPC)、9100(Metrics)
- 健康檢查:30 秒間隔
Docker Compose 部署配置:
關鍵配置項:
- Volume 掛載:Luna Client 庫(只讀)、Chrystoki.conf、稽核日誌
- 環境變數:HSM_PIN、SLOT_ID、SESSION_POOL_SIZE(50)
- 資源限制:CPU 4 核、記憶體 4 GB(限制)、2 核/2 GB(預留)
- 重啟策略:unless-stopped(除非手動停止)
- 日誌輪替:最大 100 MB,保留 10 個檔案
- 網路隔離:獨立 crypto-network
整合監控服務:
-
Prometheus(指標收集)
-
Grafana(視覺化)
-
Alertmanager(警示) - prometheus-data:/prometheus ports: - “9090:9090” command: - ‘—config.file=/etc/prometheus/prometheus.yml’ - ‘—storage.tsdb.path=/prometheus’ - ‘—storage.tsdb.retention.time=30d’ networks: - crypto-network
Grafana 視覺化
grafana: image: grafana/grafana:latest container_name: grafana volumes: - ./monitoring/grafana/dashboards:/etc/grafana/provisioning/dashboards:ro - ./monitoring/grafana/datasources:/etc/grafana/provisioning/datasources:ro - grafana-data:/var/lib/grafana ports: - “3000:3000” environment: - GF_SECURITY_ADMIN_PASSWORD=admin networks: - crypto-network
volumes: crypto-audit: driver: local crypto-logs: driver: local prometheus-data: driver: local grafana-data: driver: local
networks: crypto-network: driver: bridge
**啟動服務**:
```bash
# 建立 .env 檔案
cat > .env << EOF
HSM_PIN=your-secret-pin
EOF
# 啟動所有服務
docker compose up -d
# 查看日誌
docker compose logs -f crypto-service
# 健康檢查
curl http://localhost:8080/health
curl http://localhost:9100/metrics # Prometheus metrics
監控整合
Prometheus 配置 (monitoring/prometheus.yml):
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'crypto-service'
static_configs:
- targets: ['crypto-service:9100']
scrape_interval: 15s
metrics_path: /metrics
關鍵監控指標:
| Metric 名稱 | 類型 | 說明 |
|---|---|---|
crypto_service_requests_total | Counter | 總請求數(按 endpoint、method、status) |
crypto_service_request_duration_seconds | Histogram | 請求延遲分佈 |
crypto_service_pqc_operations_total | Counter | PQC 操作計數(按 algorithm、operation) |
crypto_service_hsm_session_pool_size | Gauge | Session Pool 大小 |
crypto_service_hsm_session_pool_idle | Gauge | 閒置 Session 數量 |
crypto_service_hsm_errors_total | Counter | HSM 錯誤計數(按 error_type) |
crypto_service_audit_logs_total | Counter | 稽核日誌寫入數 |
警示規則範例 (monitoring/alerts.yml):
groups:
- name: crypto_service
rules:
# HSM 斷線警示
- alert: HSMDisconnected
expr: rate(crypto_service_hsm_errors_total{error_type="hsm_disconnected"}[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "HSM 已斷線"
description: "過去 5 分鐘內偵測到 HSM 斷線錯誤"
# 高錯誤率警示
- alert: HighErrorRate
expr: rate(crypto_service_requests_total{status="error"}[5m]) > 10
for: 5m
labels:
severity: warning
annotations:
summary: "錯誤率過高"
description: "過去 5 分鐘平均錯誤率超過 10 req/sec"
# Session Pool 耗盡
- alert: SessionPoolExhausted
expr: crypto_service_hsm_session_pool_idle == 0
for: 2m
labels:
severity: warning
annotations:
summary: "Session Pool 耗盡"
description: "所有 HSM Session 都在使用中,可能需要增加 pool size"
Kubernetes 部署
Deployment 關鍵配置:
| 項目 | 配置 |
|---|---|
| 副本數 | 3(高可用) |
| 節點選擇器 | hsm.luna.enabled: "true"(專用節點) |
| 端口 | 8080(HTTP)、50051(gRPC)、9100(Metrics) |
| 環境變數 | HSM_PIN(Secret)、SESSION_POOL_SIZE(50) |
| Volume 掛載 | Luna Client、Chrystoki.conf、應用配置、稽核儲存(PVC) |
| 資源配置 | Request: 2 核/2 GB,Limit: 4 核/4 GB |
| 健康檢查 | Liveness: 30 秒,Readiness: 10 秒 |
Service 配置:
- 類型:ClusterIP(內部服務)
- 端口映射:80→8080(HTTP)、50051(gRPC)、9100(Metrics)
關鍵考量:
- 節點親和性:僅部署到已安裝 Luna Client 的節點
- 秘密管理:HSM PIN 儲存在 Kubernetes Secret 中
- 持久化儲存:稽核日誌使用 PVC(PersistentVolumeClaim)
- Prometheus 整合:透過 annotations 自動服務發現
最佳實踐與陷阱
金鑰管理最佳實踐
推薦做法
1. 金鑰類型選擇
- 開發/測試:使用 Session 金鑰(
token_object = false,程式結束自動清除) - 生產環境:使用 Token 金鑰(
token_object = true,持久化)
2. 金鑰標籤命名規範
// 生產環境:Token 金鑰(持久化到 HSM)
let (pub_handle, priv_handle) = session.generate_ml_dsa_keypair(
MlDsaParam::Level65,
"prod_signing_key_2026",
true, // token_object = true
).await?;
3. 使用描述性的金鑰標籤
// 好的標籤:包含用途、演算法、日期
"api_auth_ml_dsa_65_2026_02"
"firmware_sign_hss_lms_2026"
"kem_session_ml_kem_768_20260203"
// 差的標籤
"key1"
"test"
"my_key"
4. 定期輪換金鑰
- 建議每 90 天輪換一次
- 流程:產生新金鑰 → 更新配置 → 等待舊金鑰閒置 → 刪除舊金鑰
5. 匯出公鑰進行備份
- 公鑰可匯出並安全分享
- 私鑰永不離開 HSM
DON’T(避免的做法)
1. 不要在 HSM 中儲存過多金鑰
- Luna HSM 儲存容量有限
- 應定期清理不再使用的金鑰
- 金鑰數量與查詢時間成正比
2. 不要重複使用相同標籤
- 可能導致
find_key_by_label()找到舊金鑰 - 應先刪除舊金鑰或使用不同標籤(加入日期)
3. 不要忽略金鑰使用次數限制(HSS/LMS)
- HSS/LMS 金鑰簽章次數有上限(由樹高決定)
- 應定期檢查並提前輪換
4. 不要硬編碼 HSM PIN
- 硬編碼在程式碼中極度危險
- 從環境變數讀取
- 更好:從 Secret Manager(Vault/AWS Secrets Manager)讀取
安全考量
1. 網路隔離
Firewall 規則:
- 只允許 Crypto Service IP 存取 HSM(Port 1792)
- 拒絕其他來源的連線
2. mTLS 認證
gRPC Server 配置要點:
- 載入伺服器憑證和私鑰
- 配置客戶端 CA 憑證(驗證客戶端身份)
- 啟用雙向 TLS 認證 .add_service(HsmServiceServer::new(service)) .serve(addr) .await?;
#### 3. API 速率限制
```rust
use tower::limit::RateLimitLayer;
use std::time::Duration;
let rate_limit = RateLimitLayer::new(
100, // 每秒最多 100 請求
Duration::from_secs(1),
);
let app = Router::new()
.route("/api/v1/pqc/sign", post(pqc_sign))
.layer(rate_limit);
4. 輸入驗證
// 驗證資料大小
const MAX_MESSAGE_SIZE: usize = 1024 * 1024; // 1 MB
pub async fn pqc_sign(
Json(input): Json<PqcSignInput>,
) -> Result<Json<PqcSignOutput>, ApiError> {
// 驗證資料大小
if input.data.len() > MAX_MESSAGE_SIZE {
return Err(ApiError::PayloadTooLarge);
}
// 驗證演算法
if !matches!(input.algorithm, PqcAlgorithm::MlDsa44 |
PqcAlgorithm::MlDsa65 | PqcAlgorithm::MlDsa87) {
return Err(ApiError::UnsupportedAlgorithm);
}
// 執行簽章
let result = domain::handlers::pqc::pqc_sign(&core, input).await?;
Ok(Json(result))
}
效能調優
1. Session Pool 大小調整
經驗法則:
Pool Size = (預期 QPS × 平均操作延遲) × 1.5
範例:
- 預期 QPS: 1,000
- ML-DSA-65 簽章延遲: 2.5 ms = 0.0025 sec
- Pool Size = 1000 × 0.0025 × 1.5 = 3.75 ≈ 4
但考慮突發流量,建議設定為 10-20
動態調整:
// 監控 Pool 使用率
let pool_usage = (pool.status().size - pool.status().available) as f64
/ pool.status().size as f64;
if pool_usage > 0.8 {
eprintln!("Session Pool 使用率過高: {:.1}%", pool_usage * 100.0);
// 考慮增加 Pool Size 或延伸服務
}
2. 快取公鑰
use moka::future::Cache;
pub struct PublicKeyCache {
cache: Cache<String, Vec<u8>>, // label -> public_key_bytes
}
impl PublicKeyCache {
pub fn new() -> Self {
let cache = Cache::builder()
.max_capacity(1000)
.time_to_live(Duration::from_secs(3600)) // 1 小時
.build();
Self { cache }
}
pub async fn get_or_fetch(
&self,
session: &FfiSession,
label: &str,
) -> Result<Vec<u8>, Pkcs11Error> {
if let Some(cached) = self.cache.get(label).await {
return Ok(cached);
}
let pub_handle = session.find_key_by_label(label.as_bytes(), CKO_PUBLIC_KEY)?;
let pub_key_bytes = session.get_ml_dsa_public_key(pub_handle).await?;
self.cache.insert(label.to_string(), pub_key_bytes.clone()).await;
Ok(pub_key_bytes)
}
}
3. 批次處理
// 效率低:逐個簽章
for message in messages {
let sig = session.ml_dsa_sign(priv_handle, message, param).await?;
signatures.push(sig);
}
// 效率高:批次簽章(共用 Session)
let session = pool.get().await?;
let mut signatures = Vec::with_capacity(messages.len());
for message in messages {
let sig = session.ml_dsa_sign(priv_handle, message, param).await?;
signatures.push(sig);
}
drop(session); // 一次性釋放 Session
未來展望
混合密碼學方案
雙重簽章憑證(Composite Signatures):
Certificate {
tbsCertificate: {
subject: "CN=example.com",
subjectPublicKeyInfo: {
algorithm: id-composite-key,
subjectPublicKey: (ECC P-256 公鑰, ML-DSA-65 公鑰)
}
},
signatureAlgorithm: id-composite-signature,
signature: (ECDSA 簽章, ML-DSA-65 簽章)
}
優勢:
- 同時提供傳統與後量子安全性
- 平滑遷移路徑(舊系統驗證 ECDSA,新系統驗證雙重)
- 符合 IETF Draft: Composite Signatures
NIST 第四輪候選演算法
| 演算法 | 類型 | 狀態 | 預計標準化 |
|---|---|---|---|
| FALCON | 簽章 | 標準化中 (FIPS 206) | 2026 Q3 |
| BIKE | KEM | 第四輪候選 | 2027+ |
| HQC | KEM | 第四輪候選 | 2027+ |
FALCON 特色:
- 簽章大小極小(~666 B for FALCON-512)
- 驗證極快
- 適合資源受限環境(IoT)
量子安全通訊協議
TLS 1.3 PQC 支援(RFC 9370):
ClientHello:
- supported_groups: x25519_mlkem768, secp256r1_mlkem768
- signature_algorithms: ecdsa_secp256r1_mlkem768
ServerHello:
- key_share: x25519_mlkem768
- signature: ecdsa_secp256r1_mlkem768
混合金鑰交換:
- X25519 (ECDH) + ML-KEM-768
- 即使一方被破解,另一方仍提供保護
結語與資源
關鍵要點
- 後量子威脅真實存在:「先儲存後破解」攻擊已在進行中
- NIST 標準已完成:ML-DSA/ML-KEM 已正式標準化(FIPS 203/204)
- Luna HSM + Rust = 最佳組合:硬體安全 + 記憶體安全
- 自定義 FFI 綁定必要:解決相容性問題,支援 Luna 專有延伸
- 企業級特性完整:SOC2 稽核、Session Pool、Circuit Breaker
- 效能媲美傳統演算法:ML-DSA-65 簽章比 RSA-3072 更快
專案資源
參考文件:
- NIST Post-Quantum Cryptography
- FIPS 203 (ML-KEM)
- FIPS 204 (ML-DSA)
- Thales Luna HSM 文件
- rust-cryptoki GitHub
延伸閱讀:
- IETF: Composite Signatures and Encryption
- RFC 9370: TLS 1.3 Hybrid Key Exchange
- NIST SP 800-208: HSS/LMS
本文完成於 2026-02-03,基於 Thales Luna HSM Firmware 7.9.1、Rust 1.85 與 NIST FIPS 203/204 標準撰寫。
如有任何問題或建議,歡迎透過聯絡我們 頁面與我們聯繫。