Switch to exactly 13 tries

This commit is contained in:
Peter D. Gray 2019-07-26 11:23:45 -04:00
parent 0a26a6360b
commit bf3a5b8955
14 changed files with 379 additions and 173 deletions

View File

@ -1045,22 +1045,20 @@ ae_sign(uint8_t keynum, uint8_t msg_hash[32], uint8_t signature[64])
// ae_get_counter()
//
// Inc and return the one-way counter.
// Just read a one-way counter.
//
int
ae_get_counter(uint32_t *result, int counter_number, bool incr)
ae_get_counter(uint32_t *result, uint8_t counter_number)
{
ae_send(OP_Counter, incr ? 0x1 : 0x0, counter_number);
ae_send(OP_Counter, 0x0, counter_number);
ae_delay(OP_Counter);
ae_delay(OP_Counter);
// already in correct endian
int rv = ae_read_n(4, (uint8_t *)result);
RET_IF_BAD(rv);
int rv = ae_read_n(4, (uint8_t *)result);
RET_IF_BAD(rv);
// IMPORTANT: Always verify the counter's value because otherwise
// nothing prevents an active MitM changing the value that we think
// we just read. They could also stop us increamenting the counter.
// we just read.
uint8_t digest[32];
rv = ae_gendig_counter(counter_number, *result, digest);
@ -1071,7 +1069,37 @@ ae_get_counter(uint32_t *result, int counter_number, bool incr)
fatal_mitm();
}
// worked.
return 0;
}
// ae_add_counter()
//
// Add-to and return a one-way counter's value. Have to go up in
// single-unit steps, but can we loop.
//
int
ae_add_counter(uint32_t *result, uint8_t counter_number, int incr)
{
for(int i=0; i<incr; i++) {
ae_send(OP_Counter, 0x1, counter_number);
ae_delay(OP_Counter);
int rv = ae_read_n(4, (uint8_t *)result);
RET_IF_BAD(rv);
}
// IMPORTANT: Always verify the counter's value because otherwise
// nothing prevents an active MitM changing the value that we think
// we just read. They could also stop us increamenting the counter.
uint8_t digest[32];
int rv = ae_gendig_counter(counter_number, *result, digest);
RET_IF_BAD(rv);
if(!ae_is_correct_tempkey(digest)) {
// no legit way for this to happen, so just die.
fatal_mitm();
}
return 0;
}
@ -1879,6 +1907,7 @@ ae_setup_config(void)
switch(kn) {
default:
case 12: break;
case 15: break;
case KEYNUM_pairing:
@ -1888,14 +1917,17 @@ ae_setup_config(void)
break;
case KEYNUM_pin_stretch:
case KEYNUM_pin_attempt:
case KEYNUM_words: {
// - hmac key for phishing words (and then we forget it)
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];
#if 0
rng_buffer(tmp, sizeof(tmp));
#else
#warning "fixed secrets"
# warning "fixed secrets"
memset(tmp, 0x41+kn, 32);
#endif
@ -1908,6 +1940,7 @@ ae_setup_config(void)
case KEYNUM_main_pin:
case KEYNUM_lastgood:
case KEYNUM_duress_pin:
case KEYNUM_duress_lastgood:
case KEYNUM_brickme:
case KEYNUM_firmware:
if(ae_write_data_slot(kn, zeros, 32, false)) {
@ -1930,9 +1963,13 @@ ae_setup_config(void)
break;
}
case KEYNUM_match_count:
ae_write_match_count(1024, NULL);
case KEYNUM_match_count: {
uint32_t buf[32/4] = { 1024, 1024 };
if(ae_write_data_slot(KEYNUM_match_count, (const uint8_t *)buf,sizeof(buf),false)) {
INCONSISTENT("wr mc");
}
break;
}
case 0:
if(ae_write_data_slot(kn, (const uint8_t *)copyright_msg, 32, true)) {
@ -1952,12 +1989,15 @@ ae_setup_config(void)
}
#if 0
// ae_write_match_count()
//
int
ae_write_match_count(uint32_t count, const uint8_t *write_key)
{
uint32_t buf[8] = { count, count };
// ASSERT(count & 31 == 0); // not clear, but probably should have 5LSB=0
STATIC_ASSERT(sizeof(buf) == 32); // limitation of ae_write_data_slot
if(!write_key) {
@ -1967,35 +2007,54 @@ ae_write_match_count(uint32_t count, const uint8_t *write_key)
write_key, (const uint8_t *)buf);
}
}
#endif
// ae_kdf_iter()
// ae_stretch_iter()
//
// Do on-chip KDF, with lots of iterations.
// Do on-chip hashing, with lots of iterations.
//
// - always HKDF (based on TLS 1.3): hmac.new(privkey, msg, hashlib.sha256).digest()
// - results written to tmpkey if not last iter
// - output always encrypted
// - pairing dance must already be done.
// - using HMAC-SHA256 with keys that are known only to the 608a.
// - first round is with indicated keyslot, which may have a usage counter linked
// - rate limiting factor here is communication time w/ 608a, not algos.
// - caution: result here is not confidential
//
int
ae_kdf_iter(uint8_t keynum, const uint8_t start[32], uint8_t end[32], int iterations)
ae_stretch_iter(const uint8_t start[32], uint8_t end[32], int iterations)
{
ASSERT(start != end); // we can't work inplace
if(ae_pair_unlock()) return -1;
int rv = ae_hmac32(keynum, start, end);
RET_IF_BAD(rv);
memcpy(end, start, 32);
for(int i=0; i<iterations; i++) {
// must unlock again, because pin_stretch is an auth'd key
if(ae_pair_unlock()) return -2;
rv = ae_hmac32(KEYNUM_pin_stretch, end, end);
int rv = ae_hmac32(KEYNUM_pin_stretch, end, end);
RET_IF_BAD(rv);
}
return 0;
}
// ae_mixin_key()
//
// Apply HMAC using secret in chip as a HMAC key, then encrypt
// the result a little because read in clear over bus.
//
int
ae_mixin_key(uint8_t keynum, const uint8_t start[32], uint8_t end[32])
{
ASSERT(start != end); // we can't work inplace
if(ae_pair_unlock()) return -1;
if(keynum != 0) {
int rv = ae_hmac32(keynum, start, end);
RET_IF_BAD(rv);
} else {
memset(end, 0, 32);
}
// Final value was just read over bus w/o any protection, but
// we won't be using that, instead, mix in the pairing secret.
//
@ -2007,9 +2066,9 @@ ae_kdf_iter(uint8_t keynum, const uint8_t start[32], uint8_t end[32], int iterat
sha256_init(&ctx);
sha256_update(&ctx, rom_secrets->pairing_secret, 32);
sha256_update(&ctx, end, 32);
sha256_update(&ctx, start, 32);
sha256_update(&ctx, &keynum, 1);
sha256_update(&ctx, end, 32);
sha256_final(&ctx, end);
return 0;

View File

@ -4,7 +4,7 @@
*/
#pragma once
//
// Atmel ATECC508A related code.
// Atmel ATECC508A and 608A related code. Trying to keep this able to handle both devices.
//
//#define FOR_508 1
@ -126,8 +126,11 @@ int ae_unlock_ip(uint8_t keynum, const uint8_t secret[32]);
// is random and we both know is random too!
int ae_pick_nonce(const uint8_t num_in[20], uint8_t tempkey[32]);
// Increment and return a one-way counter.
int ae_get_counter(uint32_t *result, int counter_number, bool incr);
// Read a one-way counter (there are 2 of these)
int ae_get_counter(uint32_t *result, uint8_t counter_number);
// Add onto a counter. Slow; has to go by one.
int ae_add_counter(uint32_t *result, uint8_t counter_number, int incr);
// Perform HMAC on the chip, using a particular key.
//int ae_hmac(uint8_t keynum, const uint8_t *msg, uint16_t msg_len, uint8_t digest[32]);
@ -170,7 +173,10 @@ extern void fatal_mitm(void) __attribute__((noreturn));
int ae_write_match_count(uint32_t count, const uint8_t *write_key);
// Perform many key iterations and read out the result. Designed to be slow.
int ae_kdf_iter(uint8_t keynum, const uint8_t start[32], uint8_t end[32], int iterations);
int ae_stretch_iter(const uint8_t start[32], uint8_t end[32], int iterations);
// Mix in (via HMAC) the contents of a specific key on the device.
int ae_mixin_key(uint8_t keynum, const uint8_t start[32], uint8_t end[32]);
#endif

View File

@ -6,7 +6,7 @@
#define AE_CHIP_CONFIG_1 { \
0xe1, 0x00, 0x61, 0x00, 0x00, 0x00, 0x8f, 0x2d, 0x8f, 0x80, \
0x8f, 0x43, 0xaf, 0x80, 0x00, 0x43, 0x00, 0x43, 0x8f, 0x47, \
0xc3, 0x43, 0xc3, 0x43, 0xc7, 0x47, 0x8f, 0x80, 0x00, 0x00, \
0xc3, 0x43, 0xc3, 0x43, 0xc7, 0x47, 0x00, 0x47, 0x00, 0x00, \
0x8f, 0x4d, 0x8f, 0x43, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, \
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, \
@ -18,14 +18,14 @@
#define AE_CHIP_CONFIG_2 { \
0x02, 0x15, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x5c, 0x00, \
0xbc, 0x01, 0xfc, 0x01, 0xbc, 0x01, 0x9c, 0x01, 0x9c, 0x01, \
0xfc, 0x01, 0xdc, 0x03, 0xdc, 0x03, 0xdc, 0x07, 0xbc, 0x01, \
0xfc, 0x01, 0xdc, 0x03, 0xdc, 0x03, 0xdc, 0x07, 0x9c, 0x01, \
0x3c, 0x00, 0xfc, 0x01, 0xdc, 0x01, 0x3c, 0x00 \
}
// key/slot usage and names
#define KEYNUM_pairing 1
#define KEYNUM_words 2
#define KEYNUM_pin_stretch 2
#define KEYNUM_main_pin 3
#define KEYNUM_pin_attempt 4
#define KEYNUM_lastgood 5
@ -34,7 +34,7 @@
#define KEYNUM_long_secret 8
#define KEYNUM_secret 9
#define KEYNUM_duress_secret 10
#define KEYNUM_pin_stretch 11
#define KEYNUM_duress_lastgood 11
#define KEYNUM_brickme 13
#define KEYNUM_firmware 14
@ -86,8 +86,8 @@ KeyConfig[9] = 0xdc03 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, R
Slot[10] = 0xc747 = SlotConfig(ReadKey=7, NoMac=0, LimitedUse=0, EncryptRead=1, IsSecret=1, WriteKey=7, WriteConfig=4)=0x47c7
KeyConfig[10] = 0xdc07 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=1, ReqAuth=1, AuthKey=7, PersistentDisable=0, RFU=0, X509id=0)=0x07dc
Slot[11] = 0x8f80 = SlotConfig(ReadKey=15, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=1, WriteKey=0, WriteConfig=8)=0x808f
KeyConfig[11] = 0xbc01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x01bc
Slot[11] = 0x0047 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=7, WriteConfig=4)=0x4700
KeyConfig[11] = 0x9c01 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=0, ReqRandom=0, ReqAuth=1, AuthKey=1, PersistentDisable=0, RFU=0, X509id=0)=0x019c
Slot[12] = 0x0000 = SlotConfig(ReadKey=0, NoMac=0, LimitedUse=0, EncryptRead=0, IsSecret=0, WriteKey=0, WriteConfig=0)=0x0000
KeyConfig[12] = 0x3c00 = KeyConfig(Private=0, PubInfo=0, KeyType=7, Lockable=1, ReqRandom=0, ReqAuth=0, AuthKey=0, PersistentDisable=0, RFU=0, X509id=0)=0x003c

