Without atecc608b

This commit is contained in:
2026-04-17 18:56:01 +02:00
parent ab00a59f29
commit 955390c358
8 changed files with 229 additions and 52 deletions

View File

@@ -1,14 +1,4 @@
dependencies: dependencies:
esp-cryptoauthlib:
component_hash: 8d24c37df1e906f9bbee9a869ca86f263ab135179d1c2ba6062448269437b192
dependencies:
- name: idf
version: '>=4.3'
source:
git: https://github.com/espressif/esp-cryptoauthlib.git
path: .
type: git
version: d9792119ebaec0c54839e6605acd3f11dd937205
idf: idf:
source: source:
type: idf type: idf
@@ -22,9 +12,8 @@ dependencies:
type: git type: git
version: 378f0bd47900bffacbf29cac328c6e9b5391c886 version: 378f0bd47900bffacbf29cac328c6e9b5391c886
direct_dependencies: direct_dependencies:
- esp-cryptoauthlib
- idf - idf
- libssh2_esp - libssh2_esp
manifest_hash: a6766e71931c845fac37dab1b735cded43d414aa83e5ce0443ba4285e1980180 manifest_hash: 5bd8c5fcff9e9561e27fb051d1db66506755c66524ba9099cb66598d979940e3
target: esp32c6 target: esp32c5
version: 2.0.0 version: 2.0.0

View File

@@ -1,6 +1,6 @@
idf_component_register(SRCS "main.c" idf_component_register(SRCS "main.c"
"wifi.c" "wifi.c"
"atecc608a.c" "efuse_ecdsa.c"
"ssh_client.c" "ssh_client.c"
INCLUDE_DIRS "." INCLUDE_DIRS "."
REQUIRES mbedtls esp-cryptoauthlib esp_wifi nvs_flash driver) REQUIRES mbedtls efuse esp_wifi nvs_flash driver esp_hw_support)

125
main/efuse_ecdsa.c Normal file
View File

@@ -0,0 +1,125 @@
#include "efuse_ecdsa.h"
#include "sdkconfig.h"
#include <esp_efuse.h>
#include <esp_log.h>
#include <mbedtls/ecdsa.h>
#include <mbedtls/ecp.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "ecdsa/ecdsa_alt.h"
static const char *TAG = "efuse_ecdsa";
/* We use EFUSE_BLK_KEY1 (block 5) for the ECDSA key, leaving KEY0 free
* for secure boot / TEE keys. */
#define ECDSA_EFUSE_BLOCK EFUSE_BLK_KEY1
/* -------------------------------------------------------------------------
* Public API
* ---------------------------------------------------------------------- */
bool efuse_ecdsa_key_provisioned(void)
{
esp_efuse_block_t blk;
return esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY, &blk);
}
bool efuse_ecdsa_provision_key(const uint8_t key[32])
{
if (efuse_ecdsa_key_provisioned()) {
ESP_LOGW(TAG, "ECDSA key already provisioned skipping");
return true;
}
if (!esp_efuse_key_block_unused(ECDSA_EFUSE_BLOCK)) {
ESP_LOGE(TAG, "eFuse key block %d is already in use", ECDSA_EFUSE_BLOCK);
return false;
}
esp_err_t err = esp_efuse_write_key(ECDSA_EFUSE_BLOCK,
ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY,
key, 32);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_efuse_write_key failed: %s", esp_err_to_name(err));
return false;
}
ESP_LOGI(TAG, "ECDSA P-256 key burned into eFuse block %d", ECDSA_EFUSE_BLOCK);
return true;
}
bool efuse_ecdsa_get_pubkey(uint8_t pub_x[32], uint8_t pub_y[32])
{
esp_efuse_block_t blk;
if (!esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY, &blk)) {
ESP_LOGE(TAG, "No eFuse block with ECDSA_KEY purpose found");
return false;
}
esp_ecdsa_pk_conf_t conf = {
.grp_id = MBEDTLS_ECP_DP_SECP256R1,
.efuse_block = blk,
.load_pubkey = true,
};
mbedtls_pk_context pk;
if (esp_ecdsa_set_pk_context(&pk, &conf) != 0) {
ESP_LOGE(TAG, "esp_ecdsa_set_pk_context failed");
return false;
}
mbedtls_ecp_keypair *kp = mbedtls_pk_ec(pk);
int ret = 0;
ret |= mbedtls_mpi_write_binary(&kp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), pub_x, 32);
ret |= mbedtls_mpi_write_binary(&kp->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), pub_y, 32);
mbedtls_pk_free(&pk);
if (ret != 0) {
ESP_LOGE(TAG, "Failed to export public key coordinates");
return false;
}
return true;
}
bool efuse_ecdsa_sign(const uint8_t digest[32],
uint8_t r_out[32], uint8_t s_out[32])
{
esp_efuse_block_t blk;
if (!esp_efuse_find_purpose(ESP_EFUSE_KEY_PURPOSE_ECDSA_KEY, &blk)) {
ESP_LOGE(TAG, "No eFuse block with ECDSA_KEY purpose found");
return false;
}
mbedtls_ecdsa_context ctx;
mbedtls_ecdsa_init(&ctx);
mbedtls_ecp_group_load(&ctx.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1);
mbedtls_mpi key_mpi;
esp_ecdsa_privkey_load_mpi(&key_mpi, blk);
mbedtls_mpi r, s;
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
int ret = mbedtls_ecdsa_sign(&ctx.MBEDTLS_PRIVATE(grp),
&r, &s, &key_mpi,
digest, 32, NULL, NULL);
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_ecdsa_sign failed: -0x%04X", (unsigned)-ret);
goto out;
}
ret |= mbedtls_mpi_write_binary(&r, r_out, 32);
ret |= mbedtls_mpi_write_binary(&s, s_out, 32);
out:
mbedtls_mpi_free(&r);
mbedtls_mpi_free(&s);
mbedtls_mpi_free(&key_mpi);
mbedtls_ecdsa_free(&ctx);
return ret == 0;
}

