From 1e4fda0131c8d3a0e1e82dcab0968d4c83d6dbe3 Mon Sep 17 00:00:00 2001 From: Jonathan Berrisch Date: Sat, 28 Feb 2026 01:35:36 +0100 Subject: [PATCH] Add ssh auth flow (WIP) --- dependencies.lock | 11 ++++- main/idf_component.yml | 2 + main/zk_auth.h | 105 ++++++++++++++++++++++++++++++++++------- 3 files changed, 100 insertions(+), 18 deletions(-) diff --git a/dependencies.lock b/dependencies.lock index 57a04a6..35191b3 100644 --- a/dependencies.lock +++ b/dependencies.lock @@ -9,13 +9,22 @@ dependencies: path: . type: git version: d9792119ebaec0c54839e6605acd3f11dd937205 + esp-idf-ssh-client: + component_hash: d6f7b468c951d78e17e7a9b6911768c807729776338aad447f5c5dcd23642ee4 + dependencies: [] + source: + git: https://gitlab.com/ch405labs/ch405labs_esp_libssh2.git + path: . + type: git + version: 8b136ec9ee4ff26f19fad36e23062a0a79a32619 idf: source: type: idf version: 5.5.2 direct_dependencies: - esp-cryptoauthlib +- esp-idf-ssh-client - idf -manifest_hash: c756397bc94626ff7204b9330f85430937d2c27c736bf3075be0f87eb8eb0efc +manifest_hash: c0dd2805d23d2f57e818e665712d10a21c7d820835cc0b907c812de71ec4daa7 target: esp32c6 version: 2.0.0 diff --git a/main/idf_component.yml b/main/idf_component.yml index 4fa4249..9c06eb4 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -4,3 +4,5 @@ dependencies: version: ">=4.1.0" esp-cryptoauthlib: git: https://github.com/espressif/esp-cryptoauthlib.git + esp-idf-ssh-client: + git: https://gitlab.com/ch405labs/ch405labs_esp_libssh2.git diff --git a/main/zk_auth.h b/main/zk_auth.h index 4db388d..645e40d 100644 --- a/main/zk_auth.h +++ b/main/zk_auth.h @@ -3,10 +3,12 @@ #include "cryptoauthlib.h" #include "host/atca_host.h" +#include "libssh2_config.h" #include "mbedtls/sha256.h" #include #include #include +#include #include #include #include @@ -19,15 +21,11 @@ #include #include -// Zero-Knowledge Authentication Module -// Implements Client-Side KDF + ECIES Tunnel for ESP32-C6 -// Format: SHA-256 compatible with ATECC608B -// -// Encryption: AES-256-CBC + HMAC-SHA256 (Encrypt-then-MAC) -// - Provides confidentiality (CBC) + authenticity (HMAC) -// - Blob format: IV (16 bytes) + Ciphertext (32 bytes) + HMAC (32 bytes) = 80 -// bytes -// - HMAC verified BEFORE decryption (prevents padding oracle attacks) +// Added network headers for ESP-IDF (lwIP) sockets +#include +#include +#include +#include class ZKAuth { private: @@ -91,6 +89,25 @@ public: return true; } + // Marked as static so it can be passed as a C callback to libssh2 + static int sign_callback(LIBSSH2_SESSION *session, unsigned char **sig, + size_t *sig_len, const unsigned char *data, + size_t data_len, void **abstract) { + ATCA_STATUS status; + uint8_t signature[64]; + + // sign the data hash with ATECC608B + status = atcab_sign(0x0, data, signature); + if (status != ATCA_SUCCESS) { + return -1; + } + + // TODO: implement DER encoding and allocate output blob + // ... + + return 0; + } + // Helper function to write a 32-bit integer in big-endian format void write_uint32_be(uint8_t *buf, uint32_t val) { buf[0] = (val >> 24) & 0xFF; @@ -100,7 +117,8 @@ public: } // Function to generate the authorized_keys string - void generate_ssh_authorized_key(const uint8_t *atec_pubkey) { + void generate_ssh_authorized_key(const uint8_t *atecc_pubkey, + uint8_t *out_blob) { uint8_t ssh_blob[104]; uint32_t offset = 0; @@ -122,7 +140,7 @@ public: write_uint32_be(&ssh_blob[offset], 65); offset += 4; ssh_blob[offset++] = 0x04; - memcpy(&ssh_blob[offset], atec_pubkey, 64); + memcpy(&ssh_blob[offset], atecc_pubkey, 64); offset += 64; // 4. Base64 Encode the blob @@ -136,23 +154,34 @@ public: // 5. Print out the final authorized_keys line printf("ecdsa-sha2-nistp256 %s esp32-atecc608b\n", b64_out); + + memcpy(out_blob, ssh_blob, 104); } // Get device identity (for /api/identity endpoint) char *get_identity_json() { - char pubkey_hex[131]; // 65 bytes * 2 + null - uint8_t atec_pubkey[64]; // 65 bytes * 2 + null + char pubkey_hex[131]; // 65 bytes * 2 + null + uint8_t atecc_pubkey[64]; // 65 bytes * 2 + null uint8_t standard_pubkey[65]; standard_pubkey[0] = 0x04; // Get public key from ATECC608B and convert to hex - ATCA_STATUS status = atcab_get_pubkey(0, atec_pubkey); + ATCA_STATUS status = atcab_get_pubkey(0, atecc_pubkey); if (status != ATCA_SUCCESS) { printf("Failed to read public key from ATECC608B: 0x%02X\n", status); } - generate_ssh_authorized_key(atec_pubkey); + uint8_t pubkey_blob[256]; - memcpy(&standard_pubkey[1], atec_pubkey, 64); + generate_ssh_authorized_key(atecc_pubkey, pubkey_blob); + + // Print the authorized_keys line for debugging + printf("Generated authorized_keys line:\n"); + for (int i = 0; i < 104; i++) { + printf("%02x", pubkey_blob[i]); + } + printf("\n"); + + memcpy(&standard_pubkey[1], atecc_pubkey, 64); bin_to_hex(standard_pubkey, 65, pubkey_hex); // Get MAC address to use as salt @@ -167,8 +196,50 @@ public: char *json_str = cJSON_PrintUnformatted(root); cJSON_Delete(root); + + int sock; + struct sockaddr_in sin; + const char *hostname = "192.168.4.1"; + unsigned short port = 22; + const char *username = "key"; + + void *my_abstract = NULL; + LIBSSH2_SESSION *session; + + // Initialize libssh2 + libssh2_init(0); + + // Set up and connect socket + sock = socket(AF_INET, SOCK_STREAM, 0); + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + inet_pton(AF_INET, hostname, &sin.sin_addr); + connect(sock, (struct sockaddr *)(&sin), sizeof(struct sockaddr_in)); + + // Create SSH session + session = libssh2_session_init(); + libssh2_session_handshake(session, sock); + + // PUBLIC KEY AUTH: using custom callback + int rc = libssh2_userauth_publickey(session, username, pubkey_blob, + sizeof(pubkey_blob), sign_callback, + &my_abstract); + + if (rc == 0) { + printf("SSH authentication successful!\n"); + // ... Continue SSH communication ... + } else { + printf("Authentication failed\n"); + } + + // Cleanup + libssh2_session_disconnect(session, "Bye"); + libssh2_session_free(session); + close(sock); + libssh2_exit(); + return json_str; } }; -#endif // ZK_AUTH_H +#endif // ZK_AUTH_H \ No newline at end of file