View File

@ -31,7 +31,7 @@ extern void fatal_error(const char *) __attribute__((noreturn));
#define BREAKPOINT #error
#endif
// An assertion that we will check at *compile* time. GCC feature.
// An assertion that we will be checked at *compile* time. Useful GCC feature.
#define STATIC_ASSERT(cond) _Static_assert(cond, #cond)
// Similarly: for those times when you want to write ASSERT(False),

View File

@ -701,6 +701,13 @@ firewall_dispatch(int method_num, uint8_t *buf_io, int len_in,
}
break;
case 3:
// read raw counter0 value (max is 0x1fffff)
REQUIRE_OUT(4);
ae_setup();
rv = ae_get_counter((uint32_t *)buf_io, 0) ? EIO: 0;
break;
default:
rv = ENOENT;
break;

View File

@ -32,7 +32,7 @@ class KEYNUM_508: # mark 1, 2
class KEYNUM_608: # mark 3+
# reserve 0: it's weird
pairing = 1 # pairing hash key (picked by bootloader)
words = 2 # secret used just for generated 2-phase protection words (random, forgotten)
pin_stretch = 2 # secret used to stretch pin (random, forgotten)
main_pin = 3 # user-defined PIN to protect the cryptocoins (primary)
pin_attempt = 4 # secret mixed into pin generation (rate limited, random, forgotten)
lastgood = 5 # publically readable, PIN required to update: last successful PIN entry (1)
@ -41,10 +41,11 @@ class KEYNUM_608: # mark 3+
long_secret = 8 # 416 bytes protected by main pin (must be #8 - special longer slot)
secret = 9 # 72 arbitrary bytes protected by main pin (normal case)
duress_secret = 10 # 72 arbitrary bytes protected by duress pin
pin_stretch = 11 # secret used to stretch pin (random, forgotten)
duress_lastgood = 11 # counter value when duress last worked (so we can fake num_fails)
# available: 12
brickme = 13 # "Brick Me" PIN holder (no associated secret, but can roll the pairing secret)
firmware = 14 # hash of flash areas, stored as an unreadable secret, controls GPIO+light
# reserve 15: special limited use key
# reserve 15: non-special, but some fields have all ones and so point to it.
class AEConfig:
@ -159,8 +160,6 @@ def doit(partno, ae, KEYNUM, fp):
# - critical!
cc[KEYNUM.pairing].hash_key(roll_kn=KEYNUM.brickme).lockable(False)
# - "words" HMAC-key used for for 2-phase PIN words (only)
cc[KEYNUM.words].hash_key().require_auth(KEYNUM.pairing).deterministic()
if partno == 5:
# mark 1/2: most keyslots require knowledge of a PIN
@ -170,6 +169,9 @@ def doit(partno, ae, KEYNUM, fp):
(KEYNUM.pin_3, KEYNUM.secret_3, None),
(KEYNUM.pin_4, KEYNUM.secret_4, None) ]
# - "words" HMAC-key used for for 2-phase PIN words (only)
cc[KEYNUM.words].hash_key().require_auth(KEYNUM.pairing).deterministic()
main_pin = KEYNUM.pin_1
unused_slots = [0, 15]
@ -178,7 +180,7 @@ def doit(partno, ae, KEYNUM, fp):
secure_map = [
(KEYNUM.main_pin, KEYNUM.secret, KEYNUM.lastgood),
(KEYNUM.main_pin, KEYNUM.long_secret, None),
(KEYNUM.duress_pin, KEYNUM.duress_secret, None),
(KEYNUM.duress_pin, KEYNUM.duress_secret, KEYNUM.duress_lastgood),
]
main_pin = KEYNUM.main_pin
unused_slots = [0, 12, 15]
@ -215,7 +217,7 @@ def doit(partno, ae, KEYNUM, fp):
cc[kn].hash_key(write_kn=kn).require_auth(KEYNUM.pairing)
cc[sec_num].secret_storage(kn).require_auth(kn)
if lg_num is not None:
# used to hold counter[0/1] value when we last successfully got the PIN
# used to hold counter0] value when we last successfully got that PIN
cc[lg_num].writeable_storage(kn).require_auth(KEYNUM.pairing)
# "Brick Me" PIN holder: enables Roll of pairing secret + device destruction

