You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
9.4 KiB

// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. <hello@foundationdevices.com>
4 years ago
// SPDX-License-Identifier: GPL-3.0-or-later
//
// SPDX-FileCopyrightText: 2018 Coinkite, Inc. <coldcardwallet.com>
4 years ago
// SPDX-License-Identifier: GPL-3.0-only
//
4 years ago
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include "flash.h"
#include "pprng.h"
#include "hash.h"
#include "se.h"
#include "se-atecc608a.h"
#include "se-config.h"
#include "sha256.h"
static int se_write_data_slot(int slot_num, uint8_t *data, int len, bool lock_it)
{
if ((len < 32) || (len > 416) || (lock_it && slot_num == 8)) {
return -1;
}
4 years ago
for (int blk = 0, xlen = len; xlen > 0; blk++, xlen -= 32) {
int rc;
// have to write each "block" of 32-bytes, separately
// zone => data
se_write(OP_Write, 0x80|2, (blk<<8) | (slot_num<<3), data+(blk*32), 32);
rc = se_read1();
if (rc < 0)
return -1;
}
if (lock_it) {
// Assume 36/72-byte long slot, which will be partially written, and rest
// should be ones.
const int slot_len = (slot_num <= 7) ? 36 : 72;
uint8_t copy[slot_len];
memset(copy, 0xff, slot_len);
memcpy(copy, data, len);
// calc expected CRC
uint8_t crc[2] = {0, 0};
se_crc16_chain(slot_len, copy, crc);
// do the lock
se_write(OP_Lock, 2 | (slot_num << 2), (crc[1]<<8) | crc[0], NULL, 0);
return se_read1();
}
return 0;
}
static int se_lock_config_zone(const uint8_t config[128])
{
uint8_t crc[2] = {0, 0};
se_crc16_chain(128, config, crc);
// do the lock: mode=0
se_write(OP_Lock, 0x0, (crc[1]<<8) | crc[0], NULL, 0);
return se_read1();
}
static int se_lock_data_zone(void)
{
// do the lock: mode=1 (datazone) + 0x80 (no CRC check)
se_write(OP_Lock, 0x81, 0x0000, NULL, 0);
return se_read1();
}
static int se_config_write(uint8_t *config)
{
// send all 128 bytes, less some that can't be written.
for (int n = 16; n < 128; n += 4) {
int rc;
if (n == 84) continue; // that word not writable
// Must work on words, since can't write to most of the complete blocks.
// args = write_params(block=n//32, offset=n//4, is_config=True)
// p2 = (block << 3) | offset
se_write(OP_Write, 0, n/4, &config[n], 4);
rc = se_read1();
if (rc < 0)
return -1;
}
return 0;
}
// One-time config and lockdown of the chip
//
// CONCERN: Must not be possible to call this function after replacing
// the chip deployed originally. But key secrets would have been lost
// by then anyway... looks harmless, and regardless once the datazone
// is locked, none of this code will work... but:
//
// IMPORTANT: If they blocked the real chip, and provided a blank one for
// us to write the (existing) pairing secret into, they would see the pairing
// secret in cleartext. They could then restore original chip and access freely.
//
// PASSPORT NOTE: We can eliminate the above by having the factory bootloader
// be different than the normal bootloader. The factory bootloader
4 years ago
// will have the one-time setup code only, not the runtime code.
// The normal bootloader will NOT have the one-time setup code,
// but WILL have the main runtime code. So swapping in blank
4 years ago
// SE would not trigger us to write the pairing secret in the clear.
//
int se_setup_config(rom_secrets_t *secrets)
{
uint8_t config[128] = {0};
int rc = se_config_read(config);
if (rc < 0)
return -1;
uint8_t serial[9];
memcpy(serial, &config[0], 4);
memcpy(&serial[4], &config[8], 5);
memcpy(secrets->se_serial_number, serial, sizeof(serial));
// Setup steps:
// - write config zone data
// - lock that
// - write pairing secret (test it works)
// - pick RNG value for words secret (and forget it)
// - set all PIN values to known value (zeros)
// - set all money secrets to knonw value (zeros)
// - lock the data zone
if (config[87] == 0x55)
{
// config is still unlocked
// setup "config zone" area of the chip
static const uint8_t config_1[] = SE_CHIP_CONFIG_1;
static const uint8_t config_2[] = SE_CHIP_CONFIG_2;
memcpy(&config[16], config_1, sizeof(config_1));
memcpy(&config[90], config_2, sizeof(config_2));
// write it.
if (se_config_write(config))
return -2;
// lock config zone
if (se_lock_config_zone(config))
return -3;
}
if (config[86] == 0x55)
{
// data is still unlocked
uint8_t zeros[72];
memset(zeros, 0, sizeof(zeros));
uint16_t unlocked = config[88] | (((uint8_t)config[89]) << 8);
for (int kn = 0; kn < 16; kn++)
{
if (!(unlocked & (1 << kn)))
continue;
switch (kn)
{
case 12:
break;
case 15:
break;
case KEYNUM_pairing_secret:
4 years ago
if (se_write_data_slot(kn, secrets->pairing_secret, 32, false))
return -4;
break;
case KEYNUM_pin_stretch:
case KEYNUM_pin_attempt:
{
// HMAC-SHA256 key (forgotten immediately), for:
// - phishing words
// - each pin attempt (limited by counter0)
// - stretching pin/words attempts (iterated may times)
// See mathcheck.py for details.
uint8_t tmp[32];
4 years ago
rng_buffer(tmp, sizeof(tmp));
4 years ago
if (se_write_data_slot(kn, tmp, 32, true))
return -5;
}
break;
case KEYNUM_pin_hash:
4 years ago
case KEYNUM_lastgood:
case KEYNUM_firmware_hash:
4 years ago
if (se_write_data_slot(kn, zeros, 32, false))
return -6;
break;
case KEYNUM_supply_chain: {
// SCV key is in user settings flash
uint8_t* supply_chain_key = (uint8_t*)USER_SETTINGS_FLASH_ADDR;
bool is_erased = true;
for (uint32_t i=0; i<32; i++) {
if (supply_chain_key[i] != 0xFF) {
is_erased = false;
}
}
// If the scv key is not set in flash, then don't proceed, else validation will never work!
if (is_erased) {
return -11;
}
int rc = se_write_data_slot(kn, supply_chain_key, 32, false);
// Always erase the supply chain key, even if the write failed
flash_sector_erase(USER_SETTINGS_FLASH_ADDR);
if (rc)
4 years ago
return -7;
}
4 years ago
break;
case KEYNUM_seed:
case KEYNUM_user_fw_pubkey:
if (se_write_data_slot(kn, zeros, 72, false))
4 years ago
return -8;
break;
case KEYNUM_match_count:
{
uint32_t buf[32/4] = { 1024, 1024 };
if (se_write_data_slot(KEYNUM_match_count, (uint8_t *)buf,sizeof(buf),false))
4 years ago
return -9;
}
break;
case 0:
if (se_write_data_slot(kn, (uint8_t *)copyright_msg, 32, true))
return -10;
break;
default:
break;
}
}
// lock the data zone and effectively enter normal operation.
if (se_lock_data_zone())
return -11;
}
return 0;
}
// Do Info(p1=3) command, and return result.
//
// IMPORTANT: do not trust this result, could be MitM'ed.
//
uint8_t se_get_gpio(void)
{
int rc;
se_write(OP_Info, 0x3, 0, NULL, 0);
uint8_t tmp[4];
rc = se_read(tmp, 4);
if (rc < 0)
return -1;
return tmp[1];
}
int se_set_gpio(int state)
{
// 1=turn on green, 0=red light (if not yet configured to be secure)
se_write(OP_Info, 3, 2 | (!!state), NULL, 0);
// "Always return the current state in the first byte followed by three bytes of 0x00"
// - simple 1/0, in LSB.
uint8_t resp[4];
int rc = se_read(resp, 4);
se_sleep();
if (rc < 0)
return -1;
return ((resp[0] & 0x01) != state) ? -1 : 0;
}
// Set the GPIO using secure hash generated somehow already.
//
int se_set_gpio_secure(uint8_t *digest)
{
int rc;
rc = se_pair_unlock();
if (rc < 0)
return -1;
rc = se_checkmac(KEYNUM_firmware_hash, digest);
4 years ago
if (rc < 0)
return -1;
rc = se_set_gpio(1);
if (rc < 0)
return -1;
return 0;
}
int se_program_board_hash(
uint8_t *previous_hash,
uint8_t *new_hash,
4 years ago
uint8_t hash_len
)
{
#ifdef PRODUCTION_BUILD
return se_encrypted_write(KEYNUM_firmware_hash, KEYNUM_firmware_hash, previous_hash, new_hash, hash_len);
4 years ago
#else
return 0;
#endif /* PRODUCTION_BUILD */
4 years ago
}
bool se_valid_secret(
uint8_t *secret
)
{
int rc = se_checkmac(KEYNUM_pairing_secret, secret);
4 years ago
if (rc < 0)
return false;
return true;
}