48
main/efuse_ecdsa.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef EFUSE_ECDSA_H
#define EFUSE_ECDSA_H
#include <stdbool.h>
#include <stdint.h>
/**
* Check whether an ECDSA P-256 key has already been provisioned in eFuse.
*/
bool efuse_ecdsa_key_provisioned(void);
/**
* Write a 32-byte ECDSA P-256 private key into the eFuse key block.
* The key must be in **little-endian** byte order (as required by the
* ESP32-C5 ECDSA peripheral).
*
* This is a ONE-TIME, IRREVERSIBLE operation. After burning, the key
* block is read-protected so software can never read the private key
* back — only the hardware ECDSA peripheral can use it.
*
* @param key 32 bytes of private-key material (little-endian).
* @return true on success.
*/
bool efuse_ecdsa_provision_key(const uint8_t key[32]);
/**
* Export the public key that corresponds to the eFuse private key.
* Uses the hardware ECDSA peripheral to derive Q = d·G without ever
* exposing the private key to software.
*
* @param pub_x Output buffer for the X coordinate (32 bytes, big-endian).
* @param pub_y Output buffer for the Y coordinate (32 bytes, big-endian).
* @return true on success.
*/
bool efuse_ecdsa_get_pubkey(uint8_t pub_x[32], uint8_t pub_y[32]);
/**
* Sign a SHA-256 digest with the eFuse ECDSA key.
*
* @param digest 32-byte SHA-256 hash to sign.
* @param r_out Output: R component of the signature (32 bytes, big-endian).
* @param s_out Output: S component of the signature (32 bytes, big-endian).
* @return true on success.
*/
bool efuse_ecdsa_sign(const uint8_t digest[32],
uint8_t r_out[32], uint8_t s_out[32]);
#endif /* EFUSE_ECDSA_H */

View File

@@ -1,8 +1,6 @@
--- ---
dependencies: dependencies:
idf: idf:
version: ">=4.1.0" version: ">=5.5.0"
esp-cryptoauthlib:
git: https://github.com/espressif/esp-cryptoauthlib.git
libssh2_esp: libssh2_esp:
git: https://github.com/skuodi/libssh2_esp.git git: https://github.com/skuodi/libssh2_esp.git

View File

