From 45bc1314d6fe59e1ba21083c708339c1de276c18 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 24 Jan 2024 16:54:16 +0100 Subject: [PATCH] Add Software Secure Element module - This module emulates what an hardware secure element does. It is not really secure. Data is stored in plaintext --- UNOR4USBBridge/SSE.cpp | 381 +++++++++++++++++++++++++++++ UNOR4USBBridge/SSE.h | 127 ++++++++++ UNOR4USBBridge/at_handler.cpp | 2 + UNOR4USBBridge/at_handler.h | 2 + UNOR4USBBridge/cmds_se.h | 438 ++++++++++++++++++++++++++++++++++ UNOR4USBBridge/commands.h | 14 ++ 6 files changed, 964 insertions(+) create mode 100644 UNOR4USBBridge/SSE.cpp create mode 100644 UNOR4USBBridge/SSE.h create mode 100644 UNOR4USBBridge/cmds_se.h diff --git a/UNOR4USBBridge/SSE.cpp b/UNOR4USBBridge/SSE.cpp new file mode 100644 index 0000000..ae72c71 --- /dev/null +++ b/UNOR4USBBridge/SSE.cpp @@ -0,0 +1,381 @@ +/* + This file is part of the UNOR4USBBridge project. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ +#include + +#include "mbedtls/pk.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/entropy.h" + +#include "mbedtls/error.h" +#include "mbedtls/asn1.h" +#include "mbedtls/asn1write.h" +#include "mbedtls/sha256.h" + +#include "SSE.h" + +/****************************************************************************** + LOCAL MODULE FUNCTIONS + ******************************************************************************/ + +/* + * Keep an eye on this new implementation from mbedtls not yet merged + * https://github.com/Mbed-TLS/mbedtls/pull/8703/files + */ + +/* + * https://github.com/Mbed-TLS/mbedtls/blob/aa3fa98bc4a99d21a973b891bf7bda9a27647068/library/pk_wrap.c#L543 + * An ASN.1 encoded signature is a sequence of two ASN.1 integers. Parse one of + * those integers and convert it to the fixed-length encoding expected by PSA. + */ +static int extract_ecdsa_sig_int(unsigned char **from, const unsigned char *end, + unsigned char *to, size_t to_len) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t unpadded_len, padding_len; + + if ((ret = mbedtls_asn1_get_tag(from, end, &unpadded_len, + MBEDTLS_ASN1_INTEGER)) != 0) { + return ret; + } + + while (unpadded_len > 0 && **from == 0x00) { + (*from)++; + unpadded_len--; + } + + if (unpadded_len > to_len || unpadded_len == 0) { + return MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + } + + padding_len = to_len - unpadded_len; + memset(to, 0x00, padding_len); + memcpy(to + padding_len, *from, unpadded_len); + (*from) += unpadded_len; + + return 0; +} + +/* + * https://github.com/Mbed-TLS/mbedtls/blob/aa3fa98bc4a99d21a973b891bf7bda9a27647068/library/pk_wrap.c#L576 + * Convert a signature from an ASN.1 sequence of two integers + * to a raw {r,s} buffer. Note: the provided sig buffer must be at least + * twice as big as int_size. + */ +static int extract_ecdsa_sig(unsigned char **p, const unsigned char *end, + unsigned char *sig, size_t int_size) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t tmp_size; + + if ((ret = mbedtls_asn1_get_tag(p, end, &tmp_size, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { + return ret; + } + + /* Extract r */ + if ((ret = extract_ecdsa_sig_int(p, end, sig, int_size)) != 0) { + return ret; + } + /* Extract s */ + if ((ret = extract_ecdsa_sig_int(p, end, sig + int_size, int_size)) != 0) { + return ret; + } + + return 0; +} + +/* + * https://github.com/Mbed-TLS/mbedtls/blob/47c74a477378ec3f0d1ba80547db836e078fa3a0/library/ecdsa.c#L609 + * Convert a signature (given by context) to ASN.1 + */ +static int ecdsa_signature_to_asn1(const mbedtls_mpi *r, const mbedtls_mpi *s, + unsigned char *sig, size_t sig_size, + size_t *slen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + unsigned char buf[MBEDTLS_ECDSA_MAX_LEN] = { 0 }; + unsigned char *p = buf + sizeof(buf); + size_t len = 0; + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, s)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_mpi(&p, buf, r)); + + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_len(&p, buf, len)); + MBEDTLS_ASN1_CHK_ADD(len, mbedtls_asn1_write_tag(&p, buf, + MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE)); + + if (len > sig_size) { + return MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + } + + memcpy(sig, p, len); + *slen = len; + + return 0; +} + + +/****************************************************************************** + PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + +int Arduino_UNOWIFIR4_SSE::generateECKeyPair(unsigned char* derKey, int maxLen) +{ + int ret = 1; + mbedtls_pk_context key; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + const char *pers = "gen_key"; + unsigned char mbedtls_buf[SSE_EC256_DER_PRI_KEY_LENGTH] = { 0 }; + + mbedtls_pk_init(&key); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *) pers, strlen(pers))) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) -ret); + goto exit; + } + + if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_ECKEY))) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_setup returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if ((ret = mbedtls_ecp_gen_key((mbedtls_ecp_group_id) MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(key), mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_ecp_gen_key returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if ((ret = mbedtls_pk_write_key_der(&key, mbedtls_buf, sizeof(mbedtls_buf))) < 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_write_key_der returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if (ret > maxLen) { + DEBUG_ERROR(" failed\n ! output buffer too small operation needs 0x%04x bytes", (unsigned int) ret); + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + goto exit; + } + + /* Copy data from the end of mbedtls_buf buffer to der output */ + memcpy(derKey, &mbedtls_buf[sizeof(mbedtls_buf) - ret], ret); + +exit: + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + mbedtls_pk_free(&key); + + return ret; +} + +int Arduino_UNOWIFIR4_SSE::exportECKeyXY(const unsigned char* derKey, int derLen, unsigned char* pubXY) +{ + int ret = 1; + mbedtls_pk_context key; + mbedtls_ecp_keypair *ecp; + + mbedtls_pk_init(&key); + + /* Check if we can use parse public key */ + if ((ret = mbedtls_pk_parse_key(&key, derKey, derLen, NULL, 0)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_parse_key returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if (mbedtls_pk_get_type(&key) != MBEDTLS_PK_ECKEY) { + DEBUG_ERROR(" failed\n ! Not an EC KEY"); + goto exit; + } + + /* Get elliptic curve point */ + ecp = mbedtls_pk_ec(key); + if ((ret = mbedtls_mpi_write_binary(&ecp->Q.X, &pubXY[0], SSE_EC256_X_LENGTH)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_mpi_write_binary Q.X returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if ((ret = mbedtls_mpi_write_binary(&ecp->Q.Y, &pubXY[SSE_EC256_X_LENGTH], SSE_EC256_Y_LENGTH)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_mpi_write_binary Q.XYreturned -0x%04x", (unsigned int) -ret); + goto exit; + } + + /* Success */ + ret = SSE_EC256_PUB_KEY_LENGTH; + +exit: + mbedtls_pk_free(&key); + return ret; +} + +int Arduino_UNOWIFIR4_SSE::importECKeyXY(const unsigned char* pubXY, unsigned char* derKey, int maxLen) +{ + int ret = 1; + mbedtls_pk_context key; + unsigned char mbedtls_buf[SSE_EC256_DER_PUB_KEY_LENGTH] = { 0 }; + mbedtls_ecp_keypair* ecp; + + mbedtls_pk_init(&key); + + if ((ret = mbedtls_pk_setup(&key, mbedtls_pk_info_from_type((mbedtls_pk_type_t) MBEDTLS_PK_ECKEY))) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_setup returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if (mbedtls_pk_get_type(&key) != MBEDTLS_PK_ECKEY) { + DEBUG_ERROR(" failed\n ! Not an EC KEY"); + goto exit; + } + + ecp = mbedtls_pk_ec(key); + + if (( ret = mbedtls_ecp_group_load(&ecp->grp, MBEDTLS_ECP_DP_SECP256R1)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_ecp_group_load returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + /* + * Add uncompressed flag {0x04 | X | Y } + * https://github.com/Mbed-TLS/mbedtls/blob/f660c7c92308b6080f8ca97fa1739370d1b2fab5/include/psa/crypto.h#L783 + */ + mbedtls_buf[0] = 0x04; memcpy(&mbedtls_buf[1], pubXY, SSE_EC256_PUB_KEY_LENGTH); + + if (( ret = mbedtls_ecp_point_read_binary(&ecp->grp, &ecp->Q, mbedtls_buf, SSE_EC256_PUB_KEY_LENGTH + 1)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_ecp_point_read_binary returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if ((ret = mbedtls_pk_write_pubkey_der(&key, mbedtls_buf, sizeof(mbedtls_buf))) < 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_write_pubkey_der returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if (ret > maxLen) { + DEBUG_ERROR(" failed\n ! output buffer too small operation needs 0x%04x bytes", (unsigned int) ret); + ret = MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL; + goto exit; + } + + /* Copy data from the end of mbedtls_buf buffer to der output */ + memcpy(derKey, &mbedtls_buf[sizeof(mbedtls_buf) - ret], ret); + +exit: + mbedtls_pk_free(&key); + return ret; +} + +int Arduino_UNOWIFIR4_SSE::sha256(const unsigned char* message, int len, unsigned char* sha256) +{ + int ret = 1; + + if((ret = mbedtls_sha256_ret(message, len, sha256, 0)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_sha256_ret returned -0x%04x\n", (unsigned int) -ret); + return ret; + } + return SSE_SHA256_LENGTH; +} + +int Arduino_UNOWIFIR4_SSE::sign(const unsigned char* derKey, int derLen, const unsigned char* sha256, unsigned char* sigRS) +{ + int ret = 1; + mbedtls_pk_context key; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + unsigned char mbedtls_buf[MBEDTLS_PK_SIGNATURE_MAX_SIZE] = { 0 }; + unsigned char *p = (unsigned char *)&mbedtls_buf[0]; + const char *pers = "gen_key"; + size_t sig_size = 0; + + mbedtls_pk_init(&key); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers))) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_ctr_drbg_seed returned -0x%04x\n", (unsigned int) -ret); + goto exit; + } + + /* verify if work using only private key*/ + if ((ret = mbedtls_pk_parse_key(&key, derKey, derLen, NULL, 0)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_parse_key returned -0x%04x", (unsigned int) -ret); + goto exit; + } + + if ((ret = mbedtls_pk_sign(&key, MBEDTLS_MD_SHA256, sha256, 0, mbedtls_buf, &sig_size, mbedtls_ctr_drbg_random, &ctr_drbg)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_sign returned -0x%04x", (unsigned int) -ret); + goto exit; + } + +#if SSE_DEBUG_ENABLED + log_v("SSE::sign: der signature"); + log_buf_v(mbedtls_buf, sig_size); +#endif + + /* Extract {r,s} values from DER signature */ + extract_ecdsa_sig(&p, &mbedtls_buf[sig_size], sigRS, SSE_EC256_R_LENGTH); + ret = SSE_EC256_SIGNATURE_LENGTH; + +exit: + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_pk_free(&key); + return ret; +} + +int Arduino_UNOWIFIR4_SSE::verify(const unsigned char* derKey, int derLen, const unsigned char* sha256, const unsigned char* sigRS) +{ + int ret = 1; + mbedtls_pk_context key; + unsigned char mbedtls_buf[MBEDTLS_PK_SIGNATURE_MAX_SIZE] = { 0 }; + mbedtls_mpi r,s; + size_t sig_size = 0; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + mbedtls_pk_init(&key); + + /* Verify is only public key is needed */ + if ((ret = mbedtls_pk_parse_public_key(&key, derKey, derLen)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_parse_public_key returned -0x%04x", (unsigned int) -ret); + goto exit; + } + +#if SSE_DEBUG_ENABLED + log_v("SSE::verify: sha256"); + log_buf_v((const uint8_t *)sha256, SSE_SHA256_LENGTH); + log_v("SSE::verify: compressed signature"); + log_buf_v((const uint8_t *)sigRS, SSE_EC256_SIGNATURE_LENGTH); +#endif + + /* Convert signature {r,s} values to DER */ + mbedtls_mpi_read_binary( &r, sigRS, SSE_EC256_R_LENGTH ); + mbedtls_mpi_read_binary( &s, sigRS + SSE_EC256_R_LENGTH, SSE_EC256_S_LENGTH ); + ecdsa_signature_to_asn1(&r, &s, mbedtls_buf, MBEDTLS_PK_SIGNATURE_MAX_SIZE, &sig_size); + +#if SSE_DEBUG_ENABLED + log_v("SSE::verify: der signature"); + log_buf_v((const uint8_t *)mbedtls_buf, sig_size); +#endif + + if ((ret = mbedtls_pk_verify(&key, MBEDTLS_MD_SHA256, sha256, SSE_SHA256_LENGTH, mbedtls_buf, sig_size)) != 0) { + DEBUG_ERROR(" failed\n ! mbedtls_pk_verify returned -0x%04x", (unsigned int) -ret); + goto exit; + } + +exit: + mbedtls_pk_free(&key); + return ret; +} + +Preferences sse; diff --git a/UNOR4USBBridge/SSE.h b/UNOR4USBBridge/SSE.h new file mode 100644 index 0000000..356f285 --- /dev/null +++ b/UNOR4USBBridge/SSE.h @@ -0,0 +1,127 @@ +/* + This file is part of the UNOR4USBBridge project. + + Copyright (c) 2024 Arduino SA + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#ifndef ARDUINO_UNOWIFIR4_SSE_H_ +#define ARDUINO_UNOWIFIR4_SSE_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include + +/****************************************************************************** + * DEFINES + ******************************************************************************/ + +#define SSE_DEBUG_ENABLED 0 + +#define SSE_SHA256_LENGTH 32 +#define SSE_EC256_R_LENGTH 32 +#define SSE_EC256_S_LENGTH SSE_EC256_R_LENGTH +#define SSE_EC256_SIGNATURE_LENGTH (SSE_EC256_R_LENGTH + SSE_EC256_S_LENGTH) +#define SSE_EC256_X_LENGTH 32 +#define SSE_EC256_Y_LENGTH SSE_EC256_X_LENGTH +#define SSE_EC256_PUB_KEY_LENGTH (SSE_EC256_X_LENGTH + SSE_EC256_Y_LENGTH) + +/* https://github.com/Mbed-TLS/mbedtls/blob/8bdd8cdc4fc99588f6e3ed47d3dac7b2ddbdb6af/library/pkwrite.h#L82 */ +#define SSE_EC256_DER_PUB_KEY_LENGTH (30 + 2 * SSE_EC256_X_LENGTH) +#define SSE_EC256_DER_PRI_KEY_LENGTH (29 + 3 * SSE_EC256_X_LENGTH) + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class Arduino_UNOWIFIR4_SSE +{ + +public: + + /** generateECKeyPair + * + * Create a new ECCurve_NIST_P256 keypair. Only public key will be available + * inside KeyBuf with DER format. + * + * | P256 Header (26 bytes)| 0x04 (1 byte)| Public key X Y values (64 bytes) | + * + * @param[out] derKey output buffer containing the public key in DER format + * @param[in] maxLen output buffer max size in bytes + * + * @return size of der buffer on Success a negative number on Failure (mbedtls error code) + */ + static int generateECKeyPair(unsigned char* derKey, int maxLen); + + /** exportECKeyXY + * + * Exports public key XY values from a DER formatted ECCurve_NIST_P256 key + * + * @param[in] derKey input buffer containing the public key in DER format + * @param[in] derLen key size in bytes + * @param[out] pubXY output buffer containing public key XY value. Should be at least 64 bytes + * + * @return size of XY buffer on Success a negative number on Failure (mbedtls error code) + */ + static int exportECKeyXY(const unsigned char* derKey, int derLen, unsigned char* pubXY); + + /** importECKeyXY + * + * Imports public key XY values from buffer and writes a DER formatted ECCurve_NIST_P256 public key + * + * @param[in] pubXY output buffer containing public key XY value. + * @param[out] derKey output buffer containing the public key in DER format + * @param[out] maxLen output buffer max size in bytes + * + * @return size of der buffer on Success a negative number on Failure (mbedtls error code) + */ + static int importECKeyXY(const unsigned char* pubXY, unsigned char* derKey, int maxLen); + + /** sha256 + * + * One-shot sha256 + * + * @param[in] message Input data buffer + * @param[in] len Input data length + * @param[out] sha256 Output buffer should be at least 32 bytes long + * + * @return size of sha256 buffer on Success a negative number on Failure (mbedtls error code) + */ + static int sha256(const unsigned char* message, int len, unsigned char* sha256); + + /** sign + * + * Computes ECDSA signature using private derKey and input sha256 + * + * @param[in] derKey input buffer containing the private key in DER format + * @param[in] derLen key size in bytes + * @param[in] sha256 input sha256 + * @param[out] sigRS output buffer containing signature RS value. Should be at least 64 bytes + * + * @return size of RS buffer on Success a negative number on Failure (mbedtls error code) + */ + static int sign(const unsigned char* derKey, int derLen, const unsigned char* sha256, unsigned char* sigRS); + + /** verify + * + * Verify ECDSA signature using public key + * + * @param[in] derKey input buffer containing the public key in DER format + * @param[in] derLen key size in bytes + * @param[in] sha256 input sha256 + * @param[in] sigRS input buffer containing signature RS value + * + * @return 0 on Success a negative number on Failure (mbedtls error code) + */ + static int verify(const unsigned char* derKey, int derLen, const unsigned char* sha256, const unsigned char* sigRS); + +}; + +extern Preferences sse; + +#endif /* Arduino_UNOWIFIR4_SSE */ diff --git a/UNOR4USBBridge/at_handler.cpp b/UNOR4USBBridge/at_handler.cpp index 7b77c1b..433902c 100644 --- a/UNOR4USBBridge/at_handler.cpp +++ b/UNOR4USBBridge/at_handler.cpp @@ -13,6 +13,7 @@ #include "cmds_ble_bridge.h" #include "cmds_ota.h" #include "cmds_preferences.h" +#include "cmds_se.h" using namespace SudoMaker; @@ -131,4 +132,5 @@ CAtHandler::CAtHandler(HardwareSerial *s) : last_server_client_sock(0) { add_cmds_ble_bridge(); add_cmds_ota(); add_cmds_preferences(); + add_cmds_se(); } diff --git a/UNOR4USBBridge/at_handler.h b/UNOR4USBBridge/at_handler.h index 6ff4dbb..1c3cee8 100644 --- a/UNOR4USBBridge/at_handler.h +++ b/UNOR4USBBridge/at_handler.h @@ -82,8 +82,10 @@ class CAtHandler { void add_cmds_ble_bridge(); void add_cmds_ota(); void add_cmds_preferences(); + void add_cmds_se(); public: std::vector cert_buf; + std::vector se_buf; CAtHandler(HardwareSerial *s); CAtHandler() = delete ; static void onWiFiEvent(WiFiEvent_t event); diff --git a/UNOR4USBBridge/cmds_se.h b/UNOR4USBBridge/cmds_se.h new file mode 100644 index 0000000..cbccbdf --- /dev/null +++ b/UNOR4USBBridge/cmds_se.h @@ -0,0 +1,438 @@ +#ifndef CMDS_SE_H +#define CMDS_SE_H + +#include "at_handler.h" +#include "SSE.h" + + +void CAtHandler::add_cmds_se() { + + /* ....................................................................... */ + command_table[_SOFTSE_BEGIN] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 3) { + return chAT::CommandStatus::ERROR; + } + + auto &name = parser.args[0]; + if (name.empty()) { + return chAT::CommandStatus::ERROR; + } + + /* Allow to call begin multiple times */ + sse.end(); + + bool readOnly = strtol(parser.args[1].c_str(), NULL, 10) != 0; + auto &partition = parser.args[2]; + String error = String(); + if (partition.empty()) { + error = String(sse.begin(name.c_str(), readOnly)) + "\r\n"; + } else { + error = String(sse.begin(name.c_str(), readOnly, partition.c_str())) + "\r\n"; + } + + srv.write_response_prompt(); + srv.write_str((const char *)(error.c_str())); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_END] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Run: { + sse.end(); + srv.write_response_prompt(); + srv.write_str("0"); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_SERIAL] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Run: { + std::vector data; + data.resize(8); + esp_efuse_mac_get_default(data.data()); + srv.write_response_prompt(); + srv.write_str(String(8).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_RND] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 1) { + return chAT::CommandStatus::ERROR; + } + + int len = strtol(parser.args[0].c_str(), NULL, 10); + + std::vector data; + data.resize(len); + esp_fill_random(data.data(), len); + srv.write_response_prompt(); + srv.write_str(String(len).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_PRI_KEY] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 1) { + return chAT::CommandStatus::ERROR; + } + + auto &key = parser.args[0]; + int len = 0; + int ret = 0; + + std::vector der; + der.resize(SSE_EC256_DER_PRI_KEY_LENGTH); + if ((len = Arduino_UNOWIFIR4_SSE::generateECKeyPair(der.data(), static_cast(der.size()))) < 0) { + DEBUG_ERROR(" failed\n ! generateECKeyPair returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } + der.resize(len); + if ((ret = sse.putBytes(key.c_str(), der.data(), len)) != len) { + DEBUG_ERROR(" failed\n ! sse.putBytes returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_PRI_KEY: generated EC keypair"); + log_buf_v((const uint8_t *)der.data(), len); +#endif + + std::vector data; + data.resize(SSE_EC256_PUB_KEY_LENGTH); + if ((len = Arduino_UNOWIFIR4_SSE::exportECKeyXY(der.data(), static_cast(der.size()), data.data())) != SSE_EC256_PUB_KEY_LENGTH) { + DEBUG_ERROR(" failed\n ! exportECKeyXY returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } + data.resize(len); + srv.write_response_prompt(); + srv.write_str(String(len).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_PUB_KEY] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 1) { + return chAT::CommandStatus::ERROR; + } + + auto &key = parser.args[0]; + int ret = 0; + int len = 0; + + std::vector der; + len = sse.getBytesLength(key.c_str()); + der.resize(len); + if ((ret = sse.getBytes(key.c_str(), der.data(), len)) < len) { + DEBUG_ERROR(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_PUB_KEY: stored EC keypair"); + log_buf_v((const uint8_t *)der.data(), len); +#endif + std::vector data; + data.resize(SSE_EC256_PUB_KEY_LENGTH); + if ((len = Arduino_UNOWIFIR4_SSE::exportECKeyXY(der.data(), static_cast(der.size()), data.data())) != SSE_EC256_PUB_KEY_LENGTH) { + DEBUG_ERROR(" failed\n ! exportECKeyXY returned -0x%04x", (unsigned int) -len); + return chAT::CommandStatus::ERROR; + } + + data.resize(len); + srv.write_response_prompt(); + srv.write_str(String(len).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_S_V_BUF_SET] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 1) { + return chAT::CommandStatus::ERROR; + } + + int value = atoi(parser.args[0].c_str()); + + se_buf = srv.inhibit_read(value); + size_t offset = se_buf.size(); + if(offset < value) { + se_buf.resize(value); + do { + offset += serial->read(se_buf.data() + offset, value - offset); + } while (offset < value); + } + srv.continue_read(); + + /* Only stores message in se_buffer */ +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_S_V_BUF_SET: message buffer"); + log_buf_v((const uint8_t *)se_buf.data(), value); +#endif + + srv.write_response_prompt(); + srv.write_str((const char *)(parser.args[0].c_str())); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_SIGN_GET] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 1) { + return chAT::CommandStatus::ERROR; + } + + auto &key = parser.args[0]; + int len = 0; + int ret = 0; + + /* Read private key from non volatile storage */ + std::vector der; + len = sse.getBytesLength(key.c_str()); + der.resize(len); + if ((ret = sse.getBytes(key.c_str(), der.data(), len)) < len) { + DEBUG_ERROR(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } + +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_SIGN_GET: message to sign:"); + log_buf_v((const uint8_t *)se_buf.data(), se_buf.size()); + + log_v("_SOFTSE_SIGN_GET: key to use:"); + log_buf_v((const uint8_t *)der.data(), len); +#endif + + /* sign message/digest/sha256 stored in se_buffer */ + std::vector data; + data.resize(SSE_EC256_SIGNATURE_LENGTH); + if ((ret = Arduino_UNOWIFIR4_SSE::sign(der.data(), len, se_buf.data(), data.data())) != SSE_EC256_SIGNATURE_LENGTH) { + DEBUG_ERROR(" failed\n ! sign returned -0x%04x", (unsigned int) -ret); + } + +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_SIGN_GET: {r,s} array"); + log_buf_v((const uint8_t *)data.data(), ret); +#endif + + srv.write_response_prompt(); + srv.write_str(String(ret).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_VERIFY_GET] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Run: { + int ret = 0; + + /* verify data stored in se_buffer + * + * [ 0 - 31 ] SHA526 + * [ 32 - 96 ] {r,s} signature values + * [ 96 - 160] EC XY values + */ + + /* Import public key from buffer */ + std::vector pub; + pub.resize(SSE_EC256_DER_PUB_KEY_LENGTH); + if ((ret = Arduino_UNOWIFIR4_SSE::importECKeyXY(&se_buf.data()[SSE_SHA256_LENGTH + SSE_EC256_SIGNATURE_LENGTH], pub.data(), SSE_EC256_DER_PUB_KEY_LENGTH)) < 0) { + DEBUG_ERROR(" failed\n ! importECKeyXY returned -0x%04x", (unsigned int) -ret); + return chAT::CommandStatus::ERROR; + } + pub.resize(ret); + +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_VERIFY_GET: input buffer", se_buf.size()); + log_buf_v((const uint8_t *)se_buf.data(), se_buf.size()); + + log_v("_SOFTSE_VERIFY_GET: EC key XY values"); + log_buf_v((const uint8_t *)pub.data(), ret); +#endif + + /* Verify data */ + if ((ret = Arduino_UNOWIFIR4_SSE::verify(pub.data(), ret, &se_buf.data()[0], &se_buf.data()[SSE_SHA256_LENGTH])) != 0) { + DEBUG_ERROR(" failed\n ! verify returned -0x%04x", (unsigned int) -ret); + } + srv.write_response_prompt(); + srv.write_str((const char *)String(ret).c_str()); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_SHA256_GET] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Run: { + int ret = 0; + +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_SHA256_GET: message to hash"); + log_buf_v((const uint8_t *)se_buf.data(), se_buf.size()); +#endif + + /* sign message/digest/sha256 stored in se_buffer */ + std::vector data; + data.resize(SSE_SHA256_LENGTH); + if ((ret = Arduino_UNOWIFIR4_SSE::sha256(se_buf.data(), se_buf.size(), data.data())) != SSE_SHA256_LENGTH) { + DEBUG_ERROR(" failed\n ! sse.getBytes returned -0x%04x", (unsigned int) -ret); + } + +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_SHA256_GET: sha256"); + log_buf_v((const uint8_t *)data.data(), ret); +#endif + + srv.write_response_prompt(); + srv.write_str(String(ret).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_WRITE_SLOT] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 2) { + return chAT::CommandStatus::ERROR; + } + + auto &key = parser.args[0]; + int value = atoi(parser.args[1].c_str()); + + se_buf = srv.inhibit_read(value); + size_t offset = se_buf.size(); + if(offset < value) { + se_buf.resize(value); + do { + offset += serial->read(se_buf.data() + offset, value - offset); + } while (offset < value); + } + srv.continue_read(); + String error = String(sse.putBytes(key.c_str(), se_buf.data(), value)) + "\r\n"; + +#if SSE_DEBUG_ENABLED + log_v("_SOFTSE_WRITE_SLOT: input data"); + log_buf_v((const uint8_t *)se_buf.data(), value); +#endif + + srv.write_response_prompt(); + srv.write_str((const char *)(error.c_str())); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; + + /* ....................................................................... */ + command_table[_SOFTSE_READ_SLOT] = [this](auto & srv, auto & parser) { + /* ....................................................................... */ + switch (parser.cmd_mode) { + case chAT::CommandMode::Write: { + if (parser.args.size() != 1) { + return chAT::CommandStatus::ERROR; + } + + auto &key = parser.args[0]; + std::vector data; + + int len = sse.getBytesLength(key.c_str()); + data.resize(len); + sse.getBytes(key.c_str(), data.data(), len); + srv.write_response_prompt(); + srv.write_str(String(len).c_str()); + srv.write_str("|"); + srv.write_vec8(data); + srv.write_line_end(); + return chAT::CommandStatus::OK; + } + default: + return chAT::CommandStatus::ERROR; + } + }; +} + +#endif diff --git a/UNOR4USBBridge/commands.h b/UNOR4USBBridge/commands.h index 09e0d8a..6a170f6 100644 --- a/UNOR4USBBridge/commands.h +++ b/UNOR4USBBridge/commands.h @@ -128,6 +128,20 @@ enum { #define _PREF_LEN "+PREFLEN" #define _PREF_STAT "+PREFSTAT" +#define _SOFTSE_BEGIN "+SOFTSEBEGIN" +#define _SOFTSE_END "+SOFTSEEND" +#define _SOFTSE_SERIAL "+SOFTSE_SERIAL" +#define _SOFTSE_RND "+SOFTSE_RND" +#define _SOFTSE_PRI_KEY "+SOFTSE_PRI_KEY" +#define _SOFTSE_PUB_KEY "+SOFTSE_PUB_KEY" +#define _SOFTSE_WRITE_SLOT "+SOFTSE_WRITE_SLOT" +#define _SOFTSE_READ_SLOT "+SOFTSE_READ_SLOT" +#define _SOFTSE_S_V_BUF_SET "+SOFTSE_S_V_BUF_SET" +#define _SOFTSE_SIGN_GET "+SOFTSE_SIGN_GET" +#define _SOFTSE_VERIFY_GET "+SOFTSE_VERIFY_GET" +#define _SOFTSE_SHA256_GET "+SOFTSE_SHA256_GET" + + #define CMD(x) _AT x _ENDL #define PROMPT(x) x ":"