Skip to content

Commit 75e8add

Browse files
committed
Add JWS and JSON Web Token example
1 parent ddec9b9 commit 75e8add

File tree

3 files changed

+308
-0
lines changed

3 files changed

+308
-0
lines changed

Diff for: examples/JSONWebToken/JSONWebToken.ino

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
ArduinoSecureElement - JSON Web Token
3+
4+
This sketch can be used to generate a JSON Web Token from a private key
5+
stored in an ECC508/ECC608 or SE050 crypto chip slot.
6+
7+
If the SecureElement is not configured and locked it prompts
8+
the user to configure and lock the chip with a default TLS
9+
configuration.
10+
11+
The user can also select the slot number to use for the private key.
12+
A new private key can also be generated in this slot.
13+
14+
The circuit:
15+
- A board equipped with ECC508 or ECC608 or SE050 chip
16+
17+
This example code is in the public domain.
18+
*/
19+
20+
#include <Arduino_SecureElement.h>
21+
#include <Arduino_JSON.h>
22+
#include <utility/SElementJWS.h>
23+
24+
void setup() {
25+
Serial.begin(9600);
26+
while (!Serial);
27+
28+
SecureElement secureElement;
29+
30+
if (!secureElement.begin()) {
31+
Serial.println("No SecureElement present!");
32+
while (1);
33+
}
34+
35+
String serialNumber = secureElement.serialNumber();
36+
37+
Serial.print("SecureElement Serial Number = ");
38+
Serial.println(serialNumber);
39+
Serial.println();
40+
41+
if (!secureElement.locked()) {
42+
String lock = promptAndReadLine("The SecureElement on your board is not locked, would you like to PERMANENTLY configure and lock it now? (y/N)", "N");
43+
lock.toLowerCase();
44+
45+
if (!lock.startsWith("y")) {
46+
Serial.println("Unfortunately you can't proceed without locking it :(");
47+
while (1);
48+
}
49+
50+
if (!secureElement.writeConfiguration()) {
51+
Serial.println("Writing SecureElement configuration failed!");
52+
while (1);
53+
}
54+
55+
if (!secureElement.lock()) {
56+
Serial.println("Locking SecureElement configuration failed!");
57+
while (1);
58+
}
59+
60+
Serial.println("SecureElement locked successfully");
61+
Serial.println();
62+
}
63+
64+
Serial.println("Hi there, in order to generate a PEM public key for your board, we'll need the following information ...");
65+
Serial.println();
66+
67+
String slot = promptAndReadLine("What slot would you like to use? (0 - 4)", "0");
68+
String generateNewKey = promptAndReadLine("Would you like to generate a new private key? (Y/n)", "Y");
69+
String issuer = promptAndReadLine("Issuer (Device UID)", "");
70+
String iat = promptAndReadLine("Issued at (Unix Timestamp)", "");
71+
String exp = promptAndReadLine("Expires at (Unix Timestamp)", "");
72+
73+
Serial.println();
74+
75+
generateNewKey.toLowerCase();
76+
77+
SElementJWS jws;
78+
79+
String publicKeyPem = jws.publicKey(secureElement, slot.toInt(), generateNewKey.startsWith("y"));
80+
81+
if (!publicKeyPem || publicKeyPem == "") {
82+
Serial.println("Error generating public key!");
83+
while (1);
84+
}
85+
86+
Serial.println("Here's your public key PEM, enjoy!");
87+
Serial.println();
88+
Serial.println(publicKeyPem);
89+
90+
JSONVar jwtHeader;
91+
JSONVar jwtClaim;
92+
93+
jwtHeader["alg"] = "ES256";
94+
jwtHeader["typ"] = "JWT";
95+
96+
jwtClaim["iss"] = issuer;
97+
jwtClaim["iat"] = iat.toInt();
98+
jwtClaim["exp"] = exp.toInt();
99+
100+
String token = jws.sign(secureElement, slot.toInt(), JSON.stringify(jwtHeader), JSON.stringify(jwtClaim));
101+
102+
Serial.println("Here's your JSON Web Token, enjoy!");
103+
Serial.println();
104+
Serial.println(token);
105+
}
106+
107+
void loop() {
108+
// do nothing
109+
}
110+
111+
String promptAndReadLine(const char* prompt, const char* defaultValue) {
112+
Serial.print(prompt);
113+
Serial.print(" [");
114+
Serial.print(defaultValue);
115+
Serial.print("]: ");
116+
117+
String s = readLine();
118+
119+
if (s.length() == 0) {
120+
s = defaultValue;
121+
}
122+
123+
Serial.println(s);
124+
125+
return s;
126+
}
127+
128+
String readLine() {
129+
String line;
130+
131+
while (1) {
132+
if (Serial.available()) {
133+
char c = Serial.read();
134+
135+
if (c == '\r') {
136+
// ignore
137+
continue;
138+
} else if (c == '\n') {
139+
break;
140+
}
141+
142+
line += c;
143+
}
144+
}
145+
146+
return line;
147+
}