@@ -2,9 +2,15 @@
* keypitecc door controller * keypitecc door controller
* *
* Hardware: * Hardware:
* ESP32-C5 with ECDSA key stored in eFuse
* GPIO 9 Boot button (active-low, press = GND) * GPIO 9 Boot button (active-low, press = GND)
* GPIO 15 User LED (active-high, 1 = on) * GPIO 15 User LED (active-high, 1 = on)
* *
* Provisioning:
* On first boot, if no ECDSA key is present in eFuse, a random P-256
* private key is generated and burned. The derived public key is
* printed to the console each boot in SSH authorized_keys format.
*
* Button state machine (5-second window): * Button state machine (5-second window):
* IDLE * IDLE
* └─ press ──► PENDING_OPEN (slow blink, will run SSH open command) * └─ press ──► PENDING_OPEN (slow blink, will run SSH open command)
@@ -17,13 +23,14 @@
* WiFi disconnected → LED OFF * WiFi disconnected → LED OFF
*/ */
#include "atecc608a.h" #include "efuse_ecdsa.h"
#include "ssh_client.h" #include "ssh_client.h"
#include "wifi.h" #include "wifi.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include <driver/gpio.h> #include <driver/gpio.h>
#include <esp_log.h> #include <esp_log.h>
#include <esp_random.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/queue.h> #include <freertos/queue.h>
#include <freertos/task.h> #include <freertos/task.h>
@@ -251,12 +258,22 @@ void app_main(void)
} }
ESP_ERROR_CHECK(ret); ESP_ERROR_CHECK(ret);
/* ATECC608B */ /* eFuse ECDSA key provisioning / public key export */
if (!atecc608B_init()) { if (!efuse_ecdsa_key_provisioned()) {
ESP_LOGW(TAG, "ATECC608B init failed SSH authentication will not work"); ESP_LOGW(TAG, "No ECDSA key in eFuse generating and burning a new key");
uint8_t privkey[32];
esp_fill_random(privkey, sizeof(privkey));
if (!efuse_ecdsa_provision_key(privkey)) {
ESP_LOGE(TAG, "Key provisioning FAILED SSH authentication will not work");
}
/* Wipe the RAM copy immediately */
memset(privkey, 0, sizeof(privkey));
}
if (efuse_ecdsa_key_provisioned()) {
ssh_print_public_key();
} else { } else {
atecc608B_print_config(); ESP_LOGW(TAG, "ECDSA key not available SSH authentication will not work");
ssh_print_public_key(); /* print key for authorized_keys setup */
} }
/* WiFi */ /* WiFi */

View File

