Skip to content

Commit 201bf9b

Browse files
committed
espressif: Replace esphmac with portable securekey module
Reworks PR adafruit#11319 per maintainer review (dhalbert): make the eFuse-bound HMAC API port-agnostic instead of an espressif-only module. - Remove esphmac / esphmac.HMACKey entirely (nothing merged depends on it). - Add securekey.HardwareKey in shared-bindings + shared-module: HardwareKey(key_slot) key_slot is port-defined .hmac_sha256(data) -> bytes PSA psa_mac_compute .verify_hmac_sha256(data, mac) PSA psa_mac_verify, constant time (new) .key_slot, .exportable (exportable = old read_protected, inverted) - Split follows os/hashlib: shared-module owns the PSA operations on a stored psa_key_id_t; the only per-port file is common-hal construct(), which maps the key slot to a psa_key_id_t. A second port (Zephyr's PSA build included) only needs that shim. - espressif construct() imports an esp_hmac_opaque_key_t reference through ESP-IDF's vendored PSA opaque-key driver (built for every SOC_HMAC_SUPPORTED chip), caching one import per eFuse block so repeated construction does not leak PSA key slots. Still fail-closed on the HMAC_UP eFuse purpose; still no raw-key read and no burn/write. - CIRCUITPY_SECUREKEY: default off, on for espressif HMAC-capable chips (off for esp32 / esp32c2 / esp32c61). Drop the CIRCUITPY_ESPHMAC wiring and the esphmac SRC block in ports/espressif/Makefile. - Update locale/circuitpython.pot.
1 parent de9fe04 commit 201bf9b

17 files changed

Lines changed: 392 additions & 234 deletions

File tree

locale/circuitpython.pot

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,15 +1459,15 @@ msgstr ""
14591459
msgid "invalid setting"
14601460
msgstr ""
14611461

1462-
#: ports/espressif/common-hal/esphmac/HMACKey.c
1463-
msgid "key_block must be 0-5"
1462+
#: ports/espressif/common-hal/securekey/HardwareKey.c
1463+
msgid "key_slot is not configured for HMAC use"
14641464
msgstr ""
14651465

1466-
#: ports/espressif/common-hal/esphmac/HMACKey.c
1467-
msgid "key_block is not configured for HMAC use"
1466+
#: ports/espressif/common-hal/securekey/HardwareKey.c
1467+
msgid "crypto init failed"
14681468
msgstr ""
14691469

1470-
#: ports/espressif/common-hal/esphmac/HMACKey.c
1470+
#: shared-module/securekey/HardwareKey.c
14711471
msgid "HMAC calculation failed"
14721472
msgstr ""
14731473

ports/espressif/Makefile

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -726,13 +726,6 @@ SRC_ESPIDF := \
726726
SRC_C += $(SRC_ESPIDF)
727727
endif
728728

