diff --git a/src/ECP256Certificate.cpp b/src/ECP256Certificate.cpp index 1cf5de5..e146a2f 100644 --- a/src/ECP256Certificate.cpp +++ b/src/ECP256Certificate.cpp @@ -12,6 +12,9 @@ * INCLUDE ******************************************************************************/ +/* This is needed for memmem */ +#define _GNU_SOURCE +#include #include "ECP256Certificate.h" /****************************************************************************** @@ -300,6 +303,16 @@ int ECP256Certificate::importCert(const byte certDER[], size_t derLen) memcpy(_certBuffer, certDER, _certBufferLen); + /* Import Authority Key Identifier to compressed cert struct */ + if (!importCompressedAuthorityKeyIdentifier()) { + return 0; + } + + /* Import signature to compressed cert struct */ + if (!importCompressedSignature()) { + return 0; + } + return 1; } @@ -901,3 +914,80 @@ int ECP256Certificate::appendAuthorityKeyId(const byte authorityKeyId[], int len return length + 17; } + +int ECP256Certificate::importCompressedAuthorityKeyIdentifier() { + static const byte objectId[] = {0x06, 0x03, 0x55, 0x1D, 0x23}; + byte * result = nullptr; + void * ptr = memmem(_certBuffer, _certBufferLen, objectId, sizeof(objectId)); + if (ptr != nullptr) { + result = (byte*)ptr; + result += 11; + memcpy(_compressedCert.slot.two.values.authorityKeyId, result, ECP256_CERT_AUTHORITY_KEY_ID_LENGTH); + return 1; + } + return 0; +} + +int ECP256Certificate::importCompressedSignature() { + byte * result = nullptr; + byte paddingBytes = 0; + byte rLen = 0; + byte sLen = 0; + + /* Search AuthorityKeyIdentifier */ + static const byte KeyId[] = {0x06, 0x03, 0x55, 0x1D, 0x23}; + void * ptr = memmem(_certBuffer, _certBufferLen, KeyId, sizeof(KeyId)); + if(ptr == nullptr) { + return 0; + } + result = (byte*)ptr; + + /* Search Algorithm identifier */ + static const byte AlgId[] = {0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02}; + ptr = memmem(result, _certBufferLen - (_certBuffer - result), AlgId, sizeof(AlgId)); + if(ptr == nullptr) { + return 0; + } + result = (byte*)ptr; + + /* Skip algorithm identifier */ + result += sizeof(AlgId); + + /* Search signature sequence */ + if (result[0] == 0x03) { + /* Move to the first element of R sequence skipping 0x03 0x49 0x00 0x30 0xXX*/ + result += 5; + /* Check if value is padded */ + if (result[0] == 0x02 && result[1] == 0x21 && result[2] == 0x00) { + paddingBytes = 1; + } + rLen = result[1] - paddingBytes; + /* Skip padding and ASN INTEGER sequence 0x02 0xXX */ + result += (2 + paddingBytes); + /* Check data length */ + if (rLen != ECP256_CERT_SIGNATURE_R_LENGTH) { + return 0; + } + /* Copy data to compressed slot */ + memcpy(_compressedCert.slot.one.values.signature, result, rLen); + /* reset padding before importing S sequence */ + paddingBytes = 0; + /* Move to the first element of S sequence skipping R values */ + result += rLen; + /* Check if value is padded */ + if (result[0] == 0x02 && result[1] == 0x21 && result[2] == 0x00) { + paddingBytes = 1; + } + sLen = result[1] - paddingBytes; + /* Skip padding and ASN INTEGER sequence 0x02 0xXX */ + result += (2 + paddingBytes); + /* Check data length */ + if (sLen != ECP256_CERT_SIGNATURE_S_LENGTH) { + return 0; + } + /* Copy data to compressed slot */ + memcpy(&_compressedCert.slot.one.values.signature[rLen], result, sLen); + return 1; + } + return 0; +} diff --git a/src/ECP256Certificate.h b/src/ECP256Certificate.h index 4e065df..2be7c5c 100644 --- a/src/ECP256Certificate.h +++ b/src/ECP256Certificate.h @@ -22,10 +22,12 @@ #define ECP256_CERT_SERIAL_NUMBER_LENGTH 16 #define ECP256_CERT_AUTHORITY_KEY_ID_LENGTH 20 #define ECP256_CERT_PUBLIC_KEY_LENGTH 64 -#define ECP256_CERT_SIGNATURE_LENGTH 64 +#define ECP256_CERT_SIGNATURE_R_LENGTH 32 +#define ECP256_CERT_SIGNATURE_S_LENGTH ECP256_CERT_SIGNATURE_R_LENGTH +#define ECP256_CERT_SIGNATURE_LENGTH (ECP256_CERT_SIGNATURE_R_LENGTH + ECP256_CERT_SIGNATURE_S_LENGTH) #define ECP256_CERT_DATES_LENGTH 3 #define ECP256_CERT_COMPRESSED_CERT_SLOT_LENGTH 72 -#define ECP256_CERT_COMPRESSED_CERT_LENGTH ECP256_CERT_COMPRESSED_CERT_SLOT_LENGTH + ECP256_CERT_SERIAL_NUMBER_LENGTH + ECP256_CERT_AUTHORITY_KEY_ID_LENGTH +#define ECP256_CERT_COMPRESSED_CERT_LENGTH (ECP256_CERT_COMPRESSED_CERT_SLOT_LENGTH + ECP256_CERT_SERIAL_NUMBER_LENGTH + ECP256_CERT_AUTHORITY_KEY_ID_LENGTH) #include @@ -81,6 +83,9 @@ class ECP256Certificate { inline byte* subjectCommonNameBytes() { return (byte*)_subjectData.commonName.begin(); } inline int subjectCommonNameLenght() {return _subjectData.commonName.length(); } + inline const byte* authorityKeyIdentifierBytes() { return _compressedCert.slot.two.values.authorityKeyId; } + inline const byte* signatureBytes() { return _compressedCert.slot.one.values.signature; } + /* Build CSR */ int buildCSR(); int signCSR(byte signature[]); @@ -173,6 +178,9 @@ class ECP256Certificate { int appendEcdsaWithSHA256(byte out[]); int appendAuthorityKeyId(const byte authorityKeyId[], int length, byte out[]); + int importCompressedAuthorityKeyIdentifier(); + int importCompressedSignature(); + }; #endif /* ECP256_CERTIFICATE_H */ diff --git a/src/SecureElementConfig.h b/src/SecureElementConfig.h index 12815d0..064afb7 100644 --- a/src/SecureElementConfig.h +++ b/src/SecureElementConfig.h @@ -29,4 +29,30 @@ #define SECURE_ELEMENT_IS_SOFTSE #endif +#if defined __has_include + #if __has_include () + #include + #endif +#endif + +#ifndef DEBUG_ERROR + #define DEBUG_ERROR(fmt, ...) +#endif + +#ifndef DEBUG_WARNING + #define DEBUG_WARNING(fmt, ...) +#endif + +#ifndef DEBUG_INFO + #define DEBUG_INFO(fmt, ...) +#endif + +#ifndef DEBUG_DEBUG + #define DEBUG_DEBUG(fmt, ...) +#endif + +#ifndef DEBUG_VERBOSE + #define DEBUG_VERBOSE(fmt, ...) +#endif + #endif /* SECURE_ELEMENT_CONFIG_H_ */ diff --git a/src/utility/SElementArduinoCloudCertificate.cpp b/src/utility/SElementArduinoCloudCertificate.cpp index e44ccd2..598e5a5 100644 --- a/src/utility/SElementArduinoCloudCertificate.cpp +++ b/src/utility/SElementArduinoCloudCertificate.cpp @@ -14,6 +14,39 @@ #include +/****************************************************************************** + * LOCAL MODULE FUNCTIONS + ******************************************************************************/ + +static void hexStringToBytes(String in, byte out[], int length) { + int inLength = in.length(); + in.toUpperCase(); + int outLength = 0; + + for (int i = 0; i < inLength && outLength < length; i += 2) { + char highChar = in[i]; + char lowChar = in[i + 1]; + + byte highByte = (highChar <= '9') ? (highChar - '0') : (highChar + 10 - 'A'); + byte lowByte = (lowChar <= '9') ? (lowChar - '0') : (lowChar + 10 - 'A'); + + out[outLength++] = (highByte << 4) | (lowByte & 0xF); + } +} + +/****************************************************************************** + * STATIC MEMBER DEFINITIONS + ******************************************************************************/ + +const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_COUNTRY_NAME[]; +const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_ORGANIZATION_NAME[]; +const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME[]; +const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_COMMON_NAME[]; + +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + int SElementArduinoCloudCertificate::write(SecureElement & se, ECP256Certificate & cert, const SElementArduinoCloudSlot certSlot) { #if defined(SECURE_ELEMENT_IS_SE050) || defined(SECURE_ELEMENT_IS_SOFTSE) @@ -73,10 +106,10 @@ int SElementArduinoCloudCertificate::read(SecureElement & se, ECP256Certificate } cert.setSubjectCommonName(deviceId); - cert.setIssuerCountryName("US"); - cert.setIssuerOrganizationName("Arduino LLC US"); - cert.setIssuerOrganizationalUnitName("IT"); - cert.setIssuerCommonName("Arduino"); + cert.setIssuerCountryName(SEACC_ISSUER_COUNTRY_NAME); + cert.setIssuerOrganizationName(SEACC_ISSUER_ORGANIZATION_NAME); + cert.setIssuerOrganizationalUnitName(SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME); + cert.setIssuerCommonName(SEACC_ISSUER_COMMON_NAME); if (!cert.setPublicKey(publicKey, ECP256_CERT_PUBLIC_KEY_LENGTH)) { return 0; @@ -92,3 +125,68 @@ int SElementArduinoCloudCertificate::read(SecureElement & se, ECP256Certificate #endif return 1; } + +int SElementArduinoCloudCertificate::signatureCompare(const byte * signatureA, const String & signatureB) +{ + byte signatureBytes[ECP256_CERT_SIGNATURE_LENGTH]; + + if (signatureB.length() == 0 || signatureA == nullptr) { + DEBUG_ERROR("SEACC::%s input params error.", __FUNCTION__); + return -1; + } + + hexStringToBytes(signatureB, signatureBytes, sizeof(signatureBytes)); + + /* If authorityKeyId are matching there is no need to rebuild*/ + if (memcmp(signatureBytes, signatureA , sizeof(signatureBytes)) == 0) { + DEBUG_VERBOSE("SEACC::%s signatures are equal", __FUNCTION__); + return 0; + } + return 1; +} + +int SElementArduinoCloudCertificate::rebuild( + SecureElement & se, ECP256Certificate & cert, const String & deviceId, + const String & notBefore, const String & notAfter, const String & serialNumber, + const String & authorityKeyIdentifier, const String & signature, + const SElementArduinoCloudSlot keySlot) +{ + byte serialNumberBytes[ECP256_CERT_SERIAL_NUMBER_LENGTH]; + byte authorityKeyIdentifierBytes[ECP256_CERT_AUTHORITY_KEY_ID_LENGTH]; + byte signatureBytes[ECP256_CERT_SIGNATURE_LENGTH]; + + if (!deviceId.length() || !notBefore.length() || !notAfter.length() || !serialNumber.length() || !authorityKeyIdentifier.length() || !signature.length() ) { + DEBUG_ERROR("SEACC::%s input params error.", __FUNCTION__); + return 0; + } + + hexStringToBytes(serialNumber, serialNumberBytes, sizeof(serialNumberBytes)); + hexStringToBytes(authorityKeyIdentifier, authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); + hexStringToBytes(signature, signatureBytes, sizeof(signatureBytes)); + + if (!cert.begin()) { + DEBUG_ERROR("SEACC::%s cert begin error", __FUNCTION__); + return -1; + } + + cert.setSubjectCommonName(deviceId); + cert.setIssuerCountryName(SEACC_ISSUER_COUNTRY_NAME); + cert.setIssuerOrganizationName(SEACC_ISSUER_ORGANIZATION_NAME); + cert.setIssuerOrganizationalUnitName(SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME); + cert.setIssuerCommonName(SEACC_ISSUER_COMMON_NAME); + cert.setSignature(signatureBytes, sizeof(signatureBytes)); + cert.setAuthorityKeyId(authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes)); + cert.setSerialNumber(serialNumberBytes, sizeof(serialNumberBytes)); + cert.setIssueYear(notBefore.substring(0,4).toInt()); + cert.setIssueMonth(notBefore.substring(5,7).toInt()); + cert.setIssueDay(notBefore.substring(8,10).toInt()); + cert.setIssueHour(notBefore.substring(11,13).toInt()); + cert.setExpireYears(notAfter.substring(0,4).toInt() - notBefore.substring(0,4).toInt()); + + + if (!SElementCertificate::build(se, cert, static_cast(keySlot))) { + DEBUG_ERROR("SEACC::%s cert build error", __FUNCTION__); + return -1; + } + return 1; +} diff --git a/src/utility/SElementArduinoCloudCertificate.h b/src/utility/SElementArduinoCloudCertificate.h index f6a1284..d20559a 100644 --- a/src/utility/SElementArduinoCloudCertificate.h +++ b/src/utility/SElementArduinoCloudCertificate.h @@ -28,6 +28,18 @@ class SElementArduinoCloudCertificate : public SElementCertificate static int write(SecureElement & se, ECP256Certificate & cert, const SElementArduinoCloudSlot certSlot); static int read(SecureElement & se, ECP256Certificate & cert, const SElementArduinoCloudSlot certSlot, const SElementArduinoCloudSlot keySlot = SElementArduinoCloudSlot::Key); + static int signatureCompare(const byte * signatureA, const String & signatureB); + static int rebuild(SecureElement & se, ECP256Certificate & cert, const String & deviceId, + const String & notBefore, const String & notAfter, const String & serialNumber, + const String & authorityKeyIdentifier, const String & signature, + const SElementArduinoCloudSlot keySlot = SElementArduinoCloudSlot::Key); + +private: + + static const char constexpr SEACC_ISSUER_COUNTRY_NAME[] = "US"; + static const char constexpr SEACC_ISSUER_ORGANIZATION_NAME[] = "Arduino LLC US"; + static const char constexpr SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME[] = "IT"; + static const char constexpr SEACC_ISSUER_COMMON_NAME[] = "Arduino"; };