From e9c5059ac938d5380cbae175177bf460f36acfb5 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 9 Feb 2022 17:10:36 +0100 Subject: [PATCH 1/5] ArduinoIoTCloudCertClass: Add Generic cert class to produce ArduinoIoTCloud certificates --- src/tls/utility/Cert.cpp | 912 +++++++++++++++++++++++++++++++++++++++ src/tls/utility/Cert.h | 189 ++++++++ 2 files changed, 1101 insertions(+) create mode 100644 src/tls/utility/Cert.cpp create mode 100644 src/tls/utility/Cert.h diff --git a/src/tls/utility/Cert.cpp b/src/tls/utility/Cert.cpp new file mode 100644 index 000000000..39ed76253 --- /dev/null +++ b/src/tls/utility/Cert.cpp @@ -0,0 +1,912 @@ +/* + This file is part of the ArduinoECCX08 library. + Copyright (c) 2019 Arduino SA. All rights 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 Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include + +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) + +#include "Cert.h" + +/****************************************************************************** + * DEFINE + ******************************************************************************/ + +#define ASN1_INTEGER 0x02 +#define ASN1_BIT_STRING 0x03 +#define ASN1_NULL 0x05 +#define ASN1_OBJECT_IDENTIFIER 0x06 +#define ASN1_PRINTABLE_STRING 0x13 +#define ASN1_SEQUENCE 0x30 +#define ASN1_SET 0x31 + +/****************************************************************************** + * LOCAL MODULE FUNCTIONS + ******************************************************************************/ + +static String base64Encode(const byte in[], unsigned int length, const char* prefix, const char* suffix) { + static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + + int b; + String out; + + int reserveLength = 4 * ((length + 2) / 3) + ((length / 3 * 4) / 76) + strlen(prefix) + strlen(suffix); + out.reserve(reserveLength); + + if (prefix) { + out += prefix; + } + + for (unsigned int i = 0; i < length; i += 3) { + if (i > 0 && (i / 3 * 4) % 76 == 0) { + out += '\n'; + } + + b = (in[i] & 0xFC) >> 2; + out += CODES[b]; + + b = (in[i] & 0x03) << 4; + if (i + 1 < length) { + b |= (in[i + 1] & 0xF0) >> 4; + out += CODES[b]; + b = (in[i + 1] & 0x0F) << 2; + if (i + 2 < length) { + b |= (in[i + 2] & 0xC0) >> 6; + out += CODES[b]; + b = in[i + 2] & 0x3F; + out += CODES[b]; + } else { + out += CODES[b]; + out += '='; + } + } else { + out += CODES[b]; + out += "=="; + } + } + + if (suffix) { + out += suffix; + } + + return out; +} + +/****************************************************************************** + * CTOR/DTOR + ******************************************************************************/ + +ArduinoIoTCloudCertClass::ArduinoIoTCloudCertClass() +: _certBuffer(NULL) +, _certBufferLen(0) +{ + +} + +ArduinoIoTCloudCertClass::~ArduinoIoTCloudCertClass() +{ + if (_certBuffer) { + free(_certBuffer); + _certBuffer = NULL; + } +} + +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + +int ArduinoIoTCloudCertClass::begin() +{ + memset(_compressedCert.data, 0x00, CERT_COMPRESSED_CERT_LENGTH); + memset(_publicKey, 0x00, CERT_PUBLIC_KEY_LENGTH); + return 1; +} + +int ArduinoIoTCloudCertClass::buildCSR() +{ + int csrInfoLen = CSRInfoLength(); + int subjectLen = issuerOrSubjectLength(_subjectData); + + _certBufferLen = getCSRSize(); + _certBuffer = (byte*)malloc(_certBufferLen); + + if (_certBuffer == nullptr) { + return 0; + } + + byte* out = _certBuffer; + + // header + out += appendSequenceHeader(csrInfoLen, out); + + // version + out += appendVersion(0x00, out); + + // subject + out += appendSequenceHeader(subjectLen, out); + out += appendIssuerOrSubject(_subjectData, out); + + // public key + out += appendPublicKey(_publicKey, out); + + // terminator + *out++ = 0xa0; + *out++ = 0x00; + + return 1; +} + +int ArduinoIoTCloudCertClass::signCSR(byte * signature) +{ + /* copy old certbuffer in a temp buffer */ + byte* tempBuffer = (byte*)malloc(_certBufferLen); + + if (tempBuffer == nullptr) { + return 0; + } + + memcpy(tempBuffer, _certBuffer, _certBufferLen); + + _certBufferLen = getCSRSignedSize(signature); + _certBuffer = (byte*)realloc(_certBuffer, _certBufferLen); + + if (_certBuffer == nullptr) { + return 0; + } + + byte* out = _certBuffer; + + // header + out += appendSequenceHeader(getCSRSize() + signatureLength(signature), out); + + // info + memcpy(out, tempBuffer, getCSRSize()); + free(tempBuffer); + out += getCSRSize(); + + // signature + out += appendSignature(signature, out); + + return 1; +} + +String ArduinoIoTCloudCertClass::getCSRPEM() +{ + return base64Encode(_certBuffer, _certBufferLen, "-----BEGIN CERTIFICATE REQUEST-----\n", "\n-----END CERTIFICATE REQUEST-----\n"); +} + +int ArduinoIoTCloudCertClass::buildCert() +{ + _certBufferLen = getCertSize(); + _certBuffer = (byte*)malloc(_certBufferLen); + + if (_certBuffer == nullptr) { + return 0; + } + + uint8_t* out = _certBuffer; + + int certInfoLen = certInfoLength(); + + // header + out += appendSequenceHeader(certInfoLen, out); + + // version + *out++ = 0xA0; + *out++ = 0x03; + *out++ = 0x02; + *out++ = 0x01; + *out++ = 0x02; + + // serial number + out += appendSerialNumber(_compressedCert.slot.two.values.serialNumber, CERT_SERIAL_NUMBER_LENGTH, out); + + // signature type + out += appendEcdsaWithSHA256(out); + + // issuer + int issuerDataLen = issuerOrSubjectLength(_issuerData); + out += appendSequenceHeader(issuerDataLen, out); + out += appendIssuerOrSubject(_issuerData, out); + + // dates + DateInfo dateData; + getDateFromCompressedData(dateData); + + *out++ = ASN1_SEQUENCE; + *out++ = 30 + ((dateData.issueYear > 2049) ? 2 : 0) + (((dateData.issueYear + dateData.expireYears) > 2049) ? 2 : 0); + out += appendDate(dateData.issueYear, dateData.issueMonth, dateData.issueDay, dateData.issueHour, 0, 0, out); + out += appendDate(dateData.issueYear + dateData.expireYears, dateData.issueMonth, dateData.issueDay, dateData.issueHour, 0, 0, out); + + // subject + int subjectDataLen = issuerOrSubjectLength(_subjectData); + out += appendSequenceHeader(subjectDataLen, out); + out += appendIssuerOrSubject(_subjectData, out); + + // public key + out += appendPublicKey(_publicKey, out); + + int authorityKeyIdLen = authorityKeyIdLength(_compressedCert.slot.two.values.authorityKeyId, CERT_AUTHORITY_KEY_ID_LENGTH); + if (authorityKeyIdLen) + { + out += appendAuthorityKeyId(_compressedCert.slot.two.values.authorityKeyId, CERT_AUTHORITY_KEY_ID_LENGTH, out); + } + else + { + // null sequence + *out++ = 0xA3; + *out++ = 0x02; + *out++ = 0x30; + *out++ = 0x00; + } + + return 1; +} + +int ArduinoIoTCloudCertClass::signCert(const byte * signature) +{ + /* copy old certbuffer in a temp buffer */ + byte* tempBuffer = (byte*)malloc(_certBufferLen); + + if (tempBuffer == nullptr) { + return 0; + } + + memcpy(tempBuffer, _certBuffer, _certBufferLen); + + _certBufferLen = getCertSignedSize(signature); + _certBuffer = (byte*)realloc(_certBuffer, _certBufferLen); + + if (_certBuffer == nullptr) { + return 0; + } + + byte* out = _certBuffer; + + // header + out +=appendSequenceHeader(getCertSize() + signatureLength(signature), out); + + // info + memcpy(out, tempBuffer, getCertSize()); + free(tempBuffer); + out += getCertSize(); + + // signature + out += appendSignature(signature, out); + + return 1; +} + +int ArduinoIoTCloudCertClass::importCert(const byte certDER[], size_t derLen) +{ + _certBufferLen = derLen; + _certBuffer = (byte*)malloc(_certBufferLen); + + if (_certBuffer == nullptr) { + return 0; + } + + memcpy(_certBuffer, certDER, _certBufferLen); + + return 1; +} + +int ArduinoIoTCloudCertClass::signCert() +{ + return signCert(_compressedCert.slot.one.values.signature); +} + +String ArduinoIoTCloudCertClass::getCertPEM() +{ + return base64Encode(_certBuffer, _certBufferLen, "-----BEGIN CERTIFICATE-----\n", "\n-----END CERTIFICATE-----\n"); +} + +void ArduinoIoTCloudCertClass::getDateFromCompressedData(DateInfo& date) { + date.issueYear = (_compressedCert.slot.one.values.dates[0] >> 3) + 2000; + date.issueMonth = ((_compressedCert.slot.one.values.dates[0] & 0x07) << 1) | (_compressedCert.slot.one.values.dates[1] >> 7); + date.issueDay = (_compressedCert.slot.one.values.dates[1] & 0x7c) >> 2; + date.issueHour = ((_compressedCert.slot.one.values.dates[1] & 0x03) << 3) | (_compressedCert.slot.one.values.dates[2] >> 5); + date.expireYears = (_compressedCert.slot.one.values.dates[2] & 0x1f); +} + +void ArduinoIoTCloudCertClass::setIssueYear(int issueYear) { + _compressedCert.slot.one.values.dates[0] &= 0x07; + _compressedCert.slot.one.values.dates[0] |= (issueYear - 2000) << 3; +} + +void ArduinoIoTCloudCertClass::setIssueMonth(int issueMonth) { + _compressedCert.slot.one.values.dates[0] &= 0xf8; + _compressedCert.slot.one.values.dates[0] |= issueMonth >> 1; + + _compressedCert.slot.one.values.dates[1] &= 0x7f; + _compressedCert.slot.one.values.dates[1] |= issueMonth << 7; +} + +void ArduinoIoTCloudCertClass::setIssueDay(int issueDay) { + _compressedCert.slot.one.values.dates[1] &= 0x83; + _compressedCert.slot.one.values.dates[1] |= issueDay << 2; +} + +void ArduinoIoTCloudCertClass::setIssueHour(int issueHour) { + _compressedCert.slot.one.values.dates[2] &= 0x1f; + _compressedCert.slot.one.values.dates[2] |= issueHour << 5; + + _compressedCert.slot.one.values.dates[1] &= 0xfc; + _compressedCert.slot.one.values.dates[1] |= issueHour >> 3; +} + +void ArduinoIoTCloudCertClass::setExpireYears(int expireYears) { + _compressedCert.slot.one.values.dates[2] &= 0xe0; + _compressedCert.slot.one.values.dates[2] |= expireYears; +} + +int ArduinoIoTCloudCertClass::setSerialNumber(const uint8_t serialNumber[], int serialNumberLen) { + if (serialNumberLen == CERT_SERIAL_NUMBER_LENGTH) { + memcpy(_compressedCert.slot.two.values.serialNumber, serialNumber, CERT_SERIAL_NUMBER_LENGTH); + return 1; + } + return 0; +} + +int ArduinoIoTCloudCertClass::setAuthorityKeyId(const uint8_t authorityKeyId[], int authorityKeyIdLen) { + if (authorityKeyIdLen == CERT_AUTHORITY_KEY_ID_LENGTH) { + memcpy(_compressedCert.slot.two.values.authorityKeyId, authorityKeyId, CERT_AUTHORITY_KEY_ID_LENGTH); + return 1; + } + return 0; +} + +int ArduinoIoTCloudCertClass::setPublicKey(const byte* publicKey, int publicKeyLen) { + if (publicKeyLen == CERT_PUBLIC_KEY_LENGTH) { + memcpy(_publicKey, publicKey, CERT_PUBLIC_KEY_LENGTH); + return 1; + } + return 0; +} + +int ArduinoIoTCloudCertClass::setSignature(const byte* signature, int signatureLen) { + if (signatureLen == CERT_SIGNATURE_LENGTH) { + memcpy(_compressedCert.slot.one.values.signature, signature, CERT_SIGNATURE_LENGTH); + return 1; + } + return 0; +} + +/****************************************************************************** + * PRIVATE MEMBER FUNCTIONS + ******************************************************************************/ + +int ArduinoIoTCloudCertClass::versionLength() +{ + return 3; +} + +int ArduinoIoTCloudCertClass::issuerOrSubjectLength(const CertInfo& issuerOrSubjectData) +{ + int length = 0; + int countryNameLength = issuerOrSubjectData.countryName.length(); + int stateProvinceNameLength = issuerOrSubjectData.stateProvinceName.length(); + int localityNameLength = issuerOrSubjectData.localityName.length(); + int organizationNameLength = issuerOrSubjectData.organizationName.length(); + int organizationalUnitNameLength = issuerOrSubjectData.organizationalUnitName.length(); + int commonNameLength = issuerOrSubjectData.commonName.length(); + + if (countryNameLength) { + length += (11 + countryNameLength); + } + + if (stateProvinceNameLength) { + length += (11 + stateProvinceNameLength); + } + + if (localityNameLength) { + length += (11 + localityNameLength); + } + + if (organizationNameLength) { + length += (11 + organizationNameLength); + } + + if (organizationalUnitNameLength) { + length += (11 + organizationalUnitNameLength); + } + + if (commonNameLength) { + length += (11 + commonNameLength); + } + + return length; +} + +int ArduinoIoTCloudCertClass::sequenceHeaderLength(int length) +{ + if (length > 255) { + return 4; + } else if (length > 127) { + return 3; + } else { + return 2; + } +} + +int ArduinoIoTCloudCertClass::publicKeyLength() +{ + return (2 + 2 + 9 + 10 + 4 + 64); +} + +int ArduinoIoTCloudCertClass::signatureLength(const byte signature[]) +{ + const byte* r = &signature[0]; + const byte* s = &signature[32]; + + int rLength = 32; + int sLength = 32; + + while (*r == 0x00 && rLength) { + r++; + rLength--; + } + + if (*r & 0x80) { + rLength++; + } + + while (*s == 0x00 && sLength) { + s++; + sLength--; + } + + if (*s & 0x80) { + sLength++; + } + + return (21 + rLength + sLength); +} + +int ArduinoIoTCloudCertClass::serialNumberLength(const byte serialNumber[], int length) +{ + while (*serialNumber == 0 && length) { + serialNumber++; + length--; + } + + if (*serialNumber & 0x80) { + length++; + } + + return (2 + length); +} + +int ArduinoIoTCloudCertClass::authorityKeyIdLength(const byte authorityKeyId[], int length) { + bool set = false; + + // check if the authority key identifier is non-zero + for (int i = 0; i < length; i++) { + if (authorityKeyId[i] != 0) { + set = true; + break; + } + } + + return (set ? (length + 17) : 0); +} + +int ArduinoIoTCloudCertClass::CSRInfoLength() +{ + int versionLen = versionLength(); + int subjectLen = issuerOrSubjectLength(_subjectData); + int subjectHeaderLen = sequenceHeaderLength(subjectLen); + int publicKeyLen = publicKeyLength(); + + int csrInfoLen = versionLen + subjectHeaderLen + subjectLen + publicKeyLen + 2; + + return csrInfoLen; +} + +int ArduinoIoTCloudCertClass::getCSRSize() +{ + int csrInfoLen = CSRInfoLength(); + int csrInfoHeaderLen = sequenceHeaderLength(csrInfoLen); + + return (csrInfoLen + csrInfoHeaderLen); +} + +int ArduinoIoTCloudCertClass::getCSRSignedSize(byte * signature) +{ + int signatureLen = signatureLength(signature); + int csrLen = getCSRSize() + signatureLen; + return sequenceHeaderLength(csrLen) + csrLen; +} + +int ArduinoIoTCloudCertClass::certInfoLength() +{ + int datesSizeLen = 30; + DateInfo dates; + + getDateFromCompressedData(dates); + + if (dates.issueYear > 2049) { + // two more bytes for GeneralizedTime + datesSizeLen += 2; + } + + if ((dates.issueYear + dates.expireYears) > 2049) { + // two more bytes for GeneralizedTime + datesSizeLen += 2; + } + + int serialNumberLen = serialNumberLength(_compressedCert.slot.two.values.serialNumber, CERT_SERIAL_NUMBER_LENGTH); + + int issuerLen = issuerOrSubjectLength(_issuerData); + + int issuerHeaderLen = sequenceHeaderLength(issuerLen); + + int subjectLen = issuerOrSubjectLength(_subjectData); + + int subjectHeaderLen = sequenceHeaderLength(subjectLen); + + int publicKeyLen = publicKeyLength(); + + int certInfoLen = 5 + serialNumberLen + 12 + issuerHeaderLen + issuerLen + (datesSizeLen + 2) + + subjectHeaderLen + subjectLen + publicKeyLen; + + int authorityKeyIdLen = authorityKeyIdLength(_compressedCert.slot.two.values.authorityKeyId, CERT_AUTHORITY_KEY_ID_LENGTH); + + if (authorityKeyIdLen) + { + certInfoLen += authorityKeyIdLen; + } + else + { + certInfoLen += 4; + } + + return certInfoLen; +} + +int ArduinoIoTCloudCertClass::getCertSize() +{ + int certInfoLen = certInfoLength(); + int certInfoHeaderLen = sequenceHeaderLength(certInfoLen); + + return (certInfoLen + certInfoHeaderLen); +} + +int ArduinoIoTCloudCertClass::getCertSignedSize(const byte * signature) +{ + int signatureLen = signatureLength(signature); + int certLen = getCertSize() + signatureLen; + return sequenceHeaderLength(certLen) + certLen; +} + +int ArduinoIoTCloudCertClass::appendSequenceHeader(int length, byte out[]) +{ + *out++ = ASN1_SEQUENCE; + if (length > 255) { + *out++ = 0x82; + *out++ = (length >> 8) & 0xff; + } else if (length > 127) { + *out++ = 0x81; + } + *out++ = (length) & 0xff; + + if (length > 255) { + return 4; + } else if (length > 127) { + return 3; + } else { + return 2; + } +} + +int ArduinoIoTCloudCertClass::appendVersion(int version, byte out[]) +{ + out[0] = ASN1_INTEGER; + out[1] = 0x01; + out[2] = version; + + return versionLength(); +} + +int ArduinoIoTCloudCertClass::appendName(const String& name, int type, byte out[]) +{ + int nameLength = name.length(); + + *out++ = ASN1_SET; + *out++ = nameLength + 9; + + *out++ = ASN1_SEQUENCE; + *out++ = nameLength + 7; + + *out++ = ASN1_OBJECT_IDENTIFIER; + *out++ = 0x03; + *out++ = 0x55; + *out++ = 0x04; + *out++ = type; + + *out++ = ASN1_PRINTABLE_STRING; + *out++ = nameLength; + memcpy(out, name.c_str(), nameLength); + + return (nameLength + 11); +} + +int ArduinoIoTCloudCertClass::appendIssuerOrSubject(const CertInfo& issuerOrSubjectData, byte out[]) +{ + if (issuerOrSubjectData.countryName.length() > 0) { + out += appendName(issuerOrSubjectData.countryName, 0x06, out); + } + + if (issuerOrSubjectData.stateProvinceName.length() > 0) { + out += appendName(issuerOrSubjectData.stateProvinceName, 0x08, out); + } + + if (issuerOrSubjectData.localityName.length() > 0) { + out += appendName(issuerOrSubjectData.localityName, 0x07, out); + } + + if (issuerOrSubjectData.organizationName.length() > 0) { + out += appendName(issuerOrSubjectData.organizationName, 0x0a, out); + } + + if (issuerOrSubjectData.organizationalUnitName.length() > 0) { + out += appendName(issuerOrSubjectData.organizationalUnitName, 0x0b, out); + } + + if (issuerOrSubjectData.commonName.length() > 0) { + out += appendName(issuerOrSubjectData.commonName, 0x03, out); + } + + return issuerOrSubjectLength(issuerOrSubjectData); +} + +int ArduinoIoTCloudCertClass::appendPublicKey(const byte publicKey[], byte out[]) +{ + int subjectPublicKeyDataLength = 2 + 9 + 10 + 4 + 64; + + // subject public key + *out++ = ASN1_SEQUENCE; + *out++ = (subjectPublicKeyDataLength) & 0xff; + + *out++ = ASN1_SEQUENCE; + *out++ = 0x13; + + // EC public key + *out++ = ASN1_OBJECT_IDENTIFIER; + *out++ = 0x07; + *out++ = 0x2a; + *out++ = 0x86; + *out++ = 0x48; + *out++ = 0xce; + *out++ = 0x3d; + *out++ = 0x02; + *out++ = 0x01; + + // PRIME 256 v1 + *out++ = ASN1_OBJECT_IDENTIFIER; + *out++ = 0x08; + *out++ = 0x2a; + *out++ = 0x86; + *out++ = 0x48; + *out++ = 0xce; + *out++ = 0x3d; + *out++ = 0x03; + *out++ = 0x01; + *out++ = 0x07; + + *out++ = 0x03; + *out++ = 0x42; + *out++ = 0x00; + *out++ = 0x04; + + memcpy(out, publicKey, 64); + + return publicKeyLength(); +} + +int ArduinoIoTCloudCertClass::appendSignature(const byte signature[], byte out[]) +{ + // signature algorithm + *out++ = ASN1_SEQUENCE; + *out++ = 0x0a; + *out++ = ASN1_OBJECT_IDENTIFIER; + *out++ = 0x08; + + // ECDSA with SHA256 + *out++ = 0x2a; + *out++ = 0x86; + *out++ = 0x48; + *out++ = 0xce; + *out++ = 0x3d; + *out++ = 0x04; + *out++ = 0x03; + *out++ = 0x02; + + const byte* r = &signature[0]; + const byte* s = &signature[32]; + + int rLength = 32; + int sLength = 32; + + while (*r == 0 && rLength) { + r++; + rLength--; + } + + while (*s == 0 && sLength) { + s++; + sLength--; + } + + if (*r & 0x80) { + rLength++; + } + + if (*s & 0x80) { + sLength++; + } + + *out++ = ASN1_BIT_STRING; + *out++ = (rLength + sLength + 7); + *out++ = 0; + + *out++ = ASN1_SEQUENCE; + *out++ = (rLength + sLength + 4); + + *out++ = ASN1_INTEGER; + *out++ = rLength; + if ((*r & 0x80) && rLength) { + *out++ = 0; + rLength--; + } + memcpy(out, r, rLength); + out += rLength; + + *out++ = ASN1_INTEGER; + *out++ = sLength; + if ((*s & 0x80) && sLength) { + *out++ = 0; + sLength--; + } + memcpy(out, s, sLength); + out += rLength; + + return signatureLength(signature); +} + +int ArduinoIoTCloudCertClass::appendSerialNumber(const byte serialNumber[], int length, byte out[]) +{ + while (*serialNumber == 0 && length) { + serialNumber++; + length--; + } + + if (*serialNumber & 0x80) { + length++; + } + + *out++ = ASN1_INTEGER; + *out++ = length; + + if (*serialNumber & 0x80) { + *out++ = 0x00; + length--; + } + + memcpy(out, serialNumber, length); + + if (*serialNumber & 0x80) { + length++; + } + + return (2 + length); +} + +int ArduinoIoTCloudCertClass::appendDate(int year, int month, int day, int hour, int minute, int second, byte out[]) +{ + bool useGeneralizedTime = (year > 2049); + + if (useGeneralizedTime) { + *out++ = 0x18; + *out++ = 0x0f; + *out++ = '0' + (year / 1000); + *out++ = '0' + ((year % 1000) / 100); + *out++ = '0' + ((year % 100) / 10); + *out++ = '0' + (year % 10); + } else { + year -= 2000; + + *out++ = 0x17; + *out++ = 0x0d; + *out++ = '0' + (year / 10); + *out++ = '0' + (year % 10); + } + *out++ = '0' + (month / 10); + *out++ = '0' + (month % 10); + *out++ = '0' + (day / 10); + *out++ = '0' + (day % 10); + *out++ = '0' + (hour / 10); + *out++ = '0' + (hour % 10); + *out++ = '0' + (minute / 10); + *out++ = '0' + (minute % 10); + *out++ = '0' + (second / 10); + *out++ = '0' + (second % 10); + *out++ = 0x5a; // UTC + + return (useGeneralizedTime ? 17 : 15); +} + +int ArduinoIoTCloudCertClass::appendEcdsaWithSHA256(byte out[]) +{ + *out++ = ASN1_SEQUENCE; + *out++ = 0x0A; + *out++ = ASN1_OBJECT_IDENTIFIER; + *out++ = 0x08; + *out++ = 0x2A; + *out++ = 0x86; + *out++ = 0x48; + *out++ = 0xCE; + *out++ = 0x3D; + *out++ = 0x04; + *out++ = 0x03; + *out++ = 0x02; + + return 12; +} + +int ArduinoIoTCloudCertClass::appendAuthorityKeyId(const byte authorityKeyId[], int length, byte out[]) { + // [3] + *out++ = 0xa3; + *out++ = 0x23; + + // sequence + *out++ = ASN1_SEQUENCE; + *out++ = 0x21; + + // sequence + *out++ = ASN1_SEQUENCE; + *out++ = 0x1f; + + // 2.5.29.35 authorityKeyIdentifier(X.509 extension) + *out++ = 0x06; + *out++ = 0x03; + *out++ = 0x55; + *out++ = 0x1d; + *out++ = 0x23; + + // octet string + *out++ = 0x04; + *out++ = 0x18; + + // sequence + *out++ = ASN1_SEQUENCE; + *out++ = 0x16; + + *out++ = 0x80; + *out++ = 0x14; + + memcpy(out, authorityKeyId, length); + + return length + 17; +} + +#endif /* (BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) */ diff --git a/src/tls/utility/Cert.h b/src/tls/utility/Cert.h new file mode 100644 index 000000000..944a2eac1 --- /dev/null +++ b/src/tls/utility/Cert.h @@ -0,0 +1,189 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2019 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +#ifndef ARDUINO_IOT_CLOUD_CERT_H +#define ARDUINO_IOT_CLOUD_CERT_H + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include + +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) + +/****************************************************************************** + * DEFINE + ******************************************************************************/ + +#define CERT_SERIAL_NUMBER_LENGTH 16 +#define CERT_AUTHORITY_KEY_ID_LENGTH 20 +#define CERT_PUBLIC_KEY_LENGTH 64 +#define CERT_SIGNATURE_LENGTH 64 +#define CERT_DATES_LENGTH 3 +#define CERT_COMPRESSED_CERT_LENGTH 144 +#define CERT_COMPRESSED_CERT_SLOT_LENGTH 72 + +#include + +class ArduinoIoTCloudCertClass { +public: + ArduinoIoTCloudCertClass(); + virtual ~ArduinoIoTCloudCertClass(); + + int begin(); + int end(); + + /* APIs used only for Certificate generation*/ + void setIssueYear(int issueYear); + void setIssueMonth(int issueMonth); + void setIssueDay(int issueDay); + void setIssueHour(int issueHour); + void setExpireYears(int expireYears); + int setSerialNumber(const uint8_t serialNumber[], int serialNumberLen); + int setAuthorityKeyId(const uint8_t authorityKeyId[], int authorityKeyIdLen); + + inline void setIssuerCountryName(const String& countryName) { _issuerData.countryName = countryName; } + inline void setIssuerStateProvinceName(const String& stateProvinceName) { _issuerData.stateProvinceName = stateProvinceName; } + inline void setIssuerLocalityName(const String& localityName) { _issuerData.localityName = localityName; } + inline void setIssuerOrganizationName(const String& organizationName) { _issuerData.organizationName = organizationName; } + inline void setIssuerOrganizationalUnitName(const String& organizationalUnitName) { _issuerData.organizationalUnitName = organizationalUnitName; } + inline void setIssuerCommonName(const String& commonName) { _issuerData.commonName = commonName; } + + /* APIs used for both CSR and Certificate generation */ + inline void setSubjectCountryName(const String& countryName) { _subjectData.countryName = countryName; } + inline void setSubjectStateProvinceName(const String& stateProvinceName) { _subjectData.stateProvinceName = stateProvinceName; } + inline void setSubjectLocalityName(const String& localityName) { _subjectData.localityName = localityName; } + inline void setSubjectOrganizationName(const String& organizationName) { _subjectData.organizationName = organizationName; } + inline void setSubjectOrganizationalUnitName(const String& organizationalUnitName) { _subjectData.organizationalUnitName = organizationalUnitName; } + inline void setSubjectCommonName(const String& commonName) { _subjectData.commonName = commonName; } + + int setPublicKey(const byte* publicKey, int publicKeyLen); + int setSignature(const byte* signature, int signatureLen); + + /* Get Buffer */ + inline byte* bytes() { return _certBuffer; } + inline int length() { return _certBufferLen; } + +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) + /* Get Data to create ECCX08 compressed cert */ + inline byte* compressedCertBytes() { return _compressedCert.data; } + inline int compressedCertLenght() {return CERT_COMPRESSED_CERT_LENGTH; } + inline byte* compressedCertSignatureAndDatesBytes() { return _compressedCert.slot.one.data; } + inline int compressedCertSignatureAndDatesLength() {return CERT_COMPRESSED_CERT_SLOT_LENGTH; } + inline byte* compressedCertSerialAndAuthorityKeyIdBytes() { return _compressedCert.slot.two.data; } + inline int compressedCertSerialAndAuthorityKeyIdLenght() {return CERT_COMPRESSED_CERT_SLOT_LENGTH; } +#endif + + /* Build CSR */ + int buildCSR(); + int signCSR(byte signature[]); + String getCSRPEM(); + + /* Build Certificate */ + int buildCert(); + int signCert(const byte signature[]); + int signCert(); + String getCertPEM(); + + /* Import DER buffer into CertClass*/ + int importCert(const byte certDER[], size_t derLen); + +private: + + struct CertInfo { + String countryName; + String stateProvinceName; + String localityName; + String organizationName; + String organizationalUnitName; + String commonName; + }_issuerData, _subjectData; + + struct DateInfo { + int issueYear; + int issueMonth; + int issueDay; + int issueHour; + int expireYears; + }; + + union SignatureAndDateUType { + struct __attribute__((__packed__)) SignatureAndDateType { + byte signature[CERT_SIGNATURE_LENGTH]; + byte dates[CERT_DATES_LENGTH]; + byte unused[5]; + } values; + byte data[CERT_COMPRESSED_CERT_SLOT_LENGTH]; + }; + + union SerialNumberAndAuthorityKeyIdUType { + struct __attribute__((__packed__)) SerialNumberAndAuthorityKeyIdType { + byte serialNumber[CERT_SERIAL_NUMBER_LENGTH]; + byte authorityKeyId[CERT_AUTHORITY_KEY_ID_LENGTH]; + byte unused[36]; + } values; + byte data[CERT_COMPRESSED_CERT_SLOT_LENGTH]; + }; + + union CompressedCertDataUType { + struct __attribute__((__packed__)) CompressedCertDataType { + SignatureAndDateUType one; + SerialNumberAndAuthorityKeyIdUType two; + }slot; + byte data[CERT_COMPRESSED_CERT_LENGTH]; + } _compressedCert; + + /* only raw EC X Y values 64 byte */ + byte _publicKey[CERT_PUBLIC_KEY_LENGTH]; + int _publicKeyLen; + + byte * _certBuffer; + int _certBufferLen; + + int versionLength(); + int issuerOrSubjectLength(const CertInfo& issuerOrSubjectData); + int sequenceHeaderLength(int length); + int publicKeyLength(); + int signatureLength(const byte signature[]); + int serialNumberLength(const byte serialNumber[], int length); + int authorityKeyIdLength(const byte authorityKeyId[], int length); + int CSRInfoLength(); + int getCSRSize(); + int getCSRSignedSize(byte signature[]); + int certInfoLength(); + int getCertSize(); + int getCertSignedSize(const byte signature[]); + + void getDateFromCompressedData(DateInfo& date); + + int appendSequenceHeader(int length, byte out[]); + int appendVersion(int version, byte out[]); + int appendName(const String& name, int type, byte out[]); + int appendIssuerOrSubject(const CertInfo& issuerOrSubjectData, byte out[]); + int appendPublicKey(const byte publicKey[], byte out[]); + int appendSignature(const byte signature[], byte out[]); + int appendSerialNumber(const byte serialNumber[], int length, byte out[]); + int appendDate(int year, int month, int day, int hour, int minute, int second, byte out[]); + int appendEcdsaWithSHA256(byte out[]); + int appendAuthorityKeyId(const byte authorityKeyId[], int length, byte out[]); + +}; + +#endif /* BOARD_HAS_ECCX08 || BOARD_HAS_OFFLOADED_ECCX08 */ + +#endif /* ARDUINO_IOT_CLOUD_CERT_H */ From 75bbfd3ee9f284be433b9eafe590ec5d2f947adc Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 9 Feb 2022 18:22:49 +0100 Subject: [PATCH 2/5] CryptoUtil: extend class in order to abstract hardware crypto and update ArduinoIoTCloudTCP --- src/ArduinoIoTCloudTCP.cpp | 16 +-- src/ArduinoIoTCloudTCP.h | 10 +- src/tls/utility/CryptoUtil.cpp | 174 ++++++++++++++++++++++++++++----- src/tls/utility/CryptoUtil.h | 29 +++--- 4 files changed, 183 insertions(+), 46 deletions(-) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index ed0e46cd4..00c416c87 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -204,36 +204,36 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress, #endif /* OTA_ENABLED */ #ifdef BOARD_HAS_OFFLOADED_ECCX08 - if (!ECCX08.begin()) + if (!_crypto.begin()) { - DEBUG_ERROR("ECCX08.begin() failed."); + DEBUG_ERROR("_crypto.begin() failed."); return 0; } - if (!CryptoUtil::readDeviceId(ECCX08, getDeviceId(), ECCX08Slot::DeviceId)) + if (!_crypto.readDeviceId(getDeviceId(), CryptoSlot::DeviceId)) { - DEBUG_ERROR("CryptoUtil::readDeviceId(...) failed."); + DEBUG_ERROR("_crypto.readDeviceId(...) failed."); return 0; } #endif #ifdef BOARD_HAS_ECCX08 - if (!ECCX08.begin()) + if (!_crypto.begin()) { DEBUG_ERROR("Cryptography processor failure. Make sure you have a compatible board."); return 0; } - if (!CryptoUtil::readDeviceId(ECCX08, getDeviceId(), ECCX08Slot::DeviceId)) + if (!_crypto.readDeviceId(getDeviceId(), CryptoSlot::DeviceId)) { DEBUG_ERROR("Cryptography processor read failure."); return 0; } - if (!CryptoUtil::reconstructCertificate(_eccx08_cert, getDeviceId(), ECCX08Slot::Key, ECCX08Slot::CompressedCertificate, ECCX08Slot::SerialNumberAndAuthorityKeyIdentifier)) + if (!_crypto.readCert(_cert, CryptoSlot::CompressedCertificate)) { DEBUG_ERROR("Cryptography certificate reconstruction failure."); return 0; } _sslClient.setClient(_connection->getClient()); - _sslClient.setEccSlot(static_cast(ECCX08Slot::Key), _eccx08_cert.bytes(), _eccx08_cert.length()); + _sslClient.setEccSlot(static_cast(CryptoSlot::Key), _cert.bytes(), _cert.length()); #elif defined(BOARD_ESP) _sslClient.setInsecure(); #endif diff --git a/src/ArduinoIoTCloudTCP.h b/src/ArduinoIoTCloudTCP.h index b704f6762..2167b4d19 100644 --- a/src/ArduinoIoTCloudTCP.h +++ b/src/ArduinoIoTCloudTCP.h @@ -28,13 +28,13 @@ #ifdef BOARD_HAS_ECCX08 #include "tls/BearSSLClient.h" - #include "tls/utility/ECCX08Cert.h" + #include "tls/utility/CryptoUtil.h" #elif defined(BOARD_ESP) #include #endif #ifdef BOARD_HAS_OFFLOADED_ECCX08 -#include "tls/utility/ECCX08Cert.h" +#include "tls/utility/CryptoUtil.h" #include #endif @@ -133,11 +133,13 @@ class ArduinoIoTCloudTCP: public ArduinoIoTCloudClass bool _mqtt_data_request_retransmit; #if defined(BOARD_HAS_ECCX08) - ECCX08CertClass _eccx08_cert; + ArduinoIoTCloudCertClass _cert; BearSSLClient _sslClient; + CryptoUtil _crypto; #elif defined(BOARD_HAS_OFFLOADED_ECCX08) - ECCX08CertClass _eccx08_cert; + ArduinoIoTCloudCertClass _cert; WiFiBearSSLClient _sslClient; + CryptoUtil _crypto; #elif defined(BOARD_ESP) WiFiClientSecure _sslClient; String _password; diff --git a/src/tls/utility/CryptoUtil.cpp b/src/tls/utility/CryptoUtil.cpp index 4348bc38d..3d1fdf880 100644 --- a/src/tls/utility/CryptoUtil.cpp +++ b/src/tls/utility/CryptoUtil.cpp @@ -19,43 +19,171 @@ * INCLUDE ******************************************************************************/ +#include + +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) + #include "CryptoUtil.h" +#include "SHA256.h" + +/****************************************************************************** + * DEFINE + ******************************************************************************/ +#define CRYPTO_SHA256_BUFFER_LENGTH 32 +#define CRYPTO_CERT_BUFFER_LENGTH 1024 + +/************************************************************************************** + * CTOR/DTOR + **************************************************************************************/ +CryptoUtil::CryptoUtil() +: _crypto {ECCX08} +{ -#if defined(BOARD_HAS_ECCX08) || defined (BOARD_HAS_OFFLOADED_ECCX08) +} /****************************************************************************** * PUBLIC MEMBER FUNCTIONS ******************************************************************************/ -bool CryptoUtil::readDeviceId(ECCX08Class & eccx08, String & device_id, ECCX08Slot const device_id_slot) +int CryptoUtil::buildCSR(ArduinoIoTCloudCertClass & cert, const CryptoSlot keySlot, bool newPrivateKey) +{ + byte publicKey[CERT_PUBLIC_KEY_LENGTH]; + byte signature[CERT_SIGNATURE_LENGTH]; + + if (newPrivateKey) { + if (!_crypto.generatePrivateKey(static_cast(keySlot), publicKey)) { + return 0; + } + } else { + if (!_crypto.generatePublicKey(static_cast(keySlot), publicKey)) { + return 0; + } + } + + /* Store public key in csr */ + if (!cert.setPublicKey(publicKey, CERT_PUBLIC_KEY_LENGTH)) { + return 0; + } + + /* Build CSR */ + if (!cert.buildCSR()) { + return 0; + } + + /* compute CSR SHA256 */ + SHA256 sha256; + byte sha256buf[CRYPTO_SHA256_BUFFER_LENGTH]; + sha256.begin(); + sha256.update(cert.bytes(), cert.length()); + sha256.finalize(sha256buf); + + if (!_crypto.ecSign(static_cast(keySlot), sha256buf, signature)) { + return 0; + } + + /* sign CSR */ + return cert.signCSR(signature); +} + +int CryptoUtil::buildCert(ArduinoIoTCloudCertClass & cert, const CryptoSlot keySlot) +{ + byte publicKey[CERT_PUBLIC_KEY_LENGTH]; + + if (!_crypto.generatePublicKey(static_cast(keySlot), publicKey)) { + return 0; + } + + /* Store public key in csr */ + if (!cert.setPublicKey(publicKey, CERT_PUBLIC_KEY_LENGTH)) { + return 0; + } + + /* Build CSR */ + if (!cert.buildCert()) { + return 0; + } + + /* sign CSR */ + return cert.signCert(); +} + +int CryptoUtil::readDeviceId(String & device_id, const CryptoSlot device_id_slot) +{ + byte device_id_bytes[CERT_COMPRESSED_CERT_SLOT_LENGTH] = {0}; + + if (!_crypto.readSlot(static_cast(device_id_slot), device_id_bytes, sizeof(device_id_bytes))) { + return 0; + } + + device_id = String(reinterpret_cast(device_id_bytes)); + return 1; +} + +int CryptoUtil::writeDeviceId(String & device_id, const CryptoSlot device_id_slot) { - byte device_id_bytes[72] = {0}; + byte device_id_bytes[CERT_COMPRESSED_CERT_SLOT_LENGTH] = {0}; + + device_id.getBytes(device_id_bytes, sizeof(device_id_bytes)); - if (eccx08.readSlot(static_cast(device_id_slot), device_id_bytes, sizeof(device_id_bytes))) { - device_id = String(reinterpret_cast(device_id_bytes)); - return true; + if (!_crypto.writeSlot(static_cast(device_id_slot), device_id_bytes, sizeof(device_id_bytes))) { + return 0; } - else - { - return false; + return 1; +} + +int CryptoUtil::writeCert(ArduinoIoTCloudCertClass & cert, const CryptoSlot certSlot) +{ + if (!_crypto.writeSlot(static_cast(certSlot), cert.compressedCertSignatureAndDatesBytes(), cert.compressedCertSignatureAndDatesLength())) { + return 0; } + + if (!_crypto.writeSlot(static_cast(certSlot) + 1, cert.compressedCertSerialAndAuthorityKeyIdBytes(), cert.compressedCertSerialAndAuthorityKeyIdLenght())) { + return 0; + } + return 1; } -bool CryptoUtil::reconstructCertificate(ECCX08CertClass & cert, String const & device_id, ECCX08Slot const key, ECCX08Slot const compressed_certificate, ECCX08Slot const serial_number_and_authority_key) +int CryptoUtil::readCert(ArduinoIoTCloudCertClass & cert, const CryptoSlot certSlot) { - if (cert.beginReconstruction(static_cast(key), static_cast(compressed_certificate), static_cast(serial_number_and_authority_key))) - { - cert.setSubjectCommonName(device_id); - cert.setIssuerCountryName("US"); - cert.setIssuerOrganizationName("Arduino LLC US"); - cert.setIssuerOrganizationalUnitName("IT"); - cert.setIssuerCommonName("Arduino"); - return cert.endReconstruction(); - } - else - { - return false; + String deviceId; + byte publicKey[CERT_PUBLIC_KEY_LENGTH]; + + cert.begin(); + + if (!readDeviceId(deviceId, CryptoSlot::DeviceId)) { + return 0; + } + + if (!_crypto.readSlot(static_cast(certSlot), cert.compressedCertSignatureAndDatesBytes(), cert.compressedCertSignatureAndDatesLength())) { + return 0; + } + + if (!_crypto.readSlot(static_cast(certSlot) + 1, cert.compressedCertSerialAndAuthorityKeyIdBytes(), cert.compressedCertSerialAndAuthorityKeyIdLenght())) { + return 0; + } + + if (!_crypto.generatePublicKey(static_cast(CryptoSlot::Key), publicKey)) { + return 0; + } + + cert.setSubjectCommonName(deviceId); + cert.setIssuerCountryName("US"); + cert.setIssuerOrganizationName("Arduino LLC US"); + cert.setIssuerOrganizationalUnitName("IT"); + cert.setIssuerCommonName("Arduino"); + + if (!cert.setPublicKey(publicKey, CERT_PUBLIC_KEY_LENGTH)) { + return 0; + } + + if (!cert.buildCert()) { + return 0; + } + + if (!cert.signCert()) { + return 0; } + return 1; } -#endif /* BOARD_HAS_ECCX08 */ +#endif /* (BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) */ diff --git a/src/tls/utility/CryptoUtil.h b/src/tls/utility/CryptoUtil.h index 32239a486..35da6b81e 100644 --- a/src/tls/utility/CryptoUtil.h +++ b/src/tls/utility/CryptoUtil.h @@ -24,17 +24,15 @@ #include -#if defined(BOARD_HAS_ECCX08) || defined (BOARD_HAS_OFFLOADED_ECCX08) - +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) #include +#include "Cert.h" #include -#include "ECCX08Cert.h" /****************************************************************************** TYPEDEF ******************************************************************************/ - -enum class ECCX08Slot : int +enum class CryptoSlot : int { Key = 0, CompressedCertificate = 10, @@ -50,17 +48,26 @@ class CryptoUtil { public: - static bool readDeviceId(ECCX08Class & eccx08, String & device_id, ECCX08Slot const device_id_slot); - static bool reconstructCertificate(ECCX08CertClass & cert, String const & device_id, ECCX08Slot const key, ECCX08Slot const compressed_certificate, ECCX08Slot const serial_number_and_authority_key); + CryptoUtil(); + inline int begin() { return _crypto.begin(); } + inline int locked() { return _crypto.locked(); } + inline int writeConfiguration(const byte config[]) { return _crypto.writeConfiguration(config); } + inline int lock() { return _crypto.lock(); } -private: + int buildCSR(ArduinoIoTCloudCertClass & cert, const CryptoSlot keySlot, bool newPrivateKey); + int buildCert(ArduinoIoTCloudCertClass & cert, const CryptoSlot keySlot); - CryptoUtil() { } - CryptoUtil(CryptoUtil const & other) { } + int readDeviceId(String & device_id, const CryptoSlot device_id_slot); + int writeDeviceId(String & device_id, const CryptoSlot device_id_slot); + int writeCert(ArduinoIoTCloudCertClass & cert, const CryptoSlot certSlot); + int readCert(ArduinoIoTCloudCertClass & cert, const CryptoSlot certSlot); + +private: + ECCX08Class & _crypto; }; -#endif /* BOARD_HAS_ECCX08 */ +#endif /* BOARD_HAS_ECCX08 || BOARD_HAS_OFFLOADED_ECCX08 */ #endif /* ARDUINO_IOT_CLOUD_UTILITY_CRYPTO_CRYPTO_UTIL_H_ */ From e2ac7e8c6a6ace34c9a6cf5ea319529a527b3e46 Mon Sep 17 00:00:00 2001 From: pennam Date: Mon, 14 Feb 2022 15:00:38 +0100 Subject: [PATCH 3/5] ECCX08Cert: remove files --- src/tls/utility/ECCX08Cert.cpp | 945 --------------------------------- src/tls/utility/ECCX08Cert.h | 147 ----- 2 files changed, 1092 deletions(-) delete mode 100644 src/tls/utility/ECCX08Cert.cpp delete mode 100644 src/tls/utility/ECCX08Cert.h diff --git a/src/tls/utility/ECCX08Cert.cpp b/src/tls/utility/ECCX08Cert.cpp deleted file mode 100644 index 164309e9a..000000000 --- a/src/tls/utility/ECCX08Cert.cpp +++ /dev/null @@ -1,945 +0,0 @@ -/* - This file is part of ArduinoIoTCloud. - - Copyright 2019 ARDUINO SA (http://www.arduino.cc/) - - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - The terms of this license can be found at: - https://www.gnu.org/licenses/gpl-3.0.en.html - - You can be released from the requirements of the above licenses by purchasing - a commercial license. Buying such a license is mandatory if you want to modify or - otherwise use the software for commercial activities involving the Arduino - software without disclosing the source code of your own applications. To purchase - a commercial license, send an email to license@arduino.cc. -*/ - -/****************************************************************************** - * INCLUDE - ******************************************************************************/ - -#include - -#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) - -#include "../../tls/bearssl/bearssl_hash.h" -#include - -#include "ECCX08Cert.h" - -/****************************************************************************** - * DEFINE - ******************************************************************************/ - -#define ASN1_INTEGER 0x02 -#define ASN1_BIT_STRING 0x03 -#define ASN1_NULL 0x05 -#define ASN1_OBJECT_IDENTIFIER 0x06 -#define ASN1_PRINTABLE_STRING 0x13 -#define ASN1_SEQUENCE 0x30 -#define ASN1_SET 0x31 - -struct __attribute__((__packed__)) CompressedCert { - byte signature[64]; - byte dates[3]; - byte unused[5]; -}; - -#define SERIAL_NUMBER_LENGTH 16 -#define AUTHORITY_KEY_IDENTIFIER_LENGTH 20 - -struct __attribute__((__packed__)) SerialNumberAndAuthorityKeyIdentifier { - byte serialNumber[SERIAL_NUMBER_LENGTH]; - byte authorityKeyIdentifier[AUTHORITY_KEY_IDENTIFIER_LENGTH]; -}; - -/****************************************************************************** - * LOCAL MODULE FUNCTIONS - ******************************************************************************/ - -static String base64Encode(const byte in[], unsigned int length, const char* prefix, const char* suffix) { - static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; - - int b; - String out; - - int reserveLength = 4 * ((length + 2) / 3) + ((length / 3 * 4) / 76) + strlen(prefix) + strlen(suffix); - out.reserve(reserveLength); - - if (prefix) { - out += prefix; - } - - for (unsigned int i = 0; i < length; i += 3) { - if (i > 0 && (i / 3 * 4) % 76 == 0) { - out += '\n'; - } - - b = (in[i] & 0xFC) >> 2; - out += CODES[b]; - - b = (in[i] & 0x03) << 4; - if (i + 1 < length) { - b |= (in[i + 1] & 0xF0) >> 4; - out += CODES[b]; - b = (in[i + 1] & 0x0F) << 2; - if (i + 2 < length) { - b |= (in[i + 2] & 0xC0) >> 6; - out += CODES[b]; - b = in[i + 2] & 0x3F; - out += CODES[b]; - } else { - out += CODES[b]; - out += '='; - } - } else { - out += CODES[b]; - out += "=="; - } - } - - if (suffix) { - out += suffix; - } - - return out; -} - -/****************************************************************************** - * CTOR/DTOR - ******************************************************************************/ - -ECCX08CertClass::ECCX08CertClass() : - _keySlot(-1), - _compressedCertSlot(-1), - _serialNumberAndAuthorityKeyIdentifierSlot(-1), - _bytes(NULL), - _length(0) { -} - -ECCX08CertClass::~ECCX08CertClass() { - if (_bytes) { - free(_bytes); - _bytes = NULL; - } -} - -/****************************************************************************** - * PUBLIC MEMBER FUNCTIONS - ******************************************************************************/ - -int ECCX08CertClass::beginCSR(int keySlot, bool newPrivateKey) { - if (keySlot < 0 || keySlot > 8) { - return 0; - } - - _keySlot = keySlot; - - if (newPrivateKey) { - if (!ECCX08.generatePrivateKey(_keySlot, _temp)) { - return 0; - } - } else { - if (!ECCX08.generatePublicKey(_keySlot, _temp)) { - return 0; - } - } - - return 1; -} - -String ECCX08CertClass::endCSR() { - int versionLen = versionLength(); - int subjectLen = issuerOrSubjectLength(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName); - int subjectHeaderLen = sequenceHeaderLength(subjectLen); - int publicKeyLen = publicKeyLength(); - - int csrInfoLen = versionLen + subjectHeaderLen + subjectLen + publicKeyLen + 2; - int csrInfoHeaderLen = sequenceHeaderLength(csrInfoLen); - - byte csrInfo[csrInfoHeaderLen + csrInfoLen]; - byte* out = csrInfo; - - appendSequenceHeader(csrInfoLen, out); - out += csrInfoHeaderLen; - - // version - appendVersion(0x00, out); - out += versionLen; - - // subject - appendSequenceHeader(subjectLen, out); - out += subjectHeaderLen; - appendIssuerOrSubject(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName, out); - out += subjectLen; - - // public key - appendPublicKey(_temp, out); - out += publicKeyLen; - - // terminator - *out++ = 0xa0; - *out++ = 0x00; - - br_sha256_context sha256Context; - byte csrInfoSha256[64]; - byte signature[64]; - - br_sha256_init(&sha256Context); - br_sha256_update(&sha256Context, csrInfo, csrInfoHeaderLen + csrInfoLen); - br_sha256_out(&sha256Context, csrInfoSha256); - - if (!ECCX08.ecSign(_keySlot, csrInfoSha256, signature)) { - return ""; - } - - int signatureLen = signatureLength(signature); - int csrLen = csrInfoHeaderLen + csrInfoLen + signatureLen; - int csrHeaderLen = sequenceHeaderLength(csrLen); - - byte csr[csrLen + csrHeaderLen]; - out = csr; - - appendSequenceHeader(csrLen, out); - out += csrHeaderLen; - - // info - memcpy(out, csrInfo, csrInfoHeaderLen + csrInfoLen); - out += (csrInfoHeaderLen + csrInfoLen); - - // signature - appendSignature(signature, out); - out += signatureLen; - - return base64Encode(csr, csrLen + csrHeaderLen, "-----BEGIN CERTIFICATE REQUEST-----\n", "\n-----END CERTIFICATE REQUEST-----\n"); -} - -int ECCX08CertClass::beginStorage(int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot) { - if (compressedCertSlot < 8 || compressedCertSlot > 15) { - return 0; - } - - if (serialNumberAndAuthorityKeyIdentifierSlot < 8 || serialNumberAndAuthorityKeyIdentifierSlot > 15) { - return 0; - } - - _compressedCertSlot = compressedCertSlot; - _serialNumberAndAuthorityKeyIdentifierSlot = serialNumberAndAuthorityKeyIdentifierSlot; - - memset(_temp, 0x00, sizeof(_temp)); - - return 1; -} - -void ECCX08CertClass::setSignature(byte signature[]) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - memcpy(compressedCert->signature, signature, 64); -} - -void ECCX08CertClass::setIssueYear(int issueYear) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[0] &= 0x07; - compressedCert->dates[0] |= (issueYear - 2000) << 3; -} - -void ECCX08CertClass::setIssueMonth(int issueMonth) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[0] &= 0xf8; - compressedCert->dates[0] |= issueMonth >> 1; - - compressedCert->dates[1] &= 0x7f; - compressedCert->dates[1] |= issueMonth << 7; -} - -void ECCX08CertClass::setIssueDay(int issueDay) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[1] &= 0x83; - compressedCert->dates[1] |= issueDay << 2; -} - -void ECCX08CertClass::setIssueHour(int issueHour) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[2] &= 0x1f; - compressedCert->dates[2] |= issueHour << 5; - - compressedCert->dates[1] &= 0xfc; - compressedCert->dates[1] |= issueHour >> 3; -} - -void ECCX08CertClass::setExpireYears(int expireYears) { - struct CompressedCert* compressedCert = (struct CompressedCert*)_temp; - - compressedCert->dates[2] &= 0xe0; - compressedCert->dates[2] |= expireYears; -} - -void ECCX08CertClass::setSerialNumber(const byte serialNumber[]) { - memcpy(&_temp[72], serialNumber, SERIAL_NUMBER_LENGTH); -} - -void ECCX08CertClass::setAuthorityKeyIdentifier(const byte authorityKeyIdentifier[]) { - memcpy(&_temp[88], authorityKeyIdentifier, AUTHORITY_KEY_IDENTIFIER_LENGTH); -} - -int ECCX08CertClass::endStorage() { - if (!ECCX08.writeSlot(_compressedCertSlot, &_temp[0], 72)) { - return 0; - } - - if (!ECCX08.writeSlot(_serialNumberAndAuthorityKeyIdentifierSlot, &_temp[72], SERIAL_NUMBER_LENGTH + AUTHORITY_KEY_IDENTIFIER_LENGTH)) { - return 0; - } - - return 1; -} - -int ECCX08CertClass::beginReconstruction(int keySlot, int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot) { - if (keySlot < 0 || keySlot > 8) { - return 0; - } - - if (compressedCertSlot < 8 || compressedCertSlot > 15) { - return 0; - } - - if (serialNumberAndAuthorityKeyIdentifierSlot < 8 || serialNumberAndAuthorityKeyIdentifierSlot > 15) { - return 0; - } - - _keySlot = keySlot; - _compressedCertSlot = compressedCertSlot; - _serialNumberAndAuthorityKeyIdentifierSlot = serialNumberAndAuthorityKeyIdentifierSlot; - - return 1; -} - -int ECCX08CertClass::endReconstruction() { - byte publicKey[64]; - struct CompressedCert compressedCert; - struct SerialNumberAndAuthorityKeyIdentifier serialNumberAndAuthorityKeyIdentifier; - - if (!ECCX08.generatePublicKey(_keySlot, publicKey)) { - return 0; - } - - if (!ECCX08.readSlot(_compressedCertSlot, (byte*)&compressedCert, sizeof(compressedCert))) { - return 0; - } - - if (!ECCX08.readSlot(_serialNumberAndAuthorityKeyIdentifierSlot, (byte*)&serialNumberAndAuthorityKeyIdentifier, sizeof(serialNumberAndAuthorityKeyIdentifier))) { - return 0; - } - - // dates - int year = (compressedCert.dates[0] >> 3) + 2000; - int month = ((compressedCert.dates[0] & 0x07) << 1) | (compressedCert.dates[1] >> 7); - int day = (compressedCert.dates[1] & 0x7c) >> 2; - int hour = ((compressedCert.dates[1] & 0x03) << 3) | (compressedCert.dates[2] >> 5); - int expireYears = (compressedCert.dates[2] & 0x1f); - - int datesSize = 30; - - if (year > 2049) { - // two more bytes for GeneralizedTime - datesSize += 2; - } - - if ((year + expireYears) > 2049) { - // two more bytes for GeneralizedTime - datesSize += 2; - } - - int serialNumberLen = serialNumberLength(serialNumberAndAuthorityKeyIdentifier.serialNumber); - - int issuerLen = issuerOrSubjectLength(_issuerCountryName, - _issuerStateProvinceName, - _issuerLocalityName, - _issuerOrganizationName, - _issuerOrganizationalUnitName, - _issuerCommonName); - - int issuerHeaderLen = sequenceHeaderLength(issuerLen); - - int subjectLen = issuerOrSubjectLength(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName); - - int subjectHeaderLen = sequenceHeaderLength(subjectLen); - - int publicKeyLen = publicKeyLength(); - - int authorityKeyIdentifierLen = authorityKeyIdentifierLength(serialNumberAndAuthorityKeyIdentifier.authorityKeyIdentifier); - - int signatureLen = signatureLength(compressedCert.signature); - - int certInfoLen = 5 + serialNumberLen + 12 + issuerHeaderLen + issuerLen + (datesSize + 2) + - subjectHeaderLen + subjectLen + publicKeyLen; - - if (authorityKeyIdentifierLen) { - certInfoLen += authorityKeyIdentifierLen; - } else { - certInfoLen += 4; - } - - int certInfoHeaderLen = sequenceHeaderLength(certInfoLen); - - int certDataLen = certInfoLen + certInfoHeaderLen + signatureLen; - int certDataHeaderLen = sequenceHeaderLength(certDataLen); - - _length = certDataLen + certDataHeaderLen; - _bytes = (byte*)realloc(_bytes, _length); - - if (!_bytes) { - _length = 0; - return 0; - } - - byte* out = _bytes; - - appendSequenceHeader(certDataLen, out); - out += certDataHeaderLen; - - appendSequenceHeader(certInfoLen, out); - out += certInfoHeaderLen; - - // version - *out++ = 0xA0; - *out++ = 0x03; - *out++ = 0x02; - *out++ = 0x01; - *out++ = 0x02; - - // serial number - appendSerialNumber(serialNumberAndAuthorityKeyIdentifier.serialNumber, out); - out += serialNumberLen; - - // ecdsaWithSHA256 - out += appendEcdsaWithSHA256(out); - - // issuer - appendSequenceHeader(issuerLen, out); - out += issuerHeaderLen; - appendIssuerOrSubject(_issuerCountryName, - _issuerStateProvinceName, - _issuerLocalityName, - _issuerOrganizationName, - _issuerOrganizationalUnitName, - _issuerCommonName, out); - out += issuerLen; - - *out++ = ASN1_SEQUENCE; - *out++ = datesSize; - out += appendDate(year, month, day, hour, 0, 0, out); - out += appendDate(year + expireYears, month, day, hour, 0, 0, out); - - // subject - appendSequenceHeader(subjectLen, out); - out += subjectHeaderLen; - appendIssuerOrSubject(_subjectCountryName, - _subjectStateProvinceName, - _subjectLocalityName, - _subjectOrganizationName, - _subjectOrganizationalUnitName, - _subjectCommonName, out); - out += subjectLen; - - // public key - appendPublicKey(publicKey, out); - out += publicKeyLen; - - if (authorityKeyIdentifierLen) { - appendAuthorityKeyIdentifier(serialNumberAndAuthorityKeyIdentifier.authorityKeyIdentifier, out); - out += authorityKeyIdentifierLen; - } else { - // null sequence - *out++ = 0xA3; - *out++ = 0x02; - *out++ = 0x30; - *out++ = 0x00; - } - - // signature - appendSignature(compressedCert.signature, out); - out += signatureLen; - - return 1; -} - -byte* ECCX08CertClass::bytes() { - return _bytes; -} - -int ECCX08CertClass::length() { - return _length; -} - -void ECCX08CertClass::setIssuerCountryName(const String& countryName) { - _issuerCountryName = countryName; -} - -void ECCX08CertClass::setIssuerStateProvinceName(const String& stateProvinceName) { - _issuerStateProvinceName = stateProvinceName; -} - -void ECCX08CertClass::setIssuerLocalityName(const String& localityName) { - _issuerLocalityName = localityName; -} - -void ECCX08CertClass::setIssuerOrganizationName(const String& organizationName) { - _issuerOrganizationName = organizationName; -} - -void ECCX08CertClass::setIssuerOrganizationalUnitName(const String& organizationalUnitName) { - _issuerOrganizationalUnitName = organizationalUnitName; -} - -void ECCX08CertClass::setIssuerCommonName(const String& commonName) { - _issuerCommonName = commonName; -} - -void ECCX08CertClass::setSubjectCountryName(const String& countryName) { - _subjectCountryName = countryName; -} - -void ECCX08CertClass::setSubjectStateProvinceName(const String& stateProvinceName) { - _subjectStateProvinceName = stateProvinceName; -} - -void ECCX08CertClass::setSubjectLocalityName(const String& localityName) { - _subjectLocalityName = localityName; -} - -void ECCX08CertClass::setSubjectOrganizationName(const String& organizationName) { - _subjectOrganizationName = organizationName; -} - -void ECCX08CertClass::setSubjectOrganizationalUnitName(const String& organizationalUnitName) { - _subjectOrganizationName = organizationalUnitName; -} - -void ECCX08CertClass::setSubjectCommonName(const String& commonName) { - _subjectCommonName = commonName; -} - -int ECCX08CertClass::versionLength() { - return 3; -} - -int ECCX08CertClass::issuerOrSubjectLength(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName) { - int length = 0; - int countryNameLength = countryName.length(); - int stateProvinceNameLength = stateProvinceName.length(); - int localityNameLength = localityName.length(); - int organizationNameLength = organizationName.length(); - int organizationalUnitNameLength = organizationalUnitName.length(); - int commonNameLength = commonName.length(); - - if (countryNameLength) { - length += (11 + countryNameLength); - } - - if (stateProvinceNameLength) { - length += (11 + stateProvinceNameLength); - } - - if (localityNameLength) { - length += (11 + localityNameLength); - } - - if (organizationNameLength) { - length += (11 + organizationNameLength); - } - - if (organizationalUnitNameLength) { - length += (11 + organizationalUnitNameLength); - } - - if (commonNameLength) { - length += (11 + commonNameLength); - } - - return length; -} - -int ECCX08CertClass::publicKeyLength() { - return (2 + 2 + 9 + 10 + 4 + 64); -} - -int ECCX08CertClass::authorityKeyIdentifierLength(const byte authorityKeyIdentifier[]) { - bool set = false; - - // check if the authority key identifier is non-zero - for (int i = 0; i < AUTHORITY_KEY_IDENTIFIER_LENGTH; i++) { - if (authorityKeyIdentifier[i] != 0) { - set = true; - break; - } - } - - return (set ? 37 : 0); -} - -int ECCX08CertClass::signatureLength(const byte signature[]) { - const byte* r = &signature[0]; - const byte* s = &signature[32]; - - int rLength = 32; - int sLength = 32; - - while (*r == 0x00 && rLength) { - r++; - rLength--; - } - - if (*r & 0x80) { - rLength++; - } - - while (*s == 0x00 && sLength) { - s++; - sLength--; - } - - if (*s & 0x80) { - sLength++; - } - - return (21 + rLength + sLength); -} - -int ECCX08CertClass::serialNumberLength(const byte serialNumber[]) { - int length = SERIAL_NUMBER_LENGTH; - - while (*serialNumber == 0 && length) { - serialNumber++; - length--; - } - - if (*serialNumber & 0x80) { - length++; - } - - return (2 + length); -} - -int ECCX08CertClass::sequenceHeaderLength(int length) { - if (length > 255) { - return 4; - } else if (length > 127) { - return 3; - } else { - return 2; - } -} - -void ECCX08CertClass::appendVersion(int version, byte out[]) { - out[0] = ASN1_INTEGER; - out[1] = 0x01; - out[2] = version; -} - -void ECCX08CertClass::appendIssuerOrSubject(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName, - byte out[]) { - if (countryName.length() > 0) { - out += appendName(countryName, 0x06, out); - } - - if (stateProvinceName.length() > 0) { - out += appendName(stateProvinceName, 0x08, out); - } - - if (localityName.length() > 0) { - out += appendName(localityName, 0x07, out); - } - - if (organizationName.length() > 0) { - out += appendName(organizationName, 0x0a, out); - } - - if (organizationalUnitName.length() > 0) { - out += appendName(organizationalUnitName, 0x0b, out); - } - - if (commonName.length() > 0) { - out += appendName(commonName, 0x03, out); - } -} - -void ECCX08CertClass::appendPublicKey(const byte publicKey[], byte out[]) { - int subjectPublicKeyDataLength = 2 + 9 + 10 + 4 + 64; - - // subject public key - *out++ = ASN1_SEQUENCE; - *out++ = (subjectPublicKeyDataLength) & 0xff; - - *out++ = ASN1_SEQUENCE; - *out++ = 0x13; - - // EC public key - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x07; - *out++ = 0x2a; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xce; - *out++ = 0x3d; - *out++ = 0x02; - *out++ = 0x01; - - // PRIME 256 v1 - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x08; - *out++ = 0x2a; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xce; - *out++ = 0x3d; - *out++ = 0x03; - *out++ = 0x01; - *out++ = 0x07; - - *out++ = 0x03; - *out++ = 0x42; - *out++ = 0x00; - *out++ = 0x04; - - memcpy(out, publicKey, 64); -} - -void ECCX08CertClass::appendAuthorityKeyIdentifier(const byte authorityKeyIdentifier[], byte out[]) { - // [3] - *out++ = 0xa3; - *out++ = 0x23; - - // sequence - *out++ = ASN1_SEQUENCE; - *out++ = 0x21; - - // sequence - *out++ = ASN1_SEQUENCE; - *out++ = 0x1f; - - // 2.5.29.35 authorityKeyIdentifier(X.509 extension) - *out++ = 0x06; - *out++ = 0x03; - *out++ = 0x55; - *out++ = 0x1d; - *out++ = 0x23; - - // octet string - *out++ = 0x04; - *out++ = 0x18; - - // sequence - *out++ = ASN1_SEQUENCE; - *out++ = 0x16; - - *out++ = 0x80; - *out++ = 0x14; - - memcpy(out, authorityKeyIdentifier, 20); -} - -void ECCX08CertClass::appendSignature(const byte signature[], byte out[]) { - // signature algorithm - *out++ = ASN1_SEQUENCE; - *out++ = 0x0a; - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x08; - - // ECDSA with SHA256 - *out++ = 0x2a; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xce; - *out++ = 0x3d; - *out++ = 0x04; - *out++ = 0x03; - *out++ = 0x02; - - const byte* r = &signature[0]; - const byte* s = &signature[32]; - - int rLength = 32; - int sLength = 32; - - while (*r == 0 && rLength) { - r++; - rLength--; - } - - while (*s == 0 && sLength) { - s++; - sLength--; - } - - if (*r & 0x80) { - rLength++; - } - - if (*s & 0x80) { - sLength++; - } - - *out++ = ASN1_BIT_STRING; - *out++ = (rLength + sLength + 7); - *out++ = 0; - - *out++ = ASN1_SEQUENCE; - *out++ = (rLength + sLength + 4); - - *out++ = ASN1_INTEGER; - *out++ = rLength; - if ((*r & 0x80) && rLength) { - *out++ = 0; - rLength--; - } - memcpy(out, r, rLength); - out += rLength; - - *out++ = ASN1_INTEGER; - *out++ = sLength; - if ((*s & 0x80) && sLength) { - *out++ = 0; - sLength--; - } - memcpy(out, s, sLength); - out += rLength; -} - -void ECCX08CertClass::appendSerialNumber(const byte serialNumber[], byte out[]) { - int length = SERIAL_NUMBER_LENGTH; - - while (*serialNumber == 0 && length) { - serialNumber++; - length--; - } - - if (*serialNumber & 0x80) { - length++; - } - - *out++ = ASN1_INTEGER; - *out++ = length; - - if (*serialNumber & 0x80) { - *out++ = 0x00; - length--; - } - - memcpy(out, serialNumber, length); -} - -int ECCX08CertClass::appendName(const String& name, int type, byte out[]) { - int nameLength = name.length(); - - *out++ = ASN1_SET; - *out++ = nameLength + 9; - - *out++ = ASN1_SEQUENCE; - *out++ = nameLength + 7; - - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x03; - *out++ = 0x55; - *out++ = 0x04; - *out++ = type; - - *out++ = ASN1_PRINTABLE_STRING; - *out++ = nameLength; - memcpy(out, name.c_str(), nameLength); - - return (nameLength + 11); -} - -void ECCX08CertClass::appendSequenceHeader(int length, byte out[]) { - *out++ = ASN1_SEQUENCE; - if (length > 255) { - *out++ = 0x82; - *out++ = (length >> 8) & 0xff; - } else if (length > 127) { - *out++ = 0x81; - } - *out++ = (length) & 0xff; -} - -int ECCX08CertClass::appendDate(int year, int month, int day, int hour, int minute, int second, byte out[]) { - bool useGeneralizedTime = (year > 2049); - - if (useGeneralizedTime) { - *out++ = 0x18; - *out++ = 0x0f; - *out++ = '0' + (year / 1000); - *out++ = '0' + ((year % 1000) / 100); - *out++ = '0' + ((year % 100) / 10); - *out++ = '0' + (year % 10); - } else { - year -= 2000; - - *out++ = 0x17; - *out++ = 0x0d; - *out++ = '0' + (year / 10); - *out++ = '0' + (year % 10); - } - *out++ = '0' + (month / 10); - *out++ = '0' + (month % 10); - *out++ = '0' + (day / 10); - *out++ = '0' + (day % 10); - *out++ = '0' + (hour / 10); - *out++ = '0' + (hour % 10); - *out++ = '0' + (minute / 10); - *out++ = '0' + (minute % 10); - *out++ = '0' + (second / 10); - *out++ = '0' + (second % 10); - *out++ = 0x5a; // UTC - - return (useGeneralizedTime ? 17 : 15); -} - -int ECCX08CertClass::appendEcdsaWithSHA256(byte out[]) { - *out++ = ASN1_SEQUENCE; - *out++ = 0x0A; - *out++ = ASN1_OBJECT_IDENTIFIER; - *out++ = 0x08; - *out++ = 0x2A; - *out++ = 0x86; - *out++ = 0x48; - *out++ = 0xCE; - *out++ = 0x3D; - *out++ = 0x04; - *out++ = 0x03; - *out++ = 0x02; - - return 12; -} - -#endif /* BOARD_HAS_ECCX08 */ diff --git a/src/tls/utility/ECCX08Cert.h b/src/tls/utility/ECCX08Cert.h deleted file mode 100644 index 4e1ef6209..000000000 --- a/src/tls/utility/ECCX08Cert.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - This file is part of ArduinoIoTCloud. - - Copyright 2019 ARDUINO SA (http://www.arduino.cc/) - - This software is released under the GNU General Public License version 3, - which covers the main part of arduino-cli. - The terms of this license can be found at: - https://www.gnu.org/licenses/gpl-3.0.en.html - - You can be released from the requirements of the above licenses by purchasing - a commercial license. Buying such a license is mandatory if you want to modify or - otherwise use the software for commercial activities involving the Arduino - software without disclosing the source code of your own applications. To purchase - a commercial license, send an email to license@arduino.cc. -*/ - -#ifndef _ECCX08_CERT_H_ -#define _ECCX08_CERT_H_ - -/****************************************************************************** - * INCLUDE - ******************************************************************************/ - -#include - -#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) - -#include - -/****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ - -class ECCX08CertClass { - - public: - ECCX08CertClass(); - virtual ~ECCX08CertClass(); - - int beginCSR(int keySlot, bool newPrivateKey = true); - String endCSR(); - - int beginStorage(int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot); - void setSignature(byte signature[]); - void setIssueYear(int issueYear); - void setIssueMonth(int issueMonth); - void setIssueDay(int issueDay); - void setIssueHour(int issueHour); - void setExpireYears(int expireYears); - void setSerialNumber(const byte serialNumber[]); - void setAuthorityKeyIdentifier(const byte authorityKeyIdentifier[]); - int endStorage(); - - int beginReconstruction(int keySlot, int compressedCertSlot, int serialNumberAndAuthorityKeyIdentifierSlot); - int endReconstruction(); - - byte* bytes(); - int length(); - - void setIssuerCountryName(const String& countryName); - void setIssuerStateProvinceName(const String& stateProvinceName); - void setIssuerLocalityName(const String& localityName); - void setIssuerOrganizationName(const String& organizationName); - void setIssuerOrganizationalUnitName(const String& organizationalUnitName); - void setIssuerCommonName(const String& commonName); - - void setSubjectCountryName(const String& countryName); - void setSubjectStateProvinceName(const String& stateProvinceName); - void setSubjectLocalityName(const String& localityName); - void setSubjectOrganizationName(const String& organizationName); - void setSubjectOrganizationalUnitName(const String& organizationalUnitName); - void setSubjectCommonName(const String& commonName); - - private: - int versionLength(); - - int issuerOrSubjectLength(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName); - - int publicKeyLength(); - - int authorityKeyIdentifierLength(const byte authorityKeyIdentifier[]); - - int signatureLength(const byte signature[]); - - int serialNumberLength(const byte serialNumber[]); - - int sequenceHeaderLength(int length); - - void appendVersion(int version, byte out[]); - - void appendIssuerOrSubject(const String& countryName, - const String& stateProvinceName, - const String& localityName, - const String& organizationName, - const String& organizationalUnitName, - const String& commonName, - byte out[]); - - void appendPublicKey(const byte publicKey[], byte out[]); - - void appendAuthorityKeyIdentifier(const byte authorityKeyIdentifier[], byte out[]); - - void appendSignature(const byte signature[], byte out[]); - - void appendSerialNumber(const byte serialNumber[], byte out[]); - - int appendName(const String& name, int type, byte out[]); - - void appendSequenceHeader(int length, byte out[]); - - int appendDate(int year, int month, int day, int hour, int minute, int second, byte out[]); - - int appendEcdsaWithSHA256(byte out[]); - - private: - int _keySlot; - int _compressedCertSlot; - int _serialNumberAndAuthorityKeyIdentifierSlot; - - String _issuerCountryName; - String _issuerStateProvinceName; - String _issuerLocalityName; - String _issuerOrganizationName; - String _issuerOrganizationalUnitName; - String _issuerCommonName; - - String _subjectCountryName; - String _subjectStateProvinceName; - String _subjectLocalityName; - String _subjectOrganizationName; - String _subjectOrganizationalUnitName; - String _subjectCommonName; - - byte _temp[108]; - byte* _bytes; - int _length; -}; - -#endif /* BOARD_HAS_ECCX08 */ - -#endif /* _ECCX08_CERT_H_ */ From 9c565ac407bd72a98dd3022a8bdc8be0b6937793 Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 2 Mar 2022 08:44:54 +0100 Subject: [PATCH 4/5] Examples: update Provisioning.ino --- .../utility/Provisioning/Provisioning.ino | 99 +++++++++---------- 1 file changed, 46 insertions(+), 53 deletions(-) diff --git a/examples/utility/Provisioning/Provisioning.ino b/examples/utility/Provisioning/Provisioning.ino index e4ea8ce1b..c1503afcb 100644 --- a/examples/utility/Provisioning/Provisioning.ino +++ b/examples/utility/Provisioning/Provisioning.ino @@ -1,27 +1,22 @@ #include #include "ECCX08TLSConfig.h" -#include - const bool DEBUG = true; -const int keySlot = 0; -const int compressedCertSlot = 10; -const int serialNumberAndAuthorityKeyIdentifierSlot = 11; -const int deviceIdSlot = 12; -ECCX08CertClass ECCX08Cert; +ArduinoIoTCloudCertClass Certificate; +CryptoUtil Crypto; void setup() { Serial.begin(9600); while (!Serial); - if (!ECCX08.begin()) { - Serial.println("No ECCX08 present!"); + if (!Crypto.begin()) { + Serial.println("No crypto present!"); while (1); } - if (!ECCX08.locked()) { - String lockConfirm = promptAndReadLine("Your ECCX08 is unlocked, would you like to lock it (y/N): "); + if (!Crypto.locked()) { + String lockConfirm = promptAndReadLine("Your crypto is unlocked, would you like to lock it (y/N): "); lockConfirm.toLowerCase(); if (lockConfirm != "y") { @@ -29,17 +24,17 @@ void setup() { while (1); } - if (!ECCX08.writeConfiguration(DEFAULT_ECCX08_TLS_CONFIG)) { - Serial.println("Writing ECCX08 configuration failed!"); + if (!Crypto.writeConfiguration(DEFAULT_ECCX08_TLS_CONFIG)) { + Serial.println("Writing crypto configuration failed!"); while (1); } - if (!ECCX08.lock()) { - Serial.println("Locking ECCX08 configuration failed!"); + if (!Crypto.lock()) { + Serial.println("Locking crypto configuration failed!"); while (1); } - Serial.println("ECCX08 locked successfully"); + Serial.println("crypto locked successfully"); Serial.println(); } @@ -51,15 +46,20 @@ void setup() { while (1); } - if (!ECCX08Cert.beginCSR(keySlot, true)) { + if (!Certificate.begin()) { Serial.println("Error starting CSR generation!"); while (1); } String deviceId = promptAndReadLine("Please enter the device id: "); - ECCX08Cert.setSubjectCommonName(deviceId); + Certificate.setSubjectCommonName(deviceId); + + if (!Crypto.buildCSR(Certificate, CryptoSlot::Key, true)) { + Serial.println("Error generating CSR!"); + while (1); + } - String csr = ECCX08Cert.endCSR(); + String csr = Certificate.getCSRPEM(); if (!csr) { Serial.println("Error generating CSR!"); @@ -79,52 +79,45 @@ void setup() { String authorityKeyIdentifier = promptAndReadLine("Please enter the certificates authority key identifier: "); String signature = promptAndReadLine("Please enter the certificates signature: "); - byte deviceIdBytes[72]; - byte serialNumberBytes[16]; - byte authorityKeyIdentifierBytes[20]; - byte signatureBytes[64]; + byte serialNumberBytes[CERT_SERIAL_NUMBER_LENGTH]; + byte authorityKeyIdentifierBytes[CERT_AUTHORITY_KEY_ID_LENGTH]; + byte signatureBytes[CERT_SIGNATURE_LENGTH]; - deviceId.getBytes(deviceIdBytes, sizeof(deviceIdBytes)); hexStringToBytes(serialNumber, serialNumberBytes, sizeof(serialNumberBytes)); hexStringToBytes(authorityKeyIdentifier, authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); hexStringToBytes(signature, signatureBytes, sizeof(signatureBytes)); - if (!ECCX08.writeSlot(deviceIdSlot, deviceIdBytes, sizeof(deviceIdBytes))) { + if (!Crypto.writeDeviceId(deviceId, CryptoSlot::DeviceId)) { Serial.println("Error storing device id!"); while (1); } - if (!ECCX08Cert.beginStorage(compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Serial.println("Error starting ECCX08 storage!"); + if (!Certificate.begin()) { + Serial.println("Error starting crypto storage!"); while (1); } - ECCX08Cert.setSignature(signatureBytes); - ECCX08Cert.setAuthorityKeyIdentifier(authorityKeyIdentifierBytes); - ECCX08Cert.setSerialNumber(serialNumberBytes); - ECCX08Cert.setIssueYear(issueYear.toInt()); - ECCX08Cert.setIssueMonth(issueMonth.toInt()); - ECCX08Cert.setIssueDay(issueDay.toInt()); - ECCX08Cert.setIssueHour(issueHour.toInt()); - ECCX08Cert.setExpireYears(expireYears.toInt()); - - if (!ECCX08Cert.endStorage()) { - Serial.println("Error storing ECCX08 compressed cert!"); + Certificate.setSubjectCommonName(deviceId); + Certificate.setIssuerCountryName("US"); + Certificate.setIssuerOrganizationName("Arduino LLC US"); + Certificate.setIssuerOrganizationalUnitName("IT"); + Certificate.setIssuerCommonName("Arduino"); + Certificate.setSignature(signatureBytes, sizeof(signatureBytes)); + Certificate.setAuthorityKeyId(authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); + Certificate.setSerialNumber(serialNumberBytes, sizeof(serialNumberBytes)); + Certificate.setIssueYear(issueYear.toInt()); + Certificate.setIssueMonth(issueMonth.toInt()); + Certificate.setIssueDay(issueDay.toInt()); + Certificate.setIssueHour(issueHour.toInt()); + Certificate.setExpireYears(expireYears.toInt()); + + if (!Crypto.buildCert(Certificate, CryptoSlot::Key)) { + Serial.println("Error building cert!"); while (1); } - - if (!ECCX08Cert.beginReconstruction(keySlot, compressedCertSlot, serialNumberAndAuthorityKeyIdentifierSlot)) { - Serial.println("Error starting ECCX08 cert reconstruction!"); - while (1); - } - - ECCX08Cert.setIssuerCountryName("US"); - ECCX08Cert.setIssuerOrganizationName("Arduino LLC US"); - ECCX08Cert.setIssuerOrganizationalUnitName("IT"); - ECCX08Cert.setIssuerCommonName("Arduino"); - - if (!ECCX08Cert.endReconstruction()) { - Serial.println("Error reconstructing ECCX08 compressed cert!"); + + if (!Crypto.writeCert(Certificate, CryptoSlot::CompressedCertificate)) { + Serial.println("Error storing cert!"); while (1); } @@ -134,8 +127,8 @@ void setup() { Serial.println("Compressed cert = "); - const byte* certData = ECCX08Cert.bytes(); - int certLength = ECCX08Cert.length(); + const byte* certData = Certificate.bytes(); + int certLength = Certificate.length(); for (int i = 0; i < certLength; i++) { byte b = certData[i]; From bc88de4f7d920bba77f52c9588e20c635d73572b Mon Sep 17 00:00:00 2001 From: pennam Date: Wed, 2 Mar 2022 13:58:22 +0100 Subject: [PATCH 5/5] ArduinoIoTCloudCertClass: reduce RAM usage --- src/tls/utility/Cert.cpp | 14 ++++++++++---- src/tls/utility/Cert.h | 12 +++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/tls/utility/Cert.cpp b/src/tls/utility/Cert.cpp index 39ed76253..9a32bc97e 100644 --- a/src/tls/utility/Cert.cpp +++ b/src/tls/utility/Cert.cpp @@ -96,8 +96,9 @@ static String base64Encode(const byte in[], unsigned int length, const char* pre ******************************************************************************/ ArduinoIoTCloudCertClass::ArduinoIoTCloudCertClass() -: _certBuffer(NULL) +: _certBuffer(nullptr) , _certBufferLen(0) +, _publicKey(nullptr) { } @@ -106,7 +107,7 @@ ArduinoIoTCloudCertClass::~ArduinoIoTCloudCertClass() { if (_certBuffer) { free(_certBuffer); - _certBuffer = NULL; + _certBuffer = nullptr; } } @@ -117,7 +118,6 @@ ArduinoIoTCloudCertClass::~ArduinoIoTCloudCertClass() int ArduinoIoTCloudCertClass::begin() { memset(_compressedCert.data, 0x00, CERT_COMPRESSED_CERT_LENGTH); - memset(_publicKey, 0x00, CERT_PUBLIC_KEY_LENGTH); return 1; } @@ -146,6 +146,9 @@ int ArduinoIoTCloudCertClass::buildCSR() out += appendIssuerOrSubject(_subjectData, out); // public key + if (_publicKey == nullptr) { + return 0; + } out += appendPublicKey(_publicKey, out); // terminator @@ -243,6 +246,9 @@ int ArduinoIoTCloudCertClass::buildCert() out += appendIssuerOrSubject(_subjectData, out); // public key + if (_publicKey == nullptr) { + return 0; + } out += appendPublicKey(_publicKey, out); int authorityKeyIdLen = authorityKeyIdLength(_compressedCert.slot.two.values.authorityKeyId, CERT_AUTHORITY_KEY_ID_LENGTH); @@ -377,7 +383,7 @@ int ArduinoIoTCloudCertClass::setAuthorityKeyId(const uint8_t authorityKeyId[], int ArduinoIoTCloudCertClass::setPublicKey(const byte* publicKey, int publicKeyLen) { if (publicKeyLen == CERT_PUBLIC_KEY_LENGTH) { - memcpy(_publicKey, publicKey, CERT_PUBLIC_KEY_LENGTH); + _publicKey = publicKey; return 1; } return 0; diff --git a/src/tls/utility/Cert.h b/src/tls/utility/Cert.h index 944a2eac1..756747473 100644 --- a/src/tls/utility/Cert.h +++ b/src/tls/utility/Cert.h @@ -35,8 +35,8 @@ #define CERT_PUBLIC_KEY_LENGTH 64 #define CERT_SIGNATURE_LENGTH 64 #define CERT_DATES_LENGTH 3 -#define CERT_COMPRESSED_CERT_LENGTH 144 #define CERT_COMPRESSED_CERT_SLOT_LENGTH 72 +#define CERT_COMPRESSED_CERT_LENGTH CERT_COMPRESSED_CERT_SLOT_LENGTH + CERT_SERIAL_NUMBER_LENGTH + CERT_AUTHORITY_KEY_ID_LENGTH #include @@ -86,7 +86,7 @@ class ArduinoIoTCloudCertClass { inline byte* compressedCertSignatureAndDatesBytes() { return _compressedCert.slot.one.data; } inline int compressedCertSignatureAndDatesLength() {return CERT_COMPRESSED_CERT_SLOT_LENGTH; } inline byte* compressedCertSerialAndAuthorityKeyIdBytes() { return _compressedCert.slot.two.data; } - inline int compressedCertSerialAndAuthorityKeyIdLenght() {return CERT_COMPRESSED_CERT_SLOT_LENGTH; } + inline int compressedCertSerialAndAuthorityKeyIdLenght() {return CERT_SERIAL_NUMBER_LENGTH + CERT_AUTHORITY_KEY_ID_LENGTH; } #endif /* Build CSR */ @@ -135,9 +135,8 @@ class ArduinoIoTCloudCertClass { struct __attribute__((__packed__)) SerialNumberAndAuthorityKeyIdType { byte serialNumber[CERT_SERIAL_NUMBER_LENGTH]; byte authorityKeyId[CERT_AUTHORITY_KEY_ID_LENGTH]; - byte unused[36]; } values; - byte data[CERT_COMPRESSED_CERT_SLOT_LENGTH]; + byte data[CERT_SERIAL_NUMBER_LENGTH + CERT_AUTHORITY_KEY_ID_LENGTH]; }; union CompressedCertDataUType { @@ -145,12 +144,11 @@ class ArduinoIoTCloudCertClass { SignatureAndDateUType one; SerialNumberAndAuthorityKeyIdUType two; }slot; - byte data[CERT_COMPRESSED_CERT_LENGTH]; + byte data[CERT_COMPRESSED_CERT_SLOT_LENGTH + CERT_SERIAL_NUMBER_LENGTH + CERT_AUTHORITY_KEY_ID_LENGTH]; } _compressedCert; /* only raw EC X Y values 64 byte */ - byte _publicKey[CERT_PUBLIC_KEY_LENGTH]; - int _publicKeyLen; + const byte * _publicKey; byte * _certBuffer; int _certBufferLen;