iT邦幫忙

2025 iThome 鐵人賽

DAY 30
0
Security

裸機實作 SLH-DSA 簽章:FIPS 205 之演算法實作、測試、防禦與跨平台系列 第 30

Day 30 總結 FIPS 205 的 SLH-DSA 簽章實作與實驗

  • 分享至 

  • xImage
  •  

總結一下過去數周做了什麼事,實作了 FIPS 205 的簽章演算法 (沒有全部都正確實作),這個過程中知道了簽章演算法用到了什麼資料結構,包括 ADRS、compressed ADRS、FORS signature、WOTS+ signature、HT signature;知道了NIST 800-90A/90B/90C、approved RBG、熵、TRNG 和亂數彼此的關係,知道他們如何用來產生種子,如何合併 sk.seed、sk.prf、pk.seed 和 pk.root 來產生 private key 和 public key。

完成了在 GitHub Actions 上整合 Renode 到 CI 的流程中,並能在 CI 的流程裡進行基礎的測試與檢查。

實驗了在 Renode 上模擬 nRF52840 和 nRF5340 的 bare-metal 執行環境,在 Renode 觀察 UART 的輸出;也經歷了針對 nRF52840 和 nRF5340 的 bare-metal 開發,實驗如何不依賴 OS 的 library。

實驗了如何將演算法從硬體抽離,讓演算法的同一個實作能在不同的硬體上執行,也就是說,在 ci.yml 裡讓同一個實作 build 出兩個 elf file,分別在 Renode 模擬 nRF52840 和 nRF5340 的執行結果

實驗了連結到 Oberon 用他的 library 去算 HMAC-SHA-256 Day 18

實驗了連結到 MbedTLS 用他的 library 去計算 SHA-256,不過失敗了

實驗了依照 PSA Crypto API 的 function declaration 去改寫程式,為了相容 Platform Security Architecture (PSA) 做準備。這個過程中知道了如何用 PSA Crypto API 產生亂數、產生 key、計算 SHA-256 和計算 HMAC-SHA-256,但是結果經過 KAT 的比對,失敗了

在用 psa_key_id_t 改寫程式的時候也意識到了 把 sk.seed 和 sk.prf 當參數直接在 function 之間傳遞並不安全,所以 PSA Crypto API 傳遞的是 psa_key_id_t 而不是 sk.seed 和 sk.prf 的 byte array。

實驗了用 NIST 提供的 PQCgenKAT_sign.c 加上我自己的實作 能否產出和 SPHINCS+ 官方一致的 PQCsignKAT_64.rsp,很可惜並沒有成功,PQCsignKAT_64.rsp 最終還是不一樣。

實驗了將 elf 轉成 hex 並寫到 nRF5340 DK,然後觀察 nRF5340 DK 的 UART 的輸出,很可惜又沒有成功,在 Serial Terminal 還是什麼都沒看到。

另外,UART 的初始化並沒有從硬體抽象化出來,uarte0_init 的設定如果換了硬體就不能使用了,這個在 KAT 比對的時候有發現。由於 KAT 依賴 OpenSSL,所以執行環境是 ubuntu on x86,一旦真輸出到 UART 就直接 Segmentation fault (core dumped)。

雖然幾個重要的階段都失敗了,但我覺得這是一個好的開始,因為方向是正確的,只要總結失敗的經驗,30天鐵人賽結束之後能以此為基礎繼續開發。

但有一件事,我希望在 Day 30 能辦到,就是將 SHA256 的計算 offload 到 CC312,今天將以這個目標作為 ending。

首先就將呼叫 sha256 的地方都換成 psa_hash_compute

psa_status_t status = psa_hash_compute(PSA_ALG_SHA_256, 
                                       combined, 
                                       sizeof(combined), 
                                       out32, 
                                       sizeof(out32), 
                                       &olen);

然後加上 #ifndef HARD,只有在純軟的模擬環境才用自己寫的 psa_mac_compute, psa_hash_compute, ...

#ifndef HARD
psa_status_t psa_crypto_init(void)
{
    ....
}
...
#endif

然後修改 create_sk_prf,加上 #ifndef HARD,在純軟的模擬環境就直接產生一個亂數,如果是實體的開發版,就呼叫 psa_generate_key

psa_status_t create_sk_prf(psa_key_id_t *sk_prf_key_id, uint8_t desired_key_id) {
    #ifndef HARD
        // if computed by software, use random and ignore p_sk_prf_key_id
        psa_generate_random(sk_prf, SPX_N);
        return PSA_SUCCESS;        
    #endif

    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_key_id);

    return psa_generate_key(&attr, sk_prf_key_id);
}

Makefile 也要修改,加上這段

...
else ifeq ($(TARGET),nrf5340dk_hard)
  ...
  ARCH_DIR   := cortex-m33
  FLOAT_DIR  := hard-float
  ELF := sign_nrf5340dk_hard.elf
  NRF_CC_BACKEND := nrf_cc312_mbedcrypto
  # CC312/Platform(PSA)路徑
  NRFX := third_party/nrfxlib/crypto
  VER  := 0.9.19
  LIBDIR_CC312 := $(NRFX)/nrf_cc312_mbedcrypto/lib/cortex-m33/hard-float/no-interrupts
  LIBDIR_PLAT  := $(NRFX)/nrf_cc3xx_platform/lib/cortex-m33/hard-float/no-interrupts

  LIBS := \
    $(LIBDIR_CC312)/libnrf_cc312_psa_crypto_$(VER).a \
    $(LIBDIR_CC312)/libnrf_cc312_core_$(VER).a \
    $(LIBDIR_PLAT)/libnrf_cc3xx_platform_$(VER).a

endif

ci.yml 也要修改,加上這段

      - name: Make HEX from sign_nrf5340dk_hard.elf
        shell: bash
        run: |
          set -euo pipefail
          arm-none-eabi-objcopy -O ihex sign_nrf5340dk_hard.elf sign_nrf5340dk_hard.hex
          ls -l sign_nrf5340dk_hard.elf sign_nrf5340dk_hard.hex

其餘的修改屬於勞力密集,就不一一列舉,這個方向應該是對的,不過最終還是沒成功。

額外提醒一下 nrf_cc312_mbedcrypto/lib 這裡的靜態連結檔 (*.a) 是有版權的,如果要使用請參閱 Nordic 的 LICENSE

由於我對 Nordic 的 LICENSE 在散佈靜態連結檔 (*.a) 的條文沒有足夠的把握,所以我在 GitHub 上就不把靜態連結檔 (*.a) 放進 elf/hex 裡,有興趣的同好請自行參考 nrf_cc312_mbedcrypto/lib

總結這 30 天的經驗,我覺得我更加了解 SLH-DSA 簽章演算法是如何 通過一個個 signature 和公鑰的產生來組成最終的簽章,學到了 FORS signature、WOTS+ signature 和 HT signature 各自產生的順序以及各自是如何建立的。在實作 SLH-DSA 的演算法中也學到了有哪些細節要注意,從 Makefile 到 ci.yml、從 function declaration 到 library 的路徑指定、學到了產生亂數的順序如何影響 NIST KAT 比對結果。雖然一路都是失敗居多,但每一次的失敗都是一個經驗,都能讓後續的延伸開發避免犯同樣的錯誤。


上一篇
Day 29 Writing hex to nRF5340 DK Application core
系列文
裸機實作 SLH-DSA 簽章:FIPS 205 之演算法實作、測試、防禦與跨平台30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言