Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions locale/circuitpython.pot
Original file line number Diff line number Diff line change
Expand Up @@ -1481,6 +1481,7 @@ msgstr ""

#: ports/espressif/common-hal/espidf/__init__.c
#: ports/raspberrypi/common-hal/picogame/Display.c
#: shared-bindings/hardwarekey/HardwareKey.c
#: shared-bindings/picogame/__init__.c
msgid "Operation or feature not supported"
msgstr ""
Expand Down Expand Up @@ -1534,6 +1535,28 @@ msgstr ""
msgid "Only one %q can be set."
msgstr ""

#: ports/espressif/common-hal/hardwarekey/HardwareKey.c
msgid "ds_params has the wrong length"
msgstr ""

#: ports/espressif/common-hal/hardwarekey/HardwareKey.c
msgid ""
"This key already committed to a different algorithm; call load_ds_params() "
"again to use a different one"
msgstr ""

#: ports/espressif/common-hal/hardwarekey/HardwareKey.c
msgid "load_ds_params() has not been called on this key"
msgstr ""

#: ports/espressif/common-hal/hardwarekey/HardwareKey.c
msgid "ds_params is invalid for this key slot"
msgstr ""

#: ports/espressif/common-hal/hardwarekey/HardwareKey.c
msgid "Digital Signature peripheral not available on this chip"
msgstr ""

#: ports/espressif/common-hal/i2ctarget/I2CTarget.c
#: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c
msgid "Only one address is allowed"
Expand Down Expand Up @@ -3970,6 +3993,15 @@ msgstr ""
msgid "RemoteTransmissionRequests limited to 8 bytes"
msgstr ""

#: shared-bindings/crypto_primitives/__init__.c
#: shared-bindings/hardwarekey/HardwareKey.c
msgid "Only %q supported"
msgstr ""

#: shared-bindings/crypto_primitives/__init__.c
msgid "label is not supported"
msgstr ""

#: shared-bindings/digitalio/DigitalInOut.c
#: shared-bindings/i2cioexpander/IOPin.c
msgid "Cannot set value when direction is input."
Expand Down Expand Up @@ -4075,8 +4107,8 @@ msgstr ""
msgid "Unsupported hash algorithm"
msgstr ""

#: shared-bindings/hmac/__init__.c
msgid "hardware key slot is unused"
#: shared-bindings/hmac/__init__.c shared-bindings/ssl/SSLContext.c
msgid "key does not have the expected %q purpose"
msgstr ""

#: shared-bindings/i2cioexpander/IOExpander.c
Expand Down Expand Up @@ -4418,6 +4450,10 @@ msgstr ""
msgid "unsupported colorspace for GifWriter"
msgstr ""

#: shared-module/hardwarekey/HardwareKey.c
msgid "OAEP requires this build to enable TLS 1.3 support"
msgstr ""

#: shared-module/hmac/HMAC.c
msgid "key does not support this digest"
msgstr ""
Expand Down
6 changes: 5 additions & 1 deletion ports/espressif/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,11 @@ ifneq (,$(filter 1,$(CIRCUITPY_ENABLE_MPY_NATIVE) $(CIRCUITPY_LOAD_NATIVE)))
NATIVE_SDKCONFIG := ;esp-idf-config/sdkconfig-native.defaults
endif

SDKCONFIGS := esp-idf-config/sdkconfig.defaults;$(DEBUG_SDKCONFIG);$(FLASH_SIZE_SDKCONFIG);$(FLASH_MODE_SDKCONFIG);$(FLASH_SPEED_SDKCONFIG);$(PSRAM_SDKCONFIG);$(PSRAM_SIZE_SDKCONFIG);$(PSRAM_MODE_SDKCONFIG);$(PSRAM_SPEED_SDKCONFIG);$(BLE_SDKCONFIG)$(NATIVE_SDKCONFIG);$(TARGET_SDKCONFIG);boards/$(BOARD)/sdkconfig
ifeq ($(CIRCUITPY_HARDWAREKEY),1)
HARDWAREKEY_SDKCONFIG := ;esp-idf-config/sdkconfig-hardwarekey.defaults
endif

SDKCONFIGS := esp-idf-config/sdkconfig.defaults;$(DEBUG_SDKCONFIG);$(FLASH_SIZE_SDKCONFIG);$(FLASH_MODE_SDKCONFIG);$(FLASH_SPEED_SDKCONFIG);$(PSRAM_SDKCONFIG);$(PSRAM_SIZE_SDKCONFIG);$(PSRAM_MODE_SDKCONFIG);$(PSRAM_SPEED_SDKCONFIG);$(BLE_SDKCONFIG)$(NATIVE_SDKCONFIG)$(HARDWAREKEY_SDKCONFIG);$(TARGET_SDKCONFIG);boards/$(BOARD)/sdkconfig

