Rick Watson
5 years ago
10 changed files with 165 additions and 90 deletions
-
96src/ArduinoJsonJWT.cpp
-
34src/ArduinoJsonJWT.h
-
31src/SecurityManager.cpp
-
5src/SecurityManager.h
-
2src/base64.cpp
-
0src/base64.h
-
50src/jwt/ArduinoJsonJWT.cpp
-
34src/jwt/ArduinoJsonJWT.h
-
3src/sha256.cpp
-
0src/sha256.h
@ -0,0 +1,96 @@ |
|||
#include "ArduinoJsonJWT.h"
|
|||
|
|||
ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) { } |
|||
|
|||
void ArduinoJsonJWT::setSecret(String secret){ |
|||
_secret = secret; |
|||
} |
|||
|
|||
String ArduinoJsonJWT::sign(String &value) { |
|||
// create signature
|
|||
Sha256.initHmac((uint8_t*) _secret.c_str(), _secret.length()); |
|||
Sha256.print(value); |
|||
|
|||
// trim and return
|
|||
return encode(Sha256.resultHmac(), 32); |
|||
} |
|||
|
|||
String ArduinoJsonJWT::decode(unsigned char * value) { |
|||
// create buffer of approperate length
|
|||
size_t decodedLength = decode_base64_length(value) + 1; |
|||
char decoded[decodedLength]; |
|||
|
|||
// decode
|
|||
decode_base64(value, (unsigned char *) decoded); |
|||
decoded[decodedLength-1] = 0; |
|||
|
|||
// return as arduino string
|
|||
return String(decoded); |
|||
} |
|||
|
|||
String ArduinoJsonJWT::encode(unsigned char * value , int length) { |
|||
int encodedIndex = encode_base64_length(length); |
|||
|
|||
// encode
|
|||
char encoded[encodedIndex]; |
|||
encode_base64(value, length, (unsigned char *) encoded); |
|||
|
|||
// trim padding
|
|||
while (encoded[--encodedIndex] == '=') { |
|||
encoded[encodedIndex] = 0; |
|||
} |
|||
|
|||
// return as string
|
|||
return String(encoded); |
|||
} |
|||
|
|||
String ArduinoJsonJWT::buildJWT(JsonObject &payload) { |
|||
// serialize, then encode payload
|
|||
String jwt; |
|||
serializeJson(payload, jwt); |
|||
jwt = encode((unsigned char *) jwt.c_str(), jwt.length()); |
|||
|
|||
// add the header to payload
|
|||
jwt = JWT_HEADER + '.' + jwt; |
|||
|
|||
// add signature
|
|||
jwt += '.' + sign(jwt); |
|||
|
|||
return jwt; |
|||
} |
|||
|
|||
void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument &jsonDocument) { |
|||
// clear json document before we begin, jsonDocument wil be null on failure
|
|||
jsonDocument.clear(); |
|||
|
|||
// must be of minimum length or greater
|
|||
if (jwt.length() <= JWT_SIG_SIZE + JWT_HEADER_SIZE + 2) { |
|||
return; |
|||
} |
|||
// must have the correct header and delimiter
|
|||
if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) { |
|||
return; |
|||
} |
|||
// must have signature of correct length
|
|||
int signatureDelimieterIndex = jwt.length() - JWT_SIG_SIZE - 1; |
|||
if (jwt.lastIndexOf('.') != signatureDelimieterIndex) { |
|||
return; |
|||
} |
|||
|
|||
// signature must be correct
|
|||
String signature = jwt.substring(signatureDelimieterIndex + 1); |
|||
jwt = jwt.substring(0, signatureDelimieterIndex); |
|||
if (sign(jwt) != signature){ |
|||
return; |
|||
} |
|||
|
|||
// decode payload
|
|||
jwt = jwt.substring(JWT_HEADER_SIZE + 1); |
|||
jwt = decode((unsigned char *) jwt.c_str()); |
|||
|
|||
// parse payload, clearing json document after failure
|
|||
DeserializationError error = deserializeJson(jsonDocument, jwt); |
|||
if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()){ |
|||
jsonDocument.clear(); |
|||
} |
|||
} |
@ -0,0 +1,34 @@ |
|||
#ifndef ArduinoJsonJWT_H |
|||
#define ArduinoJsonJWT_H |
|||
|
|||
#include "sha256.h" |
|||
#include "base64.h" |
|||
|
|||
#include <Arduino.h> |
|||
#include <ArduinoJson.h> |
|||
|
|||
#define JWT_HEADER_SIZE 36 |
|||
#define JWT_SIG_SIZE 43 |
|||
|
|||
class ArduinoJsonJWT { |
|||
|
|||
private: |
|||
String _secret; |
|||
|
|||
// {"alg": "HS256", "typ": "JWT"} |
|||
const String JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; |
|||
|
|||
String sign(String &value); |
|||
String encode(unsigned char * value, int length); |
|||
String decode(unsigned char * value); |
|||
|
|||
public: |
|||
ArduinoJsonJWT(String secret); |
|||
|
|||
void setSecret(String secret); |
|||
String buildJWT(JsonObject &payload); |
|||
void parseJWT(String jwt, JsonDocument &jsonDocument); |
|||
}; |
|||
|
|||
|
|||
#endif |
@ -1,3 +1,5 @@ |
|||
#include "base64.h"
|
|||
|
|||
unsigned char binary_to_base64(unsigned char v) { |
|||
// Capital letters - 'A' is ascii 65 and base64 0
|
|||
if(v < 26) return v + 'A'; |
@ -1,50 +0,0 @@ |
|||
#include "jwt/ArduinoJsonJWT.h"
|
|||
|
|||
ArduinoJsonJWT::ArduinoJsonJWT(String psk) : _psk(psk) { } |
|||
|
|||
void ArduinoJsonJWT::setPSK(String psk){ |
|||
_psk = psk; |
|||
} |
|||
|
|||
String ArduinoJsonJWT::encodeJWT(JsonObject payload) { |
|||
// serialize payload
|
|||
String serializedPayload; |
|||
serializeJson(payload, serializedPayload); |
|||
|
|||
// calculate length of string
|
|||
uint16_t encodedPayloadLength = encode_base64_length(serializedPayload.length()); |
|||
|
|||
// create JWT char array
|
|||
char encodedJWT[BASE_JWT_LENGTH + encodedPayloadLength]; |
|||
unsigned char* ptr = (unsigned char*) encodedJWT; |
|||
|
|||
// 1 - add the header
|
|||
memcpy(ptr, JWT_HEADER, JWT_HEADER_LENGTH); |
|||
ptr += JWT_HEADER_LENGTH; |
|||
|
|||
// 2 - add payload, trim and null terminate
|
|||
*ptr++ = '.'; |
|||
encode_base64((unsigned char*) serializedPayload.c_str(), serializedPayload.length(), ptr); |
|||
ptr += encodedPayloadLength; |
|||
while(*(ptr - 1) == '=') { |
|||
ptr--; |
|||
} |
|||
*(ptr) = 0; |
|||
|
|||
// ... calculate ...
|
|||
Sha256.initHmac((const unsigned char*)_psk.c_str(), _psk.length()); |
|||
Sha256.print(encodedJWT); |
|||
|
|||
// 3 - add signature
|
|||
*ptr++ = '.'; |
|||
encode_base64(Sha256.resultHmac(), 32, ptr); |
|||
ptr += SIGNATURE_LENGTH; |
|||
while(*(ptr - 1) == '=') { |
|||
ptr--; |
|||
} |
|||
*(ptr) = 0; |
|||
|
|||
Serial.println(BASE_JWT_LENGTH + encodedPayloadLength); |
|||
return encodedJWT; |
|||
} |
|||
|
@ -1,34 +0,0 @@ |
|||
#ifndef ArduinoJsonJWT_H |
|||
#define ArduinoJsonJWT_H |
|||
|
|||
#include "jwt/base64.h" |
|||
#include "jwt/sha256.h" |
|||
#include "jwt/ArduinoJsonJWT.h" |
|||
|
|||
#include <Arduino.h> |
|||
#include <ArduinoJson.h> |
|||
|
|||
class ArduinoJsonJWT { |
|||
|
|||
private: |
|||
String _psk; |
|||
|
|||
// {"alg": "HS256", "typ": "JWT"} |
|||
const char* JWT_HEADER = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; |
|||
const uint16_t JWT_HEADER_LENGTH = strlen(JWT_HEADER); |
|||
const uint16_t SIGNATURE_LENGTH = encode_base64_length(32); |
|||
|
|||
// static JWT length is made of: |
|||
// - the header length |
|||
// - the signature length |
|||
// - 2 delimiters, 1 terminator |
|||
const uint16_t BASE_JWT_LENGTH = JWT_HEADER_LENGTH + SIGNATURE_LENGTH + 3; |
|||
|
|||
public: |
|||
ArduinoJsonJWT(String psk); |
|||
void setPSK(String psk); |
|||
String encodeJWT(JsonObject payload); |
|||
}; |
|||
|
|||
|
|||
#endif |
@ -1,6 +1,7 @@ |
|||
#include <string.h>
|
|||
#include "sha256.h"
|
|||
|
|||
#include <string.h>
|
|||
|
|||
uint32_t sha256K[] PROGMEM = { |
|||
0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, |
|||
0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, |
Write
Preview
Loading…
Cancel
Save
Reference in new issue