Without atecc608b
This commit is contained in:
@@ -1,14 +1,4 @@
|
||||
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:
|
||||
source:
|
||||
type: idf
|
||||
@@ -22,9 +12,8 @@ dependencies:
|
||||
type: git
|
||||
version: 378f0bd47900bffacbf29cac328c6e9b5391c886
|
||||
direct_dependencies:
|
||||
- esp-cryptoauthlib
|
||||
- idf
|
||||
- libssh2_esp
|
||||
manifest_hash: a6766e71931c845fac37dab1b735cded43d414aa83e5ce0443ba4285e1980180
|
||||
target: esp32c6
|
||||
manifest_hash: 5bd8c5fcff9e9561e27fb051d1db66506755c66524ba9099cb66598d979940e3
|
||||
target: esp32c5
|
||||
version: 2.0.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
"wifi.c"
|
||||
"atecc608a.c"
|
||||
"efuse_ecdsa.c"
|
||||
"ssh_client.c"
|
||||
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
125
main/efuse_ecdsa.c
Normal 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
48
main/efuse_ecdsa.h
Normal 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 */
|
||||
@@ -1,8 +1,6 @@
|
||||
---
|
||||
dependencies:
|
||||
idf:
|
||||
version: ">=4.1.0"
|
||||
esp-cryptoauthlib:
|
||||
git: https://github.com/espressif/esp-cryptoauthlib.git
|
||||
version: ">=5.5.0"
|
||||
libssh2_esp:
|
||||
git: https://github.com/skuodi/libssh2_esp.git
|
||||
|
||||
29
main/main.c
29
main/main.c
@@ -2,9 +2,15 @@
|
||||
* keypitecc – door controller
|
||||
*
|
||||
* Hardware:
|
||||
* ESP32-C5 with ECDSA key stored in eFuse
|
||||
* GPIO 9 – Boot button (active-low, press = GND)
|
||||
* 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):
|
||||
* IDLE
|
||||
* └─ press ──► PENDING_OPEN (slow blink, will run SSH open command)
|
||||
@@ -17,13 +23,14 @@
|
||||
* WiFi disconnected → LED OFF
|
||||
*/
|
||||
|
||||
#include "atecc608a.h"
|
||||
#include "efuse_ecdsa.h"
|
||||
#include "ssh_client.h"
|
||||
#include "wifi.h"
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include <driver/gpio.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_random.h>
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
#include <freertos/task.h>
|
||||
@@ -251,12 +258,22 @@ void app_main(void)
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
/* ATECC608B */
|
||||
if (!atecc608B_init()) {
|
||||
ESP_LOGW(TAG, "ATECC608B init failed – SSH authentication will not work");
|
||||
/* eFuse ECDSA key provisioning / public key export */
|
||||
if (!efuse_ecdsa_key_provisioned()) {
|
||||
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 {
|
||||
atecc608B_print_config();
|
||||
ssh_print_public_key(); /* print key for authorized_keys setup */
|
||||
ESP_LOGW(TAG, "ECDSA key not available – SSH authentication will not work");
|
||||
}
|
||||
|
||||
/* WiFi */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ssh_client.h"
|
||||
|
||||
#include "cryptoauthlib.h"
|
||||
#include "efuse_ecdsa.h"
|
||||
#include "sdkconfig.h"
|
||||
#include <arpa/inet.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 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.
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -87,12 +89,13 @@ static void build_pubkey_blob(const uint8_t *atecc_pubkey, uint8_t *out_blob)
|
||||
/* Uncompressed EC point: 0x04 || X || Y */
|
||||
write_be32(&out_blob[off], 65); off += 4;
|
||||
out_blob[off++] = 0x04;
|
||||
memcpy(&out_blob[off], atecc_pubkey, 64);
|
||||
/* off += 64 — total = 104 */
|
||||
memcpy(&out_blob[off], pub_x, 32); off += 32;
|
||||
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,
|
||||
unsigned char **sig, size_t *sig_len,
|
||||
@@ -103,14 +106,14 @@ static int sign_callback(LIBSSH2_SESSION *session,
|
||||
(void)abstract;
|
||||
|
||||
uint8_t digest[32];
|
||||
uint8_t raw_sig[64];
|
||||
uint8_t r_raw[32], s_raw[32];
|
||||
|
||||
/* Hash the challenge data */
|
||||
mbedtls_sha256(data, data_len, digest, 0);
|
||||
|
||||
/* Sign with the ATECC608B hardware key in slot 0 */
|
||||
if (atcab_sign(0, digest, raw_sig) != ATCA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "ATECC608B signing failed!");
|
||||
/* Sign with the eFuse hardware ECDSA key */
|
||||
if (!efuse_ecdsa_sign(digest, r_raw, s_raw)) {
|
||||
ESP_LOGE(TAG, "eFuse ECDSA signing failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -119,8 +122,8 @@ static int sign_callback(LIBSSH2_SESSION *session,
|
||||
if (!buf) return -1;
|
||||
|
||||
uint32_t off = 0;
|
||||
off += write_mpint(&buf[off], &raw_sig[0], 32); /* R */
|
||||
off += write_mpint(&buf[off], &raw_sig[32], 32); /* S */
|
||||
off += write_mpint(&buf[off], r_raw, 32); /* R */
|
||||
off += write_mpint(&buf[off], s_raw, 32); /* S */
|
||||
|
||||
*sig = buf;
|
||||
*sig_len = off;
|
||||
@@ -132,15 +135,14 @@ static int sign_callback(LIBSSH2_SESSION *session,
|
||||
* ---------------------------------------------------------------------- */
|
||||
void ssh_print_public_key(void)
|
||||
{
|
||||
uint8_t raw_key[64];
|
||||
ATCA_STATUS st = atcab_get_pubkey(0, raw_key);
|
||||
if (st != ATCA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "atcab_get_pubkey failed: 0x%02X", st);
|
||||
uint8_t pub_x[32], pub_y[32];
|
||||
if (!efuse_ecdsa_get_pubkey(pub_x, pub_y)) {
|
||||
ESP_LOGE(TAG, "Failed to export public key from eFuse");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t blob[104];
|
||||
build_pubkey_blob(raw_key, blob);
|
||||
build_pubkey_blob(pub_x, pub_y, blob);
|
||||
|
||||
size_t b64_len = 0;
|
||||
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;
|
||||
|
||||
/* --- Read public key blob ------------------------------------------ */
|
||||
uint8_t raw_key[64];
|
||||
ATCA_STATUS st = atcab_get_pubkey(0, raw_key);
|
||||
if (st != ATCA_SUCCESS) {
|
||||
ESP_LOGE(TAG, "atcab_get_pubkey failed: 0x%02X", st);
|
||||
uint8_t pub_x[32], pub_y[32];
|
||||
if (!efuse_ecdsa_get_pubkey(pub_x, pub_y)) {
|
||||
ESP_LOGE(TAG, "Failed to export public key from eFuse");
|
||||
return false;
|
||||
}
|
||||
uint8_t pubkey_blob[104];
|
||||
build_pubkey_blob(raw_key, pubkey_blob);
|
||||
build_pubkey_blob(pub_x, pub_y, pubkey_blob);
|
||||
|
||||
/* --- TCP connect ------------------------------------------------------- */
|
||||
int rc;
|
||||
@@ -227,7 +228,7 @@ bool ssh_execute_command(const char *cmd)
|
||||
}
|
||||
ESP_LOGI(TAG, "SSH handshake OK");
|
||||
|
||||
/* --- Authenticate with ATECC608B hardware key ------------------------- */
|
||||
/* --- Authenticate with eFuse ECDSA hardware key ---------------------- */
|
||||
void *abstract = NULL;
|
||||
rc = libssh2_userauth_publickey(session, CONFIG_SSH_USERNAME,
|
||||
pubkey_blob, sizeof(pubkey_blob),
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Read the ATECC608B public key, format it as an SSH authorized_keys entry,
|
||||
* and print it to the console. Call this once at boot so the key can be
|
||||
* added to the server's authorized_keys file.
|
||||
* Derive the public key from the eFuse ECDSA private key, format it as an
|
||||
* SSH authorized_keys entry, and print it to the console.
|
||||
*
|
||||
* Output format:
|
||||
* 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
|
||||
* 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.
|
||||
* @return true on success, false on any error.
|
||||
|
||||
Reference in New Issue
Block a user