Skip to content

Commit 96f4a04

Browse files
authored
Merge pull request #21 from pennam/ca-rebuild
Add function to rebuild ArduinoCloud device certificate
2 parents badc8a6 + b94975b commit 96f4a04

5 files changed

+240
-6
lines changed

src/ECP256Certificate.cpp

+90
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
* INCLUDE
1313
******************************************************************************/
1414

15+
/* This is needed for memmem */
16+
#define _GNU_SOURCE
17+
#include <string.h>
1518
#include "ECP256Certificate.h"
1619

1720
/******************************************************************************
@@ -300,6 +303,16 @@ int ECP256Certificate::importCert(const byte certDER[], size_t derLen)
300303

301304
memcpy(_certBuffer, certDER, _certBufferLen);
302305

306+
/* Import Authority Key Identifier to compressed cert struct */
307+
if (!importCompressedAuthorityKeyIdentifier()) {
308+
return 0;
309+
}
310+
311+
/* Import signature to compressed cert struct */
312+
if (!importCompressedSignature()) {
313+
return 0;
314+
}
315+
303316
return 1;
304317
}
305318

@@ -901,3 +914,80 @@ int ECP256Certificate::appendAuthorityKeyId(const byte authorityKeyId[], int len
901914

902915
return length + 17;
903916
}
917+
918+
int ECP256Certificate::importCompressedAuthorityKeyIdentifier() {
919+
static const byte objectId[] = {0x06, 0x03, 0x55, 0x1D, 0x23};
920+
byte * result = nullptr;
921+
void * ptr = memmem(_certBuffer, _certBufferLen, objectId, sizeof(objectId));
922+
if (ptr != nullptr) {
923+
result = (byte*)ptr;
924+
result += 11;
925+
memcpy(_compressedCert.slot.two.values.authorityKeyId, result, ECP256_CERT_AUTHORITY_KEY_ID_LENGTH);
926+
return 1;
927+
}
928+
return 0;
929+
}
930+
931+
int ECP256Certificate::importCompressedSignature() {
932+
byte * result = nullptr;
933+
byte paddingBytes = 0;
934+
byte rLen = 0;
935+
byte sLen = 0;
936+
937+
/* Search AuthorityKeyIdentifier */
938+
static const byte KeyId[] = {0x06, 0x03, 0x55, 0x1D, 0x23};
939+
void * ptr = memmem(_certBuffer, _certBufferLen, KeyId, sizeof(KeyId));
940+
if(ptr == nullptr) {
941+
return 0;
942+
}
943+
result = (byte*)ptr;
944+
945+
/* Search Algorithm identifier */
946+
static const byte AlgId[] = {0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02};
947+
ptr = memmem(result, _certBufferLen - (_certBuffer - result), AlgId, sizeof(AlgId));
948+
if(ptr == nullptr) {
949+
return 0;
950+
}
951+
result = (byte*)ptr;
952+
953+
/* Skip algorithm identifier */
954+
result += sizeof(AlgId);
955+
956+
/* Search signature sequence */
957+
if (result[0] == 0x03) {
958+
/* Move to the first element of R sequence skipping 0x03 0x49 0x00 0x30 0xXX*/
959+
result += 5;
960+
/* Check if value is padded */
961+
if (result[0] == 0x02 && result[1] == 0x21 && result[2] == 0x00) {
962+
paddingBytes = 1;
963+
}
964+
rLen = result[1] - paddingBytes;
965+
/* Skip padding and ASN INTEGER sequence 0x02 0xXX */
966+
result += (2 + paddingBytes);
967+
/* Check data length */
968+
if (rLen != ECP256_CERT_SIGNATURE_R_LENGTH) {
969+
return 0;
970+
}
971+
/* Copy data to compressed slot */
972+
memcpy(_compressedCert.slot.one.values.signature, result, rLen);
973+
/* reset padding before importing S sequence */
974+
paddingBytes = 0;
975+
/* Move to the first element of S sequence skipping R values */
976+
result += rLen;
977+
/* Check if value is padded */
978+
if (result[0] == 0x02 && result[1] == 0x21 && result[2] == 0x00) {
979+
paddingBytes = 1;
980+
}
981+
sLen = result[1] - paddingBytes;
982+
/* Skip padding and ASN INTEGER sequence 0x02 0xXX */
983+
result += (2 + paddingBytes);
984+
/* Check data length */
985+
if (sLen != ECP256_CERT_SIGNATURE_S_LENGTH) {
986+
return 0;
987+
}
988+
/* Copy data to compressed slot */
989+
memcpy(&_compressedCert.slot.one.values.signature[rLen], result, sLen);
990+
return 1;
991+
}
992+
return 0;
993+
}