Diff for: src/utility/SElementJWS.cpp

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*
2+
This file is part of the Arduino_SecureElement library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
/******************************************************************************
12+
* INCLUDE
13+
******************************************************************************/
14+
15+
#include <utility/SElementJWS.h>
16+
#include <utility/ASN1Utils.h>
17+
#include <utility/PEMUtils.h>
18+
19+
static String base64urlEncode(const byte in[], unsigned int length)
20+
{
21+
static const char* CODES = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
22+
23+
int b;
24+
String out;
25+
26+
int reserveLength = 4 * ((length + 2) / 3);
27+
out.reserve(reserveLength);
28+
29+
for (unsigned int i = 0; i < length; i += 3) {
30+
b = (in[i] & 0xFC) >> 2;
31+
out += CODES[b];
32+
33+
b = (in[i] & 0x03) << 4;
34+
if (i + 1 < length) {
35+
b |= (in[i + 1] & 0xF0) >> 4;
36+
out += CODES[b];
37+
b = (in[i + 1] & 0x0F) << 2;
38+
if (i + 2 < length) {
39+
b |= (in[i + 2] & 0xC0) >> 6;
40+
out += CODES[b];
41+
b = in[i + 2] & 0x3F;
42+
out += CODES[b];
43+
} else {
44+
out += CODES[b];
45+
}
46+
} else {
47+
out += CODES[b];
48+
}
49+
}
50+
51+
while (out.lastIndexOf('=') != -1) {
52+
out.remove(out.length() - 1);
53+
}
54+
55+
return out;
56+
}
57+
58+
String SElementJWS::publicKey(SecureElement & se, int slot, bool newPrivateKey)
59+
{
60+
if (slot < 0 || slot > 8) {
61+
return "";
62+
}
63+
64+
byte publicKey[64];
65+
66+
if (newPrivateKey) {
67+
if (!se.generatePrivateKey(slot, publicKey)) {
68+
return "";
69+
}
70+
} else {
71+
if (!se.generatePublicKey(slot, publicKey)) {
72+
return "";
73+
}
74+
}
75+
76+
int length = ASN1Utils.publicKeyLength();
77+
byte out[length];
78+
79+
ASN1Utils.appendPublicKey(publicKey, out);
80+
81+
return PEMUtils.base64Encode(out, length, "-----BEGIN PUBLIC KEY-----\n", "\n-----END PUBLIC KEY-----\n");
82+
}
83+
84+
String SElementJWS::sign(SecureElement & se, int slot, const char* header, const char* payload)
85+
{
86+
if (slot < 0 || slot > 8) {
87+
return "";
88+
}
89+
90+
String encodedHeader = base64urlEncode((const byte*)header, strlen(header));
91+
String encodedPayload = base64urlEncode((const byte*)payload, strlen(payload));
92+
93+
String toSign;
94+
toSign.reserve(encodedHeader.length() + 1 + encodedPayload.length());
95+
96+
toSign += encodedHeader;
97+
toSign += '.';
98+
toSign += encodedPayload;
99+
100+
101+
byte toSignSha256[32];
102+
byte signature[64];
103+
104+
se.SHA256((const uint8_t*)toSign.c_str(), toSign.length(), toSignSha256);
105+
106+
if (!se.ecSign(slot, toSignSha256, signature)) {
107+
return "";
108+
}
109+
110+
String encodedSignature = base64urlEncode(signature, sizeof(signature));
111+
112+
String result;
113+
result.reserve(toSign.length() + 1 + encodedSignature.length());
114+
115+
result += toSign;
116+
result += '.';
117+
result += encodedSignature;
118+
119+
return result;
120+
}
121+
122+
String SElementJWS::sign(SecureElement & se, int slot, const String& header, const String& payload)
123+
{
124+
return sign(se, slot, header.c_str(), payload.c_str());
125+
}

Diff for: src/utility/SElementJWS.h

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
This file is part of the Arduino_SecureElement library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
#ifndef SECURE_ELEMENT_JWS_H_
12+
#define SECURE_ELEMENT_JWS_H_
13+
14+
/******************************************************************************
15+
* INCLUDE
16+
******************************************************************************/
17+
18+
#include <Arduino_SecureElement.h>
19+
20+
/******************************************************************************
21+
* CLASS DECLARATION
22+
******************************************************************************/
23+
24+
class SElementJWS
25+
{
26+
public:
27+
28+
String publicKey(SecureElement & se, int slot, bool newPrivateKey = true);
29+
30+
String sign(SecureElement & se, int slot, const char* header, const char* payload);
31+
String sign(SecureElement & se, int slot, const String& header, const String& payload);
32+
33+
};
34+
35+
36+
#endif /* SECURE_ELEMENT_JWS_H_ */

0 commit comments

Comments
 (0)