@@ -1,6 +1,6 @@
#include "ssh_client.h" #include "ssh_client.h"
#include "cryptoauthlib.h" #include "efuse_ecdsa.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <esp_log.h> #include <esp_log.h>
@@ -67,10 +67,12 @@ static uint32_t write_mpint(uint8_t *buf, const uint8_t *val, uint32_t size)
* [uint32 8] "nistp256" * [uint32 8] "nistp256"
* [uint32 65] 0x04 || X(32) || Y(32) * [uint32 65] 0x04 || X(32) || Y(32)
* *
* @param atecc_pubkey 64-byte raw public key (X||Y) from ATECC608B. * @param pub_x 32-byte X coordinate (big-endian).
* @param pub_y 32-byte Y coordinate (big-endian).
* @param out_blob Must point to a buffer of at least 104 bytes. * @param out_blob Must point to a buffer of at least 104 bytes.
*/ */
static void build_pubkey_blob(const uint8_t *atecc_pubkey, uint8_t *out_blob) static void build_pubkey_blob(const uint8_t *pub_x, const uint8_t *pub_y,
uint8_t *out_blob)
{ {
uint32_t off = 0; uint32_t off = 0;
@@ -87,12 +89,13 @@ static void build_pubkey_blob(const uint8_t *atecc_pubkey, uint8_t *out_blob)
/* Uncompressed EC point: 0x04 || X || Y */ /* Uncompressed EC point: 0x04 || X || Y */
write_be32(&out_blob[off], 65); off += 4; write_be32(&out_blob[off], 65); off += 4;
out_blob[off++] = 0x04; out_blob[off++] = 0x04;
memcpy(&out_blob[off], atecc_pubkey, 64); memcpy(&out_blob[off], pub_x, 32); off += 32;
/* off += 64 — total = 104 */ memcpy(&out_blob[off], pub_y, 32);
/* off += 32 — total = 104 */
} }
/* ------------------------------------------------------------------------- /* -------------------------------------------------------------------------
* libssh2 signing callback (ATECC608B signs the hash) * libssh2 signing callback (eFuse ECDSA signs the hash)
* ---------------------------------------------------------------------- */ * ---------------------------------------------------------------------- */
static int sign_callback(LIBSSH2_SESSION *session, static int sign_callback(LIBSSH2_SESSION *session,
unsigned char **sig, size_t *sig_len, unsigned char **sig, size_t *sig_len,
@@ -103,14 +106,14 @@ static int sign_callback(LIBSSH2_SESSION *session,
(void)abstract; (void)abstract;
uint8_t digest[32]; uint8_t digest[32];
uint8_t raw_sig[64]; uint8_t r_raw[32], s_raw[32];
/* Hash the challenge data */ /* Hash the challenge data */
mbedtls_sha256(data, data_len, digest, 0); mbedtls_sha256(data, data_len, digest, 0);
/* Sign with the ATECC608B hardware key in slot 0 */ /* Sign with the eFuse hardware ECDSA key */
if (atcab_sign(0, digest, raw_sig) != ATCA_SUCCESS) { if (!efuse_ecdsa_sign(digest, r_raw, s_raw)) {
ESP_LOGE(TAG, "ATECC608B signing failed!"); ESP_LOGE(TAG, "eFuse ECDSA signing failed!");
return -1; return -1;
} }
@@ -119,8 +122,8 @@ static int sign_callback(LIBSSH2_SESSION *session,
if (!buf) return -1; if (!buf) return -1;
uint32_t off = 0; uint32_t off = 0;
off += write_mpint(&buf[off], &raw_sig[0], 32); /* R */ off += write_mpint(&buf[off], r_raw, 32); /* R */
off += write_mpint(&buf[off], &raw_sig[32], 32); /* S */ off += write_mpint(&buf[off], s_raw, 32); /* S */
*sig = buf; *sig = buf;
*sig_len = off; *sig_len = off;
@@ -132,15 +135,14 @@ static int sign_callback(LIBSSH2_SESSION *session,
* ---------------------------------------------------------------------- */ * ---------------------------------------------------------------------- */
void ssh_print_public_key(void) void ssh_print_public_key(void)
{ {
uint8_t raw_key[64]; uint8_t pub_x[32], pub_y[32];
ATCA_STATUS st = atcab_get_pubkey(0, raw_key); if (!efuse_ecdsa_get_pubkey(pub_x, pub_y)) {
if (st != ATCA_SUCCESS) { ESP_LOGE(TAG, "Failed to export public key from eFuse");
ESP_LOGE(TAG, "atcab_get_pubkey failed: 0x%02X", st);
return; return;
} }
uint8_t blob[104]; uint8_t blob[104];
build_pubkey_blob(raw_key, blob); build_pubkey_blob(pub_x, pub_y, blob);
size_t b64_len = 0; size_t b64_len = 0;
mbedtls_base64_encode(NULL, 0, &b64_len, blob, sizeof(blob)); mbedtls_base64_encode(NULL, 0, &b64_len, blob, sizeof(blob));
@@ -165,14 +167,13 @@ bool ssh_execute_command(const char *cmd)
if (!cmd) return false; if (!cmd) return false;
/* --- Read public key blob ------------------------------------------ */ /* --- Read public key blob ------------------------------------------ */
uint8_t raw_key[64]; uint8_t pub_x[32], pub_y[32];
ATCA_STATUS st = atcab_get_pubkey(0, raw_key); if (!efuse_ecdsa_get_pubkey(pub_x, pub_y)) {
if (st != ATCA_SUCCESS) { ESP_LOGE(TAG, "Failed to export public key from eFuse");
ESP_LOGE(TAG, "atcab_get_pubkey failed: 0x%02X", st);
return false; return false;
} }
uint8_t pubkey_blob[104]; uint8_t pubkey_blob[104];
build_pubkey_blob(raw_key, pubkey_blob); build_pubkey_blob(pub_x, pub_y, pubkey_blob);
/* --- TCP connect ------------------------------------------------------- */ /* --- TCP connect ------------------------------------------------------- */
int rc; int rc;
@@ -227,7 +228,7 @@ bool ssh_execute_command(const char *cmd)
} }
ESP_LOGI(TAG, "SSH handshake OK"); ESP_LOGI(TAG, "SSH handshake OK");
/* --- Authenticate with ATECC608B hardware key ------------------------- */ /* --- Authenticate with eFuse ECDSA hardware key ---------------------- */
void *abstract = NULL; void *abstract = NULL;
rc = libssh2_userauth_publickey(session, CONFIG_SSH_USERNAME, rc = libssh2_userauth_publickey(session, CONFIG_SSH_USERNAME,
pubkey_blob, sizeof(pubkey_blob), pubkey_blob, sizeof(pubkey_blob),

View File

@@ -4,9 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
/** /**
* Read the ATECC608B public key, format it as an SSH authorized_keys entry, * Derive the public key from the eFuse ECDSA private key, format it as an
* and print it to the console. Call this once at boot so the key can be * SSH authorized_keys entry, and print it to the console.
* added to the server's authorized_keys file.
* *
* Output format: * Output format:
* ecdsa-sha2-nistp256 <base64-blob> keypitecc * ecdsa-sha2-nistp256 <base64-blob> keypitecc
@@ -15,7 +14,7 @@ void ssh_print_public_key(void);
/** /**
* Open a TCP connection to the configured SSH server, authenticate using the * Open a TCP connection to the configured SSH server, authenticate using the
* ATECC608B hardware key, execute @p cmd, log the output, and disconnect. * eFuse ECDSA hardware key, execute @p cmd, log the output, and disconnect.
* *
* @param cmd Shell command string to run on the remote host. * @param cmd Shell command string to run on the remote host.
* @return true on success, false on any error. * @return true on success, false on any error.