src/ECP256Certificate.h

+10-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
#define ECP256_CERT_SERIAL_NUMBER_LENGTH 16
2323
#define ECP256_CERT_AUTHORITY_KEY_ID_LENGTH 20
2424
#define ECP256_CERT_PUBLIC_KEY_LENGTH 64
25-
#define ECP256_CERT_SIGNATURE_LENGTH 64
25+
#define ECP256_CERT_SIGNATURE_R_LENGTH 32
26+
#define ECP256_CERT_SIGNATURE_S_LENGTH ECP256_CERT_SIGNATURE_R_LENGTH
27+
#define ECP256_CERT_SIGNATURE_LENGTH (ECP256_CERT_SIGNATURE_R_LENGTH + ECP256_CERT_SIGNATURE_S_LENGTH)
2628
#define ECP256_CERT_DATES_LENGTH 3
2729
#define ECP256_CERT_COMPRESSED_CERT_SLOT_LENGTH 72
28-
#define ECP256_CERT_COMPRESSED_CERT_LENGTH ECP256_CERT_COMPRESSED_CERT_SLOT_LENGTH + ECP256_CERT_SERIAL_NUMBER_LENGTH + ECP256_CERT_AUTHORITY_KEY_ID_LENGTH
30+
#define ECP256_CERT_COMPRESSED_CERT_LENGTH (ECP256_CERT_COMPRESSED_CERT_SLOT_LENGTH + ECP256_CERT_SERIAL_NUMBER_LENGTH + ECP256_CERT_AUTHORITY_KEY_ID_LENGTH)
2931

3032
#include <Arduino.h>
3133

