-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathota-update.cpp
165 lines (139 loc) · 5.24 KB
/
ota-update.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* noisemeter-device - Firmware for CivicTechTO's Noisemeter Device
* Copyright (C) 2024 Clyne Sullivan
*
* 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 <https://www.gnu.org/licenses/>.
*/
#include "ota-update.h"
#include <array>
#include <cstdint>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <Update.h>
#include <WiFiClientSecure.h>
#include <mbedtls/pk.h>
#include <mbedtls/md.h>
#include <mbedtls/md_internal.h>
#include "board.h"
// Public key for OTA signature verification
const unsigned char cert_ota [] PROGMEM = R"CERT(
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAhe0evwkVr5J0ZcnPSiAY
75S1PP61ln95amfwQ4K9xCbaqHzizGVsfzKIbDHDmFMVQkNdFQRAewGit01WPZNJ
uj08vbWKcj00W/dNiMyL82+3/VSKgsWTRTrjq2PoqWXrAcYyXOPc+Nq+b+Qzhdfn
kZferH1Mkyt4mhEUoKeYRlU+lwPBzMQgeBMKYp2E8R6mtM3vscK+zpL7M65DKRay
R2ckugQeTQOOLga3lvjxgnq6yKNhRttNSLYQifAbGEftze58DwTbEaexZlTowidK
ysqIsN8SYmUFKg5CDLf39LEPFfEakLxKkoOm/2fhyzhdcTqJyVEWxKVrsplfWKLH
Bb+AKvQ0v7xbM8d3yok/wd1EJ2xEoyv7x5RJccyE7/WhoiEN5gZfwnlZnN+H10Qx
DH3BYzId+PWw9CIMkuqNME5G/h22eEzR8Z7gzQBdn5GKYHMKVLx3w9X7DsJ3RpRb
b388AnSeku0Wsorx2RhEdnjKe3FiNsbM7GXs94sIMj8X6fr+tY+K5acN0L3sAcaK
azGkUkeBYClDYT/cVWedSscUw02u0sONh4j8ayG3aDivA4BZndxQb/NJzxRD9C7e
b3NXshQuTUV+KePuT18B6J6mslQHJIO+jmCEnLh9eFKwjxHfO4mYAFL5RC/RprDz
STejf1pUQ7jnm6WdvwSBupkCAwEAAQ==
-----END PUBLIC KEY-----
)CERT";
static bool applyUpdate(WiFiClientSecure& client, int totalSize);
bool downloadOTAUpdate(String url, String rootCA)
{
if (url.isEmpty())
return false;
WiFiClientSecure client;
client.setCACert(rootCA.c_str());
HTTPClient https;
if (https.begin(client, url)) {
const auto code = https.GET();
if (code == HTTP_CODE_OK || code == HTTP_CODE_MOVED_PERMANENTLY) {
return applyUpdate(client, https.getSize());
} else {
SERIAL.print("Bad HTTP response: ");
SERIAL.println(code);
}
} else {
SERIAL.println("Unable to connect.");
}
return false;
}
bool applyUpdate(WiFiClientSecure& client, int totalSize)
{
static std::array<uint8_t, 512> buffer;
static std::array<uint8_t, 512> signature;
mbedtls_pk_context pk;
mbedtls_md_context_t rsa;
mbedtls_pk_init( &pk );
if (mbedtls_pk_parse_public_key(&pk, cert_ota, sizeof(cert_ota)) != 0) {
SERIAL.println("Parsing public key failed!");
return false;
}
if (!mbedtls_pk_can_do(&pk, MBEDTLS_PK_RSA)) {
SERIAL.println("Public key is not an rsa key!");
return false;
}
auto mdinfo = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_init(&rsa);
mbedtls_md_setup(&rsa, mdinfo, 0);
mbedtls_md_starts(&rsa);
if (totalSize <= 0) {
//SERIAL.println("Warning: Unable to determine update size.");
//totalSize = UPDATE_SIZE_UNKNOWN;
SERIAL.println("Unknown update size, stop.");
return false;
}
if (!Update.begin(totalSize)) {
SERIAL.println("Failed to begin Update.");
return false;
}
bool first = true;
while (client.connected() && (totalSize > 0 || totalSize == UPDATE_SIZE_UNKNOWN)) {
const auto size = client.available();
if (size > 0) {
const auto bytesToRead = std::min(static_cast<int>(buffer.size()), size);
const auto bytesRead = client.read(buffer.data(), bytesToRead);
if (first) {
if (bytesRead != signature.size()) {
SERIAL.println("Failed to read signature!");
return false;
}
std::copy(buffer.cbegin(), buffer.cend(), signature.begin());
first = false;
} else {
if (!Update.write(buffer.data(), bytesRead)) {
SERIAL.println("Failed to write Update.");
return false;
}
mbedtls_md_update(&rsa, buffer.data(), bytesRead);
}
if (totalSize > 0)
totalSize -= bytesRead;
} else {
delay(1);
}
}
if (totalSize == 0) {
unsigned char hash[mdinfo->size];
mbedtls_md_finish(&rsa, hash);
auto ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, hash, mdinfo->size,
signature.data(), signature.size());
mbedtls_md_free(&rsa);
mbedtls_pk_free(&pk);
if (ret == 0) {
Update.end(true);
return true;
} else {
// validation failed, overwrite the first few bytes so this partition won't boot!
SERIAL.println("Signature validation failed!");
//ESP.partitionEraseRange( partition, 0, ENCRYPTED_BLOCK_SIZE);
Update.abort();
}
}
return false;
}