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