mirror of https://github.com/arendst/Tasmota.git
Change Store AWS IoT Private Key and Certificate in SPI Flash avoiding device-specific compilations
This commit is contained in:
parent
23185218e6
commit
a50a6eff61
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Densaugeo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,9 @@
|
|||
CXX ?= g++
|
||||
CFLAGS ?= -Wall -I src
|
||||
|
||||
test: catch.cpp catch.hpp src/base64.hpp
|
||||
$(CXX) $(CFLAGS) catch.cpp -o catch
|
||||
./catch
|
||||
|
||||
clean:
|
||||
rm catch
|
|
@ -0,0 +1,44 @@
|
|||
# base64_arduino
|
||||
|
||||
Base64 encoder/decoder for arduino repo
|
||||
|
||||
[![npm](https://img.shields.io/npm/l/express.svg)]()
|
||||
[![Build Status](https://travis-ci.org/Densaugeo/base64_arduino.svg?branch=master)](https://travis-ci.org/Densaugeo/base64_arduino)
|
||||
|
||||
## Installation
|
||||
|
||||
Add base64.cpp and base64.hpp to your project folder or library search path, put `#include "base64.hpp"` in your source, and pass base64.cpp to your compiler
|
||||
|
||||
## Usage
|
||||
|
||||
~~~
|
||||
unsigned char binary[] = {133, 244, 117, 206, 178, 195};
|
||||
unsigned char base64[9];
|
||||
|
||||
unsigned int base64_length = encode_base64(binary, 6, base64);
|
||||
|
||||
printf("%d\n", base64_length); // Prints "8"
|
||||
printf((char *) base64); // Prints "hfR1zrLD"
|
||||
~~~
|
||||
|
||||
~~~
|
||||
unsigned char base64[] = "hfR1zrLD";
|
||||
unsigned char binary[6];
|
||||
|
||||
unsigned int binary_length = decode_base64(base64, binary);
|
||||
|
||||
printf("[%d, %d, %d, %d, %d, %d]\n", // Prints "[133, 244, 117, 206, 178, 195]"
|
||||
binary[0], binary[1], binary[2],
|
||||
binary[3], binary[4], binary[5]);
|
||||
printf("%d\n", binary_length); // Prints "6"
|
||||
~~~
|
||||
|
||||
## Details
|
||||
|
||||
Uses common web conventions - '+' for 62, '/' for 63, '=' for padding. Note that invalid base64 characters are interpreted as padding.
|
||||
|
||||
Can be compiled as C, uses .*pp extensions because it is usually used in C++ projects and is tested for C++.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
|
@ -0,0 +1,465 @@
|
|||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
#include "base64.hpp"
|
||||
|
||||
TEST_CASE("encode_base64_length()", "[]") {
|
||||
SECTION("Zero length") {
|
||||
REQUIRE(encode_base64_length(0) == 0);
|
||||
}
|
||||
|
||||
SECTION("Divisible by 3 (no padding)") {
|
||||
REQUIRE(encode_base64_length(3) == 4);
|
||||
REQUIRE(encode_base64_length(6) == 8);
|
||||
REQUIRE(encode_base64_length(60) == 80);
|
||||
}
|
||||
|
||||
SECTION("Not divisible by 3 (padded)") {
|
||||
REQUIRE(encode_base64_length(1) == 4);
|
||||
REQUIRE(encode_base64_length(2) == 4);
|
||||
REQUIRE(encode_base64_length(4) == 8);
|
||||
REQUIRE(encode_base64_length(5) == 8);
|
||||
REQUIRE(encode_base64_length(7) == 12);
|
||||
REQUIRE(encode_base64_length(256) == 344);
|
||||
}
|
||||
|
||||
SECTION("Large") {
|
||||
REQUIRE(encode_base64_length(65536) == 87384);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("encode_base64()", "[]") {
|
||||
unsigned char actual_base64[100];
|
||||
|
||||
SECTION("Zero length") {
|
||||
unsigned char binary_0[] = {};
|
||||
encode_base64(binary_0, 0, actual_base64); // Should give 'AQIDBUNDQQgEIIY4'
|
||||
REQUIRE(memcmp(actual_base64, "", 1) == 0);
|
||||
}
|
||||
|
||||
SECTION("Length 1 (single section padded)") {
|
||||
unsigned char binary_0[] = {0};
|
||||
encode_base64(binary_0, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "AA==", 5) == 0);
|
||||
|
||||
unsigned char binary_1[] = {3};
|
||||
encode_base64(binary_1, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "Aw==", 5) == 0);
|
||||
|
||||
unsigned char binary_2[] = {8};
|
||||
encode_base64(binary_2, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "CA==", 5) == 0);
|
||||
|
||||
unsigned char binary_3[] = {145};
|
||||
encode_base64(binary_3, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "kQ==", 5) == 0);
|
||||
|
||||
unsigned char binary_4[] = {56};
|
||||
encode_base64(binary_4, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "OA==", 5) == 0);
|
||||
|
||||
unsigned char binary_5[] = {54};
|
||||
encode_base64(binary_5, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "Ng==", 5) == 0);
|
||||
|
||||
unsigned char binary_6[] = {181};
|
||||
encode_base64(binary_6, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "tQ==", 5) == 0);
|
||||
|
||||
unsigned char binary_7[] = {79};
|
||||
encode_base64(binary_7, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "Tw==", 5) == 0);
|
||||
|
||||
unsigned char binary_8[] = {115};
|
||||
encode_base64(binary_8, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "cw==", 5) == 0);
|
||||
|
||||
unsigned char binary_9[] = {255};
|
||||
encode_base64(binary_9, 1, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "/w==", 5) == 0);
|
||||
}
|
||||
|
||||
SECTION("Length 2 (single section padded)") {
|
||||
unsigned char binary_0[] = {0, 0};
|
||||
encode_base64(binary_0, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "AAA=", 5) == 0);
|
||||
|
||||
unsigned char binary_1[] = {49, 42};
|
||||
encode_base64(binary_1, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "MSo=", 5) == 0);
|
||||
|
||||
unsigned char binary_2[] = {133, 38};
|
||||
encode_base64(binary_2, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "hSY=", 5) == 0);
|
||||
|
||||
unsigned char binary_3[] = {61, 127};
|
||||
encode_base64(binary_3, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "PX8=", 5) == 0);
|
||||
|
||||
unsigned char binary_4[] = {109, 80};
|
||||
encode_base64(binary_4, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "bVA=", 5) == 0);
|
||||
|
||||
unsigned char binary_5[] = {47, 213};
|
||||
encode_base64(binary_5, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "L9U=", 5) == 0);
|
||||
|
||||
unsigned char binary_6[] = {172, 205};
|
||||
encode_base64(binary_6, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "rM0=", 5) == 0);
|
||||
|
||||
unsigned char binary_7[] = {191, 240};
|
||||
encode_base64(binary_7, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "v/A=", 5) == 0);
|
||||
|
||||
unsigned char binary_8[] = {107, 248};
|
||||
encode_base64(binary_8, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "a/g=", 5) == 0);
|
||||
|
||||
unsigned char binary_9[] = {255, 255};
|
||||
encode_base64(binary_9, 2, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "//8=", 5) == 0);
|
||||
}
|
||||
|
||||
SECTION("Length 3 (single section no padding)") {
|
||||
unsigned char binary_0[] = {0, 0, 0};
|
||||
encode_base64(binary_0, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "AAAA", 5) == 0);
|
||||
|
||||
unsigned char binary_1[] = {151, 167, 18};
|
||||
encode_base64(binary_1, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "l6cS", 5) == 0);
|
||||
|
||||
unsigned char binary_2[] = {23, 174, 50};
|
||||
encode_base64(binary_2, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "F64y", 5) == 0);
|
||||
|
||||
unsigned char binary_3[] = {143, 205, 227};
|
||||
encode_base64(binary_3, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "j83j", 5) == 0);
|
||||
|
||||
unsigned char binary_4[] = {60, 18, 186};
|
||||
encode_base64(binary_4, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "PBK6", 5) == 0);
|
||||
|
||||
unsigned char binary_5[] = {100, 34, 201};
|
||||
encode_base64(binary_5, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "ZCLJ", 5) == 0);
|
||||
|
||||
unsigned char binary_6[] = {52, 83, 129};
|
||||
encode_base64(binary_6, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "NFOB", 5) == 0);
|
||||
|
||||
unsigned char binary_7[] = {241, 202, 185};
|
||||
encode_base64(binary_7, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "8cq5", 5) == 0);
|
||||
|
||||
unsigned char binary_8[] = {149, 190, 208};
|
||||
encode_base64(binary_8, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "lb7Q", 5) == 0);
|
||||
|
||||
unsigned char binary_9[] = {255, 255, 255};
|
||||
encode_base64(binary_9, 3, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "////", 5) == 0);
|
||||
}
|
||||
|
||||
SECTION("Length divisible by 3 (no padding)") {
|
||||
unsigned char binary_0[] = {117, 213, 35, 151, 133, 255};
|
||||
encode_base64(binary_0, 6, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "ddUjl4X/", 9) == 0);
|
||||
|
||||
unsigned char binary_1[] = {90, 95, 209, 235, 58, 255};
|
||||
encode_base64(binary_1, 6, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "Wl/R6zr/", 9) == 0);
|
||||
|
||||
unsigned char binary_2[] = {133, 244, 117, 206, 178, 195, 249, 84, 248};
|
||||
encode_base64(binary_2, 9, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "hfR1zrLD+VT4", 13) == 0);
|
||||
|
||||
unsigned char binary_3[] = {7, 27, 226, 144, 59, 237, 79, 62, 191};
|
||||
encode_base64(binary_3, 9, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "BxvikDvtTz6/", 13) == 0);
|
||||
|
||||
unsigned char binary_4[] = {99, 225, 39, 195, 8, 43, 209, 151, 8, 43, 195, 183};
|
||||
encode_base64(binary_4, 12, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "Y+Enwwgr0ZcIK8O3", 17) == 0);
|
||||
|
||||
unsigned char binary_5[] = {171, 65, 164, 64, 60, 221, 46, 226, 252, 167, 250, 252};
|
||||
encode_base64(binary_5, 12, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "q0GkQDzdLuL8p/r8", 17) == 0);
|
||||
|
||||
unsigned char binary_6[] = {248, 127, 14, 241, 93, 177, 152, 46, 255, 127, 92, 84, 56, 59, 152, 132, 113, 115, 252, 70, 190, 224, 130, 155, 86, 172, 159, 162, 30, 127};
|
||||
encode_base64(binary_6, 30, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "+H8O8V2xmC7/f1xUODuYhHFz/Ea+4IKbVqyfoh5/", 41) == 0);
|
||||
|
||||
unsigned char binary_7[] = {157, 12, 248, 83, 148, 156, 196, 30, 186, 28, 52, 192, 171, 142, 6, 105, 128, 131, 89, 5, 3, 131, 215, 192, 87, 215, 244, 141, 127, 17};
|
||||
encode_base64(binary_7, 30, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "nQz4U5ScxB66HDTAq44GaYCDWQUDg9fAV9f0jX8R", 41) == 0);
|
||||
|
||||
unsigned char binary_8[] = {180, 179, 175, 132, 162, 219, 3, 18, 96, 162, 214, 232, 49, 120, 59, 133, 102, 93, 67, 34, 186, 28, 6, 28, 195, 69, 249, 44, 140, 115, 55, 215, 68, 99, 130, 160, 32, 181, 172, 125, 144, 8, 21, 119, 60, 213, 156, 230, 243, 87, 101, 167, 136, 94, 242, 174, 239, 81, 67, 101};
|
||||
encode_base64(binary_8, 60, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "tLOvhKLbAxJgotboMXg7hWZdQyK6HAYcw0X5LIxzN9dEY4KgILWsfZAIFXc81Zzm81dlp4he8q7vUUNl", 81) == 0);
|
||||
|
||||
unsigned char binary_9[] = {165, 186, 12, 82, 241, 34, 63, 218, 215, 28, 105, 126, 37, 69, 255, 36, 235, 103, 194, 236, 81, 115, 192, 61, 247, 128, 43, 38, 58, 140, 208, 9, 34, 145, 252, 209, 150, 227, 35, 241, 46, 25, 170, 198, 191, 87, 43, 206, 250, 199, 158, 193, 96, 249, 79, 142, 39, 216, 36, 236};
|
||||
encode_base64(binary_9, 60, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "pboMUvEiP9rXHGl+JUX/JOtnwuxRc8A994ArJjqM0AkikfzRluMj8S4Zqsa/VyvO+seewWD5T44n2CTs", 81) == 0);
|
||||
}
|
||||
|
||||
SECTION("Length not divisible by 3 (padded)") {
|
||||
unsigned char binary_0[] = {216, 183, 235, 10};
|
||||
encode_base64(binary_0, 4, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "2LfrCg==", 9) == 0);
|
||||
|
||||
unsigned char binary_1[] = {7, 254, 182, 49, 158};
|
||||
encode_base64(binary_1, 5, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "B/62MZ4=", 9) == 0);
|
||||
|
||||
unsigned char binary_2[] = {71, 58, 223, 154, 93, 69, 18};
|
||||
encode_base64(binary_2, 7, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "Rzrfml1FEg==", 13) == 0);
|
||||
|
||||
unsigned char binary_3[] = {226, 127, 31, 206, 19, 75, 35, 80};
|
||||
encode_base64(binary_3, 8, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "4n8fzhNLI1A=", 13) == 0);
|
||||
|
||||
unsigned char binary_4[] = {5, 36, 50, 78, 218, 198, 242, 85, 235, 72, 78, 139, 103};
|
||||
encode_base64(binary_4, 13, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "BSQyTtrG8lXrSE6LZw==", 21) == 0);
|
||||
|
||||
unsigned char binary_5[] = {161, 176, 49, 33, 148, 150, 94, 252, 21, 249, 106, 49, 216, 124, 219, 233, 133, 102, 32, 182, 193};
|
||||
encode_base64(binary_5, 21, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "obAxIZSWXvwV+Wox2Hzb6YVmILbB", 29) == 0);
|
||||
|
||||
unsigned char binary_6[] = {136, 116, 151, 174, 215, 54, 64, 218, 197, 148, 149, 17, 183, 59, 177, 54, 172, 135, 192, 202, 183, 3, 254, 51, 83, 217};
|
||||
encode_base64(binary_6, 26, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "iHSXrtc2QNrFlJURtzuxNqyHwMq3A/4zU9k=", 37) == 0);
|
||||
|
||||
unsigned char binary_7[] = {181, 16, 71, 30, 145, 101, 21, 170, 45, 24, 201, 78, 83, 31, 175, 132, 127, 108, 88, 7, 37, 154, 196, 139, 87, 68, 243, 6, 180, 36, 89, 10, 67, 73};
|
||||
encode_base64(binary_7, 34, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "tRBHHpFlFaotGMlOUx+vhH9sWAclmsSLV0TzBrQkWQpDSQ==", 49) == 0);
|
||||
|
||||
unsigned char binary_8[] = {24, 6, 234, 175, 34, 198, 47, 173, 234, 158, 106, 203, 80, 171, 218, 163, 60, 105, 183, 152, 73, 142, 190, 107, 189, 223, 215, 169, 63, 169, 163, 29, 9, 134, 235, 107, 35, 5, 16, 50, 7};
|
||||
encode_base64(binary_8, 41, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "GAbqryLGL63qnmrLUKvaozxpt5hJjr5rvd/XqT+pox0JhutrIwUQMgc=", 57) == 0);
|
||||
|
||||
unsigned char binary_9[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33};
|
||||
encode_base64(binary_9, 55, actual_base64);
|
||||
REQUIRE(memcmp(actual_base64, "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==", 77) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("decode_base64_length()", "[]") {
|
||||
SECTION("Zero length") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "") == 0);
|
||||
}
|
||||
|
||||
SECTION("Divisible by 4 (no padding)") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "AAAA") == 3);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "////") == 3);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Y+Enwwgr0ZcIK8O3") == 12);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "tLOvhKLbAxJgotboMXg7hWZdQyK6HAYcw0X5LIxzN9dEY4KgILWsfZAIFXc81Zzm81dlp4he8q7vUUNl") == 60);
|
||||
}
|
||||
|
||||
SECTION("Not divisible by 4 (padded)") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "AA==") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw==") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g=") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "//8=") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A=") == 8);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==") == 55);
|
||||
}
|
||||
|
||||
SECTION("Not divisible by 4 (padding missing)") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "AA") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "//8") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A") == 8);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ") == 55);
|
||||
}
|
||||
|
||||
SECTION("Padding in middle cuts off string") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "AA==4n8fzhNL") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw=4n8fzhNL") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g=4n8fzhNL==") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "//8=4n8fzhNL") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A=4n8fzhNL====") == 8);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==4n8fzhNL") == 55);
|
||||
}
|
||||
|
||||
SECTION("Extra padding is ignored") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw========") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g=======") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw========") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g==========") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A===========") == 8);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ=========") == 55);
|
||||
}
|
||||
|
||||
SECTION("Non-base64 characcters are interpreted as padding") {
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw:;") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Aw`'@") == 1);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g~") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "a/g[|") == 2);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "4n8fzhNLI1A]") == 8);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "Y+Enwwgr0ZcIK8O3{}") == 12);
|
||||
REQUIRE(decode_base64_length((unsigned char*) "AA-^4n8fzhNL") == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("decode_base64()", "[]") {
|
||||
unsigned char actual_binary[100];
|
||||
|
||||
// Zero length case ignored, because it is verified to return no data in decode_base64_length()
|
||||
|
||||
SECTION("Divisible by 4 (no padding)") {
|
||||
unsigned char expected_binary_0[] = {0, 0, 0};
|
||||
decode_base64((unsigned char*) "AAAA", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_0, 3) == 0);
|
||||
|
||||
unsigned char expected_binary_1[] = {255, 255, 255};
|
||||
decode_base64((unsigned char*) "////", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_1, 3) == 0);
|
||||
|
||||
unsigned char expected_binary_2[] = {99, 225, 39, 195, 8, 43, 209, 151, 8, 43, 195, 183};
|
||||
decode_base64((unsigned char*) "Y+Enwwgr0ZcIK8O3", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_2, 12) == 0);
|
||||
|
||||
unsigned char expected_binary_3[] = {180, 179, 175, 132, 162, 219, 3, 18, 96, 162, 214, 232, 49, 120, 59, 133, 102, 93, 67, 34, 186, 28, 6, 28, 195, 69, 249, 44, 140, 115, 55, 215, 68, 99, 130, 160, 32, 181, 172, 125, 144, 8, 21, 119, 60, 213, 156, 230, 243, 87, 101, 167, 136, 94, 242, 174, 239, 81, 67, 101};
|
||||
decode_base64((unsigned char*) "tLOvhKLbAxJgotboMXg7hWZdQyK6HAYcw0X5LIxzN9dEY4KgILWsfZAIFXc81Zzm81dlp4he8q7vUUNl", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_3, 60) == 0);
|
||||
}
|
||||
|
||||
SECTION("Not divisible by 4 (padded)") {
|
||||
unsigned char expected_binary_0[] = {0};
|
||||
decode_base64((unsigned char*) "AA==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_1[] = {3};
|
||||
decode_base64((unsigned char*) "Aw==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_2[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g=", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_3[] = {255, 255};
|
||||
decode_base64((unsigned char*) "//8=", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80};
|
||||
decode_base64((unsigned char*) "4n8fzhNLI1A=", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0);
|
||||
|
||||
unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33};
|
||||
decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0);
|
||||
}
|
||||
|
||||
SECTION("Not divisible by 4 (padding missing)") {
|
||||
unsigned char expected_binary_0[] = {0};
|
||||
decode_base64((unsigned char*) "AA", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_1[] = {3};
|
||||
decode_base64((unsigned char*) "Aw", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_2[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_3[] = {255, 255};
|
||||
decode_base64((unsigned char*) "//8", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80};
|
||||
decode_base64((unsigned char*) "4n8fzhNLI1A", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0);
|
||||
|
||||
unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33};
|
||||
decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0);
|
||||
}
|
||||
|
||||
SECTION("Padding in middle cuts off string") {
|
||||
unsigned char expected_binary_0[] = {0};
|
||||
decode_base64((unsigned char*) "AA==4n8fzhNL", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_1[] = {3};
|
||||
decode_base64((unsigned char*) "Aw=4n8fzhNL", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_2[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g=4n8fzhNL==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_3[] = {255, 255};
|
||||
decode_base64((unsigned char*) "//8=4n8fzhNL", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80};
|
||||
decode_base64((unsigned char*) "4n8fzhNLI1A=4n8fzhNL====", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0);
|
||||
|
||||
unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33};
|
||||
decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ==4n8fzhNL", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0);
|
||||
}
|
||||
|
||||
SECTION("Extra padding is ignored") {
|
||||
unsigned char expected_binary_0[] = {3};
|
||||
decode_base64((unsigned char*) "Aw========", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_1[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g=======", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_1, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_2[] = {3};
|
||||
decode_base64((unsigned char*) "Aw========", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_2, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_3[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g==========", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80};
|
||||
decode_base64((unsigned char*) "4n8fzhNLI1A===========", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0);
|
||||
|
||||
unsigned char expected_binary_5[] = {220, 92, 67, 95, 157, 76, 162, 210, 224, 202, 136, 157, 104, 178, 103, 81, 35, 103, 244, 71, 92, 25, 69, 64, 61, 232, 198, 108, 217, 106, 63, 103, 234, 39, 156, 108, 4, 101, 212, 198, 57, 223, 75, 132, 160, 26, 193, 139, 16, 89, 12, 45, 183, 133, 33};
|
||||
decode_base64((unsigned char*) "3FxDX51MotLgyoidaLJnUSNn9EdcGUVAPejGbNlqP2fqJ5xsBGXUxjnfS4SgGsGLEFkMLbeFIQ=========", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_5, 55) == 0);
|
||||
}
|
||||
|
||||
SECTION("Non-base64 characcters are interpreted as padding") {
|
||||
unsigned char expected_binary_0[] = {3};
|
||||
decode_base64((unsigned char*) "Aw==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_0, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_1[] = {3};
|
||||
decode_base64((unsigned char*) "Aw===", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_1, 1) == 0);
|
||||
|
||||
unsigned char expected_binary_2[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g=", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_2, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_3[] = {107, 248};
|
||||
decode_base64((unsigned char*) "a/g==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_3, 2) == 0);
|
||||
|
||||
unsigned char expected_binary_4[] = {226, 127, 31, 206, 19, 75, 35, 80};
|
||||
decode_base64((unsigned char*) "4n8fzhNLI1A=", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_4, 8) == 0);
|
||||
|
||||
unsigned char expected_binary_5[] = {99, 225, 39, 195, 8, 43, 209, 151, 8, 43, 195, 183};
|
||||
decode_base64((unsigned char*) "Y+Enwwgr0ZcIK8O3==", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_5, 12) == 0);
|
||||
|
||||
unsigned char expected_binary_6[] = {0};
|
||||
decode_base64((unsigned char*) "AA==4n8fzhNL", actual_binary);
|
||||
REQUIRE(memcmp(actual_binary, expected_binary_6, 1) == 0);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
|||
name=base64
|
||||
version=1.1.1
|
||||
author=Densaugeo <use@git.hub>
|
||||
maintainer=Densaugeo <use@git.hub>
|
||||
sentence=Base64 encoder/decoder for arduino repo
|
||||
paragraph=Uses common web conventions - '+' for 62, '/' for 63, '=' for padding. Note that invalid base64 characters are interpreted as padding.
|
||||
category=Communication
|
||||
url=https://github.com/Densaugeo/base64_arduino
|
||||
architectures=*
|
|
@ -0,0 +1,195 @@
|
|||
/**
|
||||
* Base64 encoding and decoding of strings. Uses '+' for 62, '/' for 63, '=' for padding
|
||||
*/
|
||||
|
||||
#ifndef BASE64_H_INCLUDED
|
||||
#define BASE64_H_INCLUDED
|
||||
|
||||
/* binary_to_base64:
|
||||
* Description:
|
||||
* Converts a single byte from a binary value to the corresponding base64 character
|
||||
* Parameters:
|
||||
* v - Byte to convert
|
||||
* Returns:
|
||||
* ascii code of base64 character. If byte is >= 64, then there is not corresponding base64 character
|
||||
* and 255 is returned
|
||||
*/
|
||||
unsigned char binary_to_base64(unsigned char v);
|
||||
|
||||
/* base64_to_binary:
|
||||
* Description:
|
||||
* Converts a single byte from a base64 character to the corresponding binary value
|
||||
* Parameters:
|
||||
* c - Base64 character (as ascii code)
|
||||
* Returns:
|
||||
* 6-bit binary value
|
||||
*/
|
||||
unsigned char base64_to_binary(unsigned char c);
|
||||
|
||||
/* encode_base64_length:
|
||||
* Description:
|
||||
* Calculates length of base64 string needed for a given number of binary bytes
|
||||
* Parameters:
|
||||
* input_length - Amount of binary data in bytes
|
||||
* Returns:
|
||||
* Number of base64 characters needed to encode input_length bytes of binary data
|
||||
*/
|
||||
unsigned int encode_base64_length(unsigned int input_length);
|
||||
|
||||
/* decode_base64_length:
|
||||
* Description:
|
||||
* Calculates number of bytes of binary data in a base64 string
|
||||
* Parameters:
|
||||
* input - Base64-encoded null-terminated string
|
||||
* Returns:
|
||||
* Number of bytes of binary data in input
|
||||
*/
|
||||
unsigned int decode_base64_length(unsigned char input[]);
|
||||
|
||||
/* encode_base64:
|
||||
* Description:
|
||||
* Converts an array of bytes to a base64 null-terminated string
|
||||
* Parameters:
|
||||
* input - Pointer to input data
|
||||
* input_length - Number of bytes to read from input pointer
|
||||
* output - Pointer to output string. Null terminator will be added automatically
|
||||
* Returns:
|
||||
* Length of encoded string in bytes (not including null terminator)
|
||||
*/
|
||||
unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]);
|
||||
|
||||
/* decode_base64:
|
||||
* Description:
|
||||
* Converts a base64 null-terminated string to an array of bytes
|
||||
* Parameters:
|
||||
* input - Pointer to input string
|
||||
* output - Pointer to output array
|
||||
* Returns:
|
||||
* Number of bytes in the decoded binary
|
||||
*/
|
||||
unsigned int decode_base64(unsigned char input[], unsigned char output[]);
|
||||
|
||||
unsigned char binary_to_base64(unsigned char v) {
|
||||
// Capital letters - 'A' is ascii 65 and base64 0
|
||||
if(v < 26) return v + 'A';
|
||||
|
||||
// Lowercase letters - 'a' is ascii 97 and base64 26
|
||||
if(v < 52) return v + 71;
|
||||
|
||||
// Digits - '0' is ascii 48 and base64 52
|
||||
if(v < 62) return v - 4;
|
||||
|
||||
// '+' is ascii 43 and base64 62
|
||||
if(v == 62) return '+';
|
||||
|
||||
// '/' is ascii 47 and base64 63
|
||||
if(v == 63) return '/';
|
||||
|
||||
return 64;
|
||||
}
|
||||
|
||||
unsigned char base64_to_binary(unsigned char c) {
|
||||
// Capital letters - 'A' is ascii 65 and base64 0
|
||||
if('A' <= c && c <= 'Z') return c - 'A';
|
||||
|
||||
// Lowercase letters - 'a' is ascii 97 and base64 26
|
||||
if('a' <= c && c <= 'z') return c - 71;
|
||||
|
||||
// Digits - '0' is ascii 48 and base64 52
|
||||
if('0' <= c && c <= '9') return c + 4;
|
||||
|
||||
// '+' is ascii 43 and base64 62
|
||||
if(c == '+') return 62;
|
||||
|
||||
// '/' is ascii 47 and base64 63
|
||||
if(c == '/') return 63;
|
||||
|
||||
return 255;
|
||||
}
|
||||
|
||||
unsigned int encode_base64_length(unsigned int input_length) {
|
||||
return (input_length + 2)/3*4;
|
||||
}
|
||||
|
||||
unsigned int decode_base64_length(unsigned char input[]) {
|
||||
unsigned char *start = input;
|
||||
|
||||
while(base64_to_binary(input[0]) < 64) {
|
||||
++input;
|
||||
}
|
||||
|
||||
unsigned int input_length = input - start;
|
||||
|
||||
unsigned int output_length = input_length/4*3;
|
||||
|
||||
switch(input_length % 4) {
|
||||
default: return output_length;
|
||||
case 2: return output_length + 1;
|
||||
case 3: return output_length + 2;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int encode_base64(unsigned char input[], unsigned int input_length, unsigned char output[]) {
|
||||
unsigned int full_sets = input_length/3;
|
||||
|
||||
// While there are still full sets of 24 bits...
|
||||
for(unsigned int i = 0; i < full_sets; ++i) {
|
||||
output[0] = binary_to_base64( input[0] >> 2);
|
||||
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
|
||||
output[2] = binary_to_base64((input[1] & 0x0F) << 2 | input[2] >> 6);
|
||||
output[3] = binary_to_base64( input[2] & 0x3F);
|
||||
|
||||
input += 3;
|
||||
output += 4;
|
||||
}
|
||||
|
||||
switch(input_length % 3) {
|
||||
case 0:
|
||||
output[0] = '\0';
|
||||
break;
|
||||
case 1:
|
||||
output[0] = binary_to_base64( input[0] >> 2);
|
||||
output[1] = binary_to_base64((input[0] & 0x03) << 4);
|
||||
output[2] = '=';
|
||||
output[3] = '=';
|
||||
output[4] = '\0';
|
||||
break;
|
||||
case 2:
|
||||
output[0] = binary_to_base64( input[0] >> 2);
|
||||
output[1] = binary_to_base64((input[0] & 0x03) << 4 | input[1] >> 4);
|
||||
output[2] = binary_to_base64((input[1] & 0x0F) << 2);
|
||||
output[3] = '=';
|
||||
output[4] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
return encode_base64_length(input_length);
|
||||
}
|
||||
|
||||
unsigned int decode_base64(unsigned char input[], unsigned char output[]) {
|
||||
unsigned int output_length = decode_base64_length(input);
|
||||
|
||||
// While there are still full sets of 24 bits...
|
||||
for(unsigned int i = 2; i < output_length; i += 3) {
|
||||
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
|
||||
output[2] = base64_to_binary(input[2]) << 6 | base64_to_binary(input[3]);
|
||||
|
||||
input += 4;
|
||||
output += 3;
|
||||
}
|
||||
|
||||
switch(output_length % 3) {
|
||||
case 1:
|
||||
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||
break;
|
||||
case 2:
|
||||
output[0] = base64_to_binary(input[0]) << 2 | base64_to_binary(input[1]) >> 4;
|
||||
output[1] = base64_to_binary(input[1]) << 4 | base64_to_binary(input[2]) >> 2;
|
||||
break;
|
||||
}
|
||||
|
||||
return output_length;
|
||||
}
|
||||
|
||||
#endif // ifndef
|
|
@ -8,6 +8,7 @@
|
|||
* Add define USE_ENERGY_MARGIN_DETECTION to disable Energy Margin and Power Limit detection
|
||||
* Add define USE_ENERGY_POWER_LIMIT to disable Energy Power Limit detection while Energy Margin detection is active
|
||||
* Add allow repeat/longpress for IRSend raw, introduced IRSend<r> option (#6074)
|
||||
* Change Store AWS IoT Private Key and Certificate in SPI Flash avoiding device-specific compilations
|
||||
*
|
||||
* 6.6.0.2 20190714
|
||||
* Change commands Var and Mem to show all parameters when no index is given (#6107)
|
||||
|
|
|
@ -290,6 +290,7 @@
|
|||
#define D_CMND_MQTTCLIENT "MqttClient"
|
||||
#define D_CMND_MQTTUSER "MqttUser"
|
||||
#define D_CMND_MQTTPASSWORD "MqttPassword"
|
||||
#define D_CMND_TLSKEY "TLSKey"
|
||||
#define D_CMND_FULLTOPIC "FullTopic"
|
||||
#define D_CMND_PREFIX "Prefix"
|
||||
#define PRFX_MAX_STRING_LENGTH 5
|
||||
|
|
|
@ -267,7 +267,7 @@
|
|||
//#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake)
|
||||
// #define USE_MQTT_TLS_CA_CERT // Force full CA validation instead of fingerprints, slower, but simpler to use (+2.2k code, +1.9k mem during connection handshake)
|
||||
// #define USE_MQTT_TLS_FORCE_EC_CIPHER // Force Elliptic Curve cipher (higher security) required by some servers (automatically enabled with USE_MQTT_AWS_IOT) (+11.4k code, +0.4k mem)
|
||||
// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.4k code, +0.4k mem)
|
||||
// #define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem)
|
||||
// Note: you need to generate a private key + certificate per device and update 'sonoff/sonoff_aws_iot.cpp'
|
||||
// Full documentation here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT
|
||||
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
/*
|
||||
sonoff_aws_iot.cpp - TLS AWS IoT for Sonoff-Tasmota - Private key
|
||||
|
||||
Copyright (C) 2019 Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "my_user_config.h"
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
|
||||
#include <t_bearssl.h>
|
||||
#include <pgmspace.h>
|
||||
|
||||
// nasty hack to force PROGMEM
|
||||
#define static static PROGMEM
|
||||
|
||||
namespace aws_iot_privkey {
|
||||
/*********************************************************************************************\
|
||||
* Private key for AWS IoT
|
||||
*
|
||||
* Create the private key, generate the CSR and sign it with AWS IoT Console.
|
||||
*
|
||||
* Then generate C version of Private Key and Certificate, cut and paste below
|
||||
*
|
||||
* Downloaded from https://www.identrust.com/support/downloads
|
||||
\*********************************************************************************************/
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Export Private Key as a C structure
|
||||
*
|
||||
* $ bearssl skey -C <private_key.PEM>
|
||||
\*********************************************************************************************/
|
||||
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
|
||||
static const unsigned char EC_X[] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
|
||||
};
|
||||
|
||||
static const br_ec_private_key EC = {
|
||||
23,
|
||||
(unsigned char *)EC_X, sizeof EC_X
|
||||
};
|
||||
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Export Private Key as a C structure
|
||||
*
|
||||
* $ bearssl chain <certificate.PEM>
|
||||
\*********************************************************************************************/
|
||||
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
|
||||
static const unsigned char CERT0[] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
|
||||
};
|
||||
|
||||
static const br_x509_certificate CHAIN[] = {
|
||||
{ (unsigned char *)CERT0, sizeof CERT0 }
|
||||
};
|
||||
|
||||
#define CHAIN_LEN 1
|
||||
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */
|
||||
|
||||
const br_ec_private_key *AWS_IoT_Private_Key = &EC;
|
||||
const br_x509_certificate *AWS_IoT_Client_Certificate = &CHAIN[0];
|
||||
|
||||
}
|
||||
|
||||
#endif // USE_MQTT_AWS_IOT
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#define XDRV_02 2
|
||||
|
||||
// #define DEBUG_DUMP_TLS // allow dumping of TLS Flash keys
|
||||
|
||||
#ifdef USE_MQTT_TLS
|
||||
#include "WiFiClientSecureLightBearSSL.h"
|
||||
BearSSL::WiFiClientSecure_light *tlsClient;
|
||||
|
@ -32,6 +34,9 @@ const char kMqttCommands[] PROGMEM =
|
|||
#endif
|
||||
#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT
|
||||
D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|"
|
||||
#endif
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
D_CMND_TLSKEY "|"
|
||||
#endif
|
||||
D_CMND_MQTTHOST "|" D_CMND_MQTTPORT "|" D_CMND_MQTTRETRY "|" D_CMND_STATETEXT "|" D_CMND_MQTTCLIENT "|"
|
||||
D_CMND_FULLTOPIC "|" D_CMND_PREFIX "|" D_CMND_GROUPTOPIC "|" D_CMND_TOPIC "|" D_CMND_PUBLISH "|"
|
||||
|
@ -43,6 +48,9 @@ void (* const MqttCommand[])(void) PROGMEM = {
|
|||
#endif
|
||||
#if !defined(USE_MQTT_TLS) || !defined(USE_MQTT_AWS_IOT) // user and password are disabled with AWS IoT
|
||||
&CmndMqttUser, &CmndMqttPassword,
|
||||
#endif
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
&CmndTlsKey,
|
||||
#endif
|
||||
&CmndMqttHost, &CmndMqttPort, &CmndMqttRetry, &CmndStateText, &CmndMqttClient,
|
||||
&CmndFullTopic, &CmndPrefix, &CmndGroupTopic, &CmndTopic, &CmndPublish,
|
||||
|
@ -77,12 +85,29 @@ void to_hex(unsigned char * in, size_t insz, char * out, size_t outsz) {
|
|||
pout[-1] = 0;
|
||||
}
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
namespace aws_iot_privkey {
|
||||
// this is where the Private Key and Certificate are stored
|
||||
extern const br_ec_private_key *AWS_IoT_Private_Key;
|
||||
extern const br_x509_certificate *AWS_IoT_Client_Certificate;
|
||||
}
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
#include <base64.hpp>
|
||||
|
||||
const br_ec_private_key *AWS_IoT_Private_Key = nullptr;
|
||||
const br_x509_certificate *AWS_IoT_Client_Certificate = nullptr;
|
||||
|
||||
class tls_entry_t {
|
||||
public:
|
||||
uint32_t name; // simple 4 letters name. Currently 'skey', 'crt ', 'crt1', 'crt2'
|
||||
uint16_t start; // start offset
|
||||
uint16_t len; // len of object
|
||||
}; // 8 bytes
|
||||
|
||||
const static uint32_t TLS_NAME_SKEY = 0x2079656B; // 'key ' little endian
|
||||
const static uint32_t TLS_NAME_CRT = 0x20747263; // 'crt ' little endian
|
||||
|
||||
class tls_dir_t {
|
||||
public:
|
||||
tls_entry_t entry[4]; // 4 entries max, only 4 used today, for future use
|
||||
}; // 4*8 = 64 bytes
|
||||
|
||||
tls_dir_t tls_dir; // memory copy of tls_dir from flash
|
||||
|
||||
#endif
|
||||
|
||||
// A typical AWS IoT endpoint is 50 characters long, it does not fit
|
||||
|
@ -198,8 +223,9 @@ void MqttInit(void)
|
|||
#ifdef USE_MQTT_AWS_IOT
|
||||
snprintf(AWS_endpoint, sizeof(AWS_endpoint), PSTR("%s%s"), Settings.mqtt_user, Settings.mqtt_host);
|
||||
|
||||
tlsClient->setClientECCert(aws_iot_privkey::AWS_IoT_Client_Certificate,
|
||||
aws_iot_privkey::AWS_IoT_Private_Key,
|
||||
loadTlsDir(); // load key and certificate data from Flash
|
||||
tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
|
||||
AWS_IoT_Private_Key,
|
||||
0xFFFF /* all usages, don't care */, 0);
|
||||
#endif
|
||||
|
||||
|
@ -532,6 +558,12 @@ void MqttReconnect(void)
|
|||
if (!strlen(Settings.mqtt_host) || !Settings.mqtt_port) {
|
||||
mqtt_allowed = false;
|
||||
}
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
// don't enable MQTT for AWS IoT if Private Key or Certificate are not set
|
||||
if (!AWS_IoT_Private_Key || !AWS_IoT_Client_Certificate) {
|
||||
mqtt_allowed = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!mqtt_allowed) {
|
||||
MqttConnected();
|
||||
|
@ -570,6 +602,10 @@ void MqttReconnect(void)
|
|||
|
||||
MqttClient.setCallback(MqttDataHandler);
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
// re-assign private keys in case it was updated in between
|
||||
tlsClient->setClientECCert(AWS_IoT_Client_Certificate,
|
||||
AWS_IoT_Private_Key,
|
||||
0xFFFF /* all usages, don't care */, 0);
|
||||
MqttClient.setServer(AWS_endpoint, Settings.mqtt_port);
|
||||
#else
|
||||
MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port);
|
||||
|
@ -932,6 +968,192 @@ void CmndSensorRetain(void)
|
|||
ResponseCmndStateText(Settings.flag.mqtt_sensor_retain);
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* TLS private key and certificate - store into Flash
|
||||
\*********************************************************************************************/
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
|
||||
const static uint8_t* tls_spi_start = (uint8_t*) 0x402FF000;
|
||||
const static uint32_t tls_spi_start_write = 0x00FF000;
|
||||
const static uint16_t tls_spi_start_sector = 0x00FF;
|
||||
const static size_t tls_spi_len = 0x1000; // 4kb blocs
|
||||
const static size_t tls_block_offset = 0x0400;
|
||||
const static size_t tls_block_len = 0x0400; // 1kb
|
||||
const static size_t tls_obj_store_offset = tls_block_offset + sizeof(tls_dir_t);
|
||||
|
||||
|
||||
inline void TlsEraseBuffer(uint8_t *buffer) {
|
||||
memset(buffer + tls_block_offset, 0xFF, tls_block_len);
|
||||
}
|
||||
|
||||
// static data structures for Private Key and Certificate, only the pointer
|
||||
// to binary data will change to a region in SPI Flash
|
||||
static br_ec_private_key EC = {
|
||||
23,
|
||||
nullptr, 0
|
||||
};
|
||||
|
||||
static br_x509_certificate CHAIN[] = {
|
||||
{ nullptr, 0 }
|
||||
};
|
||||
|
||||
// load a copy of the tls_dir from flash into ram
|
||||
// and calculate the appropriate data structures for AWS_IoT_Private_Key and AWS_IoT_Client_Certificate
|
||||
void loadTlsDir(void) {
|
||||
memcpy_P(&tls_dir, tls_spi_start + tls_block_offset, sizeof(tls_dir));
|
||||
|
||||
// calculate the addresses for Key and Cert in Flash
|
||||
if ((TLS_NAME_SKEY == tls_dir.entry[0].name) && (tls_dir.entry[0].len > 0)) {
|
||||
EC.x = (unsigned char *)(tls_spi_start + tls_obj_store_offset + tls_dir.entry[0].start);
|
||||
EC.xlen = tls_dir.entry[0].len;
|
||||
AWS_IoT_Private_Key = &EC;
|
||||
} else {
|
||||
AWS_IoT_Private_Key = nullptr;
|
||||
}
|
||||
if ((TLS_NAME_CRT == tls_dir.entry[1].name) && (tls_dir.entry[1].len > 0)) {
|
||||
CHAIN[0].data = (unsigned char *) (tls_spi_start + tls_obj_store_offset + tls_dir.entry[1].start);
|
||||
CHAIN[0].data_len = tls_dir.entry[1].len;
|
||||
AWS_IoT_Client_Certificate = CHAIN;
|
||||
} else {
|
||||
AWS_IoT_Client_Certificate = nullptr;
|
||||
}
|
||||
//Serial.printf("AWS_IoT_Private_Key = %x, AWS_IoT_Client_Certificate = %x\n", AWS_IoT_Private_Key, AWS_IoT_Client_Certificate);
|
||||
}
|
||||
|
||||
const char ALLOCATE_ERROR[] PROGMEM = "TLSKey " D_JSON_ERROR ": cannot allocate buffer.";
|
||||
|
||||
void CmndTlsKey(void) {
|
||||
#ifdef DEBUG_DUMP_TLS
|
||||
if (0 == XdrvMailbox.index){
|
||||
CmndTlsDump();
|
||||
}
|
||||
#endif // DEBUG_DUMP_TLS
|
||||
if ((XdrvMailbox.index >= 1) && (XdrvMailbox.index <= 2)) {
|
||||
tls_dir_t *tls_dir_write;
|
||||
|
||||
if (XdrvMailbox.data_len > 0) { // write new value
|
||||
// first copy SPI buffer into ram
|
||||
uint8_t *spi_buffer = (uint8_t*) malloc(tls_spi_len);
|
||||
if (!spi_buffer) {
|
||||
AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR);
|
||||
return;
|
||||
}
|
||||
memcpy_P(spi_buffer, tls_spi_start, tls_spi_len);
|
||||
|
||||
// allocate buffer for decoded base64
|
||||
uint32_t bin_len = decode_base64_length((unsigned char*)XdrvMailbox.data);
|
||||
uint8_t *bin_buf = nullptr;
|
||||
if (bin_len > 0) {
|
||||
bin_buf = (uint8_t*) malloc(bin_len + 4);
|
||||
if (!bin_buf) {
|
||||
AddLog_P(LOG_LEVEL_ERROR, ALLOCATE_ERROR);
|
||||
free(spi_buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// decode base64
|
||||
if (bin_len > 0) {
|
||||
decode_base64((unsigned char*)XdrvMailbox.data, bin_buf);
|
||||
}
|
||||
|
||||
// address of writable tls_dir in buffer
|
||||
tls_dir_write = (tls_dir_t*) (spi_buffer + tls_block_offset);
|
||||
|
||||
if (1 == XdrvMailbox.index) {
|
||||
// Try to write Private key
|
||||
// Start by erasing all
|
||||
TlsEraseBuffer(spi_buffer); // Erase any previously stored data
|
||||
if (bin_len > 0) {
|
||||
if (bin_len != 32) {
|
||||
// no private key was previously stored, abort
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate must be 32 bytes: %d."), bin_len);
|
||||
free(spi_buffer);
|
||||
free(bin_buf);
|
||||
return;
|
||||
}
|
||||
tls_entry_t *entry = &tls_dir_write->entry[0];
|
||||
entry->name = TLS_NAME_SKEY;
|
||||
entry->start = 0;
|
||||
entry->len = bin_len;
|
||||
memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len);
|
||||
} else {
|
||||
// if lenght is zero, simply erase this SPI flash area
|
||||
}
|
||||
} else if (2 == XdrvMailbox.index) {
|
||||
// Try to write Certificate
|
||||
if (TLS_NAME_SKEY != tls_dir.entry[0].name) {
|
||||
// no private key was previously stored, abort
|
||||
AddLog_P(LOG_LEVEL_INFO, PSTR("TLSKey: cannot store Cert if no Key previously stored."));
|
||||
free(spi_buffer);
|
||||
free(bin_buf);
|
||||
return;
|
||||
}
|
||||
if (bin_len <= 256) {
|
||||
// Certificate lenght too short
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("TLSKey: Certificate length too short: %d."), bin_len);
|
||||
free(spi_buffer);
|
||||
free(bin_buf);
|
||||
return;
|
||||
}
|
||||
tls_entry_t *entry = &tls_dir_write->entry[1];
|
||||
entry->name = TLS_NAME_CRT;
|
||||
entry->start = (tls_dir_write->entry[0].start + tls_dir_write->entry[0].len + 3) & ~0x03; // align to 4 bytes boundary
|
||||
entry->len = bin_len;
|
||||
memcpy(spi_buffer + tls_obj_store_offset + entry->start, bin_buf, entry->len);
|
||||
}
|
||||
|
||||
TlsWriteSpiBuffer(spi_buffer);
|
||||
free(spi_buffer);
|
||||
free(bin_buf);
|
||||
}
|
||||
|
||||
loadTlsDir(); // reload into memory any potential change
|
||||
Response_P(PSTR("{\"%s1\":%d,\"%s2\":%d}"),
|
||||
XdrvMailbox.command, AWS_IoT_Private_Key ? tls_dir.entry[0].len : -1,
|
||||
XdrvMailbox.command, AWS_IoT_Client_Certificate ? tls_dir.entry[1].len : -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" {
|
||||
#include "spi_flash.h"
|
||||
}
|
||||
|
||||
void TlsWriteSpiBuffer(uint8_t *buf) {
|
||||
bool ret = false;
|
||||
SpiFlashOpResult res;
|
||||
|
||||
noInterrupts();
|
||||
res = spi_flash_erase_sector(tls_spi_start_sector);
|
||||
if (SPI_FLASH_RESULT_OK == res) {
|
||||
res = spi_flash_write(tls_spi_start_sector * SPI_FLASH_SEC_SIZE, (uint32_t*) buf, SPI_FLASH_SEC_SIZE);
|
||||
if (SPI_FLASH_RESULT_OK == res) {
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
interrupts();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DUMP_TLS
|
||||
// Dump TLS Flash data - don't activate in production to protect your private keys
|
||||
uint32_t bswap32(uint32_t x) {
|
||||
return ((x << 24) & 0xff000000 ) |
|
||||
((x << 8) & 0x00ff0000 ) |
|
||||
((x >> 8) & 0x0000ff00 ) |
|
||||
((x >> 24) & 0x000000ff );
|
||||
}
|
||||
void CmndTlsDump(void) {
|
||||
uint32_t start = 0x402FF400;
|
||||
uint32_t end = 0x402FF7FF;
|
||||
for (uint32_t pos = start; pos < end; pos += 0x10) {
|
||||
uint32_t* values = (uint32_t*)(pos);
|
||||
Serial.printf(PSTR("%08x: %08x %08x %08x %08x\n"), pos, bswap32(values[0]), bswap32(values[1]), bswap32(values[2]), bswap32(values[3]));
|
||||
}
|
||||
}
|
||||
#endif // DEBUG_DUMP_TLS
|
||||
#endif
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Presentation
|
||||
\*********************************************************************************************/
|
||||
|
|
Loading…
Reference in New Issue