今日重點: 將 sk_seed, pk_seed, sk_prf 換成 psa_key_id_t
SLH-DSA 簽章演算法的 function 是直接傳 sk_seed, pk_seed, sk_prf,因為 FIPS 205 的重點是規範演算法,所以直接傳 sk_seed, pk_seed, sk_prf 完全沒問題,但落實到一個安全的產品,就不能真把私鑰當參數直接在各 function 之間傳遞,所以 PSA Crypto API 就是以 psa_key_id_t 做為 function parameter。
在我盤點之前的實作有哪些要改的時候,發現全部以 sk.seed 或 sk.prf 當參數傳遞的 function,最終都是把 sk.seed 或 sk.prf 傳到 PRF (或者是 H_msg, PRF_msg),所以修改主要有三部份,其一,產生 sk.prf, sk.seed, pk.seed 三個亂數,細節後述;其二,是各 function 修改 function parameter list 和 implementation 的參數傳遞,這個部份雖然要改的地方比較多,不過屬於勞力密集,技術含量不高,將 source code 貼上來也略顯多餘,有興趣的直接在 GitHub 上看就可以了;其三就比較棘手了,因為要讓 PRF (或者是 H_msg, PRF_msg) 能使用 key id 取得 sk.seed, sk.prf 做 SHA-256 或 concatenation (或是 HMAC-SHA-256, MGF1-SHA-256),就要考慮到 key 能不能export,能不能直接把 key id 給 CC310/CC312 做 HMAC-SHA-256 或 SHA-256。
首先就是產生種子的環節(sk.prf, sk.seed, pk.root, pk.seed),因為 sk.prf 在 SLH-DSA 演算法裡並不需要和其他變數做 concatenation,所以理論上,直接拿 sk.prf 的 key id 給 CC310/CC312 算 HMAC-SHA-256 應該是沒問題,但我不確定,所以我會呼叫 psa_generate_key
來拿到 sk.prf, sk.seed, pk.seed。其中 call psa_generate_key 產生 sk.prf 的時候是另外寫一個 function 來取得 sk_prf_key_id。之後在實體的開發版會實驗看看能否真的直接把 sk_prf_key_id 給 CC310/CC312 就能算 HMAC-SHA-256,但在Renode模擬環境執行的時候,sk.prf, sk.seed, pk.seed 是直接用 static storage duration。
有一件事值得提,sk.seed, pk.seed 其實更適合用 psa_generate_random
,因為目前 Mbed TLS 還不支援 SLH-DSA,所以 sk.seed, pk.seed 並沒有適合的 key type 能用,他們不像 sk.prf,因為 sk.prf 在整個 SLH-DSA 演算法裡只會拿去算 HMAC-SHA-256,所以 sk.prf 用 psa_generate_key
來產生是合適的。但我還是用 psa_generate_key
,主要是因為我希望參數傳遞能一致以 psa_key_id_t 為基礎。
底下是產生三個 key_id 的 source code,只有 sk_prf_key_id 有另外指定 PSA_ALG_HMAC(PSA_ALG_SHA_256),因為之後在實體的開發版需要靠他去實驗硬體的 HMAC-SHA-256。
psa_status_t create_sk_prf(psa_key_id_t desired_id, psa_key_id_t *sk_prf_key_id) {
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&attr, PSA_KEY_TYPE_HMAC);
psa_set_key_bits(&attr, (psa_key_bits_t)(8 * SPX_N));
psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_256));
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE);
psa_set_key_id(&attr, desired_id);
return psa_generate_key(&attr, sk_prf_key_id);
}
void generate_key(psa_key_id_t *p_sk_seed_key_id,
psa_key_id_t *p_sk_prf_key_id,
psa_key_id_t *p_pk_key_id)
{
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attributes, 1);
psa_status_t status = psa_generate_key(&attributes, p_sk_seed_key_id); // sk_seed
...
status = create_sk_prf(2, p_sk_prf_key_id); // sk_prf
...
psa_set_key_id(&attributes, 3);
status = psa_generate_key(&attributes, p_pk_key_id); // pk_seed
...
}
sk.prf, sk.seed, pk.seed 是直接用 static storage duration,這樣是不安全的,千萬別在正式環境這麼做,我這裡純粹只是基於方便實現 SLH-DSA 簽章演算法,所採用的 便宜行事 的策略。
static uint8_t sk_seed[SPX_N] = {0};
static uint8_t sk_prf[SPX_N] = {0};
static uint8_t pk_seed[SPX_N] = {0};
明天就會在 Renode 的模擬環境裡,把上述的修改配合 NIST KAT 做簽章結果的驗證。