Change Store AWS IoT Private Key and Certificate in SPI Flash avoiding device-specific compilations

This commit is contained in:
Stephan Hadinger 2019-08-04 19:42:21 +02:00
parent 23185218e6
commit a50a6eff61
12 changed files with 11335 additions and 161 deletions

21
lib/base64-1.1.1/LICENSE Normal file
View File

@ -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.

View File

@ -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

View File

@ -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

465
lib/base64-1.1.1/catch.cpp Normal file
View File

@ -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);
}
}

10359
lib/base64-1.1.1/catch.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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=*

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
\*********************************************************************************************/