@@ -81,6 +83,9 @@ class ECP256Certificate {
8183
inline byte* subjectCommonNameBytes() { return (byte*)_subjectData.commonName.begin(); }
8284
inline int subjectCommonNameLenght() {return _subjectData.commonName.length(); }
8385

86+
inline const byte* authorityKeyIdentifierBytes() { return _compressedCert.slot.two.values.authorityKeyId; }
87+
inline const byte* signatureBytes() { return _compressedCert.slot.one.values.signature; }
88+
8489
/* Build CSR */
8590
int buildCSR();
8691
int signCSR(byte signature[]);
@@ -173,6 +178,9 @@ class ECP256Certificate {
173178
int appendEcdsaWithSHA256(byte out[]);
174179
int appendAuthorityKeyId(const byte authorityKeyId[], int length, byte out[]);
175180

181+
int importCompressedAuthorityKeyIdentifier();
182+
int importCompressedSignature();
183+
176184
};
177185

178186
#endif /* ECP256_CERTIFICATE_H */

src/SecureElementConfig.h

+26
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,30 @@
2929
#define SECURE_ELEMENT_IS_SOFTSE
3030
#endif
3131

32+
#if defined __has_include
33+
#if __has_include (<Arduino_DebugUtils.h>)
34+
#include <Arduino_DebugUtils.h>
35+
#endif
36+
#endif
37+
38+
#ifndef DEBUG_ERROR
39+
#define DEBUG_ERROR(fmt, ...)
40+
#endif
41+
42+
#ifndef DEBUG_WARNING
43+
#define DEBUG_WARNING(fmt, ...)
44+
#endif
45+
46+
#ifndef DEBUG_INFO
47+
#define DEBUG_INFO(fmt, ...)
48+
#endif
49+
50+
#ifndef DEBUG_DEBUG
51+
#define DEBUG_DEBUG(fmt, ...)
52+
#endif
53+
54+
#ifndef DEBUG_VERBOSE
55+
#define DEBUG_VERBOSE(fmt, ...)
56+
#endif
57+
3258
#endif /* SECURE_ELEMENT_CONFIG_H_ */

src/utility/SElementArduinoCloudCertificate.cpp

+102-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,39 @@
1414

1515
#include <utility/SElementArduinoCloudCertificate.h>
1616

17+
/******************************************************************************
18+
* LOCAL MODULE FUNCTIONS
19+
******************************************************************************/
20+
21+
static void hexStringToBytes(String in, byte out[], int length) {
22+
int inLength = in.length();
23+
in.toUpperCase();
24+
int outLength = 0;
25+
26+
for (int i = 0; i < inLength && outLength < length; i += 2) {
27+
char highChar = in[i];
28+
char lowChar = in[i + 1];
29+
30+
byte highByte = (highChar <= '9') ? (highChar - '0') : (highChar + 10 - 'A');
31+
byte lowByte = (lowChar <= '9') ? (lowChar - '0') : (lowChar + 10 - 'A');
32+
33+
out[outLength++] = (highByte << 4) | (lowByte & 0xF);
34+
}
35+
}
36+
37+
/******************************************************************************
38+
* STATIC MEMBER DEFINITIONS
39+
******************************************************************************/
40+
41+
const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_COUNTRY_NAME[];
42+
const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_ORGANIZATION_NAME[];
43+
const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME[];
44+
const char constexpr SElementArduinoCloudCertificate::SEACC_ISSUER_COMMON_NAME[];
45+
46+
/******************************************************************************
47+
* PUBLIC MEMBER FUNCTIONS
48+
******************************************************************************/
49+
1750
int SElementArduinoCloudCertificate::write(SecureElement & se, ECP256Certificate & cert, const SElementArduinoCloudSlot certSlot)
1851
{
1952
#if defined(SECURE_ELEMENT_IS_SE050) || defined(SECURE_ELEMENT_IS_SOFTSE)
@@ -73,10 +106,10 @@ int SElementArduinoCloudCertificate::read(SecureElement & se, ECP256Certificate
73106
}
74107

75108
cert.setSubjectCommonName(deviceId);
76-
cert.setIssuerCountryName("US");
77-
cert.setIssuerOrganizationName("Arduino LLC US");
78-
cert.setIssuerOrganizationalUnitName("IT");
79-
cert.setIssuerCommonName("Arduino");
109+
cert.setIssuerCountryName(SEACC_ISSUER_COUNTRY_NAME);
110+
cert.setIssuerOrganizationName(SEACC_ISSUER_ORGANIZATION_NAME);
111+
cert.setIssuerOrganizationalUnitName(SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME);
112+
cert.setIssuerCommonName(SEACC_ISSUER_COMMON_NAME);
80113

81114
if (!cert.setPublicKey(publicKey, ECP256_CERT_PUBLIC_KEY_LENGTH)) {
82115
return 0;
@@ -92,3 +125,68 @@ int SElementArduinoCloudCertificate::read(SecureElement & se, ECP256Certificate
92125
#endif
93126
return 1;
94127
}
128+
129+
int SElementArduinoCloudCertificate::signatureCompare(const byte * signatureA, const String & signatureB)
130+
{
131+
byte signatureBytes[ECP256_CERT_SIGNATURE_LENGTH];
132+
133+
if (signatureB.length() == 0 || signatureA == nullptr) {
134+
DEBUG_ERROR("SEACC::%s input params error.", __FUNCTION__);
135+
return -1;
136+
}
137+
138+
hexStringToBytes(signatureB, signatureBytes, sizeof(signatureBytes));
139+
140+
/* If authorityKeyId are matching there is no need to rebuild*/
141+
if (memcmp(signatureBytes, signatureA , sizeof(signatureBytes)) == 0) {
142+
DEBUG_VERBOSE("SEACC::%s signatures are equal", __FUNCTION__);
143+
return 0;
144+
}
145+
return 1;
146+
}
147+
148+
int SElementArduinoCloudCertificate::rebuild(
149+
SecureElement & se, ECP256Certificate & cert, const String & deviceId,
150+
const String & notBefore, const String & notAfter, const String & serialNumber,
151+
const String & authorityKeyIdentifier, const String & signature,
152+
const SElementArduinoCloudSlot keySlot)
153+
{
154+
byte serialNumberBytes[ECP256_CERT_SERIAL_NUMBER_LENGTH];
155+
byte authorityKeyIdentifierBytes[ECP256_CERT_AUTHORITY_KEY_ID_LENGTH];
156+
byte signatureBytes[ECP256_CERT_SIGNATURE_LENGTH];
157+
158+
if (!deviceId.length() || !notBefore.length() || !notAfter.length() || !serialNumber.length() || !authorityKeyIdentifier.length() || !signature.length() ) {
159+
DEBUG_ERROR("SEACC::%s input params error.", __FUNCTION__);
160+
return 0;
161+
}
162+
163+
hexStringToBytes(serialNumber, serialNumberBytes, sizeof(serialNumberBytes));
164+
hexStringToBytes(authorityKeyIdentifier, authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes));
165+
hexStringToBytes(signature, signatureBytes, sizeof(signatureBytes));
166+
167+
if (!cert.begin()) {
168+
DEBUG_ERROR("SEACC::%s cert begin error", __FUNCTION__);
169+
return -1;
170+
}
171+
172+
cert.setSubjectCommonName(deviceId);
173+
cert.setIssuerCountryName(SEACC_ISSUER_COUNTRY_NAME);
174+
cert.setIssuerOrganizationName(SEACC_ISSUER_ORGANIZATION_NAME);
175+
cert.setIssuerOrganizationalUnitName(SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME);
176+
cert.setIssuerCommonName(SEACC_ISSUER_COMMON_NAME);
177+
cert.setSignature(signatureBytes, sizeof(signatureBytes));
178+
cert.setAuthorityKeyId(authorityKeyIdentifierBytes, sizeof(authorityKeyIdentifierBytes));
179+
cert.setSerialNumber(serialNumberBytes, sizeof(serialNumberBytes));
180+
cert.setIssueYear(notBefore.substring(0,4).toInt());
181+
cert.setIssueMonth(notBefore.substring(5,7).toInt());
182+
cert.setIssueDay(notBefore.substring(8,10).toInt());
183+
cert.setIssueHour(notBefore.substring(11,13).toInt());
184+
cert.setExpireYears(notAfter.substring(0,4).toInt() - notBefore.substring(0,4).toInt());
185+
186+
187+
if (!SElementCertificate::build(se, cert, static_cast<int>(keySlot))) {
188+
DEBUG_ERROR("SEACC::%s cert build error", __FUNCTION__);
189+
return -1;
190+
}
191+
return 1;
192+
}

src/utility/SElementArduinoCloudCertificate.h

+12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ class SElementArduinoCloudCertificate : public SElementCertificate
2828

2929
static int write(SecureElement & se, ECP256Certificate & cert, const SElementArduinoCloudSlot certSlot);
3030
static int read(SecureElement & se, ECP256Certificate & cert, const SElementArduinoCloudSlot certSlot, const SElementArduinoCloudSlot keySlot = SElementArduinoCloudSlot::Key);
31+
static int signatureCompare(const byte * signatureA, const String & signatureB);
32+
static int rebuild(SecureElement & se, ECP256Certificate & cert, const String & deviceId,
33+
const String & notBefore, const String & notAfter, const String & serialNumber,
34+
const String & authorityKeyIdentifier, const String & signature,
35+
const SElementArduinoCloudSlot keySlot = SElementArduinoCloudSlot::Key);
36+
37+
private:
38+
39+
static const char constexpr SEACC_ISSUER_COUNTRY_NAME[] = "US";
40+
static const char constexpr SEACC_ISSUER_ORGANIZATION_NAME[] = "Arduino LLC US";
41+
static const char constexpr SEACC_ISSUER_ORGANIZATIONAL_UNIT_NAME[] = "IT";
42+
static const char constexpr SEACC_ISSUER_COMMON_NAME[] = "Arduino";
3143

3244
};
3345

0 commit comments

Comments
 (0)