// SPDX-FileCopyrightText: 2020 Foundation Devices, Inc. // SPDX-License-Identifier: GPL-3.0-or-later // // SPDX-FileCopyrightText: 2018 Coinkite, Inc. // SPDX-License-Identifier: GPL-3.0-only // /* * (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard * and is covered by GPLv3 license found in COPYING. * * pins.c -- PIN codes and security issues * */ #include #include #include "utils.h" #include "delay.h" #include "pprng.h" #include "se.h" #include "secrets.h" #include "sha256.h" #include "pins.h" #include "se-config.h" #include "debug-utils.h" // Number of iterations for KDF #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) // - solution: adjust both the target and counter (upwards) #define MAX_TARGET_ATTEMPTS 13 // 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 // 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_main_pin; 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_main_pin) { 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 void 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); } #ifdef FIXME ASSERT(args->magic_value == PA_MAGIC_V1); #endif /* FIXME */ memcpy(args->cached_main_pin, value, 32); } // pin_cache_restore() // static void pin_cache_restore(pinAttempt_t *args, uint8_t digest[32]) { // decrypt w/ rom secret + SRAM seed value #ifdef FIXME ASSERT(args->magic_value == PA_MAGIC_V1); #endif /* FIXME */ 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); } } // anti_phishing_words() // // Look up some bits... do HMAC(words secret) and return some LSB's // // 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 se_setup(); 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() // // TODO: Validate message is signed by us using pub key stored in ROM secrets // // TODO: Hash given data with private key from the SE // int supply_chain_validation_words(const char *data, int data_len, uint32_t *result) { SHA256_CTX ctx; sha256_init(&ctx); sha256_update(&ctx, (uint8_t*)data, data_len); // TODO: Change this to hash with a private key from the SE // sha256_update(&ctx, ???, 32)); sha256_final(&ctx, (uint8_t*)result); // Double SHA sha256_init(&ctx); sha256_update(&ctx, (uint8_t*)result, 32); sha256_final(&ctx, (uint8_t*)result); 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)) { fatal_mitm(); } *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)) { fatal_mitm(); } // Read two values from data slots uint32_t lastgood=0, match_count=0, counter=0; if (_read_slot_as_counter(KEYNUM_lastgood, &lastgood)) return -3; if (_read_slot_as_counter(KEYNUM_match_count, &match_count)) return -4; // Read the monotonically-increasing counter if (se_get_counter(&counter, 0)) return -5; 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) { // se_setup(); for (int retry=0; retry<5; retry++) { if (se_probe() == true) { // Success break; } printf("retrying...\n"); } 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) { #ifdef FIXME STATIC_ASSERT(sizeof(pinAttempt_t) == PIN_ATTEMPT_SIZE_V1); #endif /* FIXME */ 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); // TODO: WTF does "warming up" the SE mean? // 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 lef // TODO: For the case where we are setting the initial PIN, this is expected // to fail, isn't it? We won't have "last success", etc. 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_main_pin); 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}; pin_cache_save(args, zeros); // 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; // Challenge: Have to update both the counter, and the target match value because // no other way to have exact value. uint32_t mc = (count + MAX_TARGET_ATTEMPTS + 32) & ~31; #ifdef FIXME ASSERT(mc >= count); #endif /* FIXME */ int bump = (mc - MAX_TARGET_ATTEMPTS) - count; #ifdef FIXME ASSERT(bump >= 1); ASSERT(bump <= 32); // assuming MAX_TARGET_ATTEMPTS < 30 #endif /* FIXME */ // 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 = se_encrypted_write(KEYNUM_match_count, KEYNUM_main_pin, digest, (void *)tmp, 32); if (rv) goto fail; } // incr 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; #ifdef FIXME ASSERT(new_count == count + bump); ASSERT(mc > new_count); #endif /* FIXME */ // Update the "last good" counter { uint32_t tmp[32/4] = {new_count, 0 }; rv = se_encrypted_write(KEYNUM_lastgood, KEYNUM_main_pin, 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; } // OBSOLETE: did they wait long enough? // if(args->delay_achieved < args->delay_required) return EPIN_MUST_WAIT; 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)) { printf("pin_login_attempt 7\n"); // PIN code is just wrong. // - nothing to update, since the chip's done it already return EPIN_AUTH_FAIL; } secret_kn = KEYNUM_secret; // change the various counters, since this worked rv = updates_for_good_login(digest); if (rv) { printf("pin_login_attempt 8: rv=%d\n", rv); return EPIN_SE_FAIL; } // SUCCESS! "digest" holds a working value. Save it. pin_cache_save(args, digest); // 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; } } _sign_attempt(args); printf("pin_login_attempt SUCCESS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); 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) { printf("pin_change() 1\n"); // Validate args and signature int rv = _validate_attempt(args, false); printf("pin_change() 2\n"); if (rv) { printf("pin_change() 3: rv=%d\n", rv); return rv; } printf("pin_change() 4\n"); if ((args->state_flags & PA_SUCCESSFUL) != PA_SUCCESSFUL) { printf("pin_change() 5\n"); // must come here with a successful PIN login (so it's rate limited nicely) return EPIN_WRONG_SUCCESS; } printf("pin_change() 6\n"); if (args->state_flags & PA_IS_BLANK) { printf("pin_change() 7\n"); // 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; printf("pin_change() 8: change_flags=%u\n", args->change_flags); // Must be here to do something. if (cf == 0) { printf("pin_change() 9\n"); return EPIN_RANGE_ERR; } printf("pin_change() 10\n"); // unlock the AE chip if (warmup_se()) { printf("pin_change() 11\n"); return EPIN_I_AM_BRICK; } printf("pin_change() 12\n"); // what pin do they need to know to make their change? int required_kn = KEYNUM_main_pin; // 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) { printf("pin_change() 13\n"); target_slot = KEYNUM_main_pin; } else if (cf & CHANGE_SECRET) { printf("pin_change() 14\n"); target_slot = KEYNUM_secret; } else { printf("pin_change() 15\n"); return EPIN_RANGE_ERR; } printf("pin_change() 16\n"); // Determine they know hash protecting the secret/pin to be changed. uint8_t required_digest[32]; // Construct hash of pin needed. pin_hash_attempt(required_kn, args->old_pin, args->old_pin_len, required_digest); printf("pin_change() 17\n"); // 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. printf("pin_change() 18\n"); return EPIN_OLD_AUTH_FAIL; } printf("pin_change() 19\n"); // Calculate new PIN hashed value: will be slow for main pin. if (cf & CHANGE_WALLET_PIN) { printf("pin_change() 20\n"); uint8_t new_digest[32]; rv = pin_hash_attempt(required_kn, args->new_pin, args->new_pin_len, new_digest); printf("pin_change() 21\n"); if (rv) { printf("pin_change() 22\n"); goto se_fail; } printf("pin_change() 23\n"); dump_buf(required_digest, 32); if (se_encrypted_write(target_slot, required_kn, required_digest, new_digest, 32)) { printf("pin_change() 24\n"); goto se_fail; } printf("pin_change() 25\n"); if (target_slot == required_kn) { printf("pin_change() 26\n"); memcpy(required_digest, new_digest, 32); printf("pin_change() 27\n"); } printf("pin_change() 28\n"); if (target_slot == KEYNUM_main_pin) { printf("pin_change() 29\n"); // main pin is changing; reset counter to zero (good login) and our cache pin_cache_save(args, new_digest); printf("pin_change() 30\n"); updates_for_good_login(new_digest); printf("pin_change() 31\n"); } } // Record new secret. // Note the required_digest might have just changed above. if (cf & CHANGE_SECRET) { printf("pin_change() 32\n"); int secret_kn = KEYNUM_secret; printf("pin_change() 33\n"); bool is_all_zeros = check_all_zeros(args->secret, SE_SECRET_LEN); printf("pin_change() 34\n"); // encrypt new secret, but only if not zeros! uint8_t tmp[SE_SECRET_LEN] = {0}; if (!is_all_zeros) { printf("pin_change() 35\n"); xor_mixin(tmp, rom_secrets->otp_key, SE_SECRET_LEN); xor_mixin(tmp, args->secret, SE_SECRET_LEN); } printf("pin_change() 36\n"); dump_buf(required_digest, 32); if (se_encrypted_write(secret_kn, required_kn, required_digest, tmp, SE_SECRET_LEN)){ printf("pin_change() 37\n"); goto se_fail; } // update the zero-secret flag to be correct. if (cf & CHANGE_SECRET) { printf("pin_change() 38\n"); if (is_all_zeros) { printf("pin_change() 39\n"); args->state_flags |= PA_ZERO_SECRET; } else { printf("pin_change() 40\n"); args->state_flags &= ~PA_ZERO_SECRET; } } printf("pin_change() 41\n"); } se_reset_chip(); printf("pin_change() 42\n"); // need to pass back the (potentially) updated cache value and some flags. _sign_attempt(args); printf("pin_change() 43\n"); return 0; se_fail: printf("pin_change() 44\n"); se_reset_chip(); printf("pin_change() 45\n"); 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]; pin_cache_restore(args, digest); int pin_kn = KEYNUM_main_pin; int secret_slot = KEYNUM_secret; // 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; } // pin_long_secret() // // Read or write the "long" secret: an additional 416 bytes on 608a only. // int pin_long_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]; pin_cache_restore(args, digest); // which 32-byte section? #ifdef FIXME STATIC_ASSERT(CHANGE_LS_OFFSET == 0xf00); #endif /* FIXME */ int blk = (args->change_flags >> 8) & 0xf; if (blk > 13) return EPIN_RANGE_ERR; // read/write exactly 32 bytes if (!(args->change_flags & CHANGE_SECRET)) { rv = se_encrypted_read32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, args->secret); if(rv) goto fail; if(!check_all_zeros(args->secret, 32)) { xor_mixin(args->secret, rom_secrets->otp_key_long+(32*blk), 32); } } else { // write case uint8_t tmp[32] = {0}; if (!check_all_zeros(args->secret, 32)) { xor_mixin(tmp, args->secret, 32); xor_mixin(tmp, rom_secrets->otp_key_long+(32*blk), 32); } rv = se_encrypted_write32(KEYNUM_long_secret, blk, KEYNUM_main_pin, digest, tmp); if (rv) goto fail; } fail: se_reset_chip(); if (rv) return EPIN_SE_FAIL; return 0; } // EOF