From 1500f231f108ac553744f8d5ed489b6310d1bf92 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 31 Jan 2024 16:41:01 +0100 Subject: [PATCH 1/7] Software AT Secure Element SATSE initial commit --- libraries/SoftwareATSE/.unor4_only | 0 .../SATSECertificate/SATSECertificate.ino | 83 ++++++ .../SATSEPrivateKey/SATSEPrivateKey.ino | 43 ++++ .../SATSERandomNumber/SATSERandomNumber.ino | 28 ++ .../SATSESerialNumber/SATSESerialNumber.ino | 28 ++ .../SATSESignAndVerify/SATSESignAndVerify.ino | 83 ++++++ libraries/SoftwareATSE/library.properties | 9 + libraries/SoftwareATSE/src/SoftwareATSE.cpp | 240 ++++++++++++++++++ libraries/SoftwareATSE/src/SoftwareATSE.h | 64 +++++ 9 files changed, 578 insertions(+) create mode 100644 libraries/SoftwareATSE/.unor4_only create mode 100644 libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino create mode 100644 libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino create mode 100644 libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino create mode 100644 libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino create mode 100644 libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino create mode 100644 libraries/SoftwareATSE/library.properties create mode 100644 libraries/SoftwareATSE/src/SoftwareATSE.cpp create mode 100644 libraries/SoftwareATSE/src/SoftwareATSE.h diff --git a/libraries/SoftwareATSE/.unor4_only b/libraries/SoftwareATSE/.unor4_only new file mode 100644 index 000000000..e69de29bb diff --git a/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino b/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino new file mode 100644 index 000000000..4abf4cff7 --- /dev/null +++ b/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino @@ -0,0 +1,83 @@ +/* + Software Secure Element Certificate + + This sketch uses the Software Secure Element to store device certificate and read it back. + + Circuit: + - UNO R4 WiFi +*/ + +#include + +const byte certificate[410] = { + 0x30 ,0x82 ,0x01 ,0x96 ,0x30 ,0x82 ,0x01 ,0x3D ,0xA0 ,0x03 ,0x02 ,0x01 ,0x02 ,0x02 ,0x10 ,0x37, + 0xFE ,0x48 ,0x92 ,0xE6 ,0xC0 ,0xA0 ,0x64 ,0x68 ,0x91 ,0x66 ,0x5F ,0x7D ,0xE3 ,0x02 ,0xDE ,0x30, + 0x0A ,0x06 ,0x08 ,0x2A ,0x86 ,0x48 ,0xCE ,0x3D ,0x04 ,0x03 ,0x02 ,0x30 ,0x45 ,0x31 ,0x0B ,0x30, + 0x09 ,0x06 ,0x03 ,0x55 ,0x04 ,0x06 ,0x13 ,0x02 ,0x55 ,0x53 ,0x31 ,0x17 ,0x30 ,0x15 ,0x06 ,0x03, + 0x55 ,0x04 ,0x0A ,0x13 ,0x0E ,0x41 ,0x72 ,0x64 ,0x75 ,0x69 ,0x6E ,0x6F ,0x20 ,0x4C ,0x4C ,0x43, + 0x20 ,0x55 ,0x53 ,0x31 ,0x0B ,0x30 ,0x09 ,0x06 ,0x03 ,0x55 ,0x04 ,0x0B ,0x13 ,0x02 ,0x49 ,0x54, + 0x31 ,0x10 ,0x30 ,0x0E ,0x06 ,0x03 ,0x55 ,0x04 ,0x03 ,0x13 ,0x07 ,0x41 ,0x72 ,0x64 ,0x75 ,0x69, + 0x6E ,0x6F ,0x30 ,0x20 ,0x17 ,0x0D ,0x32 ,0x33 ,0x30 ,0x33 ,0x33 ,0x31 ,0x30 ,0x37 ,0x30 ,0x30, + 0x30 ,0x30 ,0x5A ,0x18 ,0x0F ,0x32 ,0x30 ,0x35 ,0x34 ,0x30 ,0x33 ,0x33 ,0x31 ,0x30 ,0x37 ,0x30, + 0x30 ,0x30 ,0x30 ,0x5A ,0x30 ,0x2F ,0x31 ,0x2D ,0x30 ,0x2B ,0x06 ,0x03 ,0x55 ,0x04 ,0x03 ,0x13, + 0x24 ,0x37 ,0x61 ,0x31 ,0x39 ,0x39 ,0x65 ,0x62 ,0x30 ,0x2D ,0x38 ,0x33 ,0x64 ,0x38 ,0x2D ,0x34, + 0x63 ,0x34 ,0x34 ,0x2D ,0x39 ,0x66 ,0x66 ,0x32 ,0x2D ,0x30 ,0x32 ,0x33 ,0x35 ,0x37 ,0x38 ,0x30, + 0x31 ,0x35 ,0x64 ,0x33 ,0x39 ,0x30 ,0x59 ,0x30 ,0x13 ,0x06 ,0x07 ,0x2A ,0x86 ,0x48 ,0xCE ,0x3D, + 0x02 ,0x01 ,0x06 ,0x08 ,0x2A ,0x86 ,0x48 ,0xCE ,0x3D ,0x03 ,0x01 ,0x07 ,0x03 ,0x42 ,0x00 ,0x04, + 0x60 ,0x53 ,0x94 ,0x10 ,0x8C ,0xA6 ,0xB6 ,0xC8 ,0xD2 ,0x05 ,0x22 ,0x61 ,0xD9 ,0x5D ,0xF8 ,0xDB, + 0xD1 ,0xF4 ,0xE4 ,0xAC ,0xC9 ,0x96 ,0x8E ,0xFF ,0xB8 ,0x7E ,0x0D ,0xDC ,0xA1 ,0xB8 ,0x0F ,0x4C, + 0xF5 ,0x66 ,0x68 ,0xF0 ,0xF4 ,0xF0 ,0x70 ,0xF3 ,0xF6 ,0xFD ,0x70 ,0xD2 ,0x7A ,0xFB ,0x20 ,0x70, + 0x30 ,0x82 ,0x5F ,0x34 ,0xF8 ,0x2A ,0x1B ,0xC5 ,0xB1 ,0x38 ,0xE5 ,0xA5 ,0xF7 ,0xC7 ,0xB4 ,0x62, + 0xA3 ,0x23 ,0x30 ,0x21 ,0x30 ,0x1F ,0x06 ,0x03 ,0x55 ,0x1D ,0x23 ,0x04 ,0x18 ,0x30 ,0x16 ,0x80, + 0x14 ,0x5B ,0x3E ,0x2A ,0x6B ,0x8E ,0xC9 ,0xB0 ,0x1A ,0xA8 ,0x54 ,0xE6 ,0x36 ,0x9B ,0x8C ,0x09, + 0xF9 ,0xFC ,0xE1 ,0xB9 ,0x80 ,0x30 ,0x0A ,0x06 ,0x08 ,0x2A ,0x86 ,0x48 ,0xCE ,0x3D ,0x04 ,0x03, + 0x02 ,0x03 ,0x47 ,0x00 ,0x30 ,0x44 ,0x02 ,0x20 ,0x16 ,0x85 ,0x8A ,0x58 ,0x07 ,0x28 ,0xEF ,0x6D, + 0x93 ,0x86 ,0xA0 ,0x0E ,0xC8 ,0xB0 ,0x0A ,0xAD ,0x3B ,0xCE ,0xBB ,0x6A ,0x19 ,0x94 ,0xF9 ,0xD3, + 0x05 ,0x2E ,0x15 ,0xF1 ,0x5E ,0x9F ,0x59 ,0xD2 ,0x02 ,0x20 ,0x45 ,0x30 ,0x88 ,0x1D ,0x24 ,0xDA, + 0xE4 ,0x60 ,0xE2 ,0xD0 ,0x6E ,0x02 ,0xB0 ,0x7D ,0x65 ,0xA8 ,0x09 ,0x63 ,0x0B ,0x44 ,0xBC ,0x24, + 0x1A ,0xE2 ,0xEC ,0x64 ,0x19 ,0xB4 ,0x59 ,0xB8 ,0x09 ,0x78 +}; + +void printBufferHex(const byte input[], size_t inputLength) { + Serial.println(inputLength); + for (size_t i = 0; i < inputLength; i++) { + Serial.print(input[i] >> 4, HEX); + Serial.print(input[i] & 0x0f, HEX); + } + Serial.println(); +} + +void setup() { + Serial.begin(9600); + while (!Serial); + + if (!SATSE.begin()) { + Serial.println("Failed to communicate with Software Secure Element!"); + while (1); + } + + const int certId = 799; + + if(SATSE.writeSlot(certId, certificate, sizeof(certificate))) { + Serial.println("Data stored"); + } else { + Serial.println("Failed to store data"); + return; + } + + byte buf[512]; + int len = 0; + + if((len = SATSE.readSlot(certId, buf, sizeof(buf))) > 0) { + Serial.print("Readback data is: "); + printBufferHex(buf, len); + } else { + Serial.println("Failed to read data"); + return; + } + +} + +void loop() { + +} diff --git a/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino b/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino new file mode 100644 index 000000000..0ae22552e --- /dev/null +++ b/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino @@ -0,0 +1,43 @@ +/* + Software Secure Element Private Key + + This sketch uses the Software Secure Element to generate a new EC NIST P-256 keypair + and store it with id 999, then the public key is printed in raw format. + + Circuit: + - UNO R4 WiFi +*/ + +#include +#include + +const int KeyId = 999; +byte rawBuf[64]; + +void printBufferHex(const byte input[], size_t inputLength) { + for (size_t i = 0; i < inputLength; i++) { + Serial.print(input[i] >> 4, HEX); + Serial.print(input[i] & 0x0f, HEX); + } + Serial.println(); +} + +void setup() { + Serial.begin(9600); + while (!Serial); + + if (!SATSE.begin()) { + Serial.println("Failed to communicate with Software Secure Element!"); + while (1); + } + + SATSE.generatePrivateKey(KeyId, rawBuf); + printBufferHex(rawBuf, sizeof(rawBuf)); + +} + +void loop() { + SATSE.generatePublicKey(KeyId, rawBuf); + printBufferHex(rawBuf, sizeof(rawBuf)); + delay(5000); +} diff --git a/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino b/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino new file mode 100644 index 000000000..2e5f9197e --- /dev/null +++ b/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino @@ -0,0 +1,28 @@ +/* + Software Secure Element Random Number + + This sketch uses the Software Secure Element to generate a random number + every second and print it to the Serial monitor + + Circuit: + - UNO R4 WiFi +*/ + +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + if (!SATSE.begin()) { + Serial.println("Failed to communicate with Software Secure Element!"); + while (1); + } +} + +void loop() { + Serial.print("Random number = "); + Serial.println(SATSE.random(65535)); + + delay(1000); +} diff --git a/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino b/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino new file mode 100644 index 000000000..243e327af --- /dev/null +++ b/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino @@ -0,0 +1,28 @@ +/* + Software Secure Element serial number + + This sketch prints the Software Secure Element serial number: + ESP32-S3-MINI-1 efuse mac address + + Circuit: + - UNO R4 WiFi +*/ + +#include + +void setup() { + Serial.begin(9600); + while (!Serial); + + if (!SATSE.begin()) { + Serial.println("Failed to communicate with Software Secure Element!"); + while (1); + } +} + +void loop() { + Serial.print("S/N = "); + Serial.println(SATSE.serialNumber()); + + delay(1000); +} diff --git a/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino b/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino new file mode 100644 index 000000000..45c8f61c4 --- /dev/null +++ b/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino @@ -0,0 +1,83 @@ +/* + Software Secure Element SignAndVerify + + This sketch uses the Software Secure Element to generate a new EC NIST P-256 keypair + and store it with id 999, then input buffer SHA256 is signed with the private + key and verified with the public key. + + Circuit: + - UNO R4 WiFi +*/ + +#include + +const byte input[64] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f +}; + +void printBufferHex(const byte input[], size_t inputLength) { + for (size_t i = 0; i < inputLength; i++) { + Serial.print(input[i] >> 4, HEX); + Serial.print(input[i] & 0x0f, HEX); + } + Serial.println(); +} + +void setup() { + Serial.begin(9600); + while (!Serial); + + if (!SATSE.begin()) { + Serial.println("Failed to communicate with Software Secure Element!"); + while (1); + } + + const int KeyId = 999; + byte pubKey[256]; + + SATSE.generatePrivateKey(KeyId, pubKey); + + // print the public key + Serial.print("Public key is: "); + printBufferHex(pubKey, 64); + + // print the input + Serial.print("Input is: "); + printBufferHex(input, sizeof(input)); + + // calculate the input SHA256 + byte sha256[256]; + size_t sha256Len; + SATSE.SHA256(input, sizeof(input), sha256); + Serial.print("Input SHA256 is: "); + printBufferHex(sha256, 32); + + // calculate the signature, input MUST be SHA256 + byte signature[256]; + SATSE.ecSign(KeyId, sha256, signature); + + // print the signature + Serial.print("Signature using KeyId "); + Serial.print(KeyId); + Serial.print(" is: "); + printBufferHex(signature, 64); + + Serial.println(); + + // To make the signature verifcation fail, uncomment the next line: + // signature[0] = 0x00; + + // validate the signature + if (SATSE.ecdsaVerify(sha256, signature, pubKey)) { + Serial.println("Verified signature successfully :D"); + } else { + Serial.println("oh no! failed to verify signature :("); + } +} + +void loop() { + +} diff --git a/libraries/SoftwareATSE/library.properties b/libraries/SoftwareATSE/library.properties new file mode 100644 index 000000000..c4820efeb --- /dev/null +++ b/libraries/SoftwareATSE/library.properties @@ -0,0 +1,9 @@ +name=SoftwareATSE +version=0.0.1 +author=Arduino +maintainer=Arduino +sentence=Arduino Library implementing base secure element functions in software +paragraph= +category=Communication +url= +architectures=renesas,renesas_uno \ No newline at end of file diff --git a/libraries/SoftwareATSE/src/SoftwareATSE.cpp b/libraries/SoftwareATSE/src/SoftwareATSE.cpp new file mode 100644 index 000000000..4f879dac6 --- /dev/null +++ b/libraries/SoftwareATSE/src/SoftwareATSE.cpp @@ -0,0 +1,240 @@ +/* + SoftwareATSE.cpp + Copyright (c) 2023 Arduino SA. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "SoftwareATSE.h" +#include "Modem.h" + +using namespace std; + +SoftwareATSEClass::SoftwareATSEClass() { + +} + +SoftwareATSEClass::~SoftwareATSEClass() { + end(); +} + +int SoftwareATSEClass::begin(const char * name, bool readOnly, const char* partition_label) +{ + string res = ""; + modem.begin(); + modem.debug(Serial,2); + if (name != nullptr && strlen(name) > 0) { + if (modem.write(string(PROMPT(_SOFTSE_BEGIN)), res, "%s%s,%d,%s\r\n", CMD_WRITE(_SOFTSE_BEGIN), name, readOnly, partition_label != NULL ? partition_label : "")) { + return atoi(res.c_str()); + } + } + return 0; +} + +void SoftwareATSEClass::end() +{ + string res = ""; + modem.write(string(PROMPT(_SOFTSE_END)), res, "%s", CMD(_SOFTSE_END)); +} + +String SoftwareATSEClass::serialNumber() +{ + string res = ""; + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_SERIAL)), res, "%s", CMD(_SOFTSE_SERIAL))) { + if (res.size()) { + String result = (char*)NULL; + result.reserve(res.size() * 2); + + for (size_t i = 0; i < res.size(); i++) { + byte b = res.data()[i]; + + if (b < 16) { + result += "0"; + } + result += String(b, HEX); + } + result.toUpperCase(); + return result; + } + } + return ""; +} + +long SoftwareATSEClass::random(long max) +{ + return random(0, max); +} + +long SoftwareATSEClass::random(long min, long max) +{ + if (min >= max) + { + return min; + } + + long diff = max - min; + + long r; + random((byte*)&r, sizeof(r)); + + if (r < 0) { + r = -r; + } + + r = (r % diff); + + return (r + min); +} + +int SoftwareATSEClass::random(byte data[], size_t length) +{ + string res = ""; + if (data != nullptr && length > 0) { + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_RND)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_RND), length)) { + if (res.size() >= length) { + memcpy(data, (uint8_t*)&res[0], length); + return 1; + } + } + } + return 0; +} + +int SoftwareATSEClass::generatePrivateKey(int keyID, byte publicKey[]) +{ + string res = ""; + if (publicKey != nullptr) { + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_PRI_KEY)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_PRI_KEY), keyID)) { + if (res.size() >= 64) { + memcpy(publicKey, (uint8_t*)&res[0], 64); + return 64; + } + } + } + return 0; +} + +int SoftwareATSEClass::generatePublicKey(int keyID, byte publicKey[]) +{ + string res = ""; + if (publicKey != nullptr) { + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_PUB_KEY)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_PUB_KEY), keyID)) { + if (res.size() >= 64) { + memcpy(publicKey, (uint8_t*)&res[0], 64); + return 64; + } + } + } + return 0; +} + +int SoftwareATSEClass::ecSign(int slot, const byte message[], byte signature[]) +{ + string res = ""; + if ( message != nullptr) { + modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), 32); + if(!modem.passthrough((uint8_t *)message, 32)) { + return 0; + } + } + + if (signature != nullptr) { + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_SIGN_GET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_SIGN_GET), slot)) { + if (res.size() == 64) { + memcpy(signature, (uint8_t*)&res[0], 64); + return 64; + } + } + } + return 0; +} + +int SoftwareATSEClass::ecdsaVerify(const byte message[], const byte signature[], const byte pubkey[]) +{ + string res = ""; + if ( message != nullptr && signature!= nullptr) { + byte tmp[256]; + memcpy(tmp, message,32); + memcpy(&tmp[32], signature, 64); + memcpy(&tmp[32+64], pubkey, 64); + modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), 32+64+64); + if(!modem.passthrough((uint8_t *)tmp, 32+64+64)) { + return 0; + } + } + + if (signature != nullptr) { + if (modem.write(string(PROMPT(_SOFTSE_VERIFY_GET)), res, "%s", CMD(_SOFTSE_VERIFY_GET))) { + return atoi(res.c_str()) == 0 ? 1: 0; + } + } + return 0; +} + +int SoftwareATSEClass::SHA256(const uint8_t *buffer, size_t size, uint8_t *digest) { + string res = ""; + if ( buffer != nullptr) { + modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), size); + if(!modem.passthrough((uint8_t *)buffer, size)) { + return 0; + } + } + + if (digest != nullptr) { + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_SHA256_GET)), res, "%s", CMD(_SOFTSE_SHA256_GET))) { + if (res.size() == 32) { + memcpy(digest, (uint8_t*)&res[0], 32); + return 32; + } + } + } + return 0; +} + +int SoftwareATSEClass::readSlot(int slot, byte data[], int length) +{ + string res = ""; + if (data != nullptr) { + modem.read_using_size(); + if (modem.write(string(PROMPT(_SOFTSE_READ_SLOT)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_READ_SLOT), slot)) { + if (res.size()) { + int len = res.size() > length ? length : res.size(); + memcpy(data, (uint8_t*)&res[0], len); + return len; + } + } + } + return 0; +} + +int SoftwareATSEClass::writeSlot(int slot, const byte data[], int length) +{ + string res = ""; + if ( data != nullptr && length > 0) { + modem.write_nowait(string(PROMPT(_SOFTSE_WRITE_SLOT)), res, "%s%d,%d\r\n", CMD_WRITE(_SOFTSE_WRITE_SLOT), slot, length); + if(modem.passthrough((uint8_t *)data, length)) { + return length; + } + } + return 0; +} + +SoftwareATSEClass SATSE; diff --git a/libraries/SoftwareATSE/src/SoftwareATSE.h b/libraries/SoftwareATSE/src/SoftwareATSE.h new file mode 100644 index 000000000..d1f1502da --- /dev/null +++ b/libraries/SoftwareATSE/src/SoftwareATSE.h @@ -0,0 +1,64 @@ +/* + SoftwareATSE.h + Copyright (c) 2023 Arduino SA. All right reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _SOFTWARE_ATSE_H_ +#define _SOFTWARE_ATSE_H_ + +#include + + +class SoftwareATSEClass +{ +public: + SoftwareATSEClass(); + virtual ~SoftwareATSEClass(); + + int begin(const char * name = "se", bool readOnly = false, const char* partition_label = NULL); + void end(); + + String serialNumber(); + + long random(long min, long max); + long random(long max); + int random(byte data[], size_t length); + + int generatePrivateKey(int slot, byte publicKey[]); + int generatePublicKey(int slot, byte publicKey[]); + + int ecdsaVerify(const byte message[], const byte signature[], const byte pubkey[]); + int ecSign(int slot, const byte message[], byte signature[]); + + int SHA256(const uint8_t *buffer, size_t size, uint8_t *digest); + + int readSlot(int slot, byte data[], int length); + int writeSlot(int slot, const byte data[], int length); + + inline int locked() { return 1; } + inline int lock() { return 1; } + + inline int writeConfiguration(const byte config[] = nullptr) { (void)config; return 1; } + + +private: + +}; + +extern SoftwareATSEClass SATSE; + +#endif From a8f7363e324c521d93f715f785d34fbf3228dc5b Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 31 Jan 2024 16:45:41 +0100 Subject: [PATCH 2/7] SATSE examples: add message about minimum required firmware version --- .../SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino | 1 + .../SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino | 1 + .../examples/SATSERandomNumber/SATSERandomNumber.ino | 1 + .../examples/SATSESerialNumber/SATSESerialNumber.ino | 1 + .../examples/SATSESignAndVerify/SATSESignAndVerify.ino | 1 + 5 files changed, 5 insertions(+) diff --git a/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino b/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino index 4abf4cff7..169dc3061 100644 --- a/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino +++ b/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino @@ -53,6 +53,7 @@ void setup() { if (!SATSE.begin()) { Serial.println("Failed to communicate with Software Secure Element!"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); while (1); } diff --git a/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino b/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino index 0ae22552e..fe33374da 100644 --- a/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino +++ b/libraries/SoftwareATSE/examples/SATSEPrivateKey/SATSEPrivateKey.ino @@ -28,6 +28,7 @@ void setup() { if (!SATSE.begin()) { Serial.println("Failed to communicate with Software Secure Element!"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); while (1); } diff --git a/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino b/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino index 2e5f9197e..fca8f40ad 100644 --- a/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino +++ b/libraries/SoftwareATSE/examples/SATSERandomNumber/SATSERandomNumber.ino @@ -16,6 +16,7 @@ void setup() { if (!SATSE.begin()) { Serial.println("Failed to communicate with Software Secure Element!"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); while (1); } } diff --git a/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino b/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino index 243e327af..57a454587 100644 --- a/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino +++ b/libraries/SoftwareATSE/examples/SATSESerialNumber/SATSESerialNumber.ino @@ -16,6 +16,7 @@ void setup() { if (!SATSE.begin()) { Serial.println("Failed to communicate with Software Secure Element!"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); while (1); } } diff --git a/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino b/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino index 45c8f61c4..25ce45f12 100644 --- a/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino +++ b/libraries/SoftwareATSE/examples/SATSESignAndVerify/SATSESignAndVerify.ino @@ -32,6 +32,7 @@ void setup() { if (!SATSE.begin()) { Serial.println("Failed to communicate with Software Secure Element!"); + Serial.println("Make sure your WiFi firmware version is greater than 0.3.0"); while (1); } From 779f1b3264376e90c371402cc529ffd131803c2b Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 1 Feb 2024 11:00:28 +0100 Subject: [PATCH 3/7] SATSE: add functions description --- libraries/SoftwareATSE/src/SoftwareATSE.h | 88 +++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/libraries/SoftwareATSE/src/SoftwareATSE.h b/libraries/SoftwareATSE/src/SoftwareATSE.h index d1f1502da..333b03136 100644 --- a/libraries/SoftwareATSE/src/SoftwareATSE.h +++ b/libraries/SoftwareATSE/src/SoftwareATSE.h @@ -38,15 +38,103 @@ class SoftwareATSEClass long random(long max); int random(byte data[], size_t length); + /** generatePrivateKey + * + * Create a new ECCurve_NIST_P256 keypair and stores it in the WiFi chip NVS. + * Public key X Y values will be available inside publicKey buffer that should + * be at least 64 bytes long. + * + * | Public key X Y values (64 bytes) | + * + * @param[in] slot objectID where to store the private key + * @param[out] publicKey Buffer containing the public key X Y values + * + * @return 0 on Failure 1 on Success + */ int generatePrivateKey(int slot, byte publicKey[]); + + /** generatePublicKey + * + * Reads ECCurve_NIST_P256 public key from KeyID. Public key X Y values will be available + * inside publicKey buffer that should be at least 64 bytes long. + * + * | Public key X Y values (64 bytes) | + * + * @param[in] slot objectID where is stored the keypair + * @param[out] pubkey Buffer containing the public key X Y values + * + * @return 0 on Failure 1 on Success + */ int generatePublicKey(int slot, byte publicKey[]); + /** ecdsaVerify + * + * Verify ECDSA signature using public key. + * + * Input SHA256 + * ? Match ? + * Signature -> public Key -> Original SHA256 + * + * @param[in] message Input SHA256 used to compute the signature 32 bytes + * @param[in] sig Input buffer containint the signature R S values 64bytes + * @param[in] pubkey Public key X Y values 64bytes + * + * @return 0 on Failure (Not match) 1 on Success (Match) + */ int ecdsaVerify(const byte message[], const byte signature[], const byte pubkey[]); + + /** ecSign + * + * Computes ECDSA signature using key stored in KeyID SE050 object. + * Output signature buffer is filled with the signature R S values + * and should be at least 64 bytes long: + * + * | R values 32 bytes | S values 32 bytes | + * + * SHA256 -> private Key -> Signature + * + * @param[in] slot object ID containing the key + * @param[in] message Input SHA256 used to compute the signature 32 bytes + * @param[out] signature Output buffer containint the signature 64 bytes + * + * @return 0 on Failure 1 on Success + */ int ecSign(int slot, const byte message[], byte signature[]); + /** SHA256 + * + * One-shot SHA256 + * + * @param[in] buffer Input data buffer + * @param[in] size Input data length + * @param[out] digest Output buffer should be at least 32 bytes long + * + * @return 0 on Failure 1 on Success + */ int SHA256(const uint8_t *buffer, size_t size, uint8_t *digest); + /** readSlot + * + * Reads binary data from Software AT Secure Element object. + * + * @param[in] slot object ID containing data + * @param[out] data Output data buffer + * @param[in] length Number of bytes to read + * + * @return 0 on Failure 1 on Success + */ int readSlot(int slot, byte data[], int length); + + /** writeSlot + * + * Writes binary data into Software AT Secure Element object. + * + * @param[in] slot object ID + * @param[in] data Input data buffer + * @param[in] length Number of bytes to write + * + * @return 0 on Failure 1 on Success + */ int writeSlot(int slot, const byte data[], int length); inline int locked() { return 1; } From 163b316092e88ba219c1b5250142f041d779bf71 Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 1 Feb 2024 11:01:26 +0100 Subject: [PATCH 4/7] SATSE: Add separate function to configure debug --- libraries/SoftwareATSE/src/SoftwareATSE.cpp | 6 +++++- libraries/SoftwareATSE/src/SoftwareATSE.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/SoftwareATSE/src/SoftwareATSE.cpp b/libraries/SoftwareATSE/src/SoftwareATSE.cpp index 4f879dac6..19c0d9a0c 100644 --- a/libraries/SoftwareATSE/src/SoftwareATSE.cpp +++ b/libraries/SoftwareATSE/src/SoftwareATSE.cpp @@ -34,7 +34,6 @@ int SoftwareATSEClass::begin(const char * name, bool readOnly, const char* parti { string res = ""; modem.begin(); - modem.debug(Serial,2); if (name != nullptr && strlen(name) > 0) { if (modem.write(string(PROMPT(_SOFTSE_BEGIN)), res, "%s%s,%d,%s\r\n", CMD_WRITE(_SOFTSE_BEGIN), name, readOnly, partition_label != NULL ? partition_label : "")) { return atoi(res.c_str()); @@ -49,6 +48,11 @@ void SoftwareATSEClass::end() modem.write(string(PROMPT(_SOFTSE_END)), res, "%s", CMD(_SOFTSE_END)); } +void SoftwareATSEClass::debug(Stream &u, uint8_t level) +{ + modem.debug(u,level); +} + String SoftwareATSEClass::serialNumber() { string res = ""; diff --git a/libraries/SoftwareATSE/src/SoftwareATSE.h b/libraries/SoftwareATSE/src/SoftwareATSE.h index 333b03136..030d2fdb8 100644 --- a/libraries/SoftwareATSE/src/SoftwareATSE.h +++ b/libraries/SoftwareATSE/src/SoftwareATSE.h @@ -32,6 +32,8 @@ class SoftwareATSEClass int begin(const char * name = "se", bool readOnly = false, const char* partition_label = NULL); void end(); + void debug(Stream &u, uint8_t level = 0); + String serialNumber(); long random(long min, long max); From 6d7ca0e8cd7252d2ca40e8120414a90b64243511 Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 1 Feb 2024 11:03:30 +0100 Subject: [PATCH 5/7] SATSE: Fix return values according function description Also: - Remove unnecessary type casts - Use constexpr instead of numbers --- libraries/SoftwareATSE/src/SoftwareATSE.cpp | 53 +++++++++++---------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/libraries/SoftwareATSE/src/SoftwareATSE.cpp b/libraries/SoftwareATSE/src/SoftwareATSE.cpp index 19c0d9a0c..43990aee3 100644 --- a/libraries/SoftwareATSE/src/SoftwareATSE.cpp +++ b/libraries/SoftwareATSE/src/SoftwareATSE.cpp @@ -22,6 +22,10 @@ using namespace std; +static uint8_t constexpr SATSE_SHA256_LENGTH = 32; +static uint8_t constexpr SATSE_EC256_SIGNATURE_LENGTH = 64; +static uint8_t constexpr SATSE_EC256_PUB_KEY_LENGTH = 64; + SoftwareATSEClass::SoftwareATSEClass() { } @@ -124,9 +128,9 @@ int SoftwareATSEClass::generatePrivateKey(int keyID, byte publicKey[]) if (publicKey != nullptr) { modem.read_using_size(); if (modem.write(string(PROMPT(_SOFTSE_PRI_KEY)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_PRI_KEY), keyID)) { - if (res.size() >= 64) { - memcpy(publicKey, (uint8_t*)&res[0], 64); - return 64; + if (res.size() == SATSE_EC256_PUB_KEY_LENGTH) { + memcpy(publicKey, (uint8_t*)&res[0], SATSE_EC256_PUB_KEY_LENGTH); + return 1; } } } @@ -139,9 +143,9 @@ int SoftwareATSEClass::generatePublicKey(int keyID, byte publicKey[]) if (publicKey != nullptr) { modem.read_using_size(); if (modem.write(string(PROMPT(_SOFTSE_PUB_KEY)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_PUB_KEY), keyID)) { - if (res.size() >= 64) { - memcpy(publicKey, (uint8_t*)&res[0], 64); - return 64; + if (res.size() == SATSE_EC256_PUB_KEY_LENGTH) { + memcpy(publicKey, (uint8_t*)&res[0], SATSE_EC256_PUB_KEY_LENGTH); + return 1; } } } @@ -152,8 +156,8 @@ int SoftwareATSEClass::ecSign(int slot, const byte message[], byte signature[]) { string res = ""; if ( message != nullptr) { - modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), 32); - if(!modem.passthrough((uint8_t *)message, 32)) { + modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), SATSE_SHA256_LENGTH); + if(!modem.passthrough(message, SATSE_SHA256_LENGTH)) { return 0; } } @@ -161,9 +165,9 @@ int SoftwareATSEClass::ecSign(int slot, const byte message[], byte signature[]) if (signature != nullptr) { modem.read_using_size(); if (modem.write(string(PROMPT(_SOFTSE_SIGN_GET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_SIGN_GET), slot)) { - if (res.size() == 64) { - memcpy(signature, (uint8_t*)&res[0], 64); - return 64; + if (res.size() == SATSE_EC256_SIGNATURE_LENGTH) { + memcpy(signature, (uint8_t*)&res[0], SATSE_EC256_SIGNATURE_LENGTH); + return 1; } } } @@ -174,12 +178,13 @@ int SoftwareATSEClass::ecdsaVerify(const byte message[], const byte signature[], { string res = ""; if ( message != nullptr && signature!= nullptr) { - byte tmp[256]; - memcpy(tmp, message,32); - memcpy(&tmp[32], signature, 64); - memcpy(&tmp[32+64], pubkey, 64); - modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), 32+64+64); - if(!modem.passthrough((uint8_t *)tmp, 32+64+64)) { + static const byte len = SATSE_SHA256_LENGTH + SATSE_EC256_SIGNATURE_LENGTH + SATSE_EC256_PUB_KEY_LENGTH; + byte tmp[len]; + memcpy(tmp, message, SATSE_SHA256_LENGTH); + memcpy(&tmp[SATSE_SHA256_LENGTH], signature, SATSE_EC256_SIGNATURE_LENGTH); + memcpy(&tmp[SATSE_SHA256_LENGTH + SATSE_EC256_SIGNATURE_LENGTH], pubkey, SATSE_EC256_PUB_KEY_LENGTH); + modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), len); + if(!modem.passthrough(tmp, len)) { return 0; } } @@ -196,7 +201,7 @@ int SoftwareATSEClass::SHA256(const uint8_t *buffer, size_t size, uint8_t *diges string res = ""; if ( buffer != nullptr) { modem.write_nowait(string(PROMPT(_SOFTSE_S_V_BUF_SET)), res, "%s%d\r\n", CMD_WRITE(_SOFTSE_S_V_BUF_SET), size); - if(!modem.passthrough((uint8_t *)buffer, size)) { + if(!modem.passthrough(buffer, size)) { return 0; } } @@ -204,9 +209,9 @@ int SoftwareATSEClass::SHA256(const uint8_t *buffer, size_t size, uint8_t *diges if (digest != nullptr) { modem.read_using_size(); if (modem.write(string(PROMPT(_SOFTSE_SHA256_GET)), res, "%s", CMD(_SOFTSE_SHA256_GET))) { - if (res.size() == 32) { - memcpy(digest, (uint8_t*)&res[0], 32); - return 32; + if (res.size() == SATSE_SHA256_LENGTH) { + memcpy(digest, (uint8_t*)&res[0], SATSE_SHA256_LENGTH); + return 1; } } } @@ -222,7 +227,7 @@ int SoftwareATSEClass::readSlot(int slot, byte data[], int length) if (res.size()) { int len = res.size() > length ? length : res.size(); memcpy(data, (uint8_t*)&res[0], len); - return len; + return 1; } } } @@ -234,8 +239,8 @@ int SoftwareATSEClass::writeSlot(int slot, const byte data[], int length) string res = ""; if ( data != nullptr && length > 0) { modem.write_nowait(string(PROMPT(_SOFTSE_WRITE_SLOT)), res, "%s%d,%d\r\n", CMD_WRITE(_SOFTSE_WRITE_SLOT), slot, length); - if(modem.passthrough((uint8_t *)data, length)) { - return length; + if(modem.passthrough(data, length)) { + return 1; } } return 0; From 777dfc7483e20cd2673e57ec80399f735ee7d4fa Mon Sep 17 00:00:00 2001 From: pennam Date: Thu, 1 Feb 2024 12:17:04 +0100 Subject: [PATCH 6/7] SATSE examples: compute certificate length from payload --- .../examples/SATSECertificate/SATSECertificate.ino | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino b/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino index 169dc3061..54c54e82e 100644 --- a/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino +++ b/libraries/SoftwareATSE/examples/SATSECertificate/SATSECertificate.ino @@ -67,10 +67,11 @@ void setup() { } byte buf[512]; - int len = 0; + int ret = 0; - if((len = SATSE.readSlot(certId, buf, sizeof(buf))) > 0) { + if((ret = SATSE.readSlot(certId, buf, sizeof(buf))) > 0) { Serial.print("Readback data is: "); + int len = (buf[2] << 8) + buf[3] + 4; printBufferHex(buf, len); } else { Serial.println("Failed to read data"); From a50ecd035576f63bc4fc9f054871810705753c8e Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 14 Feb 2024 16:32:48 +0100 Subject: [PATCH 7/7] SoftwareATSE: add library examples to compile examples workflow --- .github/workflows/compile-examples.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 1de1bc4c6..4d813e52d 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -95,6 +95,7 @@ jobs: - libraries/WiFiS3 - libraries/OTAUpdate - libraries/OPAMP + - libraries/SoftwareATSE - board: fqbn: "arduino-git:renesas:minima" additional-sketch-paths: |