From 7fa8ae136fc45ddfae94ff54be8fd763dc6b159a Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 24 Feb 2018 17:26:57 +0100 Subject: [PATCH] firmware: implement behaviour of state (still missing in PassphraseAck) --- firmware/fsm.c | 11 ++++++-- firmware/protect.c | 1 + firmware/protob/messages.options | 6 ++-- firmware/storage.c | 47 ++++++++++++++++++++++++++++++++ firmware/storage.h | 2 ++ 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index f05d80fba6..c4b633a226 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -232,10 +232,15 @@ static bool fsm_layoutAddress(const char *address, const char *desc, bool ignore void fsm_msgInitialize(Initialize *msg) { - (void)msg; recovery_abort(); signing_abort(); - session_clear(false); // do not clear PIN + if (msg->has_state && msg->state.size == 64) { + if (!session_compareState(msg->state.bytes)) { + session_clear(false); // do not clear PIN + } + } else { + session_clear(false); // do not clear PIN + } layoutHome(); fsm_msgGetFeatures(0); } @@ -301,6 +306,8 @@ void fsm_msgGetFeatures(GetFeatures *msg) resp->has_needs_backup = true; resp->needs_backup = storage_needsBackup(); resp->has_flags = true; resp->flags = storage_getFlags(); resp->has_model = true; strlcpy(resp->model, "1", sizeof(resp->model)); + resp->has_state = true; resp->state.size = 64; session_getState(NULL, resp->state.bytes); + msg_write(MessageType_MessageType_Features, resp); } diff --git a/firmware/protect.c b/firmware/protect.c index c81509f23f..3b586e5b1d 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -253,6 +253,7 @@ bool protectPassphrase(void) bool result; for (;;) { usbPoll(); + // TODO: correctly process PassphraseAck with state field set (mismatch => Failure) if (msg_tiny_id == MessageType_MessageType_PassphraseAck) { msg_tiny_id = 0xFFFF; PassphraseAck *ppa = (PassphraseAck *)msg_tiny; diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options index 90329e7d50..88e96cbdec 100644 --- a/firmware/protob/messages.options +++ b/firmware/protob/messages.options @@ -1,4 +1,4 @@ -Initialize.state max_size:32 +Initialize.state max_size:64 Features.vendor max_size:33 Features.device_id max_size:25 @@ -10,7 +10,7 @@ Features.bootloader_hash max_size:32 Features.model max_size:17 Features.fw_vendor max_size:256 Features.fw_vendor_keys max_size:32 -Features.state max_size:32 +Features.state max_size:64 ApplySettings.language max_size:17 ApplySettings.label max_size:33 @@ -27,7 +27,7 @@ ButtonRequest.data max_size:256 PinMatrixAck.pin max_size:10 PassphraseAck.passphrase max_size:51 -PassphraseAck.state max_size:32 +PassphraseAck.state max_size:64 Entropy.entropy max_size:1024 diff --git a/firmware/storage.c b/firmware/storage.c index 926aed6bfb..ebdb01cf27 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -28,6 +28,7 @@ #include "sha2.h" #include "aes/aes.h" #include "pbkdf2.h" +#include "hmac.h" #include "bip32.h" #include "bip39.h" #include "curves.h" @@ -124,6 +125,9 @@ static bool sessionPinCached; static bool sessionPassphraseCached; static char CONFIDENTIAL sessionPassphrase[51]; +static bool sessionStateSaltSet; +static uint8_t sessionStateSalt[32]; + #define STORAGE_VERSION 9 void storage_show_error(void) @@ -266,6 +270,8 @@ void session_clear(bool clear_pin) memzero(&sessionSeed, sizeof(sessionSeed)); sessionPassphraseCached = false; memzero(&sessionPassphrase, sizeof(sessionPassphrase)); + sessionStateSaltSet = false; + memzero(&sessionStateSalt, sizeof(sessionStateSalt)); if (clear_pin) { sessionPinCached = false; } @@ -709,6 +715,47 @@ bool session_isPassphraseCached(void) return sessionPassphraseCached; } +void session_getState(const uint8_t *salt, uint8_t *state) +{ + if (!salt) { + if (!sessionStateSaltSet) { + // generate a random salt if not provided and not already cached + random_buffer(sessionStateSalt, 32); + } + } else { + // otherwise copy provided salt to cached salt + memcpy(sessionStateSalt, salt, 32); + } + + sessionStateSaltSet = true; + + // state[0:32] = salt + memcpy(state, sessionStateSalt, 32); + + // state[32:64] = HMAC(passphrase, salt || device_id) + HMAC_SHA256_CTX ctx; + if (sessionPassphraseCached) { + hmac_sha256_Init(&ctx, (const uint8_t *)sessionPassphrase, strlen(sessionPassphrase)); + } else { + hmac_sha256_Init(&ctx, (const uint8_t *)"", 0); + } + hmac_sha256_Update(&ctx, sessionStateSalt, 32); + hmac_sha256_Update(&ctx, (const uint8_t *)storage_uuid, sizeof(storage_uuid)); + hmac_sha256_Final(&ctx, state + 32); + + memzero(&ctx, sizeof(ctx)); +} + +bool session_compareState(const uint8_t *state) +{ + if (!state) { + return false; + } + uint8_t istate[64]; + session_getState(state, istate); + return 0 == memcmp(state, istate, 64); +} + void session_cachePin(void) { sessionPinCached = true; diff --git a/firmware/storage.h b/firmware/storage.h index a8a883299b..2c15e7eb06 100644 --- a/firmware/storage.h +++ b/firmware/storage.h @@ -103,6 +103,8 @@ void storage_setHomescreen(const uint8_t *data, uint32_t size); void session_cachePassphrase(const char *passphrase); bool session_isPassphraseCached(void); +void session_getState(const uint8_t *salt, uint8_t *state); +bool session_compareState(const uint8_t *state); void storage_setMnemonic(const char *mnemonic); bool storage_containsMnemonic(const char *mnemonic);