View File

@ -102,9 +102,10 @@ SECTIONS
ASSERT(_pdg_hack == _erelocate,
"Sorry, no initialized data support! Set to zero or remove.")
/* ensure binary fits */
/* ensure binary fits *
ASSERT(_erelocate - BL_SRAM_BASE + _etext <= BL_FLASH_BASE + BL_FLASH_SIZE,
"Binary is too big to fit!!!")
*/
/* .bss section which is used for uninitialized data */
.bss (NOLOAD) :

View File

@ -16,11 +16,10 @@ from hmac import HMAC
keys = {}
# mdb 0x08007800 32
keys[KEYNUM.pairing] = a2b_hex(
'480d071589163dc9d6016c8af718ff0d7d4be8fa0a397745605151b423df4675')
keys[KEYNUM.pairing] = a2b_hex('2744eaab79b539cc72fc032b7516875ae0a63e8ab22f4cbf529d21f5ea665aa7')
# fixed values from instrumented version of code
for kn in [KEYNUM.pin_stretch, KEYNUM.pin_attempt, KEYNUM.words]:
for kn in [KEYNUM.pin_stretch, KEYNUM.pin_attempt]:
keys[kn] = bytes((0x41+kn) for i in range(32))
assert all(len(i) == 32 for i in keys.values()), repr(keys)
@ -28,8 +27,8 @@ assert all(len(i) == 32 for i in keys.values()), repr(keys)
PURPOSE_NORMAL = a2b_hex('58184d33')
PURPOSE_WORDS = a2b_hex('73676d2e')
KDF_ITER_WORDS = 16
KDF_ITER_PIN = 32
KDF_ITER_WORDS = 12
KDF_ITER_PIN = 8
def show(lab, val):
print('%s => \n %s' % (lab, b2a_hex(val).decode('ascii')))
@ -48,26 +47,30 @@ def pin_hash(pin, purpose):
return sha256(md.digest()).digest()
# see ae_kdf_iter in ae.c
def ae_kdf_iter(keynum, start, iterations):
hm = HMAC(keys[keynum], msg=start, digestmod=sha256)
end = hm.digest()
show('mixin(%d)' % keynum, end)
# see ae_stretch_iter in ae.c
def ae_stretch_iter(start, iterations):
end = bytes(start)
for i in range(iterations):
hs = HMAC(keys[KEYNUM.pin_stretch], msg=end, digestmod=sha256)
end = hs.digest()
show('2nd last', end)
return end
# see ae.c
def ae_mixin_key(keynum, start):
if keynum:
hm = HMAC(keys[keynum], msg=start, digestmod=sha256)
end = hm.digest()
else:
end = bytes(32)
md = sha256()
md.update(keys[KEYNUM.pairing])
md.update(end)
md.update(start)
md.update(bytes([keynum]))
md.update(end)
return md.digest()
@ -84,9 +87,9 @@ if 1:
start = pin_hash(prefix, PURPOSE_WORDS)
show('pin_hash(%r, WORDS)' % prefix, start)
end = ae_kdf_iter(KEYNUM.words, start, KDF_ITER_WORDS)
end = ae_stretch_iter(start, KDF_ITER_WORDS)
show('ae_kdf_iter()', end)
show('full hash', end)
show('words value', end[0:4])
@ -106,7 +109,10 @@ if 1:
pin = b'12-12'
start = pin_hash(pin, PURPOSE_NORMAL)
result = ae_kdf_iter(KEYNUM.pin_attempt, start, KDF_ITER_PIN);
mid_result = ae_stretch_iter(start, KDF_ITER_PIN);
show('mid-result', mid_result)
duress = ae_mixin_key(0, mid_result)
show('duress pin', duress)
result = ae_mixin_key(KEYNUM.pin_attempt, mid_result)
show('main pin', result)