729-
ifneq ($(CIRCUITPY_ESPHMAC),0)
730-
SRC_ESPHMAC := \
731-
$(wildcard common-hal/esphmac/*.c) \
732-
$(wildcard bindings/esphmac/*.c)
733-
SRC_C += $(SRC_ESPHMAC)
734-
endif
735-
736729
ifneq ($(CIRCUITPY_ESPNOW),0)
737730
SRC_ESPNOW := \
738731
$(wildcard common-hal/espnow/*.c) \

ports/espressif/bindings/esphmac/HMACKey.c

Lines changed: 0 additions & 107 deletions
This file was deleted.

ports/espressif/bindings/esphmac/__init__.c

Lines changed: 0 additions & 33 deletions
This file was deleted.

ports/espressif/common-hal/esphmac/HMACKey.c

Lines changed: 0 additions & 46 deletions
This file was deleted.

ports/espressif/common-hal/esphmac/HMACKey.h

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// This file is part of the CircuitPython project: https://circuitpython.org
2+
//
3+
// SPDX-FileCopyrightText: Copyright (c) 2026 Mike Mabey
4+
//
5+
// SPDX-License-Identifier: MIT
6+
7+
// The only port-specific step: turn a hardware key slot (here, an eFuse key
8+
// block index) into a PSA key id. Everything after that -- hmac_sha256(),
9+
// verify_hmac_sha256() -- lives in shared-module/securekey/HardwareKey.c.
10+
11+
#include "shared-module/securekey/HardwareKey.h"
12+
13+
#include "py/runtime.h"
14+
15+
#include "esp_efuse.h"
16+
17+
// Pulls in MBEDTLS_CONFIG_FILE (esp_config.h), which is what defines
18+
// ESP_HMAC_OPAQUE_DRIVER_ENABLED on HMAC-capable chips. Including only
19+
// <psa/crypto.h> goes through the tf-psa-crypto config path and does NOT
20+
// define it, so the opaque-driver header below would compile to nothing.
21+
#include "mbedtls/build_info.h"
22+
#include "psa/crypto.h"
23+
// Public header of the ESP-IDF mbedtls component's PSA opaque-key driver for
24+
// eFuse HMAC keys (components/mbedtls/port/psa_driver/include/).
25+
#include "psa_crypto_driver_esp_hmac_opaque.h"
26+
27+
#if !defined(ESP_HMAC_OPAQUE_DRIVER_ENABLED)
28+
#error "securekey requires the ESP-IDF PSA opaque HMAC driver (SOC_HMAC_SUPPORTED targets only)"
29+
#endif
30+
31+
// ESP32-S3 has BLOCK_KEY0..BLOCK_KEY5; other HMAC-capable chips match. Python
32+
// key_slot 0-5 maps to EFUSE_BLK_KEY0 + key_slot.
33+
#define EFUSE_KEY_BLOCK_COUNT 6
34+
35+
// The ESP HMAC peripheral consumes a 256-bit eFuse key.
36+
#define HMAC_KEY_BITS 256
37+
38+
// One PSA key is imported per eFuse block on first use and reused thereafter, so
39+
// repeated HardwareKey() construction does not accumulate PSA key slots. The
40+
// keys are volatile references (no key material); at most EFUSE_KEY_BLOCK_COUNT
41+
// are ever imported. On espressif this cache is safe across a CircuitPython soft
42+
// reset because ESP-IDF initializes PSA once at boot and never frees it (see the
43+
// raspberrypi port's reset path for the contrasting case).
44+
static psa_key_id_t imported_key[EFUSE_KEY_BLOCK_COUNT];
45+
46+
void common_hal_securekey_hardwarekey_construct(securekey_hardwarekey_obj_t *self, mp_int_t key_slot) {
47+
if (key_slot < 0 || key_slot >= EFUSE_KEY_BLOCK_COUNT) {
48+
mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %d-%d"),
49+
MP_QSTR_key_slot, 0, EFUSE_KEY_BLOCK_COUNT - 1);
50+
}
51+
52+
esp_efuse_block_t block = (esp_efuse_block_t)(EFUSE_BLK_KEY0 + key_slot);
53+
if (esp_efuse_get_key_purpose(block) != ESP_EFUSE_KEY_PURPOSE_HMAC_UP) {
54+
mp_raise_ValueError(MP_ERROR_TEXT("key_slot is not configured for HMAC use"));
55+
}
56+
57+
if (imported_key[key_slot] == 0) {
58+
// PSA is already initialized by ssl / hashlib, but psa_crypto_init() is
59+
// idempotent and this keeps securekey usable on its own.
60+
if (psa_crypto_init() != PSA_SUCCESS) {
61+
mp_raise_RuntimeError(MP_ERROR_TEXT("crypto init failed"));
62+
}
63+
64+
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
65+
psa_set_key_type(&attr, PSA_KEY_TYPE_HMAC);
66+
psa_set_key_bits(&attr, HMAC_KEY_BITS);
67+
psa_set_key_algorithm(&attr, PSA_ALG_HMAC(PSA_ALG_SHA_256));
68+
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE);
69+
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_ESP_HMAC_VOLATILE);
70+
71+
// Import data is a *reference* to the eFuse block, not key material. The
72+
// driver independently re-checks the HMAC_UP purpose and refuses
73+
// anything else.
74+
esp_hmac_opaque_key_t keyref = { .efuse_key_id = (uint8_t)key_slot };
75+
76+
psa_key_id_t key_id = 0;
77+
psa_status_t status = psa_import_key(&attr, (const uint8_t *)&keyref, sizeof(keyref), &key_id);
78+
if (status != PSA_SUCCESS) {
79+
mp_raise_ValueError(MP_ERROR_TEXT("key_slot is not configured for HMAC use"));
80+
}
81+
imported_key[key_slot] = key_id;
82+
}
83+
84+
self->key_id = imported_key[key_slot];
85+
self->key_slot = key_slot;
86+
self->exportable = !esp_efuse_get_key_dis_read(block);
87+
}

ports/espressif/bindings/esphmac/HMACKey.h renamed to ports/espressif/common-hal/securekey/__init__.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,5 @@
44
//
55
// SPDX-License-Identifier: MIT
66

7-
#pragma once
8-
9-
#include "py/obj.h"
10-
11-
extern const mp_obj_type_t esphmac_hmackey_type;
7+
// No securekey module-level functions. The port-specific code is the
8+
// HardwareKey constructor in HardwareKey.c.

ports/espressif/mpconfigport.mk

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ CIRCUITPY_BLEIO_NATIVE ?= 1
7676
CIRCUITPY_CANIO ?= 1
7777
CIRCUITPY_COUNTIO ?= 1
7878
CIRCUITPY_ESPCAMERA ?= 1
79-
CIRCUITPY_ESPHMAC ?= 1
8079
CIRCUITPY_ESPIDF ?= 1
8180
CIRCUITPY_ESPULP ?= 1
8281
CIRCUITPY_FRAMEBUFFERIO ?= 1
@@ -92,6 +91,7 @@ CIRCUITPY_PS2IO ?= 1
9291
CIRCUITPY_RGBMATRIX ?= 1
9392
CIRCUITPY_ROTARYIO ?= 1
9493
CIRCUITPY_SDIOIO ?= 1
94+
CIRCUITPY_SECUREKEY ?= 1
9595
CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY ?= 1
9696
CIRCUITPY_SYNTHIO_MAX_CHANNELS ?= 12
9797
CIRCUITPY_TOUCHIO ?= 1
@@ -107,7 +107,7 @@ ifeq ($(IDF_TARGET),esp32)
107107
CIRCUITPY_RGBMATRIX = 0
108108

109109
# No HMAC peripheral (introduced starting with ESP32-S2)
110-
CIRCUITPY_ESPHMAC = 0
110+
CIRCUITPY_SECUREKEY = 0
111111

112112
# Has no USB
113113
CIRCUITPY_USB_DEVICE = 0
@@ -123,7 +123,7 @@ CIRCUITPY_ESPULP = 0
123123
CIRCUITPY_MEMORYMAP = 0
124124

125125
# No HMAC peripheral (SOC_HMAC_SUPPORTED is not defined for this target)
126-
CIRCUITPY_ESPHMAC = 0
126+
CIRCUITPY_SECUREKEY = 0
127127

128128
# No capacitive touch peripheral
129129
CIRCUITPY_ALARM_TOUCH = 0
@@ -236,7 +236,7 @@ CIRCUITPY_MEMORYMAP = 0
236236
CIRCUITPY_RGBMATRIX = 0
237237

238238
# No HMAC peripheral (SOC_HMAC_SUPPORTED is not defined for this target)
239-
CIRCUITPY_ESPHMAC = 0
239+
CIRCUITPY_SECUREKEY = 0
240240

241241
# No capacitive touch peripheral
242242
CIRCUITPY_ALARM_TOUCH = 0

0 commit comments

Comments
 (0)