Fork of the excellent esp8266-react - https://github.com/rjwats/esp8266-react
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

144 lines
4.1 KiB

  1. #include "ArduinoJsonJWT.h"
  2. ArduinoJsonJWT::ArduinoJsonJWT(String secret) : _secret(secret) {
  3. }
  4. void ArduinoJsonJWT::setSecret(String secret) {
  5. _secret = secret;
  6. }
  7. String ArduinoJsonJWT::getSecret() {
  8. return _secret;
  9. }
  10. /*
  11. * ESP32 uses mbedtls, ESP2866 uses bearssl.
  12. *
  13. * Both come with decent HMAC implmentations supporting sha256, as well as others.
  14. *
  15. * No need to pull in additional crypto libraries - lets use what we already have.
  16. */
  17. String ArduinoJsonJWT::sign(String& payload) {
  18. unsigned char hmacResult[32];
  19. {
  20. #ifdef ESP32
  21. mbedtls_md_context_t ctx;
  22. mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
  23. mbedtls_md_init(&ctx);
  24. mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
  25. mbedtls_md_hmac_starts(&ctx, (unsigned char*)_secret.c_str(), _secret.length());
  26. mbedtls_md_hmac_update(&ctx, (unsigned char*)payload.c_str(), payload.length());
  27. mbedtls_md_hmac_finish(&ctx, hmacResult);
  28. mbedtls_md_free(&ctx);
  29. #elif defined(ESP8266)
  30. br_hmac_key_context keyCtx;
  31. br_hmac_key_init(&keyCtx, &br_sha256_vtable, _secret.c_str(), _secret.length());
  32. br_hmac_context hmacCtx;
  33. br_hmac_init(&hmacCtx, &keyCtx, 0);
  34. br_hmac_update(&hmacCtx, payload.c_str(), payload.length());
  35. br_hmac_out(&hmacCtx, hmacResult);
  36. #endif
  37. }
  38. return encode((char*)hmacResult, 32);
  39. }
  40. String ArduinoJsonJWT::buildJWT(JsonObject& payload) {
  41. // serialize, then encode payload
  42. String jwt;
  43. serializeJson(payload, jwt);
  44. jwt = encode(jwt.c_str(), jwt.length());
  45. // add the header to payload
  46. jwt = JWT_HEADER + '.' + jwt;
  47. // add signature
  48. jwt += '.' + sign(jwt);
  49. return jwt;
  50. }
  51. void ArduinoJsonJWT::parseJWT(String jwt, JsonDocument& jsonDocument) {
  52. // clear json document before we begin, jsonDocument wil be null on failure
  53. jsonDocument.clear();
  54. // must have the correct header and delimiter
  55. if (!jwt.startsWith(JWT_HEADER) || jwt.indexOf('.') != JWT_HEADER_SIZE) {
  56. return;
  57. }
  58. // check there is a signature delimieter
  59. int signatureDelimiterIndex = jwt.lastIndexOf('.');
  60. if (signatureDelimiterIndex == JWT_HEADER_SIZE) {
  61. return;
  62. }
  63. // check the signature is valid
  64. String signature = jwt.substring(signatureDelimiterIndex + 1);
  65. jwt = jwt.substring(0, signatureDelimiterIndex);
  66. if (sign(jwt) != signature) {
  67. return;
  68. }
  69. // decode payload
  70. jwt = jwt.substring(JWT_HEADER_SIZE + 1);
  71. jwt = decode(jwt);
  72. // parse payload, clearing json document after failure
  73. DeserializationError error = deserializeJson(jsonDocument, jwt);
  74. if (error != DeserializationError::Ok || !jsonDocument.is<JsonObject>()) {
  75. jsonDocument.clear();
  76. }
  77. }
  78. String ArduinoJsonJWT::encode(const char* cstr, int inputLen) {
  79. // prepare encoder
  80. base64_encodestate _state;
  81. #ifdef ESP32
  82. base64_init_encodestate(&_state);
  83. size_t encodedLength = base64_encode_expected_len(inputLen) + 1;
  84. #elif defined(ESP8266)
  85. base64_init_encodestate_nonewlines(&_state);
  86. size_t encodedLength = base64_encode_expected_len_nonewlines(inputLen) + 1;
  87. #endif
  88. // prepare buffer of correct length, returning an empty string on failure
  89. char* buffer = (char*)malloc(encodedLength * sizeof(char));
  90. if (buffer == nullptr) {
  91. return "";
  92. }
  93. // encode to buffer
  94. int len = base64_encode_block(cstr, inputLen, &buffer[0], &_state);
  95. len += base64_encode_blockend(&buffer[len], &_state);
  96. buffer[len] = 0;
  97. // convert to arduino string, freeing buffer
  98. String value = String(buffer);
  99. free(buffer);
  100. buffer = nullptr;
  101. // remove padding and convert to URL safe form
  102. while (value.length() > 0 && value.charAt(value.length() - 1) == '=') {
  103. value.remove(value.length() - 1);
  104. }
  105. value.replace('+', '-');
  106. value.replace('/', '_');
  107. // return as string
  108. return value;
  109. }
  110. String ArduinoJsonJWT::decode(String value) {
  111. // convert to standard base64
  112. value.replace('-', '+');
  113. value.replace('_', '/');
  114. // prepare buffer of correct length
  115. char buffer[base64_decode_expected_len(value.length()) + 1];
  116. // decode
  117. int len = base64_decode_chars(value.c_str(), value.length(), &buffer[0]);
  118. buffer[len] = 0;
  119. // return as string
  120. return String(buffer);
  121. }