View File

@ -406,4 +406,40 @@ oled_draw_bar(int percent)
}
#endif
// oled_factory_busy()
//
void
oled_factory_busy(void)
{
// Render a continuous activity (not progress) bar in lower 8 lines of display
// - using OLED itself to do the animation, so smooth and CPU free
// - cannot preserve bottom 8 lines, since we have to destructively write there
//oled_spi_setup();
static const uint8_t setup[] = {
0x21, 0x00, 0x7f, // setup column address range (start, end): 0-127
0x22, 7, 7, // setup page start/end address: page 7=last 8 lines
};
static const uint8_t animate[] = {
0x2e, // stop animations in progress
0x26, // scroll leftwards (stock ticker mode)
0, // placeholder
7, // start 'page' (vertical)
7, // scroll speed: 7=fastest,
7, // end 'page'
0, 0xff, // placeholders
0x2f // start
};
uint8_t data[128];
for(int x=0; x<128; x++) {
// each byte here is a vertical column, 8 pixels tall, MSB at bottom
data[x] = (1<<(7 - (x%8)));
}
oled_write_cmd_sequence(sizeof(setup), setup);
oled_write_data(sizeof(data), data);
oled_write_cmd_sequence(sizeof(animate), animate);
}
// EOF

View File

@ -28,4 +28,7 @@ void oled_busy_bar(bool en);
// show just a progress bar in bottom 8 rows (destructive)
void oled_draw_bar(int percent);
// just fun display in factory case
void oled_factory_busy(void);
// EOF

View File

