1 /*
2 * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include "rom/efuse.h"
9 #include "rom/hmac.h"
10 #include "rom/ets_sys.h"
11 #include "esp_efuse.h"
12 #include "esp_efuse_table.h"
13 #include "esp_hmac.h"
14 #include "esp_log.h"
15 #include "esp_crypto_lock.h"
16 #include "soc/hwcrypto_reg.h"
17 #include "soc/system_reg.h"
18
19 #if !CONFIG_IDF_TARGET_ESP32S2
20 #include "hal/hmac_hal.h"
21 #include "esp_private/periph_ctrl.h"
22 #endif
23
24 #define SHA256_BLOCK_SZ 64
25 #define SHA256_PAD_SZ 8
26
27 #if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2)
28 #define JTAG_STATUS_BIT ESP_EFUSE_HARD_DIS_JTAG
29 #else
30 /* For ESP32C3, ESP32C6, ESP32H2 */
31 #define JTAG_STATUS_BIT ESP_EFUSE_DIS_PAD_JTAG
32 #endif
33 static const char *TAG = "esp_hmac";
34
35 #if !CONFIG_IDF_TARGET_ESP32S2
36 /**
37 * @brief Apply the HMAC padding without the embedded length.
38 *
39 * @note This function does not check the data length, it is the responsibility of the other functions in this
40 * module to make sure that \c data_len is at most SHA256_BLOCK_SZ - 1 so the padding fits in.
41 * Otherwise, this function has undefined behavior.
42 * Note however, that for the actual HMAC implementation, the length also needs to be applied at the end
43 * of the block. This function alone deosn't do that.
44 */
write_and_padd(uint8_t * block,const uint8_t * data,uint16_t data_len)45 static void write_and_padd(uint8_t *block, const uint8_t *data, uint16_t data_len)
46 {
47 memcpy(block, data, data_len);
48 // Apply a one bit, followed by zero bits (refer to the TRM of respective target).
49 block[data_len] = 0x80;
50 bzero(block + data_len + 1, SHA256_BLOCK_SZ - data_len - 1);
51 }
52
esp_hmac_calculate(hmac_key_id_t key_id,const void * message,size_t message_len,uint8_t * hmac)53 esp_err_t esp_hmac_calculate(hmac_key_id_t key_id,
54 const void *message,
55 size_t message_len,
56 uint8_t *hmac)
57 {
58 const uint8_t *message_bytes = (const uint8_t *)message;
59
60 if (!message || !hmac) {
61 return ESP_ERR_INVALID_ARG;
62 }
63 if (key_id >= HMAC_KEY_MAX) {
64 return ESP_ERR_INVALID_ARG;
65 }
66
67 esp_crypto_hmac_lock_acquire();
68
69 // We also enable SHA and DS here. SHA is used by HMAC, DS will otherwise hold SHA in reset state.
70 periph_module_enable(PERIPH_HMAC_MODULE);
71 periph_module_enable(PERIPH_SHA_MODULE);
72 periph_module_enable(PERIPH_DS_MODULE);
73
74 hmac_hal_start();
75
76 uint32_t conf_error = hmac_hal_configure(HMAC_OUTPUT_USER, key_id);
77 if (conf_error) {
78 esp_crypto_hmac_lock_release();
79 return ESP_FAIL;
80 }
81
82 if (message_len + 1 + SHA256_PAD_SZ <= SHA256_BLOCK_SZ) {
83 // If message including padding is only one block...
84 // Last message block, so apply SHA-256 padding rules in software
85 uint8_t block[SHA256_BLOCK_SZ];
86 uint64_t bit_len = __builtin_bswap64(message_len * 8 + 512);
87
88 write_and_padd(block, message_bytes, message_len);
89 // Final block: append the bit length in this block and signal padding to peripheral
90 memcpy(block + SHA256_BLOCK_SZ - sizeof(bit_len),
91 &bit_len, sizeof(bit_len));
92 hmac_hal_write_one_block_512(block);
93 } else {
94 // If message including padding is needs more than one block
95
96 // write all blocks without padding except the last one
97 size_t remaining_blocks = message_len / SHA256_BLOCK_SZ;
98 for (int i = 1; i < remaining_blocks; i++) {
99 hmac_hal_write_block_512(message_bytes);
100 message_bytes += SHA256_BLOCK_SZ;
101 hmac_hal_next_block_normal();
102 }
103
104 // If message fits into one block but without padding, we must not write another block.
105 if (remaining_blocks) {
106 hmac_hal_write_block_512(message_bytes);
107 message_bytes += SHA256_BLOCK_SZ;
108 }
109
110 size_t remaining = message_len % SHA256_BLOCK_SZ;
111 // Last message block, so apply SHA-256 padding rules in software
112 uint8_t block[SHA256_BLOCK_SZ];
113 uint64_t bit_len = __builtin_bswap64(message_len * 8 + 512);
114
115 // If the remaining message and appended padding doesn't fit into a single block, we have to write an
116 // extra block with the rest of the message and potential padding first.
117 if (remaining >= SHA256_BLOCK_SZ - SHA256_PAD_SZ) {
118 write_and_padd(block, message_bytes, remaining);
119 hmac_hal_next_block_normal();
120 hmac_hal_write_block_512(block);
121 bzero(block, SHA256_BLOCK_SZ);
122 } else {
123 write_and_padd(block, message_bytes, remaining);
124 }
125 memcpy(block + SHA256_BLOCK_SZ - sizeof(bit_len),
126 &bit_len, sizeof(bit_len));
127 hmac_hal_next_block_padding();
128 hmac_hal_write_block_512(block);
129 }
130
131 // Read back result (bit swapped)
132 hmac_hal_read_result_256(hmac);
133
134 periph_module_disable(PERIPH_DS_MODULE);
135 periph_module_disable(PERIPH_SHA_MODULE);
136 periph_module_disable(PERIPH_HMAC_MODULE);
137
138 esp_crypto_hmac_lock_release();
139
140 return ESP_OK;
141 }
142
convert_key_type(hmac_key_id_t key_id)143 static ets_efuse_block_t convert_key_type(hmac_key_id_t key_id) {
144 return ETS_EFUSE_BLOCK_KEY0 + (ets_efuse_block_t) key_id;
145 }
146
esp_hmac_jtag_enable(hmac_key_id_t key_id,const uint8_t * token)147 esp_err_t esp_hmac_jtag_enable(hmac_key_id_t key_id, const uint8_t *token)
148 {
149 int ets_status;
150 esp_err_t err = ESP_OK;
151
152 if ((!token) || (key_id >= HMAC_KEY_MAX))
153 return ESP_ERR_INVALID_ARG;
154
155 /* Check if JTAG is permanently disabled by HW Disable eFuse */
156 if (esp_efuse_read_field_bit(JTAG_STATUS_BIT)) {
157 ESP_LOGE(TAG, "JTAG disabled permanently.");
158 return ESP_FAIL;
159 }
160
161 esp_crypto_hmac_lock_acquire();
162
163 ets_status = ets_jtag_enable_temporarily(token, convert_key_type(key_id));
164
165 if (ets_status != ETS_OK) {
166 // ets_jtag_enable_temporarily returns either ETS_OK or ETS_FAIL
167 err = ESP_FAIL;
168 ESP_LOGE(TAG, "JTAG re-enabling failed (%d)", err);
169 }
170
171 ESP_LOGD(TAG, "HMAC computation in downstream mode is completed.");
172
173 periph_module_disable(PERIPH_HMAC_MODULE);
174
175 esp_crypto_hmac_lock_release();
176
177 return err;
178 }
179
esp_hmac_jtag_disable()180 esp_err_t esp_hmac_jtag_disable()
181 {
182 esp_crypto_hmac_lock_acquire();
183 periph_module_enable(PERIPH_HMAC_MODULE);
184 REG_WRITE(HMAC_SET_INVALIDATE_JTAG_REG, 1);
185 periph_module_disable(PERIPH_HMAC_MODULE);
186 esp_crypto_hmac_lock_release();
187
188 ESP_LOGD(TAG, "Invalidate JTAG result register. JTAG disabled.");
189
190 return ESP_OK;
191 }
192 #else /* !CONFIG_IDF_TARGET_ESP32S2 */
193
convert_key_type(hmac_key_id_t key_id)194 static ets_efuse_block_t convert_key_type(hmac_key_id_t key_id) {
195 return ETS_EFUSE_BLOCK_KEY0 + (ets_efuse_block_t) key_id;
196 }
197
esp_hmac_calculate(hmac_key_id_t key_id,const void * message,size_t message_len,uint8_t * hmac)198 esp_err_t esp_hmac_calculate(hmac_key_id_t key_id,
199 const void *message,
200 size_t message_len,
201 uint8_t *hmac)
202 {
203 int hmac_ret;
204 if (!message || !hmac) return ESP_ERR_INVALID_ARG;
205 if (key_id >= HMAC_KEY_MAX) return ESP_ERR_INVALID_ARG;
206
207 esp_crypto_dma_lock_acquire();
208
209 ets_hmac_enable();
210 hmac_ret = ets_hmac_calculate_message(convert_key_type(key_id), message, message_len, hmac);
211 ets_hmac_disable();
212
213 esp_crypto_dma_lock_release();
214
215 if (hmac_ret != 0) {
216 return ESP_FAIL;
217 } else {
218 return ESP_OK;
219 }
220 }
221
esp_hmac_jtag_enable(hmac_key_id_t key_id,const uint8_t * token)222 esp_err_t esp_hmac_jtag_enable(hmac_key_id_t key_id, const uint8_t *token)
223 {
224 int ets_status;
225 esp_err_t err = ESP_OK;
226
227 if ((!token) || (key_id >= HMAC_KEY_MAX))
228 return ESP_ERR_INVALID_ARG;
229
230 /* Check if JTAG is permanently disabled by HW Disable eFuse */
231 if (esp_efuse_read_field_bit(ESP_EFUSE_HARD_DIS_JTAG)) {
232 ESP_LOGE(TAG, "JTAG disabled permanently.");
233 return ESP_FAIL;
234 }
235
236 esp_crypto_dma_lock_acquire();
237
238 ets_hmac_enable();
239
240 /* Token updating into HMAC module. */
241 for (int i = 0; i < 32; i += 4) {
242 uint32_t key_word;
243 memcpy(&key_word, &token[i], 4);
244 REG_WRITE(DPORT_JTAG_CTRL_0_REG + i, __builtin_bswap32(key_word));
245 }
246
247 ets_status = ets_hmac_calculate_downstream(convert_key_type(key_id), ETS_EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG);
248 if (ets_status != ETS_OK) {
249 err = ESP_FAIL;
250 ESP_LOGE(TAG, "HMAC downstream JTAG enable mode setting failed. (%d)", err);
251 }
252
253 ESP_LOGD(TAG, "HMAC computation in downstream mode is completed.");
254
255 ets_hmac_disable();
256
257 esp_crypto_dma_lock_release();
258
259 return err;
260 }
261
esp_hmac_jtag_disable()262 esp_err_t esp_hmac_jtag_disable()
263 {
264 esp_crypto_dma_lock_acquire();
265
266 ets_hmac_enable();
267 REG_WRITE(HMAC_SET_INVALIDATE_JTAG_REG, 1);
268 ets_hmac_disable();
269
270 esp_crypto_dma_lock_release();
271
272 ESP_LOGD(TAG, "Invalidate JTAG result register. JTAG disabled.");
273
274 return ESP_OK;
275 }
276 #endif /* CONFIG_IDF_TARGET_ESP32S2*/
277