hmac.rst 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. 哈希消息认证码 (HMAC)
  2. =============================================
  3. :link_to_translation:`en:[English]`
  4. 哈希消息认证码 (HMAC) 是一种安全的身份认证技术,支持使用预共享的密钥验证消息的真实性和完整性。利用烧录在 eFuse 块中的密钥,HMAC 可以为生成 SHA256-HMAC 提供硬件加速。
  5. 更多有关应用操作流程,及 HMAC 计算过程的详细信息,请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **HMAC 加速器 (HMAC)** [`PDF <{IDF_TARGET_TRM_CN_URL}#hmac>`__]。
  6. 通用应用方案
  7. -------------------
  8. 现有 A、B 双方,他们都需要验证对方发送消息的真实性和完整性。那么在开始发送消息前,双方应通过安全通道交换密钥。
  9. 要验证来自 A 的信息,B 可遵循以下步骤:
  10. - A 计算要发送的消息的 HMAC。
  11. - A 将消息及消息的 HMAC 一并发送给 B。
  12. - B 自行计算所接收消息的 HMAC。
  13. - B 检查接收到的 HMAC 是否与计算得出的 HMAC 匹配。
  14. 若两个 HMAC 匹配,消息为真。
  15. 但 HMAC 本身还可以应用于更多场景,如支持 HMAC 的挑战-应答协议,或作为更多安全模块(详见下文)的密钥输入等。
  16. {IDF_TARGET_NAME} 上的 HMAC
  17. -----------------------------
  18. 在 {IDF_TARGET_NAME} HMAC 模块的 eFuse 中会烧录一个密钥,可将该 eFuse 密钥设置为禁止所有外部资源访问,避免密钥泄露。
  19. 此外,在 {IDF_TARGET_NAME} 上的 HMAC 有以下三种应用场景:
  20. #. HMAC 支持软件使用
  21. #. HMAC 用作数字签名 (DS) 的密钥
  22. #. HMAC 用于启用软禁用的 JTAG 接口
  23. 第一种应用场景称为 **上行** 模式,后两种应用场景称为 **下行** 模式。
  24. HMAC 的 eFuse 密钥
  25. ^^^^^^^^^^^^^^^^^^^
  26. 在 {IDF_TARGET_NAME} 中,有六个物理 eFuse 块可用作 HMAC 的密钥,分别是块 4 到块 9。在 API 中,枚举类型 :cpp:enum:`hmac_key_id_t` 将这些块映射为 ``HMAC_KEY0`` ~ ``HMAC_KEY5``。
  27. 每个密钥都有相应的 eFuse 参数 **密钥功能 (key purpose)**,决定密钥应用于 HMAC 的哪种应用场景。
  28. .. list-table::
  29. :widths: 15 70
  30. :header-rows: 1
  31. * - 密钥功能
  32. - 应用场景
  33. * - 8
  34. - HMAC 支持软件使用
  35. * - 7
  36. - HMAC 用作数字签名 (DS) 的密钥
  37. * - 6
  38. - HMAC 启用软禁用的 JTAG 接口
  39. * - 5
  40. - HMAC 既用作数字签名 (DS) 的密钥,又用于启用 JTAG 接口
  41. 这样一来,可以确保密钥用于原定场景。
  42. 要计算 HMAC,软件必须同时提供包含密钥的密钥块 ID,以及 **密钥功能** (详情请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **eFuse 控制器 (eFuse)** [`PDF <{IDF_TARGET_TRM_CN_URL}#efuse>`__])。
  43. 在进行 HMAC 密钥计算前,HMAC 会验证软件所提供密钥块的功能。在软件所提供 ID 的对应密钥块中,eFuse 存储了密钥块的功能。只有当软件所提供密钥块的功能与 eFuse 中存储的密钥块功能匹配,才会继续进行计算。
  44. HMAC 支持软件使用
  45. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  46. 密钥功能值:8
  47. 在此情况下,HMAC 支持软件使用,如验证消息真实性等。
  48. API :cpp:func:`esp_hmac_calculate` 用于计算 HMAC。输入参数包括消息、消息长度以及包含密钥的 eFuse 密钥块 ID,且该密钥块的 eFuse 密钥功能设置为上行模式。
  49. HMAC 用作数字签名 (DS) 的密钥
  50. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  51. 密钥功能值:7、5
  52. HMAC 可用作密钥派生函数,解码数字签名模块使用的私钥参数。在此情况下,硬件使用标准信息进行计算。在 HMAC 部分只需提供 eFuse 密钥块和功能;而在数字签名模块则还需要一些额外参数。
  53. 无论是密钥还是实际的 HMAC,都不会暴露在 HMAC 和数字签名模块之外。对 HMAC 的计算,以及将其传递给数字签名模块的过程,均在内部进行。
  54. 详情请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **数字签名 (DS)** [`PDF <{IDF_TARGET_TRM_CN_URL}#digsig>`__]。
  55. .. _hmac_for_enabling_jtag:
  56. HMAC 启用 JTAG 接口
  57. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  58. 密钥功能值:6、5
  59. HMAC 的第三种应用场景是将其作为密钥,启用软禁用的 JTAG 接口。
  60. 重新启用 JTAG 接口的步骤如下:
  61. **第一步:设置**
  62. 1. 生成一个 256 位的 HMAC 密钥,用于重新启用 JTAG。
  63. 2. 将步骤 1 获得的密钥写入 eFuse 块,且 eFuse 块的密钥功能参数应为 HMAC_DOWN_ALL (5) 或 HMAC_DOWN_JTAG (6)。为此,可以使用固件中的 ``esp_efuse_write_key()`` 函数,或使用主机上的 ``espefuse.py`` 完成操作。
  64. 3. 使用 ``esp_efuse_set_read_protect()`` 将 eFuse 密钥块配置为读保护,防止软件读取写入到 eFuse 密钥块中的 HMAC 密钥值。
  65. 4. 在烧录到 {IDF_TARGET_NAME} 上时,将特定的位或位组设置为 ``soft JTAG disable``。这样可以永久禁用 JTAG 接口,除非软件提供正确的密钥值进行验证。
  66. .. only:: esp32s2
  67. .. note::
  68. API **esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG)** 支持在 {IDF_TARGET_NAME} 上烧录 ``soft JTAG disable`` 位。
  69. .. only:: not esp32s2
  70. .. note::
  71. API **esp_efuse_write_field_cnt(ESP_EFUSE_SOFT_DIS_JTAG, ESP_EFUSE_SOFT_DIS_JTAG[0]->bit_count)** 支持在 {IDF_TARGET_NAME} 上烧录 ``soft JTAG disable`` 位。
  72. .. only:: esp32s2 or esp32s3
  73. .. note::
  74. 置位 ``HARD_DIS_JTAG`` eFuse 时,JTAG 处于永久禁用状态,``SOFT_DIS_JTAG`` 功能将失效。
  75. .. only:: not esp32s2 and not esp32s3
  76. .. note::
  77. 置位 ``DIS_PAD_JTAG`` eFuse 时,JTAG 处于永久禁用状态,``SOFT_DIS_JTAG`` 功能将失效。
  78. 启用 JTAG
  79. 1. 以 eFuse 中的密钥和 32 个 ``0x00`` 字节为输入,经过 HMAC-SHA256 函数处理,得到的函数输出结果即重新启用 JTAG 的密钥。
  80. 2. 从固件调用 :cpp:func:`esp_hmac_jtag_enable` 函数时,传递上一步获取的密钥值。
  81. 3. 要在固件中重新禁用 JTAG,可以重置系统,或调用 :cpp:func:`esp_hmac_jtag_disable`。
  82. 更多有关详情,请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **HMAC 加速器 (HMAC)** [`PDF <{IDF_TARGET_TRM_CN_URL}#hmac>`__]。
  83. 应用示例
  84. -------------------
  85. 以下为针对特定应用场景的实例代码,可用于设置 eFuse 密钥,并将其用于计算支持软件使用的 HMAC。
  86. ``esp_efuse_write_key`` 可以设置 eFuse 中的物理密钥块 4,并设置其功能。``ESP_EFUSE_KEY_PURPOSE_HMAC_UP`` (8) 表明,该密钥仅适用于生成支持软件使用的 HMAC。
  87. .. code-block:: c
  88. #include "esp_efuse.h"
  89. const uint8_t key_data[32] = { ... };
  90. esp_err_t status = esp_efuse_write_key(EFUSE_BLK_KEY4,
  91. ESP_EFUSE_KEY_PURPOSE_HMAC_UP,
  92. key_data, sizeof(key_data));
  93. if (status == ESP_OK) {
  94. // 密钥写入成功
  95. } else {
  96. // 密钥写入失败,可能已写入过
  97. }
  98. 接下来可以使用已存储的密钥来计算 HMAC,供软件使用。
  99. .. code-block:: c
  100. #include "esp_hmac.h"
  101. uint8_t hmac[32];
  102. const char *message = "Hello, HMAC!";
  103. const size_t msg_len = 12;
  104. esp_err_t result = esp_hmac_calculate(HMAC_KEY4, message, msg_len, hmac);
  105. if (result == ESP_OK) {
  106. // HMAC 已写入 hmac 数组
  107. } else {
  108. // 计算 HMAC 失败
  109. }
  110. API 参考
  111. -------------
  112. .. include-build-file:: inc/esp_hmac.inc