@ -17,16 +17,16 @@
#include "clocks.h"
// Number of iterations for KDF
#define KDF_ITER_WORDS 16
#define KDF_ITER_PIN 32 // about 8 seconds (measured in-system)
#define KDF_ITER_WORDS 12
#define KDF_ITER_PIN 8 // about ? seconds (measured in-system)
// We try to keep at least this many PIN attempts available to legit users
// - challenge: comparitor resolution is only 32 units (5 LSB not implemented)
#define MIN_TARGET_ATTEMPTS 32
// - solution: adjust both the target and counter (upwards)
#define MAX_TARGET_ATTEMPTS 13
#if FOR_508
// better names going forward!
#define KEYNUM_main_pin KEYNUM_pin_1
#error "only supports 608 now"
#endif
// Pretty sure it doesn't matter, but adding some salt into our PIN->bytes[32] code
@ -143,10 +143,10 @@ pin_hash(const char *pin, int pin_len, uint8_t result[32], uint32_t purpose)
// pin_hash_attempt()
//
// Go from PIN to heavily hashed 32-byte value, suitable for device.
// Go from PIN to heavily hashed 32-byte value, suitable testing against device.
//
// - brickme pin doesn't do the extra KDF step, so it can be fast
// - any call to this code will cost a PIN attempt
// - call with target_kn == 0 to return a mid-state that can be used for both main and duress
//
static int
pin_hash_attempt(uint8_t target_kn, const char *pin, int pin_len, uint8_t result[32])
@ -154,21 +154,39 @@ pin_hash_attempt(uint8_t target_kn, const char *pin, int pin_len, uint8_t result
uint8_t tmp[32];
if(pin_len == 0) {
// zero len PIN is "blank" value: all zeros, no hashing
// zero len PIN is the "blank" value: all zeros, no hashing
memset(result, 0, 32);
return 0;
}
// quick local hashing
pin_hash(pin, pin_len, tmp, PIN_PURPOSE_NORMAL);
if(target_kn == KEYNUM_brickme) {
// no extra KDF for brickme case
memcpy(result, tmp, 32);
return 0;
}
// main, duress pins need mega hashing
int rv = ae_stretch_iter(tmp, result, KDF_ITER_PIN);
if(rv) return EPIN_AE_FAIL;
// CAUTION: at this point, we just read the value off the bus
// in clear text. Don't use that value directly.
if(target_kn == 0) {
// let the caller do either/both of the below mixins
return 0;
}
memcpy(tmp, result, 32);
if(target_kn == KEYNUM_main_pin) {
ae_mixin_key(KEYNUM_pin_attempt, tmp, result);
} else {
// main, duress pins need mega hashing
int rv = ae_kdf_iter(KEYNUM_pin_attempt, tmp, result, KDF_ITER_PIN);
if(rv) return EPIN_AE_FAIL;
ae_mixin_key(0, tmp, result);
}
return 0;
@ -179,8 +197,8 @@ pin_hash_attempt(uint8_t target_kn, const char *pin, int pin_len, uint8_t result
// Look up some bits... do HMAC(words secret) and return some LSB's
//
// CAUTIONS:
// - rate-limited by the chip, since it takes this many iterations
// - hash generated is not shown on bus (thanks to IO protection)
// - rate-limited by the chip, since it takes many iterations of HMAC(key we dont have)
// - hash generated is shown on bus (but further hashing happens after that)
//
int
pin_prefix_words(const char *pin_prefix, int prefix_len, uint32_t *result)
@ -191,10 +209,10 @@ pin_prefix_words(const char *pin_prefix, int prefix_len, uint32_t *result)
// hash it up, a little
pin_hash(pin_prefix, prefix_len, tmp, PIN_PURPOSE_WORDS);
// With 608a, we can do same KDF stretching to get good built-in delays
// Using 608a, we can do key stretching to get good built-in delays
ae_setup();
int rv = ae_kdf_iter(KEYNUM_words, tmp, digest, KDF_ITER_WORDS);
int rv = ae_stretch_iter(tmp, digest, KDF_ITER_WORDS);
ae_reset_chip();
if(rv) return -1;
@ -338,25 +356,35 @@ get_last_success(pinAttempt_t *args)
if(!ae_is_correct_tempkey(tempkey)) fatal_mitm();
// Read two values from data slots
uint32_t lastgood=0, match_count=0, counter=0;
uint32_t lastgood=0, match_count=0, counter=0, duress_lastgood=0;
if(_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -1;
if(_read_slot_as_counter(KEYNUM_duress_lastgood, &duress_lastgood)) return -1;
if(_read_slot_as_counter(KEYNUM_match_count, &match_count)) return -1;
// Read the monotonically-increasing counter
if(ae_get_counter(&counter, 0, false)) return -1;
if(ae_get_counter(&counter, 0)) return -1;
// Do the math
if(lastgood > counter) {
// monkey business, but impossible, right?!
args->num_fails = 99;
// Has the duress PIN been used more recently than real PIN?
// if so, lie about # of failures to make things look like good login
if(duress_lastgood > lastgood) {
// lie about # of failures, but keep the pin-rate limiting
args->num_fails = 0;
args->attempts_left = MAX_TARGET_ATTEMPTS;;
} else {
args->num_fails = counter - lastgood;
if(lastgood > counter) {
// monkey business, but impossible, right?!
args->num_fails = 99;
} else {
args->num_fails = counter - lastgood;
}
}
uint32_t mc = (match_count & ~31);
if(counter < mc) {
args->attempts_left = mc - counter;
} else {
// NOTE: 5LSB of match_count should be stored as zero.
match_count &= ~31;
if(counter < match_count) {
// typical case: some number of attempts left before death
args->attempts_left = match_count - counter;
} else if(counter >= match_count) {
// we're a brick now, but maybe say that nicer to customer
args->attempts_left = 0;
}
@ -488,14 +516,6 @@ pin_setup_attempt(pinAttempt_t *args)
return EPIN_AE_FAIL;
}
// has the duress pin (this wallet) been used this power cycle?
uint32_t fake_lastgood = backup_data_get(IDX_DURESS_USED);
if(fake_lastgood) {
// lie about # of failures, but keep the pin-rate limiting
args->num_fails = 0;
args->attempts_left = MIN_TARGET_ATTEMPTS;
}
// delays now handled by chip and our KDF process directly
args->delay_required = 0;
args->delay_achieved = 0;
@ -539,47 +559,88 @@ pin_delay(pinAttempt_t *args)
return 0;
}
// updates_for_duress_login()
//
static int
updates_for_duress_login(uint8_t digest[32])
{
// We keep another "good" login counter for duress, so we can
// show correctly-fake "num fails" and similar
uint32_t count;
int rv = ae_get_counter(&count, 0);
if(rv) return EPIN_AE_FAIL;
// update the "last good" counter for duress purposes
uint32_t tmp[32/4] = {0};
tmp[0] = count;
rv = ae_encrypted_write(KEYNUM_duress_lastgood, KEYNUM_duress_pin, digest, (void *)tmp, 32);
if(rv) {
ae_reset_chip();
return EPIN_AE_FAIL;
}
return 0;
}
// updates_for_good_login()
//
static int
updates_for_good_login(pinAttempt_t *args, uint8_t digest[32])
updates_for_good_login(uint8_t digest[32])
{
// User got the main PIN right: update the attempt counters,
// to document this (lastgood) and also bump the match counter if needed
uint32_t new_count;
int rv = ae_get_counter(&new_count, 0, true);
if(rv) return EPIN_AE_FAIL;
uint32_t count;
int rv = ae_get_counter(&count, 0);
if(rv) goto fail;
// update the "last good" counter
uint32_t tmp[32/4] = {0};
tmp[0] = new_count;
// Challenge: Have to update both the counter, and the target match value because
// no other way to have exact value.
rv = ae_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, digest, (void *)tmp, 32);
if(rv) {
ae_reset_chip();
return EPIN_AE_FAIL;
uint32_t mc = (count + MAX_TARGET_ATTEMPTS + 32) & ~31;
ASSERT(mc >= count);
int bump = (mc - MAX_TARGET_ATTEMPTS) - count;
ASSERT(bump >= 1);
ASSERT(bump < 32);
// Would rather update the counter first, so that a hostile interruption can't increase
// attempts (altho the attacker knows the pin at that point?!) .. but chip won't
// let the counter go past the match value, so that has to be first.
// set the new "match count"
{ uint32_t tmp[32/4] = {mc, mc} ;
rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32);
if(rv) goto fail;
}
uint32_t mc = (new_count + MIN_TARGET_ATTEMPTS) & ~31;
tmp[0] = tmp[1] = mc;
// incr the counter a bunch to get to that-13
uint32_t new_count = 0;
rv = ae_add_counter(&new_count, 0, bump);
if(rv) goto fail;
rv = ae_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32);
if(rv) {
ae_reset_chip();
return EPIN_AE_FAIL;
ASSERT(new_count == count + bump);
ASSERT(mc > new_count);
// Update the "last good" counter
{ uint32_t tmp[32/4] = {new_count, 0 };
rv = ae_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, digest, (void *)tmp, 32);
if(rv) goto fail;
}
args->num_fails = 0;
args->attempts_left = mc - new_count;
// NOTE: Some or all of the above writes could be blocked (trashed) by an
// active MitM attacker, but that would be pointless since these are authenticated
// writes, which have a MAC. They can't change the written value, due to the MAC, so
// all they can do is block the write, and not control it's value. Therefore, they will
// just be reducing tries. Also, rate limiting not affected but anything here.
// just be reducing attempt. Also, rate limiting not affected by anything here.
return 0;
fail:
ae_reset_chip();
return EPIN_AE_FAIL;
}
// pin_cache_save()
@ -587,6 +648,8 @@ updates_for_good_login(pinAttempt_t *args, uint8_t digest[32])
static void
pin_cache_save(pinAttempt_t *args, const uint8_t digest[32])
{
// TODO: encrypt w/ rom secret + SRAM seed value
if(args->magic_value == PA_MAGIC_V2) {
memcpy(args->cached_main_pin, digest, 32);
} else {
@ -601,6 +664,8 @@ pin_cache_save(pinAttempt_t *args, const uint8_t digest[32])
static void
pin_cache_restore(pinAttempt_t *args, uint8_t digest[32])
{
// TODO: decrypt w/ rom secret + SRAM seed value
if(args->magic_value == PA_MAGIC_V2) {
memcpy(digest, args->cached_main_pin, 32);
} else {
@ -636,12 +701,15 @@ pin_login_attempt(pinAttempt_t *args)
bool is_duress = false;
int secret_kn = -1;
// hash up the pin now, assuming we'll use it on main PIN
uint8_t digest[32];
rv = pin_hash_attempt(KEYNUM_main_pin, args->pin, args->pin_len, digest);
// hash up the pin now, assuming we'll use it on main PIN *OR* duress PIN
uint8_t mid_digest[32], digest[32];
rv = pin_hash_attempt(0, args->pin, args->pin_len, mid_digest);
if(rv) return EPIN_AE_FAIL;
// Do mixin for duress case.
rv = ae_mixin_key(0, mid_digest, digest);
if(rv) return EPIN_AE_FAIL;
// .. but first check if it's a the duress pin
if(is_duress_pin(digest, (args->pin_len == 0), &pin_kn)) {
// they gave the duress PIN for this wallet... try to continue w/o any indication
is_duress = true;
@ -649,9 +717,14 @@ pin_login_attempt(pinAttempt_t *args)
secret_kn = KEYNUM_duress_secret;
// for next run, we need to pretend like no failures (a little -- imperfect)
backup_data_set(IDX_DURESS_USED, 1);
rv = updates_for_duress_login(digest);
if(rv) return EPIN_AE_FAIL;
} else {
// Assume it's the real PIN, and register as an attempt on that.
// It is not the "duress pin", so assume it's the real PIN, and register
// as an attempt on that.
rv = ae_mixin_key(KEYNUM_pin_attempt, mid_digest, digest);
if(rv) return EPIN_AE_FAIL;
if(!is_main_pin(digest, &pin_kn)) {
// PIN code is just wrong.
@ -662,22 +735,23 @@ pin_login_attempt(pinAttempt_t *args)
secret_kn = KEYNUM_secret;
// change the various counters, since this worked
rv = updates_for_good_login(args, digest);
rv = updates_for_good_login(digest);
if(rv) return EPIN_AE_FAIL;
}
// SUCCESS! "digest" holds a working value. Save it.
pin_cache_save(args, digest);
// update flag about duress and weakly hide in some chaff
args->private_state = (rng_sample() & ~1) | is_duress;
// ASIDE: even if the above was bypassed, the following code will
// fail when it tries to read/update the corresponding slots in the SE
// mark as success
args->state_flags = PA_SUCCESSFUL | PA_HAS_608A;
// these are constants, and user doesn't care because they got in... but consistency.
args->num_fails = 0;
args->attempts_left = MAX_TARGET_ATTEMPTS;
// I used to always read the secret, since it's so hard to get to this point,
// but now just indicating if zero or non-zero so that we don't contaminate the
// caller w/ sensitive data that they may not want yet.
@ -713,9 +787,9 @@ pin_login_attempt(pinAttempt_t *args)
}
// In mark1/2, was thinking of maybe storing duress flag into private state,
// but no real need, but testing for it's expensive in mark3, so going to use
// but no real need, but testing for it is expensive in mark3, so going to use
// LSB here for that.
args->private_state = rng_sample() & ~1;
args->private_state = (rng_sample() & ~1) | is_duress;
_sign_attempt(args);
@ -744,16 +818,15 @@ pin_change(pinAttempt_t *args)
}
// Look at change flags.
const uint32_t cf = args->change_flags;
// obsolete secondary support
// Obsolete secondary support, can't support.
ASSERT(!args->is_secondary);
if(cf & CHANGE_SECONDARY_WALLET_PIN) {
return EPIN_BAD_REQUEST;
}
// must be here to do something.
// Must be here to do something.
if(cf == 0) return EPIN_RANGE_ERR;
if(cf & CHANGE_BRICKME_PIN) {
@ -768,17 +841,18 @@ pin_change(pinAttempt_t *args)
}
// ASIDE: Can always change a PIN you already know
// but can only prove you know the primary/secondary
// pin up to this point ... none of the others.
// but can only prove you know the primary pin up
// to this point (via login process)... none of the others.
// That's why we need old_pin fields.
// Restore cached version of PIN digest
uint8_t digest[32];
pin_cache_restore(args, digest);
// unlock the AE chip
if(warmup_ae()) return EPIN_I_AM_BRICK;
// what pin do they need to know to make their change?
int required_kn = -1;
// what slot (key number) are updating?
int target_slot = -1;
// If they authorized w/ the duress password, we let them
// change it (the duress one) while they think they are changing
// the main one. Always pretend like the duress wallet is already enabled.
@ -786,14 +860,6 @@ pin_change(pinAttempt_t *args)
// Same for brickme PIN.
// SO ... we need to know if they started w/ a duress wallet.
// what pin got us here? (ie. in 'digest' already)
int pin_kn = -1;
// what pin do they need to know to make their change?
int required_kn = -1;
// what slot (key number) are updating?
int target_slot = -1;
bool is_duress = (args->private_state & 0x1);
if(is_duress) {
@ -813,11 +879,12 @@ pin_change(pinAttempt_t *args)
return EPIN_OLD_AUTH_FAIL;
}
pin_kn = required_kn = target_slot = KEYNUM_duress_pin;
required_kn = target_slot = KEYNUM_duress_pin;
} else {
// No need to re-prove PIN knowledge.
// If they tricked us, doesn't matter as below the SE validates it all again.
pin_kn = required_kn = KEYNUM_main_pin;
// No real need to re-prove PIN knowledge.
// If they tricked us to get to this point, doesn't matter as
// below the SE validates it all again.
required_kn = KEYNUM_main_pin;
if(cf & CHANGE_WALLET_PIN) {
target_slot = KEYNUM_main_pin;
@ -830,19 +897,25 @@ pin_change(pinAttempt_t *args)
required_kn = KEYNUM_duress_pin;
target_slot = KEYNUM_duress_secret;
} else if(cf & CHANGE_BRICKME_PIN) {
required_kn = KEYNUM_main_pin;
required_kn = KEYNUM_brickme; // but main_pin would be better: rate limited
target_slot = KEYNUM_brickme;
} else {
return EPIN_RANGE_ERR;
}
}
// Determine they known hash protecting the secret/pin to be changed.
// Determine they know hash protecting the secret/pin to be changed.
uint8_t required_digest[32];
if(required_kn != pin_kn) {
if( (!is_duress && required_kn == KEYNUM_main_pin)
|| (is_duress && required_kn == KEYNUM_duress_pin)
) {
// Restore cached version of PIN digest: faster
pin_cache_restore(args, required_digest);
} else {
// Construct hash of pin needed.
pin_hash_attempt(required_kn, args->old_pin, args->old_pin_len, required_digest);
// Check the old pin is right.
// Check the old pin provided, is right.
ae_pair_unlock();
if(ae_checkmac(required_kn, required_digest)) {
// they got old PIN wrong, we won't be able to help them
@ -850,20 +923,17 @@ pin_change(pinAttempt_t *args)
// NOTE: altho we are changing flow based on result of ae_checkmac() here,
// if the response is faked by an active bus attacker, it doesn't matter
// because the change to the keyslot below will fail due to wrong PIN.
// because the change to the dataslot below will fail due to wrong PIN.
return EPIN_OLD_AUTH_FAIL;
}
} else {
memcpy(required_digest, digest, 32);
}
// Record new PIN value.
// Calculate new PIN hashed value: will be slow for main pin.
if(cf & (CHANGE_WALLET_PIN | CHANGE_DURESS_PIN | CHANGE_BRICKME_PIN)) {
// First calculate new PIN hased value.
uint8_t new_digest[32];
rv = pin_hash_attempt(target_slot, args->new_pin, args->new_pin_len, new_digest);
rv = pin_hash_attempt(required_kn, args->new_pin, args->new_pin_len, new_digest);
if(rv) goto ae_fail;
if(ae_encrypted_write(target_slot, required_kn, required_digest, new_digest, 32)) {
@ -873,15 +943,21 @@ pin_change(pinAttempt_t *args)
if(target_slot == required_kn) {
memcpy(required_digest, new_digest, 32);
}
if(target_slot == KEYNUM_main_pin) {
// main pin is changing; reset counter to zero (good login) and our cache
pin_cache_save(args, new_digest);
updates_for_good_login(args, new_digest);
updates_for_good_login(new_digest);
}
if(is_duress && (target_slot == KEYNUM_duress_pin)) {
// duress pin changed, and we're the duress thug, so update cache
pin_cache_save(args, new_digest);
}
}
// Record new secret.
// Note the digest might have just changed above.
// Note the required_digest might have just changed above.
if(cf & (CHANGE_SECRET | CHANGE_DURESS_SECRET)) {
int secret_kn = (required_kn == KEYNUM_main_pin) ? KEYNUM_secret : KEYNUM_duress_secret;

View File

@ -1267,7 +1267,8 @@ uint32_t HAL_RCCEx_GetPeriphCLKFreq(uint32_t PeriphClk)
#if defined(RCC_HSI48_SUPPORT)
else if((srcclk == 0U) && (HAL_IS_BIT_SET(RCC->CRRCR, RCC_CRRCR_HSI48RDY))) /* HSI48 ? */
{
frequency = HSI48_VALUE;
//PDG//frequency = HSI48_VALUE;
frequency = 0;
}
else /* No clock source */
{

View File

@ -273,6 +273,8 @@ pick_pairing_secret(void)
oled_show_raw(sizeof(tmp), (void *)tmp);
}
oled_factory_busy();
// .. but don't use those numbers, because those are semi-public now.
uint32_t secret[8];
for(int i=0; i<8; i++) {
@ -306,13 +308,15 @@ pick_pairing_secret(void)
}
// Also at this point, pick RNG noise to use as our one-time-pad
// for encrypting the secrets held in the 608a
// for encrypting the secrets held in the 608a.
{
uint32_t dest = (uint32_t)&rom_secrets->otp_key;
const uint32_t blen = sizeof(rom_secrets->otp_key) + sizeof(rom_secrets->otp_key_long);
const uint32_t blen = sizeof(rom_secrets->otp_key)
+ sizeof(rom_secrets->otp_key_long)
+ sizeof(rom_secrets->hash_cache_secret);
STATIC_ASSERT(blen % 8 == 0);
STATIC_ASSERT(blen == (72+416));
STATIC_ASSERT(blen == (72+416+32));
flash_unlock();
for(int i=0; i<blen; i+=8, dest += 8) {
@ -517,6 +521,7 @@ flash_lockdown_hard(uint8_t rdp_level_code)
}
#if 0
// backup_data_get()
//
uint32_t
@ -544,6 +549,7 @@ backup_data_set(int idx, uint32_t new_value)
// doesn't seem to work tho? stays unlocked
RTC->WPR = 0xff;
}
#endif
// record_highwater_version()
//

View File

@ -22,8 +22,9 @@ typedef struct {
uint8_t pairing_secret_xor[32];
uint64_t ae_serial_number[2]; // 9 bytes active
uint8_t bag_number[32]; // 32 bytes max, zero padded string
uint8_t otp_key[72]; // pad for secret encryption (seed storage)
uint8_t otp_key[72]; // key for secret encryption (seed storage)
uint8_t otp_key_long[416]; // same, but for longer secret area
uint8_t hash_cache_secret[32]; // encryption for cached pin hash value
} rom_secrets_t;
// This area is defined in linker script as last page of boot loader flash.
@ -47,6 +48,7 @@ static inline bool flash_is_security_level2(void) {
return ((FLASH->OPTR & FLASH_OPTR_RDP_Msk) == 0xCC);
}
#if 0
// We store some values in the RTC "backup" registers
// - these are protected against accidental writes
// - not cleared by system reset, full power cycle required
@ -58,6 +60,7 @@ static inline bool flash_is_security_level2(void) {
uint32_t backup_data_get(int idx);
void backup_data_set(int idx, uint32_t new_value);
#endif
// generial purpose flash functions