// Copyright 2019 SoloKeys Developers // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. #include "data_migration.h" #include "log.h" #include "device.h" #include "crypto.h" // TODO move from macro to function/assert for better readability? #define check(x) assert(state_prev_0xff->x == state_tmp_ptr->x); #define check_buf(x) assert(memcmp(state_prev_0xff->x, state_tmp_ptr->x, sizeof(state_tmp_ptr->x)) == 0); bool migrate_from_FF_to_01(AuthenticatorState_0xFF* state_prev_0xff, AuthenticatorState_0x01* state_tmp_ptr){ // Calculate PIN hash, and replace PIN raw storage with it; add version to structure // other ingredients do not change if (state_tmp_ptr->data_version != 0xFF) return false; static_assert(sizeof(AuthenticatorState_0xFF) <= sizeof(AuthenticatorState_0x01), "New state structure is smaller, than current one, which is not handled"); if (ctap_generate_rng(state_tmp_ptr->PIN_SALT, sizeof(state_tmp_ptr->PIN_SALT)) != 1) { printf2(TAG_ERR, "Error, rng failed\n"); return false; } if (state_prev_0xff->is_pin_set){ crypto_sha256_init(); crypto_sha256_update(state_prev_0xff->pin_code, state_prev_0xff->pin_code_length); uint8_t intermediateHash[32]; crypto_sha256_final(intermediateHash); crypto_sha256_init(); crypto_sha256_update(intermediateHash, 16); memset(intermediateHash, 0, sizeof(intermediateHash)); crypto_sha256_update(state_tmp_ptr->PIN_SALT, sizeof(state_tmp_ptr->PIN_SALT)); crypto_sha256_final(state_tmp_ptr->PIN_CODE_HASH); } assert(state_tmp_ptr->_reserved == state_prev_0xff->pin_code_length); state_tmp_ptr->_reserved = 0xFF; state_tmp_ptr->data_version = 1; check(is_initialized); check(is_pin_set); check(remaining_tries); check(rk_stored); check_buf(key_lens); check_buf(key_space); assert(state_tmp_ptr->data_version != 0xFF); return true; } void save_migrated_state(AuthenticatorState *state_tmp_ptr) { memmove(&STATE, state_tmp_ptr, sizeof(AuthenticatorState)); authenticator_write_state(state_tmp_ptr, 0); authenticator_write_state(state_tmp_ptr, 1); } void do_migration_if_required(AuthenticatorState* state_current){ // Currently handles only state structures with the same size, or bigger // FIXME rework to raw buffers with fixed size to allow state structure size decrease if(!state_current->is_initialized) return; AuthenticatorState state_tmp; AuthenticatorState state_previous; authenticator_read_state(&state_previous); authenticator_read_state(&state_tmp); if(state_current->data_version == 0xFF){ printf2(TAG_ERR, "Running migration\n"); bool success = migrate_from_FF_to_01((AuthenticatorState_0xFF *) &state_previous, &state_tmp); if (!success){ printf2(TAG_ERR, "Failed migration from 0xFF to 1\n"); // FIXME discuss migration failure behavior goto return_cleanup; } dump_hex1(TAG_ERR, (void*)&state_tmp, sizeof(state_tmp)); dump_hex1(TAG_ERR, (void*)&state_previous, sizeof(state_previous)); save_migrated_state(&state_tmp); } assert(state_current->data_version == STATE_VERSION); return_cleanup: memset(&state_tmp, 0, sizeof(AuthenticatorState)); memset(&state_previous, 0, sizeof(AuthenticatorState)); }