forked from Foundation-Devices/passport-firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
update.c
399 lines (332 loc) · 13.3 KB
/
update.c
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
// SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
// SPDX-License-Identifier: GPL-3.0-only
//
/*
* (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
* and is covered by GPLv3 license found in COPYING.
*
* update.c -- firmware update processing
*
*/
#include <string.h>
#include <stdlib.h>
#include "display.h"
#include "fwheader.h"
#include "hash.h"
#include "se-config.h"
#include "sha256.h"
#include "spiflash.h"
#include "splash.h"
#include "utils.h"
#include "se-atecc608a.h"
#include "verify.h"
#include "flash.h"
#include "update.h"
#include "ui.h"
#include "gpio.h"
#include "firmware-keys.h"
// Global so we can compare with it later in do_update()
static uint8_t spi_hdr_hash[HASH_LEN] = {0};
static void clear_update_from_spi_flash()
{
uint8_t zeros[FW_HEADER_SIZE] = {0};
spi_write(0, 256, zeros);
spi_write(256, sizeof(zeros), zeros);
}
static void calculate_spi_hash(
passport_firmware_header_t *hdr,
uint8_t *hash,
uint8_t hashlen
)
{
SHA256_CTX ctx;
uint32_t pos = FW_HEADER_SIZE + 256; // Skip over the update hash page
uint32_t remaining = hdr->info.fwlength;
uint8_t *buf = (uint8_t *)D1_AXISRAM_BASE; /* Working memory */
sha256_init(&ctx);
sha256_update(&ctx, (uint8_t *)&hdr->info, sizeof(fw_info_t));
while (remaining > 0)
{
size_t bufsize;
if (remaining >= 8192)
bufsize = 8192;
else
bufsize = remaining;
if (spi_read(pos, bufsize, buf) != HAL_OK)
goto out;
sha256_update(&ctx, buf, bufsize);
remaining -= bufsize;
pos += bufsize;
}
sha256_final(&ctx, hash);
/* double SHA256 */
sha256_init(&ctx);
sha256_update(&ctx, hash, hashlen);
sha256_final(&ctx, hash);
out:
return;
}
static void calculate_spi_hdr_hash(
passport_firmware_header_t *hdr,
uint8_t *hash,
uint8_t hashlen
)
{
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, (uint8_t *)hdr, sizeof(passport_firmware_header_t));
sha256_final(&ctx, hash);
/* double SHA256 */
sha256_init(&ctx);
sha256_update(&ctx, hash, hashlen);
sha256_final(&ctx, hash);
}
// Hash the spi hash with the device hash value -- used to prevent external attacker from beign able to insert a firmware
// update directly in external SPI flash. They won't be able to replicate this hash.
static void calculate_update_hash(
uint8_t *spi_hash,
uint8_t spi_hashlen,
uint8_t *update_hash,
uint8_t update_hashlen
)
{
SHA256_CTX ctx;
uint8_t device_hash[HASH_LEN];
get_device_hash(device_hash);
sha256_init(&ctx);
sha256_update(&ctx, (uint8_t *)spi_hash, spi_hashlen);
sha256_update(&ctx, device_hash, sizeof(device_hash));
sha256_final(&ctx, update_hash);
}
static int do_update(uint32_t size)
{
int rc;
uint8_t flash_word_len = sizeof(uint32_t) * FLASH_NB_32BITWORD_IN_FLASHWORD;
uint32_t pos;
uint32_t addr;
uint32_t data[FLASH_NB_32BITWORD_IN_FLASHWORD] __attribute__((aligned(8)));
uint32_t total = FW_END - FW_START;
uint8_t percent_done = 0;
uint8_t last_percent_done = 255;
uint8_t curr_spi_hdr_hash[HASH_LEN] = {0};
uint32_t remaining_bytes_to_hash = sizeof(passport_firmware_header_t);
secresult not_checked = SEC_TRUE;
SHA256_CTX ctx;
sha256_init(&ctx);
flash_unlock();
// Make sure header still fits in one page or this check will be more complex.
if (sizeof(passport_firmware_header_t) > 256) {
clear_update_from_spi_flash();
ui_show_fatal_error("sizeof(passport_firmware_header_t) > 256");
}
for (pos = 0, addr = FW_START; pos < size; pos += flash_word_len, addr += flash_word_len)
{
// We read starting 256 bytes in as the first page holds the update request hash
if (spi_read(pos + 256, sizeof(data), (uint8_t *)data) != HAL_OK)
{
rc = -1;
break;
}
// TOCTOU check by hashing the header again and comparing to the hash we took earlier when we verified it.
if (remaining_bytes_to_hash > 0) {
// Calculate the running hash 32 bytes at a time until we reach sizeof(passport_firmware_header_t)
size_t hash_size = MIN(remaining_bytes_to_hash, flash_word_len);
sha256_update(&ctx, (uint8_t *)data, hash_size);
remaining_bytes_to_hash -= hash_size;
}
if (not_checked == SEC_TRUE && remaining_bytes_to_hash == 0) {
// Finalize the hash and check it
sha256_final(&ctx, curr_spi_hdr_hash);
/* double SHA256 */
sha256_init(&ctx);
sha256_update(&ctx, curr_spi_hdr_hash, HASH_LEN);
sha256_final(&ctx, curr_spi_hdr_hash);
// ui_show_hex_buffer("Prev Hash", spi_hdr_hash, HASH_LEN);
// ui_show_hex_buffer("TOCTOU Hash", curr_spi_hdr_hash, HASH_LEN);
// Compare the hashes
if (memcmp(curr_spi_hdr_hash, spi_hdr_hash, HASH_LEN) != 0) {
// Someone may be hacking on the SPI flash!
clear_update_from_spi_flash();
ui_show_fatal_error("\nSPI flash appears to have been actively modified during firmware update.");
}
not_checked = SEC_FALSE;
}
if (addr % FLASH_SECTOR_SIZE == 0)
{
rc = flash_sector_erase(addr);
if (rc < 0)
break;
}
rc = flash_burn(addr, (uint32_t)data);
if (rc < 0)
break;
/* Update the progress bar only if the percentage changed */
percent_done = (uint8_t)((float)pos/(float)total * 100.0f);
if (percent_done != last_percent_done)
{
display_progress_bar(PROGRESS_BAR_MARGIN, PROGRESS_BAR_Y, SCREEN_WIDTH - (PROGRESS_BAR_MARGIN * 2), PROGRESS_BAR_HEIGHT, percent_done);
/* Showing just the lines that changed is much faster and avoids full-screen flicker */
display_show_lines(PROGRESS_BAR_Y, PROGRESS_BAR_Y + PROGRESS_BAR_HEIGHT);
last_percent_done = percent_done;
}
}
/* Clear the remainder of flash */
memset(data, 0, sizeof(data));
for (; addr < FW_END; pos += flash_word_len, addr += flash_word_len)
{
if (addr % FLASH_SECTOR_SIZE == 0)
{
rc = flash_sector_erase(addr);
if (rc < 0)
break;
}
rc = flash_burn(addr, (uint32_t)data);
if (rc < 0)
break;
/* Update the progress bar only if the percentage changed */
percent_done = (uint8_t)((float)pos/(float)total * 100.0f);
if (percent_done != last_percent_done)
{
display_progress_bar(PROGRESS_BAR_MARGIN, PROGRESS_BAR_Y, SCREEN_WIDTH - (PROGRESS_BAR_MARGIN * 2), PROGRESS_BAR_HEIGHT, percent_done);
/* Showing just the lines that changed is much faster and avoids full-screen flicker */
display_show_lines(PROGRESS_BAR_Y, PROGRESS_BAR_Y + PROGRESS_BAR_HEIGHT);
last_percent_done = percent_done;
}
}
/* Make sure the progress bar goes to 100 */
display_progress_bar(PROGRESS_BAR_MARGIN, PROGRESS_BAR_Y, SCREEN_WIDTH - (PROGRESS_BAR_MARGIN * 2), PROGRESS_BAR_HEIGHT, 100);
display_show_lines(PROGRESS_BAR_Y, PROGRESS_BAR_Y + PROGRESS_BAR_HEIGHT);
flash_lock();
return rc;
}
secresult is_firmware_update_present(void)
{
passport_firmware_header_t hdr = {};
if (spi_setup() != HAL_OK)
return SEC_FALSE;
// Skip first page of flash
if (spi_read(256, sizeof(hdr), (void *)&hdr) != HAL_OK)
return SEC_FALSE;
if (!verify_header(&hdr))
return SEC_FALSE;
return SEC_TRUE;
}
void update_firmware(void)
{
int rc;
passport_firmware_header_t *internalhdr = FW_HDR;
passport_firmware_header_t spihdr = {0};
uint8_t internal_fw_hash[HASH_LEN] = {0};
uint8_t spi_fw_hash[HASH_LEN] = {0};
uint8_t current_board_hash[HASH_LEN] = {0};
uint8_t new_board_hash[HASH_LEN] = {0};
uint8_t actual_update_hash[HASH_LEN] = {0};
uint8_t expected_update_hash[HASH_LEN] = {0};
/*
* If we fail to either setup the SPI bus or read the SPI flash
* then just return...something is wrong in hardware but maybe it's
* temporary.
*/
if (spi_setup() != HAL_OK)
return;
// If the update was requested by the user, then there will be a hash in the first 32 bytes that combines
// the firmware hash with the device hash.
if (spi_read(0, HASH_LEN, (void *)&actual_update_hash) != HAL_OK)
return;
// Start reading one page in as there is a 32-byte hash in the first page
if (spi_read(256, sizeof(spihdr), (void *)&spihdr) != HAL_OK)
return;
// ui_show_hex_buffer("SPI Hdr 1", (uint8_t*)&spihdr, 170);
calculate_spi_hdr_hash(&spihdr, spi_hdr_hash, HASH_LEN);
// ui_show_hex_buffer("SPI Hdr Hash", spi_hdr_hash, HASH_LEN);
calculate_update_hash(spi_hdr_hash, sizeof(spi_hdr_hash), expected_update_hash, sizeof(expected_update_hash));
// Ensure that the hashes match!
if (memcmp(expected_update_hash, actual_update_hash, sizeof(expected_update_hash)) != 0) {
// This looks like an unrequested update (i.e., a possible attack)
goto out;
}
/* Verify firmware header in SPI flash and bail if it fails */
if (!verify_header(&spihdr))
{
if (ui_show_message("Update Error", "The firmware update you chose has an invalid header and will not be installed.", "SHUTDOWN", "OK", true)){
goto out;
} else {
display_clean_shutdown();
}
}
/*
* If the current firmeware verification passes then compare
* timestamps and don't allow an earlier version. However, if the
* internal firmware header verification fails then proceed with the
* update...maybe the previous update attempt failed because we lost
* power.
*
* We also allow going back and forth between user-signed firmware and Foundation-signed firmware.
*/
if (verify_current_firmware(true) == SEC_TRUE)
{
if ((spihdr.signature.pubkey1 != FW_USER_KEY && internalhdr->signature.pubkey1 != FW_USER_KEY) &&
(spihdr.info.timestamp < internalhdr->info.timestamp))
{
if (ui_show_message("Update Error", "This firmware update is older than the current firmware and will not be installed.", "SHUTDOWN", "OK", true))
goto out;
else
display_clean_shutdown();
}
// Handle the firmware hash update
uint8_t *fwptr = (uint8_t *)internalhdr + FW_HEADER_SIZE;
hash_fw(&internalhdr->info, fwptr, internalhdr->info.fwlength, internal_fw_hash, sizeof(internal_fw_hash));
hash_board(internal_fw_hash, sizeof(internal_fw_hash), current_board_hash, sizeof(current_board_hash));
calculate_spi_hash(&spihdr, spi_fw_hash, sizeof(spi_fw_hash));
/* Verify the signature and bail if it fails */
if (verify_signature(&spihdr, spi_fw_hash, sizeof(spi_fw_hash)) == SEC_FALSE)
{
if (ui_show_message("Update Error", "The firmware update does not appear to be properly signed and will not be installed.\n\nThis can also occur if you lost power during a firmware update.", "SHUTDOWN", "OK", true))
goto out;
else
display_clean_shutdown();
}
/*
* Calculate a new board hash based on the SPI firmware and then
* reprogram the board hash in the SE. If the update fails it
* will be retried until it succeeds or the board is declared dead.
*/
hash_board(spi_fw_hash, sizeof(spi_fw_hash), new_board_hash, sizeof(new_board_hash));
#ifdef CONVERSION_BUILD
/*
* Conversion build is temporary and used to get current demo
* boards which have 0's programmed for the board hash to be
* properly programmed with a real board hash. Thereafter they
* will only be able to update via SD card.
* Delete this code once this has been done.
*/
memset(current_board_hash, 0, sizeof(current_board_hash));
#endif /* CONVERSION_BUILD */
rc = se_program_board_hash(current_board_hash, new_board_hash, sizeof(new_board_hash));
if (rc < 0) {
if (ui_show_message("Update Error", "Unable to update the firmware hash in the Secure Element. Update will continue, but may not be successful.", "SHUTDOWN", "OK", true)){
// Nothing to do
} else {
display_clean_shutdown();
}
}
}
// Draw the logo and message - progress bar gets drawn and updated periodically in do_update()
show_splash("Updating Firmware...");
rc = do_update(FW_HEADER_SIZE + spihdr.info.fwlength);
if (rc < 0)
{
if (ui_show_message("Update Error", "Failed to install the firmware update.", "SHUTDOWN", "RESTART", true))
passport_reset();
else
// TODO: Should we have an option here to clear the SPI flash and restart (we could run a verify_current_firmware() first to make sure it's safe to boot there
display_clean_shutdown();
}
out:
clear_update_from_spi_flash();
}
secresult is_user_signed_firmware_installed(void)
{
passport_firmware_header_t *hdr = FW_HDR;
return (hdr->signature.pubkey1 == FW_USER_KEY && hdr->signature.pubkey2 == 0) ? SEC_TRUE : SEC_FALSE;
}