-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathpins.c
845 lines (677 loc) · 24.3 KB
/
pins.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
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
// SPDX-FileCopyrightText: © 2020 Foundation Devices, Inc. <[email protected]>
// SPDX-License-Identifier: GPL-3.0-or-later
//
// 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.
*
* pins.c -- PIN codes and security issues
*
*/
#include <stdio.h>
#include <string.h>
#include "delay.h"
#include "pprng.h"
#include "se.h"
#include "secrets.h"
#include "sha256.h"
#include "utils.h"
#include "debug-utils.h"
#include "pins.h"
#include "se-config.h"
// Number of iterations for KDF
#define KDF_ITER_WORDS 2
#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)
// - solution: adjust both the target and counter (upwards)
#define MAX_TARGET_ATTEMPTS 21
// Pretty sure it doesn't matter, but adding some salt into our PIN->bytes[32] code
// based on the purpose of the PIN code.
//
#define PIN_PURPOSE_NORMAL 0x334d1858
#define PIN_PURPOSE_ANTI_PHISH_WORDS 0x2e6d6773
#define PIN_PURPOSE_SUPPLY_CHAIN_WORDS 0xb6c9f792
// Keep this around after the user logs in successfully
uint8_t g_cached_main_pin[32];
// Hash up a PIN for indicated purpose.
static void pin_hash(const char* pin, int pin_len, uint8_t result[32], uint32_t purpose);
// pin_is_blank()
//
// Is a specific PIN defined already? Not safe to expose this directly to callers!
//
static bool pin_is_blank(uint8_t keynum) {
uint8_t blank[32] = {0};
se_reset_chip();
se_pair_unlock();
// Passing this check with zeros, means PIN was blank.
// Failure here means nothing (except not blank).
int is_blank = (se_checkmac_hard(keynum, blank) == 0);
// CAUTION? We've unlocked something maybe, but it's blank, so...
se_reset_chip();
return is_blank;
}
// is_main_pin()
//
// Do the checkmac thing using a PIN, and if it works, great.
//
static bool is_main_pin(const uint8_t digest[32], int* pin_kn) {
int kn = KEYNUM_pin_hash;
se_reset_chip();
se_pair_unlock();
if (se_checkmac_hard(kn, digest) == 0) {
*pin_kn = kn;
return true;
}
return false;
}
// pin_hash()
//
// Hash up a string of digits in 32-byte goodness.
//
static void pin_hash(const char* pin, int pin_len, uint8_t result[32], uint32_t purpose) {
// Used for supply chain validation too...not sure if that is less than MAX_PIN_LEN yet.
// ASSERT(pin_len <= MAX_PIN_LEN);
if (pin_len == 0) {
// zero-length PIN is considered the "blank" one: all zero
memset(result, 0, 32);
return;
}
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, rom_secrets->pairing_secret, 32);
sha256_update(&ctx, (uint8_t*)&purpose, 4);
sha256_update(&ctx, (uint8_t*)pin, pin_len);
sha256_update(&ctx, rom_secrets->otp_key, 32);
sha256_final(&ctx, result);
// and a second-sha256 on that, just in case.
sha256_init(&ctx);
sha256_update(&ctx, result, 32);
sha256_final(&ctx, result);
}
// pin_hash_attempt()
//
// Go from PIN to heavily hashed 32-byte value, suitable for testing against device.
//
// - call with target_kn == 0 to return a mid-state that can be used for main pin
//
static int pin_hash_attempt(uint8_t target_kn, const char* pin, int pin_len, uint8_t result[32]) {
uint8_t tmp[32];
if (pin_len == 0) {
// 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);
// main pin needs mega hashing
int rv = se_stretch_iter(tmp, result, KDF_ITER_PIN);
if (rv) return EPIN_SE_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_pin_hash) {
se_mixin_key(KEYNUM_pin_attempt, tmp, result);
} else {
se_mixin_key(0, tmp, result);
}
return 0;
}
// pin_cache_get_key()
//
void pin_cache_get_key(uint8_t key[32]) {
// per-boot unique key.
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, rom_secrets->hash_cache_secret, 32);
sha256_final(&ctx, key);
}
// pin_cache_save()
//
static int pin_cache_save(pinAttempt_t* args, uint8_t* digest) {
// encrypt w/ rom secret + SRAM seed value
uint8_t value[32];
if (!check_all_zeros(digest, 32)) {
pin_cache_get_key(value);
xor_mixin(value, digest, 32);
} else {
memset(value, 0, 32);
}
if (args->magic_value != PA_MAGIC_V1) {
return EPIN_BAD_MAGIC;
}
memcpy(args->cached_main_pin, value, 32);
// Keep a copy around so we can do other auth'd actions later like set a user firmware pubkey
memcpy(g_cached_main_pin, value, 32);
return 0;
}
// pin_cache_restore()
//
int pin_cache_restore(pinAttempt_t* args, uint8_t digest[32]) {
// decrypt w/ rom secret + SRAM seed value
if (args->magic_value != PA_MAGIC_V1) {
return EPIN_BAD_MAGIC;
}
memcpy(digest, args->cached_main_pin, 32);
if (!check_all_zeros(digest, 32)) {
uint8_t key[32];
pin_cache_get_key(key);
xor_mixin(digest, key, 32);
}
return 0;
}
// anti_phishing_words()
//
// Do HMAC(words secret) and return digest
//
// CAUTIONS:
// - 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 anti_phishing_words(const char* pin_prefix, int prefix_len, uint32_t* result) {
uint8_t tmp[32];
uint8_t digest[32];
// hash it up, a little
pin_hash(pin_prefix, prefix_len, tmp, PIN_PURPOSE_ANTI_PHISH_WORDS);
// Using 608a, we can do key stretching to get good built-in delays
int rv = se_stretch_iter(tmp, digest, KDF_ITER_WORDS);
se_reset_chip();
if (rv) return -1;
// Return all 32 bytes - will be used as input to bip32.from_data()
// This is OK because MAX_PIN_LEN is 32, and the result buf is the same
// as the pin_prefix buf.
memcpy(result, digest, 32);
return 0;
}
// supply_chain_validation_words()
//
// Perform HMAC_SHA256 using SE and supply chain slot. This hashes the given
// data with the secret value in the SE and returns the result.
//
// Caller will use these 32 bytes to generate words.
//
int supply_chain_validation_words(const char* data, int data_len, uint32_t* result) {
se_pair_unlock();
int rc = se_hmac32(KEYNUM_supply_chain, (uint8_t*)data, (uint8_t*)result);
if (rc < 0) {
return -1;
}
return 0;
}
// _hmac_attempt()
//
// Maybe should be proper HMAC from fips std? Can be changed later.
//
static void _hmac_attempt(const pinAttempt_t* args, uint8_t result[32]) {
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx, rom_secrets->pairing_secret, 32);
sha256_update(&ctx, (uint8_t*)args, offsetof(pinAttempt_t, hmac));
sha256_final(&ctx, result);
// and a second-sha256 on that, just in case.
sha256_init(&ctx);
sha256_update(&ctx, result, 32);
sha256_final(&ctx, result);
}
// _validate_attempt()
//
static int _validate_attempt(pinAttempt_t* args, bool first_time) {
if (first_time) {
// no hmac needed for setup call
} else {
// if hmac is defined, better be right.
uint8_t actual[32];
_hmac_attempt(args, actual);
// printf("args->hmac=%02x %02x %02x %02x\n", args->hmac[0], args->hmac[1], args->hmac[2], args->hmac[3] );
// printf("actual =%02x %02x %02x %02x\n", actual[0], actual[1], actual[2], actual[3]);
if (!check_equal(actual, args->hmac, 32)) {
// hmac is wrong?
return EPIN_HMAC_FAIL;
}
}
// check fields.
if (args->magic_value == PA_MAGIC_V1) {
// ok
} else {
return EPIN_BAD_MAGIC;
}
// check fields
if (args->pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR;
if (args->old_pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR;
if (args->new_pin_len > MAX_PIN_LEN) return EPIN_RANGE_ERR;
if ((args->change_flags & CHANGE__MASK) != args->change_flags) return EPIN_RANGE_ERR;
return 0;
}
// _sign_attempt()
//
// Provide our "signature" validating struct contents as coming from us.
//
static void _sign_attempt(pinAttempt_t* args) {
_hmac_attempt(args, args->hmac);
}
// _read_slot_as_counter()
//
static int _read_slot_as_counter(uint8_t slot, uint32_t* dest) {
// Read (typically a) counter value held in a dataslot.
// Important that this be authenticated.
//
// - using first 32-bits only, others will be zero/ignored
// - but need to read whole thing for the digest check
uint32_t padded[32 / 4] = {0};
se_pair_unlock();
if (se_read_data_slot(slot, (uint8_t*)padded, 32)) return -1;
uint8_t tempkey[32];
se_pair_unlock();
if (se_gendig_slot(slot, (const uint8_t*)padded, tempkey)) return -1;
if (!se_is_correct_tempkey(tempkey)) {
return -1;
}
*dest = padded[0];
return 0;
}
// get_last_success()
//
// Read state about previous attempt(s) from AE. Calculate number of failures,
// and how many attempts are left. The need for verifing the values from AE is
// not really so strong with the 608a, since it's all enforced on that side, but
// we'll do it anyway.
//
static int __attribute__((noinline)) get_last_success(pinAttempt_t* args) {
const int slot = KEYNUM_lastgood;
se_pair_unlock();
// Read counter value of last-good login. Important that this be authenticated.
// - using first 32-bits only, others will be zero
uint32_t padded[32 / 4] = {0};
if (se_read_data_slot(slot, (uint8_t*)padded, 32)) return -1;
uint8_t tempkey[32];
se_pair_unlock();
if (se_gendig_slot(slot, (const uint8_t*)padded, tempkey)) return -2;
if (!se_is_correct_tempkey(tempkey)) {
return -3;
}
// Read two values from data slots
uint32_t lastgood = 0, match_count = 0, counter = 0;
if (_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -4;
if (_read_slot_as_counter(KEYNUM_match_count, &match_count)) return -5;
// Read the monotonically-increasing counter
if (se_get_counter(&counter, 0)) return -6;
if (lastgood > counter) {
// monkey business, but impossible, right?!
args->num_fails = 99;
} else {
args->num_fails = counter - lastgood;
}
// 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;
}
return 0;
}
// warmup_ae()
//
static int warmup_se(void) {
for (int retry = 0; retry < 5; retry++) {
if (se_probe() == true) {
// Success
break;
}
}
if (se_pair_unlock()) return -1;
// reset watchdog timer
se_keep_alive();
return 0;
}
// pin_setup_attempt()
//
// Get number of failed attempts on a PIN, since last success. Calculate
// required delay, and setup initial struct for later attempts.
//
int pin_setup_attempt(pinAttempt_t* args) {
if (sizeof(pinAttempt_t) != PIN_ATTEMPT_SIZE_V1) {
return EPIN_BAD_REQUEST;
}
int rv = _validate_attempt(args, true);
if (rv) return rv;
// wipe most of struct, keep only what we expect and want!
// - old firmware wrote zero to magic before this point, and so we set it here
char pin_copy[MAX_PIN_LEN];
int pin_len = args->pin_len;
memcpy(pin_copy, args->pin, pin_len);
memset(args, 0, PIN_ATTEMPT_SIZE_V1);
args->state_flags = 0;
args->magic_value = PA_MAGIC_V1;
args->pin_len = pin_len;
memcpy(args->pin, pin_copy, pin_len);
// unlock the AE chip
int result = warmup_se();
if (result) {
printf("pin_setup_attempt() ERROR: 1 result = %d\n", result);
return EPIN_I_AM_BRICK;
}
// Read counters, and calc number of PIN attempts left
result = get_last_success(args);
if (result) {
printf("pin_setup_attempt() ERROR: 2 result = %d\n", result);
se_reset_chip();
return EPIN_SE_FAIL;
}
// delays now handled by chip and our KDF process directly
args->delay_required = 0;
args->delay_achieved = 0;
// need to know if we are blank/unused device
result = pin_is_blank(KEYNUM_pin_hash);
if (result) {
printf("pin_setup_attempt() ERROR: 3 (BLANK PIN!): result = %d\n", result);
args->state_flags |= PA_SUCCESSFUL | PA_IS_BLANK;
// We need to save this 'zero' value because it's encrypted, and/or might be
// un-initialized memory.
uint8_t zeros[32] = {0};
result = pin_cache_save(args, zeros);
if (result) {
return result;
}
// need legit value in here
args->private_state = (rng_sample() & ~1) ^ rom_secrets->hash_cache_secret[0];
}
_sign_attempt(args);
return 0;
}
// updates_for_good_login()
//
static int 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 count;
int rv = se_get_counter(&count, 0);
if (rv) goto fail;
// The weird math here is because the match count slot in the SE ignores the least
// significant 5 bits, so the match count must be a multiple of 32. When a good
// login occurs, we need to update both the match count and the monotonic counter.
//
// For example, if the monotonic counter was 19 and the match count was 32, and the
// user just provided the correct PIN, you would normally just bump the match count
// to 33, but since that is not a multiple of 32, we have to bump it to 64. That
// would then give 64-19 = 45 login attempts remaining though, so further down,
// in se_add_counter(), we bump the monotonic counter in a loop until there are
// MAX_TARGET_ATTEMPTS left (match count - counter0 = MAX_TARGET_ATTEMPTS_LEFT).
uint32_t mc = (count + MAX_TARGET_ATTEMPTS + 32) & ~31;
int bump = (mc - MAX_TARGET_ATTEMPTS) - count;
// The SE won't let the counter go past the match count, so we have to update the
// match count first.
// Set the new "match count"
{
uint32_t tmp[32 / 4] = {mc, mc};
rv = se_encrypted_write(KEYNUM_match_count, KEYNUM_pin_hash, digest, (void*)tmp, 32);
if (rv) goto fail;
}
// Increment the counter a bunch to get to that-13
uint32_t new_count = 0;
rv = se_add_counter(&new_count, 0, bump);
if (rv) goto fail;
// Update the "last good" counter
{
uint32_t tmp[32 / 4] = {new_count, 0};
rv = se_encrypted_write(KEYNUM_lastgood, KEYNUM_pin_hash, digest, (void*)tmp, 32);
if (rv) goto fail;
}
// 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 attempt. Also, rate limiting not affected by anything here.
return 0;
fail:
se_reset_chip();
return EPIN_SE_FAIL;
}
// pin_login_attempt()
//
// Do the PIN check, and return a value. Or fail.
//
int pin_login_attempt(pinAttempt_t* args) {
int rv = _validate_attempt(args, false);
if (rv) {
printf("pin_login_attempt 1: rv=%d\n", rv);
return rv;
}
if (args->state_flags & PA_SUCCESSFUL) {
printf("pin_login_attempt 2\n");
// already worked, or is blank
return EPIN_WRONG_SUCCESS;
}
// unlock the AE chip
if (warmup_se()) {
printf("pin_login_attempt 3\n");
return EPIN_I_AM_BRICK;
}
int pin_kn = -1;
int secret_kn = -1;
// hash up the pin now, assuming we'll use it on main PIN
uint8_t mid_digest[32], digest[32];
rv = pin_hash_attempt(0, args->pin, args->pin_len, mid_digest);
if (rv) {
printf("pin_login_attempt 4: rv=%d\n", rv);
return EPIN_SE_FAIL;
}
// Do mixin
rv = se_mixin_key(0, mid_digest, digest);
if (rv) {
printf("pin_login_attempt 5: rv=%d\n", rv);
return EPIN_SE_FAIL;
}
// Register an attempt on the pin
rv = se_mixin_key(KEYNUM_pin_attempt, mid_digest, digest);
if (rv) {
printf("pin_login_attempt 6: rv=%d\n", rv);
return EPIN_SE_FAIL;
}
if (!is_main_pin(digest, &pin_kn)) {
// Update args with latest attempts remaining
get_last_success(args);
printf("BAD PIN: attempts_left: %lu num_fails: %lu\n", args->attempts_left, args->num_fails);
// PIN code is just wrong.
// - nothing to update, since the chip's done it already
return EPIN_AUTH_FAIL;
}
secret_kn = KEYNUM_seed;
// change the various counters, since this worked
rv = updates_for_good_login(digest);
if (rv) {
// Update args with latest attempts remaining
get_last_success(args);
printf("GOOD PIN: attempts_left: %lu num_fails: %lu\n", args->attempts_left, args->num_fails);
return EPIN_SE_FAIL;
}
// SUCCESS! "digest" holds a working value. Save it.
rv = pin_cache_save(args, digest);
if (rv) {
return rv;
}
// 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;
// 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.
{
uint8_t ts[SE_SECRET_LEN];
rv = se_encrypted_read(secret_kn, pin_kn, digest, ts, SE_SECRET_LEN);
if (rv) {
printf("pin_login_attempt 9: rv=%d\n", rv);
se_reset_chip();
return EPIN_SE_FAIL;
}
se_reset_chip();
if (check_all_zeros(ts, SE_SECRET_LEN)) {
args->state_flags |= PA_ZERO_SECRET;
}
}
lv_refresh();
_sign_attempt(args);
return 0;
}
// pin_change()
//
// Change the PIN and/or secrets (must also know the value, or it must be blank)
//
int pin_change(pinAttempt_t* args) {
// Validate args and signature
int rv = _validate_attempt(args, false);
if (rv) {
return rv;
}
if ((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) {
// must come here with a successful PIN login (so it's rate limited nicely)
return EPIN_WRONG_SUCCESS;
}
if (args->state_flags & PA_IS_BLANK) {
// if blank, must provide blank value
if (args->pin_len) return EPIN_RANGE_ERR;
}
// Look at change flags.
const uint32_t cf = args->change_flags;
// Must be here to do something.
if (cf == 0) {
return EPIN_RANGE_ERR;
}
// unlock the AE chip
if (warmup_se()) {
return EPIN_I_AM_BRICK;
}
// what pin do they need to know to make their change?
int required_kn = KEYNUM_pin_hash;
// what slot (key number) are updating?
int target_slot = -1;
// 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.
if (cf & CHANGE_WALLET_PIN) {
target_slot = KEYNUM_pin_hash;
} else if (cf & CHANGE_SECRET) {
target_slot = KEYNUM_seed;
} else {
return EPIN_RANGE_ERR;
}
// Determine they know hash protecting the secret/pin to be changed.
uint8_t required_digest[32]; // Construct hash of pin needed.
// Use pin cache when changing the secret, otherwise it's a pin change and we rehash
if (cf & CHANGE_SECRET) {
// Restore cached version of PIN digest: faster
pin_cache_restore(args, required_digest);
} else {
pin_hash_attempt(required_kn, args->old_pin, args->old_pin_len, required_digest);
// Check the old pin provided, is right.
se_pair_unlock();
if (se_checkmac(required_kn, required_digest)) {
// they got old PIN wrong, we won't be able to help them
se_reset_chip();
// 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 dataslot below will fail due to wrong PIN.
return EPIN_OLD_AUTH_FAIL;
}
}
// Calculate new PIN hashed value: will be slow for main pin.
if (cf & CHANGE_WALLET_PIN) {
uint8_t new_digest[32];
rv = pin_hash_attempt(required_kn, args->new_pin, args->new_pin_len, new_digest);
if (rv) {
goto se_fail;
}
// dump_buf(required_digest, 32);
if (se_encrypted_write(target_slot, required_kn, required_digest, new_digest, 32)) {
goto se_fail;
}
if (target_slot == required_kn) {
memcpy(required_digest, new_digest, 32);
}
if (target_slot == KEYNUM_pin_hash) {
// main pin is changing; reset counter to zero (good login) and our cache
rv = pin_cache_save(args, new_digest);
if (rv) {
return rv;
}
updates_for_good_login(new_digest);
}
}
// Record new secret.
// Note the required_digest might have just changed above.
if (cf & CHANGE_SECRET) {
int secret_kn = KEYNUM_seed;
bool is_all_zeros = check_all_zeros(args->secret, SE_SECRET_LEN);
// encrypt new secret, but only if not zeros!
uint8_t tmp[SE_SECRET_LEN] = {0};
if (!is_all_zeros) {
xor_mixin(tmp, rom_secrets->otp_key, SE_SECRET_LEN);
xor_mixin(tmp, args->secret, SE_SECRET_LEN);
}
// dump_buf(required_digest, 32);
if (se_encrypted_write(secret_kn, required_kn, required_digest, tmp, SE_SECRET_LEN)) {
goto se_fail;
}
// update the zero-secret flag to be correct.
if (cf & CHANGE_SECRET) {
if (is_all_zeros) {
args->state_flags |= PA_ZERO_SECRET;
} else {
args->state_flags &= ~PA_ZERO_SECRET;
}
}
}
se_reset_chip();
// need to pass back the (potentially) updated cache value and some flags.
_sign_attempt(args);
return 0;
se_fail:
se_reset_chip();
return EPIN_SE_FAIL;
}
// pin_fetch_secret()
//
// To encourage not keeping the secret in memory, a way to fetch it after already
// have proven you know the PIN.
//
int pin_fetch_secret(pinAttempt_t* args) {
// Validate args and signature
int rv = _validate_attempt(args, false);
if (rv) return rv;
if ((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) {
// must come here with a successful PIN login (so it's rate limited nicely)
return EPIN_WRONG_SUCCESS;
}
// fetch the already-hashed pin
// - no real need to re-prove PIN knowledge.
// - if they tricked us, doesn't matter as below the SE validates it all again
uint8_t digest[32];
rv = pin_cache_restore(args, digest);
if (rv) {
return rv;
}
int pin_kn = KEYNUM_pin_hash;
int secret_slot = KEYNUM_seed;
// read out the secret that corresponds to the pin
rv = se_encrypted_read(secret_slot, pin_kn, digest, args->secret, SE_SECRET_LEN);
bool is_all_zeros = check_all_zeros(args->secret, SE_SECRET_LEN);
// decrypt the secret, but only if not zeros!
if (!is_all_zeros) xor_mixin(args->secret, rom_secrets->otp_key, SE_SECRET_LEN);
se_reset_chip();
if (rv) return EPIN_SE_FAIL;
return 0;
}
// EOF