# create the config headers
.PHONY: do-sdkconfig
Expand Down
146 changes: 141 additions & 5 deletions ports/espressif/common-hal/hardwarekey/HardwareKey.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,31 @@
// SPDX-License-Identifier: MIT

// The one port-specific step: turn an eFuse key block into a PSA key id.
// Everything after that -- hmac_sha256(), verify_hmac_sha256() -- lives in
// Everything after that -- hmac.new()'s use of the key id, sign() -- lives in
// shared-module/hardwarekey/HardwareKey.c.

#include <string.h>

#include "py/runtime.h"

#include "common-hal/hardwarekey/__init__.h"
#include "common-hal/hardwarekey/board.h"

#include "shared-module/hardwarekey/HardwareKey.h"

#include "esp_efuse.h"
#include "esp_heap_caps.h"

// board.h hardcodes the slot count (enum values can't be used in #if); make sure
// it still matches this chip's eFuse layout.
_Static_assert(HARDWAREKEY_EFUSE_SLOT_COUNT == EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY0,
"eFuse key block count changed; update common-hal/hardwarekey/board.h");

// Pulls in MBEDTLS_CONFIG_FILE (esp_config.h), which is what defines
// ESP_HMAC_OPAQUE_DRIVER_ENABLED on HMAC-capable chips. Including only
// <psa/crypto.h> goes through the tf-psa-crypto config path and does NOT
// define it, so the opaque-driver header below would compile to nothing.
// ESP_HMAC_OPAQUE_DRIVER_ENABLED / ESP_RSA_DS_DRIVER_ENABLED on chips that
// have those peripherals. Including only <psa/crypto.h> goes through the
// tf-psa-crypto config path and does NOT define either, so the opaque-driver
// headers below would compile to nothing.
#include "mbedtls/build_info.h"
#include "psa/crypto.h"
// Public header of the ESP-IDF mbedtls component's PSA opaque-key driver for
Expand All @@ -34,6 +40,17 @@ _Static_assert(HARDWAREKEY_EFUSE_SLOT_COUNT == EFUSE_BLK_KEY_MAX - EFUSE_BLK_KEY
#error "hardwarekey requires the ESP-IDF PSA opaque HMAC driver (SOC_HMAC_SUPPORTED targets only)"
#endif

// The Digital Signature peripheral driver, by contrast, is genuinely optional:
// CIRCUITPY_HARDWAREKEY is on for every HMAC-capable chip, but not every one
// of those also has SOC_DIG_SIGN_SUPPORTED. Where it's absent,
// ESP_RSA_DS_DRIVER_ENABLED is undefined and DS purpose is simply never
// reported by hardwarekey_efuse_slot_load() below -- no build-time #error.
#if defined(ESP_RSA_DS_DRIVER_ENABLED)
#include "esp_ds.h"
#include "psa_crypto_driver_esp_rsa_ds.h"
#include "psa_crypto_driver_esp_rsa_ds_contexts.h"
#endif

// The ESP HMAC peripheral consumes a 256-bit eFuse key.
#define HMAC_KEY_BITS 256

Expand Down Expand Up @@ -64,9 +81,25 @@ bool hardwarekey_efuse_slot_load(mp_int_t slot, hardwarekey_hardwarekey_obj_t *k
key->key_id = 0;
key->purpose = HARDWAREKEY_PURPOSE_UNUSED;
key->exportable = false;
key->rsa_key_bits = 0;

esp_efuse_block_t block = (esp_efuse_block_t)(EFUSE_BLK_KEY0 + slot);
if (esp_efuse_get_key_purpose(block) != ESP_EFUSE_KEY_PURPOSE_HMAC_UP) {
esp_efuse_purpose_t block_purpose = esp_efuse_get_key_purpose(block);

if (block_purpose == ESP_EFUSE_KEY_PURPOSE_HMAC_DOWN_DIGITAL_SIGNATURE) {
// No PSA import yet: unlike HMAC_UP, this purpose alone doesn't name a
// full key -- the caller still has to supply ds_params via
// load_ds_params(). Just record that the slot is provisioned for it.
#if defined(ESP_RSA_DS_DRIVER_ENABLED)
key->purpose = HARDWAREKEY_PURPOSE_DS;
key->exportable = !esp_efuse_get_key_dis_read(block);
return true;
#else
return false;
#endif
}

if (block_purpose != ESP_EFUSE_KEY_PURPOSE_HMAC_UP) {
return false;
}

Expand All @@ -86,3 +119,106 @@ bool hardwarekey_efuse_slot_load(mp_int_t slot, hardwarekey_hardwarekey_obj_t *k
key->exportable = !esp_efuse_get_key_dis_read(block);
return true;
}

#if defined(ESP_RSA_DS_DRIVER_ENABLED)

// Persistent (non-GC) storage for a DS slot's imported key. Allocated lazily
// on first load_ds_params() and reused (overwritten in place) on a later
// call for the same slot -- the PSA RSA-DS driver only supports volatile
// keys (IDF-15427), so there is no psa_destroy_key() to pair a replacement
// with; the old PSA key id is simply abandoned along with its one HMAC-key
// eFuse block's worth of state.
typedef struct {
esp_ds_data_t *data;
esp_ds_data_ctx_t *ctx;
esp_rsa_ds_opaque_key_t *opaque_key;
} ds_slot_cache_t;
static ds_slot_cache_t ds_slot_cache[HARDWAREKEY_EFUSE_SLOT_COUNT];

void common_hal_hardwarekey_hardwarekey_load_ds_params(hardwarekey_hardwarekey_obj_t *self,
const uint8_t *ds_params, size_t ds_params_len) {
if (ds_params_len != sizeof(esp_ds_data_t)) {
mp_raise_ValueError(MP_ERROR_TEXT("ds_params has the wrong length"));
}

ds_slot_cache_t *cache = &ds_slot_cache[self->key_slot];
if (cache->data == NULL) {
cache->data = heap_caps_malloc(sizeof(esp_ds_data_t), MALLOC_CAP_8BIT);
cache->ctx = heap_caps_malloc(sizeof(esp_ds_data_ctx_t), MALLOC_CAP_8BIT);
cache->opaque_key = heap_caps_malloc(sizeof(esp_rsa_ds_opaque_key_t), MALLOC_CAP_8BIT);
if (cache->data == NULL || cache->ctx == NULL || cache->opaque_key == NULL) {
m_malloc_fail(sizeof(esp_ds_data_t));
}
}
memcpy(cache->data, ds_params, sizeof(esp_ds_data_t));

// rsa_length is stored as (bits / 32) - 1 (see esp_digital_signature_length_t).
mp_int_t rsa_bits = ((mp_int_t)cache->data->rsa_length + 1) * 32;

*cache->ctx = (esp_ds_data_ctx_t) {
.esp_ds_data = cache->data,
.efuse_key_id = (uint8_t)self->key_slot,
.rsa_length_bits = (uint16_t)rsa_bits,
};
*cache->opaque_key = (esp_rsa_ds_opaque_key_t) {
.ds_data_ctx = cache->ctx,
};

// The actual PSA import is deferred to ensure_algorithm(), on the first
// sign()/decrypt() call -- see its declaration in shared-module for why.
self->key_id = 0;
self->committed_alg = PSA_ALG_NONE;
self->rsa_key_bits = rsa_bits;
}

void common_hal_hardwarekey_hardwarekey_ensure_algorithm(hardwarekey_hardwarekey_obj_t *self,
psa_algorithm_t alg, psa_key_usage_t usage) {
if (self->key_id != 0) {
if (self->committed_alg != alg) {
mp_raise_ValueError(MP_ERROR_TEXT(
"This key already committed to a different algorithm; call load_ds_params() again to use a different one"));
}
return;
}

ds_slot_cache_t *cache = &ds_slot_cache[self->key_slot];
if (cache->opaque_key == NULL) {
mp_raise_ValueError(MP_ERROR_TEXT("load_ds_params() has not been called on this key"));
}

psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
psa_set_key_type(&attr, PSA_KEY_TYPE_RSA_KEY_PAIR);
psa_set_key_bits(&attr, self->rsa_key_bits);
psa_set_key_usage_flags(&attr, usage);
psa_set_key_algorithm(&attr, alg);
psa_set_key_lifetime(&attr, PSA_KEY_LIFETIME_ESP_RSA_DS_VOLATILE);

psa_key_id_t key_id = 0;
psa_status_t status = psa_import_key(&attr,
(const uint8_t *)cache->opaque_key, sizeof(*cache->opaque_key), &key_id);
if (status != PSA_SUCCESS) {
mp_raise_ValueError(MP_ERROR_TEXT("ds_params is invalid for this key slot"));
}

self->key_id = key_id;
self->committed_alg = alg;
}

#else

void common_hal_hardwarekey_hardwarekey_load_ds_params(hardwarekey_hardwarekey_obj_t *self,
const uint8_t *ds_params, size_t ds_params_len) {
// Unreachable in practice: a Digital Signature purpose is never reported
// by hardwarekey_efuse_slot_load() on a chip without the driver, and
// shared-bindings checks `purpose` before calling this. Kept as a body
// (not a build error) so this file still compiles on those chips.
mp_raise_NotImplementedError(MP_ERROR_TEXT("Digital Signature peripheral not available on this chip"));
}

void common_hal_hardwarekey_hardwarekey_ensure_algorithm(hardwarekey_hardwarekey_obj_t *self,
psa_algorithm_t alg, psa_key_usage_t usage) {
// Also unreachable: load_ds_params() above always raises first.
mp_raise_NotImplementedError(MP_ERROR_TEXT("Digital Signature peripheral not available on this chip"));
}

#endif
14 changes: 14 additions & 0 deletions ports/espressif/esp-idf-config/sdkconfig-hardwarekey.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Applied only when CIRCUITPY_HARDWAREKEY=1 (see ports/espressif/Makefile).
#
# A DS-purpose hardwarekey.HardwareKey signs through the ESP32-S2/S3 (and
# C3/C6/H2/P4) Digital Signature peripheral by way of ESP-IDF's PSA opaque
# RSA-DS driver. That driver is only compiled in when this option is set; it
# defaults to "n" upstream. On chips without SOC_DIG_SIGN_SUPPORTED the option
# has an unmet dependency and is ignored.
CONFIG_MBEDTLS_HARDWARE_RSA_DS_PERIPHERAL=y

# The PSA RSA-DS driver needs RSA + PKCS#1 v1.5 signing wanted in the PSA build.
# Both are ESP-IDF defaults (TLS needs them), set here so a slimmed-down board
# config can't silently drop the DS peripheral.
CONFIG_MBEDTLS_RSA_C=y
CONFIG_MBEDTLS_PKCS1_V15=y
4 changes: 4 additions & 0 deletions ports/espressif/mpconfigport.mk
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ CIRCUITPY_ESPULP ?= 1
CIRCUITPY_FRAMEBUFFERIO ?= 1
CIRCUITPY_FREQUENCYIO ?= 1
CIRCUITPY_HARDWAREKEY ?= 1
CIRCUITPY_CRYPTO_PRIMITIVES ?= 1
CIRCUITPY_HASHLIB ?= 1
CIRCUITPY_I2CTARGET = 0
CIRCUITPY_MAX3421E ?= 1
Expand Down Expand Up @@ -111,6 +112,7 @@ CIRCUITPY_RGBMATRIX = 0

# No HMAC peripheral (introduced starting with ESP32-S2)
CIRCUITPY_HARDWAREKEY = 0
CIRCUITPY_CRYPTO_PRIMITIVES = 0

# Has no USB
CIRCUITPY_USB_DEVICE = 0
Expand All @@ -127,6 +129,7 @@ CIRCUITPY_MEMORYMAP = 0

# No HMAC peripheral (SOC_HMAC_SUPPORTED is not defined for this target)
CIRCUITPY_HARDWAREKEY = 0
CIRCUITPY_CRYPTO_PRIMITIVES = 0

# No capacitive touch peripheral
CIRCUITPY_ALARM_TOUCH = 0
Expand Down Expand Up @@ -271,6 +274,7 @@ CIRCUITPY_RGBMATRIX = 0

# No HMAC peripheral (SOC_HMAC_SUPPORTED is not defined for this target)
CIRCUITPY_HARDWAREKEY = 0
CIRCUITPY_CRYPTO_PRIMITIVES = 0

# No capacitive touch peripheral
CIRCUITPY_ALARM_TOUCH = 0
Expand Down
4 changes: 4 additions & 0 deletions py/circuitpy_defns.mk
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ endif
ifeq ($(CIRCUITPY_CODEOP),1)
SRC_PATTERNS += codeop/%
endif
ifeq ($(CIRCUITPY_CRYPTO_PRIMITIVES),1)
SRC_PATTERNS += crypto_primitives/%
endif
ifeq ($(CIRCUITPY_COUNTIO),1)
SRC_PATTERNS += countio/%
endif
Expand Down Expand Up @@ -669,6 +672,7 @@ $(filter $(SRC_PATTERNS), \
canio/Match.c \
codeop/__init__.c \
countio/Edge.c \
crypto_primitives/__init__.c \
digitalio/DigitalInOutProtocol.c \
digitalio/Direction.c \
digitalio/DriveMode.c \
Expand Down
3 changes: 3 additions & 0 deletions py/circuitpy_mpconfig.mk
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@ CFLAGS += -DCIRCUITPY_COLLECTIONS=$(CIRCUITPY_COLLECTIONS)
CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 0
CFLAGS += -DCIRCUITPY_COMPUTED_GOTO_SAVE_SPACE=$(CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE)

CIRCUITPY_CRYPTO_PRIMITIVES ?= 0
CFLAGS += -DCIRCUITPY_CRYPTO_PRIMITIVES=$(CIRCUITPY_CRYPTO_PRIMITIVES)

CIRCUITPY_CYW43 ?= 0
CFLAGS += -DCIRCUITPY_CYW43=$(CIRCUITPY_CYW43)

Expand Down
Loading
Loading