diff --git a/.clang-format b/.clang-format new file mode 100644 index 000000000..58d4b3b68 --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +--- +BasedOnStyle: Google diff --git a/bootloader/bootloader.c b/bootloader/bootloader.c index 6525ca283..e30cc9939 100644 --- a/bootloader/bootloader.c +++ b/bootloader/bootloader.c @@ -19,132 +19,135 @@ #include -#include -#include #include +#include +#include #include "bootloader.h" -#include "signatures.h" #include "buttons.h" +#include "layout.h" +#include "memory.h" +#include "oled.h" +#include "rng.h" #include "setup.h" +#include "signatures.h" #include "usb.h" -#include "oled.h" #include "util.h" -#include "signatures.h" -#include "layout.h" -#include "rng.h" -#include "memory.h" -void layoutFirmwareFingerprint(const uint8_t *hash) -{ - char str[4][17]; - for (int i = 0; i < 4; i++) { - data2hex(hash + i * 8, 8, str[i]); - } - layoutDialog(&bmp_icon_question, "Abort", "Continue", "Compare fingerprints", str[0], str[1], str[2], str[3], NULL, NULL); +void layoutFirmwareFingerprint(const uint8_t *hash) { + char str[4][17]; + for (int i = 0; i < 4; i++) { + data2hex(hash + i * 8, 8, str[i]); + } + layoutDialog(&bmp_icon_question, "Abort", "Continue", "Compare fingerprints", + str[0], str[1], str[2], str[3], NULL, NULL); } -bool get_button_response(void) -{ - do { - delay(100000); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); - return button.YesUp; +bool get_button_response(void) { + do { + delay(100000); + buttonUpdate(); + } while (!button.YesUp && !button.NoUp); + return button.YesUp; } -void show_halt(const char *line1, const char *line2) -{ - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, NULL, "Unplug your TREZOR,", "reinstall firmware.", NULL); - shutdown(); +void show_halt(const char *line1, const char *line2) { + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, NULL, + "Unplug your TREZOR,", "reinstall firmware.", NULL); + shutdown(); } -static void show_unofficial_warning(const uint8_t *hash) -{ - layoutDialog(&bmp_icon_warning, "Abort", "I'll take the risk", NULL, "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, NULL); +static void show_unofficial_warning(const uint8_t *hash) { + layoutDialog(&bmp_icon_warning, "Abort", "I'll take the risk", NULL, + "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, + NULL); - bool but = get_button_response(); - if (!but) { // no button was pressed -> halt - show_halt("Unofficial firmware", "aborted."); - } + bool but = get_button_response(); + if (!but) { // no button was pressed -> halt + show_halt("Unofficial firmware", "aborted."); + } - layoutFirmwareFingerprint(hash); + layoutFirmwareFingerprint(hash); - but = get_button_response(); - if (!but) { // no button was pressed -> halt - show_halt("Unofficial firmware", "aborted."); - } + but = get_button_response(); + if (!but) { // no button was pressed -> halt + show_halt("Unofficial firmware", "aborted."); + } - // everything is OK, user pressed 2x Continue -> continue program + // everything is OK, user pressed 2x Continue -> continue program } -static void __attribute__((noreturn)) load_app(int signed_firmware) -{ - // zero out SRAM - memset_reg(_ram_start, _ram_end, 0); +static void __attribute__((noreturn)) load_app(int signed_firmware) { + // zero out SRAM + memset_reg(_ram_start, _ram_end, 0); - jump_to_firmware((const vector_table_t *) FLASH_PTR(FLASH_APP_START), signed_firmware); + jump_to_firmware((const vector_table_t *)FLASH_PTR(FLASH_APP_START), + signed_firmware); } -static void bootloader_loop(void) -{ - oledClear(); - oledDrawBitmap(0, 0, &bmp_logo64); - if (firmware_present_new()) { - oledDrawStringCenter(90, 10, "TREZOR", FONT_STANDARD); - oledDrawStringCenter(90, 30, "Bootloader", FONT_STANDARD); - oledDrawStringCenter(90, 50, VERSTR(VERSION_MAJOR) "." VERSTR(VERSION_MINOR) "." VERSTR(VERSION_PATCH), FONT_STANDARD); - } else { - oledDrawStringCenter(90, 10, "Welcome!", FONT_STANDARD); - oledDrawStringCenter(90, 30, "Please visit", FONT_STANDARD); - oledDrawStringCenter(90, 50, "trezor.io/start", FONT_STANDARD); - } - oledRefresh(); - - usbLoop(); +static void bootloader_loop(void) { + oledClear(); + oledDrawBitmap(0, 0, &bmp_logo64); + if (firmware_present_new()) { + oledDrawStringCenter(90, 10, "TREZOR", FONT_STANDARD); + oledDrawStringCenter(90, 30, "Bootloader", FONT_STANDARD); + oledDrawStringCenter(90, 50, + VERSTR(VERSION_MAJOR) "." VERSTR( + VERSION_MINOR) "." VERSTR(VERSION_PATCH), + FONT_STANDARD); + } else { + oledDrawStringCenter(90, 10, "Welcome!", FONT_STANDARD); + oledDrawStringCenter(90, 30, "Please visit", FONT_STANDARD); + oledDrawStringCenter(90, 50, "trezor.io/start", FONT_STANDARD); + } + oledRefresh(); + + usbLoop(); } -int main(void) -{ +int main(void) { #ifndef APPVER - setup(); + setup(); #endif - __stack_chk_guard = random32(); // this supports compiler provided unpredictable stack protection checks + __stack_chk_guard = random32(); // this supports compiler provided + // unpredictable stack protection checks #ifndef APPVER - memory_protect(); - oledInit(); + memory_protect(); + oledInit(); #endif - mpu_config_bootloader(); + mpu_config_bootloader(); #ifndef APPVER - bool left_pressed = (buttonRead() & BTN_PIN_NO) == 0; - - if (firmware_present_new() && !left_pressed) { - - oledClear(); - oledDrawBitmap(40, 0, &bmp_logo64_empty); - oledRefresh(); - - const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); - - uint8_t fingerprint[32]; - int signed_firmware = signatures_new_ok(hdr, fingerprint); - if (SIG_OK != signed_firmware) { - show_unofficial_warning(fingerprint); - } - - if (SIG_OK != check_firmware_hashes(hdr)) { - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Broken firmware", "detected.", NULL, "Unplug your TREZOR,", "reinstall firmware.", NULL); - shutdown(); - } - - mpu_config_off(); - load_app(signed_firmware); - } + bool left_pressed = (buttonRead() & BTN_PIN_NO) == 0; + + if (firmware_present_new() && !left_pressed) { + oledClear(); + oledDrawBitmap(40, 0, &bmp_logo64_empty); + oledRefresh(); + + const image_header *hdr = + (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); + + uint8_t fingerprint[32]; + int signed_firmware = signatures_new_ok(hdr, fingerprint); + if (SIG_OK != signed_firmware) { + show_unofficial_warning(fingerprint); + } + + if (SIG_OK != check_firmware_hashes(hdr)) { + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Broken firmware", + "detected.", NULL, "Unplug your TREZOR,", + "reinstall firmware.", NULL); + shutdown(); + } + + mpu_config_off(); + load_app(signed_firmware); + } #endif - bootloader_loop(); + bootloader_loop(); - return 0; + return 0; } diff --git a/bootloader/bootloader.h b/bootloader/bootloader.h index 1217569b5..b967c7f0b 100644 --- a/bootloader/bootloader.h +++ b/bootloader/bootloader.h @@ -31,8 +31,8 @@ #define VERSION_MINOR_CHAR "\x08" #define VERSION_PATCH_CHAR "\x00" -#include #include +#include void show_halt(const char *line1, const char *line2); void layoutFirmwareFingerprint(const uint8_t *hash); diff --git a/bootloader/signatures.c b/bootloader/signatures.c index ec6ca3af6..7e9824757 100644 --- a/bootloader/signatures.c +++ b/bootloader/signatures.c @@ -19,16 +19,16 @@ #include -#include "signatures.h" -#include "ecdsa.h" -#include "secp256k1.h" -#include "sha2.h" #include "bootloader.h" +#include "ecdsa.h" #include "memory.h" #include "memzero.h" +#include "secp256k1.h" +#include "sha2.h" +#include "signatures.h" -const uint32_t FIRMWARE_MAGIC_OLD = 0x525a5254; // TRZR -const uint32_t FIRMWARE_MAGIC_NEW = 0x465a5254; // TRZF +const uint32_t FIRMWARE_MAGIC_OLD = 0x525a5254; // TRZR +const uint32_t FIRMWARE_MAGIC_NEW = 0x465a5254; // TRZF #define PUBKEYS 5 @@ -42,152 +42,162 @@ static const uint8_t * const pubkey[PUBKEYS] = { #define SIGNATURES 3 -#define FLASH_META_START 0x08008000 -#define FLASH_META_CODELEN (FLASH_META_START + 0x0004) -#define FLASH_META_SIGINDEX1 (FLASH_META_START + 0x0008) -#define FLASH_META_SIGINDEX2 (FLASH_META_START + 0x0009) -#define FLASH_META_SIGINDEX3 (FLASH_META_START + 0x000A) -#define FLASH_OLD_APP_START 0x08010000 -#define FLASH_META_SIG1 (FLASH_META_START + 0x0040) -#define FLASH_META_SIG2 (FLASH_META_START + 0x0080) -#define FLASH_META_SIG3 (FLASH_META_START + 0x00C0) - -bool firmware_present_old(void) -{ - if (memcmp(FLASH_PTR(FLASH_META_START), &FIRMWARE_MAGIC_OLD, 4)) { // magic does not match - return false; - } - if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) < 8192) { // firmware reports smaller size than 8192 - return false; - } - if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) > FLASH_APP_LEN) { // firmware reports bigger size than flash size - return false; - } - - return true; +#define FLASH_META_START 0x08008000 +#define FLASH_META_CODELEN (FLASH_META_START + 0x0004) +#define FLASH_META_SIGINDEX1 (FLASH_META_START + 0x0008) +#define FLASH_META_SIGINDEX2 (FLASH_META_START + 0x0009) +#define FLASH_META_SIGINDEX3 (FLASH_META_START + 0x000A) +#define FLASH_OLD_APP_START 0x08010000 +#define FLASH_META_SIG1 (FLASH_META_START + 0x0040) +#define FLASH_META_SIG2 (FLASH_META_START + 0x0080) +#define FLASH_META_SIG3 (FLASH_META_START + 0x00C0) + +bool firmware_present_old(void) { + if (memcmp(FLASH_PTR(FLASH_META_START), &FIRMWARE_MAGIC_OLD, + 4)) { // magic does not match + return false; + } + if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) < + 8192) { // firmware reports smaller size than 8192 + return false; + } + if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) > + FLASH_APP_LEN) { // firmware reports bigger size than flash size + return false; + } + + return true; } -int signatures_old_ok(void) -{ - const uint32_t codelen = *((const uint32_t *)FLASH_META_CODELEN); - const uint8_t sigindex1 = *((const uint8_t *)FLASH_META_SIGINDEX1); - const uint8_t sigindex2 = *((const uint8_t *)FLASH_META_SIGINDEX2); - const uint8_t sigindex3 = *((const uint8_t *)FLASH_META_SIGINDEX3); - - if (codelen > FLASH_APP_LEN) { - return false; - } - - uint8_t hash[32]; - sha256_Raw(FLASH_PTR(FLASH_OLD_APP_START), codelen, hash); - - if (sigindex1 < 1 || sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index - if (sigindex2 < 1 || sigindex2 > PUBKEYS) return SIG_FAIL; // invalid index - if (sigindex3 < 1 || sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index - - if (sigindex1 == sigindex2) return SIG_FAIL; // duplicate use - if (sigindex1 == sigindex3) return SIG_FAIL; // duplicate use - if (sigindex2 == sigindex3) return SIG_FAIL; // duplicate use - - if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1], (const uint8_t *)FLASH_META_SIG1, hash)) { // failure - return SIG_FAIL; - } - if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex2 - 1], (const uint8_t *)FLASH_META_SIG2, hash)) { // failure - return SIG_FAIL; - } - if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex3 - 1], (const uint8_t *)FLASH_META_SIG3, hash)) { // failture - return SIG_FAIL; - } - - return SIG_OK; +int signatures_old_ok(void) { + const uint32_t codelen = *((const uint32_t *)FLASH_META_CODELEN); + const uint8_t sigindex1 = *((const uint8_t *)FLASH_META_SIGINDEX1); + const uint8_t sigindex2 = *((const uint8_t *)FLASH_META_SIGINDEX2); + const uint8_t sigindex3 = *((const uint8_t *)FLASH_META_SIGINDEX3); + + if (codelen > FLASH_APP_LEN) { + return false; + } + + uint8_t hash[32]; + sha256_Raw(FLASH_PTR(FLASH_OLD_APP_START), codelen, hash); + + if (sigindex1 < 1 || sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index + if (sigindex2 < 1 || sigindex2 > PUBKEYS) return SIG_FAIL; // invalid index + if (sigindex3 < 1 || sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index + + if (sigindex1 == sigindex2) return SIG_FAIL; // duplicate use + if (sigindex1 == sigindex3) return SIG_FAIL; // duplicate use + if (sigindex2 == sigindex3) return SIG_FAIL; // duplicate use + + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1], + (const uint8_t *)FLASH_META_SIG1, + hash)) { // failure + return SIG_FAIL; + } + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex2 - 1], + (const uint8_t *)FLASH_META_SIG2, + hash)) { // failure + return SIG_FAIL; + } + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex3 - 1], + (const uint8_t *)FLASH_META_SIG3, + hash)) { // failture + return SIG_FAIL; + } + + return SIG_OK; } -void compute_firmware_fingerprint(const image_header *hdr, uint8_t hash[32]) -{ - image_header copy; - memcpy(©, hdr, sizeof(image_header)); - memzero(copy.sig1, sizeof(copy.sig1)); - memzero(copy.sig2, sizeof(copy.sig2)); - memzero(copy.sig3, sizeof(copy.sig3)); - copy.sigindex1 = 0; - copy.sigindex2 = 0; - copy.sigindex3 = 0; - sha256_Raw((const uint8_t *)©, sizeof(image_header), hash); +void compute_firmware_fingerprint(const image_header *hdr, uint8_t hash[32]) { + image_header copy; + memcpy(©, hdr, sizeof(image_header)); + memzero(copy.sig1, sizeof(copy.sig1)); + memzero(copy.sig2, sizeof(copy.sig2)); + memzero(copy.sig3, sizeof(copy.sig3)); + copy.sigindex1 = 0; + copy.sigindex2 = 0; + copy.sigindex3 = 0; + sha256_Raw((const uint8_t *)©, sizeof(image_header), hash); } -bool firmware_present_new(void) -{ - const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); - if (hdr->magic != FIRMWARE_MAGIC_NEW) return false; - // we need to ignore hdrlen for now - // because we keep reset_handler ptr there - // for compatibility with older bootloaders - // after this is no longer necessary, let's uncomment the line below: - // if (hdr->hdrlen != FLASH_FWHEADER_LEN) return false; - if (hdr->codelen > FLASH_APP_LEN) return false; - if (hdr->codelen < 4096) return false; - - return true; +bool firmware_present_new(void) { + const image_header *hdr = + (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); + if (hdr->magic != FIRMWARE_MAGIC_NEW) return false; + // we need to ignore hdrlen for now + // because we keep reset_handler ptr there + // for compatibility with older bootloaders + // after this is no longer necessary, let's uncomment the line below: + // if (hdr->hdrlen != FLASH_FWHEADER_LEN) return false; + if (hdr->codelen > FLASH_APP_LEN) return false; + if (hdr->codelen < 4096) return false; + + return true; } -int signatures_new_ok(const image_header *hdr, uint8_t store_fingerprint[32]) -{ - uint8_t hash[32]; - compute_firmware_fingerprint(hdr, hash); - - if (store_fingerprint) { - memcpy(store_fingerprint, hash, 32); - } - - if (hdr->sigindex1 < 1 || hdr->sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index - if (hdr->sigindex2 < 1 || hdr->sigindex2 > PUBKEYS) return SIG_FAIL; // invalid index - if (hdr->sigindex3 < 1 || hdr->sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index - - if (hdr->sigindex1 == hdr->sigindex2) return SIG_FAIL; // duplicate use - if (hdr->sigindex1 == hdr->sigindex3) return SIG_FAIL; // duplicate use - if (hdr->sigindex2 == hdr->sigindex3) return SIG_FAIL; // duplicate use - - if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex1 - 1], hdr->sig1, hash)) { // failure - return SIG_FAIL; - } - if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex2 - 1], hdr->sig2, hash)) { // failure - return SIG_FAIL; - } - if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex3 - 1], hdr->sig3, hash)) { // failure - return SIG_FAIL; - } - - return SIG_OK; +int signatures_new_ok(const image_header *hdr, uint8_t store_fingerprint[32]) { + uint8_t hash[32]; + compute_firmware_fingerprint(hdr, hash); + + if (store_fingerprint) { + memcpy(store_fingerprint, hash, 32); + } + + if (hdr->sigindex1 < 1 || hdr->sigindex1 > PUBKEYS) + return SIG_FAIL; // invalid index + if (hdr->sigindex2 < 1 || hdr->sigindex2 > PUBKEYS) + return SIG_FAIL; // invalid index + if (hdr->sigindex3 < 1 || hdr->sigindex3 > PUBKEYS) + return SIG_FAIL; // invalid index + + if (hdr->sigindex1 == hdr->sigindex2) return SIG_FAIL; // duplicate use + if (hdr->sigindex1 == hdr->sigindex3) return SIG_FAIL; // duplicate use + if (hdr->sigindex2 == hdr->sigindex3) return SIG_FAIL; // duplicate use + + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex1 - 1], + hdr->sig1, hash)) { // failure + return SIG_FAIL; + } + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex2 - 1], + hdr->sig2, hash)) { // failure + return SIG_FAIL; + } + if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex3 - 1], + hdr->sig3, hash)) { // failure + return SIG_FAIL; + } + + return SIG_OK; } -int mem_is_empty(const uint8_t *src, uint32_t len) -{ - for (uint32_t i = 0; i < len; i++) { - if (src[i]) return 0; - } - return 1; +int mem_is_empty(const uint8_t *src, uint32_t len) { + for (uint32_t i = 0; i < len; i++) { + if (src[i]) return 0; + } + return 1; } -int check_firmware_hashes(const image_header *hdr) -{ - uint8_t hash[32]; - // check hash of the first code chunk - sha256_Raw(FLASH_PTR(FLASH_APP_START), (64 - 1) * 1024, hash); - if (0 != memcmp(hash, hdr->hashes, 32)) return SIG_FAIL; - // check remaining used chunks - uint32_t total_len = FLASH_FWHEADER_LEN + hdr->codelen; - int used_chunks = total_len / FW_CHUNK_SIZE; - if (total_len % FW_CHUNK_SIZE > 0) { - used_chunks++; - } - for (int i = 1; i < used_chunks; i++) { - sha256_Raw(FLASH_PTR(FLASH_FWHEADER_START + (64 * i) * 1024), 64 * 1024, hash); - if (0 != memcmp(hdr->hashes + 32 * i, hash, 32)) return SIG_FAIL; - } - // check unused chunks - for (int i = used_chunks; i < 16; i++) { - if (!mem_is_empty(hdr->hashes + 32 * i, 32)) return SIG_FAIL; - } - // all OK - return SIG_OK; +int check_firmware_hashes(const image_header *hdr) { + uint8_t hash[32]; + // check hash of the first code chunk + sha256_Raw(FLASH_PTR(FLASH_APP_START), (64 - 1) * 1024, hash); + if (0 != memcmp(hash, hdr->hashes, 32)) return SIG_FAIL; + // check remaining used chunks + uint32_t total_len = FLASH_FWHEADER_LEN + hdr->codelen; + int used_chunks = total_len / FW_CHUNK_SIZE; + if (total_len % FW_CHUNK_SIZE > 0) { + used_chunks++; + } + for (int i = 1; i < used_chunks; i++) { + sha256_Raw(FLASH_PTR(FLASH_FWHEADER_START + (64 * i) * 1024), 64 * 1024, + hash); + if (0 != memcmp(hdr->hashes + 32 * i, hash, 32)) return SIG_FAIL; + } + // check unused chunks + for (int i = used_chunks; i < 16; i++) { + if (!mem_is_empty(hdr->hashes + 32 * i, 32)) return SIG_FAIL; + } + // all OK + return SIG_OK; } diff --git a/bootloader/signatures.h b/bootloader/signatures.h index 6d3c00f96..c47e325e4 100644 --- a/bootloader/signatures.h +++ b/bootloader/signatures.h @@ -23,11 +23,11 @@ #include #include -extern const uint32_t FIRMWARE_MAGIC_OLD; // TRZR -extern const uint32_t FIRMWARE_MAGIC_NEW; // TRZF +extern const uint32_t FIRMWARE_MAGIC_OLD; // TRZR +extern const uint32_t FIRMWARE_MAGIC_NEW; // TRZF -#define SIG_OK 0x5A3CA5C3 -#define SIG_FAIL 0x00000000 +#define SIG_OK 0x5A3CA5C3 +#define SIG_FAIL 0x00000000 bool firmware_present_old(void); int signatures_old_ok(void); @@ -38,23 +38,23 @@ int signatures_old_ok(void); // immediately following the chunk hashes typedef struct { - uint32_t magic; - uint32_t hdrlen; - uint32_t expiry; - uint32_t codelen; - uint32_t version; - uint32_t fix_version; - uint8_t __reserved1[8]; - uint8_t hashes[512]; - uint8_t sig1[64]; - uint8_t sig2[64]; - uint8_t sig3[64]; - uint8_t sigindex1; - uint8_t sigindex2; - uint8_t sigindex3; - uint8_t __reserved2[220]; - uint8_t __sigmask; - uint8_t __sig[64]; + uint32_t magic; + uint32_t hdrlen; + uint32_t expiry; + uint32_t codelen; + uint32_t version; + uint32_t fix_version; + uint8_t __reserved1[8]; + uint8_t hashes[512]; + uint8_t sig1[64]; + uint8_t sig2[64]; + uint8_t sig3[64]; + uint8_t sigindex1; + uint8_t sigindex2; + uint8_t sigindex3; + uint8_t __reserved2[220]; + uint8_t __sigmask; + uint8_t __sig[64]; } __attribute__((packed)) image_header; #define FW_CHUNK_SIZE 65536 diff --git a/bootloader/usb.c b/bootloader/usb.c index 84885e851..8af36b7c9 100644 --- a/bootloader/usb.c +++ b/bootloader/usb.c @@ -17,425 +17,451 @@ * along with this library. If not, see . */ -#include #include +#include #include -#include "buttons.h" #include "bootloader.h" +#include "buttons.h" +#include "ecdsa.h" +#include "layout.h" +#include "memory.h" +#include "memzero.h" #include "oled.h" #include "rng.h" +#include "secp256k1.h" +#include "sha2.h" +#include "signatures.h" #include "usb.h" -#include "layout.h" #include "util.h" -#include "signatures.h" -#include "sha2.h" -#include "ecdsa.h" -#include "secp256k1.h" -#include "memzero.h" -#include "memory.h" #include "usb21_standard.h" #include "webusb.h" #include "winusb.h" #include "usb_desc.h" -#include "usb_send.h" #include "usb_erase.h" +#include "usb_send.h" enum { - STATE_READY, - STATE_OPEN, - STATE_FLASHSTART, - STATE_FLASHING, - STATE_CHECK, - STATE_END, + STATE_READY, + STATE_OPEN, + STATE_FLASHSTART, + STATE_FLASHING, + STATE_CHECK, + STATE_END, }; static uint32_t flash_pos = 0, flash_len = 0; static uint32_t chunk_idx = 0; static char flash_state = STATE_READY; -static uint32_t FW_HEADER[FLASH_FWHEADER_LEN/sizeof(uint32_t)]; -static uint32_t FW_CHUNK[FW_CHUNK_SIZE/sizeof(uint32_t)]; - -static void check_and_write_chunk(void) -{ - uint32_t offset = (chunk_idx == 0) ? FLASH_FWHEADER_LEN : 0; - uint32_t chunk_pos = flash_pos % FW_CHUNK_SIZE; - if (chunk_pos == 0) { - chunk_pos = FW_CHUNK_SIZE; - } - uint8_t hash[32]; - SHA256_CTX ctx; - sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)FW_CHUNK + offset, chunk_pos - offset); - if (chunk_pos < 64 * 1024) { - // pad with FF - for (uint32_t i = chunk_pos; i < 64 * 1024; i += 4) { - sha256_Update(&ctx, (const uint8_t *)"\xFF\xFF\xFF\xFF", 4); - } - } - sha256_Final(&ctx, hash); - - const image_header *hdr = (const image_header *)FW_HEADER; - // invalid chunk sent - if (0 != memcmp(hash, hdr->hashes + chunk_idx * 32, 32)) { - // erase storage - erase_storage(); - flash_state = STATE_END; - show_halt("Error installing", "firmware."); - return; - } - - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - for (uint32_t i = offset/sizeof(uint32_t); i < chunk_pos/sizeof(uint32_t); i++) { - flash_program_word(FLASH_FWHEADER_START + chunk_idx * FW_CHUNK_SIZE + i * sizeof(uint32_t), FW_CHUNK[i]); - } - flash_wait_for_last_operation(); - flash_lock(); - - // all done - if (flash_len == flash_pos) { - // check remaining chunks if any - for (uint32_t i = chunk_idx + 1; i < 16; i++) { - // hash should be empty if the chunk is unused - if (!mem_is_empty(hdr->hashes + 32 * i, 32)) { - flash_state = STATE_END; - show_halt("Error installing", "firmware."); - return; - } - } - } - - memzero(FW_CHUNK, sizeof(FW_CHUNK)); - chunk_idx++; +static uint32_t FW_HEADER[FLASH_FWHEADER_LEN / sizeof(uint32_t)]; +static uint32_t FW_CHUNK[FW_CHUNK_SIZE / sizeof(uint32_t)]; + +static void check_and_write_chunk(void) { + uint32_t offset = (chunk_idx == 0) ? FLASH_FWHEADER_LEN : 0; + uint32_t chunk_pos = flash_pos % FW_CHUNK_SIZE; + if (chunk_pos == 0) { + chunk_pos = FW_CHUNK_SIZE; + } + uint8_t hash[32]; + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)FW_CHUNK + offset, chunk_pos - offset); + if (chunk_pos < 64 * 1024) { + // pad with FF + for (uint32_t i = chunk_pos; i < 64 * 1024; i += 4) { + sha256_Update(&ctx, (const uint8_t *)"\xFF\xFF\xFF\xFF", 4); + } + } + sha256_Final(&ctx, hash); + + const image_header *hdr = (const image_header *)FW_HEADER; + // invalid chunk sent + if (0 != memcmp(hash, hdr->hashes + chunk_idx * 32, 32)) { + // erase storage + erase_storage(); + flash_state = STATE_END; + show_halt("Error installing", "firmware."); + return; + } + + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (uint32_t i = offset / sizeof(uint32_t); i < chunk_pos / sizeof(uint32_t); + i++) { + flash_program_word( + FLASH_FWHEADER_START + chunk_idx * FW_CHUNK_SIZE + i * sizeof(uint32_t), + FW_CHUNK[i]); + } + flash_wait_for_last_operation(); + flash_lock(); + + // all done + if (flash_len == flash_pos) { + // check remaining chunks if any + for (uint32_t i = chunk_idx + 1; i < 16; i++) { + // hash should be empty if the chunk is unused + if (!mem_is_empty(hdr->hashes + 32 * i, 32)) { + flash_state = STATE_END; + show_halt("Error installing", "firmware."); + return; + } + } + } + + memzero(FW_CHUNK, sizeof(FW_CHUNK)); + chunk_idx++; } -static void rx_callback(usbd_device *dev, uint8_t ep) -{ - (void)ep; - static uint16_t msg_id = 0xFFFF; - static uint8_t buf[64] __attribute__((aligned(4))); - static uint32_t w; - static int wi; - static int old_was_signed; - - if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return; - - if (flash_state == STATE_END) { - return; - } - - if (flash_state == STATE_READY || flash_state == STATE_OPEN || flash_state == STATE_FLASHSTART || flash_state == STATE_CHECK) { - if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { // invalid start - discard - return; - } - // struct.unpack(">HL") => msg, size - msg_id = (buf[3] << 8) + buf[4]; - } - - if (flash_state == STATE_READY || flash_state == STATE_OPEN) { - if (msg_id == 0x0000) { // Initialize message (id 0) - send_msg_features(dev); - flash_state = STATE_OPEN; - return; - } - if (msg_id == 0x0037) { // GetFeatures message (id 55) - send_msg_features(dev); - return; - } - if (msg_id == 0x0001) { // Ping message (id 1) - send_msg_success(dev); - return; - } - if (msg_id == 0x0005) { // WipeDevice message (id 5) - layoutDialog(&bmp_icon_question, "Cancel", "Confirm", NULL, "Do you really want to", "wipe the device?", NULL, "All data will be lost.", NULL, NULL); - bool but = get_button_response(); - if (but) { - erase_storage_code_progress(); - flash_state = STATE_END; - layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "Device", "successfully wiped.", NULL, "You may now", "unplug your TREZOR.", NULL); - send_msg_success(dev); - } else { - flash_state = STATE_END; - layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Device wipe", "aborted.", NULL, "You may now", "unplug your TREZOR.", NULL); - send_msg_failure(dev); - } - return; - } - } - - if (flash_state == STATE_OPEN) { - if (msg_id == 0x0006) { // FirmwareErase message (id 6) - bool proceed = false; - if (firmware_present_new()) { - layoutDialog(&bmp_icon_question, "Abort", "Continue", NULL, "Install new", "firmware?", NULL, "Never do this without", "your recovery card!", NULL); - proceed = get_button_response(); - } else { - proceed = true; - } - if (proceed) { - // check whether the current firmware is signed (old or new method) - if (firmware_present_new()) { - const image_header *hdr = (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); - old_was_signed = signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr); - } else if (firmware_present_old()) { - old_was_signed = signatures_old_ok(); - } else { - old_was_signed = SIG_FAIL; - } - erase_code_progress(); - send_msg_success(dev); - flash_state = STATE_FLASHSTART; - } else { - send_msg_failure(dev); - flash_state = STATE_END; - layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You may now", "unplug your TREZOR.", NULL); - } - return; - } - return; - } - - if (flash_state == STATE_FLASHSTART) { - if (msg_id == 0x0007) { // FirmwareUpload message (id 7) - if (buf[9] != 0x0a) { // invalid contents - send_msg_failure(dev); - flash_state = STATE_END; - show_halt("Error installing", "firmware."); - return; - } - // read payload length - const uint8_t *p = buf + 10; - flash_len = readprotobufint(&p); - if (flash_len <= FLASH_FWHEADER_LEN) { // firmware is too small - send_msg_failure(dev); - flash_state = STATE_END; - show_halt("Firmware is too small.", NULL); - return; - } - if (flash_len > FLASH_FWHEADER_LEN + FLASH_APP_LEN) { // firmware is too big - send_msg_failure(dev); - flash_state = STATE_END; - show_halt("Firmware is too big.", NULL); - return; - } - // check firmware magic - if (memcmp(p, &FIRMWARE_MAGIC_NEW, 4) != 0) { - send_msg_failure(dev); - flash_state = STATE_END; - show_halt("Wrong firmware header.", NULL); - return; - } - memzero(FW_HEADER, sizeof(FW_HEADER)); - memzero(FW_CHUNK, sizeof(FW_CHUNK)); - flash_state = STATE_FLASHING; - flash_pos = 0; - chunk_idx = 0; - w = 0; - while (p < buf + 64) { - w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w - wi++; - if (wi == 4) { - FW_HEADER[flash_pos / 4] = w; - flash_pos += 4; - wi = 0; - } - p++; - } - return; - } - return; - } - - if (flash_state == STATE_FLASHING) { - if (buf[0] != '?') { // invalid contents - send_msg_failure(dev); - flash_state = STATE_END; - show_halt("Error installing", "firmware."); - return; - } - - static uint8_t flash_anim = 0; - if (flash_anim % 32 == 4) { - layoutProgress("INSTALLING ... Please wait", 1000 * flash_pos / flash_len); - } - flash_anim++; - - const uint8_t *p = buf + 1; - while (p < buf + 64 && flash_pos < flash_len) { - w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w - wi++; - if (wi == 4) { - if (flash_pos < FLASH_FWHEADER_LEN) { - FW_HEADER[flash_pos / 4] = w; - } else { - FW_CHUNK[(flash_pos % FW_CHUNK_SIZE)/ 4] = w; - } - flash_pos += 4; - wi = 0; - // finished the whole chunk - if (flash_pos % FW_CHUNK_SIZE == 0) { - check_and_write_chunk(); - } - } - p++; - } - // flashing done - if (flash_pos == flash_len) { - // flush remaining data in the last chunk - if (flash_pos % FW_CHUNK_SIZE > 0) { - check_and_write_chunk(); - } - flash_state = STATE_CHECK; - const image_header *hdr = (const image_header *)FW_HEADER; - if (SIG_OK != signatures_new_ok(hdr, NULL)) { - send_msg_buttonrequest_firmwarecheck(dev); - return; - } - } else { - return; - } - } - - if (flash_state == STATE_CHECK) { - - // use the firmware header from RAM - const image_header *hdr = (const image_header *)FW_HEADER; - - bool hash_check_ok; - // show fingerprint of unsigned firmware - if (SIG_OK != signatures_new_ok(hdr, NULL)) { - if (msg_id != 0x001B) { // ButtonAck message (id 27) - return; - } - uint8_t hash[32]; - compute_firmware_fingerprint(hdr, hash); - layoutFirmwareFingerprint(hash); - hash_check_ok = get_button_response(); - } else { - hash_check_ok = true; - } - - layoutProgress("INSTALLING ... Please wait", 1000); - // wipe storage if: - // 1) old firmware was unsigned or not present - // 2) signatures are not OK - // 3) hashes are not OK - if (SIG_OK != old_was_signed || SIG_OK != signatures_new_ok(hdr, NULL) || SIG_OK != check_firmware_hashes(hdr)) { - // erase storage - erase_storage(); - // check erasure - uint8_t hash[32]; - sha256_Raw(FLASH_PTR(FLASH_STORAGE_START), FLASH_STORAGE_LEN, hash); - if (memcmp(hash, "\x2d\x86\x4c\x0b\x78\x9a\x43\x21\x4e\xee\x85\x24\xd3\x18\x20\x75\x12\x5e\x5c\xa2\xcd\x52\x7f\x35\x82\xec\x87\xff\xd9\x40\x76\xbc", 32) != 0) { - send_msg_failure(dev); - show_halt("Error installing", "firmware."); - return; - } - } - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - // write firmware header only when hash was confirmed - if (hash_check_ok) { - for (size_t i = 0; i < FLASH_FWHEADER_LEN/sizeof(uint32_t); i++) { - flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), FW_HEADER[i]); - } - } else { - for (size_t i = 0; i < FLASH_FWHEADER_LEN/sizeof(uint32_t); i++) { - flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), 0); - } - } - flash_wait_for_last_operation(); - flash_lock(); - - flash_state = STATE_END; - if (hash_check_ok) { - layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware", "successfully installed.", NULL, "You may now", "unplug your TREZOR.", NULL); - send_msg_success(dev); - shutdown(); - } else { - layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", "aborted.", NULL, "You need to repeat", "the procedure with", "the correct firmware."); - send_msg_failure(dev); - shutdown(); - } - return; - } +static void rx_callback(usbd_device *dev, uint8_t ep) { + (void)ep; + static uint16_t msg_id = 0xFFFF; + static uint8_t buf[64] __attribute__((aligned(4))); + static uint32_t w; + static int wi; + static int old_was_signed; + + if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return; + + if (flash_state == STATE_END) { + return; + } + + if (flash_state == STATE_READY || flash_state == STATE_OPEN || + flash_state == STATE_FLASHSTART || flash_state == STATE_CHECK) { + if (buf[0] != '?' || buf[1] != '#' || + buf[2] != '#') { // invalid start - discard + return; + } + // struct.unpack(">HL") => msg, size + msg_id = (buf[3] << 8) + buf[4]; + } + + if (flash_state == STATE_READY || flash_state == STATE_OPEN) { + if (msg_id == 0x0000) { // Initialize message (id 0) + send_msg_features(dev); + flash_state = STATE_OPEN; + return; + } + if (msg_id == 0x0037) { // GetFeatures message (id 55) + send_msg_features(dev); + return; + } + if (msg_id == 0x0001) { // Ping message (id 1) + send_msg_success(dev); + return; + } + if (msg_id == 0x0005) { // WipeDevice message (id 5) + layoutDialog(&bmp_icon_question, "Cancel", "Confirm", NULL, + "Do you really want to", "wipe the device?", NULL, + "All data will be lost.", NULL, NULL); + bool but = get_button_response(); + if (but) { + erase_storage_code_progress(); + flash_state = STATE_END; + layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "Device", + "successfully wiped.", NULL, "You may now", + "unplug your TREZOR.", NULL); + send_msg_success(dev); + } else { + flash_state = STATE_END; + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Device wipe", + "aborted.", NULL, "You may now", "unplug your TREZOR.", + NULL); + send_msg_failure(dev); + } + return; + } + } + + if (flash_state == STATE_OPEN) { + if (msg_id == 0x0006) { // FirmwareErase message (id 6) + bool proceed = false; + if (firmware_present_new()) { + layoutDialog(&bmp_icon_question, "Abort", "Continue", NULL, + "Install new", "firmware?", NULL, "Never do this without", + "your recovery card!", NULL); + proceed = get_button_response(); + } else { + proceed = true; + } + if (proceed) { + // check whether the current firmware is signed (old or new method) + if (firmware_present_new()) { + const image_header *hdr = + (const image_header *)FLASH_PTR(FLASH_FWHEADER_START); + old_was_signed = + signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr); + } else if (firmware_present_old()) { + old_was_signed = signatures_old_ok(); + } else { + old_was_signed = SIG_FAIL; + } + erase_code_progress(); + send_msg_success(dev); + flash_state = STATE_FLASHSTART; + } else { + send_msg_failure(dev); + flash_state = STATE_END; + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, + "Firmware installation", "aborted.", NULL, "You may now", + "unplug your TREZOR.", NULL); + } + return; + } + return; + } + + if (flash_state == STATE_FLASHSTART) { + if (msg_id == 0x0007) { // FirmwareUpload message (id 7) + if (buf[9] != 0x0a) { // invalid contents + send_msg_failure(dev); + flash_state = STATE_END; + show_halt("Error installing", "firmware."); + return; + } + // read payload length + const uint8_t *p = buf + 10; + flash_len = readprotobufint(&p); + if (flash_len <= FLASH_FWHEADER_LEN) { // firmware is too small + send_msg_failure(dev); + flash_state = STATE_END; + show_halt("Firmware is too small.", NULL); + return; + } + if (flash_len > + FLASH_FWHEADER_LEN + FLASH_APP_LEN) { // firmware is too big + send_msg_failure(dev); + flash_state = STATE_END; + show_halt("Firmware is too big.", NULL); + return; + } + // check firmware magic + if (memcmp(p, &FIRMWARE_MAGIC_NEW, 4) != 0) { + send_msg_failure(dev); + flash_state = STATE_END; + show_halt("Wrong firmware header.", NULL); + return; + } + memzero(FW_HEADER, sizeof(FW_HEADER)); + memzero(FW_CHUNK, sizeof(FW_CHUNK)); + flash_state = STATE_FLASHING; + flash_pos = 0; + chunk_idx = 0; + w = 0; + while (p < buf + 64) { + w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w + wi++; + if (wi == 4) { + FW_HEADER[flash_pos / 4] = w; + flash_pos += 4; + wi = 0; + } + p++; + } + return; + } + return; + } + + if (flash_state == STATE_FLASHING) { + if (buf[0] != '?') { // invalid contents + send_msg_failure(dev); + flash_state = STATE_END; + show_halt("Error installing", "firmware."); + return; + } + + static uint8_t flash_anim = 0; + if (flash_anim % 32 == 4) { + layoutProgress("INSTALLING ... Please wait", + 1000 * flash_pos / flash_len); + } + flash_anim++; + + const uint8_t *p = buf + 1; + while (p < buf + 64 && flash_pos < flash_len) { + w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w + wi++; + if (wi == 4) { + if (flash_pos < FLASH_FWHEADER_LEN) { + FW_HEADER[flash_pos / 4] = w; + } else { + FW_CHUNK[(flash_pos % FW_CHUNK_SIZE) / 4] = w; + } + flash_pos += 4; + wi = 0; + // finished the whole chunk + if (flash_pos % FW_CHUNK_SIZE == 0) { + check_and_write_chunk(); + } + } + p++; + } + // flashing done + if (flash_pos == flash_len) { + // flush remaining data in the last chunk + if (flash_pos % FW_CHUNK_SIZE > 0) { + check_and_write_chunk(); + } + flash_state = STATE_CHECK; + const image_header *hdr = (const image_header *)FW_HEADER; + if (SIG_OK != signatures_new_ok(hdr, NULL)) { + send_msg_buttonrequest_firmwarecheck(dev); + return; + } + } else { + return; + } + } + + if (flash_state == STATE_CHECK) { + // use the firmware header from RAM + const image_header *hdr = (const image_header *)FW_HEADER; + + bool hash_check_ok; + // show fingerprint of unsigned firmware + if (SIG_OK != signatures_new_ok(hdr, NULL)) { + if (msg_id != 0x001B) { // ButtonAck message (id 27) + return; + } + uint8_t hash[32]; + compute_firmware_fingerprint(hdr, hash); + layoutFirmwareFingerprint(hash); + hash_check_ok = get_button_response(); + } else { + hash_check_ok = true; + } + + layoutProgress("INSTALLING ... Please wait", 1000); + // wipe storage if: + // 1) old firmware was unsigned or not present + // 2) signatures are not OK + // 3) hashes are not OK + if (SIG_OK != old_was_signed || SIG_OK != signatures_new_ok(hdr, NULL) || + SIG_OK != check_firmware_hashes(hdr)) { + // erase storage + erase_storage(); + // check erasure + uint8_t hash[32]; + sha256_Raw(FLASH_PTR(FLASH_STORAGE_START), FLASH_STORAGE_LEN, hash); + if (memcmp(hash, + "\x2d\x86\x4c\x0b\x78\x9a\x43\x21\x4e\xee\x85\x24\xd3\x18\x20" + "\x75\x12\x5e\x5c\xa2\xcd\x52\x7f\x35\x82\xec\x87\xff\xd9\x40" + "\x76\xbc", + 32) != 0) { + send_msg_failure(dev); + show_halt("Error installing", "firmware."); + return; + } + } + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + // write firmware header only when hash was confirmed + if (hash_check_ok) { + for (size_t i = 0; i < FLASH_FWHEADER_LEN / sizeof(uint32_t); i++) { + flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), + FW_HEADER[i]); + } + } else { + for (size_t i = 0; i < FLASH_FWHEADER_LEN / sizeof(uint32_t); i++) { + flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), 0); + } + } + flash_wait_for_last_operation(); + flash_lock(); + + flash_state = STATE_END; + if (hash_check_ok) { + layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware", + "successfully installed.", NULL, "You may now", + "unplug your TREZOR.", NULL); + send_msg_success(dev); + shutdown(); + } else { + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation", + "aborted.", NULL, "You need to repeat", "the procedure with", + "the correct firmware."); + send_msg_failure(dev); + shutdown(); + } + return; + } } -static void set_config(usbd_device *dev, uint16_t wValue) -{ - (void)wValue; +static void set_config(usbd_device *dev, uint16_t wValue) { + (void)wValue; - usbd_ep_setup(dev, ENDPOINT_ADDRESS_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); - usbd_ep_setup(dev, ENDPOINT_ADDRESS_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, rx_callback); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, + rx_callback); } static usbd_device *usbd_dev; -static uint8_t usbd_control_buffer[256] __attribute__ ((aligned (2))); +static uint8_t usbd_control_buffer[256] __attribute__((aligned(2))); -static const struct usb_device_capability_descriptor* capabilities[] = { - (const struct usb_device_capability_descriptor*)&webusb_platform_capability_descriptor, +static const struct usb_device_capability_descriptor *capabilities[] = { + (const struct usb_device_capability_descriptor + *)&webusb_platform_capability_descriptor, }; static const struct usb_bos_descriptor bos_descriptor = { - .bLength = USB_DT_BOS_SIZE, - .bDescriptorType = USB_DT_BOS, - .bNumDeviceCaps = sizeof(capabilities)/sizeof(capabilities[0]), - .capabilities = capabilities -}; - -static void usbInit(void) -{ - usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, sizeof(usb_strings)/sizeof(const char *), usbd_control_buffer, sizeof(usbd_control_buffer)); - usbd_register_set_config_callback(usbd_dev, set_config); - usb21_setup(usbd_dev, &bos_descriptor); - webusb_setup(usbd_dev, "trezor.io/start"); - winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN); + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + .bNumDeviceCaps = sizeof(capabilities) / sizeof(capabilities[0]), + .capabilities = capabilities}; + +static void usbInit(void) { + usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, + sizeof(usb_strings) / sizeof(const char *), + usbd_control_buffer, sizeof(usbd_control_buffer)); + usbd_register_set_config_callback(usbd_dev, set_config); + usb21_setup(usbd_dev, &bos_descriptor); + webusb_setup(usbd_dev, "trezor.io/start"); + winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN); } -static void checkButtons(void) -{ - static bool btn_left = false, btn_right = false, btn_final = false; - if (btn_final) { - return; - } - uint16_t state = gpio_port_read(BTN_PORT); - if ((state & (BTN_PIN_YES | BTN_PIN_NO)) != (BTN_PIN_YES | BTN_PIN_NO)) { - if ((state & BTN_PIN_NO) != BTN_PIN_NO) { - btn_left = true; - } - if ((state & BTN_PIN_YES) != BTN_PIN_YES) { - btn_right = true; - } - } - if (btn_left) { - oledBox(0, 0, 3, 3, true); - } - if (btn_right) { - oledBox(OLED_WIDTH - 4, 0, OLED_WIDTH - 1, 3, true); - } - if (btn_left || btn_right) { - oledRefresh(); - } - if (btn_left && btn_right) { - btn_final = true; - } +static void checkButtons(void) { + static bool btn_left = false, btn_right = false, btn_final = false; + if (btn_final) { + return; + } + uint16_t state = gpio_port_read(BTN_PORT); + if ((state & (BTN_PIN_YES | BTN_PIN_NO)) != (BTN_PIN_YES | BTN_PIN_NO)) { + if ((state & BTN_PIN_NO) != BTN_PIN_NO) { + btn_left = true; + } + if ((state & BTN_PIN_YES) != BTN_PIN_YES) { + btn_right = true; + } + } + if (btn_left) { + oledBox(0, 0, 3, 3, true); + } + if (btn_right) { + oledBox(OLED_WIDTH - 4, 0, OLED_WIDTH - 1, 3, true); + } + if (btn_left || btn_right) { + oledRefresh(); + } + if (btn_left && btn_right) { + btn_final = true; + } } -void usbLoop(void) -{ - bool firmware_present = firmware_present_new(); - usbInit(); - for (;;) { - usbd_poll(usbd_dev); - if (!firmware_present && (flash_state == STATE_READY || flash_state == STATE_OPEN)) { - checkButtons(); - } - } +void usbLoop(void) { + bool firmware_present = firmware_present_new(); + usbInit(); + for (;;) { + usbd_poll(usbd_dev); + if (!firmware_present && + (flash_state == STATE_READY || flash_state == STATE_OPEN)) { + checkButtons(); + } + } } diff --git a/bootloader/usb_desc.h b/bootloader/usb_desc.h index d4044b930..e23edbe7e 100644 --- a/bootloader/usb_desc.h +++ b/bootloader/usb_desc.h @@ -1,75 +1,77 @@ #define USB_INTERFACE_INDEX_MAIN 0 -#define ENDPOINT_ADDRESS_IN (0x81) -#define ENDPOINT_ADDRESS_OUT (0x01) +#define ENDPOINT_ADDRESS_IN (0x81) +#define ENDPOINT_ADDRESS_OUT (0x01) static const struct usb_device_descriptor dev_descr = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0210, - .bDeviceClass = 0, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - .bMaxPacketSize0 = 64, - .idVendor = 0x1209, - .idProduct = 0x53c0, - .bcdDevice = 0x0100, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, - .bNumConfigurations = 1, + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0210, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1209, + .idProduct = 0x53c0, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, }; -static const struct usb_endpoint_descriptor endpoints[2] = {{ - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_IN, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}, { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_OUT, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}}; +static const struct usb_endpoint_descriptor endpoints[2] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_IN, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }}; static const struct usb_interface_descriptor iface[] = {{ - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = USB_INTERFACE_INDEX_MAIN, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - .endpoint = endpoints, - .extra = NULL, - .extralen = 0, + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_INTERFACE_INDEX_MAIN, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = endpoints, + .extra = NULL, + .extralen = 0, }}; static const struct usb_interface ifaces[] = {{ - .num_altsetting = 1, - .altsetting = iface, + .num_altsetting = 1, + .altsetting = iface, }}; static const struct usb_config_descriptor config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, - .bMaxPower = 0x32, - .interface = ifaces, + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + .interface = ifaces, }; static const char *usb_strings[] = { - "SatoshiLabs", - "TREZOR", - "000000000000000000000000", + "SatoshiLabs", + "TREZOR", + "000000000000000000000000", }; diff --git a/bootloader/usb_erase.h b/bootloader/usb_erase.h index cadcf4573..d5ebba0b1 100644 --- a/bootloader/usb_erase.h +++ b/bootloader/usb_erase.h @@ -1,44 +1,49 @@ -static void erase_storage_code_progress(void) -{ - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - // erase storage area - for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; i++) { - layoutProgress("WIPING ... Please wait", 1000 * (i - FLASH_STORAGE_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - // erase code area - for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { - layoutProgress("WIPING ... Please wait", 1000 * (i - FLASH_STORAGE_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - flash_wait_for_last_operation(); - flash_lock(); +static void erase_storage_code_progress(void) { + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + // erase storage area + for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; + i++) { + layoutProgress("WIPING ... Please wait", + 1000 * (i - FLASH_STORAGE_SECTOR_FIRST) / + (FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST)); + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + // erase code area + for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { + layoutProgress("WIPING ... Please wait", + 1000 * (i - FLASH_STORAGE_SECTOR_FIRST) / + (FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST)); + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + flash_wait_for_last_operation(); + flash_lock(); } -static void erase_code_progress(void) -{ - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { - layoutProgress("PREPARING ... Please wait", 1000 * (i - FLASH_CODE_SECTOR_FIRST) / (FLASH_CODE_SECTOR_LAST - FLASH_CODE_SECTOR_FIRST)); - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - layoutProgress("INSTALLING ... Please wait", 0); - flash_wait_for_last_operation(); - flash_lock(); +static void erase_code_progress(void) { + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) { + layoutProgress("PREPARING ... Please wait", + 1000 * (i - FLASH_CODE_SECTOR_FIRST) / + (FLASH_CODE_SECTOR_LAST - FLASH_CODE_SECTOR_FIRST)); + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + layoutProgress("INSTALLING ... Please wait", 0); + flash_wait_for_last_operation(); + flash_lock(); } -static void erase_storage(void) -{ - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; i++) { - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - flash_wait_for_last_operation(); - flash_lock(); +static void erase_storage(void) { + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; + i++) { + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + flash_wait_for_last_operation(); + flash_lock(); } diff --git a/bootloader/usb_send.h b/bootloader/usb_send.h index ed73fbe08..7f94ef9d8 100644 --- a/bootloader/usb_send.h +++ b/bootloader/usb_send.h @@ -1,86 +1,92 @@ -static void send_msg_success(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: Success message (id 2), payload len 0 - memcpy(response, - // header - "?##" - // msg_id - "\x00\x02" - // msg_size - "\x00\x00\x00\x00", - 9); - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +static void send_msg_success(usbd_device *dev) { + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: Success message (id 2), payload len 0 + memcpy(response, + // header + "?##" + // msg_id + "\x00\x02" + // msg_size + "\x00\x00\x00\x00", + 9); + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) { + } } -static void send_msg_failure(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: Failure message (id 3), payload len 2 - // - code = 99 (Failure_FirmwareError) - memcpy(response, - // header - "?##" - // msg_id - "\x00\x03" - // msg_size - "\x00\x00\x00\x02" - // data - "\x08" "\x63", - 11); - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +static void send_msg_failure(usbd_device *dev) { + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: Failure message (id 3), payload len 2 + // - code = 99 (Failure_FirmwareError) + memcpy(response, + // header + "?##" + // msg_id + "\x00\x03" + // msg_size + "\x00\x00\x00\x02" + // data + "\x08" + "\x63", + 11); + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) { + } } -static void send_msg_features(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: Features message (id 17), payload len 25 - // - vendor = "trezor.io" - // - major_version = VERSION_MAJOR - // - minor_version = VERSION_MINOR - // - patch_version = VERSION_PATCH - // - bootloader_mode = True - // - firmware_present = True/False - // - model = "1" - memcpy(response, - // header - "?##" - // msg_id - "\x00\x11" - // msg_size - "\x00\x00\x00\x16" - // data - "\x0a" "\x09" "trezor.io" - "\x10" VERSION_MAJOR_CHAR - "\x18" VERSION_MINOR_CHAR - "\x20" VERSION_PATCH_CHAR - "\x28" "\x01" - "\x90\x01" "\x00" - "\xaa" "\x01" "1", - 34); - response[30] = firmware_present_new() ? 0x01 : 0x00; - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +static void send_msg_features(usbd_device *dev) { + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: Features message (id 17), payload len 25 + // - vendor = "trezor.io" + // - major_version = VERSION_MAJOR + // - minor_version = VERSION_MINOR + // - patch_version = VERSION_PATCH + // - bootloader_mode = True + // - firmware_present = True/False + // - model = "1" + memcpy(response, + // header + "?##" + // msg_id + "\x00\x11" + // msg_size + "\x00\x00\x00\x16" + // data + "\x0a" + "\x09" + "trezor.io" + "\x10" VERSION_MAJOR_CHAR "\x18" VERSION_MINOR_CHAR + "\x20" VERSION_PATCH_CHAR + "\x28" + "\x01" + "\x90\x01" + "\x00" + "\xaa" + "\x01" + "1", + 34); + response[30] = firmware_present_new() ? 0x01 : 0x00; + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) { + } } -static void send_msg_buttonrequest_firmwarecheck(usbd_device *dev) -{ - uint8_t response[64]; - memzero(response, sizeof(response)); - // response: ButtonRequest message (id 26), payload len 2 - // - code = ButtonRequest_FirmwareCheck (9) - memcpy(response, - // header - "?##" - // msg_id - "\x00\x1a" - // msg_size - "\x00\x00\x00\x02" - // data - "\x08" "\x09", - 11 - ); - while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {} +static void send_msg_buttonrequest_firmwarecheck(usbd_device *dev) { + uint8_t response[64]; + memzero(response, sizeof(response)); + // response: ButtonRequest message (id 26), payload len 2 + // - code = ButtonRequest_FirmwareCheck (9) + memcpy(response, + // header + "?##" + // msg_id + "\x00\x1a" + // msg_size + "\x00\x00\x00\x02" + // data + "\x08" + "\x09", + 11); + while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) { + } } diff --git a/buttons.c b/buttons.c index f416736c3..0a86dbe61 100644 --- a/buttons.c +++ b/buttons.c @@ -22,52 +22,49 @@ struct buttonState button; #if !EMULATOR -uint16_t buttonRead(void) { - return gpio_port_read(BTN_PORT); -} +uint16_t buttonRead(void) { return gpio_port_read(BTN_PORT); } #endif -void buttonUpdate() -{ - static uint16_t last_state = BTN_PIN_YES | BTN_PIN_NO; +void buttonUpdate() { + static uint16_t last_state = BTN_PIN_YES | BTN_PIN_NO; - uint16_t state = buttonRead(); + uint16_t state = buttonRead(); - if ((state & BTN_PIN_YES) == 0) { // Yes button is down - if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down - if (button.YesDown < 2000000000) button.YesDown++; - button.YesUp = false; - } else { // last Yes was up - button.YesDown = 0; - button.YesUp = false; - } - } else { // Yes button is up - if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down - button.YesDown = 0; - button.YesUp = true; - } else { // last Yes was up - button.YesDown = 0; - button.YesUp = false; - } - } + if ((state & BTN_PIN_YES) == 0) { // Yes button is down + if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down + if (button.YesDown < 2000000000) button.YesDown++; + button.YesUp = false; + } else { // last Yes was up + button.YesDown = 0; + button.YesUp = false; + } + } else { // Yes button is up + if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down + button.YesDown = 0; + button.YesUp = true; + } else { // last Yes was up + button.YesDown = 0; + button.YesUp = false; + } + } - if ((state & BTN_PIN_NO) == 0) { // No button is down - if ((last_state & BTN_PIN_NO) == 0) { // last No was down - if (button.NoDown < 2000000000) button.NoDown++; - button.NoUp = false; - } else { // last No was up - button.NoDown = 0; - button.NoUp = false; - } - } else { // No button is up - if ((last_state & BTN_PIN_NO) == 0) { // last No was down - button.NoDown = 0; - button.NoUp = true; - } else { // last No was up - button.NoDown = 0; - button.NoUp = false; - } - } + if ((state & BTN_PIN_NO) == 0) { // No button is down + if ((last_state & BTN_PIN_NO) == 0) { // last No was down + if (button.NoDown < 2000000000) button.NoDown++; + button.NoUp = false; + } else { // last No was up + button.NoDown = 0; + button.NoUp = false; + } + } else { // No button is up + if ((last_state & BTN_PIN_NO) == 0) { // last No was down + button.NoDown = 0; + button.NoUp = true; + } else { // last No was up + button.NoDown = 0; + button.NoUp = false; + } + } - last_state = state; + last_state = state; } diff --git a/buttons.h b/buttons.h index fb24d58dd..48f3c9984 100644 --- a/buttons.h +++ b/buttons.h @@ -24,10 +24,10 @@ #include struct buttonState { - volatile bool YesUp; - volatile int YesDown; - volatile bool NoUp; - volatile int NoDown; + volatile bool YesUp; + volatile int YesDown; + volatile bool NoUp; + volatile int NoDown; }; extern struct buttonState button; @@ -36,15 +36,15 @@ uint16_t buttonRead(void); void buttonUpdate(void); #ifndef BTN_PORT -#define BTN_PORT GPIOC +#define BTN_PORT GPIOC #endif #ifndef BTN_PIN_YES -#define BTN_PIN_YES GPIO2 +#define BTN_PIN_YES GPIO2 #endif #ifndef BTN_PIN_NO -#define BTN_PIN_NO GPIO5 +#define BTN_PIN_NO GPIO5 #endif #endif diff --git a/common.c b/common.c index ae7230613..0bd282ead 100644 --- a/common.c +++ b/common.c @@ -17,63 +17,67 @@ * along with this program. If not, see . */ -#include #include "common.h" -#include "rng.h" +#include +#include "bitmaps.h" +#include "firmware/usb.h" #include "layout.h" #include "oled.h" +#include "rng.h" #include "util.h" -#include "bitmaps.h" -#include "firmware/usb.h" uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN]; -void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, const char *file, int line_num, const char *func) { - const BITMAP *icon = &bmp_icon_error; - char line[128] = {0}; - int y = icon->height + 3; - oledClear(); +void __attribute__((noreturn)) +__fatal_error(const char *expr, const char *msg, const char *file, int line_num, + const char *func) { + const BITMAP *icon = &bmp_icon_error; + char line[128] = {0}; + int y = icon->height + 3; + oledClear(); - oledDrawBitmap(0, 0, icon); - oledDrawStringCenter(OLED_WIDTH / 2, (icon->height - FONT_HEIGHT)/2 + 1, "FATAL ERROR", FONT_STANDARD); + oledDrawBitmap(0, 0, icon); + oledDrawStringCenter(OLED_WIDTH / 2, (icon->height - FONT_HEIGHT) / 2 + 1, + "FATAL ERROR", FONT_STANDARD); - snprintf(line, sizeof(line), "Expr: %s", expr ? expr : "(null)"); - oledDrawString(0, y, line, FONT_STANDARD); - y += FONT_HEIGHT + 1; + snprintf(line, sizeof(line), "Expr: %s", expr ? expr : "(null)"); + oledDrawString(0, y, line, FONT_STANDARD); + y += FONT_HEIGHT + 1; - snprintf(line, sizeof(line), "Msg: %s", msg ? msg : "(null)"); - oledDrawString(0, y, line, FONT_STANDARD); - y += FONT_HEIGHT + 1; + snprintf(line, sizeof(line), "Msg: %s", msg ? msg : "(null)"); + oledDrawString(0, y, line, FONT_STANDARD); + y += FONT_HEIGHT + 1; - const char *label = "File: "; - snprintf(line, sizeof(line), "%s:%d", file ? file : "(null)", line_num); - oledDrawStringRight(OLED_WIDTH - 1, y, line, FONT_STANDARD); - oledBox(0, y, oledStringWidth(label, FONT_STANDARD), y + FONT_HEIGHT, false); - oledDrawString(0, y, label, FONT_STANDARD); - y += FONT_HEIGHT + 1; + const char *label = "File: "; + snprintf(line, sizeof(line), "%s:%d", file ? file : "(null)", line_num); + oledDrawStringRight(OLED_WIDTH - 1, y, line, FONT_STANDARD); + oledBox(0, y, oledStringWidth(label, FONT_STANDARD), y + FONT_HEIGHT, false); + oledDrawString(0, y, label, FONT_STANDARD); + y += FONT_HEIGHT + 1; - snprintf(line, sizeof(line), "Func: %s", func ? func : "(null)"); - oledDrawString(0, y, line, FONT_STANDARD); - y += FONT_HEIGHT + 1; + snprintf(line, sizeof(line), "Func: %s", func ? func : "(null)"); + oledDrawString(0, y, line, FONT_STANDARD); + y += FONT_HEIGHT + 1; - oledDrawString(0, y, "Contact TREZOR support.", FONT_STANDARD); - oledRefresh(); + oledDrawString(0, y, "Contact TREZOR support.", FONT_STANDARD); + oledRefresh(); - shutdown(); + shutdown(); } -void __attribute__((noreturn)) error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4) { - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, line3, line4, "Please unplug", "the device."); - shutdown(); +void __attribute__((noreturn)) +error_shutdown(const char *line1, const char *line2, const char *line3, + const char *line4) { + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, line3, line4, + "Please unplug", "the device."); + shutdown(); } #ifndef NDEBUG -void __assert_func(const char *file, int line, const char *func, const char *expr) { - __fatal_error(expr, "assert failed", file, line, func); +void __assert_func(const char *file, int line, const char *func, + const char *expr) { + __fatal_error(expr, "assert failed", file, line, func); } #endif -void hal_delay(uint32_t ms) -{ - usbSleep(ms); -} +void hal_delay(uint32_t ms) { usbSleep(ms); } diff --git a/common.h b/common.h index a4fd9886e..1774bc7f0 100644 --- a/common.h +++ b/common.h @@ -26,10 +26,17 @@ #define HW_ENTROPY_LEN (12 + 32) extern uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN]; -void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg, const char *file, int line, const char *func); -void __attribute__((noreturn)) error_shutdown(const char *line1, const char *line2, const char *line3, const char *line4); +void __attribute__((noreturn)) +__fatal_error(const char *expr, const char *msg, const char *file, int line, + const char *func); +void __attribute__((noreturn)) +error_shutdown(const char *line1, const char *line2, const char *line3, + const char *line4); -#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__)) +#define ensure(expr, msg) \ + (((expr) == sectrue) \ + ? (void)0 \ + : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__)) void hal_delay(uint32_t ms); diff --git a/demo/demo.c b/demo/demo.c index b2bbe8f98..4ece26d9b 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -17,17 +17,17 @@ * along with this library. If not, see . */ -#include -#include #include +#include +#include #include "bitmaps.h" #include "buttons.h" +#include "hmac.h" #include "layout.h" #include "oled.h" -#include "setup.h" -#include "hmac.h" #include "pbkdf2.h" #include "rng.h" +#include "setup.h" const int states = 2; int state = 0; @@ -40,252 +40,270 @@ uint8_t *salt = (uint8_t *)"TREZOR"; uint32_t saltlen; static const struct usb_device_descriptor dev_descr = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = 0, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - .bMaxPacketSize0 = 64, - .idVendor = 0x1209, - .idProduct = 0x53c1, - .bcdDevice = 0x0100, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 3, - .bNumConfigurations = 1, + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1209, + .idProduct = 0x53c1, + .bcdDevice = 0x0100, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, }; /* got via usbhid-dump from CP2110 */ static const uint8_t hid_report_descriptor[] = { - 0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x09, 0x01, 0x75, 0x08, 0x95, 0x40, 0x26, 0xFF, 0x00, - 0x15, 0x00, 0x85, 0x01, 0x95, 0x01, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x02, - 0x95, 0x02, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x03, 0x95, 0x03, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x04, 0x95, 0x04, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x05, 0x95, 0x05, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x06, - 0x95, 0x06, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x07, 0x95, 0x07, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x08, 0x95, 0x08, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x09, 0x95, 0x09, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0A, - 0x95, 0x0A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0B, 0x95, 0x0B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0C, 0x95, 0x0C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x0D, 0x95, 0x0D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0E, - 0x95, 0x0E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0F, 0x95, 0x0F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x10, 0x95, 0x10, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x11, 0x95, 0x11, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x12, - 0x95, 0x12, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x13, 0x95, 0x13, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x14, 0x95, 0x14, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x15, 0x95, 0x15, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x16, - 0x95, 0x16, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x17, 0x95, 0x17, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x18, 0x95, 0x18, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x19, 0x95, 0x19, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1A, - 0x95, 0x1A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1B, 0x95, 0x1B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1C, 0x95, 0x1C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x1D, 0x95, 0x1D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1E, - 0x95, 0x1E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1F, 0x95, 0x1F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x20, 0x95, 0x20, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x21, 0x95, 0x21, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x22, - 0x95, 0x22, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x23, 0x95, 0x23, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x24, 0x95, 0x24, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x25, 0x95, 0x25, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x26, - 0x95, 0x26, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x27, 0x95, 0x27, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x28, 0x95, 0x28, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x29, 0x95, 0x29, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2A, - 0x95, 0x2A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2B, 0x95, 0x2B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2C, 0x95, 0x2C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x2D, 0x95, 0x2D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2E, - 0x95, 0x2E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2F, 0x95, 0x2F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x30, 0x95, 0x30, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x31, 0x95, 0x31, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x32, - 0x95, 0x32, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x33, 0x95, 0x33, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x34, 0x95, 0x34, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x35, 0x95, 0x35, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x36, - 0x95, 0x36, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x37, 0x95, 0x37, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x38, 0x95, 0x38, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x39, 0x95, 0x39, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3A, - 0x95, 0x3A, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3B, 0x95, 0x3B, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3C, 0x95, 0x3C, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, - 0x91, 0x02, 0x85, 0x3D, 0x95, 0x3D, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3E, - 0x95, 0x3E, 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3F, 0x95, 0x3F, 0x09, 0x01, - 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x40, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x41, - 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x42, 0x95, 0x06, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x43, - 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x44, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x45, - 0x95, 0x04, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x46, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x47, - 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x50, 0x95, 0x08, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x51, - 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x52, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x60, - 0x95, 0x0A, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x61, 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x62, - 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x63, 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x64, - 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x65, 0x95, 0x3E, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x66, - 0x95, 0x13, 0x09, 0x01, 0xB1, 0x02, 0xC0, + 0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x09, 0x01, 0x75, 0x08, 0x95, + 0x40, 0x26, 0xFF, 0x00, 0x15, 0x00, 0x85, 0x01, 0x95, 0x01, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x02, 0x95, 0x02, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x03, 0x95, 0x03, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x04, 0x95, 0x04, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x05, 0x95, 0x05, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x06, 0x95, 0x06, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x07, 0x95, 0x07, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x08, 0x95, 0x08, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x09, 0x95, 0x09, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0A, 0x95, 0x0A, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0B, 0x95, 0x0B, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0C, 0x95, 0x0C, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0D, 0x95, 0x0D, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0E, 0x95, 0x0E, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0F, 0x95, 0x0F, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x10, 0x95, 0x10, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x11, 0x95, 0x11, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x12, 0x95, 0x12, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x13, 0x95, 0x13, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x14, 0x95, 0x14, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x15, 0x95, 0x15, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x16, 0x95, 0x16, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x17, 0x95, 0x17, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x18, 0x95, 0x18, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x19, 0x95, 0x19, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1A, 0x95, 0x1A, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1B, 0x95, 0x1B, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1C, 0x95, 0x1C, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1D, 0x95, 0x1D, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1E, 0x95, 0x1E, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1F, 0x95, 0x1F, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x20, 0x95, 0x20, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x21, 0x95, 0x21, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x22, 0x95, 0x22, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x23, 0x95, 0x23, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x24, 0x95, 0x24, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x25, 0x95, 0x25, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x26, 0x95, 0x26, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x27, 0x95, 0x27, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x28, 0x95, 0x28, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x29, 0x95, 0x29, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2A, 0x95, 0x2A, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2B, 0x95, 0x2B, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2C, 0x95, 0x2C, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2D, 0x95, 0x2D, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2E, 0x95, 0x2E, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2F, 0x95, 0x2F, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x30, 0x95, 0x30, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x31, 0x95, 0x31, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x32, 0x95, 0x32, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x33, 0x95, 0x33, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x34, 0x95, 0x34, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x35, 0x95, 0x35, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x36, 0x95, 0x36, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x37, 0x95, 0x37, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x38, 0x95, 0x38, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x39, 0x95, 0x39, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3A, 0x95, 0x3A, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3B, 0x95, 0x3B, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3C, 0x95, 0x3C, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3D, 0x95, 0x3D, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3E, 0x95, 0x3E, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3F, 0x95, 0x3F, 0x09, 0x01, + 0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x40, 0x95, 0x01, 0x09, 0x01, + 0xB1, 0x02, 0x85, 0x41, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x42, + 0x95, 0x06, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x43, 0x95, 0x01, 0x09, 0x01, + 0xB1, 0x02, 0x85, 0x44, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x45, + 0x95, 0x04, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x46, 0x95, 0x02, 0x09, 0x01, + 0xB1, 0x02, 0x85, 0x47, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x50, + 0x95, 0x08, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x51, 0x95, 0x01, 0x09, 0x01, + 0xB1, 0x02, 0x85, 0x52, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x60, + 0x95, 0x0A, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x61, 0x95, 0x3F, 0x09, 0x01, + 0xB1, 0x02, 0x85, 0x62, 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x63, + 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x64, 0x95, 0x3F, 0x09, 0x01, + 0xB1, 0x02, 0x85, 0x65, 0x95, 0x3E, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x66, + 0x95, 0x13, 0x09, 0x01, 0xB1, 0x02, 0xC0, }; static const struct { - struct usb_hid_descriptor hid_descriptor; - struct { - uint8_t bReportDescriptorType; - uint16_t wDescriptorLength; - } __attribute__((packed)) hid_report; -} __attribute__((packed)) hid_function = { - .hid_descriptor = { - .bLength = sizeof(hid_function), - .bDescriptorType = USB_DT_HID, - .bcdHID = 0x0111, - .bCountryCode = 0, - .bNumDescriptors = 1, - }, - .hid_report = { - .bReportDescriptorType = USB_DT_REPORT, - .wDescriptorLength = sizeof(hid_report_descriptor), - } -}; + struct usb_hid_descriptor hid_descriptor; + struct { + uint8_t bReportDescriptorType; + uint16_t wDescriptorLength; + } __attribute__((packed)) hid_report; +} __attribute__((packed)) +hid_function = {.hid_descriptor = + { + .bLength = sizeof(hid_function), + .bDescriptorType = USB_DT_HID, + .bcdHID = 0x0111, + .bCountryCode = 0, + .bNumDescriptors = 1, + }, + .hid_report = { + .bReportDescriptorType = USB_DT_REPORT, + .wDescriptorLength = sizeof(hid_report_descriptor), + }}; -static const struct usb_endpoint_descriptor hid_endpoints[2] = {{ - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x81, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}, { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x02, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}}; +static const struct usb_endpoint_descriptor hid_endpoints[2] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x81, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x02, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }}; static const struct usb_interface_descriptor hid_iface[] = {{ - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - .endpoint = hid_endpoints, - .extra = &hid_function, - .extralen = sizeof(hid_function), + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + .endpoint = hid_endpoints, + .extra = &hid_function, + .extralen = sizeof(hid_function), }}; static const struct usb_interface ifaces[] = {{ - .num_altsetting = 1, - .altsetting = hid_iface, + .num_altsetting = 1, + .altsetting = hid_iface, }}; static const struct usb_config_descriptor config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = 1, - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, - .bMaxPower = 0x32, - .interface = ifaces, + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + .interface = ifaces, }; static const char *usb_strings[] = { - "SatoshiLabs", - "TREZOR", - "01234567", + "SatoshiLabs", + "TREZOR", + "01234567", }; -static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) -{ - (void)complete; - (void)dev; +static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback *complete) { + (void)complete; + (void)dev; - if ((req->bmRequestType != 0x81) || - (req->bRequest != USB_REQ_GET_DESCRIPTOR) || - (req->wValue != 0x2200)) - return 0; + if ((req->bmRequestType != 0x81) || + (req->bRequest != USB_REQ_GET_DESCRIPTOR) || (req->wValue != 0x2200)) + return 0; - /* Handle the HID report descriptor. */ - *buf = (uint8_t *)hid_report_descriptor; - *len = sizeof(hid_report_descriptor); + /* Handle the HID report descriptor. */ + *buf = (uint8_t *)hid_report_descriptor; + *len = sizeof(hid_report_descriptor); - return 1; + return 1; } -static void hid_rx_callback(usbd_device *dev, uint8_t ep) -{ - (void)dev; - (void)ep; +static void hid_rx_callback(usbd_device *dev, uint8_t ep) { + (void)dev; + (void)ep; } -static void hid_set_config(usbd_device *dev, uint16_t wValue) -{ - (void)wValue; +static void hid_set_config(usbd_device *dev, uint16_t wValue) { + (void)wValue; - usbd_ep_setup(dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); - usbd_ep_setup(dev, 0x02, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_rx_callback); + usbd_ep_setup(dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); + usbd_ep_setup(dev, 0x02, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_rx_callback); - usbd_register_control_callback( - dev, - USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - hid_control_request); + usbd_register_control_callback( + dev, USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, hid_control_request); } static usbd_device *usbd_dev; static uint8_t usbd_control_buffer[128]; -void usbInit(void) -{ - usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer)); - usbd_register_set_config_callback(usbd_dev, hid_set_config); +void usbInit(void) { + usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, 3, + usbd_control_buffer, sizeof(usbd_control_buffer)); + usbd_register_set_config_callback(usbd_dev, hid_set_config); } -int main(void) -{ +int main(void) { #ifndef APPVER - setup(); - __stack_chk_guard = random32(); // this supports compiler provided unpredictable stack protection checks - oledInit(); + setup(); + __stack_chk_guard = random32(); // this supports compiler provided + // unpredictable stack protection checks + oledInit(); #else - setupApp(); - __stack_chk_guard = random32(); // this supports compiler provided unpredictable stack protection checks + setupApp(); + __stack_chk_guard = random32(); // this supports compiler provided + // unpredictable stack protection checks #endif - usbInit(); + usbInit(); - passlen = strlen((char *)pass); - saltlen = strlen((char *)salt); + passlen = strlen((char *)pass); + saltlen = strlen((char *)salt); - for (;;) { - frame = 0; - switch (state) { - case 0: - oledClear(); - oledDrawBitmap(40, 0, &bmp_logo64); - break; - } - oledRefresh(); + for (;;) { + frame = 0; + switch (state) { + case 0: + oledClear(); + oledDrawBitmap(40, 0, &bmp_logo64); + break; + } + oledRefresh(); - do { - usbd_poll(usbd_dev); - switch (state) { - case 1: - layoutProgress("WORKING", frame % 41 * 25); - pbkdf2_hmac_sha512(pass, passlen, salt, saltlen, 100, seed, 64); - usbd_ep_write_packet(usbd_dev, 0x81, seed, 64); - break; - } + do { + usbd_poll(usbd_dev); + switch (state) { + case 1: + layoutProgress("WORKING", frame % 41 * 25); + pbkdf2_hmac_sha512(pass, passlen, salt, saltlen, 100, seed, 64); + usbd_ep_write_packet(usbd_dev, 0x81, seed, 64); + break; + } - buttonUpdate(); - frame += 1; - } while (!button.YesUp && !button.NoUp); + buttonUpdate(); + frame += 1; + } while (!button.YesUp && !button.NoUp); - if (button.YesUp) { - state = (state + 1) % states; - oledSwipeLeft(); - } else { - state = (state + states - 1) % states; - oledSwipeRight(); - } - } + if (button.YesUp) { + state = (state + 1) % states; + oledSwipeLeft(); + } else { + state = (state + states - 1) % states; + oledSwipeRight(); + } + } - return 0; + return 0; } diff --git a/emulator/buttons.c b/emulator/buttons.c index 71b94a442..2d456aafb 100644 --- a/emulator/buttons.c +++ b/emulator/buttons.c @@ -24,17 +24,17 @@ #endif uint16_t buttonRead(void) { - uint16_t state = 0; + uint16_t state = 0; #if !HEADLESS - const uint8_t *scancodes = SDL_GetKeyboardState(NULL); - if (scancodes[SDL_SCANCODE_LEFT]) { - state |= BTN_PIN_NO; - } - if (scancodes[SDL_SCANCODE_RIGHT]) { - state |= BTN_PIN_YES; - } + const uint8_t *scancodes = SDL_GetKeyboardState(NULL); + if (scancodes[SDL_SCANCODE_LEFT]) { + state |= BTN_PIN_NO; + } + if (scancodes[SDL_SCANCODE_RIGHT]) { + state |= BTN_PIN_YES; + } #endif - return ~state; + return ~state; } diff --git a/emulator/memory.c b/emulator/memory.c index 4bcda3886..a1acac60c 100644 --- a/emulator/memory.c +++ b/emulator/memory.c @@ -17,16 +17,14 @@ * along with this library. If not, see . */ -#include #include #include +#include #include #include "memory.h" -void flash_lock(void) { - sync(); -} +void flash_lock(void) { sync(); } void flash_unlock(void) {} @@ -35,105 +33,104 @@ void flash_clear_status_flags(void) {} void flash_lock_option_bytes(void) {} void flash_unlock_option_bytes(void) {} -void flash_program_option_bytes(uint32_t data) { - (void) data; -} +void flash_program_option_bytes(uint32_t data) { (void)data; } static ssize_t sector_to_offset(uint8_t sector) { - switch (sector) { - case 0: - return 0x0; - case 1: - return 0x4000; - case 2: - return 0x8000; - case 3: - return 0xC000; - case 4: - return 0x10000; - case 5: - return 0x20000; - case 6: - return 0x40000; - case 7: - return 0x60000; - case 8: - return 0x80000; - default: - return -1; - } + switch (sector) { + case 0: + return 0x0; + case 1: + return 0x4000; + case 2: + return 0x8000; + case 3: + return 0xC000; + case 4: + return 0x10000; + case 5: + return 0x20000; + case 6: + return 0x40000; + case 7: + return 0x60000; + case 8: + return 0x80000; + default: + return -1; + } } static void *sector_to_address(uint8_t sector) { - ssize_t offset = sector_to_offset(sector); - if (offset < 0) { - return NULL; - } + ssize_t offset = sector_to_offset(sector); + if (offset < 0) { + return NULL; + } - return (void *) FLASH_PTR(FLASH_ORIGIN + offset); + return (void *)FLASH_PTR(FLASH_ORIGIN + offset); } static ssize_t sector_to_size(uint8_t sector) { - ssize_t start = sector_to_offset(sector); - if (start < 0) { - return -1; - } + ssize_t start = sector_to_offset(sector); + if (start < 0) { + return -1; + } - ssize_t end = sector_to_offset(sector + 1); - if (end < 0) { - return -1; - } + ssize_t end = sector_to_offset(sector + 1); + if (end < 0) { + return -1; + } - return end - start; + return end - start; } void flash_erase_sector(uint8_t sector, uint32_t program_size) { - (void) program_size; + (void)program_size; - void *address = sector_to_address(sector); - if (address == NULL) { - return; - } + void *address = sector_to_address(sector); + if (address == NULL) { + return; + } - ssize_t size = sector_to_size(sector); - if (size < 0) { - return; - } + ssize_t size = sector_to_size(sector); + if (size < 0) { + return; + } - memset(address, 0xFF, size); + memset(address, 0xFF, size); } void flash_erase_all_sectors(uint32_t program_size) { - (void) program_size; + (void)program_size; - memset(emulator_flash_base, 0xFF, FLASH_TOTAL_SIZE); + memset(emulator_flash_base, 0xFF, FLASH_TOTAL_SIZE); } void flash_program_word(uint32_t address, uint32_t data) { - *(volatile uint32_t *)FLASH_PTR(address) = data; + *(volatile uint32_t *)FLASH_PTR(address) = data; } void flash_program_byte(uint32_t address, uint8_t data) { - *(volatile uint8_t *)FLASH_PTR(address) = data; + *(volatile uint8_t *)FLASH_PTR(address) = data; } static bool flash_locked = true; void svc_flash_unlock(void) { - assert (flash_locked); - flash_locked = false; + assert(flash_locked); + flash_locked = false; } void svc_flash_program(uint32_t size) { - (void) size; - assert (!flash_locked); + (void)size; + assert(!flash_locked); } void svc_flash_erase_sector(uint16_t sector) { - assert (!flash_locked); - assert (sector >= FLASH_STORAGE_SECTOR_FIRST && sector <= FLASH_STORAGE_SECTOR_LAST); - flash_erase_sector(sector, 3); + assert(!flash_locked); + assert(sector >= FLASH_STORAGE_SECTOR_FIRST && + sector <= FLASH_STORAGE_SECTOR_LAST); + flash_erase_sector(sector, 3); } uint32_t svc_flash_lock(void) { - assert (!flash_locked); - flash_locked = true; - sync(); - return 0; + assert(!flash_locked); + flash_locked = true; + sync(); + return 0; } diff --git a/emulator/oled.c b/emulator/oled.c index cbb853c85..a4466e993 100644 --- a/emulator/oled.c +++ b/emulator/oled.c @@ -37,114 +37,114 @@ static SDL_Rect dstrect; #define ENV_OLED_SCALE "TREZOR_OLED_SCALE" static int emulatorFullscreen(void) { - const char *variable = getenv(ENV_OLED_FULLSCREEN); - if (!variable) { - return 0; - } - return atoi(variable); + const char *variable = getenv(ENV_OLED_FULLSCREEN); + if (!variable) { + return 0; + } + return atoi(variable); } static int emulatorScale(void) { - const char *variable = getenv(ENV_OLED_SCALE); - if (!variable) { - return 1; - } - int scale = atoi(variable); - if (scale >= 1 && scale <= 16) { - return scale; - } - return 1; + const char *variable = getenv(ENV_OLED_SCALE); + if (!variable) { + return 1; + } + int scale = atoi(variable); + if (scale >= 1 && scale <= 16) { + return scale; + } + return 1; } void oledInit(void) { - if (SDL_Init(SDL_INIT_VIDEO) != 0) { - fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); - exit(1); - } - atexit(SDL_Quit); - - int scale = emulatorScale(); - int fullscreen = emulatorFullscreen(); - - SDL_Window *window = SDL_CreateWindow("TREZOR", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - OLED_WIDTH * scale, - OLED_HEIGHT * scale, - fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - - if (window == NULL) { - fprintf(stderr, "Failed to create window: %s\n", SDL_GetError()); - exit(1); - } - - renderer = SDL_CreateRenderer(window, -1, 0); - if (!renderer) { - fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError()); - exit(1); - } - if (fullscreen) { - SDL_DisplayMode current_mode; - if (SDL_GetCurrentDisplayMode(0, ¤t_mode) != 0) - { - fprintf(stderr, "Failed to get current display mode: %s\n", SDL_GetError()); - exit(1); - } - - dstrect.x = (current_mode.w - OLED_WIDTH * scale) / 2; - dstrect.y = (current_mode.h - OLED_HEIGHT * scale) / 2; - - SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); - SDL_RenderClear(renderer); - SDL_ShowCursor(SDL_DISABLE); - } else { - dstrect.x = 0; - dstrect.y = 0; - } - - dstrect.w = OLED_WIDTH * scale; - dstrect.h = OLED_HEIGHT * scale; - - texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, OLED_WIDTH, OLED_HEIGHT); - - oledClear(); - oledRefresh(); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + + int scale = emulatorScale(); + int fullscreen = emulatorFullscreen(); + + SDL_Window *window = SDL_CreateWindow( + "TREZOR", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + OLED_WIDTH * scale, OLED_HEIGHT * scale, + fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + + if (window == NULL) { + fprintf(stderr, "Failed to create window: %s\n", SDL_GetError()); + exit(1); + } + + renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer) { + fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError()); + exit(1); + } + if (fullscreen) { + SDL_DisplayMode current_mode; + if (SDL_GetCurrentDisplayMode(0, ¤t_mode) != 0) { + fprintf(stderr, "Failed to get current display mode: %s\n", + SDL_GetError()); + exit(1); + } + + dstrect.x = (current_mode.w - OLED_WIDTH * scale) / 2; + dstrect.y = (current_mode.h - OLED_HEIGHT * scale) / 2; + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(renderer); + SDL_ShowCursor(SDL_DISABLE); + } else { + dstrect.x = 0; + dstrect.y = 0; + } + + dstrect.w = OLED_WIDTH * scale; + dstrect.h = OLED_HEIGHT * scale; + + texture = + SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, OLED_WIDTH, OLED_HEIGHT); + + oledClear(); + oledRefresh(); } void oledRefresh(void) { - /* Draw triangle in upper right corner */ - oledInvertDebugLink(); + /* Draw triangle in upper right corner */ + oledInvertDebugLink(); - const uint8_t *buffer = oledGetBuffer(); + const uint8_t *buffer = oledGetBuffer(); - static uint32_t data[OLED_HEIGHT][OLED_WIDTH]; + static uint32_t data[OLED_HEIGHT][OLED_WIDTH]; - for (size_t i = 0; i < OLED_BUFSIZE; i++) { - int x = (OLED_BUFSIZE - 1 - i) % OLED_WIDTH; - int y = (OLED_BUFSIZE - 1 - i) / OLED_WIDTH * 8 + 7; + for (size_t i = 0; i < OLED_BUFSIZE; i++) { + int x = (OLED_BUFSIZE - 1 - i) % OLED_WIDTH; + int y = (OLED_BUFSIZE - 1 - i) / OLED_WIDTH * 8 + 7; - for (uint8_t shift = 0; shift < 8; shift++, y--) { - bool set = (buffer[i] >> shift) & 1; - data[y][x] = set ? 0xFFFFFFFF : 0xFF000000; - } - } + for (uint8_t shift = 0; shift < 8; shift++, y--) { + bool set = (buffer[i] >> shift) & 1; + data[y][x] = set ? 0xFFFFFFFF : 0xFF000000; + } + } - SDL_UpdateTexture(texture, NULL, data, OLED_WIDTH * sizeof(uint32_t)); - SDL_RenderCopy(renderer, texture, NULL, &dstrect); - SDL_RenderPresent(renderer); + SDL_UpdateTexture(texture, NULL, data, OLED_WIDTH * sizeof(uint32_t)); + SDL_RenderCopy(renderer, texture, NULL, &dstrect); + SDL_RenderPresent(renderer); - /* Return it back */ - oledInvertDebugLink(); + /* Return it back */ + oledInvertDebugLink(); } void emulatorPoll(void) { - SDL_Event event; + SDL_Event event; - if (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { - exit(1); - } - } + if (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + exit(1); + } + } } #endif diff --git a/emulator/rng.c b/emulator/rng.c index 8d53de28d..cd6eb616e 100644 --- a/emulator/rng.c +++ b/emulator/rng.c @@ -20,13 +20,13 @@ #include "rng.h" uint32_t random32(void) { - static uint32_t last = 0; - uint32_t new; + static uint32_t last = 0; + uint32_t new; - do { - emulatorRandom(&new, sizeof(new)); - } while (last == new); + do { + emulatorRandom(&new, sizeof(new)); + } while (last == new); - last = new; - return new; + last = new; + return new; } diff --git a/emulator/setup.c b/emulator/setup.c index 61b4bad14..81b283ecc 100644 --- a/emulator/setup.c +++ b/emulator/setup.c @@ -48,60 +48,61 @@ static void setup_urandom(void); static void setup_flash(void); void setup(void) { - setup_urandom(); - setup_flash(); + setup_urandom(); + setup_flash(); } void __attribute__((noreturn)) shutdown(void) { - for(;;) pause(); + for (;;) pause(); } void emulatorRandom(void *buffer, size_t size) { - ssize_t n, len = 0; - do { - n = read(random_fd, (char*)buffer + len, size - len); - if (n < 0) { - perror("Failed to read " RANDOM_DEV_FILE); - exit(1); - } - len += n; - } while (len != (ssize_t)size); + ssize_t n, len = 0; + do { + n = read(random_fd, (char *)buffer + len, size - len); + if (n < 0) { + perror("Failed to read " RANDOM_DEV_FILE); + exit(1); + } + len += n; + } while (len != (ssize_t)size); } static void setup_urandom(void) { - random_fd = open(RANDOM_DEV_FILE, O_RDONLY); - if (random_fd < 0) { - perror("Failed to open " RANDOM_DEV_FILE); - exit(1); - } + random_fd = open(RANDOM_DEV_FILE, O_RDONLY); + if (random_fd < 0) { + perror("Failed to open " RANDOM_DEV_FILE); + exit(1); + } } static void setup_flash(void) { - int fd = open(EMULATOR_FLASH_FILE, O_RDWR | O_SYNC | O_CREAT, 0644); - if (fd < 0) { - perror("Failed to open flash emulation file"); - exit(1); - } - - off_t length = lseek(fd, 0, SEEK_END); - if (length < 0) { - perror("Failed to read length of flash emulation file"); - exit(1); - } - - emulator_flash_base = mmap(NULL, FLASH_TOTAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (emulator_flash_base == MAP_FAILED) { - perror("Failed to map flash emulation file"); - exit(1); - } - - if (length < FLASH_TOTAL_SIZE) { - if (ftruncate(fd, FLASH_TOTAL_SIZE) != 0) { - perror("Failed to initialize flash emulation file"); - exit(1); - } - - /* Initialize the flash */ - flash_erase_all_sectors(FLASH_CR_PROGRAM_X32); - } + int fd = open(EMULATOR_FLASH_FILE, O_RDWR | O_SYNC | O_CREAT, 0644); + if (fd < 0) { + perror("Failed to open flash emulation file"); + exit(1); + } + + off_t length = lseek(fd, 0, SEEK_END); + if (length < 0) { + perror("Failed to read length of flash emulation file"); + exit(1); + } + + emulator_flash_base = + mmap(NULL, FLASH_TOTAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (emulator_flash_base == MAP_FAILED) { + perror("Failed to map flash emulation file"); + exit(1); + } + + if (length < FLASH_TOTAL_SIZE) { + if (ftruncate(fd, FLASH_TOTAL_SIZE) != 0) { + perror("Failed to initialize flash emulation file"); + exit(1); + } + + /* Initialize the flash */ + flash_erase_all_sectors(FLASH_CR_PROGRAM_X32); + } } diff --git a/emulator/strl.c b/emulator/strl.c index 26668b80a..da48ac86f 100644 --- a/emulator/strl.c +++ b/emulator/strl.c @@ -24,21 +24,21 @@ #if (!defined __APPLE__) && (!defined HAVE_STRLCPY) size_t strlcpy(char *dst, const char *src, size_t size) { - size_t ret = strlen(src); + size_t ret = strlen(src); - if (size) { - size_t len = MIN(ret, size - 1); - memcpy(dst, src, len); - dst[len] = '\0'; - } + if (size) { + size_t len = MIN(ret, size - 1); + memcpy(dst, src, len); + dst[len] = '\0'; + } - return ret; + return ret; } size_t strlcat(char *dst, const char *src, size_t size) { - size_t n = strnlen(dst, size); + size_t n = strnlen(dst, size); - return n + strlcpy(&dst[n], src, size - n); + return n + strlcpy(&dst[n], src, size - n); } #endif diff --git a/emulator/timer.c b/emulator/timer.c index efb95564b..e994d9613 100644 --- a/emulator/timer.c +++ b/emulator/timer.c @@ -24,9 +24,9 @@ void timer_init(void) {} uint32_t timer_ms(void) { - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); + struct timespec t; + clock_gettime(CLOCK_MONOTONIC, &t); - uint32_t msec = t.tv_sec * 1000 + (t.tv_nsec / 1000000); - return msec; + uint32_t msec = t.tv_sec * 1000 + (t.tv_nsec / 1000000); + return msec; } diff --git a/emulator/udp.c b/emulator/udp.c index b7b18a914..4a8005996 100644 --- a/emulator/udp.c +++ b/emulator/udp.c @@ -27,97 +27,101 @@ #define TREZOR_UDP_PORT 21324 struct usb_socket { - int fd; - struct sockaddr_in from; - socklen_t fromlen; + int fd; + struct sockaddr_in from; + socklen_t fromlen; }; static struct usb_socket usb_main; static struct usb_socket usb_debug; static int socket_setup(int port) { - int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) { - perror("Failed to create socket"); - exit(1); - } - - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0) { - perror("Failed to bind socket"); - exit(1); - } - - return fd; + int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (fd < 0) { + perror("Failed to create socket"); + exit(1); + } + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + perror("Failed to bind socket"); + exit(1); + } + + return fd; } -static size_t socket_write(struct usb_socket *sock, const void *buffer, size_t size) { - if (sock->fromlen > 0) { - ssize_t n = sendto(sock->fd, buffer, size, MSG_DONTWAIT, (const struct sockaddr *) &sock->from, sock->fromlen); - if (n < 0 || ((size_t) n) != size) { - perror("Failed to write socket"); - return 0; - } - } - - return size; +static size_t socket_write(struct usb_socket *sock, const void *buffer, + size_t size) { + if (sock->fromlen > 0) { + ssize_t n = sendto(sock->fd, buffer, size, MSG_DONTWAIT, + (const struct sockaddr *)&sock->from, sock->fromlen); + if (n < 0 || ((size_t)n) != size) { + perror("Failed to write socket"); + return 0; + } + } + + return size; } static size_t socket_read(struct usb_socket *sock, void *buffer, size_t size) { - sock->fromlen = sizeof(sock->from); - ssize_t n = recvfrom(sock->fd, buffer, size, MSG_DONTWAIT, (struct sockaddr *) &sock->from, &sock->fromlen); - - if (n < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - perror("Failed to read socket"); - } - return 0; - } - - static const char msg_ping[] = { 'P', 'I', 'N', 'G', 'P', 'I', 'N', 'G' }; - static const char msg_pong[] = { 'P', 'O', 'N', 'G', 'P', 'O', 'N', 'G' }; - - if (n == sizeof(msg_ping) && memcmp(buffer, msg_ping, sizeof(msg_ping)) == 0) { - socket_write(sock, msg_pong, sizeof(msg_pong)); - return 0; - } - - return n; + sock->fromlen = sizeof(sock->from); + ssize_t n = recvfrom(sock->fd, buffer, size, MSG_DONTWAIT, + (struct sockaddr *)&sock->from, &sock->fromlen); + + if (n < 0) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { + perror("Failed to read socket"); + } + return 0; + } + + static const char msg_ping[] = {'P', 'I', 'N', 'G', 'P', 'I', 'N', 'G'}; + static const char msg_pong[] = {'P', 'O', 'N', 'G', 'P', 'O', 'N', 'G'}; + + if (n == sizeof(msg_ping) && + memcmp(buffer, msg_ping, sizeof(msg_ping)) == 0) { + socket_write(sock, msg_pong, sizeof(msg_pong)); + return 0; + } + + return n; } void emulatorSocketInit(void) { - usb_main.fd = socket_setup(TREZOR_UDP_PORT); - usb_main.fromlen = 0; - usb_debug.fd = socket_setup(TREZOR_UDP_PORT + 1); - usb_debug.fromlen = 0; + usb_main.fd = socket_setup(TREZOR_UDP_PORT); + usb_main.fromlen = 0; + usb_debug.fd = socket_setup(TREZOR_UDP_PORT + 1); + usb_debug.fromlen = 0; } size_t emulatorSocketRead(int *iface, void *buffer, size_t size) { - size_t n = socket_read(&usb_main, buffer, size); - if (n > 0) { - *iface = 0; - return n; - } - - n = socket_read(&usb_debug, buffer, size); - if (n > 0) { - *iface = 1; - return n; - } - - return 0; + size_t n = socket_read(&usb_main, buffer, size); + if (n > 0) { + *iface = 0; + return n; + } + + n = socket_read(&usb_debug, buffer, size); + if (n > 0) { + *iface = 1; + return n; + } + + return 0; } size_t emulatorSocketWrite(int iface, const void *buffer, size_t size) { - if (iface == 0) { - return socket_write(&usb_main, buffer, size); - } - if (iface == 1) { - return socket_write(&usb_debug, buffer, size); - } - return 0; + if (iface == 0) { + return socket_write(&usb_main, buffer, size); + } + if (iface == 1) { + return socket_write(&usb_debug, buffer, size); + } + return 0; } diff --git a/firmware/bl_check.c b/firmware/bl_check.c index 45238a5de..68728902f 100644 --- a/firmware/bl_check.c +++ b/firmware/bl_check.c @@ -17,91 +17,176 @@ * along with this library. If not, see . */ +#include #include #include -#include #include "bl_data.h" -#include "memory.h" -#include "layout.h" #include "gettext.h" +#include "layout.h" +#include "memory.h" #include "util.h" static int known_bootloader(int r, const uint8_t *hash) { - if (r != 32) return 0; - if (0 == memcmp(hash, "\xbf\x72\xe2\x5e\x2c\x2f\xc1\xba\x57\x04\x50\xfa\xdf\xb6\x6f\xaa\x5a\x71\x6d\xcd\xc0\x33\x35\x88\x55\x7b\x77\x54\x0a\xb8\x7e\x98", 32)) return 1; // 1.2.0a - if (0 == memcmp(hash, "\x77\xb8\xe2\xf2\x5f\xaa\x8e\x8c\x7d\x9f\x5b\x32\x3b\x27\xce\x05\x6c\xa3\xdb\xc2\x3f\x56\xc3\x7e\xe3\x3f\x97\x7c\xa6\xeb\x4d\x3e", 32)) return 1; // 1.2.0b - if (0 == memcmp(hash, "\xc4\xc3\x25\x39\xb4\xa0\x25\xa8\xe7\x53\xa4\xc4\x62\x64\x28\x59\x11\xa4\x5f\xcb\x14\xf4\x71\x81\x79\xe7\x11\xb1\xce\x99\x05\x24", 32)) return 1; // 1.2.5 - if (0 == memcmp(hash, "\x42\x59\x66\x94\xa0\xf2\x9d\x1e\xc2\x35\x71\x29\x2d\x54\x39\xd8\x2f\xa1\x8c\x07\x37\xcb\x10\x7e\x98\xf6\x1e\xf5\x93\x4d\xe7\x16", 32)) return 1; // 1.3.0a - if (0 == memcmp(hash, "\x3a\xcf\x2e\x51\x0b\x0f\xe1\x56\xb5\x58\xbb\xf7\x9c\x7e\x48\x5e\xb0\x26\xe5\xe0\x8c\xb4\x4d\x15\x2d\x44\xd6\x4e\x0c\x6a\x41\x37", 32)) return 1; // 1.3.0b - if (0 == memcmp(hash, "\x15\x85\x21\x5b\xc6\xe5\x5a\x34\x07\xa8\xb3\xee\xe2\x79\x03\x4e\x95\xb9\xc4\x34\x00\x33\xe1\xb6\xae\x16\x0c\xe6\x61\x19\x67\x15", 32)) return 1; // 1.3.1 - if (0 == memcmp(hash, "\x76\x51\xb7\xca\xba\x5a\xae\x0c\xc1\xc6\x5c\x83\x04\xf7\x60\x39\x6f\x77\x60\x6c\xd3\x99\x0c\x99\x15\x98\xf0\xe2\x2a\x81\xe0\x07", 32)) return 1; // 1.3.2 - // note to those verifying these values: bootloader versions above this comment are aligned/padded to 32KiB with trailing 0xFF bytes and versions below are padded with 0x00. - // for more info, refer to "make -C bootloader align" and "firmware/bl_data.py". - if (0 == memcmp(hash, "\x8c\xe8\xd7\x9e\xdf\x43\x0c\x03\x42\x64\x68\x6c\xa9\xb1\xd7\x8d\x26\xed\xb2\xac\xab\x71\x39\xbe\x8f\x98\x5c\x2a\x3c\x6c\xae\x11", 32)) return 1; // 1.3.3 - if (0 == memcmp(hash, "\x63\x30\xfc\xec\x16\x72\xfa\xd3\x0b\x42\x1b\x60\xf7\x4f\x83\x9a\x39\x39\x33\x45\x65\xcb\x70\x3b\x2b\xd7\x18\x2e\xa2\xdd\xa0\x19", 32)) return 1; // 1.4.0 shipped with fw 1.6.1 - if (0 == memcmp(hash, "\xaf\xb4\xcf\x7a\x4a\x57\x96\x10\x0e\xd5\x41\x6b\x75\x12\x1b\xc7\x10\x08\xc2\xa2\xfd\x54\x49\xbd\x8f\x63\xcc\x22\xa6\xa7\xd6\x80", 32)) return 1; // 1.5.0 shipped with fw 1.6.2 - if (0 == memcmp(hash, "\x51\x12\x90\xa8\x72\x3f\xaf\xe7\x34\x15\x25\x9d\x25\x96\x76\x54\x06\x32\x5c\xe2\x4b\x4b\x80\x03\x2c\x0b\x70\xb0\x5d\x98\x46\xe9", 32)) return 1; // 1.5.1 shipped with fw 1.6.3 - if (0 == memcmp(hash, "\x3e\xc4\xbd\xd5\x77\xea\x0c\x36\xc7\xba\xb7\xb9\xa3\x5b\x87\x17\xb3\xf1\xfc\x2f\x80\x9e\x69\x0c\x8a\xbe\x5b\x05\xfb\xc2\x43\xc6", 32)) return 1; // 1.6.0 shipped with fw 1.7.0 - if (0 == memcmp(hash, "\x8e\x83\x02\x3f\x0d\x4f\x82\x4f\x64\x71\x20\x75\x2b\x6c\x71\x6f\x55\xd7\x95\x70\x66\x8f\xd4\x90\x65\xd5\xb7\x97\x6e\x7a\x6e\x19", 32)) return 1; // 1.6.0 shipped with fw 1.7.1 and 1.7.2 - if (0 == memcmp(hash, "\xa2\x36\x6e\x77\xde\x8e\xfd\xfd\xc9\x99\xf4\x72\x20\xc0\x16\xe3\x3f\x6d\x24\x24\xe2\x45\x90\x79\x11\x7a\x90\xb3\xa8\x88\xba\xdd", 32)) return 1; // 1.6.1 shipped with fw 1.7.3 - if (0 == memcmp(hash, "\xf7\xfa\x16\x5b\xe6\xd7\x80\xf3\xe1\xaf\x00\xab\xc0\x7d\xf8\xb3\x07\x6b\xcd\xad\x72\xd7\x0d\xa2\x2a\x63\xd8\x89\x6b\x63\x91\xd8", 32)) return 1; // 1.8.0 shipped with fw 1.8.0 - return 0; + if (r != 32) return 0; + if (0 == + memcmp(hash, + "\xbf\x72\xe2\x5e\x2c\x2f\xc1\xba\x57\x04\x50\xfa\xdf\xb6\x6f\xaa" + "\x5a\x71\x6d\xcd\xc0\x33\x35\x88\x55\x7b\x77\x54\x0a\xb8\x7e\x98", + 32)) + return 1; // 1.2.0a + if (0 == + memcmp(hash, + "\x77\xb8\xe2\xf2\x5f\xaa\x8e\x8c\x7d\x9f\x5b\x32\x3b\x27\xce\x05" + "\x6c\xa3\xdb\xc2\x3f\x56\xc3\x7e\xe3\x3f\x97\x7c\xa6\xeb\x4d\x3e", + 32)) + return 1; // 1.2.0b + if (0 == + memcmp(hash, + "\xc4\xc3\x25\x39\xb4\xa0\x25\xa8\xe7\x53\xa4\xc4\x62\x64\x28\x59" + "\x11\xa4\x5f\xcb\x14\xf4\x71\x81\x79\xe7\x11\xb1\xce\x99\x05\x24", + 32)) + return 1; // 1.2.5 + if (0 == + memcmp(hash, + "\x42\x59\x66\x94\xa0\xf2\x9d\x1e\xc2\x35\x71\x29\x2d\x54\x39\xd8" + "\x2f\xa1\x8c\x07\x37\xcb\x10\x7e\x98\xf6\x1e\xf5\x93\x4d\xe7\x16", + 32)) + return 1; // 1.3.0a + if (0 == + memcmp(hash, + "\x3a\xcf\x2e\x51\x0b\x0f\xe1\x56\xb5\x58\xbb\xf7\x9c\x7e\x48\x5e" + "\xb0\x26\xe5\xe0\x8c\xb4\x4d\x15\x2d\x44\xd6\x4e\x0c\x6a\x41\x37", + 32)) + return 1; // 1.3.0b + if (0 == + memcmp(hash, + "\x15\x85\x21\x5b\xc6\xe5\x5a\x34\x07\xa8\xb3\xee\xe2\x79\x03\x4e" + "\x95\xb9\xc4\x34\x00\x33\xe1\xb6\xae\x16\x0c\xe6\x61\x19\x67\x15", + 32)) + return 1; // 1.3.1 + if (0 == + memcmp(hash, + "\x76\x51\xb7\xca\xba\x5a\xae\x0c\xc1\xc6\x5c\x83\x04\xf7\x60\x39" + "\x6f\x77\x60\x6c\xd3\x99\x0c\x99\x15\x98\xf0\xe2\x2a\x81\xe0\x07", + 32)) + return 1; // 1.3.2 + // note to those verifying these values: bootloader versions above this + // comment are aligned/padded to 32KiB with trailing 0xFF bytes and versions + // below are padded with 0x00. + // for more info, refer to "make -C + // bootloader align" and + // "firmware/bl_data.py". + if (0 == + memcmp(hash, + "\x8c\xe8\xd7\x9e\xdf\x43\x0c\x03\x42\x64\x68\x6c\xa9\xb1\xd7\x8d" + "\x26\xed\xb2\xac\xab\x71\x39\xbe\x8f\x98\x5c\x2a\x3c\x6c\xae\x11", + 32)) + return 1; // 1.3.3 + if (0 == + memcmp(hash, + "\x63\x30\xfc\xec\x16\x72\xfa\xd3\x0b\x42\x1b\x60\xf7\x4f\x83\x9a" + "\x39\x39\x33\x45\x65\xcb\x70\x3b\x2b\xd7\x18\x2e\xa2\xdd\xa0\x19", + 32)) + return 1; // 1.4.0 shipped with fw 1.6.1 + if (0 == + memcmp(hash, + "\xaf\xb4\xcf\x7a\x4a\x57\x96\x10\x0e\xd5\x41\x6b\x75\x12\x1b\xc7" + "\x10\x08\xc2\xa2\xfd\x54\x49\xbd\x8f\x63\xcc\x22\xa6\xa7\xd6\x80", + 32)) + return 1; // 1.5.0 shipped with fw 1.6.2 + if (0 == + memcmp(hash, + "\x51\x12\x90\xa8\x72\x3f\xaf\xe7\x34\x15\x25\x9d\x25\x96\x76\x54" + "\x06\x32\x5c\xe2\x4b\x4b\x80\x03\x2c\x0b\x70\xb0\x5d\x98\x46\xe9", + 32)) + return 1; // 1.5.1 shipped with fw 1.6.3 + if (0 == + memcmp(hash, + "\x3e\xc4\xbd\xd5\x77\xea\x0c\x36\xc7\xba\xb7\xb9\xa3\x5b\x87\x17" + "\xb3\xf1\xfc\x2f\x80\x9e\x69\x0c\x8a\xbe\x5b\x05\xfb\xc2\x43\xc6", + 32)) + return 1; // 1.6.0 shipped with fw 1.7.0 + if (0 == + memcmp(hash, + "\x8e\x83\x02\x3f\x0d\x4f\x82\x4f\x64\x71\x20\x75\x2b\x6c\x71\x6f" + "\x55\xd7\x95\x70\x66\x8f\xd4\x90\x65\xd5\xb7\x97\x6e\x7a\x6e\x19", + 32)) + return 1; // 1.6.0 shipped with fw 1.7.1 and 1.7.2 + if (0 == + memcmp(hash, + "\xa2\x36\x6e\x77\xde\x8e\xfd\xfd\xc9\x99\xf4\x72\x20\xc0\x16\xe3" + "\x3f\x6d\x24\x24\xe2\x45\x90\x79\x11\x7a\x90\xb3\xa8\x88\xba\xdd", + 32)) + return 1; // 1.6.1 shipped with fw 1.7.3 + if (0 == + memcmp(hash, + "\xf7\xfa\x16\x5b\xe6\xd7\x80\xf3\xe1\xaf\x00\xab\xc0\x7d\xf8\xb3" + "\x07\x6b\xcd\xad\x72\xd7\x0d\xa2\x2a\x63\xd8\x89\x6b\x63\x91\xd8", + 32)) + return 1; // 1.8.0 shipped with fw 1.8.0 + return 0; } -void check_bootloader(void) -{ +void check_bootloader(void) { #if MEMORY_PROTECT - uint8_t hash[32]; - int r = memory_bootloader_hash(hash); + uint8_t hash[32]; + int r = memory_bootloader_hash(hash); - if (!known_bootloader(r, hash)) { - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Unknown bootloader"), _("detected."), NULL, _("Unplug your TREZOR"), _("contact our support."), NULL); - shutdown(); - } + if (!known_bootloader(r, hash)) { + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Unknown bootloader"), + _("detected."), NULL, _("Unplug your TREZOR"), + _("contact our support."), NULL); + shutdown(); + } - if (is_mode_unprivileged()) { - return; - } + if (is_mode_unprivileged()) { + return; + } - if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) { - // all OK -> done - return; - } + if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) { + // all OK -> done + return; + } - // ENABLE THIS AT YOUR OWN RISK - // ATTEMPTING TO OVERWRITE BOOTLOADER WITH UNSIGNED FIRMWARE MAY BRICK - // YOUR DEVICE. + // ENABLE THIS AT YOUR OWN RISK + // ATTEMPTING TO OVERWRITE BOOTLOADER WITH UNSIGNED FIRMWARE MAY BRICK + // YOUR DEVICE. - layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, _("Updating bootloader"), NULL, NULL, _("DO NOT UNPLUG"), _("YOUR TREZOR!"), NULL); + layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, _("Updating bootloader"), + NULL, NULL, _("DO NOT UNPLUG"), _("YOUR TREZOR!"), NULL); - // unlock sectors - memory_write_unlock(); + // unlock sectors + memory_write_unlock(); - for (int tries = 0; tries < 10; tries++) { - // replace bootloader - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); - for (int i = FLASH_BOOT_SECTOR_FIRST; i <= FLASH_BOOT_SECTOR_LAST; i++) { - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - for (int i = 0; i < FLASH_BOOT_LEN / 4; i++) { - const uint32_t *w = (const uint32_t *)(bl_data + i * 4); - flash_program_word(FLASH_BOOT_START + i * 4, *w); - } - flash_wait_for_last_operation(); - flash_lock(); - // check whether the write was OK - r = memory_bootloader_hash(hash); - if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) { - // OK -> show info and halt - layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Update finished"), _("successfully."), NULL, _("Please reconnect"), _("the device."), NULL); - shutdown(); - return; - } - } - // show info and halt - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Bootloader update"), _("broken."), NULL, _("Unplug your TREZOR"), _("contact our support."), NULL); - shutdown(); + for (int tries = 0; tries < 10; tries++) { + // replace bootloader + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); + for (int i = FLASH_BOOT_SECTOR_FIRST; i <= FLASH_BOOT_SECTOR_LAST; i++) { + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + for (int i = 0; i < FLASH_BOOT_LEN / 4; i++) { + const uint32_t *w = (const uint32_t *)(bl_data + i * 4); + flash_program_word(FLASH_BOOT_START + i * 4, *w); + } + flash_wait_for_last_operation(); + flash_lock(); + // check whether the write was OK + r = memory_bootloader_hash(hash); + if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) { + // OK -> show info and halt + layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Update finished"), + _("successfully."), NULL, _("Please reconnect"), + _("the device."), NULL); + shutdown(); + return; + } + } + // show info and halt + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Bootloader update"), + _("broken."), NULL, _("Unplug your TREZOR"), + _("contact our support."), NULL); + shutdown(); #endif } diff --git a/firmware/coins.c b/firmware/coins.c index a53155fbd..55ae24388 100644 --- a/firmware/coins.c +++ b/firmware/coins.c @@ -17,64 +17,64 @@ * along with this library. If not, see . */ -#include #include "coins.h" +#include #include "address.h" -#include "ecdsa.h" #include "base58.h" +#include "ecdsa.h" -const CoinInfo *coinByName(const char *name) -{ - if (!name) return 0; - for (int i = 0; i < COINS_COUNT; i++) { - if (strcmp(name, coins[i].coin_name) == 0) { - return &(coins[i]); - } - } - return 0; +const CoinInfo *coinByName(const char *name) { + if (!name) return 0; + for (int i = 0; i < COINS_COUNT; i++) { + if (strcmp(name, coins[i].coin_name) == 0) { + return &(coins[i]); + } + } + return 0; } -const CoinInfo *coinByAddressType(uint32_t address_type) -{ - for (int i = 0; i < COINS_COUNT; i++) { - if (address_type == coins[i].address_type) { - return &(coins[i]); - } - } - return 0; +const CoinInfo *coinByAddressType(uint32_t address_type) { + for (int i = 0; i < COINS_COUNT; i++) { + if (address_type == coins[i].address_type) { + return &(coins[i]); + } + } + return 0; } -const CoinInfo *coinBySlip44(uint32_t coin_type) -{ - for (int i = 0; i < COINS_COUNT; i++) { - if (coin_type == coins[i].coin_type) { - return &(coins[i]); - } - } - return 0; +const CoinInfo *coinBySlip44(uint32_t coin_type) { + for (int i = 0; i < COINS_COUNT; i++) { + if (coin_type == coins[i].coin_type) { + return &(coins[i]); + } + } + return 0; } -bool coinExtractAddressType(const CoinInfo *coin, const char *addr, uint32_t *address_type) -{ - if (!addr) return false; - uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - int len = base58_decode_check(addr, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); - if (len >= 21) { - return coinExtractAddressTypeRaw(coin, addr_raw, address_type); - } - return false; +bool coinExtractAddressType(const CoinInfo *coin, const char *addr, + uint32_t *address_type) { + if (!addr) return false; + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + int len = base58_decode_check(addr, coin->curve->hasher_base58, addr_raw, + MAX_ADDR_RAW_SIZE); + if (len >= 21) { + return coinExtractAddressTypeRaw(coin, addr_raw, address_type); + } + return false; } -bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw, uint32_t *address_type) -{ - if (coin->has_address_type && address_check_prefix(addr_raw, coin->address_type)) { - *address_type = coin->address_type; - return true; - } - if (coin->has_address_type_p2sh && address_check_prefix(addr_raw, coin->address_type_p2sh)) { - *address_type = coin->address_type_p2sh; - return true; - } - *address_type = 0; - return false; +bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw, + uint32_t *address_type) { + if (coin->has_address_type && + address_check_prefix(addr_raw, coin->address_type)) { + *address_type = coin->address_type; + return true; + } + if (coin->has_address_type_p2sh && + address_check_prefix(addr_raw, coin->address_type_p2sh)) { + *address_type = coin->address_type_p2sh; + return true; + } + *address_type = 0; + return false; } diff --git a/firmware/coins.h b/firmware/coins.h index 96ed77a05..166cc47fc 100644 --- a/firmware/coins.h +++ b/firmware/coins.h @@ -20,35 +20,35 @@ #ifndef __COINS_H__ #define __COINS_H__ -#include #include +#include #include "bip32.h" #include "hasher.h" typedef struct _CoinInfo { - const char *coin_name; - const char *coin_shortcut; - uint64_t maxfee_kb; - const char *signed_message_header; - bool has_address_type; - bool has_address_type_p2sh; - bool has_segwit; - bool has_fork_id; - bool force_bip143; - bool decred; - // address types > 0xFF represent a two-byte prefix in big-endian order - uint32_t address_type; - uint32_t address_type_p2sh; - uint32_t xpub_magic; - uint32_t xpub_magic_segwit_p2sh; - uint32_t xpub_magic_segwit_native; - uint32_t fork_id; - const char *bech32_prefix; - const char *cashaddr_prefix; - uint32_t coin_type; - const char *curve_name; - const curve_info *curve; + const char *coin_name; + const char *coin_shortcut; + uint64_t maxfee_kb; + const char *signed_message_header; + bool has_address_type; + bool has_address_type_p2sh; + bool has_segwit; + bool has_fork_id; + bool force_bip143; + bool decred; + // address types > 0xFF represent a two-byte prefix in big-endian order + uint32_t address_type; + uint32_t address_type_p2sh; + uint32_t xpub_magic; + uint32_t xpub_magic_segwit_p2sh; + uint32_t xpub_magic_segwit_native; + uint32_t fork_id; + const char *bech32_prefix; + const char *cashaddr_prefix; + uint32_t coin_type; + const char *curve_name; + const curve_info *curve; } CoinInfo; #include "coin_info.h" @@ -56,7 +56,9 @@ typedef struct _CoinInfo { const CoinInfo *coinByName(const char *name); const CoinInfo *coinByAddressType(uint32_t address_type); const CoinInfo *coinBySlip44(uint32_t coin_type); -bool coinExtractAddressType(const CoinInfo *coin, const char *addr, uint32_t *address_type); -bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw, uint32_t *address_type); +bool coinExtractAddressType(const CoinInfo *coin, const char *addr, + uint32_t *address_type); +bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw, + uint32_t *address_type); #endif diff --git a/firmware/config.c b/firmware/config.c index 22293cdb9..16cac6e3c 100644 --- a/firmware/config.c +++ b/firmware/config.c @@ -17,66 +17,67 @@ * along with this library. If not, see . */ -#include -#include #include +#include +#include #include "messages.pb.h" -#include "common.h" -#include "trezor.h" -#include "sha2.h" #include "aes/aes.h" -#include "pbkdf2.h" -#include "hmac.h" #include "bip32.h" #include "bip39.h" -#include "curves.h" -#include "util.h" -#include "memory.h" -#include "rng.h" +#include "common.h" #include "config.h" +#include "curves.h" #include "debug.h" -#include "protect.h" -#include "layout2.h" -#include "usb.h" #include "gettext.h" -#include "u2f.h" +#include "hmac.h" +#include "layout2.h" +#include "memory.h" #include "memzero.h" -#include "supervise.h" +#include "pbkdf2.h" +#include "protect.h" +#include "rng.h" +#include "sha2.h" #include "storage.h" +#include "supervise.h" +#include "trezor.h" +#include "u2f.h" +#include "usb.h" +#include "util.h" -/* Magic constants to check validity of storage block for storage versions 1 to 10. */ -static const uint32_t CONFIG_MAGIC_V10 = 0x726f7473; // 'stor' as uint32_t +/* Magic constants to check validity of storage block for storage versions 1 + * to 10. */ +static const uint32_t CONFIG_MAGIC_V10 = 0x726f7473; // 'stor' as uint32_t #if !EMULATOR -static const uint32_t META_MAGIC_V10 = 0x525a5254; // 'TRZR' as uint32_t +static const uint32_t META_MAGIC_V10 = 0x525a5254; // 'TRZR' as uint32_t #else -static const uint32_t META_MAGIC_V10 = 0xFFFFFFFF; +static const uint32_t META_MAGIC_V10 = 0xFFFFFFFF; #endif -#define APP 0x0100 +#define APP 0x0100 #define FLAG_PUBLIC 0x8000 #define FLAGS_WRITE 0xC000 -#define KEY_UUID ( 0 | APP | FLAG_PUBLIC) // bytes(12) -#define KEY_VERSION ( 1 | APP) // uint32 -#define KEY_MNEMONIC ( 2 | APP) // string(241) -#define KEY_LANGUAGE ( 3 | APP | FLAG_PUBLIC) // string(17) -#define KEY_LABEL ( 4 | APP | FLAG_PUBLIC) // string(33) -#define KEY_PASSPHRASE_PROTECTION ( 5 | APP | FLAG_PUBLIC) // bool -#define KEY_HOMESCREEN ( 6 | APP | FLAG_PUBLIC) // bytes(1024) -#define KEY_NEEDS_BACKUP ( 7 | APP) // bool -#define KEY_FLAGS ( 8 | APP) // uint32 -#define KEY_U2F_COUNTER ( 9 | APP | FLAGS_WRITE) // uint32 -#define KEY_UNFINISHED_BACKUP ( 11 | APP) // bool -#define KEY_AUTO_LOCK_DELAY_MS ( 12 | APP) // uint32 -#define KEY_NO_BACKUP ( 13 | APP) // bool -#define KEY_INITIALIZED ( 14 | APP | FLAG_PUBLIC) // uint32 -#define KEY_NODE ( 15 | APP) // node -#define KEY_IMPORTED ( 16 | APP) // bool -#define KEY_U2F_ROOT ( 17 | APP | FLAG_PUBLIC) // node -#define KEY_DEBUG_LINK_PIN (255 | APP | FLAG_PUBLIC) // string(10) +#define KEY_UUID (0 | APP | FLAG_PUBLIC) // bytes(12) +#define KEY_VERSION (1 | APP) // uint32 +#define KEY_MNEMONIC (2 | APP) // string(241) +#define KEY_LANGUAGE (3 | APP | FLAG_PUBLIC) // string(17) +#define KEY_LABEL (4 | APP | FLAG_PUBLIC) // string(33) +#define KEY_PASSPHRASE_PROTECTION (5 | APP | FLAG_PUBLIC) // bool +#define KEY_HOMESCREEN (6 | APP | FLAG_PUBLIC) // bytes(1024) +#define KEY_NEEDS_BACKUP (7 | APP) // bool +#define KEY_FLAGS (8 | APP) // uint32 +#define KEY_U2F_COUNTER (9 | APP | FLAGS_WRITE) // uint32 +#define KEY_UNFINISHED_BACKUP (11 | APP) // bool +#define KEY_AUTO_LOCK_DELAY_MS (12 | APP) // uint32 +#define KEY_NO_BACKUP (13 | APP) // bool +#define KEY_INITIALIZED (14 | APP | FLAG_PUBLIC) // uint32 +#define KEY_NODE (15 | APP) // node +#define KEY_IMPORTED (16 | APP) // bool +#define KEY_U2F_ROOT (17 | APP | FLAG_PUBLIC) // node +#define KEY_DEBUG_LINK_PIN (255 | APP | FLAG_PUBLIC) // string(10) // The PIN value corresponding to an empty PIN. static const uint32_t PIN_EMPTY = 1; @@ -84,7 +85,7 @@ static const uint32_t PIN_EMPTY = 1; static uint32_t config_uuid[UUID_SIZE / sizeof(uint32_t)]; _Static_assert(sizeof(config_uuid) == UUID_SIZE, "config_uuid has wrong size"); -char config_uuid_str[2*UUID_SIZE + 1]; +char config_uuid_str[2 * UUID_SIZE + 1]; /* Old storage layout: @@ -103,7 +104,7 @@ The area for pin failures looks like this: 0 ... 0 pinfail 0xffffffff .. 0xffffffff The pinfail is a binary number of the form 1...10...0, the number of zeros is the number of pin failures. -This layout is used because we can only clear bits without +This layout is used because we can only clear bits without erasing the flash. The area for u2f counter updates is just a sequence of zero-bits @@ -123,7 +124,7 @@ static uint8_t CONFIDENTIAL sessionSeed[64]; static secbool sessionPassphraseCached = secfalse; static char CONFIDENTIAL sessionPassphrase[51]; -#define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes +#define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes static secbool autoLockDelayMsCached = secfalse; static uint32_t autoLockDelayMs = autoLockDelayMsDefault; @@ -132,818 +133,805 @@ static const uint32_t CONFIG_VERSION = 11; static const uint8_t FALSE_BYTE = '\x00'; static const uint8_t TRUE_BYTE = '\x01'; -static uint32_t pin_to_int(const char *pin) -{ - uint32_t val = 1; - size_t i = 0; - for (i = 0; i < MAX_PIN_LEN && pin[i] != '\0'; ++i) { - if (pin[i] < '0' || pin[i] > '9') { - return 0; - } - val = 10*val + pin[i] - '0'; - } - - if (pin[i] != '\0') { - return 0; +static uint32_t pin_to_int(const char *pin) { + uint32_t val = 1; + size_t i = 0; + for (i = 0; i < MAX_PIN_LEN && pin[i] != '\0'; ++i) { + if (pin[i] < '0' || pin[i] > '9') { + return 0; } + val = 10 * val + pin[i] - '0'; + } - return val; -} - -static secbool config_set_bool(uint16_t key, bool value) -{ - if (value) { - return storage_set(key, &TRUE_BYTE, sizeof(TRUE_BYTE)); - } else { - return storage_set(key, &FALSE_BYTE, sizeof(FALSE_BYTE)); - } -} - -static secbool config_get_bool(uint16_t key, bool *value) -{ - uint8_t val = 0; - uint16_t len = 0; - if (sectrue == storage_get(key, &val, sizeof(val), &len) && len == sizeof(TRUE_BYTE)) { - *value = (val == TRUE_BYTE); - return sectrue; - } else { - *value = false; - return secfalse; - } -} - -static secbool config_get_bytes(uint16_t key, uint8_t *dest, uint16_t dest_size, uint16_t *real_size) -{ - if (dest_size == 0) { - return secfalse; - } + if (pin[i] != '\0') { + return 0; + } - if (sectrue != storage_get(key, dest, dest_size, real_size)) { - return secfalse; - } - return sectrue; + return val; } -static secbool config_get_string(uint16_t key, char *dest, uint16_t dest_size) -{ - if (dest_size == 0) { - return secfalse; - } - - uint16_t len = 0; - if (sectrue != storage_get(key, dest, dest_size - 1, &len)) { - dest[0] = '\0'; - return secfalse; - } - dest[len] = '\0'; - return sectrue; +static secbool config_set_bool(uint16_t key, bool value) { + if (value) { + return storage_set(key, &TRUE_BYTE, sizeof(TRUE_BYTE)); + } else { + return storage_set(key, &FALSE_BYTE, sizeof(FALSE_BYTE)); + } } -static secbool config_get_uint32(uint16_t key, uint32_t *value) -{ - uint16_t len = 0; - if (sectrue != storage_get(key, value, sizeof(uint32_t), &len) || len != sizeof(uint32_t)) { - *value = 0; - return secfalse; - } +static secbool config_get_bool(uint16_t key, bool *value) { + uint8_t val = 0; + uint16_t len = 0; + if (sectrue == storage_get(key, &val, sizeof(val), &len) && + len == sizeof(TRUE_BYTE)) { + *value = (val == TRUE_BYTE); return sectrue; -} - -#define FLASH_META_START 0x08008000 -#define FLASH_META_LEN 0x100 - -static secbool config_upgrade_v10(void) -{ -#define OLD_STORAGE_SIZE(last_member) (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + 3) & ~3) - - if (memcmp(FLASH_PTR(FLASH_META_START), &META_MAGIC_V10, sizeof(META_MAGIC_V10)) != 0 || - memcmp(FLASH_PTR(FLASH_META_START + FLASH_META_LEN), &CONFIG_MAGIC_V10, sizeof(CONFIG_MAGIC_V10)) != 0) { - // wrong magic - return secfalse; - } - - Storage config __attribute__((aligned(4))); - _Static_assert((sizeof(config) & 3) == 0, "storage unaligned"); - - memcpy(config_uuid, FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10)), sizeof(config_uuid)); - memcpy(&config, FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)), sizeof(config)); - - // version 1: since 1.0.0 - // version 2: since 1.2.1 - // version 3: since 1.3.1 - // version 4: since 1.3.2 - // version 5: since 1.3.3 - // version 6: since 1.3.6 - // version 7: since 1.5.1 - // version 8: since 1.5.2 - // version 9: since 1.6.1 - // version 10: since 1.7.2 - if (config.version > CONFIG_VERSION) { - // downgrade -> clear storage - config_wipe(); - return secfalse; - } - - size_t old_config_size = 0; - if (config.version == 0) { - } else if (config.version <= 2) { - old_config_size = OLD_STORAGE_SIZE(imported); - } else if (config.version <= 5) { - // added homescreen - old_config_size = OLD_STORAGE_SIZE(homescreen); - } else if (config.version <= 7) { - // added u2fcounter - old_config_size = OLD_STORAGE_SIZE(u2f_counter); - } else if (config.version <= 8) { - // added flags and needsBackup - old_config_size = OLD_STORAGE_SIZE(flags); - } else if (config.version <= 9) { - // added u2froot, unfinished_backup and auto_lock_delay_ms - old_config_size = OLD_STORAGE_SIZE(auto_lock_delay_ms); - } else if (config.version <= 10) { - // added no_backup - old_config_size = OLD_STORAGE_SIZE(no_backup); - } - - // Erase newly added fields. - if (old_config_size != sizeof(Storage)) { - memzero((char*)&config + old_config_size, sizeof(Storage) - old_config_size); - } - - const uint32_t FLASH_STORAGE_PINAREA = FLASH_META_START + 0x4000; - uint32_t pin_wait = 0; - if (config.version <= 5) { - // Get PIN failure counter from version 5 format. - uint32_t pinctr = config.has_pin_failed_attempts ? config.pin_failed_attempts : 0; - if (pinctr > 31) { - pinctr = 31; - } - - pin_wait = (1 << pinctr) - 1; - } else { - // Get PIN failure counter from version 10 format. - uint32_t flash_pinfails = FLASH_STORAGE_PINAREA; - while (*(const uint32_t*)FLASH_PTR(flash_pinfails) == 0) { - flash_pinfails += sizeof(uint32_t); - } - pin_wait = ~*(const uint32_t*)FLASH_PTR(flash_pinfails); - } - - uint32_t u2f_offset = 0; - if (config.has_u2f_counter) { - const uint32_t FLASH_STORAGE_U2FAREA = FLASH_STORAGE_PINAREA + 0x1000; - const uint32_t *u2fptr = (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA); - while (*u2fptr == 0) { - u2fptr++; - } - u2f_offset = 32 * (u2fptr - (const uint32_t*) FLASH_PTR(FLASH_STORAGE_U2FAREA)); - uint32_t u2fword = *u2fptr; - while ((u2fword & 1) == 0) { - u2f_offset++; - u2fword >>= 1; - } - } - - storage_init(NULL, HW_ENTROPY_DATA, HW_ENTROPY_LEN); + } else { + *value = false; + return secfalse; + } +} + +static secbool config_get_bytes(uint16_t key, uint8_t *dest, uint16_t dest_size, + uint16_t *real_size) { + if (dest_size == 0) { + return secfalse; + } + + if (sectrue != storage_get(key, dest, dest_size, real_size)) { + return secfalse; + } + return sectrue; +} + +static secbool config_get_string(uint16_t key, char *dest, uint16_t dest_size) { + if (dest_size == 0) { + return secfalse; + } + + uint16_t len = 0; + if (sectrue != storage_get(key, dest, dest_size - 1, &len)) { + dest[0] = '\0'; + return secfalse; + } + dest[len] = '\0'; + return sectrue; +} + +static secbool config_get_uint32(uint16_t key, uint32_t *value) { + uint16_t len = 0; + if (sectrue != storage_get(key, value, sizeof(uint32_t), &len) || + len != sizeof(uint32_t)) { + *value = 0; + return secfalse; + } + return sectrue; +} + +#define FLASH_META_START 0x08008000 +#define FLASH_META_LEN 0x100 + +static secbool config_upgrade_v10(void) { +#define OLD_STORAGE_SIZE(last_member) \ + (((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + \ + 3) & \ + ~3) + + if (memcmp(FLASH_PTR(FLASH_META_START), &META_MAGIC_V10, + sizeof(META_MAGIC_V10)) != 0 || + memcmp(FLASH_PTR(FLASH_META_START + FLASH_META_LEN), &CONFIG_MAGIC_V10, + sizeof(CONFIG_MAGIC_V10)) != 0) { + // wrong magic + return secfalse; + } + + Storage config __attribute__((aligned(4))); + _Static_assert((sizeof(config) & 3) == 0, "storage unaligned"); + + memcpy( + config_uuid, + FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10)), + sizeof(config_uuid)); + memcpy(&config, + FLASH_PTR(FLASH_META_START + FLASH_META_LEN + + sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)), + sizeof(config)); + + // version 1: since 1.0.0 + // version 2: since 1.2.1 + // version 3: since 1.3.1 + // version 4: since 1.3.2 + // version 5: since 1.3.3 + // version 6: since 1.3.6 + // version 7: since 1.5.1 + // version 8: since 1.5.2 + // version 9: since 1.6.1 + // version 10: since 1.7.2 + if (config.version > CONFIG_VERSION) { + // downgrade -> clear storage + config_wipe(); + return secfalse; + } + + size_t old_config_size = 0; + if (config.version == 0) { + } else if (config.version <= 2) { + old_config_size = OLD_STORAGE_SIZE(imported); + } else if (config.version <= 5) { + // added homescreen + old_config_size = OLD_STORAGE_SIZE(homescreen); + } else if (config.version <= 7) { + // added u2fcounter + old_config_size = OLD_STORAGE_SIZE(u2f_counter); + } else if (config.version <= 8) { + // added flags and needsBackup + old_config_size = OLD_STORAGE_SIZE(flags); + } else if (config.version <= 9) { + // added u2froot, unfinished_backup and auto_lock_delay_ms + old_config_size = OLD_STORAGE_SIZE(auto_lock_delay_ms); + } else if (config.version <= 10) { + // added no_backup + old_config_size = OLD_STORAGE_SIZE(no_backup); + } + + // Erase newly added fields. + if (old_config_size != sizeof(Storage)) { + memzero((char *)&config + old_config_size, + sizeof(Storage) - old_config_size); + } + + const uint32_t FLASH_STORAGE_PINAREA = FLASH_META_START + 0x4000; + uint32_t pin_wait = 0; + if (config.version <= 5) { + // Get PIN failure counter from version 5 format. + uint32_t pinctr = + config.has_pin_failed_attempts ? config.pin_failed_attempts : 0; + if (pinctr > 31) { + pinctr = 31; + } + + pin_wait = (1 << pinctr) - 1; + } else { + // Get PIN failure counter from version 10 format. + uint32_t flash_pinfails = FLASH_STORAGE_PINAREA; + while (*(const uint32_t *)FLASH_PTR(flash_pinfails) == 0) { + flash_pinfails += sizeof(uint32_t); + } + pin_wait = ~*(const uint32_t *)FLASH_PTR(flash_pinfails); + } + + uint32_t u2f_offset = 0; + if (config.has_u2f_counter) { + const uint32_t FLASH_STORAGE_U2FAREA = FLASH_STORAGE_PINAREA + 0x1000; + const uint32_t *u2fptr = (const uint32_t *)FLASH_PTR(FLASH_STORAGE_U2FAREA); + while (*u2fptr == 0) { + u2fptr++; + } + u2f_offset = + 32 * (u2fptr - (const uint32_t *)FLASH_PTR(FLASH_STORAGE_U2FAREA)); + uint32_t u2fword = *u2fptr; + while ((u2fword & 1) == 0) { + u2f_offset++; + u2fword >>= 1; + } + } + + storage_init(NULL, HW_ENTROPY_DATA, HW_ENTROPY_LEN); + storage_unlock(PIN_EMPTY); + if (config.has_pin) { + storage_change_pin(PIN_EMPTY, pin_to_int(config.pin)); + } + + while (pin_wait != 0) { + storage_pin_fails_increase(); + pin_wait >>= 1; + } + + storage_set(KEY_UUID, config_uuid, sizeof(config_uuid)); + storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION)); + if (config.has_node) { + if (sectrue == storage_set(KEY_NODE, &config.node, sizeof(config.node))) { + config_set_bool(KEY_INITIALIZED, true); + } + } + if (config.has_mnemonic) { + config_setMnemonic(config.mnemonic); + } + if (config.has_passphrase_protection) { + config_setPassphraseProtection(config.passphrase_protection); + } + if (config.has_language) { + config_setLanguage(config.language); + } + if (config.has_label) { + config_setLabel(config.label); + } + if (config.has_imported) { + config_setImported(config.imported); + } + if (config.has_homescreen) { + config_setHomescreen(config.homescreen.bytes, config.homescreen.size); + } + if (config.has_u2f_counter) { + config_setU2FCounter(config.u2f_counter + u2f_offset); + } + if (config.has_needs_backup) { + config_setNeedsBackup(config.needs_backup); + } + if (config.has_flags) { + config_applyFlags(config.flags); + } + if (config.has_unfinished_backup) { + config_setUnfinishedBackup(config.unfinished_backup); + } + if (config.has_auto_lock_delay_ms) { + config_setAutoLockDelayMs(config.auto_lock_delay_ms); + } + if (config.has_no_backup && config.no_backup) { + config_setNoBackup(); + } + memzero(&config, sizeof(config)); + + session_clear(true); + + return sectrue; +} + +void config_init(void) { + char oldTiny = usbTiny(1); + + config_upgrade_v10(); + + storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN); + memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA)); + + // Auto-unlock storage if no PIN is set. + if (storage_is_unlocked() == secfalse && storage_has_pin() == secfalse) { storage_unlock(PIN_EMPTY); - if (config.has_pin) { - storage_change_pin(PIN_EMPTY, pin_to_int(config.pin)); - } - - while (pin_wait != 0) { - storage_pin_fails_increase(); - pin_wait >>= 1; - } + } + uint16_t len = 0; + // If UUID is not set, then the config is uninitialized. + if (sectrue != + storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) || + len != sizeof(config_uuid)) { + random_buffer((uint8_t *)config_uuid, sizeof(config_uuid)); storage_set(KEY_UUID, config_uuid, sizeof(config_uuid)); storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION)); - if (config.has_node) { - if (sectrue == storage_set(KEY_NODE, &config.node, sizeof(config.node))) { - config_set_bool(KEY_INITIALIZED, true); - } - } - if (config.has_mnemonic) { - config_setMnemonic(config.mnemonic); - } - if (config.has_passphrase_protection) { - config_setPassphraseProtection(config.passphrase_protection); - } - if (config.has_language) { - config_setLanguage(config.language); - } - if (config.has_label) { - config_setLabel(config.label); - } - if (config.has_imported) { - config_setImported(config.imported); - } - if (config.has_homescreen) { - config_setHomescreen(config.homescreen.bytes, config.homescreen.size); - } - if (config.has_u2f_counter) { - config_setU2FCounter(config.u2f_counter + u2f_offset); - } - if (config.has_needs_backup) { - config_setNeedsBackup(config.needs_backup); - } - if (config.has_flags) { - config_applyFlags(config.flags); - } - if (config.has_unfinished_backup) { - config_setUnfinishedBackup(config.unfinished_backup); - } - if (config.has_auto_lock_delay_ms) { - config_setAutoLockDelayMs(config.auto_lock_delay_ms); - } - if (config.has_no_backup && config.no_backup) { - config_setNoBackup(); - } - memzero(&config, sizeof(config)); - - session_clear(true); - - return sectrue; -} - -void config_init(void) -{ - char oldTiny = usbTiny(1); - - config_upgrade_v10(); - - storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN); - memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA)); - - // Auto-unlock storage if no PIN is set. - if (storage_is_unlocked() == secfalse && storage_has_pin() == secfalse) { - storage_unlock(PIN_EMPTY); - } - - uint16_t len = 0; - // If UUID is not set, then the config is uninitialized. - if (sectrue != storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) || len != sizeof(config_uuid)) { - random_buffer((uint8_t *)config_uuid, sizeof(config_uuid)); - storage_set(KEY_UUID, config_uuid, sizeof(config_uuid)); - storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION)); - } - data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); - - usbTiny(oldTiny); -} - -void session_clear(bool lock) -{ - sessionSeedCached = secfalse; - memzero(&sessionSeed, sizeof(sessionSeed)); - sessionPassphraseCached = secfalse; - memzero(&sessionPassphrase, sizeof(sessionPassphrase)); - if (lock) { - storage_lock(); - } -} - -static void get_u2froot_callback(uint32_t iter, uint32_t total) -{ - layoutProgress(_("Updating"), 1000 * iter / total); -} - -static void config_compute_u2froot(const char* mnemonic, StorageHDNode *u2froot) { - static CONFIDENTIAL HDNode node; - char oldTiny = usbTiny(1); - mnemonic_to_seed(mnemonic, "", sessionSeed, get_u2froot_callback); // BIP-0039 - usbTiny(oldTiny); - hdnode_from_seed(sessionSeed, 64, NIST256P1_NAME, &node); - hdnode_private_ckd(&node, U2F_KEY_PATH); - u2froot->depth = node.depth; - u2froot->child_num = U2F_KEY_PATH; - u2froot->chain_code.size = sizeof(node.chain_code); - memcpy(u2froot->chain_code.bytes, node.chain_code, sizeof(node.chain_code)); - u2froot->has_private_key = true; - u2froot->private_key.size = sizeof(node.private_key); - memcpy(u2froot->private_key.bytes, node.private_key, sizeof(node.private_key)); - memzero(&node, sizeof(node)); - session_clear(false); // invalidate seed cache + } + data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); + + usbTiny(oldTiny); +} + +void session_clear(bool lock) { + sessionSeedCached = secfalse; + memzero(&sessionSeed, sizeof(sessionSeed)); + sessionPassphraseCached = secfalse; + memzero(&sessionPassphrase, sizeof(sessionPassphrase)); + if (lock) { + storage_lock(); + } +} + +static void get_u2froot_callback(uint32_t iter, uint32_t total) { + layoutProgress(_("Updating"), 1000 * iter / total); +} + +static void config_compute_u2froot(const char *mnemonic, + StorageHDNode *u2froot) { + static CONFIDENTIAL HDNode node; + char oldTiny = usbTiny(1); + mnemonic_to_seed(mnemonic, "", sessionSeed, + get_u2froot_callback); // BIP-0039 + usbTiny(oldTiny); + hdnode_from_seed(sessionSeed, 64, NIST256P1_NAME, &node); + hdnode_private_ckd(&node, U2F_KEY_PATH); + u2froot->depth = node.depth; + u2froot->child_num = U2F_KEY_PATH; + u2froot->chain_code.size = sizeof(node.chain_code); + memcpy(u2froot->chain_code.bytes, node.chain_code, sizeof(node.chain_code)); + u2froot->has_private_key = true; + u2froot->private_key.size = sizeof(node.private_key); + memcpy(u2froot->private_key.bytes, node.private_key, + sizeof(node.private_key)); + memzero(&node, sizeof(node)); + session_clear(false); // invalidate seed cache } static void config_setNode(const HDNodeType *node) { - StorageHDNode storageHDNode; - memzero(&storageHDNode, sizeof(storageHDNode)); - - storageHDNode.depth = node->depth; - storageHDNode.fingerprint = node->fingerprint; - storageHDNode.child_num = node->child_num; - storageHDNode.chain_code.size = 32; - memcpy(storageHDNode.chain_code.bytes, node->chain_code.bytes, 32); - - if (node->has_private_key) { - storageHDNode.has_private_key = true; - storageHDNode.private_key.size = 32; - memcpy(storageHDNode.private_key.bytes, node->private_key.bytes, 32); - } - if (sectrue == storage_set(KEY_NODE, &storageHDNode, sizeof(storageHDNode))) { - config_set_bool(KEY_INITIALIZED, true); - } - memzero(&storageHDNode, sizeof(storageHDNode)); + StorageHDNode storageHDNode; + memzero(&storageHDNode, sizeof(storageHDNode)); + + storageHDNode.depth = node->depth; + storageHDNode.fingerprint = node->fingerprint; + storageHDNode.child_num = node->child_num; + storageHDNode.chain_code.size = 32; + memcpy(storageHDNode.chain_code.bytes, node->chain_code.bytes, 32); + + if (node->has_private_key) { + storageHDNode.has_private_key = true; + storageHDNode.private_key.size = 32; + memcpy(storageHDNode.private_key.bytes, node->private_key.bytes, 32); + } + if (sectrue == storage_set(KEY_NODE, &storageHDNode, sizeof(storageHDNode))) { + config_set_bool(KEY_INITIALIZED, true); + } + memzero(&storageHDNode, sizeof(storageHDNode)); } #if DEBUG_LINK -bool config_dumpNode(HDNodeType *node) -{ - memzero(node, sizeof(HDNodeType)); - - StorageHDNode storageNode; - uint16_t len = 0; - if (sectrue != storage_get(KEY_NODE, &storageNode, sizeof(storageNode), &len) || len != sizeof(StorageHDNode)) { - memzero(&storageNode, sizeof(storageNode)); - return false; - } +bool config_dumpNode(HDNodeType *node) { + memzero(node, sizeof(HDNodeType)); + + StorageHDNode storageNode; + uint16_t len = 0; + if (sectrue != + storage_get(KEY_NODE, &storageNode, sizeof(storageNode), &len) || + len != sizeof(StorageHDNode)) { + memzero(&storageNode, sizeof(storageNode)); + return false; + } - node->depth = storageNode.depth; - node->fingerprint = storageNode.fingerprint; - node->child_num = storageNode.child_num; + node->depth = storageNode.depth; + node->fingerprint = storageNode.fingerprint; + node->child_num = storageNode.child_num; - node->chain_code.size = 32; - memcpy(node->chain_code.bytes, storageNode.chain_code.bytes, 32); + node->chain_code.size = 32; + memcpy(node->chain_code.bytes, storageNode.chain_code.bytes, 32); - if (storageNode.has_private_key) { - node->has_private_key = true; - node->private_key.size = 32; - memcpy(node->private_key.bytes, storageNode.private_key.bytes, 32); - } + if (storageNode.has_private_key) { + node->has_private_key = true; + node->private_key.size = 32; + memcpy(node->private_key.bytes, storageNode.private_key.bytes, 32); + } - memzero(&storageNode, sizeof(storageNode)); - return true; + memzero(&storageNode, sizeof(storageNode)); + return true; } #endif -void config_loadDevice(const LoadDevice *msg) -{ - session_clear(false); - config_set_bool(KEY_IMPORTED, true); - config_setPassphraseProtection(msg->has_passphrase_protection && msg->passphrase_protection); +void config_loadDevice(const LoadDevice *msg) { + session_clear(false); + config_set_bool(KEY_IMPORTED, true); + config_setPassphraseProtection(msg->has_passphrase_protection && + msg->passphrase_protection); - if (msg->has_pin) { - config_changePin("", msg->pin); - } + if (msg->has_pin) { + config_changePin("", msg->pin); + } - if (msg->has_node) { - storage_delete(KEY_MNEMONIC); - config_setNode(&(msg->node)); - } else if (msg->has_mnemonic) { - storage_delete(KEY_NODE); - config_setMnemonic(msg->mnemonic); - } + if (msg->has_node) { + storage_delete(KEY_MNEMONIC); + config_setNode(&(msg->node)); + } else if (msg->has_mnemonic) { + storage_delete(KEY_NODE); + config_setMnemonic(msg->mnemonic); + } - if (msg->has_language) { - config_setLanguage(msg->language); - } + if (msg->has_language) { + config_setLanguage(msg->language); + } - config_setLabel(msg->has_label ? msg->label : ""); + config_setLabel(msg->has_label ? msg->label : ""); - if (msg->has_u2f_counter) { - config_setU2FCounter(msg->u2f_counter); - } + if (msg->has_u2f_counter) { + config_setU2FCounter(msg->u2f_counter); + } } -void config_setLabel(const char *label) -{ - if (label == NULL || label[0] == '\0') { - storage_delete(KEY_LABEL); - } else { - storage_set(KEY_LABEL, label, strnlen(label, MAX_LABEL_LEN)); - } +void config_setLabel(const char *label) { + if (label == NULL || label[0] == '\0') { + storage_delete(KEY_LABEL); + } else { + storage_set(KEY_LABEL, label, strnlen(label, MAX_LABEL_LEN)); + } } -void config_setLanguage(const char *lang) -{ - if (lang == NULL) { - return; - } +void config_setLanguage(const char *lang) { + if (lang == NULL) { + return; + } - // Sanity check. - if (strcmp(lang, "english") != 0) { - return; - } - storage_set(KEY_LANGUAGE, lang, strnlen(lang, MAX_LANGUAGE_LEN)); + // Sanity check. + if (strcmp(lang, "english") != 0) { + return; + } + storage_set(KEY_LANGUAGE, lang, strnlen(lang, MAX_LANGUAGE_LEN)); } -void config_setPassphraseProtection(bool passphrase_protection) -{ - sessionSeedCached = secfalse; - sessionPassphraseCached = secfalse; - config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection); +void config_setPassphraseProtection(bool passphrase_protection) { + sessionSeedCached = secfalse; + sessionPassphraseCached = secfalse; + config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection); } -bool config_getPassphraseProtection(bool *passphrase_protection) -{ - return sectrue == config_get_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection); +bool config_getPassphraseProtection(bool *passphrase_protection) { + return sectrue == + config_get_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection); } -void config_setHomescreen(const uint8_t *data, uint32_t size) -{ - if (data != NULL && size == HOMESCREEN_SIZE) { - storage_set(KEY_HOMESCREEN, data, size); - } else { - storage_delete(KEY_HOMESCREEN); - } +void config_setHomescreen(const uint8_t *data, uint32_t size) { + if (data != NULL && size == HOMESCREEN_SIZE) { + storage_set(KEY_HOMESCREEN, data, size); + } else { + storage_delete(KEY_HOMESCREEN); + } } -static void get_root_node_callback(uint32_t iter, uint32_t total) -{ - usbSleep(1); - layoutProgress(_("Waking up"), 1000 * iter / total); +static void get_root_node_callback(uint32_t iter, uint32_t total) { + usbSleep(1); + layoutProgress(_("Waking up"), 1000 * iter / total); } -const uint8_t *config_getSeed(bool usePassphrase) -{ - // root node is properly cached - if (usePassphrase == (sectrue == sessionSeedUsesPassphrase) - && sectrue == sessionSeedCached) { - return sessionSeed; - } +const uint8_t *config_getSeed(bool usePassphrase) { + // root node is properly cached + if (usePassphrase == (sectrue == sessionSeedUsesPassphrase) && + sectrue == sessionSeedCached) { + return sessionSeed; + } - // if storage has mnemonic, convert it to node and use it - char mnemonic[MAX_MNEMONIC_LEN + 1]; - if (config_getMnemonic(mnemonic, sizeof(mnemonic))) { - if (usePassphrase && !protectPassphrase()) { - memzero(mnemonic, sizeof(mnemonic)); - return NULL; - } - // if storage was not imported (i.e. it was properly generated or recovered) - bool imported = false; - config_get_bool(KEY_IMPORTED, &imported); - if (!imported) { - // test whether mnemonic is a valid BIP-0039 mnemonic - if (!mnemonic_check(mnemonic)) { - // and if not then halt the device - error_shutdown(_("Storage failure"), _("detected."), NULL, NULL); - } - } - char oldTiny = usbTiny(1); - mnemonic_to_seed(mnemonic, usePassphrase ? sessionPassphrase : "", sessionSeed, get_root_node_callback); // BIP-0039 - memzero(mnemonic, sizeof(mnemonic)); - usbTiny(oldTiny); - sessionSeedCached = sectrue; - sessionSeedUsesPassphrase = usePassphrase ? sectrue : secfalse; - return sessionSeed; + // if storage has mnemonic, convert it to node and use it + char mnemonic[MAX_MNEMONIC_LEN + 1]; + if (config_getMnemonic(mnemonic, sizeof(mnemonic))) { + if (usePassphrase && !protectPassphrase()) { + memzero(mnemonic, sizeof(mnemonic)); + return NULL; + } + // if storage was not imported (i.e. it was properly generated or recovered) + bool imported = false; + config_get_bool(KEY_IMPORTED, &imported); + if (!imported) { + // test whether mnemonic is a valid BIP-0039 mnemonic + if (!mnemonic_check(mnemonic)) { + // and if not then halt the device + error_shutdown(_("Storage failure"), _("detected."), NULL, NULL); + } } + char oldTiny = usbTiny(1); + mnemonic_to_seed(mnemonic, usePassphrase ? sessionPassphrase : "", + sessionSeed, get_root_node_callback); // BIP-0039 + memzero(mnemonic, sizeof(mnemonic)); + usbTiny(oldTiny); + sessionSeedCached = sectrue; + sessionSeedUsesPassphrase = usePassphrase ? sectrue : secfalse; + return sessionSeed; + } - return NULL; + return NULL; } -static bool config_loadNode(const StorageHDNode *node, const char *curve, HDNode *out) { - return hdnode_from_xprv(node->depth, node->child_num, node->chain_code.bytes, node->private_key.bytes, curve, out); +static bool config_loadNode(const StorageHDNode *node, const char *curve, + HDNode *out) { + return hdnode_from_xprv(node->depth, node->child_num, node->chain_code.bytes, + node->private_key.bytes, curve, out); } -bool config_getU2FRoot(HDNode *node) -{ - StorageHDNode u2fNode; - uint16_t len = 0; - if (sectrue != storage_get(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode), &len) || len != sizeof(StorageHDNode)) { - memzero(&u2fNode, sizeof(u2fNode)); - return false; - } - bool ret = config_loadNode(&u2fNode, NIST256P1_NAME, node); +bool config_getU2FRoot(HDNode *node) { + StorageHDNode u2fNode; + uint16_t len = 0; + if (sectrue != storage_get(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode), &len) || + len != sizeof(StorageHDNode)) { memzero(&u2fNode, sizeof(u2fNode)); - return ret; -} - -bool config_getRootNode(HDNode *node, const char *curve, bool usePassphrase) -{ - // if storage has node, decrypt and use it - StorageHDNode storageHDNode; - uint16_t len = 0; - if (strcmp(curve, SECP256K1_NAME) == 0 && sectrue == storage_get(KEY_NODE, &storageHDNode, sizeof(storageHDNode), &len) && len == sizeof(StorageHDNode)) { - if (!protectPassphrase()) { - memzero(&storageHDNode, sizeof(storageHDNode)); - return false; - } - if (!config_loadNode(&storageHDNode, curve, node)) { - memzero(&storageHDNode, sizeof(storageHDNode)); - return false; - } - bool passphrase_protection = false; - config_getPassphraseProtection(&passphrase_protection); - if (passphrase_protection && sectrue == sessionPassphraseCached && sessionPassphrase[0] != '\0') { - // decrypt hd node - uint8_t secret[64]; - PBKDF2_HMAC_SHA512_CTX pctx; - char oldTiny = usbTiny(1); - pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), (const uint8_t *)"TREZORHD", 8, 1); - get_root_node_callback(0, BIP39_PBKDF2_ROUNDS); - for (int i = 0; i < 8; i++) { - pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8); - get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8, BIP39_PBKDF2_ROUNDS); - } - pbkdf2_hmac_sha512_Final(&pctx, secret); - usbTiny(oldTiny); - aes_decrypt_ctx ctx; - aes_decrypt_key256(secret, &ctx); - aes_cbc_decrypt(node->chain_code, node->chain_code, 32, secret + 32, &ctx); - aes_cbc_decrypt(node->private_key, node->private_key, 32, secret + 32, &ctx); - } - return true; + return false; + } + bool ret = config_loadNode(&u2fNode, NIST256P1_NAME, node); + memzero(&u2fNode, sizeof(u2fNode)); + return ret; +} + +bool config_getRootNode(HDNode *node, const char *curve, bool usePassphrase) { + // if storage has node, decrypt and use it + StorageHDNode storageHDNode; + uint16_t len = 0; + if (strcmp(curve, SECP256K1_NAME) == 0 && + sectrue == + storage_get(KEY_NODE, &storageHDNode, sizeof(storageHDNode), &len) && + len == sizeof(StorageHDNode)) { + if (!protectPassphrase()) { + memzero(&storageHDNode, sizeof(storageHDNode)); + return false; + } + if (!config_loadNode(&storageHDNode, curve, node)) { + memzero(&storageHDNode, sizeof(storageHDNode)); + return false; + } + bool passphrase_protection = false; + config_getPassphraseProtection(&passphrase_protection); + if (passphrase_protection && sectrue == sessionPassphraseCached && + sessionPassphrase[0] != '\0') { + // decrypt hd node + uint8_t secret[64]; + PBKDF2_HMAC_SHA512_CTX pctx; + char oldTiny = usbTiny(1); + pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)sessionPassphrase, + strlen(sessionPassphrase), + (const uint8_t *)"TREZORHD", 8, 1); + get_root_node_callback(0, BIP39_PBKDF2_ROUNDS); + for (int i = 0; i < 8; i++) { + pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8); + get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8, + BIP39_PBKDF2_ROUNDS); + } + pbkdf2_hmac_sha512_Final(&pctx, secret); + usbTiny(oldTiny); + aes_decrypt_ctx ctx; + aes_decrypt_key256(secret, &ctx); + aes_cbc_decrypt(node->chain_code, node->chain_code, 32, secret + 32, + &ctx); + aes_cbc_decrypt(node->private_key, node->private_key, 32, secret + 32, + &ctx); } - memzero(&storageHDNode, sizeof(storageHDNode)); + return true; + } + memzero(&storageHDNode, sizeof(storageHDNode)); - const uint8_t *seed = config_getSeed(usePassphrase); - if (seed == NULL) { - return false; - } - - return hdnode_from_seed(seed, 64, curve, node); + const uint8_t *seed = config_getSeed(usePassphrase); + if (seed == NULL) { + return false; + } + + return hdnode_from_seed(seed, 64, curve, node); } -bool config_getLabel(char *dest, uint16_t dest_size) -{ - return sectrue == config_get_string(KEY_LABEL, dest, dest_size); +bool config_getLabel(char *dest, uint16_t dest_size) { + return sectrue == config_get_string(KEY_LABEL, dest, dest_size); } -bool config_getLanguage(char *dest, uint16_t dest_size) -{ - return sectrue == config_get_string(KEY_LANGUAGE, dest, dest_size); +bool config_getLanguage(char *dest, uint16_t dest_size) { + return sectrue == config_get_string(KEY_LANGUAGE, dest, dest_size); } -bool config_getHomescreen(uint8_t *dest, uint16_t dest_size) -{ - uint16_t len = 0; - secbool ret = storage_get(KEY_HOMESCREEN, dest, dest_size, &len); - if (sectrue != ret || len != HOMESCREEN_SIZE) { - return false; - } - return true; +bool config_getHomescreen(uint8_t *dest, uint16_t dest_size) { + uint16_t len = 0; + secbool ret = storage_get(KEY_HOMESCREEN, dest, dest_size, &len); + if (sectrue != ret || len != HOMESCREEN_SIZE) { + return false; + } + return true; } -bool config_setMnemonic(const char *mnemonic) -{ - if (mnemonic == NULL) { - return false; - } +bool config_setMnemonic(const char *mnemonic) { + if (mnemonic == NULL) { + return false; + } - if (sectrue != storage_set(KEY_MNEMONIC, mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN))) { - return false; - } + if (sectrue != storage_set(KEY_MNEMONIC, mnemonic, + strnlen(mnemonic, MAX_MNEMONIC_LEN))) { + return false; + } - StorageHDNode u2fNode; - memzero(&u2fNode, sizeof(u2fNode)); - config_compute_u2froot(mnemonic, &u2fNode); - secbool ret = storage_set(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode)); - memzero(&u2fNode, sizeof(u2fNode)); + StorageHDNode u2fNode; + memzero(&u2fNode, sizeof(u2fNode)); + config_compute_u2froot(mnemonic, &u2fNode); + secbool ret = storage_set(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode)); + memzero(&u2fNode, sizeof(u2fNode)); - if (sectrue != ret) { - storage_delete(KEY_MNEMONIC); - return false; - } + if (sectrue != ret) { + storage_delete(KEY_MNEMONIC); + return false; + } - config_set_bool(KEY_INITIALIZED, true); + config_set_bool(KEY_INITIALIZED, true); - return true; + return true; } -bool config_getMnemonicBytes(uint8_t *dest, uint16_t dest_size, uint16_t *real_size) -{ - return sectrue == config_get_bytes(KEY_MNEMONIC, dest, dest_size, real_size); +bool config_getMnemonicBytes(uint8_t *dest, uint16_t dest_size, + uint16_t *real_size) { + return sectrue == config_get_bytes(KEY_MNEMONIC, dest, dest_size, real_size); } -bool config_getMnemonic(char *dest, uint16_t dest_size) -{ - return sectrue == config_get_string(KEY_MNEMONIC, dest, dest_size); +bool config_getMnemonic(char *dest, uint16_t dest_size) { + return sectrue == config_get_string(KEY_MNEMONIC, dest, dest_size); } /* Check whether mnemonic matches storage. The mnemonic must be * a null-terminated string. */ -bool config_containsMnemonic(const char *mnemonic) -{ - uint16_t len = 0; - uint8_t stored_mnemonic[MAX_MNEMONIC_LEN]; - if (sectrue != storage_get(KEY_MNEMONIC, stored_mnemonic, sizeof(stored_mnemonic), &len)) { - return false; - } - - // Compare the digests to mitigate side-channel attacks. - uint8_t digest_stored[SHA256_DIGEST_LENGTH]; - sha256_Raw(stored_mnemonic, len, digest_stored); - memzero(stored_mnemonic, sizeof(stored_mnemonic)); - - uint8_t digest_input[SHA256_DIGEST_LENGTH]; - sha256_Raw((const uint8_t*)mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN), digest_input); - - uint8_t diff = 0; - for (size_t i = 0; i < sizeof(digest_input); i++) { - diff |= (digest_stored[i] - digest_input[i]); - } - memzero(digest_stored, sizeof(digest_stored)); - memzero(digest_input, sizeof(digest_input)); - return diff == 0; +bool config_containsMnemonic(const char *mnemonic) { + uint16_t len = 0; + uint8_t stored_mnemonic[MAX_MNEMONIC_LEN]; + if (sectrue != storage_get(KEY_MNEMONIC, stored_mnemonic, + sizeof(stored_mnemonic), &len)) { + return false; + } + + // Compare the digests to mitigate side-channel attacks. + uint8_t digest_stored[SHA256_DIGEST_LENGTH]; + sha256_Raw(stored_mnemonic, len, digest_stored); + memzero(stored_mnemonic, sizeof(stored_mnemonic)); + + uint8_t digest_input[SHA256_DIGEST_LENGTH]; + sha256_Raw((const uint8_t *)mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN), + digest_input); + + uint8_t diff = 0; + for (size_t i = 0; i < sizeof(digest_input); i++) { + diff |= (digest_stored[i] - digest_input[i]); + } + memzero(digest_stored, sizeof(digest_stored)); + memzero(digest_input, sizeof(digest_input)); + return diff == 0; } /* Check whether pin matches storage. The pin must be * a null-terminated string with at most 9 characters. */ -bool config_unlock(const char *pin) -{ - char oldTiny = usbTiny(1); - secbool ret = storage_unlock(pin_to_int(pin)); - usbTiny(oldTiny); - return sectrue == ret; +bool config_unlock(const char *pin) { + char oldTiny = usbTiny(1); + secbool ret = storage_unlock(pin_to_int(pin)); + usbTiny(oldTiny); + return sectrue == ret; } -bool config_hasPin(void) -{ - return sectrue == storage_has_pin(); -} +bool config_hasPin(void) { return sectrue == storage_has_pin(); } -bool config_changePin(const char *old_pin, const char *new_pin) -{ - uint32_t new_pin_int = pin_to_int(new_pin); - if (new_pin_int == 0) { - return false; - } +bool config_changePin(const char *old_pin, const char *new_pin) { + uint32_t new_pin_int = pin_to_int(new_pin); + if (new_pin_int == 0) { + return false; + } - char oldTiny = usbTiny(1); - secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int); - usbTiny(oldTiny); + char oldTiny = usbTiny(1); + secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int); + usbTiny(oldTiny); #if DEBUG_LINK - if (sectrue == ret) { - if (new_pin_int != PIN_EMPTY) { - storage_set(KEY_DEBUG_LINK_PIN, new_pin, strnlen(new_pin, MAX_PIN_LEN)); - } else { - storage_delete(KEY_DEBUG_LINK_PIN); - } + if (sectrue == ret) { + if (new_pin_int != PIN_EMPTY) { + storage_set(KEY_DEBUG_LINK_PIN, new_pin, strnlen(new_pin, MAX_PIN_LEN)); + } else { + storage_delete(KEY_DEBUG_LINK_PIN); } + } #endif - memzero(&new_pin_int, sizeof(new_pin_int)); + memzero(&new_pin_int, sizeof(new_pin_int)); - return sectrue == ret; + return sectrue == ret; } #if DEBUG_LINK -bool config_getPin(char *dest, uint16_t dest_size) -{ - return sectrue == config_get_string(KEY_DEBUG_LINK_PIN, dest, dest_size); +bool config_getPin(char *dest, uint16_t dest_size) { + return sectrue == config_get_string(KEY_DEBUG_LINK_PIN, dest, dest_size); } #endif -void session_cachePassphrase(const char *passphrase) -{ - strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase)); - sessionPassphraseCached = sectrue; +void session_cachePassphrase(const char *passphrase) { + strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase)); + sessionPassphraseCached = sectrue; } -bool session_isPassphraseCached(void) -{ - return sectrue == sessionPassphraseCached; +bool session_isPassphraseCached(void) { + return sectrue == sessionPassphraseCached; } -bool session_getState(const uint8_t *salt, uint8_t *state, const char *passphrase) -{ - if (!passphrase && sectrue != sessionPassphraseCached) { - return false; - } else { - passphrase = sessionPassphrase; - } - if (!salt) { - // if salt is not provided fill the first half of the state with random data - random_buffer(state, 32); - } else { - // if salt is provided fill the first half of the state with salt - memcpy(state, salt, 32); - } - // state[0:32] = salt - // state[32:64] = HMAC(passphrase, salt || device_id) - HMAC_SHA256_CTX ctx; - hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase)); - hmac_sha256_Update(&ctx, state, 32); - hmac_sha256_Update(&ctx, (const uint8_t *)config_uuid, sizeof(config_uuid)); - hmac_sha256_Final(&ctx, state + 32); +bool session_getState(const uint8_t *salt, uint8_t *state, + const char *passphrase) { + if (!passphrase && sectrue != sessionPassphraseCached) { + return false; + } else { + passphrase = sessionPassphrase; + } + if (!salt) { + // if salt is not provided fill the first half of the state with random data + random_buffer(state, 32); + } else { + // if salt is provided fill the first half of the state with salt + memcpy(state, salt, 32); + } + // state[0:32] = salt + // state[32:64] = HMAC(passphrase, salt || device_id) + HMAC_SHA256_CTX ctx; + hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase)); + hmac_sha256_Update(&ctx, state, 32); + hmac_sha256_Update(&ctx, (const uint8_t *)config_uuid, sizeof(config_uuid)); + hmac_sha256_Final(&ctx, state + 32); - memzero(&ctx, sizeof(ctx)); + memzero(&ctx, sizeof(ctx)); - return true; + return true; } -bool session_isUnlocked(void) -{ - return sectrue == storage_is_unlocked(); -} +bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); } -bool config_isInitialized(void) -{ - bool initialized = false; - config_get_bool(KEY_INITIALIZED, &initialized); - return initialized; +bool config_isInitialized(void) { + bool initialized = false; + config_get_bool(KEY_INITIALIZED, &initialized); + return initialized; } -bool config_getImported(bool* imported) -{ - return sectrue == config_get_bool(KEY_IMPORTED, imported); +bool config_getImported(bool *imported) { + return sectrue == config_get_bool(KEY_IMPORTED, imported); } -void config_setImported(bool imported) -{ - config_set_bool(KEY_IMPORTED, imported); +void config_setImported(bool imported) { + config_set_bool(KEY_IMPORTED, imported); } -bool config_getNeedsBackup(bool *needs_backup) -{ - return sectrue == config_get_bool(KEY_NEEDS_BACKUP, needs_backup); +bool config_getNeedsBackup(bool *needs_backup) { + return sectrue == config_get_bool(KEY_NEEDS_BACKUP, needs_backup); } -void config_setNeedsBackup(bool needs_backup) -{ - config_set_bool(KEY_NEEDS_BACKUP, needs_backup); +void config_setNeedsBackup(bool needs_backup) { + config_set_bool(KEY_NEEDS_BACKUP, needs_backup); } -bool config_getUnfinishedBackup(bool *unfinished_backup) -{ - return sectrue == config_get_bool(KEY_UNFINISHED_BACKUP, unfinished_backup); +bool config_getUnfinishedBackup(bool *unfinished_backup) { + return sectrue == config_get_bool(KEY_UNFINISHED_BACKUP, unfinished_backup); } -void config_setUnfinishedBackup(bool unfinished_backup) -{ - config_set_bool(KEY_UNFINISHED_BACKUP, unfinished_backup); +void config_setUnfinishedBackup(bool unfinished_backup) { + config_set_bool(KEY_UNFINISHED_BACKUP, unfinished_backup); } -bool config_getNoBackup(bool *no_backup) -{ - return sectrue == config_get_bool(KEY_NO_BACKUP, no_backup); +bool config_getNoBackup(bool *no_backup) { + return sectrue == config_get_bool(KEY_NO_BACKUP, no_backup); } -void config_setNoBackup(void) -{ - config_set_bool(KEY_NO_BACKUP, true); -} +void config_setNoBackup(void) { config_set_bool(KEY_NO_BACKUP, true); } -void config_applyFlags(uint32_t flags) -{ - uint32_t old_flags = 0; - config_get_uint32(KEY_FLAGS, &old_flags); - flags |= old_flags; - if (flags == old_flags) { - return; // no new flags - } - storage_set(KEY_FLAGS, &flags, sizeof(flags)); +void config_applyFlags(uint32_t flags) { + uint32_t old_flags = 0; + config_get_uint32(KEY_FLAGS, &old_flags); + flags |= old_flags; + if (flags == old_flags) { + return; // no new flags + } + storage_set(KEY_FLAGS, &flags, sizeof(flags)); } -bool config_getFlags(uint32_t *flags) -{ - return sectrue == config_get_uint32(KEY_FLAGS, flags); +bool config_getFlags(uint32_t *flags) { + return sectrue == config_get_uint32(KEY_FLAGS, flags); } -uint32_t config_nextU2FCounter(void) -{ - uint32_t u2fcounter = 0; - storage_next_counter(KEY_U2F_COUNTER, &u2fcounter); - return u2fcounter; +uint32_t config_nextU2FCounter(void) { + uint32_t u2fcounter = 0; + storage_next_counter(KEY_U2F_COUNTER, &u2fcounter); + return u2fcounter; } -void config_setU2FCounter(uint32_t u2fcounter) -{ - storage_set_counter(KEY_U2F_COUNTER, u2fcounter); +void config_setU2FCounter(uint32_t u2fcounter) { + storage_set_counter(KEY_U2F_COUNTER, u2fcounter); } -uint32_t config_getAutoLockDelayMs() -{ - if (sectrue == autoLockDelayMsCached) { - return autoLockDelayMs; - } +uint32_t config_getAutoLockDelayMs() { + if (sectrue == autoLockDelayMsCached) { + return autoLockDelayMs; + } - if (sectrue != storage_is_unlocked()) { - return autoLockDelayMsDefault; - } + if (sectrue != storage_is_unlocked()) { + return autoLockDelayMsDefault; + } - if (sectrue != config_get_uint32(KEY_AUTO_LOCK_DELAY_MS, &autoLockDelayMs)) { - autoLockDelayMs = autoLockDelayMsDefault; - } - autoLockDelayMsCached = sectrue; - return autoLockDelayMs; + if (sectrue != config_get_uint32(KEY_AUTO_LOCK_DELAY_MS, &autoLockDelayMs)) { + autoLockDelayMs = autoLockDelayMsDefault; + } + autoLockDelayMsCached = sectrue; + return autoLockDelayMs; } -void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms) -{ - const uint32_t min_delay_ms = 10 * 1000U; // 10 seconds - auto_lock_delay_ms = MAX(auto_lock_delay_ms, min_delay_ms); - if (sectrue == storage_set(KEY_AUTO_LOCK_DELAY_MS, &auto_lock_delay_ms, sizeof(auto_lock_delay_ms))) { - autoLockDelayMs = auto_lock_delay_ms; - autoLockDelayMsCached = sectrue; - } +void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms) { + const uint32_t min_delay_ms = 10 * 1000U; // 10 seconds + auto_lock_delay_ms = MAX(auto_lock_delay_ms, min_delay_ms); + if (sectrue == storage_set(KEY_AUTO_LOCK_DELAY_MS, &auto_lock_delay_ms, + sizeof(auto_lock_delay_ms))) { + autoLockDelayMs = auto_lock_delay_ms; + autoLockDelayMsCached = sectrue; + } } -void config_wipe(void) -{ - char oldTiny = usbTiny(1); - storage_wipe(); - if (storage_is_unlocked() != sectrue) { - storage_unlock(PIN_EMPTY); - } - usbTiny(oldTiny); - random_buffer((uint8_t *)config_uuid, sizeof(config_uuid)); - data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); - autoLockDelayMsCached = secfalse; - storage_set(KEY_UUID, config_uuid, sizeof(config_uuid)); - storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION)); - session_clear(false); +void config_wipe(void) { + char oldTiny = usbTiny(1); + storage_wipe(); + if (storage_is_unlocked() != sectrue) { + storage_unlock(PIN_EMPTY); + } + usbTiny(oldTiny); + random_buffer((uint8_t *)config_uuid, sizeof(config_uuid)); + data2hex(config_uuid, sizeof(config_uuid), config_uuid_str); + autoLockDelayMsCached = secfalse; + storage_set(KEY_UUID, config_uuid, sizeof(config_uuid)); + storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION)); + session_clear(false); } diff --git a/firmware/config.h b/firmware/config.h index 2f2c26da2..593cb90fe 100644 --- a/firmware/config.h +++ b/firmware/config.h @@ -24,66 +24,66 @@ #include "messages-management.pb.h" #define STORAGE_FIELD(TYPE, NAME) \ - bool has_##NAME; \ - TYPE NAME; + bool has_##NAME; \ + TYPE NAME; #define STORAGE_STRING(NAME, SIZE) \ - bool has_##NAME; \ - char NAME[SIZE]; + bool has_##NAME; \ + char NAME[SIZE]; #define STORAGE_BYTES(NAME, SIZE) \ - bool has_##NAME; \ - struct { \ - uint32_t size; \ - uint8_t bytes[SIZE]; \ - } NAME; + bool has_##NAME; \ + struct { \ + uint32_t size; \ + uint8_t bytes[SIZE]; \ + } NAME; -#define STORAGE_BOOL(NAME) STORAGE_FIELD(bool, NAME) -#define STORAGE_NODE(NAME) STORAGE_FIELD(StorageHDNode, NAME) -#define STORAGE_UINT32(NAME) STORAGE_FIELD(uint32_t, NAME) +#define STORAGE_BOOL(NAME) STORAGE_FIELD(bool, NAME) +#define STORAGE_NODE(NAME) STORAGE_FIELD(StorageHDNode, NAME) +#define STORAGE_UINT32(NAME) STORAGE_FIELD(uint32_t, NAME) typedef struct { - uint32_t depth; - uint32_t fingerprint; - uint32_t child_num; - struct { - uint32_t size; - uint8_t bytes[32]; - } chain_code; - - STORAGE_BYTES(private_key, 32); - STORAGE_BYTES(public_key, 33); + uint32_t depth; + uint32_t fingerprint; + uint32_t child_num; + struct { + uint32_t size; + uint8_t bytes[32]; + } chain_code; + + STORAGE_BYTES(private_key, 32); + STORAGE_BYTES(public_key, 33); } StorageHDNode; typedef struct _Storage { - uint32_t version; - - STORAGE_NODE (node) - STORAGE_STRING (mnemonic, 241) - STORAGE_BOOL (passphrase_protection) - STORAGE_UINT32 (pin_failed_attempts) - STORAGE_STRING (pin, 10) - STORAGE_STRING (language, 17) - STORAGE_STRING (label, 33) - STORAGE_BOOL (imported) - STORAGE_BYTES (homescreen, 1024) - STORAGE_UINT32 (u2f_counter) - STORAGE_BOOL (needs_backup) - STORAGE_UINT32 (flags) - STORAGE_NODE (u2froot) - STORAGE_BOOL (unfinished_backup) - STORAGE_UINT32 (auto_lock_delay_ms) - STORAGE_BOOL (no_backup) + uint32_t version; + + STORAGE_NODE(node) + STORAGE_STRING(mnemonic, 241) + STORAGE_BOOL(passphrase_protection) + STORAGE_UINT32(pin_failed_attempts) + STORAGE_STRING(pin, 10) + STORAGE_STRING(language, 17) + STORAGE_STRING(label, 33) + STORAGE_BOOL(imported) + STORAGE_BYTES(homescreen, 1024) + STORAGE_UINT32(u2f_counter) + STORAGE_BOOL(needs_backup) + STORAGE_UINT32(flags) + STORAGE_NODE(u2froot) + STORAGE_BOOL(unfinished_backup) + STORAGE_UINT32(auto_lock_delay_ms) + STORAGE_BOOL(no_backup) } Storage; extern Storage configUpdate; -#define MAX_PIN_LEN 9 -#define MAX_LABEL_LEN 32 -#define MAX_LANGUAGE_LEN 16 -#define MAX_MNEMONIC_LEN 240 -#define HOMESCREEN_SIZE 1024 -#define UUID_SIZE 12 +#define MAX_PIN_LEN 9 +#define MAX_LABEL_LEN 32 +#define MAX_LANGUAGE_LEN 16 +#define MAX_MNEMONIC_LEN 240 +#define HOMESCREEN_SIZE 1024 +#define UUID_SIZE 12 void config_init(void); void session_clear(bool lock); @@ -109,12 +109,14 @@ void config_setHomescreen(const uint8_t *data, uint32_t size); void session_cachePassphrase(const char *passphrase); bool session_isPassphraseCached(void); -bool session_getState(const uint8_t *salt, uint8_t *state, const char *passphrase); +bool session_getState(const uint8_t *salt, uint8_t *state, + const char *passphrase); bool config_setMnemonic(const char *mnemonic); bool config_containsMnemonic(const char *mnemonic); bool config_getMnemonic(char *dest, uint16_t dest_size); -bool config_getMnemonicBytes(uint8_t *dest, uint16_t dest_size, uint16_t *real_size); +bool config_getMnemonicBytes(uint8_t *dest, uint16_t dest_size, + uint16_t *real_size); #if DEBUG_LINK bool config_dumpNode(HDNodeType *node); @@ -151,6 +153,6 @@ void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms); void config_wipe(void); -extern char config_uuid_str[2*UUID_SIZE + 1]; +extern char config_uuid_str[2 * UUID_SIZE + 1]; #endif diff --git a/firmware/crypto.c b/firmware/crypto.c index f71b09332..3b7aac7d6 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -17,452 +17,482 @@ * along with this library. If not, see . */ -#include #include "crypto.h" -#include "sha2.h" -#include "pbkdf2.h" +#include +#include "address.h" #include "aes/aes.h" -#include "hmac.h" +#include "base58.h" #include "bip32.h" -#include "layout.h" +#include "cash_addr.h" +#include "coins.h" #include "curves.h" +#include "hmac.h" +#include "layout.h" +#include "pbkdf2.h" #include "secp256k1.h" -#include "address.h" -#include "coins.h" -#include "base58.h" #include "segwit_addr.h" -#include "cash_addr.h" +#include "sha2.h" -uint32_t ser_length(uint32_t len, uint8_t *out) -{ - if (len < 253) { - out[0] = len & 0xFF; - return 1; - } - if (len < 0x10000) { - out[0] = 253; - out[1] = len & 0xFF; - out[2] = (len >> 8) & 0xFF; - return 3; - } - out[0] = 254; - out[1] = len & 0xFF; - out[2] = (len >> 8) & 0xFF; - out[3] = (len >> 16) & 0xFF; - out[4] = (len >> 24) & 0xFF; - return 5; +uint32_t ser_length(uint32_t len, uint8_t *out) { + if (len < 253) { + out[0] = len & 0xFF; + return 1; + } + if (len < 0x10000) { + out[0] = 253; + out[1] = len & 0xFF; + out[2] = (len >> 8) & 0xFF; + return 3; + } + out[0] = 254; + out[1] = len & 0xFF; + out[2] = (len >> 8) & 0xFF; + out[3] = (len >> 16) & 0xFF; + out[4] = (len >> 24) & 0xFF; + return 5; } -uint32_t ser_length_hash(Hasher *hasher, uint32_t len) -{ - if (len < 253) { - hasher_Update(hasher, (const uint8_t *)&len, 1); - return 1; - } - if (len < 0x10000) { - uint8_t d = 253; - hasher_Update(hasher, &d, 1); - hasher_Update(hasher, (const uint8_t *)&len, 2); - return 3; - } - uint8_t d = 254; - hasher_Update(hasher, &d, 1); - hasher_Update(hasher, (const uint8_t *)&len, 4); - return 5; +uint32_t ser_length_hash(Hasher *hasher, uint32_t len) { + if (len < 253) { + hasher_Update(hasher, (const uint8_t *)&len, 1); + return 1; + } + if (len < 0x10000) { + uint8_t d = 253; + hasher_Update(hasher, &d, 1); + hasher_Update(hasher, (const uint8_t *)&len, 2); + return 3; + } + uint8_t d = 254; + hasher_Update(hasher, &d, 1); + hasher_Update(hasher, (const uint8_t *)&len, 4); + return 5; } -uint32_t deser_length(const uint8_t *in, uint32_t *out) -{ - if (in[0] < 253) { - *out = in[0]; - return 1; - } - if (in[0] == 253) { - *out = in[1] + (in[2] << 8); - return 1 + 2; - } - if (in[0] == 254) { - *out = in[1] + (in[2] << 8) + (in[3] << 16) + ((uint32_t) in[4] << 24); - return 1 + 4; - } - *out = 0; // ignore 64 bit - return 1 + 8; +uint32_t deser_length(const uint8_t *in, uint32_t *out) { + if (in[0] < 253) { + *out = in[0]; + return 1; + } + if (in[0] == 253) { + *out = in[1] + (in[2] << 8); + return 1 + 2; + } + if (in[0] == 254) { + *out = in[1] + (in[2] << 8) + (in[3] << 16) + ((uint32_t)in[4] << 24); + return 1 + 4; + } + *out = 0; // ignore 64 bit + return 1 + 8; } -int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature) -{ - signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes - return hdnode_sign(node, message, message_len, HASHER_SHA2, signature + 1, NULL, NULL); +int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, + uint8_t *signature) { + signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes + return hdnode_sign(node, message, message_len, HASHER_SHA2, signature + 1, + NULL, NULL); } -int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature) -{ - signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes - const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME); - if (ed25519_curve_info && node->curve == ed25519_curve_info) { - // GPG supports variable size digest for Ed25519 signatures - return hdnode_sign(node, message, message_len, 0, signature + 1, NULL, NULL); - } else { - // Ensure 256-bit digest before proceeding - if (message_len != 32) { - return 1; - } - return hdnode_sign_digest(node, message, signature + 1, NULL, NULL); - } +int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, + uint8_t *signature) { + signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes + const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME); + if (ed25519_curve_info && node->curve == ed25519_curve_info) { + // GPG supports variable size digest for Ed25519 signatures + return hdnode_sign(node, message, message_len, 0, signature + 1, NULL, + NULL); + } else { + // Ensure 256-bit digest before proceeding + if (message_len != 32) { + return 1; + } + return hdnode_sign_digest(node, message, signature + 1, NULL, NULL); + } } -static void cryptoMessageHash(const CoinInfo *coin, const uint8_t *message, size_t message_len, uint8_t hash[HASHER_DIGEST_LENGTH]) { - Hasher hasher; - hasher_Init(&hasher, coin->curve->hasher_sign); - hasher_Update(&hasher, (const uint8_t *)coin->signed_message_header, strlen(coin->signed_message_header)); - uint8_t varint[5]; - uint32_t l = ser_length(message_len, varint); - hasher_Update(&hasher, varint, l); - hasher_Update(&hasher, message, message_len); - hasher_Final(&hasher, hash); +static void cryptoMessageHash(const CoinInfo *coin, const uint8_t *message, + size_t message_len, + uint8_t hash[HASHER_DIGEST_LENGTH]) { + Hasher hasher; + hasher_Init(&hasher, coin->curve->hasher_sign); + hasher_Update(&hasher, (const uint8_t *)coin->signed_message_header, + strlen(coin->signed_message_header)); + uint8_t varint[5]; + uint32_t l = ser_length(message_len, varint); + hasher_Update(&hasher, varint, l); + hasher_Update(&hasher, message, message_len); + hasher_Final(&hasher, hash); } -int cryptoMessageSign(const CoinInfo *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature) -{ - uint8_t hash[HASHER_DIGEST_LENGTH]; - cryptoMessageHash(coin, message, message_len, hash); +int cryptoMessageSign(const CoinInfo *coin, HDNode *node, + InputScriptType script_type, const uint8_t *message, + size_t message_len, uint8_t *signature) { + uint8_t hash[HASHER_DIGEST_LENGTH]; + cryptoMessageHash(coin, message, message_len, hash); - uint8_t pby; - int result = hdnode_sign_digest(node, hash, signature + 1, &pby, NULL); - if (result == 0) { - switch (script_type) { - case InputScriptType_SPENDP2SHWITNESS: - // segwit-in-p2sh - signature[0] = 35 + pby; - break; - case InputScriptType_SPENDWITNESS: - // segwit - signature[0] = 39 + pby; - break; - default: - // p2pkh - signature[0] = 31 + pby; - break; - } - } - return result; + uint8_t pby; + int result = hdnode_sign_digest(node, hash, signature + 1, &pby, NULL); + if (result == 0) { + switch (script_type) { + case InputScriptType_SPENDP2SHWITNESS: + // segwit-in-p2sh + signature[0] = 35 + pby; + break; + case InputScriptType_SPENDWITNESS: + // segwit + signature[0] = 39 + pby; + break; + default: + // p2pkh + signature[0] = 31 + pby; + break; + } + } + return result; } -int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t message_len, const char *address, const uint8_t *signature) -{ - // check for invalid signature prefix - if (signature[0] < 27 || signature[0] > 43) { - return 1; - } +int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, + size_t message_len, const char *address, + const uint8_t *signature) { + // check for invalid signature prefix + if (signature[0] < 27 || signature[0] > 43) { + return 1; + } - uint8_t hash[HASHER_DIGEST_LENGTH]; - cryptoMessageHash(coin, message, message_len, hash); + uint8_t hash[HASHER_DIGEST_LENGTH]; + cryptoMessageHash(coin, message, message_len, hash); - uint8_t recid = (signature[0] - 27) % 4; - bool compressed = signature[0] >= 31; + uint8_t recid = (signature[0] - 27) % 4; + bool compressed = signature[0] >= 31; - // check if signature verifies the digest and recover the public key - uint8_t pubkey[65]; - if (ecdsa_recover_pub_from_sig(coin->curve->params, pubkey, signature + 1, hash, recid) != 0) { - return 3; - } - // convert public key to compressed pubkey if necessary - if (compressed) { - pubkey[0] = 0x02 | (pubkey[64] & 1); - } + // check if signature verifies the digest and recover the public key + uint8_t pubkey[65]; + if (ecdsa_recover_pub_from_sig(coin->curve->params, pubkey, signature + 1, + hash, recid) != 0) { + return 3; + } + // convert public key to compressed pubkey if necessary + if (compressed) { + pubkey[0] = 0x02 | (pubkey[64] & 1); + } - // check if the address is correct - uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - uint8_t recovered_raw[MAX_ADDR_RAW_SIZE]; + // check if the address is correct + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + uint8_t recovered_raw[MAX_ADDR_RAW_SIZE]; - // p2pkh - if (signature[0] >= 27 && signature[0] <= 34) { - size_t len; - if (coin->cashaddr_prefix) { - if (!cash_addr_decode(addr_raw, &len, coin->cashaddr_prefix, address)) { - return 2; - } - } else { - len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); - } - ecdsa_get_address_raw(pubkey, coin->address_type, coin->curve->hasher_pubkey, recovered_raw); - if (memcmp(recovered_raw, addr_raw, len) != 0 - || len != address_prefix_bytes_len(coin->address_type) + 20) { - return 2; - } - } else - // segwit-in-p2sh - if (signature[0] >= 35 && signature[0] <= 38) { - size_t len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); - ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, coin->curve->hasher_pubkey, recovered_raw); - if (memcmp(recovered_raw, addr_raw, len) != 0 - || len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) { - return 2; - } - } else - // segwit - if (signature[0] >= 39 && signature[0] <= 42) { - int witver; - size_t len; - if (!coin->bech32_prefix - || !segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix, address)) { - return 4; - } - ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw); - if (memcmp(recovered_raw, addr_raw, len) != 0 - || witver != 0 || len != 20) { - return 2; - } - } else { - return 4; - } + // p2pkh + if (signature[0] >= 27 && signature[0] <= 34) { + size_t len; + if (coin->cashaddr_prefix) { + if (!cash_addr_decode(addr_raw, &len, coin->cashaddr_prefix, address)) { + return 2; + } + } else { + len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, + MAX_ADDR_RAW_SIZE); + } + ecdsa_get_address_raw(pubkey, coin->address_type, + coin->curve->hasher_pubkey, recovered_raw); + if (memcmp(recovered_raw, addr_raw, len) != 0 || + len != address_prefix_bytes_len(coin->address_type) + 20) { + return 2; + } + } else + // segwit-in-p2sh + if (signature[0] >= 35 && signature[0] <= 38) { + size_t len = base58_decode_check(address, coin->curve->hasher_base58, + addr_raw, MAX_ADDR_RAW_SIZE); + ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, + coin->curve->hasher_pubkey, + recovered_raw); + if (memcmp(recovered_raw, addr_raw, len) != 0 || + len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) { + return 2; + } + } else + // segwit + if (signature[0] >= 39 && signature[0] <= 42) { + int witver; + size_t len; + if (!coin->bech32_prefix || + !segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix, + address)) { + return 4; + } + ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw); + if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) { + return 2; + } + } else { + return 4; + } - return 0; + return 0; } /* ECIES disabled -int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t *payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t *privkey, const uint8_t *address_raw) +int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t +msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t +*payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t +*privkey, const uint8_t *address_raw) { - if (privkey && address_raw) { // signing == true - HDNode node; - payload[0] = display_only ? 0x81 : 0x01; - uint32_t l = ser_length(msg_size, payload + 1); - memcpy(payload + 1 + l, msg, msg_size); - memcpy(payload + 1 + l + msg_size, address_raw, 21); - hdnode_from_xprv(0, 0, 0, privkey, privkey, SECP256K1_NAME, &node); - if (cryptoMessageSign(&node, msg, msg_size, payload + 1 + l + msg_size + 21) != 0) { - return 1; - } - *payload_len = 1 + l + msg_size + 21 + 65; - } else { - payload[0] = display_only ? 0x80 : 0x00; - uint32_t l = ser_length(msg_size, payload + 1); - memcpy(payload + 1 + l, msg, msg_size); - *payload_len = 1 + l + msg_size; - } - // generate random nonce - curve_point R; - bignum256 k; - if (generate_k_random(&secp256k1, &k) != 0) { - return 2; - } - // compute k*G - scalar_multiply(&secp256k1, &k, &R); - nonce[0] = 0x02 | (R.y.val[0] & 0x01); - bn_write_be(&R.x, nonce + 1); - *nonce_len = 33; - // compute shared secret - point_multiply(&secp256k1, &k, pubkey, &R); - uint8_t shared_secret[33]; - shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); - bn_write_be(&R.x, shared_secret + 1); - // generate keying bytes - uint8_t keying_bytes[80]; - uint8_t salt[22 + 33]; - memcpy(salt, "Bitcoin Secure Message", 22); - memcpy(salt + 22, nonce, 33); - pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes, 80); - // encrypt payload - aes_encrypt_ctx ctx; - aes_encrypt_key256(keying_bytes, &ctx); - aes_cfb_encrypt(payload, payload, *payload_len, keying_bytes + 64, &ctx); - // compute hmac - uint8_t out[32]; - hmac_sha256(keying_bytes + 32, 32, payload, *payload_len, out); - memcpy(hmac, out, 8); - *hmac_len = 8; + if (privkey && address_raw) { // signing == true + HDNode node; + payload[0] = display_only ? 0x81 : 0x01; + uint32_t l = ser_length(msg_size, payload + 1); + memcpy(payload + 1 + l, msg, msg_size); + memcpy(payload + 1 + l + msg_size, address_raw, 21); + hdnode_from_xprv(0, 0, 0, privkey, privkey, SECP256K1_NAME, +&node); if (cryptoMessageSign(&node, msg, msg_size, payload + 1 + l + msg_size + +21) != 0) { return 1; + } + *payload_len = 1 + l + msg_size + 21 + 65; + } else { + payload[0] = display_only ? 0x80 : 0x00; + uint32_t l = ser_length(msg_size, payload + 1); + memcpy(payload + 1 + l, msg, msg_size); + *payload_len = 1 + l + msg_size; + } + // generate random nonce + curve_point R; + bignum256 k; + if (generate_k_random(&secp256k1, &k) != 0) { + return 2; + } + // compute k*G + scalar_multiply(&secp256k1, &k, &R); + nonce[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, nonce + 1); + *nonce_len = 33; + // compute shared secret + point_multiply(&secp256k1, &k, pubkey, &R); + uint8_t shared_secret[33]; + shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, shared_secret + 1); + // generate keying bytes + uint8_t keying_bytes[80]; + uint8_t salt[22 + 33]; + memcpy(salt, "Bitcoin Secure Message", 22); + memcpy(salt + 22, nonce, 33); + pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes, +80); + // encrypt payload + aes_encrypt_ctx ctx; + aes_encrypt_key256(keying_bytes, &ctx); + aes_cfb_encrypt(payload, payload, *payload_len, keying_bytes + 64, +&ctx); + // compute hmac + uint8_t out[32]; + hmac_sha256(keying_bytes + 32, 32, payload, *payload_len, out); + memcpy(hmac, out, 8); + *hmac_len = 8; - return 0; + return 0; } -int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey, uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw) +int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t +payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey, +uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t +*address_raw) { - if (hmac_len != 8) { - return 1; - } - // compute shared secret - curve_point R; - bignum256 k; - bn_read_be(privkey, &k); - point_multiply(&secp256k1, &k, nonce, &R); - uint8_t shared_secret[33]; - shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); - bn_write_be(&R.x, shared_secret + 1); - // generate keying bytes - uint8_t keying_bytes[80]; - uint8_t salt[22 + 33]; - memcpy(salt, "Bitcoin Secure Message", 22); - salt[22] = 0x02 | (nonce->y.val[0] & 0x01); - bn_write_be(&(nonce->x), salt + 23); - pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes, 80); - // compute hmac - uint8_t out[32]; - hmac_sha256(keying_bytes + 32, 32, payload, payload_len, out); - if (memcmp(hmac, out, 8) != 0) { - return 2; - } - // decrypt payload - aes_encrypt_ctx ctx; - aes_encrypt_key256(keying_bytes, &ctx); - aes_cfb_decrypt(payload, payload, payload_len, keying_bytes + 64, &ctx); - // check first byte - if (payload[0] != 0x00 && payload[0] != 0x01 && payload[0] != 0x80 && payload[0] != 0x81) { - return 3; - } - *signing = payload[0] & 0x01; - *display_only = payload[0] & 0x80; - uint32_t l, o; - l = deser_length(payload + 1, &o); - if (*signing) { - // FIXME: assumes a raw address is 21 bytes (also below). - if (1 + l + o + 21 + 65 != payload_len) { - return 4; - } - // FIXME: cryptoMessageVerify changed to take the address_type as a parameter. - if (cryptoMessageVerify(payload + 1 + l, o, payload + 1 + l + o, payload + 1 + l + o + 21) != 0) { - return 5; - } - memcpy(address_raw, payload + 1 + l + o, 21); - } else { - if (1 + l + o != payload_len) { - return 4; - } - } - memcpy(msg, payload + 1 + l, o); - *msg_len = o; - return 0; + if (hmac_len != 8) { + return 1; + } + // compute shared secret + curve_point R; + bignum256 k; + bn_read_be(privkey, &k); + point_multiply(&secp256k1, &k, nonce, &R); + uint8_t shared_secret[33]; + shared_secret[0] = 0x02 | (R.y.val[0] & 0x01); + bn_write_be(&R.x, shared_secret + 1); + // generate keying bytes + uint8_t keying_bytes[80]; + uint8_t salt[22 + 33]; + memcpy(salt, "Bitcoin Secure Message", 22); + salt[22] = 0x02 | (nonce->y.val[0] & 0x01); + bn_write_be(&(nonce->x), salt + 23); + pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes, +80); + // compute hmac + uint8_t out[32]; + hmac_sha256(keying_bytes + 32, 32, payload, payload_len, out); + if (memcmp(hmac, out, 8) != 0) { + return 2; + } + // decrypt payload + aes_encrypt_ctx ctx; + aes_encrypt_key256(keying_bytes, &ctx); + aes_cfb_decrypt(payload, payload, payload_len, keying_bytes + 64, &ctx); + // check first byte + if (payload[0] != 0x00 && payload[0] != 0x01 && payload[0] != 0x80 && +payload[0] != 0x81) { return 3; + } + *signing = payload[0] & 0x01; + *display_only = payload[0] & 0x80; + uint32_t l, o; + l = deser_length(payload + 1, &o); + if (*signing) { + // FIXME: assumes a raw address is 21 bytes (also below). + if (1 + l + o + 21 + 65 != payload_len) { + return 4; + } + // FIXME: cryptoMessageVerify changed to take the address_type +as a parameter. if (cryptoMessageVerify(payload + 1 + l, o, payload + 1 + l + o, +payload + 1 + l + o + 21) != 0) { return 5; + } + memcpy(address_raw, payload + 1 + l + o, 21); + } else { + if (1 + l + o != payload_len) { + return 4; + } + } + memcpy(msg, payload + 1 + l, o); + *msg_len = o; + return 0; } */ -const HDNode *cryptoMultisigPubkey(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint32_t index) -{ - const HDNodeType *node_ptr; - const uint32_t *address_n; - uint32_t address_n_count; - if (multisig->nodes_count) { // use multisig->nodes - if (index >= multisig->nodes_count) { - return 0; - } - node_ptr = &(multisig->nodes[index]); - address_n = multisig->address_n; - address_n_count = multisig->address_n_count; - } else - if (multisig->pubkeys_count) { // use multisig->pubkeys - if (index >= multisig->pubkeys_count) { - return 0; - } - node_ptr = &(multisig->pubkeys[index].node); - address_n = multisig->pubkeys[index].address_n; - address_n_count = multisig->pubkeys[index].address_n_count; - } else { - return 0; - } - if (node_ptr->chain_code.size != 32) return 0; - if (!node_ptr->has_public_key || node_ptr->public_key.size != 33) return 0; - static HDNode node; - if (!hdnode_from_xpub(node_ptr->depth, node_ptr->child_num, node_ptr->chain_code.bytes, node_ptr->public_key.bytes, coin->curve_name, &node)) { - return 0; - } - layoutProgressUpdate(true); - for (uint32_t i = 0; i < address_n_count; i++) { - if (!hdnode_public_ckd(&node, address_n[i])) { - return 0; - } - layoutProgressUpdate(true); - } - return &node; +const HDNode *cryptoMultisigPubkey(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint32_t index) { + const HDNodeType *node_ptr; + const uint32_t *address_n; + uint32_t address_n_count; + if (multisig->nodes_count) { // use multisig->nodes + if (index >= multisig->nodes_count) { + return 0; + } + node_ptr = &(multisig->nodes[index]); + address_n = multisig->address_n; + address_n_count = multisig->address_n_count; + } else if (multisig->pubkeys_count) { // use multisig->pubkeys + if (index >= multisig->pubkeys_count) { + return 0; + } + node_ptr = &(multisig->pubkeys[index].node); + address_n = multisig->pubkeys[index].address_n; + address_n_count = multisig->pubkeys[index].address_n_count; + } else { + return 0; + } + if (node_ptr->chain_code.size != 32) return 0; + if (!node_ptr->has_public_key || node_ptr->public_key.size != 33) return 0; + static HDNode node; + if (!hdnode_from_xpub(node_ptr->depth, node_ptr->child_num, + node_ptr->chain_code.bytes, node_ptr->public_key.bytes, + coin->curve_name, &node)) { + return 0; + } + layoutProgressUpdate(true); + for (uint32_t i = 0; i < address_n_count; i++) { + if (!hdnode_public_ckd(&node, address_n[i])) { + return 0; + } + layoutProgressUpdate(true); + } + return &node; } -uint32_t cryptoMultisigPubkeyCount(const MultisigRedeemScriptType *multisig) -{ - return multisig->nodes_count ? multisig->nodes_count : multisig->pubkeys_count; +uint32_t cryptoMultisigPubkeyCount(const MultisigRedeemScriptType *multisig) { + return multisig->nodes_count ? multisig->nodes_count + : multisig->pubkeys_count; } -int cryptoMultisigPubkeyIndex(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, const uint8_t *pubkey) -{ - for (size_t i = 0; i < cryptoMultisigPubkeyCount(multisig); i++) { - const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); - if (pubnode && memcmp(pubnode->public_key, pubkey, 33) == 0) { - return i; - } - } - return -1; +int cryptoMultisigPubkeyIndex(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + const uint8_t *pubkey) { + for (size_t i = 0; i < cryptoMultisigPubkeyCount(multisig); i++) { + const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); + if (pubnode && memcmp(pubnode->public_key, pubkey, 33) == 0) { + return i; + } + } + return -1; } -int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, uint8_t *hash) -{ - static const HDNodeType *pubnodes[15], *swap; - const uint32_t n = cryptoMultisigPubkeyCount(multisig); - if (n < 1 || n > 15) { - return 0; - } - if (!multisig->has_m || multisig->m < 1 || multisig->m > 15) { - return 0; - } - for (uint32_t i = 0; i < n; i++) { - if (multisig->nodes_count) { // use multisig->nodes - pubnodes[i] = &(multisig->nodes[i]); - } else - if (multisig->pubkeys_count) { // use multisig->pubkeys - pubnodes[i] = &(multisig->pubkeys[i].node); - } else { - return 0; - } - } - for (uint32_t i = 0; i < n; i++) { - if (!pubnodes[i]->has_public_key || pubnodes[i]->public_key.size != 33) return 0; - if (pubnodes[i]->chain_code.size != 32) return 0; - } - // minsort according to pubkey - for (uint32_t i = 0; i < n - 1; i++) { - for (uint32_t j = n - 1; j > i; j--) { - if (memcmp(pubnodes[i]->public_key.bytes, pubnodes[j]->public_key.bytes, 33) > 0) { - swap = pubnodes[i]; - pubnodes[i] = pubnodes[j]; - pubnodes[j] = swap; - } - } - } - // hash sorted nodes - SHA256_CTX ctx; - sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)&(multisig->m), sizeof(uint32_t)); - for (uint32_t i = 0; i < n; i++) { - sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->depth), sizeof(uint32_t)); - sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->fingerprint), sizeof(uint32_t)); - sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->child_num), sizeof(uint32_t)); - sha256_Update(&ctx, pubnodes[i]->chain_code.bytes, 32); - sha256_Update(&ctx, pubnodes[i]->public_key.bytes, 33); - } - sha256_Update(&ctx, (const uint8_t *)&n, sizeof(uint32_t)); - sha256_Final(&ctx, hash); - layoutProgressUpdate(true); - return 1; +int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, + uint8_t *hash) { + static const HDNodeType *pubnodes[15], *swap; + const uint32_t n = cryptoMultisigPubkeyCount(multisig); + if (n < 1 || n > 15) { + return 0; + } + if (!multisig->has_m || multisig->m < 1 || multisig->m > 15) { + return 0; + } + for (uint32_t i = 0; i < n; i++) { + if (multisig->nodes_count) { // use multisig->nodes + pubnodes[i] = &(multisig->nodes[i]); + } else if (multisig->pubkeys_count) { // use multisig->pubkeys + pubnodes[i] = &(multisig->pubkeys[i].node); + } else { + return 0; + } + } + for (uint32_t i = 0; i < n; i++) { + if (!pubnodes[i]->has_public_key || pubnodes[i]->public_key.size != 33) + return 0; + if (pubnodes[i]->chain_code.size != 32) return 0; + } + // minsort according to pubkey + for (uint32_t i = 0; i < n - 1; i++) { + for (uint32_t j = n - 1; j > i; j--) { + if (memcmp(pubnodes[i]->public_key.bytes, pubnodes[j]->public_key.bytes, + 33) > 0) { + swap = pubnodes[i]; + pubnodes[i] = pubnodes[j]; + pubnodes[j] = swap; + } + } + } + // hash sorted nodes + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)&(multisig->m), sizeof(uint32_t)); + for (uint32_t i = 0; i < n; i++) { + sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->depth), + sizeof(uint32_t)); + sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->fingerprint), + sizeof(uint32_t)); + sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->child_num), + sizeof(uint32_t)); + sha256_Update(&ctx, pubnodes[i]->chain_code.bytes, 32); + sha256_Update(&ctx, pubnodes[i]->public_key.bytes, 33); + } + sha256_Update(&ctx, (const uint8_t *)&n, sizeof(uint32_t)); + sha256_Final(&ctx, hash); + layoutProgressUpdate(true); + return 1; } -int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash) -{ - SHA256_CTX ctx; - sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)&(identity->index), sizeof(uint32_t)); - if (identity->has_proto && identity->proto[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->proto), strlen(identity->proto)); - sha256_Update(&ctx, (const uint8_t *)"://", 3); - } - if (identity->has_user && identity->user[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->user), strlen(identity->user)); - sha256_Update(&ctx, (const uint8_t *)"@", 1); - } - if (identity->has_host && identity->host[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->host), strlen(identity->host)); - } - if (identity->has_port && identity->port[0]) { - sha256_Update(&ctx, (const uint8_t *)":", 1); - sha256_Update(&ctx, (const uint8_t *)(identity->port), strlen(identity->port)); - } - if (identity->has_path && identity->path[0]) { - sha256_Update(&ctx, (const uint8_t *)(identity->path), strlen(identity->path)); - } - sha256_Final(&ctx, hash); - return 1; +int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash) { + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)&(identity->index), sizeof(uint32_t)); + if (identity->has_proto && identity->proto[0]) { + sha256_Update(&ctx, (const uint8_t *)(identity->proto), + strlen(identity->proto)); + sha256_Update(&ctx, (const uint8_t *)"://", 3); + } + if (identity->has_user && identity->user[0]) { + sha256_Update(&ctx, (const uint8_t *)(identity->user), + strlen(identity->user)); + sha256_Update(&ctx, (const uint8_t *)"@", 1); + } + if (identity->has_host && identity->host[0]) { + sha256_Update(&ctx, (const uint8_t *)(identity->host), + strlen(identity->host)); + } + if (identity->has_port && identity->port[0]) { + sha256_Update(&ctx, (const uint8_t *)":", 1); + sha256_Update(&ctx, (const uint8_t *)(identity->port), + strlen(identity->port)); + } + if (identity->has_path && identity->path[0]) { + sha256_Update(&ctx, (const uint8_t *)(identity->path), + strlen(identity->path)); + } + sha256_Final(&ctx, hash); + return 1; } diff --git a/firmware/crypto.h b/firmware/crypto.h index f8d77c920..ab550864e 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -20,13 +20,13 @@ #ifndef __CRYPTO_H__ #define __CRYPTO_H__ +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include #include "coins.h" #include "hasher.h" #include "messages-bitcoin.pb.h" @@ -38,27 +38,44 @@ uint32_t ser_length(uint32_t len, uint8_t *out); uint32_t ser_length_hash(Hasher *hasher, uint32_t len); -int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); +int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, + uint8_t *signature); -int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); +int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, + uint8_t *signature); -int cryptoMessageSign(const CoinInfo *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature); +int cryptoMessageSign(const CoinInfo *coin, HDNode *node, + InputScriptType script_type, const uint8_t *message, + size_t message_len, uint8_t *signature); -int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t message_len, const char *address, const uint8_t *signature); +int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, + size_t message_len, const char *address, + const uint8_t *signature); /* ECIES disabled -int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t *payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t *privkey, const uint8_t *address_raw); - -int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey, uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw); +int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t +msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t +*payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t +*privkey, const uint8_t *address_raw); + +int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t +payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey, +uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t +*address_raw); */ -const HDNode *cryptoMultisigPubkey(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint32_t index); +const HDNode *cryptoMultisigPubkey(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint32_t index); uint32_t cryptoMultisigPubkeyCount(const MultisigRedeemScriptType *multisig); -int cryptoMultisigPubkeyIndex(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, const uint8_t *pubkey); +int cryptoMultisigPubkeyIndex(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + const uint8_t *pubkey); -int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, uint8_t *hash); +int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, + uint8_t *hash); int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash); diff --git a/firmware/debug.c b/firmware/debug.c index eb2731492..288041a4c 100644 --- a/firmware/debug.c +++ b/firmware/debug.c @@ -17,52 +17,49 @@ * along with this library. If not, see . */ -#include "trezor.h" #include "debug.h" #include "oled.h" +#include "trezor.h" #include "util.h" #if DEBUG_LOG -void oledDebug(const char *line) -{ - static const char *lines[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - static char id = 3; - for (int i = 0; i < 7; i++) { - lines[i] = lines[i + 1]; - } - lines[7] = line; - oledClear(); - for (int i = 0; i < 8; i++) { - if (lines[i]) { - oledDrawChar(0, i * 8, '0' + (id + i) % 10, FONT_STANDARD); - oledDrawString(8, i * 8, lines[i], FONT_STANDARD); - } - } - oledRefresh(); - id = (id + 1) % 10; +void oledDebug(const char *line) { + static const char *lines[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + static char id = 3; + for (int i = 0; i < 7; i++) { + lines[i] = lines[i + 1]; + } + lines[7] = line; + oledClear(); + for (int i = 0; i < 8; i++) { + if (lines[i]) { + oledDrawChar(0, i * 8, '0' + (id + i) % 10, FONT_STANDARD); + oledDrawString(8, i * 8, lines[i], FONT_STANDARD); + } + } + oledRefresh(); + id = (id + 1) % 10; } -void debugLog(int level, const char *bucket, const char *text) -{ - (void)level; - (void)bucket; +void debugLog(int level, const char *bucket, const char *text) { + (void)level; + (void)bucket; #if EMULATOR - puts(text); + puts(text); #else - oledDebug(text); + oledDebug(text); #endif } -char *debugInt(const uint32_t i) -{ - static uint8_t n = 0; - static char id[8][9]; - uint32hex(i, id[n]); - debugLog(0, "", id[n]); - char *ret = (char *)id[n]; - n = (n + 1) % 8; - return ret; +char *debugInt(const uint32_t i) { + static uint8_t n = 0; + static char id[8][9]; + uint32hex(i, id[n]); + debugLog(0, "", id[n]); + char *ret = (char *)id[n]; + n = (n + 1) % 8; + return ret; } #endif diff --git a/firmware/debug.h b/firmware/debug.h index 94f9a0642..0a20807cd 100644 --- a/firmware/debug.h +++ b/firmware/debug.h @@ -20,8 +20,8 @@ #ifndef __DEBUG_H__ #define __DEBUG_H__ -#include "trezor.h" #include +#include "trezor.h" #if DEBUG_LOG @@ -30,8 +30,12 @@ char *debugInt(const uint32_t i); #else -#define debugLog(L, B, T) do{}while(0) -#define debugInt(I) do{}while(0) +#define debugLog(L, B, T) \ + do { \ + } while (0) +#define debugInt(I) \ + do { \ + } while (0) #endif diff --git a/firmware/ethereum.c b/firmware/ethereum.c index c9de7c9cc..51011cd6a 100644 --- a/firmware/ethereum.c +++ b/firmware/ethereum.c @@ -20,22 +20,22 @@ */ #include "ethereum.h" +#include "address.h" +#include "crypto.h" +#include "ecdsa.h" +#include "ethereum_networks.h" +#include "ethereum_tokens.h" #include "fsm.h" +#include "gettext.h" #include "layout2.h" +#include "memzero.h" #include "messages.h" -#include "transaction.h" -#include "ecdsa.h" +#include "messages.pb.h" #include "protect.h" -#include "crypto.h" #include "secp256k1.h" #include "sha3.h" -#include "address.h" +#include "transaction.h" #include "util.h" -#include "gettext.h" -#include "ethereum_tokens.h" -#include "ethereum_networks.h" -#include "memzero.h" -#include "messages.pb.h" /* maximum supported chain id. v must fit in an uint32_t. */ #define MAX_CHAIN_ID 2147483629 @@ -48,95 +48,90 @@ static uint32_t chain_id; static uint32_t tx_type; struct SHA3_CTX keccak_ctx; -static inline void hash_data(const uint8_t *buf, size_t size) -{ - sha3_Update(&keccak_ctx, buf, size); +static inline void hash_data(const uint8_t *buf, size_t size) { + sha3_Update(&keccak_ctx, buf, size); } /* * Push an RLP encoded length to the hash buffer. */ -static void hash_rlp_length(uint32_t length, uint8_t firstbyte) -{ - uint8_t buf[4]; - if (length == 1 && firstbyte <= 0x7f) { - /* empty length header */ - } else if (length <= 55) { - buf[0] = 0x80 + length; - hash_data(buf, 1); - } else if (length <= 0xff) { - buf[0] = 0xb7 + 1; - buf[1] = length; - hash_data(buf, 2); - } else if (length <= 0xffff) { - buf[0] = 0xb7 + 2; - buf[1] = length >> 8; - buf[2] = length & 0xff; - hash_data(buf, 3); - } else { - buf[0] = 0xb7 + 3; - buf[1] = length >> 16; - buf[2] = length >> 8; - buf[3] = length & 0xff; - hash_data(buf, 4); - } +static void hash_rlp_length(uint32_t length, uint8_t firstbyte) { + uint8_t buf[4]; + if (length == 1 && firstbyte <= 0x7f) { + /* empty length header */ + } else if (length <= 55) { + buf[0] = 0x80 + length; + hash_data(buf, 1); + } else if (length <= 0xff) { + buf[0] = 0xb7 + 1; + buf[1] = length; + hash_data(buf, 2); + } else if (length <= 0xffff) { + buf[0] = 0xb7 + 2; + buf[1] = length >> 8; + buf[2] = length & 0xff; + hash_data(buf, 3); + } else { + buf[0] = 0xb7 + 3; + buf[1] = length >> 16; + buf[2] = length >> 8; + buf[3] = length & 0xff; + hash_data(buf, 4); + } } /* * Push an RLP encoded list length to the hash buffer. */ -static void hash_rlp_list_length(uint32_t length) -{ - uint8_t buf[4]; - if (length <= 55) { - buf[0] = 0xc0 + length; - hash_data(buf, 1); - } else if (length <= 0xff) { - buf[0] = 0xf7 + 1; - buf[1] = length; - hash_data(buf, 2); - } else if (length <= 0xffff) { - buf[0] = 0xf7 + 2; - buf[1] = length >> 8; - buf[2] = length & 0xff; - hash_data(buf, 3); - } else { - buf[0] = 0xf7 + 3; - buf[1] = length >> 16; - buf[2] = length >> 8; - buf[3] = length & 0xff; - hash_data(buf, 4); - } +static void hash_rlp_list_length(uint32_t length) { + uint8_t buf[4]; + if (length <= 55) { + buf[0] = 0xc0 + length; + hash_data(buf, 1); + } else if (length <= 0xff) { + buf[0] = 0xf7 + 1; + buf[1] = length; + hash_data(buf, 2); + } else if (length <= 0xffff) { + buf[0] = 0xf7 + 2; + buf[1] = length >> 8; + buf[2] = length & 0xff; + hash_data(buf, 3); + } else { + buf[0] = 0xf7 + 3; + buf[1] = length >> 16; + buf[2] = length >> 8; + buf[3] = length & 0xff; + hash_data(buf, 4); + } } /* * Push an RLP encoded length field and data to the hash buffer. */ -static void hash_rlp_field(const uint8_t *buf, size_t size) -{ - hash_rlp_length(size, buf[0]); - hash_data(buf, size); +static void hash_rlp_field(const uint8_t *buf, size_t size) { + hash_rlp_length(size, buf[0]); + hash_data(buf, size); } /* * Push an RLP encoded number to the hash buffer. * Ethereum yellow paper says to convert to big endian and strip leading zeros. */ -static void hash_rlp_number(uint32_t number) -{ - if (!number) { - return; - } - uint8_t data[4]; - data[0] = (number >> 24) & 0xff; - data[1] = (number >> 16) & 0xff; - data[2] = (number >> 8) & 0xff; - data[3] = (number) & 0xff; - int offset = 0; - while (!data[offset]) { - offset++; - } - hash_rlp_field(data + offset, 4 - offset); +static void hash_rlp_number(uint32_t number) { + if (!number) { + return; + } + uint8_t data[4]; + data[0] = (number >> 24) & 0xff; + data[1] = (number >> 16) & 0xff; + data[2] = (number >> 8) & 0xff; + data[3] = (number)&0xff; + int offset = 0; + while (!data[offset]) { + offset++; + } + hash_rlp_field(data + offset, 4 - offset); } /* @@ -144,274 +139,242 @@ static void hash_rlp_number(uint32_t number) * NOTE: supports up to 16MB of data (how unlikely...) * FIXME: improve */ -static int rlp_calculate_length(int length, uint8_t firstbyte) -{ - if (length == 1 && firstbyte <= 0x7f) { - return 1; - } else if (length <= 55) { - return 1 + length; - } else if (length <= 0xff) { - return 2 + length; - } else if (length <= 0xffff) { - return 3 + length; - } else { - return 4 + length; - } +static int rlp_calculate_length(int length, uint8_t firstbyte) { + if (length == 1 && firstbyte <= 0x7f) { + return 1; + } else if (length <= 55) { + return 1 + length; + } else if (length <= 0xff) { + return 2 + length; + } else if (length <= 0xffff) { + return 3 + length; + } else { + return 4 + length; + } } -static int rlp_calculate_number_length(uint32_t number) -{ - if (number <= 0x7f) { - return 1; - } - else if (number <= 0xff) { - return 2; - } - else if (number <= 0xffff) { - return 3; - } - else if (number <= 0xffffff) { - return 4; - } else { - return 5; - } +static int rlp_calculate_number_length(uint32_t number) { + if (number <= 0x7f) { + return 1; + } else if (number <= 0xff) { + return 2; + } else if (number <= 0xffff) { + return 3; + } else if (number <= 0xffffff) { + return 4; + } else { + return 5; + } } -static void send_request_chunk(void) -{ - int progress = 1000 - (data_total > 1000000 - ? data_left / (data_total/800) - : data_left * 800 / data_total); - layoutProgress(_("Signing"), progress); - msg_tx_request.has_data_length = true; - msg_tx_request.data_length = data_left <= 1024 ? data_left : 1024; - msg_write(MessageType_MessageType_EthereumTxRequest, &msg_tx_request); +static void send_request_chunk(void) { + int progress = 1000 - (data_total > 1000000 ? data_left / (data_total / 800) + : data_left * 800 / data_total); + layoutProgress(_("Signing"), progress); + msg_tx_request.has_data_length = true; + msg_tx_request.data_length = data_left <= 1024 ? data_left : 1024; + msg_write(MessageType_MessageType_EthereumTxRequest, &msg_tx_request); } -static int ethereum_is_canonic(uint8_t v, uint8_t signature[64]) -{ - (void) signature; - return (v & 2) == 0; +static int ethereum_is_canonic(uint8_t v, uint8_t signature[64]) { + (void)signature; + return (v & 2) == 0; } -static void send_signature(void) -{ - uint8_t hash[32], sig[64]; - uint8_t v; - layoutProgress(_("Signing"), 1000); - - /* eip-155 replay protection */ - if (chain_id) { - /* hash v=chain_id, r=0, s=0 */ - hash_rlp_number(chain_id); - hash_rlp_length(0, 0); - hash_rlp_length(0, 0); - } - - keccak_Final(&keccak_ctx, hash); - if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v, ethereum_is_canonic) != 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); - ethereum_signing_abort(); - return; - } - - memzero(privkey, sizeof(privkey)); - - /* Send back the result */ - msg_tx_request.has_data_length = false; - - msg_tx_request.has_signature_v = true; - if (chain_id > MAX_CHAIN_ID) { - msg_tx_request.signature_v = v; - } else if (chain_id) { - msg_tx_request.signature_v = v + 2 * chain_id + 35; - } else { - msg_tx_request.signature_v = v + 27; - } - - msg_tx_request.has_signature_r = true; - msg_tx_request.signature_r.size = 32; - memcpy(msg_tx_request.signature_r.bytes, sig, 32); - - msg_tx_request.has_signature_s = true; - msg_tx_request.signature_s.size = 32; - memcpy(msg_tx_request.signature_s.bytes, sig + 32, 32); - - msg_write(MessageType_MessageType_EthereumTxRequest, &msg_tx_request); - - ethereum_signing_abort(); +static void send_signature(void) { + uint8_t hash[32], sig[64]; + uint8_t v; + layoutProgress(_("Signing"), 1000); + + /* eip-155 replay protection */ + if (chain_id) { + /* hash v=chain_id, r=0, s=0 */ + hash_rlp_number(chain_id); + hash_rlp_length(0, 0); + hash_rlp_length(0, 0); + } + + keccak_Final(&keccak_ctx, hash); + if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v, + ethereum_is_canonic) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); + ethereum_signing_abort(); + return; + } + + memzero(privkey, sizeof(privkey)); + + /* Send back the result */ + msg_tx_request.has_data_length = false; + + msg_tx_request.has_signature_v = true; + if (chain_id > MAX_CHAIN_ID) { + msg_tx_request.signature_v = v; + } else if (chain_id) { + msg_tx_request.signature_v = v + 2 * chain_id + 35; + } else { + msg_tx_request.signature_v = v + 27; + } + + msg_tx_request.has_signature_r = true; + msg_tx_request.signature_r.size = 32; + memcpy(msg_tx_request.signature_r.bytes, sig, 32); + + msg_tx_request.has_signature_s = true; + msg_tx_request.signature_s.size = 32; + memcpy(msg_tx_request.signature_s.bytes, sig + 32, 32); + + msg_write(MessageType_MessageType_EthereumTxRequest, &msg_tx_request); + + ethereum_signing_abort(); } /* Format a 256 bit number (amount in wei) into a human readable format * using standard ethereum units. * The buffer must be at least 25 bytes. */ -static void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token, char *buf, int buflen) -{ - bignum256 bn1e9; - bn_read_uint32(1000000000, &bn1e9); - const char *suffix = NULL; - int decimals = 18; - if (token == UnknownToken) { - strlcpy(buf, "Unknown token value", buflen); - return; - } else - if (token != NULL) { - suffix = token->ticker; - decimals = token->decimals; - } else - if (bn_is_less(amnt, &bn1e9)) { - suffix = " Wei"; - decimals = 0; - } else { - if (tx_type == 1 || tx_type == 6) { - suffix = " WAN"; - } else { - ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id); - } - } - bn_format(amnt, NULL, suffix, decimals, 0, false, buf, buflen); +static void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token, + char *buf, int buflen) { + bignum256 bn1e9; + bn_read_uint32(1000000000, &bn1e9); + const char *suffix = NULL; + int decimals = 18; + if (token == UnknownToken) { + strlcpy(buf, "Unknown token value", buflen); + return; + } else if (token != NULL) { + suffix = token->ticker; + decimals = token->decimals; + } else if (bn_is_less(amnt, &bn1e9)) { + suffix = " Wei"; + decimals = 0; + } else { + if (tx_type == 1 || tx_type == 6) { + suffix = " WAN"; + } else { + ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id); + } + } + bn_format(amnt, NULL, suffix, decimals, 0, false, buf, buflen); } -static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, const uint8_t *value, uint32_t value_len, const TokenType *token) -{ - bignum256 val; - uint8_t pad_val[32]; - memzero(pad_val, sizeof(pad_val)); - memcpy(pad_val + (32 - value_len), value, value_len); - bn_read_be(pad_val, &val); - - char amount[32]; - if (token == NULL) { - if (bn_is_zero(&val)) { - strcpy(amount, _("message")); - } else { - ethereumFormatAmount(&val, NULL, amount, sizeof(amount)); - } - } else { - ethereumFormatAmount(&val, token, amount, sizeof(amount)); - } - - char _to1[] = "to 0x__________"; - char _to2[] = "_______________"; - char _to3[] = "_______________?"; - - if (to_len) { - char to_str[41]; - - bool rskip60 = false; - // constants from trezor-common/defs/ethereum/networks.json - switch (chain_id) { - case 30: rskip60 = true; break; - case 31: rskip60 = true; break; - } - - ethereum_address_checksum(to, to_str, rskip60, chain_id); - memcpy(_to1 + 5, to_str, 10); - memcpy(_to2, to_str + 10, 15); - memcpy(_to3, to_str + 25, 15); - } else { - strlcpy(_to1, _("to new contract?"), sizeof(_to1)); - strlcpy(_to2, "", sizeof(_to2)); - strlcpy(_to3, "", sizeof(_to3)); - } - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Send"), - amount, - _to1, - _to2, - _to3, - NULL - ); +static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len, + const uint8_t *value, uint32_t value_len, + const TokenType *token) { + bignum256 val; + uint8_t pad_val[32]; + memzero(pad_val, sizeof(pad_val)); + memcpy(pad_val + (32 - value_len), value, value_len); + bn_read_be(pad_val, &val); + + char amount[32]; + if (token == NULL) { + if (bn_is_zero(&val)) { + strcpy(amount, _("message")); + } else { + ethereumFormatAmount(&val, NULL, amount, sizeof(amount)); + } + } else { + ethereumFormatAmount(&val, token, amount, sizeof(amount)); + } + + char _to1[] = "to 0x__________"; + char _to2[] = "_______________"; + char _to3[] = "_______________?"; + + if (to_len) { + char to_str[41]; + + bool rskip60 = false; + // constants from trezor-common/defs/ethereum/networks.json + switch (chain_id) { + case 30: + rskip60 = true; + break; + case 31: + rskip60 = true; + break; + } + + ethereum_address_checksum(to, to_str, rskip60, chain_id); + memcpy(_to1 + 5, to_str, 10); + memcpy(_to2, to_str + 10, 15); + memcpy(_to3, to_str + 25, 15); + } else { + strlcpy(_to1, _("to new contract?"), sizeof(_to1)); + strlcpy(_to2, "", sizeof(_to2)); + strlcpy(_to3, "", sizeof(_to3)); + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Send"), amount, _to1, _to2, _to3, NULL); } -static void layoutEthereumData(const uint8_t *data, uint32_t len, uint32_t total_len) -{ - char hexdata[3][17]; - char summary[20]; - uint32_t printed = 0; - for (int i = 0; i < 3; i++) { - uint32_t linelen = len - printed; - if (linelen > 8) { - linelen = 8; - } - data2hex(data, linelen, hexdata[i]); - data += linelen; - printed += linelen; - } - - strcpy(summary, "... bytes"); - char *p = summary + 11; - uint32_t number = total_len; - while (number > 0) { - *p-- = '0' + number % 10; - number = number / 10; - } - char *summarystart = summary; - if (total_len == printed) - summarystart = summary + 4; - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Transaction data:"), - hexdata[0], - hexdata[1], - hexdata[2], - summarystart, - NULL - ); +static void layoutEthereumData(const uint8_t *data, uint32_t len, + uint32_t total_len) { + char hexdata[3][17]; + char summary[20]; + uint32_t printed = 0; + for (int i = 0; i < 3; i++) { + uint32_t linelen = len - printed; + if (linelen > 8) { + linelen = 8; + } + data2hex(data, linelen, hexdata[i]); + data += linelen; + printed += linelen; + } + + strcpy(summary, "... bytes"); + char *p = summary + 11; + uint32_t number = total_len; + while (number > 0) { + *p-- = '0' + number % 10; + number = number / 10; + } + char *summarystart = summary; + if (total_len == printed) summarystart = summary + 4; + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Transaction data:"), hexdata[0], hexdata[1], hexdata[2], + summarystart, NULL); } static void layoutEthereumFee(const uint8_t *value, uint32_t value_len, - const uint8_t *gas_price, uint32_t gas_price_len, - const uint8_t *gas_limit, uint32_t gas_limit_len, - bool is_token) -{ - bignum256 val, gas; - uint8_t pad_val[32]; - char tx_value[32]; - char gas_value[32]; - - memzero(tx_value, sizeof(tx_value)); - memzero(gas_value, sizeof(gas_value)); - - memzero(pad_val, sizeof(pad_val)); - memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len); - bn_read_be(pad_val, &val); - - memzero(pad_val, sizeof(pad_val)); - memcpy(pad_val + (32 - gas_limit_len), gas_limit, gas_limit_len); - bn_read_be(pad_val, &gas); - bn_multiply(&val, &gas, &secp256k1.prime); - - ethereumFormatAmount(&gas, NULL, gas_value, sizeof(gas_value)); - - memzero(pad_val, sizeof(pad_val)); - memcpy(pad_val + (32 - value_len), value, value_len); - bn_read_be(pad_val, &val); - - if (bn_is_zero(&val)) { - strcpy(tx_value, is_token ? _("token") : _("message")); - } else { - ethereumFormatAmount(&val, NULL, tx_value, sizeof(tx_value)); - } - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Really send"), - tx_value, - _("paying up to"), - gas_value, - _("for gas?"), - NULL - ); + const uint8_t *gas_price, uint32_t gas_price_len, + const uint8_t *gas_limit, uint32_t gas_limit_len, + bool is_token) { + bignum256 val, gas; + uint8_t pad_val[32]; + char tx_value[32]; + char gas_value[32]; + + memzero(tx_value, sizeof(tx_value)); + memzero(gas_value, sizeof(gas_value)); + + memzero(pad_val, sizeof(pad_val)); + memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len); + bn_read_be(pad_val, &val); + + memzero(pad_val, sizeof(pad_val)); + memcpy(pad_val + (32 - gas_limit_len), gas_limit, gas_limit_len); + bn_read_be(pad_val, &gas); + bn_multiply(&val, &gas, &secp256k1.prime); + + ethereumFormatAmount(&gas, NULL, gas_value, sizeof(gas_value)); + + memzero(pad_val, sizeof(pad_val)); + memcpy(pad_val + (32 - value_len), value, value_len); + bn_read_be(pad_val, &val); + + if (bn_is_zero(&val)) { + strcpy(tx_value, is_token ? _("token") : _("message")); + } else { + ethereumFormatAmount(&val, NULL, tx_value, sizeof(tx_value)); + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Really send"), tx_value, _("paying up to"), gas_value, + _("for gas?"), NULL); } /* @@ -424,353 +387,385 @@ static void layoutEthereumFee(const uint8_t *value, uint32_t value_len, * - data (0 ..) */ -static bool ethereum_signing_check(const EthereumSignTx *msg) -{ - if (!msg->has_gas_price || !msg->has_gas_limit) { - return false; - } - - size_t tolen = msg->has_to ? strlen(msg->to) : 0; - - if (tolen != 42 && tolen != 40 && tolen != 0) { - /* Address has wrong length */ - return false; - } - - // sending transaction to address 0 (contract creation) without a data field - if (tolen == 0 && (!msg->has_data_length || msg->data_length == 0)) { - return false; - } - - if (msg->gas_price.size + msg->gas_limit.size > 30) { - // sanity check that fee doesn't overflow - return false; - } - - return true; +static bool ethereum_signing_check(const EthereumSignTx *msg) { + if (!msg->has_gas_price || !msg->has_gas_limit) { + return false; + } + + size_t tolen = msg->has_to ? strlen(msg->to) : 0; + + if (tolen != 42 && tolen != 40 && tolen != 0) { + /* Address has wrong length */ + return false; + } + + // sending transaction to address 0 (contract creation) without a data field + if (tolen == 0 && (!msg->has_data_length || msg->data_length == 0)) { + return false; + } + + if (msg->gas_price.size + msg->gas_limit.size > 30) { + // sanity check that fee doesn't overflow + return false; + } + + return true; } -void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) -{ - ethereum_signing = true; - sha3_256_Init(&keccak_ctx); - - memzero(&msg_tx_request, sizeof(EthereumTxRequest)); - /* set fields to 0, to avoid conditions later */ - if (!msg->has_value) - msg->value.size = 0; - if (!msg->has_data_initial_chunk) - msg->data_initial_chunk.size = 0; - bool toset; - uint8_t pubkeyhash[20]; - if (msg->has_to && ethereum_parse(msg->to, pubkeyhash)) { - toset = true; - } else { - msg->to[0] = 0; - toset = false; - memzero(pubkeyhash, sizeof(pubkeyhash)); - } - if (!msg->has_nonce) - msg->nonce.size = 0; - - /* eip-155 chain id */ - if (msg->has_chain_id) { - if (msg->chain_id < 1) { - fsm_sendFailure(FailureType_Failure_DataError, _("Chain Id out of bounds")); - ethereum_signing_abort(); - return; - } - chain_id = msg->chain_id; - } else { - chain_id = 0; - } - - /* Wanchain txtype */ - if (msg->has_tx_type) { - if (msg->tx_type == 1 || msg->tx_type == 6) { - tx_type = msg->tx_type; - } else { - fsm_sendFailure(FailureType_Failure_DataError, _("Txtype out of bounds")); - ethereum_signing_abort(); - return; - } - } else { - tx_type = 0; - } - - if (msg->has_data_length && msg->data_length > 0) { - if (!msg->has_data_initial_chunk || msg->data_initial_chunk.size == 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Data length provided, but no initial chunk")); - ethereum_signing_abort(); - return; - } - /* Our encoding only supports transactions up to 2^24 bytes. To - * prevent exceeding the limit we use a stricter limit on data length. - */ - if (msg->data_length > 16000000) { - fsm_sendFailure(FailureType_Failure_DataError, _("Data length exceeds limit")); - ethereum_signing_abort(); - return; - } - data_total = msg->data_length; - } else { - data_total = 0; - } - if (msg->data_initial_chunk.size > data_total) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid size of initial chunk")); - ethereum_signing_abort(); - return; - } - - // safety checks - if (!ethereum_signing_check(msg)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Safety check failed")); - ethereum_signing_abort(); - return; - } - - const TokenType *token = NULL; - - // detect ERC-20 token - if (toset && msg->value.size == 0 && data_total == 68 && msg->data_initial_chunk.size == 68 - && memcmp(msg->data_initial_chunk.bytes, "\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) { - token = tokenByChainAddress(chain_id, pubkeyhash); - } - - if (token != NULL) { - layoutEthereumConfirmTx(msg->data_initial_chunk.bytes + 16, 20, msg->data_initial_chunk.bytes + 36, 32, token); - } else { - layoutEthereumConfirmTx(pubkeyhash, 20, msg->value.bytes, msg->value.size, NULL); - } - - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - ethereum_signing_abort(); - return; - } - - if (token == NULL && data_total > 0) { - layoutEthereumData(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size, data_total); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - ethereum_signing_abort(); - return; - } - } - - layoutEthereumFee(msg->value.bytes, msg->value.size, - msg->gas_price.bytes, msg->gas_price.size, - msg->gas_limit.bytes, msg->gas_limit.size, token != NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - ethereum_signing_abort(); - return; - } - - /* Stage 1: Calculate total RLP length */ - uint32_t rlp_length = 0; - - layoutProgress(_("Signing"), 0); - - rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); - rlp_length += rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); - rlp_length += rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); - rlp_length += rlp_calculate_length(toset ? 20 : 0, pubkeyhash[0]); - rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); - rlp_length += rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]); - if (tx_type) { - rlp_length += rlp_calculate_number_length(tx_type); - } - if (chain_id) { - rlp_length += rlp_calculate_number_length(chain_id); - rlp_length += rlp_calculate_length(0, 0); - rlp_length += rlp_calculate_length(0, 0); - } - - /* Stage 2: Store header fields */ - hash_rlp_list_length(rlp_length); - - layoutProgress(_("Signing"), 100); - - if (tx_type) { - hash_rlp_number(tx_type); - } - hash_rlp_field(msg->nonce.bytes, msg->nonce.size); - hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); - hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); - hash_rlp_field(pubkeyhash, toset ? 20 : 0); - hash_rlp_field(msg->value.bytes, msg->value.size); - hash_rlp_length(data_total, msg->data_initial_chunk.bytes[0]); - hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); - data_left = data_total - msg->data_initial_chunk.size; - - memcpy(privkey, node->private_key, 32); - - if (data_left > 0) { - send_request_chunk(); - } else { - send_signature(); - } +void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) { + ethereum_signing = true; + sha3_256_Init(&keccak_ctx); + + memzero(&msg_tx_request, sizeof(EthereumTxRequest)); + /* set fields to 0, to avoid conditions later */ + if (!msg->has_value) msg->value.size = 0; + if (!msg->has_data_initial_chunk) msg->data_initial_chunk.size = 0; + bool toset; + uint8_t pubkeyhash[20]; + if (msg->has_to && ethereum_parse(msg->to, pubkeyhash)) { + toset = true; + } else { + msg->to[0] = 0; + toset = false; + memzero(pubkeyhash, sizeof(pubkeyhash)); + } + if (!msg->has_nonce) msg->nonce.size = 0; + + /* eip-155 chain id */ + if (msg->has_chain_id) { + if (msg->chain_id < 1) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Chain Id out of bounds")); + ethereum_signing_abort(); + return; + } + chain_id = msg->chain_id; + } else { + chain_id = 0; + } + + /* Wanchain txtype */ + if (msg->has_tx_type) { + if (msg->tx_type == 1 || msg->tx_type == 6) { + tx_type = msg->tx_type; + } else { + fsm_sendFailure(FailureType_Failure_DataError, _("Txtype out of bounds")); + ethereum_signing_abort(); + return; + } + } else { + tx_type = 0; + } + + if (msg->has_data_length && msg->data_length > 0) { + if (!msg->has_data_initial_chunk || msg->data_initial_chunk.size == 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Data length provided, but no initial chunk")); + ethereum_signing_abort(); + return; + } + /* Our encoding only supports transactions up to 2^24 bytes. To + * prevent exceeding the limit we use a stricter limit on data length. + */ + if (msg->data_length > 16000000) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Data length exceeds limit")); + ethereum_signing_abort(); + return; + } + data_total = msg->data_length; + } else { + data_total = 0; + } + if (msg->data_initial_chunk.size > data_total) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid size of initial chunk")); + ethereum_signing_abort(); + return; + } + + // safety checks + if (!ethereum_signing_check(msg)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Safety check failed")); + ethereum_signing_abort(); + return; + } + + const TokenType *token = NULL; + + // detect ERC-20 token + if (toset && msg->value.size == 0 && data_total == 68 && + msg->data_initial_chunk.size == 68 && + memcmp(msg->data_initial_chunk.bytes, + "\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 16) == 0) { + token = tokenByChainAddress(chain_id, pubkeyhash); + } + + if (token != NULL) { + layoutEthereumConfirmTx(msg->data_initial_chunk.bytes + 16, 20, + msg->data_initial_chunk.bytes + 36, 32, token); + } else { + layoutEthereumConfirmTx(pubkeyhash, 20, msg->value.bytes, msg->value.size, + NULL); + } + + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + ethereum_signing_abort(); + return; + } + + if (token == NULL && data_total > 0) { + layoutEthereumData(msg->data_initial_chunk.bytes, + msg->data_initial_chunk.size, data_total); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + ethereum_signing_abort(); + return; + } + } + + layoutEthereumFee(msg->value.bytes, msg->value.size, msg->gas_price.bytes, + msg->gas_price.size, msg->gas_limit.bytes, + msg->gas_limit.size, token != NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + ethereum_signing_abort(); + return; + } + + /* Stage 1: Calculate total RLP length */ + uint32_t rlp_length = 0; + + layoutProgress(_("Signing"), 0); + + rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]); + rlp_length += + rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]); + rlp_length += + rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]); + rlp_length += rlp_calculate_length(toset ? 20 : 0, pubkeyhash[0]); + rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]); + rlp_length += + rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]); + if (tx_type) { + rlp_length += rlp_calculate_number_length(tx_type); + } + if (chain_id) { + rlp_length += rlp_calculate_number_length(chain_id); + rlp_length += rlp_calculate_length(0, 0); + rlp_length += rlp_calculate_length(0, 0); + } + + /* Stage 2: Store header fields */ + hash_rlp_list_length(rlp_length); + + layoutProgress(_("Signing"), 100); + + if (tx_type) { + hash_rlp_number(tx_type); + } + hash_rlp_field(msg->nonce.bytes, msg->nonce.size); + hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size); + hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size); + hash_rlp_field(pubkeyhash, toset ? 20 : 0); + hash_rlp_field(msg->value.bytes, msg->value.size); + hash_rlp_length(data_total, msg->data_initial_chunk.bytes[0]); + hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size); + data_left = data_total - msg->data_initial_chunk.size; + + memcpy(privkey, node->private_key, 32); + + if (data_left > 0) { + send_request_chunk(); + } else { + send_signature(); + } } -void ethereum_signing_txack(const EthereumTxAck *tx) -{ - if (!ethereum_signing) { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Ethereum signing mode")); - layoutHome(); - return; - } - - if (tx->data_chunk.size > data_left) { - fsm_sendFailure(FailureType_Failure_DataError, _("Too much data")); - ethereum_signing_abort(); - return; - } - - if (data_left > 0 && (!tx->has_data_chunk || tx->data_chunk.size == 0)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Empty data chunk received")); - ethereum_signing_abort(); - return; - } - - hash_data(tx->data_chunk.bytes, tx->data_chunk.size); - - data_left -= tx->data_chunk.size; - - if (data_left > 0) { - send_request_chunk(); - } else { - send_signature(); - } +void ethereum_signing_txack(const EthereumTxAck *tx) { + if (!ethereum_signing) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Not in Ethereum signing mode")); + layoutHome(); + return; + } + + if (tx->data_chunk.size > data_left) { + fsm_sendFailure(FailureType_Failure_DataError, _("Too much data")); + ethereum_signing_abort(); + return; + } + + if (data_left > 0 && (!tx->has_data_chunk || tx->data_chunk.size == 0)) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Empty data chunk received")); + ethereum_signing_abort(); + return; + } + + hash_data(tx->data_chunk.bytes, tx->data_chunk.size); + + data_left -= tx->data_chunk.size; + + if (data_left > 0) { + send_request_chunk(); + } else { + send_signature(); + } } -void ethereum_signing_abort(void) -{ - if (ethereum_signing) { - memzero(privkey, sizeof(privkey)); - layoutHome(); - ethereum_signing = false; - } +void ethereum_signing_abort(void) { + if (ethereum_signing) { + memzero(privkey, sizeof(privkey)); + layoutHome(); + ethereum_signing = false; + } } -static void ethereum_message_hash(const uint8_t *message, size_t message_len, uint8_t hash[32]) -{ - struct SHA3_CTX ctx; - sha3_256_Init(&ctx); - sha3_Update(&ctx, (const uint8_t *)"\x19" "Ethereum Signed Message:\n", 26); - uint8_t c; - if (message_len > 1000000000) { c = '0' + message_len / 1000000000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 100000000) { c = '0' + message_len / 100000000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 10000000) { c = '0' + message_len / 10000000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 1000000) { c = '0' + message_len / 1000000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 100000) { c = '0' + message_len / 100000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 10000) { c = '0' + message_len / 10000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 1000) { c = '0' + message_len / 1000 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 100) { c = '0' + message_len / 100 % 10; sha3_Update(&ctx, &c, 1); } - if (message_len > 10) { c = '0' + message_len / 10 % 10; sha3_Update(&ctx, &c, 1); } - c = '0' + message_len % 10; sha3_Update(&ctx, &c, 1); - sha3_Update(&ctx, message, message_len); - keccak_Final(&ctx, hash); +static void ethereum_message_hash(const uint8_t *message, size_t message_len, + uint8_t hash[32]) { + struct SHA3_CTX ctx; + sha3_256_Init(&ctx); + sha3_Update(&ctx, (const uint8_t *)"\x19" "Ethereum Signed Message:\n", 26); + uint8_t c; + if (message_len > 1000000000) { + c = '0' + message_len / 1000000000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 100000000) { + c = '0' + message_len / 100000000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 10000000) { + c = '0' + message_len / 10000000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 1000000) { + c = '0' + message_len / 1000000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 100000) { + c = '0' + message_len / 100000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 10000) { + c = '0' + message_len / 10000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 1000) { + c = '0' + message_len / 1000 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 100) { + c = '0' + message_len / 100 % 10; + sha3_Update(&ctx, &c, 1); + } + if (message_len > 10) { + c = '0' + message_len / 10 % 10; + sha3_Update(&ctx, &c, 1); + } + c = '0' + message_len % 10; + sha3_Update(&ctx, &c, 1); + sha3_Update(&ctx, message, message_len); + keccak_Final(&ctx, hash); } -void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, EthereumMessageSignature *resp) -{ - uint8_t pubkeyhash[20]; - if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) { - return; - } - - resp->has_address = true; - resp->address[0] = '0'; - resp->address[1] = 'x'; - ethereum_address_checksum(pubkeyhash, resp->address + 2, false, 0); - // ethereum_address_checksum adds trailing zero - - uint8_t hash[32]; - ethereum_message_hash(msg->message.bytes, msg->message.size, hash); - - uint8_t v; - if (ecdsa_sign_digest(&secp256k1, node->private_key, hash, resp->signature.bytes, &v, ethereum_is_canonic) != 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); - return; - } - - resp->has_signature = true; - resp->signature.bytes[64] = 27 + v; - resp->signature.size = 65; - msg_write(MessageType_MessageType_EthereumMessageSignature, resp); +void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, + EthereumMessageSignature *resp) { + uint8_t pubkeyhash[20]; + if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) { + return; + } + + resp->has_address = true; + resp->address[0] = '0'; + resp->address[1] = 'x'; + ethereum_address_checksum(pubkeyhash, resp->address + 2, false, 0); + // ethereum_address_checksum adds trailing zero + + uint8_t hash[32]; + ethereum_message_hash(msg->message.bytes, msg->message.size, hash); + + uint8_t v; + if (ecdsa_sign_digest(&secp256k1, node->private_key, hash, + resp->signature.bytes, &v, ethereum_is_canonic) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); + return; + } + + resp->has_signature = true; + resp->signature.bytes[64] = 27 + v; + resp->signature.size = 65; + msg_write(MessageType_MessageType_EthereumMessageSignature, resp); } -int ethereum_message_verify(const EthereumVerifyMessage *msg) -{ - if (msg->signature.size != 65) { - fsm_sendFailure(FailureType_Failure_DataError, _("Malformed signature")); - return 1; - } - - uint8_t pubkeyhash[20]; - if (!ethereum_parse(msg->address, pubkeyhash)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Malformed address")); - return 1; - } - - uint8_t pubkey[65]; - uint8_t hash[32]; - - ethereum_message_hash(msg->message.bytes, msg->message.size, hash); - - /* v should be 27, 28 but some implementations use 0,1. We are - * compatible with both. - */ - uint8_t v = msg->signature.bytes[64]; - if (v >= 27) { - v -= 27; - } - if (v >= 2 || - ecdsa_recover_pub_from_sig(&secp256k1, pubkey, msg->signature.bytes, hash, v) != 0) { - return 2; - } - - struct SHA3_CTX ctx; - sha3_256_Init(&ctx); - sha3_Update(&ctx, pubkey + 1, 64); - keccak_Final(&ctx, hash); - - /* result are the least significant 160 bits */ - if (memcmp(pubkeyhash, hash + 12, 20) != 0) { - return 2; - } - return 0; +int ethereum_message_verify(const EthereumVerifyMessage *msg) { + if (msg->signature.size != 65) { + fsm_sendFailure(FailureType_Failure_DataError, _("Malformed signature")); + return 1; + } + + uint8_t pubkeyhash[20]; + if (!ethereum_parse(msg->address, pubkeyhash)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Malformed address")); + return 1; + } + + uint8_t pubkey[65]; + uint8_t hash[32]; + + ethereum_message_hash(msg->message.bytes, msg->message.size, hash); + + /* v should be 27, 28 but some implementations use 0,1. We are + * compatible with both. + */ + uint8_t v = msg->signature.bytes[64]; + if (v >= 27) { + v -= 27; + } + if (v >= 2 || ecdsa_recover_pub_from_sig( + &secp256k1, pubkey, msg->signature.bytes, hash, v) != 0) { + return 2; + } + + struct SHA3_CTX ctx; + sha3_256_Init(&ctx); + sha3_Update(&ctx, pubkey + 1, 64); + keccak_Final(&ctx, hash); + + /* result are the least significant 160 bits */ + if (memcmp(pubkeyhash, hash + 12, 20) != 0) { + return 2; + } + return 0; } -bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) -{ - memzero(pubkeyhash, 20); - size_t len = strlen(address); - if (len == 40) { - // do nothing - } else - if (len == 42) { - // check for "0x" prefix and strip it when required - if (address[0] != '0') return false; - if (address[1] != 'x' && address[1] != 'X') return false; - address += 2; - len -= 2; - } else { - return false; - } - for (size_t i = 0; i < len; i++) { - if (address[i] >= '0' && address[i] <= '9') { - pubkeyhash[i / 2] |= (address[i] - '0') << ((1 - (i % 2)) * 4); - } else - if (address[i] >= 'a' && address[i] <= 'f') { - pubkeyhash[i / 2] |= ((address[i] - 'a') + 10) << ((1 - (i % 2)) * 4); - } else - if (address[i] >= 'A' && address[i] <= 'F') { - pubkeyhash[i / 2] |= ((address[i] - 'A') + 10) << ((1 - (i % 2)) * 4); - } else { - return false; - } - } - return true; +bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) { + memzero(pubkeyhash, 20); + size_t len = strlen(address); + if (len == 40) { + // do nothing + } else if (len == 42) { + // check for "0x" prefix and strip it when required + if (address[0] != '0') return false; + if (address[1] != 'x' && address[1] != 'X') return false; + address += 2; + len -= 2; + } else { + return false; + } + for (size_t i = 0; i < len; i++) { + if (address[i] >= '0' && address[i] <= '9') { + pubkeyhash[i / 2] |= (address[i] - '0') << ((1 - (i % 2)) * 4); + } else if (address[i] >= 'a' && address[i] <= 'f') { + pubkeyhash[i / 2] |= ((address[i] - 'a') + 10) << ((1 - (i % 2)) * 4); + } else if (address[i] >= 'A' && address[i] <= 'F') { + pubkeyhash[i / 2] |= ((address[i] - 'A') + 10) << ((1 - (i % 2)) * 4); + } else { + return false; + } + } + return true; } diff --git a/firmware/ethereum.h b/firmware/ethereum.h index 0bcc00627..3e8ad02f6 100644 --- a/firmware/ethereum.h +++ b/firmware/ethereum.h @@ -20,8 +20,8 @@ #ifndef __ETHEREUM_H__ #define __ETHEREUM_H__ -#include #include +#include #include "bip32.h" #include "messages-ethereum.pb.h" @@ -29,7 +29,8 @@ void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node); void ethereum_signing_abort(void); void ethereum_signing_txack(const EthereumTxAck *msg); -void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, EthereumMessageSignature *resp); +void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node, + EthereumMessageSignature *resp); int ethereum_message_verify(const EthereumVerifyMessage *msg); bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]); diff --git a/firmware/fsm.c b/firmware/fsm.c index ce0732fa1..59f93774f 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -19,235 +19,242 @@ #include -#include "trezor.h" -#include "fsm.h" -#include "messages.h" -#include "bip32.h" -#include "config.h" -#include "coins.h" -#include "debug.h" -#include "transaction.h" -#include "rng.h" -#include "oled.h" -#include "protect.h" -#include "pinmatrix.h" -#include "layout2.h" #include "address.h" -#include "base58.h" -#include "ecdsa.h" -#include "reset.h" -#include "recovery.h" -#include "memory.h" -#include "usb.h" -#include "util.h" -#include "signing.h" #include "aes/aes.h" -#include "hmac.h" -#include "crypto.h" #include "base58.h" +#include "bip32.h" #include "bip39.h" +#include "coins.h" +#include "config.h" +#include "crypto.h" #include "curves.h" -#include "secp256k1.h" +#include "debug.h" +#include "ecdsa.h" #include "ethereum.h" +#include "fsm.h" +#include "gettext.h" +#include "hmac.h" +#include "layout2.h" +#include "lisk.h" +#include "memory.h" +#include "memzero.h" +#include "messages.h" +#include "messages.pb.h" #include "nem.h" #include "nem2.h" +#include "oled.h" +#include "pinmatrix.h" +#include "protect.h" +#include "recovery.h" +#include "reset.h" #include "rfc6979.h" -#include "gettext.h" -#include "supervise.h" -#include "messages.pb.h" +#include "rng.h" +#include "secp256k1.h" +#include "signing.h" #include "stellar.h" -#include "lisk.h" -#include "memzero.h" +#include "supervise.h" +#include "transaction.h" +#include "trezor.h" +#include "usb.h" +#include "util.h" // message methods -static uint8_t msg_resp[MSG_OUT_SIZE] __attribute__ ((aligned)); - -#define RESP_INIT(TYPE) \ - TYPE *resp = (TYPE *) (void *) msg_resp; \ - _Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \ - memzero(resp, sizeof(TYPE)); - -#define CHECK_INITIALIZED \ - if (!config_isInitialized()) { \ - fsm_sendFailure(FailureType_Failure_NotInitialized, NULL); \ - return; \ - } - -#define CHECK_NOT_INITIALIZED \ - if (config_isInitialized()) { \ - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Device is already initialized. Use Wipe first.")); \ - return; \ - } - -#define CHECK_PIN \ - if (!protectPin(true)) { \ - layoutHome(); \ - return; \ - } - -#define CHECK_PIN_UNCACHED \ - if (!protectPin(false)) { \ - layoutHome(); \ - return; \ - } - -#define CHECK_PARAM(cond, errormsg) \ - if (!(cond)) { \ - fsm_sendFailure(FailureType_Failure_DataError, (errormsg)); \ - layoutHome(); \ - return; \ - } - -void fsm_sendSuccess(const char *text) -{ - RESP_INIT(Success); - if (text) { - resp->has_message = true; - strlcpy(resp->message, text, sizeof(resp->message)); - } - msg_write(MessageType_MessageType_Success, resp); +static uint8_t msg_resp[MSG_OUT_SIZE] __attribute__((aligned)); + +#define RESP_INIT(TYPE) \ + TYPE *resp = (TYPE *)(void *)msg_resp; \ + _Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \ + memzero(resp, sizeof(TYPE)); + +#define CHECK_INITIALIZED \ + if (!config_isInitialized()) { \ + fsm_sendFailure(FailureType_Failure_NotInitialized, NULL); \ + return; \ + } + +#define CHECK_NOT_INITIALIZED \ + if (config_isInitialized()) { \ + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, \ + _("Device is already initialized. Use Wipe first.")); \ + return; \ + } + +#define CHECK_PIN \ + if (!protectPin(true)) { \ + layoutHome(); \ + return; \ + } + +#define CHECK_PIN_UNCACHED \ + if (!protectPin(false)) { \ + layoutHome(); \ + return; \ + } + +#define CHECK_PARAM(cond, errormsg) \ + if (!(cond)) { \ + fsm_sendFailure(FailureType_Failure_DataError, (errormsg)); \ + layoutHome(); \ + return; \ + } + +void fsm_sendSuccess(const char *text) { + RESP_INIT(Success); + if (text) { + resp->has_message = true; + strlcpy(resp->message, text, sizeof(resp->message)); + } + msg_write(MessageType_MessageType_Success, resp); } #if DEBUG_LINK -void fsm_sendFailureDebug(FailureType code, const char *text, const char *source) +void fsm_sendFailureDebug(FailureType code, const char *text, + const char *source) #else void fsm_sendFailure(FailureType code, const char *text) #endif { - if (protectAbortedByCancel) { - protectAbortedByCancel = false; - } - if (protectAbortedByInitialize) { - fsm_msgInitialize((Initialize *)0); - protectAbortedByInitialize = false; - return; - } - RESP_INIT(Failure); - resp->has_code = true; - resp->code = code; - if (!text) { - switch (code) { - case FailureType_Failure_UnexpectedMessage: - text = _("Unexpected message"); - break; - case FailureType_Failure_ButtonExpected: - text = _("Button expected"); - break; - case FailureType_Failure_DataError: - text = _("Data error"); - break; - case FailureType_Failure_ActionCancelled: - text = _("Action cancelled by user"); - break; - case FailureType_Failure_PinExpected: - text = _("PIN expected"); - break; - case FailureType_Failure_PinCancelled: - text = _("PIN cancelled"); - break; - case FailureType_Failure_PinInvalid: - text = _("PIN invalid"); - break; - case FailureType_Failure_InvalidSignature: - text = _("Invalid signature"); - break; - case FailureType_Failure_ProcessError: - text = _("Process error"); - break; - case FailureType_Failure_NotEnoughFunds: - text = _("Not enough funds"); - break; - case FailureType_Failure_NotInitialized: - text = _("Device not initialized"); - break; - case FailureType_Failure_PinMismatch: - text = _("PIN mismatch"); - break; - case FailureType_Failure_FirmwareError: - text = _("Firmware error"); - break; - } - } + if (protectAbortedByCancel) { + protectAbortedByCancel = false; + } + if (protectAbortedByInitialize) { + fsm_msgInitialize((Initialize *)0); + protectAbortedByInitialize = false; + return; + } + RESP_INIT(Failure); + resp->has_code = true; + resp->code = code; + if (!text) { + switch (code) { + case FailureType_Failure_UnexpectedMessage: + text = _("Unexpected message"); + break; + case FailureType_Failure_ButtonExpected: + text = _("Button expected"); + break; + case FailureType_Failure_DataError: + text = _("Data error"); + break; + case FailureType_Failure_ActionCancelled: + text = _("Action cancelled by user"); + break; + case FailureType_Failure_PinExpected: + text = _("PIN expected"); + break; + case FailureType_Failure_PinCancelled: + text = _("PIN cancelled"); + break; + case FailureType_Failure_PinInvalid: + text = _("PIN invalid"); + break; + case FailureType_Failure_InvalidSignature: + text = _("Invalid signature"); + break; + case FailureType_Failure_ProcessError: + text = _("Process error"); + break; + case FailureType_Failure_NotEnoughFunds: + text = _("Not enough funds"); + break; + case FailureType_Failure_NotInitialized: + text = _("Device not initialized"); + break; + case FailureType_Failure_PinMismatch: + text = _("PIN mismatch"); + break; + case FailureType_Failure_FirmwareError: + text = _("Firmware error"); + break; + } + } #if DEBUG_LINK - resp->has_message = true; - strlcpy(resp->message, source, sizeof(resp->message)); - if (text) { - strlcat(resp->message, text, sizeof(resp->message)); - } + resp->has_message = true; + strlcpy(resp->message, source, sizeof(resp->message)); + if (text) { + strlcat(resp->message, text, sizeof(resp->message)); + } #else - if (text) { - resp->has_message = true; - strlcpy(resp->message, text, sizeof(resp->message)); - } + if (text) { + resp->has_message = true; + strlcpy(resp->message, text, sizeof(resp->message)); + } #endif - msg_write(MessageType_MessageType_Failure, resp); + msg_write(MessageType_MessageType_Failure, resp); } -static const CoinInfo *fsm_getCoin(bool has_name, const char *name) -{ - const CoinInfo *coin; - if (has_name) { - coin = coinByName(name); - } else { - coin = coinByName("Bitcoin"); - } - if (!coin) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid coin name")); - layoutHome(); - return 0; - } - return coin; +static const CoinInfo *fsm_getCoin(bool has_name, const char *name) { + const CoinInfo *coin; + if (has_name) { + coin = coinByName(name); + } else { + coin = coinByName("Bitcoin"); + } + if (!coin) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid coin name")); + layoutHome(); + return 0; + } + return coin; } -static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, size_t address_n_count, uint32_t *fingerprint) -{ - static CONFIDENTIAL HDNode node; - if (fingerprint) { - *fingerprint = 0; - } - if (!config_getRootNode(&node, curve, true)) { - fsm_sendFailure(FailureType_Failure_NotInitialized, _("Device not initialized or passphrase request cancelled or unsupported curve")); - layoutHome(); - return 0; - } - if (!address_n || address_n_count == 0) { - return &node; - } - if (hdnode_private_ckd_cached(&node, address_n, address_n_count, fingerprint) == 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to derive private key")); - layoutHome(); - return 0; - } - return &node; +static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n, + size_t address_n_count, + uint32_t *fingerprint) { + static CONFIDENTIAL HDNode node; + if (fingerprint) { + *fingerprint = 0; + } + if (!config_getRootNode(&node, curve, true)) { + fsm_sendFailure(FailureType_Failure_NotInitialized, + _("Device not initialized or passphrase request cancelled " + "or unsupported curve")); + layoutHome(); + return 0; + } + if (!address_n || address_n_count == 0) { + return &node; + } + if (hdnode_private_ckd_cached(&node, address_n, address_n_count, + fingerprint) == 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive private key")); + layoutHome(); + return 0; + } + return &node; } -static bool fsm_layoutAddress(const char *address, const char *desc, bool ignorecase, size_t prefixlen, const uint32_t *address_n, size_t address_n_count, bool address_is_account) -{ - bool qrcode = false; - for (;;) { - const char* display_addr = address; - if (prefixlen && !qrcode) { - display_addr += prefixlen; - } - layoutAddress(display_addr, desc, qrcode, ignorecase, address_n, address_n_count, address_is_account); - if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) { - return true; - } - if (protectAbortedByCancel || protectAbortedByInitialize) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return false; - } - qrcode = !qrcode; - } +static bool fsm_layoutAddress(const char *address, const char *desc, + bool ignorecase, size_t prefixlen, + const uint32_t *address_n, size_t address_n_count, + bool address_is_account) { + bool qrcode = false; + for (;;) { + const char *display_addr = address; + if (prefixlen && !qrcode) { + display_addr += prefixlen; + } + layoutAddress(display_addr, desc, qrcode, ignorecase, address_n, + address_n_count, address_is_account); + if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) { + return true; + } + if (protectAbortedByCancel || protectAbortedByInitialize) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return false; + } + qrcode = !qrcode; + } } -#include "fsm_msg_common.h" #include "fsm_msg_coin.h" -#include "fsm_msg_ethereum.h" +#include "fsm_msg_common.h" #include "fsm_msg_crypto.h" +#include "fsm_msg_debug.h" +#include "fsm_msg_ethereum.h" +#include "fsm_msg_lisk.h" #include "fsm_msg_nem.h" #include "fsm_msg_stellar.h" -#include "fsm_msg_lisk.h" -#include "fsm_msg_debug.h" diff --git a/firmware/fsm.h b/firmware/fsm.h index 4a8e53b38..d4cde689b 100644 --- a/firmware/fsm.h +++ b/firmware/fsm.h @@ -24,19 +24,21 @@ #include "messages-crypto.pb.h" #include "messages-debug.pb.h" #include "messages-ethereum.pb.h" +#include "messages-lisk.pb.h" #include "messages-management.pb.h" #include "messages-nem.pb.h" #include "messages-stellar.pb.h" -#include "messages-lisk.pb.h" // message functions void fsm_sendSuccess(const char *text); #if DEBUG_LINK -void fsm_sendFailureDebug(FailureType code, const char *text, const char *source); +void fsm_sendFailureDebug(FailureType code, const char *text, + const char *source); -#define fsm_sendFailure(code, text) fsm_sendFailureDebug((code), (text), __FILE__ ":" VERSTR(__LINE__) ":") +#define fsm_sendFailure(code, text) \ + fsm_sendFailureDebug((code), (text), __FILE__ ":" VERSTR(__LINE__) ":") #else void fsm_sendFailure(FailureType code, const char *text); #endif @@ -67,7 +69,8 @@ void fsm_msgSetU2FCounter(const SetU2FCounter *msg); // coin void fsm_msgGetPublicKey(const GetPublicKey *msg); void fsm_msgSignTx(const SignTx *msg); -void fsm_msgTxAck(TxAck *msg); // not const because we mutate input/output scripts +void fsm_msgTxAck( + TxAck *msg); // not const because we mutate input/output scripts void fsm_msgGetAddress(const GetAddress *msg); void fsm_msgSignMessage(const SignMessage *msg); void fsm_msgVerifyMessage(const VerifyMessage *msg); @@ -92,7 +95,9 @@ void fsm_msgDebugLinkFlashErase(const DebugLinkFlashErase *msg); // ethereum void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg); void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg); -void fsm_msgEthereumSignTx(EthereumSignTx *msg); // not const because we mutate transaction during validation +void fsm_msgEthereumSignTx( + EthereumSignTx + *msg); // not const because we mutate transaction during validation void fsm_msgEthereumTxAck(const EthereumTxAck *msg); void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg); void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg); @@ -102,12 +107,16 @@ void fsm_msgLiskGetAddress(const LiskGetAddress *msg); void fsm_msgLiskGetPublicKey(const LiskGetPublicKey *msg); void fsm_msgLiskSignMessage(const LiskSignMessage *msg); void fsm_msgLiskVerifyMessage(const LiskVerifyMessage *msg); -void fsm_msgLiskSignTx(LiskSignTx *msg); // not const because we mutate transaction during validation +void fsm_msgLiskSignTx(LiskSignTx *msg); // not const because we mutate + // transaction during validation // nem -void fsm_msgNEMGetAddress(NEMGetAddress *msg); // not const because we mutate msg->network -void fsm_msgNEMSignTx(NEMSignTx *msg); // not const because we mutate msg->network -void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg); // not const because we mutate msg->payload +void fsm_msgNEMGetAddress( + NEMGetAddress *msg); // not const because we mutate msg->network +void fsm_msgNEMSignTx( + NEMSignTx *msg); // not const because we mutate msg->network +void fsm_msgNEMDecryptMessage( + NEMDecryptMessage *msg); // not const because we mutate msg->payload // stellar void fsm_msgStellarGetAddress(const StellarGetAddress *msg); diff --git a/firmware/fsm_msg_coin.h b/firmware/fsm_msg_coin.h index 7e07cadcc..30b5e1747 100644 --- a/firmware/fsm_msg_coin.h +++ b/firmware/fsm_msg_coin.h @@ -17,296 +17,313 @@ * along with this library. If not, see . */ -void fsm_msgGetPublicKey(const GetPublicKey *msg) -{ - RESP_INIT(PublicKey); - - CHECK_INITIALIZED - - CHECK_PIN - - InputScriptType script_type = msg->has_script_type ? msg->script_type : InputScriptType_SPENDADDRESS; - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - - const char *curve = coin->curve_name; - if (msg->has_ecdsa_curve_name) { - curve = msg->ecdsa_curve_name; - } - uint32_t fingerprint; - HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, &fingerprint); - if (!node) return; - hdnode_fill_public_key(node); - - if (msg->has_show_display && msg->show_display) { - layoutPublicKey(node->public_key); - if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - resp->has_node = true; - resp->node.depth = node->depth; - resp->node.fingerprint = fingerprint; - resp->node.child_num = node->child_num; - resp->node.chain_code.size = 32; - memcpy(resp->node.chain_code.bytes, node->chain_code, 32); - resp->node.has_private_key = false; - resp->node.has_public_key = true; - resp->node.public_key.size = 33; - memcpy(resp->node.public_key.bytes, node->public_key, 33); - if (node->public_key[0] == 1) { - /* ed25519 public key */ - resp->node.public_key.bytes[0] = 0; - } - - resp->has_xpub = true; - if (coin->xpub_magic && (script_type == InputScriptType_SPENDADDRESS || script_type == InputScriptType_SPENDMULTISIG)) { - hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, sizeof(resp->xpub)); - } else - if (coin->has_segwit && coin->xpub_magic_segwit_p2sh && script_type == InputScriptType_SPENDP2SHWITNESS) { - hdnode_serialize_public(node, fingerprint, coin->xpub_magic_segwit_p2sh, resp->xpub, sizeof(resp->xpub)); - } else - if (coin->has_segwit && coin->xpub_magic_segwit_native && script_type == InputScriptType_SPENDWITNESS) { - hdnode_serialize_public(node, fingerprint, coin->xpub_magic_segwit_native, resp->xpub, sizeof(resp->xpub)); - } else { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid combination of coin and script_type")); - layoutHome(); - return; - } - - msg_write(MessageType_MessageType_PublicKey, resp); - layoutHome(); +void fsm_msgGetPublicKey(const GetPublicKey *msg) { + RESP_INIT(PublicKey); + + CHECK_INITIALIZED + + CHECK_PIN + + InputScriptType script_type = + msg->has_script_type ? msg->script_type : InputScriptType_SPENDADDRESS; + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + + const char *curve = coin->curve_name; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + uint32_t fingerprint; + HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n, + msg->address_n_count, &fingerprint); + if (!node) return; + hdnode_fill_public_key(node); + + if (msg->has_show_display && msg->show_display) { + layoutPublicKey(node->public_key); + if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + resp->has_node = true; + resp->node.depth = node->depth; + resp->node.fingerprint = fingerprint; + resp->node.child_num = node->child_num; + resp->node.chain_code.size = 32; + memcpy(resp->node.chain_code.bytes, node->chain_code, 32); + resp->node.has_private_key = false; + resp->node.has_public_key = true; + resp->node.public_key.size = 33; + memcpy(resp->node.public_key.bytes, node->public_key, 33); + if (node->public_key[0] == 1) { + /* ed25519 public key */ + resp->node.public_key.bytes[0] = 0; + } + + resp->has_xpub = true; + if (coin->xpub_magic && (script_type == InputScriptType_SPENDADDRESS || + script_type == InputScriptType_SPENDMULTISIG)) { + hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, + sizeof(resp->xpub)); + } else if (coin->has_segwit && coin->xpub_magic_segwit_p2sh && + script_type == InputScriptType_SPENDP2SHWITNESS) { + hdnode_serialize_public(node, fingerprint, coin->xpub_magic_segwit_p2sh, + resp->xpub, sizeof(resp->xpub)); + } else if (coin->has_segwit && coin->xpub_magic_segwit_native && + script_type == InputScriptType_SPENDWITNESS) { + hdnode_serialize_public(node, fingerprint, coin->xpub_magic_segwit_native, + resp->xpub, sizeof(resp->xpub)); + } else { + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid combination of coin and script_type")); + layoutHome(); + return; + } + + msg_write(MessageType_MessageType_PublicKey, resp); + layoutHome(); } -void fsm_msgSignTx(const SignTx *msg) -{ - CHECK_INITIALIZED +void fsm_msgSignTx(const SignTx *msg) { + CHECK_INITIALIZED - CHECK_PARAM(msg->inputs_count > 0, _("Transaction must have at least one input")); - CHECK_PARAM(msg->outputs_count > 0, _("Transaction must have at least one output")); - CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count, _("Value overflow")); + CHECK_PARAM(msg->inputs_count > 0, + _("Transaction must have at least one input")); + CHECK_PARAM(msg->outputs_count > 0, + _("Transaction must have at least one output")); + CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count, + _("Value overflow")); - CHECK_PIN + CHECK_PIN - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL); - if (!node) return; + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL); + if (!node) return; - signing_init(msg, coin, node); + signing_init(msg, coin, node); } -void fsm_msgTxAck(TxAck *msg) -{ - CHECK_PARAM(msg->has_tx, _("No transaction provided")); +void fsm_msgTxAck(TxAck *msg) { + CHECK_PARAM(msg->has_tx, _("No transaction provided")); - signing_txack(&(msg->tx)); + signing_txack(&(msg->tx)); } -static bool path_mismatched(const CoinInfo *coin, const GetAddress *msg) -{ - bool mismatch = false; - - // m : no path - if (msg->address_n_count == 0) { - return false; - } - - // m/44' : BIP44 Legacy - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 44)) { - mismatch |= (msg->script_type != InputScriptType_SPENDADDRESS); - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/45' - BIP45 Copay Abandoned Multisig P2SH - // m / purpose' / cosigner_index / change / address_index - if (msg->address_n[0] == (0x80000000 + 45)) { - mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); - mismatch |= (msg->address_n_count != 4); - mismatch |= (msg->address_n[1] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[2] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/48' - BIP48 Copay Multisig P2SH - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 48)) { - mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/49' : BIP49 SegWit - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 49)) { - mismatch |= (msg->script_type != InputScriptType_SPENDP2SHWITNESS); - mismatch |= !coin->has_segwit; - mismatch |= !coin->has_address_type_p2sh; - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - // m/84' : BIP84 Native SegWit - // m / purpose' / coin_type' / account' / change / address_index - if (msg->address_n[0] == (0x80000000 + 84)) { - mismatch |= (msg->script_type != InputScriptType_SPENDWITNESS); - mismatch |= !coin->has_segwit; - mismatch |= !coin->bech32_prefix; - mismatch |= (msg->address_n_count != 5); - mismatch |= (msg->address_n[1] != coin->coin_type); - mismatch |= (msg->address_n[2] & 0x80000000) == 0; - mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; - mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; - return mismatch; - } - - return false; +static bool path_mismatched(const CoinInfo *coin, const GetAddress *msg) { + bool mismatch = false; + + // m : no path + if (msg->address_n_count == 0) { + return false; + } + + // m/44' : BIP44 Legacy + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 44)) { + mismatch |= (msg->script_type != InputScriptType_SPENDADDRESS); + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/45' - BIP45 Copay Abandoned Multisig P2SH + // m / purpose' / cosigner_index / change / address_index + if (msg->address_n[0] == (0x80000000 + 45)) { + mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); + mismatch |= (msg->address_n_count != 4); + mismatch |= (msg->address_n[1] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[2] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/48' - BIP48 Copay Multisig P2SH + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 48)) { + mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG); + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/49' : BIP49 SegWit + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 49)) { + mismatch |= (msg->script_type != InputScriptType_SPENDP2SHWITNESS); + mismatch |= !coin->has_segwit; + mismatch |= !coin->has_address_type_p2sh; + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + // m/84' : BIP84 Native SegWit + // m / purpose' / coin_type' / account' / change / address_index + if (msg->address_n[0] == (0x80000000 + 84)) { + mismatch |= (msg->script_type != InputScriptType_SPENDWITNESS); + mismatch |= !coin->has_segwit; + mismatch |= !coin->bech32_prefix; + mismatch |= (msg->address_n_count != 5); + mismatch |= (msg->address_n[1] != coin->coin_type); + mismatch |= (msg->address_n[2] & 0x80000000) == 0; + mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000; + mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000; + return mismatch; + } + + return false; } -void fsm_msgGetAddress(const GetAddress *msg) -{ - RESP_INIT(Address); - - CHECK_INITIALIZED - - CHECK_PIN - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - hdnode_fill_public_key(node); - - char address[MAX_ADDR_SIZE]; - if (msg->has_multisig) { // use progress bar only for multisig - layoutProgress(_("Computing address"), 0); - } - if (!compute_address(coin, msg->script_type, node, msg->has_multisig, &msg->multisig, address)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Can't encode address")); - layoutHome(); - return; - } - - if (msg->has_show_display && msg->show_display) { - char desc[20]; - if (msg->has_multisig) { - strlcpy(desc, "Multisig __ of __:", sizeof(desc)); - const uint32_t m = msg->multisig.m; - const uint32_t n = msg->multisig.pubkeys_count; - desc[9] = (m < 10) ? ' ': ('0' + (m / 10)); - desc[10] = '0' + (m % 10); - desc[15] = (n < 10) ? ' ': ('0' + (n / 10)); - desc[16] = '0' + (n % 10); - } else { - strlcpy(desc, _("Address:"), sizeof(desc)); - } - - bool mismatch = path_mismatched(coin, msg); - - if (mismatch) { - layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL, _("Wrong address path"), _("for selected coin."), NULL, _("Continue at your"), _("own risk!"), NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - bool is_cashaddr = coin->cashaddr_prefix != NULL; - bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS; - if (!fsm_layoutAddress(address, desc, is_cashaddr || is_bech32, is_cashaddr ? strlen(coin->cashaddr_prefix) + 1 : 0, msg->address_n, msg->address_n_count, false)) { - return; - } - } - - strlcpy(resp->address, address, sizeof(resp->address)); - msg_write(MessageType_MessageType_Address, resp); - layoutHome(); +void fsm_msgGetAddress(const GetAddress *msg) { + RESP_INIT(Address); + + CHECK_INITIALIZED + + CHECK_PIN + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + hdnode_fill_public_key(node); + + char address[MAX_ADDR_SIZE]; + if (msg->has_multisig) { // use progress bar only for multisig + layoutProgress(_("Computing address"), 0); + } + if (!compute_address(coin, msg->script_type, node, msg->has_multisig, + &msg->multisig, address)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Can't encode address")); + layoutHome(); + return; + } + + if (msg->has_show_display && msg->show_display) { + char desc[20]; + if (msg->has_multisig) { + strlcpy(desc, "Multisig __ of __:", sizeof(desc)); + const uint32_t m = msg->multisig.m; + const uint32_t n = msg->multisig.pubkeys_count; + desc[9] = (m < 10) ? ' ' : ('0' + (m / 10)); + desc[10] = '0' + (m % 10); + desc[15] = (n < 10) ? ' ' : ('0' + (n / 10)); + desc[16] = '0' + (n % 10); + } else { + strlcpy(desc, _("Address:"), sizeof(desc)); + } + + bool mismatch = path_mismatched(coin, msg); + + if (mismatch) { + layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL, + _("Wrong address path"), _("for selected coin."), NULL, + _("Continue at your"), _("own risk!"), NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + bool is_cashaddr = coin->cashaddr_prefix != NULL; + bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS; + if (!fsm_layoutAddress(address, desc, is_cashaddr || is_bech32, + is_cashaddr ? strlen(coin->cashaddr_prefix) + 1 : 0, + msg->address_n, msg->address_n_count, false)) { + return; + } + } + + strlcpy(resp->address, address, sizeof(resp->address)); + msg_write(MessageType_MessageType_Address, resp); + layoutHome(); } -void fsm_msgSignMessage(const SignMessage *msg) -{ - // CHECK_PARAM(is_ascii_only(msg->message.bytes, msg->message.size), _("Cannot sign non-ASCII strings")); - - RESP_INIT(MessageSignature); - - CHECK_INITIALIZED - - layoutSignMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - layoutProgressSwipe(_("Signing"), 0); - if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { - resp->has_address = true; - hdnode_fill_public_key(node); - if (!compute_address(coin, msg->script_type, node, false, NULL, resp->address)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing address")); - layoutHome(); - return; - } - resp->has_signature = true; - resp->signature.size = 65; - msg_write(MessageType_MessageType_MessageSignature, resp); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error signing message")); - } - layoutHome(); +void fsm_msgSignMessage(const SignMessage *msg) { + // CHECK_PARAM(is_ascii_only(msg->message.bytes, msg->message.size), _("Cannot + // sign non-ASCII strings")); + + RESP_INIT(MessageSignature); + + CHECK_INITIALIZED + + layoutSignMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + layoutProgressSwipe(_("Signing"), 0); + if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes, + msg->message.size, resp->signature.bytes) == 0) { + resp->has_address = true; + hdnode_fill_public_key(node); + if (!compute_address(coin, msg->script_type, node, false, NULL, + resp->address)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Error computing address")); + layoutHome(); + return; + } + resp->has_signature = true; + resp->signature.size = 65; + msg_write(MessageType_MessageType_MessageSignature, resp); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Error signing message")); + } + layoutHome(); } -void fsm_msgVerifyMessage(const VerifyMessage *msg) -{ - CHECK_PARAM(msg->has_address, _("No address provided")); - CHECK_PARAM(msg->has_message, _("No message provided")); - - const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); - if (!coin) return; - layoutProgressSwipe(_("Verifying"), 0); - if (msg->signature.size == 65 && cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, msg->address, msg->signature.bytes) == 0) { - layoutVerifyAddress(coin, msg->address); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - layoutVerifyMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - fsm_sendSuccess(_("Message verified")); - } else { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); - } - layoutHome(); +void fsm_msgVerifyMessage(const VerifyMessage *msg) { + CHECK_PARAM(msg->has_address, _("No address provided")); + CHECK_PARAM(msg->has_message, _("No message provided")); + + const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name); + if (!coin) return; + layoutProgressSwipe(_("Verifying"), 0); + if (msg->signature.size == 65 && + cryptoMessageVerify(coin, msg->message.bytes, msg->message.size, + msg->address, msg->signature.bytes) == 0) { + layoutVerifyAddress(coin, msg->address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + layoutVerifyMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + fsm_sendSuccess(_("Message verified")); + } else { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); + } + layoutHome(); } diff --git a/firmware/fsm_msg_common.h b/firmware/fsm_msg_common.h index 96da11d83..265b629d4 100644 --- a/firmware/fsm_msg_common.h +++ b/firmware/fsm_msg_common.h @@ -17,364 +17,396 @@ * along with this library. If not, see . */ -void fsm_msgInitialize(const Initialize *msg) -{ - recovery_abort(); - signing_abort(); - if (msg && msg->has_state && msg->state.size == 64) { - uint8_t i_state[64]; - if (!session_getState(msg->state.bytes, i_state, NULL)) { - session_clear(false); // do not clear PIN - } else { - if (0 != memcmp(msg->state.bytes, i_state, 64)) { - session_clear(false); // do not clear PIN - } - } - } else { - session_clear(false); // do not clear PIN - } - layoutHome(); - fsm_msgGetFeatures(0); +void fsm_msgInitialize(const Initialize *msg) { + recovery_abort(); + signing_abort(); + if (msg && msg->has_state && msg->state.size == 64) { + uint8_t i_state[64]; + if (!session_getState(msg->state.bytes, i_state, NULL)) { + session_clear(false); // do not clear PIN + } else { + if (0 != memcmp(msg->state.bytes, i_state, 64)) { + session_clear(false); // do not clear PIN + } + } + } else { + session_clear(false); // do not clear PIN + } + layoutHome(); + fsm_msgGetFeatures(0); } -void fsm_msgGetFeatures(const GetFeatures *msg) -{ - (void)msg; - RESP_INIT(Features); - resp->has_vendor = true; strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor)); - resp->has_major_version = true; resp->major_version = VERSION_MAJOR; - resp->has_minor_version = true; resp->minor_version = VERSION_MINOR; - resp->has_patch_version = true; resp->patch_version = VERSION_PATCH; - resp->has_device_id = true; strlcpy(resp->device_id, config_uuid_str, sizeof(resp->device_id)); - resp->has_pin_protection = true; resp->pin_protection = config_hasPin(); - resp->has_passphrase_protection = true; config_getPassphraseProtection(&(resp->passphrase_protection)); +void fsm_msgGetFeatures(const GetFeatures *msg) { + (void)msg; + RESP_INIT(Features); + resp->has_vendor = true; + strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor)); + resp->has_major_version = true; + resp->major_version = VERSION_MAJOR; + resp->has_minor_version = true; + resp->minor_version = VERSION_MINOR; + resp->has_patch_version = true; + resp->patch_version = VERSION_PATCH; + resp->has_device_id = true; + strlcpy(resp->device_id, config_uuid_str, sizeof(resp->device_id)); + resp->has_pin_protection = true; + resp->pin_protection = config_hasPin(); + resp->has_passphrase_protection = true; + config_getPassphraseProtection(&(resp->passphrase_protection)); #ifdef SCM_REVISION - int len = sizeof(SCM_REVISION) - 1; - resp->has_revision = true; memcpy(resp->revision.bytes, SCM_REVISION, len); resp->revision.size = len; + int len = sizeof(SCM_REVISION) - 1; + resp->has_revision = true; + memcpy(resp->revision.bytes, SCM_REVISION, len); + resp->revision.size = len; #endif - resp->has_bootloader_hash = true; resp->bootloader_hash.size = memory_bootloader_hash(resp->bootloader_hash.bytes); - - resp->has_language = config_getLanguage(resp->language, sizeof(resp->language)); - resp->has_label = config_getLabel(resp->label, sizeof(resp->label)); - resp->has_initialized = true; resp->initialized = config_isInitialized(); - resp->has_imported = config_getImported(&(resp->imported)); - resp->has_pin_cached = true; resp->pin_cached = session_isUnlocked() && config_hasPin(); - resp->has_passphrase_cached = true; resp->passphrase_cached = session_isPassphraseCached(); - resp->has_needs_backup = true; config_getNeedsBackup(&(resp->needs_backup)); - resp->has_unfinished_backup = true; config_getUnfinishedBackup(&(resp->unfinished_backup)); - resp->has_no_backup = true; config_getNoBackup(&(resp->no_backup)); - resp->has_flags = config_getFlags(&(resp->flags)); - resp->has_model = true; strlcpy(resp->model, "1", sizeof(resp->model)); - - msg_write(MessageType_MessageType_Features, resp); + resp->has_bootloader_hash = true; + resp->bootloader_hash.size = + memory_bootloader_hash(resp->bootloader_hash.bytes); + + resp->has_language = + config_getLanguage(resp->language, sizeof(resp->language)); + resp->has_label = config_getLabel(resp->label, sizeof(resp->label)); + resp->has_initialized = true; + resp->initialized = config_isInitialized(); + resp->has_imported = config_getImported(&(resp->imported)); + resp->has_pin_cached = true; + resp->pin_cached = session_isUnlocked() && config_hasPin(); + resp->has_passphrase_cached = true; + resp->passphrase_cached = session_isPassphraseCached(); + resp->has_needs_backup = true; + config_getNeedsBackup(&(resp->needs_backup)); + resp->has_unfinished_backup = true; + config_getUnfinishedBackup(&(resp->unfinished_backup)); + resp->has_no_backup = true; + config_getNoBackup(&(resp->no_backup)); + resp->has_flags = config_getFlags(&(resp->flags)); + resp->has_model = true; + strlcpy(resp->model, "1", sizeof(resp->model)); + + msg_write(MessageType_MessageType_Features, resp); } -void fsm_msgPing(const Ping *msg) -{ - RESP_INIT(Success); - - if (msg->has_button_protection && msg->button_protection) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("answer to ping?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (msg->has_pin_protection && msg->pin_protection) { - CHECK_PIN - } - - if (msg->has_passphrase_protection && msg->passphrase_protection) { - if (!protectPassphrase()) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - return; - } - } - - if (msg->has_message) { - resp->has_message = true; - memcpy(&(resp->message), &(msg->message), sizeof(resp->message)); - } - msg_write(MessageType_MessageType_Success, resp); - layoutHome(); +void fsm_msgPing(const Ping *msg) { + RESP_INIT(Success); + + if (msg->has_button_protection && msg->button_protection) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("answer to ping?"), NULL, + NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (msg->has_pin_protection && msg->pin_protection) { + CHECK_PIN + } + + if (msg->has_passphrase_protection && msg->passphrase_protection) { + if (!protectPassphrase()) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + return; + } + } + + if (msg->has_message) { + resp->has_message = true; + memcpy(&(resp->message), &(msg->message), sizeof(resp->message)); + } + msg_write(MessageType_MessageType_Success, resp); + layoutHome(); } -void fsm_msgChangePin(const ChangePin *msg) -{ - bool removal = msg->has_remove && msg->remove; - if (removal) { - if (config_hasPin()) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("remove current PIN?"), NULL, NULL, NULL, NULL); - } else { - fsm_sendSuccess(_("PIN removed")); - return; - } - } else { - if (config_hasPin()) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change current PIN?"), NULL, NULL, NULL, NULL); - } else { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("set new PIN?"), NULL, NULL, NULL, NULL); - } - } - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - if (protectChangePin(removal)) { - if (removal) { - fsm_sendSuccess(_("PIN removed")); - } else { - fsm_sendSuccess(_("PIN changed")); - } - } - - layoutHome(); +void fsm_msgChangePin(const ChangePin *msg) { + bool removal = msg->has_remove && msg->remove; + if (removal) { + if (config_hasPin()) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("remove current PIN?"), + NULL, NULL, NULL, NULL); + } else { + fsm_sendSuccess(_("PIN removed")); + return; + } + } else { + if (config_hasPin()) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("change current PIN?"), + NULL, NULL, NULL, NULL); + } else { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("set new PIN?"), NULL, + NULL, NULL, NULL); + } + } + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (protectChangePin(removal)) { + if (removal) { + fsm_sendSuccess(_("PIN removed")); + } else { + fsm_sendSuccess(_("PIN changed")); + } + } + + layoutHome(); } -void fsm_msgWipeDevice(const WipeDevice *msg) -{ - (void)msg; - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("wipe the device?"), NULL, _("All data will be lost."), NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_WipeDevice, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - config_wipe(); - // the following does not work on Mac anyway :-/ Linux/Windows are fine, so it is not needed - // usbReconnect(); // force re-enumeration because of the serial number change - fsm_sendSuccess(_("Device wiped")); - layoutHome(); +void fsm_msgWipeDevice(const WipeDevice *msg) { + (void)msg; + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("wipe the device?"), NULL, + _("All data will be lost."), NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_WipeDevice, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + config_wipe(); + // the following does not work on Mac anyway :-/ Linux/Windows are fine, so it + // is not needed usbReconnect(); // force re-enumeration because of the serial + // number change + fsm_sendSuccess(_("Device wiped")); + layoutHome(); } -void fsm_msgGetEntropy(const GetEntropy *msg) -{ +void fsm_msgGetEntropy(const GetEntropy *msg) { #if !DEBUG_RNG - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("send entropy?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("send entropy?"), NULL, NULL, + NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } #endif - RESP_INIT(Entropy); - uint32_t len = msg->size; - if (len > 1024) { - len = 1024; - } - resp->entropy.size = len; - random_buffer(resp->entropy.bytes, len); - msg_write(MessageType_MessageType_Entropy, resp); - layoutHome(); + RESP_INIT(Entropy); + uint32_t len = msg->size; + if (len > 1024) { + len = 1024; + } + resp->entropy.size = len; + random_buffer(resp->entropy.bytes, len); + msg_write(MessageType_MessageType_Entropy, resp); + layoutHome(); } -void fsm_msgLoadDevice(const LoadDevice *msg) -{ - CHECK_PIN - - CHECK_NOT_INITIALIZED - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), NULL, _("Loading private seed"), _("is not recommended."), _("Continue only if you"), _("know what you are"), _("doing!"), NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - if (msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum) ) { - if (!mnemonic_check(msg->mnemonic)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Mnemonic with wrong checksum provided")); - layoutHome(); - return; - } - } - - config_loadDevice(msg); - fsm_sendSuccess(_("Device loaded")); - layoutHome(); +void fsm_msgLoadDevice(const LoadDevice *msg) { + CHECK_PIN + + CHECK_NOT_INITIALIZED + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), NULL, + _("Loading private seed"), _("is not recommended."), + _("Continue only if you"), _("know what you are"), + _("doing!"), NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + if (msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum)) { + if (!mnemonic_check(msg->mnemonic)) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Mnemonic with wrong checksum provided")); + layoutHome(); + return; + } + } + + config_loadDevice(msg); + fsm_sendSuccess(_("Device loaded")); + layoutHome(); } -void fsm_msgResetDevice(const ResetDevice *msg) -{ - CHECK_PIN - - CHECK_NOT_INITIALIZED - - CHECK_PARAM(!msg->has_strength || msg->strength == 128 || msg->strength == 192 || msg->strength == 256, _("Invalid seed strength")); - - reset_init( - msg->has_display_random && msg->display_random, - msg->has_strength ? msg->strength : 128, - msg->has_passphrase_protection && msg->passphrase_protection, - msg->has_pin_protection && msg->pin_protection, - msg->has_language ? msg->language : 0, - msg->has_label ? msg->label : 0, - msg->has_u2f_counter ? msg->u2f_counter : 0, - msg->has_skip_backup ? msg->skip_backup : false, - msg->has_no_backup ? msg->no_backup : false - ); +void fsm_msgResetDevice(const ResetDevice *msg) { + CHECK_PIN + + CHECK_NOT_INITIALIZED + + CHECK_PARAM(!msg->has_strength || msg->strength == 128 || + msg->strength == 192 || msg->strength == 256, + _("Invalid seed strength")); + + reset_init(msg->has_display_random && msg->display_random, + msg->has_strength ? msg->strength : 128, + msg->has_passphrase_protection && msg->passphrase_protection, + msg->has_pin_protection && msg->pin_protection, + msg->has_language ? msg->language : 0, + msg->has_label ? msg->label : 0, + msg->has_u2f_counter ? msg->u2f_counter : 0, + msg->has_skip_backup ? msg->skip_backup : false, + msg->has_no_backup ? msg->no_backup : false); } -void fsm_msgEntropyAck(const EntropyAck *msg) -{ - if (msg->has_entropy) { - reset_entropy(msg->entropy.bytes, msg->entropy.size); - } else { - reset_entropy(0, 0); - } +void fsm_msgEntropyAck(const EntropyAck *msg) { + if (msg->has_entropy) { + reset_entropy(msg->entropy.bytes, msg->entropy.size); + } else { + reset_entropy(0, 0); + } } -void fsm_msgBackupDevice(const BackupDevice *msg) -{ - CHECK_INITIALIZED +void fsm_msgBackupDevice(const BackupDevice *msg) { + CHECK_INITIALIZED - CHECK_PIN_UNCACHED + CHECK_PIN_UNCACHED - (void)msg; - char mnemonic[MAX_MNEMONIC_LEN + 1]; - if (config_getMnemonic(mnemonic, sizeof(mnemonic))) { - reset_backup(true, mnemonic); - } - memzero(mnemonic, sizeof(mnemonic)); + (void) + msg; + char mnemonic[MAX_MNEMONIC_LEN + 1]; + if (config_getMnemonic(mnemonic, sizeof(mnemonic))) { + reset_backup(true, mnemonic); + } + memzero(mnemonic, sizeof(mnemonic)); } -void fsm_msgCancel(const Cancel *msg) -{ - (void)msg; - recovery_abort(); - signing_abort(); - ethereum_signing_abort(); - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); +void fsm_msgCancel(const Cancel *msg) { + (void)msg; + recovery_abort(); + signing_abort(); + ethereum_signing_abort(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); } -void fsm_msgClearSession(const ClearSession *msg) -{ - (void)msg; - session_clear(true); // clear PIN as well - layoutScreensaver(); - fsm_sendSuccess(_("Session cleared")); +void fsm_msgClearSession(const ClearSession *msg) { + (void)msg; + session_clear(true); // clear PIN as well + layoutScreensaver(); + fsm_sendSuccess(_("Session cleared")); } -void fsm_msgApplySettings(const ApplySettings *msg) -{ - CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase || msg->has_homescreen || msg->has_auto_lock_delay_ms, - _("No setting provided")); - - CHECK_PIN - - if (msg->has_label) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change name to"), msg->label, "?", NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - if (msg->has_language) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change language to"), msg->language, "?", NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - if (msg->has_use_passphrase) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), msg->use_passphrase ? _("enable passphrase") : _("disable passphrase"), _("protection?"), NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - if (msg->has_homescreen) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change the home"), _("screen?"), NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (msg->has_auto_lock_delay_ms) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("change auto-lock"), _("delay?"), NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (msg->has_label) { - config_setLabel(msg->label); - } - if (msg->has_language) { - config_setLanguage(msg->language); - } - if (msg->has_use_passphrase) { - config_setPassphraseProtection(msg->use_passphrase); - } - if (msg->has_homescreen) { - config_setHomescreen(msg->homescreen.bytes, msg->homescreen.size); - } - if (msg->has_auto_lock_delay_ms) { - config_setAutoLockDelayMs(msg->auto_lock_delay_ms); - } - fsm_sendSuccess(_("Settings applied")); - layoutHome(); +void fsm_msgApplySettings(const ApplySettings *msg) { + CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase || + msg->has_homescreen || msg->has_auto_lock_delay_ms, + _("No setting provided")); + + CHECK_PIN + + if (msg->has_label) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("change name to"), + msg->label, "?", NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + if (msg->has_language) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("change language to"), + msg->language, "?", NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + if (msg->has_use_passphrase) { + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), + msg->use_passphrase ? _("enable passphrase") : _("disable passphrase"), + _("protection?"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + if (msg->has_homescreen) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("change the home"), + _("screen?"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (msg->has_auto_lock_delay_ms) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("change auto-lock"), + _("delay?"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (msg->has_label) { + config_setLabel(msg->label); + } + if (msg->has_language) { + config_setLanguage(msg->language); + } + if (msg->has_use_passphrase) { + config_setPassphraseProtection(msg->use_passphrase); + } + if (msg->has_homescreen) { + config_setHomescreen(msg->homescreen.bytes, msg->homescreen.size); + } + if (msg->has_auto_lock_delay_ms) { + config_setAutoLockDelayMs(msg->auto_lock_delay_ms); + } + fsm_sendSuccess(_("Settings applied")); + layoutHome(); } -void fsm_msgApplyFlags(const ApplyFlags *msg) -{ - CHECK_PIN - - if (msg->has_flags) { - config_applyFlags(msg->flags); - } - fsm_sendSuccess(_("Flags applied")); -} +void fsm_msgApplyFlags(const ApplyFlags *msg) { + CHECK_PIN -void fsm_msgRecoveryDevice(const RecoveryDevice *msg) -{ - CHECK_PIN_UNCACHED - - const bool dry_run = msg->has_dry_run ? msg->dry_run : false; - if (!dry_run) { - CHECK_NOT_INITIALIZED - } - - CHECK_PARAM(!msg->has_word_count || msg->word_count == 12 || msg->word_count == 18 || msg->word_count == 24, _("Invalid word count")); - - recovery_init( - msg->has_word_count ? msg->word_count : 12, - msg->has_passphrase_protection && msg->passphrase_protection, - msg->has_pin_protection && msg->pin_protection, - msg->has_language ? msg->language : 0, - msg->has_label ? msg->label : 0, - msg->has_enforce_wordlist && msg->enforce_wordlist, - msg->has_type ? msg->type : 0, - msg->has_u2f_counter ? msg->u2f_counter : 0, - dry_run - ); + if (msg->has_flags) { + config_applyFlags(msg->flags); + } + fsm_sendSuccess(_("Flags applied")); } -void fsm_msgWordAck(const WordAck *msg) -{ - recovery_word(msg->word); +void fsm_msgRecoveryDevice(const RecoveryDevice *msg) { + CHECK_PIN_UNCACHED + + const bool dry_run = msg->has_dry_run ? msg->dry_run : false; + if (!dry_run) { + CHECK_NOT_INITIALIZED + } + + CHECK_PARAM(!msg->has_word_count || msg->word_count == 12 || + msg->word_count == 18 || msg->word_count == 24, + _("Invalid word count")); + + recovery_init(msg->has_word_count ? msg->word_count : 12, + msg->has_passphrase_protection && msg->passphrase_protection, + msg->has_pin_protection && msg->pin_protection, + msg->has_language ? msg->language : 0, + msg->has_label ? msg->label : 0, + msg->has_enforce_wordlist && msg->enforce_wordlist, + msg->has_type ? msg->type : 0, + msg->has_u2f_counter ? msg->u2f_counter : 0, dry_run); } -void fsm_msgSetU2FCounter(const SetU2FCounter *msg) -{ - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you want to set"), _("the U2F counter?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - config_setU2FCounter(msg->u2f_counter); - fsm_sendSuccess(_("U2F counter set")); - layoutHome(); +void fsm_msgWordAck(const WordAck *msg) { recovery_word(msg->word); } + +void fsm_msgSetU2FCounter(const SetU2FCounter *msg) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you want to set"), _("the U2F counter?"), NULL, NULL, + NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + config_setU2FCounter(msg->u2f_counter); + fsm_sendSuccess(_("U2F counter set")); + layoutHome(); } diff --git a/firmware/fsm_msg_crypto.h b/firmware/fsm_msg_crypto.h index b6cc5329e..ed0784017 100644 --- a/firmware/fsm_msg_crypto.h +++ b/firmware/fsm_msg_crypto.h @@ -17,254 +17,283 @@ * along with this library. If not, see . */ -void fsm_msgCipherKeyValue(const CipherKeyValue *msg) -{ - CHECK_INITIALIZED - - CHECK_PARAM(msg->has_key, _("No key provided")); - CHECK_PARAM(msg->has_value, _("No value provided")); - CHECK_PARAM(msg->value.size % 16 == 0, _("Value length must be a multiple of 16")); - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - bool encrypt = msg->has_encrypt && msg->encrypt; - bool ask_on_encrypt = msg->has_ask_on_encrypt && msg->ask_on_encrypt; - bool ask_on_decrypt = msg->has_ask_on_decrypt && msg->ask_on_decrypt; - if ((encrypt && ask_on_encrypt) || (!encrypt && ask_on_decrypt)) { - layoutCipherKeyValue(encrypt, msg->key); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - uint8_t data[256 + 4]; - strlcpy((char *)data, msg->key, sizeof(data)); - strlcat((char *)data, ask_on_encrypt ? "E1" : "E0", sizeof(data)); - strlcat((char *)data, ask_on_decrypt ? "D1" : "D0", sizeof(data)); - - hmac_sha512(node->private_key, 32, data, strlen((char *)data), data); - - if (msg->iv.size == 16) { - // override iv if provided - memcpy(data + 32, msg->iv.bytes, 16); - } - - RESP_INIT(CipheredKeyValue); - if (encrypt) { - aes_encrypt_ctx ctx; - aes_encrypt_key256(data, &ctx); - aes_cbc_encrypt(msg->value.bytes, resp->value.bytes, msg->value.size, data + 32, &ctx); - } else { - aes_decrypt_ctx ctx; - aes_decrypt_key256(data, &ctx); - aes_cbc_decrypt(msg->value.bytes, resp->value.bytes, msg->value.size, data + 32, &ctx); - } - resp->has_value = true; - resp->value.size = msg->value.size; - msg_write(MessageType_MessageType_CipheredKeyValue, resp); - layoutHome(); +void fsm_msgCipherKeyValue(const CipherKeyValue *msg) { + CHECK_INITIALIZED + + CHECK_PARAM(msg->has_key, _("No key provided")); + CHECK_PARAM(msg->has_value, _("No value provided")); + CHECK_PARAM(msg->value.size % 16 == 0, + _("Value length must be a multiple of 16")); + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + bool encrypt = msg->has_encrypt && msg->encrypt; + bool ask_on_encrypt = msg->has_ask_on_encrypt && msg->ask_on_encrypt; + bool ask_on_decrypt = msg->has_ask_on_decrypt && msg->ask_on_decrypt; + if ((encrypt && ask_on_encrypt) || (!encrypt && ask_on_decrypt)) { + layoutCipherKeyValue(encrypt, msg->key); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + uint8_t data[256 + 4]; + strlcpy((char *)data, msg->key, sizeof(data)); + strlcat((char *)data, ask_on_encrypt ? "E1" : "E0", sizeof(data)); + strlcat((char *)data, ask_on_decrypt ? "D1" : "D0", sizeof(data)); + + hmac_sha512(node->private_key, 32, data, strlen((char *)data), data); + + if (msg->iv.size == 16) { + // override iv if provided + memcpy(data + 32, msg->iv.bytes, 16); + } + + RESP_INIT(CipheredKeyValue); + if (encrypt) { + aes_encrypt_ctx ctx; + aes_encrypt_key256(data, &ctx); + aes_cbc_encrypt(msg->value.bytes, resp->value.bytes, msg->value.size, + data + 32, &ctx); + } else { + aes_decrypt_ctx ctx; + aes_decrypt_key256(data, &ctx); + aes_cbc_decrypt(msg->value.bytes, resp->value.bytes, msg->value.size, + data + 32, &ctx); + } + resp->has_value = true; + resp->value.size = msg->value.size; + msg_write(MessageType_MessageType_CipheredKeyValue, resp); + layoutHome(); } -void fsm_msgSignIdentity(const SignIdentity *msg) -{ - RESP_INIT(SignedIdentity); - - CHECK_INITIALIZED - - layoutSignIdentity(&(msg->identity), msg->has_challenge_visual ? msg->challenge_visual : 0); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - uint8_t hash[32]; - if (!msg->has_identity || cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); - layoutHome(); - return; - } - - uint32_t address_n[5]; - address_n[0] = 0x80000000 | 13; - address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | ((uint32_t) hash[ 3] << 24); - address_n[2] = 0x80000000 | hash[ 4] | (hash[ 5] << 8) | (hash[ 6] << 16) | ((uint32_t) hash[ 7] << 24); - address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | ((uint32_t) hash[11] << 24); - address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t) hash[15] << 24); - - const char *curve = SECP256K1_NAME; - if (msg->has_ecdsa_curve_name) { - curve = msg->ecdsa_curve_name; - } - HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); - if (!node) return; - - bool sign_ssh = msg->identity.has_proto && (strcmp(msg->identity.proto, "ssh") == 0); - bool sign_gpg = msg->identity.has_proto && (strcmp(msg->identity.proto, "gpg") == 0); - - int result = 0; - layoutProgressSwipe(_("Signing"), 0); - if (sign_ssh) { // SSH does not sign visual challenge - result = sshMessageSign(node, msg->challenge_hidden.bytes, msg->challenge_hidden.size, resp->signature.bytes); - } else if (sign_gpg) { // GPG should sign a message digest - result = gpgMessageSign(node, msg->challenge_hidden.bytes, msg->challenge_hidden.size, resp->signature.bytes); - } else { - uint8_t digest[64]; - sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest); - sha256_Raw((const uint8_t *)msg->challenge_visual, strlen(msg->challenge_visual), digest + 32); - result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS, digest, 64, resp->signature.bytes); - } - - if (result == 0) { - hdnode_fill_public_key(node); - if (strcmp(curve, SECP256K1_NAME) != 0) { - resp->has_address = false; - } else { - resp->has_address = true; - hdnode_get_address(node, 0x00, resp->address, sizeof(resp->address)); // hardcoded Bitcoin address type - } - resp->has_public_key = true; - resp->public_key.size = 33; - memcpy(resp->public_key.bytes, node->public_key, 33); - if (node->public_key[0] == 1) { - /* ed25519 public key */ - resp->public_key.bytes[0] = 0; - } - resp->has_signature = true; - resp->signature.size = 65; - msg_write(MessageType_MessageType_SignedIdentity, resp); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error signing identity")); - } - layoutHome(); +void fsm_msgSignIdentity(const SignIdentity *msg) { + RESP_INIT(SignedIdentity); + + CHECK_INITIALIZED + + layoutSignIdentity(&(msg->identity), + msg->has_challenge_visual ? msg->challenge_visual : 0); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + uint8_t hash[32]; + if (!msg->has_identity || + cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); + layoutHome(); + return; + } + + uint32_t address_n[5]; + address_n[0] = 0x80000000 | 13; + address_n[1] = 0x80000000 | hash[0] | (hash[1] << 8) | (hash[2] << 16) | + ((uint32_t)hash[3] << 24); + address_n[2] = 0x80000000 | hash[4] | (hash[5] << 8) | (hash[6] << 16) | + ((uint32_t)hash[7] << 24); + address_n[3] = 0x80000000 | hash[8] | (hash[9] << 8) | (hash[10] << 16) | + ((uint32_t)hash[11] << 24); + address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | + ((uint32_t)hash[15] << 24); + + const char *curve = SECP256K1_NAME; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); + if (!node) return; + + bool sign_ssh = + msg->identity.has_proto && (strcmp(msg->identity.proto, "ssh") == 0); + bool sign_gpg = + msg->identity.has_proto && (strcmp(msg->identity.proto, "gpg") == 0); + + int result = 0; + layoutProgressSwipe(_("Signing"), 0); + if (sign_ssh) { // SSH does not sign visual challenge + result = sshMessageSign(node, msg->challenge_hidden.bytes, + msg->challenge_hidden.size, resp->signature.bytes); + } else if (sign_gpg) { // GPG should sign a message digest + result = gpgMessageSign(node, msg->challenge_hidden.bytes, + msg->challenge_hidden.size, resp->signature.bytes); + } else { + uint8_t digest[64]; + sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest); + sha256_Raw((const uint8_t *)msg->challenge_visual, + strlen(msg->challenge_visual), digest + 32); + result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS, + digest, 64, resp->signature.bytes); + } + + if (result == 0) { + hdnode_fill_public_key(node); + if (strcmp(curve, SECP256K1_NAME) != 0) { + resp->has_address = false; + } else { + resp->has_address = true; + hdnode_get_address( + node, 0x00, resp->address, + sizeof(resp->address)); // hardcoded Bitcoin address type + } + resp->has_public_key = true; + resp->public_key.size = 33; + memcpy(resp->public_key.bytes, node->public_key, 33); + if (node->public_key[0] == 1) { + /* ed25519 public key */ + resp->public_key.bytes[0] = 0; + } + resp->has_signature = true; + resp->signature.size = 65; + msg_write(MessageType_MessageType_SignedIdentity, resp); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Error signing identity")); + } + layoutHome(); } -void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) -{ - RESP_INIT(ECDHSessionKey); - - CHECK_INITIALIZED - - layoutDecryptIdentity(&msg->identity); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - uint8_t hash[32]; - if (!msg->has_identity || cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); - layoutHome(); - return; - } - - uint32_t address_n[5]; - address_n[0] = 0x80000000 | 17; - address_n[1] = 0x80000000 | hash[ 0] | (hash[ 1] << 8) | (hash[ 2] << 16) | ((uint32_t) hash[ 3] << 24); - address_n[2] = 0x80000000 | hash[ 4] | (hash[ 5] << 8) | (hash[ 6] << 16) | ((uint32_t) hash[ 7] << 24); - address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | ((uint32_t) hash[11] << 24); - address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | ((uint32_t) hash[15] << 24); - - const char *curve = SECP256K1_NAME; - if (msg->has_ecdsa_curve_name) { - curve = msg->ecdsa_curve_name; - } - - const HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); - if (!node) return; - - int result_size = 0; - if (hdnode_get_shared_key(node, msg->peer_public_key.bytes, resp->session_key.bytes, &result_size) == 0) { - resp->has_session_key = true; - resp->session_key.size = result_size; - msg_write(MessageType_MessageType_ECDHSessionKey, resp); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error getting ECDH session key")); - } - layoutHome(); +void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) { + RESP_INIT(ECDHSessionKey); + + CHECK_INITIALIZED + + layoutDecryptIdentity(&msg->identity); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + uint8_t hash[32]; + if (!msg->has_identity || + cryptoIdentityFingerprint(&(msg->identity), hash) == 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity")); + layoutHome(); + return; + } + + uint32_t address_n[5]; + address_n[0] = 0x80000000 | 17; + address_n[1] = 0x80000000 | hash[0] | (hash[1] << 8) | (hash[2] << 16) | + ((uint32_t)hash[3] << 24); + address_n[2] = 0x80000000 | hash[4] | (hash[5] << 8) | (hash[6] << 16) | + ((uint32_t)hash[7] << 24); + address_n[3] = 0x80000000 | hash[8] | (hash[9] << 8) | (hash[10] << 16) | + ((uint32_t)hash[11] << 24); + address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | + ((uint32_t)hash[15] << 24); + + const char *curve = SECP256K1_NAME; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + + const HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL); + if (!node) return; + + int result_size = 0; + if (hdnode_get_shared_key(node, msg->peer_public_key.bytes, + resp->session_key.bytes, &result_size) == 0) { + resp->has_session_key = true; + resp->session_key.size = result_size; + msg_write(MessageType_MessageType_ECDHSessionKey, resp); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Error getting ECDH session key")); + } + layoutHome(); } -void fsm_msgCosiCommit(const CosiCommit *msg) -{ - RESP_INIT(CosiCommitment); +void fsm_msgCosiCommit(const CosiCommit *msg) { + RESP_INIT(CosiCommitment); - CHECK_INITIALIZED + CHECK_INITIALIZED - CHECK_PARAM(msg->has_data, _("No data provided")); + CHECK_PARAM(msg->has_data, _("No data provided")); - layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, msg->data.size, false); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } + layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, + msg->data.size, false); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } - CHECK_PIN + CHECK_PIN - const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - uint8_t nonce[32]; - sha256_Raw(msg->data.bytes, msg->data.size, nonce); - rfc6979_state rng; - init_rfc6979(node->private_key, nonce, &rng); - generate_rfc6979(nonce, &rng); + uint8_t nonce[32]; + sha256_Raw(msg->data.bytes, msg->data.size, nonce); + rfc6979_state rng; + init_rfc6979(node->private_key, nonce, &rng); + generate_rfc6979(nonce, &rng); - resp->has_commitment = true; - resp->has_pubkey = true; - resp->commitment.size = 32; - resp->pubkey.size = 32; + resp->has_commitment = true; + resp->has_pubkey = true; + resp->commitment.size = 32; + resp->pubkey.size = 32; - ed25519_publickey(nonce, resp->commitment.bytes); - ed25519_publickey(node->private_key, resp->pubkey.bytes); + ed25519_publickey(nonce, resp->commitment.bytes); + ed25519_publickey(node->private_key, resp->pubkey.bytes); - msg_write(MessageType_MessageType_CosiCommitment, resp); - layoutHome(); + msg_write(MessageType_MessageType_CosiCommitment, resp); + layoutHome(); } -void fsm_msgCosiSign(const CosiSign *msg) -{ - RESP_INIT(CosiSignature); +void fsm_msgCosiSign(const CosiSign *msg) { + RESP_INIT(CosiSignature); - CHECK_INITIALIZED + CHECK_INITIALIZED - CHECK_PARAM(msg->has_data, _("No data provided")); - CHECK_PARAM(msg->has_global_commitment && msg->global_commitment.size == 32, _("Invalid global commitment")); - CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32, _("Invalid global pubkey")); + CHECK_PARAM(msg->has_data, _("No data provided")); + CHECK_PARAM(msg->has_global_commitment && msg->global_commitment.size == 32, + _("Invalid global commitment")); + CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32, + _("Invalid global pubkey")); - layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, msg->data.size, true); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } + layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes, + msg->data.size, true); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } - CHECK_PIN + CHECK_PIN - const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - uint8_t nonce[32]; - sha256_Raw(msg->data.bytes, msg->data.size, nonce); - rfc6979_state rng; - init_rfc6979(node->private_key, nonce, &rng); - generate_rfc6979(nonce, &rng); + uint8_t nonce[32]; + sha256_Raw(msg->data.bytes, msg->data.size, nonce); + rfc6979_state rng; + init_rfc6979(node->private_key, nonce, &rng); + generate_rfc6979(nonce, &rng); - resp->has_signature = true; - resp->signature.size = 32; + resp->has_signature = true; + resp->signature.size = 32; - ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key, nonce, msg->global_commitment.bytes, msg->global_pubkey.bytes, resp->signature.bytes); + ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key, nonce, + msg->global_commitment.bytes, msg->global_pubkey.bytes, + resp->signature.bytes); - msg_write(MessageType_MessageType_CosiSignature, resp); - layoutHome(); + msg_write(MessageType_MessageType_CosiSignature, resp); + layoutHome(); } diff --git a/firmware/fsm_msg_debug.h b/firmware/fsm_msg_debug.h index e338d6eaa..ff835b761 100644 --- a/firmware/fsm_msg_debug.h +++ b/firmware/fsm_msg_debug.h @@ -19,89 +19,85 @@ #if DEBUG_LINK -void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg) -{ - (void)msg; +void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg) { + (void)msg; - // Do not use RESP_INIT because it clears msg_resp, but another message - // might be being handled - DebugLinkState resp; - memzero(&resp, sizeof(resp)); + // Do not use RESP_INIT because it clears msg_resp, but another message + // might be being handled + DebugLinkState resp; + memzero(&resp, sizeof(resp)); - resp.has_layout = true; - resp.layout.size = OLED_BUFSIZE; - memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE); + resp.has_layout = true; + resp.layout.size = OLED_BUFSIZE; + memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE); - resp.has_pin = config_getPin(resp.pin, sizeof(resp.pin)); + resp.has_pin = config_getPin(resp.pin, sizeof(resp.pin)); - resp.has_matrix = true; - strlcpy(resp.matrix, pinmatrix_get(), sizeof(resp.matrix)); + resp.has_matrix = true; + strlcpy(resp.matrix, pinmatrix_get(), sizeof(resp.matrix)); - resp.has_reset_entropy = true; - resp.reset_entropy.size = reset_get_int_entropy(resp.reset_entropy.bytes); + resp.has_reset_entropy = true; + resp.reset_entropy.size = reset_get_int_entropy(resp.reset_entropy.bytes); - resp.has_reset_word = true; - strlcpy(resp.reset_word, reset_get_word(), sizeof(resp.reset_word)); + resp.has_reset_word = true; + strlcpy(resp.reset_word, reset_get_word(), sizeof(resp.reset_word)); - resp.has_recovery_fake_word = true; - strlcpy(resp.recovery_fake_word, recovery_get_fake_word(), sizeof(resp.recovery_fake_word)); + resp.has_recovery_fake_word = true; + strlcpy(resp.recovery_fake_word, recovery_get_fake_word(), + sizeof(resp.recovery_fake_word)); - resp.has_recovery_word_pos = true; - resp.recovery_word_pos = recovery_get_word_pos(); + resp.has_recovery_word_pos = true; + resp.recovery_word_pos = recovery_get_word_pos(); - resp.has_mnemonic_secret = config_getMnemonicBytes(resp.mnemonic_secret.bytes, sizeof(resp.mnemonic_secret.bytes), &resp.mnemonic_secret.size); - resp.mnemonic_type = 0; // BIP-39 + resp.has_mnemonic_secret = config_getMnemonicBytes( + resp.mnemonic_secret.bytes, sizeof(resp.mnemonic_secret.bytes), + &resp.mnemonic_secret.size); + resp.mnemonic_type = 0; // BIP-39 - resp.has_node = config_dumpNode(&(resp.node)); + resp.has_node = config_dumpNode(&(resp.node)); - resp.has_passphrase_protection = config_getPassphraseProtection(&(resp.passphrase_protection)); + resp.has_passphrase_protection = + config_getPassphraseProtection(&(resp.passphrase_protection)); - msg_debug_write(MessageType_MessageType_DebugLinkState, &resp); + msg_debug_write(MessageType_MessageType_DebugLinkState, &resp); } -void fsm_msgDebugLinkStop(const DebugLinkStop *msg) -{ - (void)msg; -} +void fsm_msgDebugLinkStop(const DebugLinkStop *msg) { (void)msg; } + +void fsm_msgDebugLinkMemoryRead(const DebugLinkMemoryRead *msg) { + RESP_INIT(DebugLinkMemory); -void fsm_msgDebugLinkMemoryRead(const DebugLinkMemoryRead *msg) -{ - RESP_INIT(DebugLinkMemory); - - uint32_t length = 1024; - if (msg->has_length && msg->length < length) - length = msg->length; - resp->has_memory = true; - memcpy(resp->memory.bytes, FLASH_PTR(msg->address), length); - resp->memory.size = length; - msg_debug_write(MessageType_MessageType_DebugLinkMemory, resp); + uint32_t length = 1024; + if (msg->has_length && msg->length < length) length = msg->length; + resp->has_memory = true; + memcpy(resp->memory.bytes, FLASH_PTR(msg->address), length); + resp->memory.size = length; + msg_debug_write(MessageType_MessageType_DebugLinkMemory, resp); } -void fsm_msgDebugLinkMemoryWrite(const DebugLinkMemoryWrite *msg) -{ - uint32_t length = msg->memory.size; - if (msg->flash) { - svc_flash_unlock(); - svc_flash_program(FLASH_CR_PROGRAM_X32); - for (uint32_t i = 0; i < length; i += 4) { - uint32_t word; - memcpy(&word, msg->memory.bytes + i, 4); - flash_write32(msg->address + i, word); - } - uint32_t dummy = svc_flash_lock(); - (void)dummy; - } else { +void fsm_msgDebugLinkMemoryWrite(const DebugLinkMemoryWrite *msg) { + uint32_t length = msg->memory.size; + if (msg->flash) { + svc_flash_unlock(); + svc_flash_program(FLASH_CR_PROGRAM_X32); + for (uint32_t i = 0; i < length; i += 4) { + uint32_t word; + memcpy(&word, msg->memory.bytes + i, 4); + flash_write32(msg->address + i, word); + } + uint32_t dummy = svc_flash_lock(); + (void)dummy; + } else { #if !EMULATOR - memcpy((void *) msg->address, msg->memory.bytes, length); + memcpy((void *)msg->address, msg->memory.bytes, length); #endif - } + } } -void fsm_msgDebugLinkFlashErase(const DebugLinkFlashErase *msg) -{ - svc_flash_unlock(); - svc_flash_erase_sector(msg->sector); - uint32_t dummy = svc_flash_lock(); - (void)dummy; +void fsm_msgDebugLinkFlashErase(const DebugLinkFlashErase *msg) { + svc_flash_unlock(); + svc_flash_erase_sector(msg->sector); + uint32_t dummy = svc_flash_lock(); + (void)dummy; } #endif diff --git a/firmware/fsm_msg_ethereum.h b/firmware/fsm_msg_ethereum.h index 2a9290772..d5047623e 100644 --- a/firmware/fsm_msg_ethereum.h +++ b/firmware/fsm_msg_ethereum.h @@ -17,163 +17,169 @@ * along with this library. If not, see . */ -void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) -{ - RESP_INIT(EthereumPublicKey); - - CHECK_INITIALIZED - - CHECK_PIN - - // we use Bitcoin-like format for ETH - const CoinInfo *coin = fsm_getCoin(true, "Bitcoin"); - if (!coin) return; - - const char *curve = coin->curve_name; - uint32_t fingerprint; - HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count, &fingerprint); - if (!node) return; - hdnode_fill_public_key(node); - - if (msg->has_show_display && msg->show_display) { - layoutPublicKey(node->public_key); - if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - resp->has_node = true; - resp->node.depth = node->depth; - resp->node.fingerprint = fingerprint; - resp->node.child_num = node->child_num; - resp->node.chain_code.size = 32; - memcpy(resp->node.chain_code.bytes, node->chain_code, 32); - resp->node.has_private_key = false; - resp->node.has_public_key = true; - resp->node.public_key.size = 33; - memcpy(resp->node.public_key.bytes, node->public_key, 33); - - resp->has_xpub = true; - hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, sizeof(resp->xpub)); - - msg_write(MessageType_MessageType_EthereumPublicKey, resp); - layoutHome(); +void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) { + RESP_INIT(EthereumPublicKey); + + CHECK_INITIALIZED + + CHECK_PIN + + // we use Bitcoin-like format for ETH + const CoinInfo *coin = fsm_getCoin(true, "Bitcoin"); + if (!coin) return; + + const char *curve = coin->curve_name; + uint32_t fingerprint; + HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n, + msg->address_n_count, &fingerprint); + if (!node) return; + hdnode_fill_public_key(node); + + if (msg->has_show_display && msg->show_display) { + layoutPublicKey(node->public_key); + if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + resp->has_node = true; + resp->node.depth = node->depth; + resp->node.fingerprint = fingerprint; + resp->node.child_num = node->child_num; + resp->node.chain_code.size = 32; + memcpy(resp->node.chain_code.bytes, node->chain_code, 32); + resp->node.has_private_key = false; + resp->node.has_public_key = true; + resp->node.public_key.size = 33; + memcpy(resp->node.public_key.bytes, node->public_key, 33); + + resp->has_xpub = true; + hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub, + sizeof(resp->xpub)); + + msg_write(MessageType_MessageType_EthereumPublicKey, resp); + layoutHome(); } -void fsm_msgEthereumSignTx(EthereumSignTx *msg) -{ - CHECK_INITIALIZED +void fsm_msgEthereumSignTx(EthereumSignTx *msg) { + CHECK_INITIALIZED - CHECK_PIN + CHECK_PIN - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - ethereum_signing_init(msg, node); + ethereum_signing_init(msg, node); } -void fsm_msgEthereumTxAck(const EthereumTxAck *msg) -{ - ethereum_signing_txack(msg); +void fsm_msgEthereumTxAck(const EthereumTxAck *msg) { + ethereum_signing_txack(msg); } -void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) -{ - RESP_INIT(EthereumAddress); - - CHECK_INITIALIZED - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - uint8_t pubkeyhash[20]; - - if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) - return; - - uint32_t slip44 = (msg->address_n_count > 1) ? (msg->address_n[1] & 0x7fffffff) : 0; - bool rskip60 = false; - uint32_t chain_id = 0; - // constants from trezor-common/defs/ethereum/networks.json - switch (slip44) { - case 137: rskip60 = true; chain_id = 30; break; - case 37310: rskip60 = true; chain_id = 31; break; - } - - resp->has_address = true; - resp->address[0] = '0'; - resp->address[1] = 'x'; - ethereum_address_checksum(pubkeyhash, resp->address + 2, rskip60, chain_id); - // ethereum_address_checksum adds trailing zero - - if (msg->has_show_display && msg->show_display) { - char desc[16]; - strlcpy(desc, "Address:", sizeof(desc)); - - if (!fsm_layoutAddress(resp->address, desc, false, 0, msg->address_n, msg->address_n_count, true)) { - return; - } - } - - msg_write(MessageType_MessageType_EthereumAddress, resp); - layoutHome(); +void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) { + RESP_INIT(EthereumAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + uint8_t pubkeyhash[20]; + + if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) return; + + uint32_t slip44 = + (msg->address_n_count > 1) ? (msg->address_n[1] & 0x7fffffff) : 0; + bool rskip60 = false; + uint32_t chain_id = 0; + // constants from trezor-common/defs/ethereum/networks.json + switch (slip44) { + case 137: + rskip60 = true; + chain_id = 30; + break; + case 37310: + rskip60 = true; + chain_id = 31; + break; + } + + resp->has_address = true; + resp->address[0] = '0'; + resp->address[1] = 'x'; + ethereum_address_checksum(pubkeyhash, resp->address + 2, rskip60, chain_id); + // ethereum_address_checksum adds trailing zero + + if (msg->has_show_display && msg->show_display) { + char desc[16]; + strlcpy(desc, "Address:", sizeof(desc)); + + if (!fsm_layoutAddress(resp->address, desc, false, 0, msg->address_n, + msg->address_n_count, true)) { + return; + } + } + + msg_write(MessageType_MessageType_EthereumAddress, resp); + layoutHome(); } -void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg) -{ - RESP_INIT(EthereumMessageSignature); +void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg) { + RESP_INIT(EthereumMessageSignature); - CHECK_INITIALIZED + CHECK_INITIALIZED - layoutSignMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } + layoutSignMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } - CHECK_PIN + CHECK_PIN - const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - ethereum_message_sign(msg, node, resp); - layoutHome(); + ethereum_message_sign(msg, node, resp); + layoutHome(); } -void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) -{ - CHECK_PARAM(msg->has_address, _("No address provided")); - CHECK_PARAM(msg->has_message, _("No message provided")); - - if (ethereum_message_verify(msg) != 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); - return; - } - - uint8_t pubkeyhash[20]; - if (!ethereum_parse(msg->address, pubkeyhash)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid address")); - return; - } - - layoutVerifyAddress(NULL, msg->address); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - layoutVerifyMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - fsm_sendSuccess(_("Message verified")); - - layoutHome(); +void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) { + CHECK_PARAM(msg->has_address, _("No address provided")); + CHECK_PARAM(msg->has_message, _("No message provided")); + + if (ethereum_message_verify(msg) != 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); + return; + } + + uint8_t pubkeyhash[20]; + if (!ethereum_parse(msg->address, pubkeyhash)) { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid address")); + return; + } + + layoutVerifyAddress(NULL, msg->address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + layoutVerifyMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + fsm_sendSuccess(_("Message verified")); + + layoutHome(); } diff --git a/firmware/fsm_msg_lisk.h b/firmware/fsm_msg_lisk.h index c8c37540e..e95820f57 100644 --- a/firmware/fsm_msg_lisk.h +++ b/firmware/fsm_msg_lisk.h @@ -17,126 +17,128 @@ * along with this library. If not, see . */ -void fsm_msgLiskGetAddress(const LiskGetAddress *msg) -{ - CHECK_INITIALIZED +void fsm_msgLiskGetAddress(const LiskGetAddress *msg) { + CHECK_INITIALIZED - CHECK_PIN + CHECK_PIN - RESP_INIT(LiskAddress); + RESP_INIT(LiskAddress); - HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - resp->has_address = true; - hdnode_fill_public_key(node); - lisk_get_address_from_public_key(&node->public_key[1], resp->address); + resp->has_address = true; + hdnode_fill_public_key(node); + lisk_get_address_from_public_key(&node->public_key[1], resp->address); - if (msg->has_show_display && msg->show_display) { - if (!fsm_layoutAddress(resp->address, _("Address:"), true, 0, msg->address_n, msg->address_n_count, false)) { - return; - } - } + if (msg->has_show_display && msg->show_display) { + if (!fsm_layoutAddress(resp->address, _("Address:"), true, 0, + msg->address_n, msg->address_n_count, false)) { + return; + } + } - msg_write(MessageType_MessageType_LiskAddress, resp); + msg_write(MessageType_MessageType_LiskAddress, resp); - layoutHome(); + layoutHome(); } -void fsm_msgLiskGetPublicKey(const LiskGetPublicKey *msg) -{ - CHECK_INITIALIZED +void fsm_msgLiskGetPublicKey(const LiskGetPublicKey *msg) { + CHECK_INITIALIZED - CHECK_PIN + CHECK_PIN - RESP_INIT(LiskPublicKey); + RESP_INIT(LiskPublicKey); - HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - hdnode_fill_public_key(node); + hdnode_fill_public_key(node); - resp->has_public_key = true; - resp->public_key.size = 32; + resp->has_public_key = true; + resp->public_key.size = 32; - if (msg->has_show_display && msg->show_display) { - layoutLiskPublicKey(&node->public_key[1]); - if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } + if (msg->has_show_display && msg->show_display) { + layoutLiskPublicKey(&node->public_key[1]); + if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } - memcpy(&resp->public_key.bytes, &node->public_key[1], sizeof(resp->public_key.bytes)); + memcpy(&resp->public_key.bytes, &node->public_key[1], + sizeof(resp->public_key.bytes)); - msg_write(MessageType_MessageType_LiskPublicKey, resp); + msg_write(MessageType_MessageType_LiskPublicKey, resp); - layoutHome(); + layoutHome(); } -void fsm_msgLiskSignMessage(const LiskSignMessage *msg) -{ - CHECK_INITIALIZED +void fsm_msgLiskSignMessage(const LiskSignMessage *msg) { + CHECK_INITIALIZED - CHECK_PIN + CHECK_PIN - RESP_INIT(LiskMessageSignature); + RESP_INIT(LiskMessageSignature); - HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - hdnode_fill_public_key(node); + hdnode_fill_public_key(node); - lisk_sign_message(node, msg, resp); + lisk_sign_message(node, msg, resp); - msg_write(MessageType_MessageType_LiskMessageSignature, resp); + msg_write(MessageType_MessageType_LiskMessageSignature, resp); - layoutHome(); + layoutHome(); } -void fsm_msgLiskVerifyMessage(const LiskVerifyMessage *msg) -{ - if (lisk_verify_message(msg)) { - char address[MAX_LISK_ADDRESS_SIZE]; - lisk_get_address_from_public_key((const uint8_t*)&msg->public_key, address); - - layoutLiskVerifyAddress(address); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - layoutVerifyMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - fsm_sendSuccess(_("Message verified")); - } else { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); - } - - layoutHome(); +void fsm_msgLiskVerifyMessage(const LiskVerifyMessage *msg) { + if (lisk_verify_message(msg)) { + char address[MAX_LISK_ADDRESS_SIZE]; + lisk_get_address_from_public_key((const uint8_t *)&msg->public_key, + address); + + layoutLiskVerifyAddress(address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + layoutVerifyMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + fsm_sendSuccess(_("Message verified")); + } else { + fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature")); + } + + layoutHome(); } -void fsm_msgLiskSignTx(LiskSignTx *msg) -{ - CHECK_INITIALIZED +void fsm_msgLiskSignTx(LiskSignTx *msg) { + CHECK_INITIALIZED - CHECK_PIN + CHECK_PIN - RESP_INIT(LiskSignedTx); + RESP_INIT(LiskSignedTx); - HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - hdnode_fill_public_key(node); + hdnode_fill_public_key(node); - lisk_sign_tx(node, msg, resp); + lisk_sign_tx(node, msg, resp); - msg_write(MessageType_MessageType_LiskSignedTx, resp); + msg_write(MessageType_MessageType_LiskSignedTx, resp); - layoutHome(); + layoutHome(); } diff --git a/firmware/fsm_msg_nem.h b/firmware/fsm_msg_nem.h index 04c3a63d8..ce7f29603 100644 --- a/firmware/fsm_msg_nem.h +++ b/firmware/fsm_msg_nem.h @@ -17,290 +17,336 @@ * along with this library. If not, see . */ -void fsm_msgNEMGetAddress(NEMGetAddress *msg) -{ - if (!msg->has_network) { - msg->network = NEM_NETWORK_MAINNET; - } +void fsm_msgNEMGetAddress(NEMGetAddress *msg) { + if (!msg->has_network) { + msg->network = NEM_NETWORK_MAINNET; + } - const char *network; - CHECK_PARAM((network = nem_network_name(msg->network)), _("Invalid NEM network")); + const char *network; + CHECK_PARAM((network = nem_network_name(msg->network)), + _("Invalid NEM network")); - CHECK_INITIALIZED - CHECK_PIN + CHECK_INITIALIZED + CHECK_PIN - RESP_INIT(NEMAddress); + RESP_INIT(NEMAddress); - HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; + HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; - if (!hdnode_get_nem_address(node, msg->network, resp->address)) - return; + if (!hdnode_get_nem_address(node, msg->network, resp->address)) return; - if (msg->has_show_display && msg->show_display) { - char desc[16]; - strlcpy(desc, network, sizeof(desc)); - strlcat(desc, ":", sizeof(desc)); + if (msg->has_show_display && msg->show_display) { + char desc[16]; + strlcpy(desc, network, sizeof(desc)); + strlcat(desc, ":", sizeof(desc)); - if (!fsm_layoutAddress(resp->address, desc, true, 0, msg->address_n, msg->address_n_count, false)) { - return; - } - } + if (!fsm_layoutAddress(resp->address, desc, true, 0, msg->address_n, + msg->address_n_count, false)) { + return; + } + } - msg_write(MessageType_MessageType_NEMAddress, resp); - layoutHome(); + msg_write(MessageType_MessageType_NEMAddress, resp); + layoutHome(); } void fsm_msgNEMSignTx(NEMSignTx *msg) { - const char *reason; - -#define NEM_CHECK_PARAM(s) CHECK_PARAM( (reason = (s)) == NULL, reason) -#define NEM_CHECK_PARAM_WHEN(b, s) CHECK_PARAM(!(b) || (reason = (s)) == NULL, reason) - - CHECK_PARAM(msg->has_transaction, _("No common provided")); - - // Ensure exactly one transaction is provided - unsigned int provided = msg->has_transfer + - msg->has_provision_namespace + - msg->has_mosaic_creation + - msg->has_supply_change + - msg->has_aggregate_modification + - msg->has_importance_transfer; - CHECK_PARAM(provided != 0, _("No transaction provided")); - CHECK_PARAM(provided == 1, _("More than one transaction provided")); - - NEM_CHECK_PARAM(nem_validate_common(&msg->transaction, false)); - NEM_CHECK_PARAM_WHEN(msg->has_transfer, nem_validate_transfer(&msg->transfer, msg->transaction.network)); - NEM_CHECK_PARAM_WHEN(msg->has_provision_namespace, nem_validate_provision_namespace(&msg->provision_namespace, msg->transaction.network)); - NEM_CHECK_PARAM_WHEN(msg->has_mosaic_creation, nem_validate_mosaic_creation(&msg->mosaic_creation, msg->transaction.network)); - NEM_CHECK_PARAM_WHEN(msg->has_supply_change, nem_validate_supply_change(&msg->supply_change)); - NEM_CHECK_PARAM_WHEN(msg->has_aggregate_modification, nem_validate_aggregate_modification(&msg->aggregate_modification, !msg->has_multisig)); - NEM_CHECK_PARAM_WHEN(msg->has_importance_transfer, nem_validate_importance_transfer(&msg->importance_transfer)); - - bool cosigning = msg->has_cosigning && msg->cosigning; - if (msg->has_multisig) { - NEM_CHECK_PARAM(nem_validate_common(&msg->multisig, true)); - - CHECK_PARAM(msg->transaction.network == msg->multisig.network, _("Inner transaction network is different")); - } else { - CHECK_PARAM(!cosigning, _("No multisig transaction to cosign")); - } - - CHECK_INITIALIZED - CHECK_PIN - - const char *network = nem_network_name(msg->transaction.network); - - if (msg->has_multisig) { - char address[NEM_ADDRESS_SIZE + 1]; - nem_get_address(msg->multisig.signer.bytes, msg->multisig.network, address); - - if (!nem_askMultisig(address, network, cosigning, msg->transaction.fee)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - } - - RESP_INIT(NEMSignedTx); - - HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, msg->transaction.address_n_count, NULL); - if (!node) return; - - hdnode_fill_public_key(node); - - const NEMTransactionCommon *common = msg->has_multisig ? &msg->multisig : &msg->transaction; - - char address[NEM_ADDRESS_SIZE + 1]; - hdnode_get_nem_address(node, common->network, address); - - if (msg->has_transfer) { - msg->transfer.mosaics_count = nem_canonicalizeMosaics(msg->transfer.mosaics, msg->transfer.mosaics_count); - } - - if (msg->has_transfer && !nem_askTransfer(common, &msg->transfer, network)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - - if (msg->has_provision_namespace && !nem_askProvisionNamespace(common, &msg->provision_namespace, network)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - - if (msg->has_mosaic_creation && !nem_askMosaicCreation(common, &msg->mosaic_creation, network, address)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - - if (msg->has_supply_change && !nem_askSupplyChange(common, &msg->supply_change, network)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - - if (msg->has_aggregate_modification && !nem_askAggregateModification(common, &msg->aggregate_modification, network, !msg->has_multisig)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - - if (msg->has_importance_transfer && !nem_askImportanceTransfer(common, &msg->importance_transfer, network)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, _("Signing cancelled by user")); - layoutHome(); - return; - } - - nem_transaction_ctx context; - nem_transaction_start(&context, &node->public_key[1], resp->data.bytes, sizeof(resp->data.bytes)); - - if (msg->has_multisig) { - uint8_t buffer[sizeof(resp->data.bytes)]; - - nem_transaction_ctx inner; - nem_transaction_start(&inner, msg->multisig.signer.bytes, buffer, sizeof(buffer)); - - if (msg->has_transfer && !nem_fsmTransfer(&inner, NULL, &msg->multisig, &msg->transfer)) { - layoutHome(); - return; - } - - if (msg->has_provision_namespace && !nem_fsmProvisionNamespace(&inner, &msg->multisig, &msg->provision_namespace)) { - layoutHome(); - return; - } - - if (msg->has_mosaic_creation && !nem_fsmMosaicCreation(&inner, &msg->multisig, &msg->mosaic_creation)) { - layoutHome(); - return; - } - - if (msg->has_supply_change && !nem_fsmSupplyChange(&inner, &msg->multisig, &msg->supply_change)) { - layoutHome(); - return; - } - - if (msg->has_aggregate_modification && !nem_fsmAggregateModification(&inner, &msg->multisig, &msg->aggregate_modification)) { - layoutHome(); - return; - } - - if (msg->has_importance_transfer && !nem_fsmImportanceTransfer(&inner, &msg->multisig, &msg->importance_transfer)) { - layoutHome(); - return; - } - - if (!nem_fsmMultisig(&context, &msg->transaction, &inner, cosigning)) { - layoutHome(); - return; - } - } else { - if (msg->has_transfer && !nem_fsmTransfer(&context, node, &msg->transaction, &msg->transfer)) { - layoutHome(); - return; - } - - if (msg->has_provision_namespace && !nem_fsmProvisionNamespace(&context, &msg->transaction, &msg->provision_namespace)) { - layoutHome(); - return; - } - - if (msg->has_mosaic_creation && !nem_fsmMosaicCreation(&context, &msg->transaction, &msg->mosaic_creation)) { - layoutHome(); - return; - } - - if (msg->has_supply_change && !nem_fsmSupplyChange(&context, &msg->transaction, &msg->supply_change)) { - layoutHome(); - return; - } - - if (msg->has_aggregate_modification && !nem_fsmAggregateModification(&context, &msg->transaction, &msg->aggregate_modification)) { - layoutHome(); - return; - } - - if (msg->has_importance_transfer && !nem_fsmImportanceTransfer(&context, &msg->transaction, &msg->importance_transfer)) { - layoutHome(); - return; - } - } - - resp->has_data = true; - resp->data.size = nem_transaction_end(&context, node->private_key, resp->signature.bytes); - - resp->has_signature = true; - resp->signature.size = sizeof(ed25519_signature); - - msg_write(MessageType_MessageType_NEMSignedTx, resp); - layoutHome(); + const char *reason; + +#define NEM_CHECK_PARAM(s) CHECK_PARAM((reason = (s)) == NULL, reason) +#define NEM_CHECK_PARAM_WHEN(b, s) \ + CHECK_PARAM(!(b) || (reason = (s)) == NULL, reason) + + CHECK_PARAM(msg->has_transaction, _("No common provided")); + + // Ensure exactly one transaction is provided + unsigned int provided = msg->has_transfer + msg->has_provision_namespace + + msg->has_mosaic_creation + msg->has_supply_change + + msg->has_aggregate_modification + + msg->has_importance_transfer; + CHECK_PARAM(provided != 0, _("No transaction provided")); + CHECK_PARAM(provided == 1, _("More than one transaction provided")); + + NEM_CHECK_PARAM(nem_validate_common(&msg->transaction, false)); + NEM_CHECK_PARAM_WHEN( + msg->has_transfer, + nem_validate_transfer(&msg->transfer, msg->transaction.network)); + NEM_CHECK_PARAM_WHEN( + msg->has_provision_namespace, + nem_validate_provision_namespace(&msg->provision_namespace, + msg->transaction.network)); + NEM_CHECK_PARAM_WHEN(msg->has_mosaic_creation, + nem_validate_mosaic_creation(&msg->mosaic_creation, + msg->transaction.network)); + NEM_CHECK_PARAM_WHEN(msg->has_supply_change, + nem_validate_supply_change(&msg->supply_change)); + NEM_CHECK_PARAM_WHEN(msg->has_aggregate_modification, + nem_validate_aggregate_modification( + &msg->aggregate_modification, !msg->has_multisig)); + NEM_CHECK_PARAM_WHEN( + msg->has_importance_transfer, + nem_validate_importance_transfer(&msg->importance_transfer)); + + bool cosigning = msg->has_cosigning && msg->cosigning; + if (msg->has_multisig) { + NEM_CHECK_PARAM(nem_validate_common(&msg->multisig, true)); + + CHECK_PARAM(msg->transaction.network == msg->multisig.network, + _("Inner transaction network is different")); + } else { + CHECK_PARAM(!cosigning, _("No multisig transaction to cosign")); + } + + CHECK_INITIALIZED + CHECK_PIN + + const char *network = nem_network_name(msg->transaction.network); + + if (msg->has_multisig) { + char address[NEM_ADDRESS_SIZE + 1]; + nem_get_address(msg->multisig.signer.bytes, msg->multisig.network, address); + + if (!nem_askMultisig(address, network, cosigning, msg->transaction.fee)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + } + + RESP_INIT(NEMSignedTx); + + HDNode *node = + fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n, + msg->transaction.address_n_count, NULL); + if (!node) return; + + hdnode_fill_public_key(node); + + const NEMTransactionCommon *common = + msg->has_multisig ? &msg->multisig : &msg->transaction; + + char address[NEM_ADDRESS_SIZE + 1]; + hdnode_get_nem_address(node, common->network, address); + + if (msg->has_transfer) { + msg->transfer.mosaics_count = nem_canonicalizeMosaics( + msg->transfer.mosaics, msg->transfer.mosaics_count); + } + + if (msg->has_transfer && !nem_askTransfer(common, &msg->transfer, network)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + + if (msg->has_provision_namespace && + !nem_askProvisionNamespace(common, &msg->provision_namespace, network)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + + if (msg->has_mosaic_creation && + !nem_askMosaicCreation(common, &msg->mosaic_creation, network, address)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + + if (msg->has_supply_change && + !nem_askSupplyChange(common, &msg->supply_change, network)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + + if (msg->has_aggregate_modification && + !nem_askAggregateModification(common, &msg->aggregate_modification, + network, !msg->has_multisig)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + + if (msg->has_importance_transfer && + !nem_askImportanceTransfer(common, &msg->importance_transfer, network)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, + _("Signing cancelled by user")); + layoutHome(); + return; + } + + nem_transaction_ctx context; + nem_transaction_start(&context, &node->public_key[1], resp->data.bytes, + sizeof(resp->data.bytes)); + + if (msg->has_multisig) { + uint8_t buffer[sizeof(resp->data.bytes)]; + + nem_transaction_ctx inner; + nem_transaction_start(&inner, msg->multisig.signer.bytes, buffer, + sizeof(buffer)); + + if (msg->has_transfer && + !nem_fsmTransfer(&inner, NULL, &msg->multisig, &msg->transfer)) { + layoutHome(); + return; + } + + if (msg->has_provision_namespace && + !nem_fsmProvisionNamespace(&inner, &msg->multisig, + &msg->provision_namespace)) { + layoutHome(); + return; + } + + if (msg->has_mosaic_creation && + !nem_fsmMosaicCreation(&inner, &msg->multisig, &msg->mosaic_creation)) { + layoutHome(); + return; + } + + if (msg->has_supply_change && + !nem_fsmSupplyChange(&inner, &msg->multisig, &msg->supply_change)) { + layoutHome(); + return; + } + + if (msg->has_aggregate_modification && + !nem_fsmAggregateModification(&inner, &msg->multisig, + &msg->aggregate_modification)) { + layoutHome(); + return; + } + + if (msg->has_importance_transfer && + !nem_fsmImportanceTransfer(&inner, &msg->multisig, + &msg->importance_transfer)) { + layoutHome(); + return; + } + + if (!nem_fsmMultisig(&context, &msg->transaction, &inner, cosigning)) { + layoutHome(); + return; + } + } else { + if (msg->has_transfer && + !nem_fsmTransfer(&context, node, &msg->transaction, &msg->transfer)) { + layoutHome(); + return; + } + + if (msg->has_provision_namespace && + !nem_fsmProvisionNamespace(&context, &msg->transaction, + &msg->provision_namespace)) { + layoutHome(); + return; + } + + if (msg->has_mosaic_creation && + !nem_fsmMosaicCreation(&context, &msg->transaction, + &msg->mosaic_creation)) { + layoutHome(); + return; + } + + if (msg->has_supply_change && + !nem_fsmSupplyChange(&context, &msg->transaction, + &msg->supply_change)) { + layoutHome(); + return; + } + + if (msg->has_aggregate_modification && + !nem_fsmAggregateModification(&context, &msg->transaction, + &msg->aggregate_modification)) { + layoutHome(); + return; + } + + if (msg->has_importance_transfer && + !nem_fsmImportanceTransfer(&context, &msg->transaction, + &msg->importance_transfer)) { + layoutHome(); + return; + } + } + + resp->has_data = true; + resp->data.size = + nem_transaction_end(&context, node->private_key, resp->signature.bytes); + + resp->has_signature = true; + resp->signature.size = sizeof(ed25519_signature); + + msg_write(MessageType_MessageType_NEMSignedTx, resp); + layoutHome(); } -void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg) -{ - RESP_INIT(NEMDecryptedMessage); - - CHECK_INITIALIZED - - CHECK_PARAM(nem_network_name(msg->network), _("Invalid NEM network")); - CHECK_PARAM(msg->has_payload, _("No payload provided")); - CHECK_PARAM(msg->payload.size >= NEM_ENCRYPTED_PAYLOAD_SIZE(0), _("Invalid encrypted payload")); - CHECK_PARAM(msg->has_public_key, _("No public key provided")); - CHECK_PARAM(msg->public_key.size == 32, _("Invalid public key")); - - char address[NEM_ADDRESS_SIZE + 1]; - nem_get_address(msg->public_key.bytes, msg->network, address); - - layoutNEMDialog(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - _("Decrypt message"), - _("Confirm address?"), - address); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - CHECK_PIN - - const HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n, msg->address_n_count, NULL); - if (!node) return; - - const uint8_t *salt = msg->payload.bytes; - uint8_t *iv = &msg->payload.bytes[NEM_SALT_SIZE]; - - const uint8_t *payload = &msg->payload.bytes[NEM_SALT_SIZE + AES_BLOCK_SIZE]; - size_t size = msg->payload.size - NEM_SALT_SIZE - AES_BLOCK_SIZE; - - // hdnode_nem_decrypt mutates the IV, so this will modify msg - bool ret = hdnode_nem_decrypt(node, - msg->public_key.bytes, - iv, - salt, - payload, - size, - resp->payload.bytes); - if (!ret) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to decrypt payload")); - layoutHome(); - return; - } - - resp->has_payload = true; - resp->payload.size = NEM_DECRYPTED_SIZE(resp->payload.bytes, size); - - layoutNEMTransferPayload(resp->payload.bytes, resp->payload.size, true); - if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - msg_write(MessageType_MessageType_NEMDecryptedMessage, resp); - layoutHome(); +void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg) { + RESP_INIT(NEMDecryptedMessage); + + CHECK_INITIALIZED + + CHECK_PARAM(nem_network_name(msg->network), _("Invalid NEM network")); + CHECK_PARAM(msg->has_payload, _("No payload provided")); + CHECK_PARAM(msg->payload.size >= NEM_ENCRYPTED_PAYLOAD_SIZE(0), + _("Invalid encrypted payload")); + CHECK_PARAM(msg->has_public_key, _("No public key provided")); + CHECK_PARAM(msg->public_key.size == 32, _("Invalid public key")); + + char address[NEM_ADDRESS_SIZE + 1]; + nem_get_address(msg->public_key.bytes, msg->network, address); + + layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Confirm"), + _("Decrypt message"), _("Confirm address?"), address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + CHECK_PIN + + const HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n, + msg->address_n_count, NULL); + if (!node) return; + + const uint8_t *salt = msg->payload.bytes; + uint8_t *iv = &msg->payload.bytes[NEM_SALT_SIZE]; + + const uint8_t *payload = &msg->payload.bytes[NEM_SALT_SIZE + AES_BLOCK_SIZE]; + size_t size = msg->payload.size - NEM_SALT_SIZE - AES_BLOCK_SIZE; + + // hdnode_nem_decrypt mutates the IV, so this will modify msg + bool ret = hdnode_nem_decrypt(node, msg->public_key.bytes, iv, salt, payload, + size, resp->payload.bytes); + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to decrypt payload")); + layoutHome(); + return; + } + + resp->has_payload = true; + resp->payload.size = NEM_DECRYPTED_SIZE(resp->payload.bytes, size); + + layoutNEMTransferPayload(resp->payload.bytes, resp->payload.size, true); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + msg_write(MessageType_MessageType_NEMDecryptedMessage, resp); + layoutHome(); } diff --git a/firmware/fsm_msg_stellar.h b/firmware/fsm_msg_stellar.h index 0c989f4b3..1aa1bbf82 100644 --- a/firmware/fsm_msg_stellar.h +++ b/firmware/fsm_msg_stellar.h @@ -17,271 +17,258 @@ * along with this library. If not, see . */ -void fsm_msgStellarGetAddress(const StellarGetAddress *msg) -{ - RESP_INIT(StellarAddress); - - CHECK_INITIALIZED - - CHECK_PIN - - const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count); - if (!node) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to derive private key")); - return; - } - - if (msg->has_show_display && msg->show_display) { - const char **str_addr_rows = stellar_lineBreakAddress(node->public_key + 1); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), _("Share public account ID?"), - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2], - NULL, - NULL, NULL - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - resp->has_address = true; - stellar_publicAddressAsStr(node->public_key + 1, resp->address, sizeof(resp->address)); - - msg_write(MessageType_MessageType_StellarAddress, resp); - - layoutHome(); +void fsm_msgStellarGetAddress(const StellarGetAddress *msg) { + RESP_INIT(StellarAddress); + + CHECK_INITIALIZED + + CHECK_PIN + + const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count); + if (!node) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive private key")); + return; + } + + if (msg->has_show_display && msg->show_display) { + const char **str_addr_rows = stellar_lineBreakAddress(node->public_key + 1); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), + _("Share public account ID?"), str_addr_rows[0], + str_addr_rows[1], str_addr_rows[2], NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + resp->has_address = true; + stellar_publicAddressAsStr(node->public_key + 1, resp->address, + sizeof(resp->address)); + + msg_write(MessageType_MessageType_StellarAddress, resp); + + layoutHome(); } -void fsm_msgStellarSignTx(const StellarSignTx *msg) -{ - CHECK_INITIALIZED - CHECK_PIN +void fsm_msgStellarSignTx(const StellarSignTx *msg) { + CHECK_INITIALIZED + CHECK_PIN - if (!stellar_signingInit(msg)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to derive private key")); - layoutHome(); - return; - } + if (!stellar_signingInit(msg)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to derive private key")); + layoutHome(); + return; + } - // Confirm transaction basics - stellar_layoutTransactionSummary(msg); + // Confirm transaction basics + stellar_layoutTransactionSummary(msg); - // Respond with a request for the first operation - RESP_INIT(StellarTxOpRequest); + // Respond with a request for the first operation + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); } -void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg) -{ - if (!stellar_confirmCreateAccountOp(msg)) return; +void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg) { + if (!stellar_confirmCreateAccountOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) -{ - // This will display additional dialogs to the user - if (!stellar_confirmPaymentOp(msg)) return; - - // Last operation was confirmed, send a StellarSignedTx - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); - - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); - - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } +void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) { + // This will display additional dialogs to the user + if (!stellar_confirmPaymentOp(msg)) return; + + // Last operation was confirmed, send a StellarSignedTx + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); + + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); + + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg) -{ - if (!stellar_confirmPathPaymentOp(msg)) return; +void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg) { + if (!stellar_confirmPathPaymentOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg) -{ - if (!stellar_confirmManageOfferOp(msg)) return; +void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg) { + if (!stellar_confirmManageOfferOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarCreatePassiveOfferOp(const StellarCreatePassiveOfferOp *msg) -{ - if (!stellar_confirmCreatePassiveOfferOp(msg)) return; +void fsm_msgStellarCreatePassiveOfferOp( + const StellarCreatePassiveOfferOp *msg) { + if (!stellar_confirmCreatePassiveOfferOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg) -{ - if (!stellar_confirmSetOptionsOp(msg)) return; +void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg) { + if (!stellar_confirmSetOptionsOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg) -{ - if (!stellar_confirmChangeTrustOp(msg)) return; +void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg) { + if (!stellar_confirmChangeTrustOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg) -{ - if (!stellar_confirmAllowTrustOp(msg)) return; +void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg) { + if (!stellar_confirmAllowTrustOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg) -{ - if (!stellar_confirmAccountMergeOp(msg)) return; +void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg) { + if (!stellar_confirmAccountMergeOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg) -{ - if (!stellar_confirmManageDataOp(msg)) return; +void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg) { + if (!stellar_confirmManageDataOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } -void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg) -{ - if (!stellar_confirmBumpSequenceOp(msg)) return; +void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg) { + if (!stellar_confirmBumpSequenceOp(msg)) return; - if (stellar_allOperationsConfirmed()) { - RESP_INIT(StellarSignedTx); + if (stellar_allOperationsConfirmed()) { + RESP_INIT(StellarSignedTx); - stellar_fillSignedTx(resp); - msg_write(MessageType_MessageType_StellarSignedTx, resp); - layoutHome(); - } - // Request the next operation to sign - else { - RESP_INIT(StellarTxOpRequest); + stellar_fillSignedTx(resp); + msg_write(MessageType_MessageType_StellarSignedTx, resp); + layoutHome(); + } + // Request the next operation to sign + else { + RESP_INIT(StellarTxOpRequest); - msg_write(MessageType_MessageType_StellarTxOpRequest, resp); - } + msg_write(MessageType_MessageType_StellarTxOpRequest, resp); + } } diff --git a/firmware/layout2.c b/firmware/layout2.c index b61a46790..234d27b50 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -17,957 +17,926 @@ * along with this library. If not, see . */ +#include #include #include -#include -#include "layout2.h" +#include "bignum.h" +#include "bitmaps.h" #include "config.h" +#include "gettext.h" +#include "layout2.h" +#include "memzero.h" +#include "nem2.h" #include "oled.h" -#include "bitmaps.h" -#include "string.h" -#include "util.h" #include "qrcodegen.h" -#include "timer.h" -#include "bignum.h" #include "secp256k1.h" -#include "nem2.h" -#include "gettext.h" -#include "memzero.h" +#include "string.h" +#include "timer.h" +#include "util.h" #define BITCOIN_DIVISIBILITY (8) -static const char *slip44_extras(uint32_t coin_type) -{ - if ((coin_type & 0x80000000) == 0) { - return 0; - } - switch (coin_type & 0x7fffffff) { - case 40: return "EXP"; // Expanse - case 43: return "NEM"; // NEM - case 60: return "ETH"; // Ethereum Mainnet - case 61: return "ETC"; // Ethereum Classic Mainnet - case 108: return "UBQ"; // UBIQ - case 137: return "RSK"; // Rootstock Mainnet - case 37310: return "tRSK"; // Rootstock Testnet - } - return 0; +static const char *slip44_extras(uint32_t coin_type) { + if ((coin_type & 0x80000000) == 0) { + return 0; + } + switch (coin_type & 0x7fffffff) { + case 40: + return "EXP"; // Expanse + case 43: + return "NEM"; // NEM + case 60: + return "ETH"; // Ethereum Mainnet + case 61: + return "ETC"; // Ethereum Classic Mainnet + case 108: + return "UBQ"; // UBIQ + case 137: + return "RSK"; // Rootstock Mainnet + case 37310: + return "tRSK"; // Rootstock Testnet + } + return 0; } #define BIP32_MAX_LAST_ELEMENT 1000000 -static const char *address_n_str(const uint32_t *address_n, size_t address_n_count, bool address_is_account) -{ - if (address_n_count > 8) { - return _("Unknown long path"); - } - if (address_n_count == 0) { - return _("Path: m"); - } - - // known BIP44/49 path - static char path[100]; - if (address_n_count == 5 && - (address_n[0] == (0x80000000 + 44) || address_n[0] == (0x80000000 + 49) || address_n[0] == (0x80000000 + 84)) && - (address_n[1] & 0x80000000) && - (address_n[2] & 0x80000000) && - (address_n[3] <= 1) && - (address_n[4] <= BIP32_MAX_LAST_ELEMENT)) { - bool native_segwit = (address_n[0] == (0x80000000 + 84)); - bool p2sh_segwit = (address_n[0] == (0x80000000 + 49)); - bool legacy = false; - const CoinInfo *coin = coinBySlip44(address_n[1]); - const char *abbr = 0; - if (native_segwit) { - if (coin && coin->has_segwit && coin->bech32_prefix) { - abbr = coin->coin_shortcut + 1; - } - } else - if (p2sh_segwit) { - if (coin && coin->has_segwit && coin->has_address_type_p2sh) { - abbr = coin->coin_shortcut + 1; - } - } else { - if (coin) { - if (coin->has_segwit && coin->has_address_type_p2sh) { - legacy = true; - } - abbr = coin->coin_shortcut + 1; - } else { - abbr = slip44_extras(address_n[1]); - } - } - const uint32_t accnum = address_is_account ? ((address_n[4] & 0x7fffffff) + 1) : (address_n[2] & 0x7fffffff) + 1; - if (abbr && accnum < 100) { - memzero(path, sizeof(path)); - strlcpy(path, abbr, sizeof(path)); - // TODO: how to name accounts? - // currently we have "legacy account", "account" and "segwit account" - // for BIP44/P2PKH, BIP49/P2SH-P2WPKH and BIP84/P2WPKH respectivelly - if (legacy) { - strlcat(path, " legacy", sizeof(path)); - } - if (native_segwit) { - strlcat(path, " segwit", sizeof(path)); - } - if (address_is_account) { - strlcat(path, " address #", sizeof(path)); - } else { - strlcat(path, " account #", sizeof(path)); - } - char acc[3]; - memzero(acc, sizeof(acc)); - if (accnum < 10) { - acc[0] = '0' + accnum; - } else { - acc[0] = '0' + (accnum / 10); - acc[1] = '0' + (accnum % 10); - } - strlcat(path, acc, sizeof(path)); - return path; - } - } - - // "Path: m" / i ' - static char address_str[7 + 8 * (1 + 10 + 1) + 1]; - char *c = address_str + sizeof(address_str) - 1; - - *c = 0; c--; - - for (int n = (int)address_n_count - 1; n >= 0; n--) { - uint32_t i = address_n[n]; - if (i & 0x80000000) { - *c = '\''; c--; - } - i = i & 0x7fffffff; - do { - *c = '0' + (i % 10); c--; - i /= 10; - } while (i > 0); - *c = '/'; c--; - } - *c = 'm'; c--; - *c = ' '; c--; - *c = ':'; c--; - *c = 'h'; c--; - *c = 't'; c--; - *c = 'a'; c--; - *c = 'P'; - - return c; +static const char *address_n_str(const uint32_t *address_n, + size_t address_n_count, + bool address_is_account) { + if (address_n_count > 8) { + return _("Unknown long path"); + } + if (address_n_count == 0) { + return _("Path: m"); + } + + // known BIP44/49 path + static char path[100]; + if (address_n_count == 5 && + (address_n[0] == (0x80000000 + 44) || address_n[0] == (0x80000000 + 49) || + address_n[0] == (0x80000000 + 84)) && + (address_n[1] & 0x80000000) && (address_n[2] & 0x80000000) && + (address_n[3] <= 1) && (address_n[4] <= BIP32_MAX_LAST_ELEMENT)) { + bool native_segwit = (address_n[0] == (0x80000000 + 84)); + bool p2sh_segwit = (address_n[0] == (0x80000000 + 49)); + bool legacy = false; + const CoinInfo *coin = coinBySlip44(address_n[1]); + const char *abbr = 0; + if (native_segwit) { + if (coin && coin->has_segwit && coin->bech32_prefix) { + abbr = coin->coin_shortcut + 1; + } + } else if (p2sh_segwit) { + if (coin && coin->has_segwit && coin->has_address_type_p2sh) { + abbr = coin->coin_shortcut + 1; + } + } else { + if (coin) { + if (coin->has_segwit && coin->has_address_type_p2sh) { + legacy = true; + } + abbr = coin->coin_shortcut + 1; + } else { + abbr = slip44_extras(address_n[1]); + } + } + const uint32_t accnum = address_is_account + ? ((address_n[4] & 0x7fffffff) + 1) + : (address_n[2] & 0x7fffffff) + 1; + if (abbr && accnum < 100) { + memzero(path, sizeof(path)); + strlcpy(path, abbr, sizeof(path)); + // TODO: how to name accounts? + // currently we have "legacy account", "account" and "segwit account" + // for BIP44/P2PKH, BIP49/P2SH-P2WPKH and BIP84/P2WPKH respectivelly + if (legacy) { + strlcat(path, " legacy", sizeof(path)); + } + if (native_segwit) { + strlcat(path, " segwit", sizeof(path)); + } + if (address_is_account) { + strlcat(path, " address #", sizeof(path)); + } else { + strlcat(path, " account #", sizeof(path)); + } + char acc[3]; + memzero(acc, sizeof(acc)); + if (accnum < 10) { + acc[0] = '0' + accnum; + } else { + acc[0] = '0' + (accnum / 10); + acc[1] = '0' + (accnum % 10); + } + strlcat(path, acc, sizeof(path)); + return path; + } + } + + // "Path: m" / i ' + static char address_str[7 + 8 * (1 + 10 + 1) + 1]; + char *c = address_str + sizeof(address_str) - 1; + + *c = 0; + c--; + + for (int n = (int)address_n_count - 1; n >= 0; n--) { + uint32_t i = address_n[n]; + if (i & 0x80000000) { + *c = '\''; + c--; + } + i = i & 0x7fffffff; + do { + *c = '0' + (i % 10); + c--; + i /= 10; + } while (i > 0); + *c = '/'; + c--; + } + *c = 'm'; + c--; + *c = ' '; + c--; + *c = ':'; + c--; + *c = 'h'; + c--; + *c = 't'; + c--; + *c = 'a'; + c--; + *c = 'P'; + + return c; } // split longer string into 4 rows, rowlen chars each -const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen) -{ - static char str[4][32 + 1]; - if (rowlen > 32) { - rowlen = 32; - } - memzero(str, sizeof(str)); - strlcpy(str[0], (char *)msg, rowlen + 1); - if (len > rowlen) { - strlcpy(str[1], (char *)msg + rowlen, rowlen + 1); - } - if (len > rowlen * 2) { - strlcpy(str[2], (char *)msg + rowlen * 2, rowlen + 1); - } - if (len > rowlen * 3) { - strlcpy(str[3], (char *)msg + rowlen * 3, rowlen + 1); - } - if (len > rowlen * 4) { - str[3][rowlen - 1] = '.'; - str[3][rowlen - 2] = '.'; - str[3][rowlen - 3] = '.'; - } - static const char *ret[4] = { str[0], str[1], str[2], str[3] }; - return ret; -} - -const char **split_message_hex(const uint8_t *msg, uint32_t len) -{ - char hex[32 * 2 + 1]; - memzero(hex, sizeof(hex)); - uint32_t size = len; - if (len > 32) { - size = 32; - } - data2hex(msg, size, hex); - if (len > 32) { - hex[63] = '.'; - hex[62] = '.'; - } - return split_message((const uint8_t *)hex, size * 2, 16); +const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen) { + static char str[4][32 + 1]; + if (rowlen > 32) { + rowlen = 32; + } + memzero(str, sizeof(str)); + strlcpy(str[0], (char *)msg, rowlen + 1); + if (len > rowlen) { + strlcpy(str[1], (char *)msg + rowlen, rowlen + 1); + } + if (len > rowlen * 2) { + strlcpy(str[2], (char *)msg + rowlen * 2, rowlen + 1); + } + if (len > rowlen * 3) { + strlcpy(str[3], (char *)msg + rowlen * 3, rowlen + 1); + } + if (len > rowlen * 4) { + str[3][rowlen - 1] = '.'; + str[3][rowlen - 2] = '.'; + str[3][rowlen - 3] = '.'; + } + static const char *ret[4] = {str[0], str[1], str[2], str[3]}; + return ret; +} + +const char **split_message_hex(const uint8_t *msg, uint32_t len) { + char hex[32 * 2 + 1]; + memzero(hex, sizeof(hex)); + uint32_t size = len; + if (len > 32) { + size = 32; + } + data2hex(msg, size, hex); + if (len > 32) { + hex[63] = '.'; + hex[62] = '.'; + } + return split_message((const uint8_t *)hex, size * 2, 16); } void *layoutLast = layoutHome; -void layoutDialogSwipe(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) -{ - layoutLast = layoutDialogSwipe; - layoutSwipe(); - layoutDialog(icon, btnNo, btnYes, desc, line1, line2, line3, line4, line5, line6); -} - -void layoutProgressSwipe(const char *desc, int permil) -{ - if (layoutLast == layoutProgressSwipe) { - oledClear(); - } else { - layoutLast = layoutProgressSwipe; - layoutSwipe(); - } - layoutProgress(desc, permil); -} - -void layoutScreensaver(void) -{ - layoutLast = layoutScreensaver; - oledClear(); - oledRefresh(); -} - -void layoutHome(void) -{ - if (layoutLast == layoutHome || layoutLast == layoutScreensaver) { - oledClear(); - } else { - layoutSwipe(); - } - layoutLast = layoutHome; - - char label[MAX_LABEL_LEN + 1] = _("Go to trezor.io/start"); - if (config_isInitialized()) { - config_getLabel(label, sizeof(label)); - } - - uint8_t homescreen[HOMESCREEN_SIZE]; - if (config_getHomescreen(homescreen, sizeof(homescreen))) { - BITMAP b; - b.width = 128; - b.height = 64; - b.data = homescreen; - oledDrawBitmap(0, 0, &b); - } else { - if (label[0] != '\0') { - oledDrawBitmap(44, 4, &bmp_logo48); - oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 8, label, FONT_STANDARD); - } else { - oledDrawBitmap(40, 0, &bmp_logo64); - } - } - - bool no_backup = false; - bool unfinished_backup = false; - bool needs_backup = false; - config_getNoBackup(&no_backup); - config_getUnfinishedBackup(&unfinished_backup); - config_getNeedsBackup(&needs_backup); - if (no_backup) { - oledBox(0, 0, 127, 8, false); - oledDrawStringCenter(OLED_WIDTH / 2, 0, "SEEDLESS", FONT_STANDARD); - } else if (unfinished_backup) { - oledBox(0, 0, 127, 8, false); - oledDrawStringCenter(OLED_WIDTH / 2, 0, "BACKUP FAILED!", FONT_STANDARD); - } else if (needs_backup) { - oledBox(0, 0, 127, 8, false); - oledDrawStringCenter(OLED_WIDTH / 2, 0, "NEEDS BACKUP!", FONT_STANDARD); - } - oledRefresh(); - - // Reset lock screen timeout - system_millis_lock_start = timer_ms(); -} - -static void render_address_dialog(const CoinInfo *coin, const char *address, const char *line1, const char *line2, const char *extra_line) -{ - if (coin && coin->cashaddr_prefix) { - /* If this is a cashaddr address, remove the prefix from the - * string presented to the user - */ - int prefix_len = strlen(coin->cashaddr_prefix); - if (strncmp(address, coin->cashaddr_prefix, prefix_len) == 0 - && address[prefix_len] == ':') { - address += prefix_len + 1; - } - } - int addrlen = strlen(address); - int numlines = addrlen <= 42 ? 2 : 3; - int linelen = (addrlen - 1) / numlines + 1; - if (linelen > 21) { - linelen = 21; - } - const char **str = split_message((const uint8_t *)address, addrlen, linelen); - layoutLast = layoutDialogSwipe; - layoutSwipe(); - oledClear(); - oledDrawBitmap(0, 0, &bmp_icon_question); - oledDrawString(20, 0 * 9, line1, FONT_STANDARD); - oledDrawString(20, 1 * 9, line2, FONT_STANDARD); - int left = linelen > 18 ? 0 : 20; - oledDrawString(left, 2 * 9, str[0], FONT_FIXED); - oledDrawString(left, 3 * 9, str[1], FONT_FIXED); - oledDrawString(left, 4 * 9, str[2], FONT_FIXED); - oledDrawString(left, 5 * 9, str[3], FONT_FIXED); - if (!str[3][0]) { - if (extra_line) { - oledDrawString(0, 5 * 9, extra_line, FONT_STANDARD); - } else { - oledHLine(OLED_HEIGHT - 13); - } - } - layoutButtonNo(_("Cancel")); - layoutButtonYes(_("Confirm")); - oledRefresh(); -} - -void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out) -{ - char str_out[32 + 3]; - bn_format_uint64(out->amount, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out) - 3); - strlcat(str_out, " to", sizeof(str_out)); - const char *address = out->address; - const char *extra_line = (out->address_n_count > 0) ? address_n_str(out->address_n, out->address_n_count, false) : 0; - render_address_dialog(coin, address, _("Confirm sending"), str_out, extra_line); -} - -void layoutConfirmOmni(const uint8_t *data, uint32_t size) -{ - const char *desc; - char str_out[32]; - const uint32_t tx_type = *(const uint32_t *)(data + 4); - if (tx_type == 0x00000000 && size == 20) { // OMNI simple send - desc = _("Simple send of "); - const uint32_t currency = *(const uint32_t *)(data + 8); - const char *suffix = "UNKN"; - switch (currency) { - case 1: - suffix = "OMNI"; - break; - case 2: - suffix = "tOMNI"; - break; - case 3: - suffix = "MAID"; - break; - case 31: - suffix = "USDT"; - break; - } - const uint64_t amount = *(const uint64_t *)(data + 12); - bn_format_uint64(amount, NULL, suffix, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out)); - } else { - desc = _("Unknown transaction"); - str_out[0] = 0; - } - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Confirm OMNI Transaction:"), - NULL, - desc, - NULL, - str_out, - NULL - ); -} - -static bool is_valid_ascii(const uint8_t *data, uint32_t size) -{ - for (uint32_t i = 0; i < size; i++) { - if (data[i] < ' ' || data[i] > '~') { - return false; - } - } - return true; -} - -void layoutConfirmOpReturn(const uint8_t *data, uint32_t size) -{ - const char **str; - if (!is_valid_ascii(data, size)) { - str = split_message_hex(data, size); - } else { - str = split_message(data, size, 20); - } - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Confirm OP_RETURN:"), - str[0], - str[1], - str[2], - str[3], - NULL - ); -} - -void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out, uint64_t amount_fee) -{ - char str_out[32], str_fee[32]; - bn_format_uint64(amount_out, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_out, sizeof(str_out)); - bn_format_uint64(amount_fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_fee, sizeof(str_fee)); - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Really send"), - str_out, - _("from your wallet?"), - _("Fee included:"), - str_fee, - NULL - ); -} - -void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee) -{ - char str_fee[32]; - bn_format_uint64(fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, false, str_fee, sizeof(str_fee)); - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - NULL, - _("Fee"), - str_fee, - _("is unexpectedly high."), - NULL, - _("Send anyway?"), - NULL - ); -} - -void layoutSignMessage(const uint8_t *msg, uint32_t len) -{ - const char **str; - if (!is_valid_ascii(msg, len)) { - str = split_message_hex(msg, len); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - _("Sign binary message?"), - str[0], str[1], str[2], str[3], NULL, NULL); - } else { - str = split_message(msg, len, 20); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - _("Sign message?"), - str[0], str[1], str[2], str[3], NULL, NULL); - } -} - -void layoutVerifyMessage(const uint8_t *msg, uint32_t len) -{ - const char **str; - if (!is_valid_ascii(msg, len)) { - str = split_message_hex(msg, len); - layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"), - _("Verified binary message"), - str[0], str[1], str[2], str[3], NULL, NULL); - } else { - str = split_message(msg, len, 20); - layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"), - _("Verified message"), - str[0], str[1], str[2], str[3], NULL, NULL); - } -} - -void layoutVerifyAddress(const CoinInfo *coin, const char *address) -{ - render_address_dialog(coin, address, _("Confirm address?"), _("Message signed by:"), 0); -} - -void layoutCipherKeyValue(bool encrypt, const char *key) -{ - const char **str = split_message((const uint8_t *)key, strlen(key), 16); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - encrypt ? _("Encrypt value of this key?") : _("Decrypt value of this key?"), - str[0], str[1], str[2], str[3], NULL, NULL); -} - -void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing) -{ - const char **str = split_message(msg, len, 16); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - signing ? _("Encrypt+Sign message?") : _("Encrypt message?"), - str[0], str[1], str[2], str[3], NULL, NULL); -} - -void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address) -{ - const char **str = split_message(msg, len, 16); - layoutDialogSwipe(&bmp_icon_info, NULL, _("OK"), - address ? _("Decrypted signed message") : _("Decrypted message"), - str[0], str[1], str[2], str[3], NULL, NULL); -} - -void layoutResetWord(const char *word, int pass, int word_pos, bool last) -{ - layoutLast = layoutResetWord; - layoutSwipe(); - - const char *btnYes; - if (last) { - if (pass == 1) { - btnYes = _("Finish"); - } else { - btnYes = _("Again"); - } - } else { - btnYes = _("Next"); - } - - const char *action; - if (pass == 1) { - action = _("Please check the seed"); - } else { - action = _("Write down the seed"); - } - - char index_str[] = "##th word is:"; - if (word_pos < 10) { - index_str[0] = ' '; - } else { - index_str[0] = '0' + word_pos / 10; - } - index_str[1] = '0' + word_pos % 10; - if (word_pos == 1 || word_pos == 21) { - index_str[2] = 's'; index_str[3] = 't'; - } else - if (word_pos == 2 || word_pos == 22) { - index_str[2] = 'n'; index_str[3] = 'd'; - } else - if (word_pos == 3 || word_pos == 23) { - index_str[2] = 'r'; index_str[3] = 'd'; - } - - int left = 0; - oledClear(); - oledDrawBitmap(0, 0, &bmp_icon_info); - left = bmp_icon_info.width + 4; - - oledDrawString(left, 0 * 9, action, FONT_STANDARD); - oledDrawString(left, 2 * 9, word_pos < 10 ? index_str + 1 : index_str, FONT_STANDARD); - oledDrawString(left, 3 * 9, word, FONT_STANDARD | FONT_DOUBLE); - oledHLine(OLED_HEIGHT - 13); - layoutButtonYes(btnYes); - oledRefresh(); +void layoutDialogSwipe(const BITMAP *icon, const char *btnNo, + const char *btnYes, const char *desc, const char *line1, + const char *line2, const char *line3, const char *line4, + const char *line5, const char *line6) { + layoutLast = layoutDialogSwipe; + layoutSwipe(); + layoutDialog(icon, btnNo, btnYes, desc, line1, line2, line3, line4, line5, + line6); +} + +void layoutProgressSwipe(const char *desc, int permil) { + if (layoutLast == layoutProgressSwipe) { + oledClear(); + } else { + layoutLast = layoutProgressSwipe; + layoutSwipe(); + } + layoutProgress(desc, permil); +} + +void layoutScreensaver(void) { + layoutLast = layoutScreensaver; + oledClear(); + oledRefresh(); +} + +void layoutHome(void) { + if (layoutLast == layoutHome || layoutLast == layoutScreensaver) { + oledClear(); + } else { + layoutSwipe(); + } + layoutLast = layoutHome; + + char label[MAX_LABEL_LEN + 1] = _("Go to trezor.io/start"); + if (config_isInitialized()) { + config_getLabel(label, sizeof(label)); + } + + uint8_t homescreen[HOMESCREEN_SIZE]; + if (config_getHomescreen(homescreen, sizeof(homescreen))) { + BITMAP b; + b.width = 128; + b.height = 64; + b.data = homescreen; + oledDrawBitmap(0, 0, &b); + } else { + if (label[0] != '\0') { + oledDrawBitmap(44, 4, &bmp_logo48); + oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 8, label, + FONT_STANDARD); + } else { + oledDrawBitmap(40, 0, &bmp_logo64); + } + } + + bool no_backup = false; + bool unfinished_backup = false; + bool needs_backup = false; + config_getNoBackup(&no_backup); + config_getUnfinishedBackup(&unfinished_backup); + config_getNeedsBackup(&needs_backup); + if (no_backup) { + oledBox(0, 0, 127, 8, false); + oledDrawStringCenter(OLED_WIDTH / 2, 0, "SEEDLESS", FONT_STANDARD); + } else if (unfinished_backup) { + oledBox(0, 0, 127, 8, false); + oledDrawStringCenter(OLED_WIDTH / 2, 0, "BACKUP FAILED!", FONT_STANDARD); + } else if (needs_backup) { + oledBox(0, 0, 127, 8, false); + oledDrawStringCenter(OLED_WIDTH / 2, 0, "NEEDS BACKUP!", FONT_STANDARD); + } + oledRefresh(); + + // Reset lock screen timeout + system_millis_lock_start = timer_ms(); +} + +static void render_address_dialog(const CoinInfo *coin, const char *address, + const char *line1, const char *line2, + const char *extra_line) { + if (coin && coin->cashaddr_prefix) { + /* If this is a cashaddr address, remove the prefix from the + * string presented to the user + */ + int prefix_len = strlen(coin->cashaddr_prefix); + if (strncmp(address, coin->cashaddr_prefix, prefix_len) == 0 && + address[prefix_len] == ':') { + address += prefix_len + 1; + } + } + int addrlen = strlen(address); + int numlines = addrlen <= 42 ? 2 : 3; + int linelen = (addrlen - 1) / numlines + 1; + if (linelen > 21) { + linelen = 21; + } + const char **str = split_message((const uint8_t *)address, addrlen, linelen); + layoutLast = layoutDialogSwipe; + layoutSwipe(); + oledClear(); + oledDrawBitmap(0, 0, &bmp_icon_question); + oledDrawString(20, 0 * 9, line1, FONT_STANDARD); + oledDrawString(20, 1 * 9, line2, FONT_STANDARD); + int left = linelen > 18 ? 0 : 20; + oledDrawString(left, 2 * 9, str[0], FONT_FIXED); + oledDrawString(left, 3 * 9, str[1], FONT_FIXED); + oledDrawString(left, 4 * 9, str[2], FONT_FIXED); + oledDrawString(left, 5 * 9, str[3], FONT_FIXED); + if (!str[3][0]) { + if (extra_line) { + oledDrawString(0, 5 * 9, extra_line, FONT_STANDARD); + } else { + oledHLine(OLED_HEIGHT - 13); + } + } + layoutButtonNo(_("Cancel")); + layoutButtonYes(_("Confirm")); + oledRefresh(); +} + +void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out) { + char str_out[32 + 3]; + bn_format_uint64(out->amount, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, + 0, false, str_out, sizeof(str_out) - 3); + strlcat(str_out, " to", sizeof(str_out)); + const char *address = out->address; + const char *extra_line = + (out->address_n_count > 0) + ? address_n_str(out->address_n, out->address_n_count, false) + : 0; + render_address_dialog(coin, address, _("Confirm sending"), str_out, + extra_line); +} + +void layoutConfirmOmni(const uint8_t *data, uint32_t size) { + const char *desc; + char str_out[32]; + const uint32_t tx_type = *(const uint32_t *)(data + 4); + if (tx_type == 0x00000000 && size == 20) { // OMNI simple send + desc = _("Simple send of "); + const uint32_t currency = *(const uint32_t *)(data + 8); + const char *suffix = "UNKN"; + switch (currency) { + case 1: + suffix = "OMNI"; + break; + case 2: + suffix = "tOMNI"; + break; + case 3: + suffix = "MAID"; + break; + case 31: + suffix = "USDT"; + break; + } + const uint64_t amount = *(const uint64_t *)(data + 12); + bn_format_uint64(amount, NULL, suffix, BITCOIN_DIVISIBILITY, 0, false, + str_out, sizeof(str_out)); + } else { + desc = _("Unknown transaction"); + str_out[0] = 0; + } + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm OMNI Transaction:"), NULL, desc, NULL, str_out, + NULL); +} + +static bool is_valid_ascii(const uint8_t *data, uint32_t size) { + for (uint32_t i = 0; i < size; i++) { + if (data[i] < ' ' || data[i] > '~') { + return false; + } + } + return true; +} + +void layoutConfirmOpReturn(const uint8_t *data, uint32_t size) { + const char **str; + if (!is_valid_ascii(data, size)) { + str = split_message_hex(data, size); + } else { + str = split_message(data, size, 20); + } + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm OP_RETURN:"), str[0], str[1], str[2], str[3], + NULL); +} + +void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out, + uint64_t amount_fee) { + char str_out[32], str_fee[32]; + bn_format_uint64(amount_out, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, + 0, false, str_out, sizeof(str_out)); + bn_format_uint64(amount_fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, + 0, false, str_fee, sizeof(str_fee)); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Really send"), str_out, _("from your wallet?"), + _("Fee included:"), str_fee, NULL); +} + +void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee) { + char str_fee[32]; + bn_format_uint64(fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0, + false, str_fee, sizeof(str_fee)); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Fee"), str_fee, _("is unexpectedly high."), NULL, + _("Send anyway?"), NULL); +} + +void layoutSignMessage(const uint8_t *msg, uint32_t len) { + const char **str; + if (!is_valid_ascii(msg, len)) { + str = split_message_hex(msg, len); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), + _("Sign binary message?"), str[0], str[1], str[2], str[3], + NULL, NULL); + } else { + str = split_message(msg, len, 20); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), + _("Sign message?"), str[0], str[1], str[2], str[3], NULL, + NULL); + } +} + +void layoutVerifyMessage(const uint8_t *msg, uint32_t len) { + const char **str; + if (!is_valid_ascii(msg, len)) { + str = split_message_hex(msg, len); + layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"), + _("Verified binary message"), str[0], str[1], str[2], + str[3], NULL, NULL); + } else { + str = split_message(msg, len, 20); + layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"), + _("Verified message"), str[0], str[1], str[2], str[3], + NULL, NULL); + } +} + +void layoutVerifyAddress(const CoinInfo *coin, const char *address) { + render_address_dialog(coin, address, _("Confirm address?"), + _("Message signed by:"), 0); +} + +void layoutCipherKeyValue(bool encrypt, const char *key) { + const char **str = split_message((const uint8_t *)key, strlen(key), 16); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), + encrypt ? _("Encrypt value of this key?") + : _("Decrypt value of this key?"), + str[0], str[1], str[2], str[3], NULL, NULL); +} + +void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing) { + const char **str = split_message(msg, len, 16); + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Confirm"), + signing ? _("Encrypt+Sign message?") : _("Encrypt message?"), str[0], + str[1], str[2], str[3], NULL, NULL); +} + +void layoutDecryptMessage(const uint8_t *msg, uint32_t len, + const char *address) { + const char **str = split_message(msg, len, 16); + layoutDialogSwipe( + &bmp_icon_info, NULL, _("OK"), + address ? _("Decrypted signed message") : _("Decrypted message"), str[0], + str[1], str[2], str[3], NULL, NULL); +} + +void layoutResetWord(const char *word, int pass, int word_pos, bool last) { + layoutLast = layoutResetWord; + layoutSwipe(); + + const char *btnYes; + if (last) { + if (pass == 1) { + btnYes = _("Finish"); + } else { + btnYes = _("Again"); + } + } else { + btnYes = _("Next"); + } + + const char *action; + if (pass == 1) { + action = _("Please check the seed"); + } else { + action = _("Write down the seed"); + } + + char index_str[] = "##th word is:"; + if (word_pos < 10) { + index_str[0] = ' '; + } else { + index_str[0] = '0' + word_pos / 10; + } + index_str[1] = '0' + word_pos % 10; + if (word_pos == 1 || word_pos == 21) { + index_str[2] = 's'; + index_str[3] = 't'; + } else if (word_pos == 2 || word_pos == 22) { + index_str[2] = 'n'; + index_str[3] = 'd'; + } else if (word_pos == 3 || word_pos == 23) { + index_str[2] = 'r'; + index_str[3] = 'd'; + } + + int left = 0; + oledClear(); + oledDrawBitmap(0, 0, &bmp_icon_info); + left = bmp_icon_info.width + 4; + + oledDrawString(left, 0 * 9, action, FONT_STANDARD); + oledDrawString(left, 2 * 9, word_pos < 10 ? index_str + 1 : index_str, + FONT_STANDARD); + oledDrawString(left, 3 * 9, word, FONT_STANDARD | FONT_DOUBLE); + oledHLine(OLED_HEIGHT - 13); + layoutButtonYes(btnYes); + oledRefresh(); } #define QR_MAX_VERSION 9 -void layoutAddress(const char *address, const char *desc, bool qrcode, bool ignorecase, const uint32_t *address_n, size_t address_n_count, bool address_is_account) -{ - if (layoutLast != layoutAddress) { - layoutSwipe(); - } else { - oledClear(); - } - layoutLast = layoutAddress; - - uint32_t addrlen = strlen(address); - if (qrcode) { - char address_upcase[addrlen + 1]; - if (ignorecase) { - for (uint32_t i = 0; i < addrlen + 1; i++) { - address_upcase[i] = address[i] >= 'a' && address[i] <= 'z' ? - address[i] + 'A' - 'a' : address[i]; - } - } - uint8_t codedata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)]; - uint8_t tempdata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)]; - - int side = 0; - if (qrcodegen_encodeText( - ignorecase ? address_upcase : address, - tempdata, - codedata, - qrcodegen_Ecc_LOW, - qrcodegen_VERSION_MIN, - QR_MAX_VERSION, - qrcodegen_Mask_AUTO, - true)) { - side = qrcodegen_getSize(codedata); - } - - oledInvert(0, 0, 63, 63); - if (side > 0 && side <= 29) { - int offset = 32 - side; - for (int i = 0; i < side; i++) { - for (int j = 0; j< side; j++) { - if (qrcodegen_getModule(codedata, i, j)) { - oledBox(offset + i * 2, offset + j * 2, - offset + 1 + i * 2, offset + 1 + j * 2, false); - } - } - } - } else if (side > 0 && side <= 60) { - int offset = 32 - (side / 2); - for (int i = 0; i < side; i++) { - for (int j = 0; j< side; j++) { - if (qrcodegen_getModule(codedata, i, j)) { - oledClearPixel(offset + i, offset + j); - } - } - } - } - } else { - if (desc) { - oledDrawString(0, 0 * 9, desc, FONT_STANDARD); - } - if (addrlen > 10) { // don't split short addresses - uint32_t rowlen = (addrlen - 1) / (addrlen <= 42 ? 2 : addrlen <= 63 ? 3 : 4) + 1; - const char **str = split_message((const uint8_t *)address, addrlen, rowlen); - for (int i = 0; i < 4; i++) { - oledDrawString(0, (i + 1) * 9 + 4, str[i], FONT_FIXED); - } - } else { - oledDrawString(0, (0 + 1) * 9 + 4, address, FONT_FIXED); - } - oledDrawString(0, 42, address_n_str(address_n, address_n_count, address_is_account), FONT_STANDARD); - } - - if (!qrcode) { - layoutButtonNo(_("QR Code")); - } - - layoutButtonYes(_("Continue")); - oledRefresh(); -} - -void layoutPublicKey(const uint8_t *pubkey) -{ - char desc[16]; - strlcpy(desc, "Public Key: 00", sizeof(desc)); - if (pubkey[0] == 1) { - /* ed25519 public key */ - // pass - leave 00 - } else { - data2hex(pubkey, 1, desc + 12); - } - const char **str = split_message_hex(pubkey + 1, 32 * 2); - layoutDialogSwipe(&bmp_icon_question, NULL, _("Continue"), NULL, - desc, str[0], str[1], str[2], str[3], NULL); -} - -void layoutSignIdentity(const IdentityType *identity, const char *challenge) -{ - char row_proto[8 + 11 + 1]; - char row_hostport[64 + 6 + 1]; - char row_user[64 + 8 + 1]; - - bool is_gpg = (strcmp(identity->proto, "gpg") == 0); - - if (identity->has_proto && identity->proto[0]) { - if (strcmp(identity->proto, "https") == 0) { - strlcpy(row_proto, _("Web sign in to:"), sizeof(row_proto)); - } else if (is_gpg) { - strlcpy(row_proto, _("GPG sign for:"), sizeof(row_proto)); - } else { - strlcpy(row_proto, identity->proto, sizeof(row_proto)); - char *p = row_proto; - while (*p) { *p = toupper((int)*p); p++; } - strlcat(row_proto, _(" login to:"), sizeof(row_proto)); - } - } else { - strlcpy(row_proto, _("Login to:"), sizeof(row_proto)); - } - - if (identity->has_host && identity->host[0]) { - strlcpy(row_hostport, identity->host, sizeof(row_hostport)); - if (identity->has_port && identity->port[0]) { - strlcat(row_hostport, ":", sizeof(row_hostport)); - strlcat(row_hostport, identity->port, sizeof(row_hostport)); - } - } else { - row_hostport[0] = 0; - } - - if (identity->has_user && identity->user[0]) { - strlcpy(row_user, _("user: "), sizeof(row_user)); - strlcat(row_user, identity->user, sizeof(row_user)); - } else { - row_user[0] = 0; - } - - if (is_gpg) { - // Split "First Last " into 2 lines: - // "First Last" - // "first@last.com" - char *email_start = strchr(row_hostport, '<'); - if (email_start) { - strlcpy(row_user, email_start + 1, sizeof(row_user)); - *email_start = 0; - char *email_end = strchr(row_user, '>'); - if (email_end) { - *email_end = 0; - } - } - } - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - _("Do you want to sign in?"), - row_proto[0] ? row_proto : NULL, - row_hostport[0] ? row_hostport : NULL, - row_user[0] ? row_user : NULL, - challenge, - NULL, - NULL); -} - -void layoutDecryptIdentity(const IdentityType *identity) -{ - char row_proto[8 + 11 + 1]; - char row_hostport[64 + 6 + 1]; - char row_user[64 + 8 + 1]; - - if (identity->has_proto && identity->proto[0]) { - strlcpy(row_proto, identity->proto, sizeof(row_proto)); - char *p = row_proto; - while (*p) { *p = toupper((int)*p); p++; } - strlcat(row_proto, _(" decrypt for:"), sizeof(row_proto)); - } else { - strlcpy(row_proto, _("Decrypt for:"), sizeof(row_proto)); - } - - if (identity->has_host && identity->host[0]) { - strlcpy(row_hostport, identity->host, sizeof(row_hostport)); - if (identity->has_port && identity->port[0]) { - strlcat(row_hostport, ":", sizeof(row_hostport)); - strlcat(row_hostport, identity->port, sizeof(row_hostport)); - } - } else { - row_hostport[0] = 0; - } - - if (identity->has_user && identity->user[0]) { - strlcpy(row_user, _("user: "), sizeof(row_user)); - strlcat(row_user, identity->user, sizeof(row_user)); - } else { - row_user[0] = 0; - } - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - _("Do you want to decrypt?"), - row_proto[0] ? row_proto : NULL, - row_hostport[0] ? row_hostport : NULL, - row_user[0] ? row_user : NULL, - NULL, - NULL, - NULL); +void layoutAddress(const char *address, const char *desc, bool qrcode, + bool ignorecase, const uint32_t *address_n, + size_t address_n_count, bool address_is_account) { + if (layoutLast != layoutAddress) { + layoutSwipe(); + } else { + oledClear(); + } + layoutLast = layoutAddress; + + uint32_t addrlen = strlen(address); + if (qrcode) { + char address_upcase[addrlen + 1]; + if (ignorecase) { + for (uint32_t i = 0; i < addrlen + 1; i++) { + address_upcase[i] = address[i] >= 'a' && address[i] <= 'z' + ? address[i] + 'A' - 'a' + : address[i]; + } + } + uint8_t codedata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)]; + uint8_t tempdata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)]; + + int side = 0; + if (qrcodegen_encodeText(ignorecase ? address_upcase : address, tempdata, + codedata, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, + QR_MAX_VERSION, qrcodegen_Mask_AUTO, true)) { + side = qrcodegen_getSize(codedata); + } + + oledInvert(0, 0, 63, 63); + if (side > 0 && side <= 29) { + int offset = 32 - side; + for (int i = 0; i < side; i++) { + for (int j = 0; j < side; j++) { + if (qrcodegen_getModule(codedata, i, j)) { + oledBox(offset + i * 2, offset + j * 2, offset + 1 + i * 2, + offset + 1 + j * 2, false); + } + } + } + } else if (side > 0 && side <= 60) { + int offset = 32 - (side / 2); + for (int i = 0; i < side; i++) { + for (int j = 0; j < side; j++) { + if (qrcodegen_getModule(codedata, i, j)) { + oledClearPixel(offset + i, offset + j); + } + } + } + } + } else { + if (desc) { + oledDrawString(0, 0 * 9, desc, FONT_STANDARD); + } + if (addrlen > 10) { // don't split short addresses + uint32_t rowlen = + (addrlen - 1) / (addrlen <= 42 ? 2 : addrlen <= 63 ? 3 : 4) + 1; + const char **str = + split_message((const uint8_t *)address, addrlen, rowlen); + for (int i = 0; i < 4; i++) { + oledDrawString(0, (i + 1) * 9 + 4, str[i], FONT_FIXED); + } + } else { + oledDrawString(0, (0 + 1) * 9 + 4, address, FONT_FIXED); + } + oledDrawString( + 0, 42, address_n_str(address_n, address_n_count, address_is_account), + FONT_STANDARD); + } + + if (!qrcode) { + layoutButtonNo(_("QR Code")); + } + + layoutButtonYes(_("Continue")); + oledRefresh(); +} + +void layoutPublicKey(const uint8_t *pubkey) { + char desc[16]; + strlcpy(desc, "Public Key: 00", sizeof(desc)); + if (pubkey[0] == 1) { + /* ed25519 public key */ + // pass - leave 00 + } else { + data2hex(pubkey, 1, desc + 12); + } + const char **str = split_message_hex(pubkey + 1, 32 * 2); + layoutDialogSwipe(&bmp_icon_question, NULL, _("Continue"), NULL, desc, str[0], + str[1], str[2], str[3], NULL); +} + +void layoutSignIdentity(const IdentityType *identity, const char *challenge) { + char row_proto[8 + 11 + 1]; + char row_hostport[64 + 6 + 1]; + char row_user[64 + 8 + 1]; + + bool is_gpg = (strcmp(identity->proto, "gpg") == 0); + + if (identity->has_proto && identity->proto[0]) { + if (strcmp(identity->proto, "https") == 0) { + strlcpy(row_proto, _("Web sign in to:"), sizeof(row_proto)); + } else if (is_gpg) { + strlcpy(row_proto, _("GPG sign for:"), sizeof(row_proto)); + } else { + strlcpy(row_proto, identity->proto, sizeof(row_proto)); + char *p = row_proto; + while (*p) { + *p = toupper((int)*p); + p++; + } + strlcat(row_proto, _(" login to:"), sizeof(row_proto)); + } + } else { + strlcpy(row_proto, _("Login to:"), sizeof(row_proto)); + } + + if (identity->has_host && identity->host[0]) { + strlcpy(row_hostport, identity->host, sizeof(row_hostport)); + if (identity->has_port && identity->port[0]) { + strlcat(row_hostport, ":", sizeof(row_hostport)); + strlcat(row_hostport, identity->port, sizeof(row_hostport)); + } + } else { + row_hostport[0] = 0; + } + + if (identity->has_user && identity->user[0]) { + strlcpy(row_user, _("user: "), sizeof(row_user)); + strlcat(row_user, identity->user, sizeof(row_user)); + } else { + row_user[0] = 0; + } + + if (is_gpg) { + // Split "First Last " into 2 lines: + // "First Last" + // "first@last.com" + char *email_start = strchr(row_hostport, '<'); + if (email_start) { + strlcpy(row_user, email_start + 1, sizeof(row_user)); + *email_start = 0; + char *email_end = strchr(row_user, '>'); + if (email_end) { + *email_end = 0; + } + } + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), + _("Do you want to sign in?"), + row_proto[0] ? row_proto : NULL, + row_hostport[0] ? row_hostport : NULL, + row_user[0] ? row_user : NULL, challenge, NULL, NULL); +} + +void layoutDecryptIdentity(const IdentityType *identity) { + char row_proto[8 + 11 + 1]; + char row_hostport[64 + 6 + 1]; + char row_user[64 + 8 + 1]; + + if (identity->has_proto && identity->proto[0]) { + strlcpy(row_proto, identity->proto, sizeof(row_proto)); + char *p = row_proto; + while (*p) { + *p = toupper((int)*p); + p++; + } + strlcat(row_proto, _(" decrypt for:"), sizeof(row_proto)); + } else { + strlcpy(row_proto, _("Decrypt for:"), sizeof(row_proto)); + } + + if (identity->has_host && identity->host[0]) { + strlcpy(row_hostport, identity->host, sizeof(row_hostport)); + if (identity->has_port && identity->port[0]) { + strlcat(row_hostport, ":", sizeof(row_hostport)); + strlcat(row_hostport, identity->port, sizeof(row_hostport)); + } + } else { + row_hostport[0] = 0; + } + + if (identity->has_user && identity->user[0]) { + strlcpy(row_user, _("user: "), sizeof(row_user)); + strlcat(row_user, identity->user, sizeof(row_user)); + } else { + row_user[0] = 0; + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), + _("Do you want to decrypt?"), + row_proto[0] ? row_proto : NULL, + row_hostport[0] ? row_hostport : NULL, + row_user[0] ? row_user : NULL, NULL, NULL, NULL); } void layoutU2FDialog(const char *verb, const char *appname) { - layoutDialog(&bmp_webauthn, NULL, verb, NULL, verb, _("U2F security key?"), NULL, appname, NULL, NULL); -} - -void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address) { - static char first_third[NEM_ADDRESS_SIZE / 3 + 1]; - strlcpy(first_third, address, sizeof(first_third)); - - static char second_third[NEM_ADDRESS_SIZE / 3 + 1]; - strlcpy(second_third, &address[NEM_ADDRESS_SIZE / 3], sizeof(second_third)); - - const char *third_third = &address[NEM_ADDRESS_SIZE * 2 / 3]; - - layoutDialogSwipe(icon, - btnNo, - btnYes, - desc, - line1, - first_third, - second_third, - third_third, - NULL, - NULL); -} - -void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *multiplier, uint64_t fee) { - char str_out[32], str_fee[32]; - - nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, quantity, multiplier, str_out, sizeof(str_out)); - nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee, NULL, str_fee, sizeof(str_fee)); - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - _("Confirm transfer of"), - str_out, - _("and network fee of"), - str_fee, - NULL, - NULL); -} - -void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, uint64_t fee1, const char *fee2_desc, uint64_t fee2) { - char str_fee1[32], str_fee2[32]; - - nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee1, NULL, str_fee1, sizeof(str_fee1)); - - if (fee2_desc) { - nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee2, NULL, str_fee2, sizeof(str_fee2)); - } - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - confirm ? _("Confirm") : _("Next"), - desc, - fee1_desc, - str_fee1, - fee2_desc, - fee2_desc ? str_fee2 : NULL, - NULL, - NULL); -} - -void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, uint8_t network) { - char str_out[32], str_levy[32]; - - nem_mosaicFormatAmount(definition, quantity, multiplier, str_out, sizeof(str_out)); - - if (definition->has_levy) { - nem_mosaicFormatLevy(definition, quantity, multiplier, network, str_levy, sizeof(str_levy)); - } - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - definition->has_name ? definition->name : _("Mosaic"), - _("Confirm transfer of"), - str_out, - definition->has_levy ? _("and levy of") : NULL, - definition->has_levy ? str_levy : NULL, - NULL, - NULL); -} - -void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *multiplier) { - char mosaic_name[32]; - nem_mosaicFormatName(namespace, mosaic, mosaic_name, sizeof(mosaic_name)); - - char str_out[32]; - nem_mosaicFormatAmount(NULL, quantity, multiplier, str_out, sizeof(str_out)); - - char *decimal = strchr(str_out, '.'); - if (decimal != NULL) { - *decimal = '\0'; - } - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("I take the risk"), - _("Unknown Mosaic"), - _("Confirm transfer of"), - str_out, - _("raw units of"), - mosaic_name, - NULL, - NULL); -} - -void layoutNEMTransferPayload(const uint8_t *payload, size_t length, bool encrypted) { - if (length >= 1 && payload[0] == 0xFE) { - char encoded[(length - 1) * 2 + 1]; - data2hex(&payload[1], length - 1, encoded); - - const char **str = split_message((uint8_t *) encoded, sizeof(encoded) - 1, 16); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), - encrypted ? _("Encrypted hex data") : _("Unencrypted hex data"), - str[0], str[1], str[2], str[3], NULL, NULL); - } else { - const char **str = split_message(payload, length, 16); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), - encrypted ? _("Encrypted message") : _("Unencrypted message"), - str[0], str[1], str[2], str[3], NULL, NULL); - } + layoutDialog(&bmp_webauthn, NULL, verb, NULL, verb, _("U2F security key?"), + NULL, appname, NULL, NULL); +} + +void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, + const char *desc, const char *line1, const char *address) { + static char first_third[NEM_ADDRESS_SIZE / 3 + 1]; + strlcpy(first_third, address, sizeof(first_third)); + + static char second_third[NEM_ADDRESS_SIZE / 3 + 1]; + strlcpy(second_third, &address[NEM_ADDRESS_SIZE / 3], sizeof(second_third)); + + const char *third_third = &address[NEM_ADDRESS_SIZE * 2 / 3]; + + layoutDialogSwipe(icon, btnNo, btnYes, desc, line1, first_third, second_third, + third_third, NULL, NULL); +} + +void layoutNEMTransferXEM(const char *desc, uint64_t quantity, + const bignum256 *multiplier, uint64_t fee) { + char str_out[32], str_fee[32]; + + nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, quantity, multiplier, + str_out, sizeof(str_out)); + nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee, NULL, str_fee, + sizeof(str_fee)); + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc, + _("Confirm transfer of"), str_out, _("and network fee of"), + str_fee, NULL, NULL); +} + +void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, + uint64_t fee1, const char *fee2_desc, uint64_t fee2) { + char str_fee1[32], str_fee2[32]; + + nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee1, NULL, str_fee1, + sizeof(str_fee1)); + + if (fee2_desc) { + nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee2, NULL, str_fee2, + sizeof(str_fee2)); + } + + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), confirm ? _("Confirm") : _("Next"), desc, + fee1_desc, str_fee1, fee2_desc, fee2_desc ? str_fee2 : NULL, NULL, NULL); +} + +void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition, + uint64_t quantity, const bignum256 *multiplier, + uint8_t network) { + char str_out[32], str_levy[32]; + + nem_mosaicFormatAmount(definition, quantity, multiplier, str_out, + sizeof(str_out)); + + if (definition->has_levy) { + nem_mosaicFormatLevy(definition, quantity, multiplier, network, str_levy, + sizeof(str_levy)); + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), + definition->has_name ? definition->name : _("Mosaic"), + _("Confirm transfer of"), str_out, + definition->has_levy ? _("and levy of") : NULL, + definition->has_levy ? str_levy : NULL, NULL, NULL); +} + +void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic, + uint64_t quantity, + const bignum256 *multiplier) { + char mosaic_name[32]; + nem_mosaicFormatName(namespace, mosaic, mosaic_name, sizeof(mosaic_name)); + + char str_out[32]; + nem_mosaicFormatAmount(NULL, quantity, multiplier, str_out, sizeof(str_out)); + + char *decimal = strchr(str_out, '.'); + if (decimal != NULL) { + *decimal = '\0'; + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), + _("Unknown Mosaic"), _("Confirm transfer of"), str_out, + _("raw units of"), mosaic_name, NULL, NULL); +} + +void layoutNEMTransferPayload(const uint8_t *payload, size_t length, + bool encrypted) { + if (length >= 1 && payload[0] == 0xFE) { + char encoded[(length - 1) * 2 + 1]; + data2hex(&payload[1], length - 1, encoded); + + const char **str = + split_message((uint8_t *)encoded, sizeof(encoded) - 1, 16); + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), + encrypted ? _("Encrypted hex data") : _("Unencrypted hex data"), str[0], + str[1], str[2], str[3], NULL, NULL); + } else { + const char **str = split_message(payload, length, 16); + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), + encrypted ? _("Encrypted message") : _("Unencrypted message"), str[0], + str[1], str[2], str[3], NULL, NULL); + } } void layoutNEMMosaicDescription(const char *description) { - const char **str = split_message((uint8_t *) description, strlen(description), 16); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), - _("Mosaic Description"), - str[0], str[1], str[2], str[3], NULL, NULL); + const char **str = + split_message((uint8_t *)description, strlen(description), 16); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), + _("Mosaic Description"), str[0], str[1], str[2], str[3], + NULL, NULL); } void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network) { - const NEMMosaicDefinition *mosaic; - if (nem_mosaicMatches(definition, definition->levy_namespace, definition->levy_mosaic, network)) { - mosaic = definition; - } else { - mosaic = nem_mosaicByName(definition->levy_namespace, definition->levy_mosaic, network); - } - - char mosaic_name[32]; - if (mosaic == NULL) { - nem_mosaicFormatName(definition->levy_namespace, definition->levy_mosaic, mosaic_name, sizeof(mosaic_name)); - } - - char str_out[32]; - - switch (definition->levy) { - case NEMMosaicLevy_MosaicLevy_Percentile: - bn_format_uint64(definition->fee, NULL, NULL, 0, 0, false, str_out, sizeof(str_out)); - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - _("Percentile Levy"), - _("Raw levy value is"), - str_out, - _("in"), - mosaic ? (mosaic == definition ? _("the same mosaic") : mosaic->name) : mosaic_name, - NULL, - NULL); - break; - - case NEMMosaicLevy_MosaicLevy_Absolute: - default: - nem_mosaicFormatAmount(mosaic, definition->fee, NULL, str_out, sizeof(str_out)); - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - _("Absolute Levy"), - _("Levy is"), - str_out, - mosaic ? (mosaic == definition ? _("in the same mosaic") : NULL) : _("in raw units of"), - mosaic ? NULL : mosaic_name, - NULL, - NULL); - break; - } -} - -static inline bool is_slip18(const uint32_t *address_n, size_t address_n_count) -{ - return address_n_count == 2 && address_n[0] == (0x80000000 + 10018) && (address_n[1] & 0x80000000) && (address_n[1] & 0x7FFFFFFF) <= 9; -} - -void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count, const uint8_t *data, uint32_t len, bool final_sign) -{ - char *desc = final_sign ? _("CoSi sign message?") : _("CoSi commit message?"); - char desc_buf[32]; - if (is_slip18(address_n, address_n_count)) { - if (final_sign) { - strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf)); - desc_buf[16] = '0' + (address_n[1] & 0x7FFFFFFF); - } else { - strlcpy(desc_buf, _("CoSi commit index #?"), sizeof(desc_buf)); - desc_buf[18] = '0' + (address_n[1] & 0x7FFFFFFF); - } - desc = desc_buf; - } - char str[4][17]; - if (len == 32) { - data2hex(data , 8, str[0]); - data2hex(data + 8, 8, str[1]); - data2hex(data + 16, 8, str[2]); - data2hex(data + 24, 8, str[3]); - } else { - strlcpy(str[0], "Data", sizeof(str[0])); - strlcpy(str[1], "of", sizeof(str[1])); - strlcpy(str[2], "unsupported", sizeof(str[2])); - strlcpy(str[3], "length", sizeof(str[3])); - } - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), desc, - str[0], str[1], str[2], str[3], NULL, NULL); + const NEMMosaicDefinition *mosaic; + if (nem_mosaicMatches(definition, definition->levy_namespace, + definition->levy_mosaic, network)) { + mosaic = definition; + } else { + mosaic = nem_mosaicByName(definition->levy_namespace, + definition->levy_mosaic, network); + } + + char mosaic_name[32]; + if (mosaic == NULL) { + nem_mosaicFormatName(definition->levy_namespace, definition->levy_mosaic, + mosaic_name, sizeof(mosaic_name)); + } + + char str_out[32]; + + switch (definition->levy) { + case NEMMosaicLevy_MosaicLevy_Percentile: + bn_format_uint64(definition->fee, NULL, NULL, 0, 0, false, str_out, + sizeof(str_out)); + + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), _("Percentile Levy"), + _("Raw levy value is"), str_out, _("in"), + mosaic ? (mosaic == definition ? _("the same mosaic") : mosaic->name) + : mosaic_name, + NULL, NULL); + break; + + case NEMMosaicLevy_MosaicLevy_Absolute: + default: + nem_mosaicFormatAmount(mosaic, definition->fee, NULL, str_out, + sizeof(str_out)); + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), _("Absolute Levy"), + _("Levy is"), str_out, + mosaic ? (mosaic == definition ? _("in the same mosaic") : NULL) + : _("in raw units of"), + mosaic ? NULL : mosaic_name, NULL, NULL); + break; + } +} + +static inline bool is_slip18(const uint32_t *address_n, + size_t address_n_count) { + return address_n_count == 2 && address_n[0] == (0x80000000 + 10018) && + (address_n[1] & 0x80000000) && (address_n[1] & 0x7FFFFFFF) <= 9; +} + +void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count, + const uint8_t *data, uint32_t len, bool final_sign) { + char *desc = final_sign ? _("CoSi sign message?") : _("CoSi commit message?"); + char desc_buf[32]; + if (is_slip18(address_n, address_n_count)) { + if (final_sign) { + strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf)); + desc_buf[16] = '0' + (address_n[1] & 0x7FFFFFFF); + } else { + strlcpy(desc_buf, _("CoSi commit index #?"), sizeof(desc_buf)); + desc_buf[18] = '0' + (address_n[1] & 0x7FFFFFFF); + } + desc = desc_buf; + } + char str[4][17]; + if (len == 32) { + data2hex(data, 8, str[0]); + data2hex(data + 8, 8, str[1]); + data2hex(data + 16, 8, str[2]); + data2hex(data + 24, 8, str[3]); + } else { + strlcpy(str[0], "Data", sizeof(str[0])); + strlcpy(str[1], "of", sizeof(str[1])); + strlcpy(str[2], "unsupported", sizeof(str[2])); + strlcpy(str[3], "length", sizeof(str[3])); + } + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), desc, str[0], + str[1], str[2], str[3], NULL, NULL); } diff --git a/firmware/layout2.h b/firmware/layout2.h index f3935f409..54b32baee 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -20,10 +20,10 @@ #ifndef __LAYOUT2_H__ #define __LAYOUT2_H__ -#include "layout.h" -#include "coins.h" -#include "bitmaps.h" #include "bignum.h" +#include "bitmaps.h" +#include "coins.h" +#include "layout.h" #include "trezor.h" #include "messages-bitcoin.pb.h" @@ -38,7 +38,10 @@ extern void *layoutLast; #define layoutSwipe oledSwipeLeft #endif -void layoutDialogSwipe(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); +void layoutDialogSwipe(const BITMAP *icon, const char *btnNo, + const char *btnYes, const char *desc, const char *line1, + const char *line2, const char *line3, const char *line4, + const char *line5, const char *line6); void layoutProgressSwipe(const char *desc, int permil); void layoutScreensaver(void); @@ -46,31 +49,44 @@ void layoutHome(void); void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out); void layoutConfirmOmni(const uint8_t *data, uint32_t size); void layoutConfirmOpReturn(const uint8_t *data, uint32_t size); -void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out, uint64_t amount_fee); +void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out, + uint64_t amount_fee); void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee); void layoutSignMessage(const uint8_t *msg, uint32_t len); void layoutVerifyAddress(const CoinInfo *coin, const char *address); void layoutVerifyMessage(const uint8_t *msg, uint32_t len); void layoutCipherKeyValue(bool encrypt, const char *key); void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing); -void layoutDecryptMessage(const uint8_t *msg, uint32_t len, const char *address); +void layoutDecryptMessage(const uint8_t *msg, uint32_t len, + const char *address); void layoutResetWord(const char *word, int pass, int word_pos, bool last); -void layoutAddress(const char *address, const char *desc, bool qrcode, bool ignorecase, const uint32_t *address_n, size_t address_n_count, bool address_is_account); +void layoutAddress(const char *address, const char *desc, bool qrcode, + bool ignorecase, const uint32_t *address_n, + size_t address_n_count, bool address_is_account); void layoutPublicKey(const uint8_t *pubkey); void layoutSignIdentity(const IdentityType *identity, const char *challenge); void layoutDecryptIdentity(const IdentityType *identity); void layoutU2FDialog(const char *verb, const char *appname); -void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *address); -void layoutNEMTransferXEM(const char *desc, uint64_t quantity, const bignum256 *multiplier, uint64_t fee); -void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, uint64_t fee1, const char *fee2_desc, uint64_t fee2); -void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, uint8_t network); -void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic, uint64_t quantity, const bignum256 *multiplier); -void layoutNEMTransferPayload(const uint8_t *payload, size_t length, bool encrypted); +void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, + const char *desc, const char *line1, const char *address); +void layoutNEMTransferXEM(const char *desc, uint64_t quantity, + const bignum256 *multiplier, uint64_t fee); +void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc, + uint64_t fee1, const char *fee2_desc, uint64_t fee2); +void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition, + uint64_t quantity, const bignum256 *multiplier, + uint8_t network); +void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic, + uint64_t quantity, + const bignum256 *multiplier); +void layoutNEMTransferPayload(const uint8_t *payload, size_t length, + bool encrypted); void layoutNEMMosaicDescription(const char *description); void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network); -void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count, const uint8_t *data, uint32_t len, bool final_sign); +void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count, + const uint8_t *data, uint32_t len, bool final_sign); const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen); const char **split_message_hex(const uint8_t *msg, uint32_t len); diff --git a/firmware/lisk.c b/firmware/lisk.c index efc872110..054b35266 100644 --- a/firmware/lisk.c +++ b/firmware/lisk.c @@ -18,353 +18,337 @@ */ #include "lisk.h" -#include "fsm.h" -#include "curves.h" -#include "layout2.h" #include "bitmaps.h" -#include "util.h" -#include "gettext.h" #include "crypto.h" -#include "protect.h" +#include "curves.h" +#include "fsm.h" +#include "gettext.h" +#include "layout2.h" #include "messages.pb.h" +#include "protect.h" +#include "util.h" -void lisk_get_address_from_public_key(const uint8_t *public_key, char *address) { - uint64_t digest[4]; - sha256_Raw(public_key, 32, (uint8_t *)digest); - bn_format_uint64(digest[0], NULL, "L", 0, 0, false, address, MAX_LISK_ADDRESS_SIZE); +void lisk_get_address_from_public_key(const uint8_t *public_key, + char *address) { + uint64_t digest[4]; + sha256_Raw(public_key, 32, (uint8_t *)digest); + bn_format_uint64(digest[0], NULL, "L", 0, 0, false, address, + MAX_LISK_ADDRESS_SIZE); } -void lisk_message_hash(const uint8_t *message, size_t message_len, uint8_t hash[32]) { - SHA256_CTX ctx; - sha256_Init(&ctx); - sha256_Update(&ctx, (const uint8_t *)"\x15" "Lisk Signed Message:\n", 22); - uint8_t varint[5]; - uint32_t l = ser_length(message_len, varint); - sha256_Update(&ctx, varint, l); - sha256_Update(&ctx, message, message_len); - sha256_Final(&ctx, hash); - sha256_Raw(hash, 32, hash); +void lisk_message_hash(const uint8_t *message, size_t message_len, + uint8_t hash[32]) { + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, (const uint8_t *)"\x15" "Lisk Signed Message:\n", 22); + uint8_t varint[5]; + uint32_t l = ser_length(message_len, varint); + sha256_Update(&ctx, varint, l); + sha256_Update(&ctx, message, message_len); + sha256_Final(&ctx, hash); + sha256_Raw(hash, 32, hash); } -void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg, LiskMessageSignature *resp) -{ - layoutSignMessage(msg->message.bytes, msg->message.size); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } +void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg, + LiskMessageSignature *resp) { + layoutSignMessage(msg->message.bytes, msg->message.size); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } - layoutProgressSwipe(_("Signing"), 0); + layoutProgressSwipe(_("Signing"), 0); - uint8_t signature[64]; - uint8_t hash[32]; - lisk_message_hash(msg->message.bytes, msg->message.size, hash); + uint8_t signature[64]; + uint8_t hash[32]; + lisk_message_hash(msg->message.bytes, msg->message.size, hash); - ed25519_sign(hash, 32, node->private_key, &node->public_key[1], signature); + ed25519_sign(hash, 32, node->private_key, &node->public_key[1], signature); - memcpy(resp->signature.bytes, signature, sizeof(signature)); - memcpy(resp->public_key.bytes, &node->public_key[1], 32); + memcpy(resp->signature.bytes, signature, sizeof(signature)); + memcpy(resp->public_key.bytes, &node->public_key[1], 32); - resp->has_signature = true; - resp->signature.size = 64; - resp->has_public_key = true; - resp->public_key.size = 32; + resp->has_signature = true; + resp->signature.size = 64; + resp->has_public_key = true; + resp->public_key.size = 32; } -bool lisk_verify_message(const LiskVerifyMessage *msg) -{ - uint8_t hash[32]; - lisk_message_hash(msg->message.bytes, msg->message.size, hash); - return 0 == ed25519_sign_open(hash, 32, msg->public_key.bytes, msg->signature.bytes ); +bool lisk_verify_message(const LiskVerifyMessage *msg) { + uint8_t hash[32]; + lisk_message_hash(msg->message.bytes, msg->message.size, hash); + return 0 == ed25519_sign_open(hash, 32, msg->public_key.bytes, + msg->signature.bytes); } -static void lisk_update_raw_tx(const HDNode *node, LiskSignTx *msg) -{ - if(!msg->transaction.has_sender_public_key) { - memcpy(msg->transaction.sender_public_key.bytes, &node->public_key[1], 32); - } - - // For CastVotes transactions, recipientId should be equal to transaction creator address. - if(msg->transaction.type == LiskTransactionType_CastVotes && !msg->transaction.has_recipient_id) { - msg->transaction.has_recipient_id = true; - lisk_get_address_from_public_key(&node->public_key[1], msg->transaction.recipient_id); - } +static void lisk_update_raw_tx(const HDNode *node, LiskSignTx *msg) { + if (!msg->transaction.has_sender_public_key) { + memcpy(msg->transaction.sender_public_key.bytes, &node->public_key[1], 32); + } + + // For CastVotes transactions, recipientId should be equal to transaction + // creator address. + if (msg->transaction.type == LiskTransactionType_CastVotes && + !msg->transaction.has_recipient_id) { + msg->transaction.has_recipient_id = true; + lisk_get_address_from_public_key(&node->public_key[1], + msg->transaction.recipient_id); + } } -static void lisk_hashupdate_uint32(SHA256_CTX *ctx, uint32_t value) -{ - uint8_t data[4]; - write_le(data, value); - sha256_Update(ctx, data, sizeof(data)); +static void lisk_hashupdate_uint32(SHA256_CTX *ctx, uint32_t value) { + uint8_t data[4]; + write_le(data, value); + sha256_Update(ctx, data, sizeof(data)); } -static void lisk_hashupdate_uint64_le(SHA256_CTX *ctx, uint64_t value) -{ - sha256_Update(ctx, (uint8_t *)&value, sizeof(uint64_t)); +static void lisk_hashupdate_uint64_le(SHA256_CTX *ctx, uint64_t value) { + sha256_Update(ctx, (uint8_t *)&value, sizeof(uint64_t)); } -static void lisk_hashupdate_uint64_be(SHA256_CTX *ctx, uint64_t value) -{ - uint8_t data[8]; - data[0] = value >> 56; - data[1] = value >> 48; - data[2] = value >> 40; - data[3] = value >> 32; - data[4] = value >> 24; - data[5] = value >> 16; - data[6] = value >> 8; - data[7] = value; - sha256_Update(ctx, data, sizeof(data)); +static void lisk_hashupdate_uint64_be(SHA256_CTX *ctx, uint64_t value) { + uint8_t data[8]; + data[0] = value >> 56; + data[1] = value >> 48; + data[2] = value >> 40; + data[3] = value >> 32; + data[4] = value >> 24; + data[5] = value >> 16; + data[6] = value >> 8; + data[7] = value; + sha256_Update(ctx, data, sizeof(data)); } -static void lisk_hashupdate_asset(SHA256_CTX *ctx, LiskTransactionType type, LiskTransactionAsset *asset) -{ - switch (type) { - case LiskTransactionType_Transfer: - if (asset->has_data) { - sha256_Update(ctx, (const uint8_t *)asset->data, strlen(asset->data)); - } - break; - case LiskTransactionType_RegisterDelegate: - if (asset->has_delegate && asset->delegate.has_username) { - sha256_Update(ctx, (const uint8_t *)asset->delegate.username, strlen(asset->delegate.username)); - } - break; - case LiskTransactionType_CastVotes: { - for (int i = 0; i < asset->votes_count; i++) { - sha256_Update(ctx, (uint8_t *)asset->votes[i], strlen(asset->votes[i])); - } - break; - } - case LiskTransactionType_RegisterSecondPassphrase: - if (asset->has_signature && asset->signature.has_public_key) { - sha256_Update(ctx, asset->signature.public_key.bytes, asset->signature.public_key.size); - } - break; - case LiskTransactionType_RegisterMultisignatureAccount: - if (asset->has_multisignature) { - sha256_Update(ctx, (uint8_t *)&(asset->multisignature.min), 1); - sha256_Update(ctx, (uint8_t *)&(asset->multisignature.life_time), 1); - for (int i = 0; i < asset->multisignature.keys_group_count; i++) { - sha256_Update(ctx, (uint8_t *)asset->multisignature.keys_group[i], strlen(asset->multisignature.keys_group[i])); - }; - } - break; - default: - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid transaction type")); - break; - } +static void lisk_hashupdate_asset(SHA256_CTX *ctx, LiskTransactionType type, + LiskTransactionAsset *asset) { + switch (type) { + case LiskTransactionType_Transfer: + if (asset->has_data) { + sha256_Update(ctx, (const uint8_t *)asset->data, strlen(asset->data)); + } + break; + case LiskTransactionType_RegisterDelegate: + if (asset->has_delegate && asset->delegate.has_username) { + sha256_Update(ctx, (const uint8_t *)asset->delegate.username, + strlen(asset->delegate.username)); + } + break; + case LiskTransactionType_CastVotes: { + for (int i = 0; i < asset->votes_count; i++) { + sha256_Update(ctx, (uint8_t *)asset->votes[i], strlen(asset->votes[i])); + } + break; + } + case LiskTransactionType_RegisterSecondPassphrase: + if (asset->has_signature && asset->signature.has_public_key) { + sha256_Update(ctx, asset->signature.public_key.bytes, + asset->signature.public_key.size); + } + break; + case LiskTransactionType_RegisterMultisignatureAccount: + if (asset->has_multisignature) { + sha256_Update(ctx, (uint8_t *)&(asset->multisignature.min), 1); + sha256_Update(ctx, (uint8_t *)&(asset->multisignature.life_time), 1); + for (int i = 0; i < asset->multisignature.keys_group_count; i++) { + sha256_Update(ctx, (uint8_t *)asset->multisignature.keys_group[i], + strlen(asset->multisignature.keys_group[i])); + }; + } + break; + default: + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid transaction type")); + break; + } } #define MAX_LISK_VALUE_SIZE 20 -static void lisk_format_value(uint64_t value, char *formated_value) -{ - bn_format_uint64(value, NULL, " LSK", 8, 0, false, formated_value, MAX_LISK_VALUE_SIZE); +static void lisk_format_value(uint64_t value, char *formated_value) { + bn_format_uint64(value, NULL, " LSK", 8, 0, false, formated_value, + MAX_LISK_VALUE_SIZE); } -void lisk_sign_tx(const HDNode *node, LiskSignTx *msg, LiskSignedTx *resp) -{ - lisk_update_raw_tx(node, msg); - - if(msg->has_transaction) { - SHA256_CTX ctx; - sha256_Init(&ctx); - - switch (msg->transaction.type) { - case LiskTransactionType_Transfer: - layoutRequireConfirmTx(msg->transaction.recipient_id, msg->transaction.amount); - break; - case LiskTransactionType_RegisterDelegate: - layoutRequireConfirmDelegateRegistration(&msg->transaction.asset); - break; - case LiskTransactionType_CastVotes: - layoutRequireConfirmCastVotes(&msg->transaction.asset); - break; - case LiskTransactionType_RegisterSecondPassphrase: - layoutLiskPublicKey(msg->transaction.asset.signature.public_key.bytes); - break; - case LiskTransactionType_RegisterMultisignatureAccount: - layoutRequireConfirmMultisig(&msg->transaction.asset); - break; - default: - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid transaction type")); - layoutHome(); - break; - } - if (!protectButton(( - msg->transaction.type == LiskTransactionType_RegisterSecondPassphrase ? - ButtonRequestType_ButtonRequest_PublicKey : - ButtonRequestType_ButtonRequest_SignTx), - false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); - layoutHome(); - return; - } - - layoutRequireConfirmFee(msg->transaction.fee, msg->transaction.amount); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); - layoutHome(); - return; - } - layoutProgressSwipe(_("Signing transaction"), 0); - - sha256_Update(&ctx, (const uint8_t *)&msg->transaction.type, 1); - - lisk_hashupdate_uint32(&ctx, msg->transaction.timestamp); - - sha256_Update(&ctx, msg->transaction.sender_public_key.bytes, 32); - - if (msg->transaction.has_requester_public_key) { - sha256_Update(&ctx, msg->transaction.requester_public_key.bytes, msg->transaction.requester_public_key.size); - } - - uint64_t recipient_id = 0; - if (msg->transaction.has_recipient_id && msg->transaction.recipient_id[0] != 0) { - // parse integer from lisk address ("123L" -> 123) - for (size_t i = 0; i < strlen(msg->transaction.recipient_id) - 1; i++) { - if (msg->transaction.recipient_id[i] < '0' || msg->transaction.recipient_id[i] > '9') { - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid recipient_id")); - layoutHome(); - return; - } - recipient_id *= 10; - recipient_id += (msg->transaction.recipient_id[i] - '0'); - } - } - lisk_hashupdate_uint64_be(&ctx, recipient_id); - lisk_hashupdate_uint64_le(&ctx, msg->transaction.amount); - - lisk_hashupdate_asset(&ctx, msg->transaction.type, &msg->transaction.asset); - - // if signature exist calculate second signature - if (msg->transaction.has_signature) { - sha256_Update(&ctx, msg->transaction.signature.bytes, msg->transaction.signature.size); - } - - uint8_t hash[32]; - sha256_Final(&ctx, hash); - ed25519_sign(hash, 32, node->private_key, &node->public_key[1], resp->signature.bytes); - - resp->has_signature = true; - resp->signature.size = 64; - } +void lisk_sign_tx(const HDNode *node, LiskSignTx *msg, LiskSignedTx *resp) { + lisk_update_raw_tx(node, msg); + + if (msg->has_transaction) { + SHA256_CTX ctx; + sha256_Init(&ctx); + + switch (msg->transaction.type) { + case LiskTransactionType_Transfer: + layoutRequireConfirmTx(msg->transaction.recipient_id, + msg->transaction.amount); + break; + case LiskTransactionType_RegisterDelegate: + layoutRequireConfirmDelegateRegistration(&msg->transaction.asset); + break; + case LiskTransactionType_CastVotes: + layoutRequireConfirmCastVotes(&msg->transaction.asset); + break; + case LiskTransactionType_RegisterSecondPassphrase: + layoutLiskPublicKey(msg->transaction.asset.signature.public_key.bytes); + break; + case LiskTransactionType_RegisterMultisignatureAccount: + layoutRequireConfirmMultisig(&msg->transaction.asset); + break; + default: + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid transaction type")); + layoutHome(); + break; + } + if (!protectButton((msg->transaction.type == + LiskTransactionType_RegisterSecondPassphrase + ? ButtonRequestType_ButtonRequest_PublicKey + : ButtonRequestType_ButtonRequest_SignTx), + false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); + layoutHome(); + return; + } + + layoutRequireConfirmFee(msg->transaction.fee, msg->transaction.amount); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled"); + layoutHome(); + return; + } + layoutProgressSwipe(_("Signing transaction"), 0); + + sha256_Update(&ctx, (const uint8_t *)&msg->transaction.type, 1); + + lisk_hashupdate_uint32(&ctx, msg->transaction.timestamp); + + sha256_Update(&ctx, msg->transaction.sender_public_key.bytes, 32); + + if (msg->transaction.has_requester_public_key) { + sha256_Update(&ctx, msg->transaction.requester_public_key.bytes, + msg->transaction.requester_public_key.size); + } + + uint64_t recipient_id = 0; + if (msg->transaction.has_recipient_id && + msg->transaction.recipient_id[0] != 0) { + // parse integer from lisk address ("123L" -> 123) + for (size_t i = 0; i < strlen(msg->transaction.recipient_id) - 1; i++) { + if (msg->transaction.recipient_id[i] < '0' || + msg->transaction.recipient_id[i] > '9') { + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid recipient_id")); + layoutHome(); + return; + } + recipient_id *= 10; + recipient_id += (msg->transaction.recipient_id[i] - '0'); + } + } + lisk_hashupdate_uint64_be(&ctx, recipient_id); + lisk_hashupdate_uint64_le(&ctx, msg->transaction.amount); + + lisk_hashupdate_asset(&ctx, msg->transaction.type, &msg->transaction.asset); + + // if signature exist calculate second signature + if (msg->transaction.has_signature) { + sha256_Update(&ctx, msg->transaction.signature.bytes, + msg->transaction.signature.size); + } + + uint8_t hash[32]; + sha256_Final(&ctx, hash); + ed25519_sign(hash, 32, node->private_key, &node->public_key[1], + resp->signature.bytes); + + resp->has_signature = true; + resp->signature.size = 64; + } } // Layouts -void layoutLiskPublicKey(const uint8_t *pubkey) -{ - const char **str = split_message_hex(pubkey, 32); - layoutDialogSwipe(&bmp_icon_question, NULL, _("Continue"), NULL, - _("Public Key:"), str[0], str[1], str[2], str[3], NULL); +void layoutLiskPublicKey(const uint8_t *pubkey) { + const char **str = split_message_hex(pubkey, 32); + layoutDialogSwipe(&bmp_icon_question, NULL, _("Continue"), NULL, + _("Public Key:"), str[0], str[1], str[2], str[3], NULL); } -void layoutLiskVerifyAddress(const char *address) -{ - const char **str = split_message((const uint8_t *)address, strlen(address), 10); - layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"), - _("Confirm address?"), - _("Message signed by:"), - str[0], str[1], NULL, NULL, NULL); +void layoutLiskVerifyAddress(const char *address) { + const char **str = + split_message((const uint8_t *)address, strlen(address), 10); + layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"), + _("Confirm address?"), _("Message signed by:"), str[0], + str[1], NULL, NULL, NULL); } -void layoutRequireConfirmTx(char *recipient_id, uint64_t amount) -{ - char formated_amount[MAX_LISK_VALUE_SIZE]; - const char **str = split_message((const uint8_t *)recipient_id, strlen(recipient_id), 16); - lisk_format_value(amount, formated_amount); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - NULL, - _("Confirm sending"), - formated_amount, - _("to:"), - str[0], - str[1], - NULL - ); +void layoutRequireConfirmTx(char *recipient_id, uint64_t amount) { + char formated_amount[MAX_LISK_VALUE_SIZE]; + const char **str = + split_message((const uint8_t *)recipient_id, strlen(recipient_id), 16); + lisk_format_value(amount, formated_amount); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm sending"), formated_amount, _("to:"), str[0], + str[1], NULL); } -void layoutRequireConfirmFee(uint64_t fee, uint64_t amount) -{ - char formated_amount[MAX_LISK_VALUE_SIZE]; - char formated_fee[MAX_LISK_VALUE_SIZE]; - lisk_format_value(amount, formated_amount); - lisk_format_value(fee, formated_fee); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - NULL, - _("Confirm transaction"), - formated_amount, - _("fee:"), - formated_fee, - NULL, - NULL - ); +void layoutRequireConfirmFee(uint64_t fee, uint64_t amount) { + char formated_amount[MAX_LISK_VALUE_SIZE]; + char formated_fee[MAX_LISK_VALUE_SIZE]; + lisk_format_value(amount, formated_amount); + lisk_format_value(fee, formated_fee); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm transaction"), formated_amount, _("fee:"), + formated_fee, NULL, NULL); } -void layoutRequireConfirmDelegateRegistration(LiskTransactionAsset *asset) -{ - if (asset->has_delegate && asset->delegate.has_username) { - const char **str = split_message((const uint8_t *)asset->delegate.username, strlen(asset->delegate.username), 20); - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - NULL, - _("Confirm transaction"), - _("Do you really want to"), - _("register a delegate?"), - str[0], - str[1], - NULL - ); - } +void layoutRequireConfirmDelegateRegistration(LiskTransactionAsset *asset) { + if (asset->has_delegate && asset->delegate.has_username) { + const char **str = split_message((const uint8_t *)asset->delegate.username, + strlen(asset->delegate.username), 20); + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm transaction"), _("Do you really want to"), + _("register a delegate?"), str[0], str[1], NULL); + } } -void layoutRequireConfirmCastVotes(LiskTransactionAsset *asset) -{ - uint8_t plus = 0; - uint8_t minus = 0; - char add_votes_txt[13]; - char remove_votes_txt[16]; - - for (int i = 0; i < asset->votes_count; i++) { - if (asset->votes[i][0] == '+') { - plus += 1; - } else { - minus += 1; - } - } - - bn_format_uint64(plus, "Add ", NULL, 0, 0, false, add_votes_txt, sizeof(add_votes_txt)); - bn_format_uint64(minus, "Remove ", NULL, 0, 0, false, remove_votes_txt, sizeof(remove_votes_txt)); - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - NULL, - _("Confirm transaction"), - add_votes_txt, - remove_votes_txt, - NULL, - NULL, - NULL - ); +void layoutRequireConfirmCastVotes(LiskTransactionAsset *asset) { + uint8_t plus = 0; + uint8_t minus = 0; + char add_votes_txt[13]; + char remove_votes_txt[16]; + + for (int i = 0; i < asset->votes_count; i++) { + if (asset->votes[i][0] == '+') { + plus += 1; + } else { + minus += 1; + } + } + + bn_format_uint64(plus, "Add ", NULL, 0, 0, false, add_votes_txt, + sizeof(add_votes_txt)); + bn_format_uint64(minus, "Remove ", NULL, 0, 0, false, remove_votes_txt, + sizeof(remove_votes_txt)); + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm transaction"), add_votes_txt, remove_votes_txt, + NULL, NULL, NULL); } -void layoutRequireConfirmMultisig(LiskTransactionAsset *asset) -{ - char keys_group_str[25]; - char life_time_str[14]; - char min_str[8]; - - bn_format_uint64(asset->multisignature.keys_group_count, "Keys group length: ", NULL, 0, 0, false, keys_group_str, sizeof(keys_group_str)); - bn_format_uint64(asset->multisignature.life_time, "Life time: ", NULL, 0, 0, false, life_time_str, sizeof(life_time_str)); - bn_format_uint64(asset->multisignature.min, "Min: ", NULL, 0, 0, false, min_str, sizeof(min_str)); - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), - NULL, - _("Confirm transaction"), - keys_group_str, - life_time_str, - min_str, - NULL, - NULL - ); +void layoutRequireConfirmMultisig(LiskTransactionAsset *asset) { + char keys_group_str[25]; + char life_time_str[14]; + char min_str[8]; + + bn_format_uint64(asset->multisignature.keys_group_count, + "Keys group length: ", NULL, 0, 0, false, keys_group_str, + sizeof(keys_group_str)); + bn_format_uint64(asset->multisignature.life_time, "Life time: ", NULL, 0, 0, + false, life_time_str, sizeof(life_time_str)); + bn_format_uint64(asset->multisignature.min, "Min: ", NULL, 0, 0, false, + min_str, sizeof(min_str)); + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Confirm transaction"), keys_group_str, life_time_str, + min_str, NULL, NULL); } \ No newline at end of file diff --git a/firmware/lisk.h b/firmware/lisk.h index bac931eff..bf0456b58 100644 --- a/firmware/lisk.h +++ b/firmware/lisk.h @@ -26,7 +26,8 @@ #define MAX_LISK_ADDRESS_SIZE 23 -void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg, LiskMessageSignature *resp); +void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg, + LiskMessageSignature *resp); bool lisk_verify_message(const LiskVerifyMessage *msg); void lisk_sign_tx(const HDNode *node, LiskSignTx *msg, LiskSignedTx *resp); diff --git a/firmware/messages.c b/firmware/messages.c index d944ccbe4..f04b07291 100644 --- a/firmware/messages.c +++ b/firmware/messages.c @@ -19,56 +19,53 @@ #include -#include "trezor.h" -#include "messages.h" #include "debug.h" #include "fsm.h" -#include "util.h" #include "gettext.h" #include "memzero.h" +#include "messages.h" +#include "trezor.h" +#include "util.h" +#include "messages.pb.h" #include "pb_decode.h" #include "pb_encode.h" -#include "messages.pb.h" struct MessagesMap_t { - char type; // n = normal, d = debug - char dir; // i = in, o = out - uint16_t msg_id; - const pb_field_t *fields; - void (*process_func)(const void *ptr); + char type; // n = normal, d = debug + char dir; // i = in, o = out + uint16_t msg_id; + const pb_field_t *fields; + void (*process_func)(const void *ptr); }; static const struct MessagesMap_t MessagesMap[] = { #include "messages_map.h" - // end - {0, 0, 0, 0, 0} -}; + // end + {0, 0, 0, 0, 0}}; #include "messages_map_limits.h" -const pb_field_t *MessageFields(char type, char dir, uint16_t msg_id) -{ - const struct MessagesMap_t *m = MessagesMap; - while (m->type) { - if (type == m->type && dir == m->dir && msg_id == m->msg_id) { - return m->fields; - } - m++; - } - return 0; +const pb_field_t *MessageFields(char type, char dir, uint16_t msg_id) { + const struct MessagesMap_t *m = MessagesMap; + while (m->type) { + if (type == m->type && dir == m->dir && msg_id == m->msg_id) { + return m->fields; + } + m++; + } + return 0; } -void MessageProcessFunc(char type, char dir, uint16_t msg_id, void *ptr) -{ - const struct MessagesMap_t *m = MessagesMap; - while (m->type) { - if (type == m->type && dir == m->dir && msg_id == m->msg_id) { - m->process_func(ptr); - return; - } - m++; - } +void MessageProcessFunc(char type, char dir, uint16_t msg_id, void *ptr) { + const struct MessagesMap_t *m = MessagesMap; + while (m->type) { + if (type == m->type && dir == m->dir && msg_id == m->msg_id) { + m->process_func(ptr); + return; + } + m++; + } } static uint32_t msg_out_start = 0; @@ -85,225 +82,219 @@ static uint8_t msg_debug_out[MSG_DEBUG_OUT_SIZE]; #endif -static inline void msg_out_append(uint8_t c) -{ - if (msg_out_cur == 0) { - msg_out[msg_out_end * 64] = '?'; - msg_out_cur = 1; - } - msg_out[msg_out_end * 64 + msg_out_cur] = c; - msg_out_cur++; - if (msg_out_cur == 64) { - msg_out_cur = 0; - msg_out_end = (msg_out_end + 1) % (MSG_OUT_SIZE / 64); - } +static inline void msg_out_append(uint8_t c) { + if (msg_out_cur == 0) { + msg_out[msg_out_end * 64] = '?'; + msg_out_cur = 1; + } + msg_out[msg_out_end * 64 + msg_out_cur] = c; + msg_out_cur++; + if (msg_out_cur == 64) { + msg_out_cur = 0; + msg_out_end = (msg_out_end + 1) % (MSG_OUT_SIZE / 64); + } } #if DEBUG_LINK -static inline void msg_debug_out_append(uint8_t c) -{ - if (msg_debug_out_cur == 0) { - msg_debug_out[msg_debug_out_end * 64] = '?'; - msg_debug_out_cur = 1; - } - msg_debug_out[msg_debug_out_end * 64 + msg_debug_out_cur] = c; - msg_debug_out_cur++; - if (msg_debug_out_cur == 64) { - msg_debug_out_cur = 0; - msg_debug_out_end = (msg_debug_out_end + 1) % (MSG_DEBUG_OUT_SIZE / 64); - } +static inline void msg_debug_out_append(uint8_t c) { + if (msg_debug_out_cur == 0) { + msg_debug_out[msg_debug_out_end * 64] = '?'; + msg_debug_out_cur = 1; + } + msg_debug_out[msg_debug_out_end * 64 + msg_debug_out_cur] = c; + msg_debug_out_cur++; + if (msg_debug_out_cur == 64) { + msg_debug_out_cur = 0; + msg_debug_out_end = (msg_debug_out_end + 1) % (MSG_DEBUG_OUT_SIZE / 64); + } } #endif -static inline void msg_out_pad(void) -{ - if (msg_out_cur == 0) return; - while (msg_out_cur < 64) { - msg_out[msg_out_end * 64 + msg_out_cur] = 0; - msg_out_cur++; - } - msg_out_cur = 0; - msg_out_end = (msg_out_end + 1) % (MSG_OUT_SIZE / 64); +static inline void msg_out_pad(void) { + if (msg_out_cur == 0) return; + while (msg_out_cur < 64) { + msg_out[msg_out_end * 64 + msg_out_cur] = 0; + msg_out_cur++; + } + msg_out_cur = 0; + msg_out_end = (msg_out_end + 1) % (MSG_OUT_SIZE / 64); } #if DEBUG_LINK -static inline void msg_debug_out_pad(void) -{ - if (msg_debug_out_cur == 0) return; - while (msg_debug_out_cur < 64) { - msg_debug_out[msg_debug_out_end * 64 + msg_debug_out_cur] = 0; - msg_debug_out_cur++; - } - msg_debug_out_cur = 0; - msg_debug_out_end = (msg_debug_out_end + 1) % (MSG_DEBUG_OUT_SIZE / 64); +static inline void msg_debug_out_pad(void) { + if (msg_debug_out_cur == 0) return; + while (msg_debug_out_cur < 64) { + msg_debug_out[msg_debug_out_end * 64 + msg_debug_out_cur] = 0; + msg_debug_out_cur++; + } + msg_debug_out_cur = 0; + msg_debug_out_end = (msg_debug_out_end + 1) % (MSG_DEBUG_OUT_SIZE / 64); } #endif -static bool pb_callback_out(pb_ostream_t *stream, const uint8_t *buf, size_t count) -{ - (void)stream; - for (size_t i = 0; i < count; i++) { - msg_out_append(buf[i]); - } - return true; +static bool pb_callback_out(pb_ostream_t *stream, const uint8_t *buf, + size_t count) { + (void)stream; + for (size_t i = 0; i < count; i++) { + msg_out_append(buf[i]); + } + return true; } #if DEBUG_LINK -static bool pb_debug_callback_out(pb_ostream_t *stream, const uint8_t *buf, size_t count) -{ - (void)stream; - for (size_t i = 0; i < count; i++) { - msg_debug_out_append(buf[i]); - } - return true; +static bool pb_debug_callback_out(pb_ostream_t *stream, const uint8_t *buf, + size_t count) { + (void)stream; + for (size_t i = 0; i < count; i++) { + msg_debug_out_append(buf[i]); + } + return true; } #endif -bool msg_write_common(char type, uint16_t msg_id, const void *msg_ptr) -{ - const pb_field_t *fields = MessageFields(type, 'o', msg_id); - if (!fields) { // unknown message - return false; - } - - size_t len; - if (!pb_get_encoded_size(&len, fields, msg_ptr)) { - return false; - } - - void (*append)(uint8_t); - bool (*pb_callback)(pb_ostream_t *, const uint8_t *, size_t); - - if (type == 'n') { - append = msg_out_append; - pb_callback = pb_callback_out; - } else +bool msg_write_common(char type, uint16_t msg_id, const void *msg_ptr) { + const pb_field_t *fields = MessageFields(type, 'o', msg_id); + if (!fields) { // unknown message + return false; + } + + size_t len; + if (!pb_get_encoded_size(&len, fields, msg_ptr)) { + return false; + } + + void (*append)(uint8_t); + bool (*pb_callback)(pb_ostream_t *, const uint8_t *, size_t); + + if (type == 'n') { + append = msg_out_append; + pb_callback = pb_callback_out; + } else #if DEBUG_LINK - if (type == 'd') { - append = msg_debug_out_append; - pb_callback = pb_debug_callback_out; - } else + if (type == 'd') { + append = msg_debug_out_append; + pb_callback = pb_debug_callback_out; + } else #endif - { - return false; - } - - append('#'); - append('#'); - append((msg_id >> 8) & 0xFF); - append(msg_id & 0xFF); - append((len >> 24) & 0xFF); - append((len >> 16) & 0xFF); - append((len >> 8) & 0xFF); - append(len & 0xFF); - pb_ostream_t stream = {pb_callback, 0, SIZE_MAX, 0, 0}; - bool status = pb_encode(&stream, fields, msg_ptr); - if (type == 'n') { - msg_out_pad(); - } + { + return false; + } + + append('#'); + append('#'); + append((msg_id >> 8) & 0xFF); + append(msg_id & 0xFF); + append((len >> 24) & 0xFF); + append((len >> 16) & 0xFF); + append((len >> 8) & 0xFF); + append(len & 0xFF); + pb_ostream_t stream = {pb_callback, 0, SIZE_MAX, 0, 0}; + bool status = pb_encode(&stream, fields, msg_ptr); + if (type == 'n') { + msg_out_pad(); + } #if DEBUG_LINK - else if (type == 'd') { - msg_debug_out_pad(); - } + else if (type == 'd') { + msg_debug_out_pad(); + } #endif - return status; + return status; } enum { - READSTATE_IDLE, - READSTATE_READING, + READSTATE_IDLE, + READSTATE_READING, }; -void msg_process(char type, uint16_t msg_id, const pb_field_t *fields, uint8_t *msg_raw, uint32_t msg_size) -{ - static uint8_t msg_data[MSG_IN_SIZE]; - memzero(msg_data, sizeof(msg_data)); - pb_istream_t stream = pb_istream_from_buffer(msg_raw, msg_size); - bool status = pb_decode(&stream, fields, msg_data); - if (status) { - MessageProcessFunc(type, 'i', msg_id, msg_data); - } else { - fsm_sendFailure(FailureType_Failure_DataError, stream.errmsg); - } +void msg_process(char type, uint16_t msg_id, const pb_field_t *fields, + uint8_t *msg_raw, uint32_t msg_size) { + static uint8_t msg_data[MSG_IN_SIZE]; + memzero(msg_data, sizeof(msg_data)); + pb_istream_t stream = pb_istream_from_buffer(msg_raw, msg_size); + bool status = pb_decode(&stream, fields, msg_data); + if (status) { + MessageProcessFunc(type, 'i', msg_id, msg_data); + } else { + fsm_sendFailure(FailureType_Failure_DataError, stream.errmsg); + } } -void msg_read_common(char type, const uint8_t *buf, uint32_t len) -{ - static char read_state = READSTATE_IDLE; - static uint8_t msg_in[MSG_IN_SIZE]; - static uint16_t msg_id = 0xFFFF; - static uint32_t msg_size = 0; - static uint32_t msg_pos = 0; - static const pb_field_t *fields = 0; - - if (len != 64) return; - - if (read_state == READSTATE_IDLE) { - if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { // invalid start - discard - return; - } - msg_id = (buf[3] << 8) + buf[4]; - msg_size = ((uint32_t) buf[5] << 24)+ (buf[6] << 16) + (buf[7] << 8) + buf[8]; - - fields = MessageFields(type, 'i', msg_id); - if (!fields) { // unknown message - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Unknown message")); - return; - } - if (msg_size > MSG_IN_SIZE) { // message is too big :( - fsm_sendFailure(FailureType_Failure_DataError, _("Message too big")); - return; - } - - read_state = READSTATE_READING; - - memcpy(msg_in, buf + 9, len - 9); - msg_pos = len - 9; - } else - if (read_state == READSTATE_READING) { - if (buf[0] != '?') { // invalid contents - read_state = READSTATE_IDLE; - return; - } - /* raw data starts at buf + 1 with len - 1 bytes */ - buf++; - len = MIN(len - 1, MSG_IN_SIZE - msg_pos); - - memcpy(msg_in + msg_pos, buf, len); - msg_pos += len; - } - - if (msg_pos >= msg_size) { - msg_process(type, msg_id, fields, msg_in, msg_size); - msg_pos = 0; - read_state = READSTATE_IDLE; - } +void msg_read_common(char type, const uint8_t *buf, uint32_t len) { + static char read_state = READSTATE_IDLE; + static uint8_t msg_in[MSG_IN_SIZE]; + static uint16_t msg_id = 0xFFFF; + static uint32_t msg_size = 0; + static uint32_t msg_pos = 0; + static const pb_field_t *fields = 0; + + if (len != 64) return; + + if (read_state == READSTATE_IDLE) { + if (buf[0] != '?' || buf[1] != '#' || + buf[2] != '#') { // invalid start - discard + return; + } + msg_id = (buf[3] << 8) + buf[4]; + msg_size = + ((uint32_t)buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; + + fields = MessageFields(type, 'i', msg_id); + if (!fields) { // unknown message + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Unknown message")); + return; + } + if (msg_size > MSG_IN_SIZE) { // message is too big :( + fsm_sendFailure(FailureType_Failure_DataError, _("Message too big")); + return; + } + + read_state = READSTATE_READING; + + memcpy(msg_in, buf + 9, len - 9); + msg_pos = len - 9; + } else if (read_state == READSTATE_READING) { + if (buf[0] != '?') { // invalid contents + read_state = READSTATE_IDLE; + return; + } + /* raw data starts at buf + 1 with len - 1 bytes */ + buf++; + len = MIN(len - 1, MSG_IN_SIZE - msg_pos); + + memcpy(msg_in + msg_pos, buf, len); + msg_pos += len; + } + + if (msg_pos >= msg_size) { + msg_process(type, msg_id, fields, msg_in, msg_size); + msg_pos = 0; + read_state = READSTATE_IDLE; + } } -const uint8_t *msg_out_data(void) -{ - if (msg_out_start == msg_out_end) return 0; - uint8_t *data = msg_out + (msg_out_start * 64); - msg_out_start = (msg_out_start + 1) % (MSG_OUT_SIZE / 64); - debugLog(0, "", "msg_out_data"); - return data; +const uint8_t *msg_out_data(void) { + if (msg_out_start == msg_out_end) return 0; + uint8_t *data = msg_out + (msg_out_start * 64); + msg_out_start = (msg_out_start + 1) % (MSG_OUT_SIZE / 64); + debugLog(0, "", "msg_out_data"); + return data; } #if DEBUG_LINK -const uint8_t *msg_debug_out_data(void) -{ - if (msg_debug_out_start == msg_debug_out_end) return 0; - uint8_t *data = msg_debug_out + (msg_debug_out_start * 64); - msg_debug_out_start = (msg_debug_out_start + 1) % (MSG_DEBUG_OUT_SIZE / 64); - debugLog(0, "", "msg_debug_out_data"); - return data; +const uint8_t *msg_debug_out_data(void) { + if (msg_debug_out_start == msg_debug_out_end) return 0; + uint8_t *data = msg_debug_out + (msg_debug_out_start * 64); + msg_debug_out_start = (msg_debug_out_start + 1) % (MSG_DEBUG_OUT_SIZE / 64); + debugLog(0, "", "msg_debug_out_data"); + return data; } #endif @@ -315,61 +306,64 @@ _Static_assert(sizeof(msg_tiny) >= sizeof(PassphraseAck), "msg_tiny too tiny"); _Static_assert(sizeof(msg_tiny) >= sizeof(ButtonAck), "msg_tiny too tiny"); _Static_assert(sizeof(msg_tiny) >= sizeof(PinMatrixAck), "msg_tiny too tiny"); #if DEBUG_LINK -_Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkDecision), "msg_tiny too tiny"); -_Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkGetState), "msg_tiny too tiny"); +_Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkDecision), + "msg_tiny too tiny"); +_Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkGetState), + "msg_tiny too tiny"); #endif uint16_t msg_tiny_id = 0xFFFF; -void msg_read_tiny(const uint8_t *buf, int len) -{ - if (len != 64) return; - if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { - return; - } - uint16_t msg_id = (buf[3] << 8) + buf[4]; - uint32_t msg_size = ((uint32_t) buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; - if (msg_size > 64 || len - msg_size < 9) { - return; - } - - const pb_field_t *fields = 0; - pb_istream_t stream = pb_istream_from_buffer(buf + 9, msg_size); - - switch (msg_id) { - case MessageType_MessageType_PinMatrixAck: - fields = PinMatrixAck_fields; - break; - case MessageType_MessageType_ButtonAck: - fields = ButtonAck_fields; - break; - case MessageType_MessageType_PassphraseAck: - fields = PassphraseAck_fields; - break; - case MessageType_MessageType_Cancel: - fields = Cancel_fields; - break; - case MessageType_MessageType_Initialize: - fields = Initialize_fields; - break; +void msg_read_tiny(const uint8_t *buf, int len) { + if (len != 64) return; + if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { + return; + } + uint16_t msg_id = (buf[3] << 8) + buf[4]; + uint32_t msg_size = + ((uint32_t)buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8]; + if (msg_size > 64 || len - msg_size < 9) { + return; + } + + const pb_field_t *fields = 0; + pb_istream_t stream = pb_istream_from_buffer(buf + 9, msg_size); + + switch (msg_id) { + case MessageType_MessageType_PinMatrixAck: + fields = PinMatrixAck_fields; + break; + case MessageType_MessageType_ButtonAck: + fields = ButtonAck_fields; + break; + case MessageType_MessageType_PassphraseAck: + fields = PassphraseAck_fields; + break; + case MessageType_MessageType_Cancel: + fields = Cancel_fields; + break; + case MessageType_MessageType_Initialize: + fields = Initialize_fields; + break; #if DEBUG_LINK - case MessageType_MessageType_DebugLinkDecision: - fields = DebugLinkDecision_fields; - break; - case MessageType_MessageType_DebugLinkGetState: - fields = DebugLinkGetState_fields; - break; + case MessageType_MessageType_DebugLinkDecision: + fields = DebugLinkDecision_fields; + break; + case MessageType_MessageType_DebugLinkGetState: + fields = DebugLinkGetState_fields; + break; #endif - } - if (fields) { - bool status = pb_decode(&stream, fields, msg_tiny); - if (status) { - msg_tiny_id = msg_id; - } else { - fsm_sendFailure(FailureType_Failure_DataError, stream.errmsg); - msg_tiny_id = 0xFFFF; - } - } else { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Unknown message")); - msg_tiny_id = 0xFFFF; - } + } + if (fields) { + bool status = pb_decode(&stream, fields, msg_tiny); + if (status) { + msg_tiny_id = msg_id; + } else { + fsm_sendFailure(FailureType_Failure_DataError, stream.errmsg); + msg_tiny_id = 0xFFFF; + } + } else { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Unknown message")); + msg_tiny_id = 0xFFFF; + } } diff --git a/firmware/messages.h b/firmware/messages.h index 1806a7dae..fe3ca2898 100644 --- a/firmware/messages.h +++ b/firmware/messages.h @@ -20,8 +20,8 @@ #ifndef __MESSAGES_H__ #define __MESSAGES_H__ -#include #include +#include #include "trezor.h" #define MSG_IN_SIZE (15 * 1024) diff --git a/firmware/nem2.c b/firmware/nem2.c index 65c3cf77b..b9b3a6144 100644 --- a/firmware/nem2.c +++ b/firmware/nem2.c @@ -23,788 +23,768 @@ #include "fsm.h" #include "gettext.h" #include "layout2.h" +#include "memzero.h" #include "protect.h" #include "rng.h" #include "secp256k1.h" -#include "memzero.h" const char *nem_validate_common(NEMTransactionCommon *common, bool inner) { - if (!common->has_network) { - common->has_network = true; - common->network = NEM_NETWORK_MAINNET; - } - - if (common->network > 0xFF || nem_network_name(common->network) == NULL) { - return inner ? _("Invalid NEM network in inner transaction") : _("Invalid NEM network"); - } - - if (!common->has_timestamp) { - return inner ? _("No timestamp provided in inner transaction") : _("No timestamp provided"); - } - - if (!common->has_fee) { - return inner ? _("No fee provided in inner transaction") : _("No fee provided"); - } - - if (!common->has_deadline) { - return inner ? _("No deadline provided in inner transaction") : _("No deadline provided"); - } - - if (inner != common->has_signer) { - return inner ? _("No signer provided in inner transaction") : _("Signer not allowed in outer transaction"); - } - - if (common->has_signer && common->signer.size != sizeof(ed25519_public_key)) { - return _("Invalid signer public key in inner transaction"); - } - - return NULL; + if (!common->has_network) { + common->has_network = true; + common->network = NEM_NETWORK_MAINNET; + } + + if (common->network > 0xFF || nem_network_name(common->network) == NULL) { + return inner ? _("Invalid NEM network in inner transaction") + : _("Invalid NEM network"); + } + + if (!common->has_timestamp) { + return inner ? _("No timestamp provided in inner transaction") + : _("No timestamp provided"); + } + + if (!common->has_fee) { + return inner ? _("No fee provided in inner transaction") + : _("No fee provided"); + } + + if (!common->has_deadline) { + return inner ? _("No deadline provided in inner transaction") + : _("No deadline provided"); + } + + if (inner != common->has_signer) { + return inner ? _("No signer provided in inner transaction") + : _("Signer not allowed in outer transaction"); + } + + if (common->has_signer && common->signer.size != sizeof(ed25519_public_key)) { + return _("Invalid signer public key in inner transaction"); + } + + return NULL; } -const char *nem_validate_transfer(const NEMTransfer *transfer, uint8_t network) { - if (!transfer->has_recipient) return _("No recipient provided"); - if (!transfer->has_amount) return _("No amount provided"); +const char *nem_validate_transfer(const NEMTransfer *transfer, + uint8_t network) { + if (!transfer->has_recipient) return _("No recipient provided"); + if (!transfer->has_amount) return _("No amount provided"); - if (transfer->has_public_key && transfer->public_key.size != sizeof(ed25519_public_key)) { - return _("Invalid recipient public key"); - } + if (transfer->has_public_key && + transfer->public_key.size != sizeof(ed25519_public_key)) { + return _("Invalid recipient public key"); + } - if (!nem_validate_address(transfer->recipient, network)) return _("Invalid recipient address"); + if (!nem_validate_address(transfer->recipient, network)) + return _("Invalid recipient address"); - for (size_t i = 0; i < transfer->mosaics_count; i++) { - const NEMMosaic *mosaic = &transfer->mosaics[i]; + for (size_t i = 0; i < transfer->mosaics_count; i++) { + const NEMMosaic *mosaic = &transfer->mosaics[i]; - if (!mosaic->has_namespace) return _("No mosaic namespace provided"); - if (!mosaic->has_mosaic) return _("No mosaic name provided"); - if (!mosaic->has_quantity) return _("No mosaic quantity provided"); - } + if (!mosaic->has_namespace) return _("No mosaic namespace provided"); + if (!mosaic->has_mosaic) return _("No mosaic name provided"); + if (!mosaic->has_quantity) return _("No mosaic quantity provided"); + } - return NULL; + return NULL; } -const char *nem_validate_provision_namespace(const NEMProvisionNamespace *provision_namespace, uint8_t network) { - if (!provision_namespace->has_namespace) return _("No namespace provided"); - if (!provision_namespace->has_sink) return _("No rental sink provided"); - if (!provision_namespace->has_fee) return _("No rental sink fee provided"); +const char *nem_validate_provision_namespace( + const NEMProvisionNamespace *provision_namespace, uint8_t network) { + if (!provision_namespace->has_namespace) return _("No namespace provided"); + if (!provision_namespace->has_sink) return _("No rental sink provided"); + if (!provision_namespace->has_fee) return _("No rental sink fee provided"); - if (!nem_validate_address(provision_namespace->sink, network)) return _("Invalid rental sink address"); + if (!nem_validate_address(provision_namespace->sink, network)) + return _("Invalid rental sink address"); - return NULL; + return NULL; } -const char *nem_validate_mosaic_creation(const NEMMosaicCreation *mosaic_creation, uint8_t network) { - if (!mosaic_creation->has_definition) return _("No mosaic definition provided"); - if (!mosaic_creation->has_sink) return _("No creation sink provided"); - if (!mosaic_creation->has_fee) return _("No creation sink fee provided"); - - if (!nem_validate_address(mosaic_creation->sink, network)) return _("Invalid creation sink address"); - - if (mosaic_creation->definition.has_name) return _("Name not allowed in mosaic creation transactions"); - if (mosaic_creation->definition.has_ticker) return _("Ticker not allowed in mosaic creation transactions"); - if (mosaic_creation->definition.networks_count) return _("Networks not allowed in mosaic creation transactions"); - - if (!mosaic_creation->definition.has_namespace) return _("No mosaic namespace provided"); - if (!mosaic_creation->definition.has_mosaic) return _("No mosaic name provided"); - - if (mosaic_creation->definition.has_levy) { - if (!mosaic_creation->definition.has_fee) return _("No levy address provided"); - if (!mosaic_creation->definition.has_levy_address) return _("No levy address provided"); - if (!mosaic_creation->definition.has_levy_namespace) return _("No levy namespace provided"); - if (!mosaic_creation->definition.has_levy_mosaic) return _("No levy mosaic name provided"); - - if (!mosaic_creation->definition.has_divisibility) return _("No divisibility provided"); - if (!mosaic_creation->definition.has_supply) return _("No supply provided"); - if (!mosaic_creation->definition.has_mutable_supply) return _("No supply mutability provided"); - if (!mosaic_creation->definition.has_transferable) return _("No mosaic transferability provided"); - if (!mosaic_creation->definition.has_description) return _("No description provided"); - - if (mosaic_creation->definition.divisibility > NEM_MAX_DIVISIBILITY) return _("Invalid divisibility provided"); - if (mosaic_creation->definition.supply > NEM_MAX_SUPPLY) return _("Invalid supply provided"); - - if (!nem_validate_address(mosaic_creation->definition.levy_address, network)) return _("Invalid levy address"); - } - - return NULL; +const char *nem_validate_mosaic_creation( + const NEMMosaicCreation *mosaic_creation, uint8_t network) { + if (!mosaic_creation->has_definition) + return _("No mosaic definition provided"); + if (!mosaic_creation->has_sink) return _("No creation sink provided"); + if (!mosaic_creation->has_fee) return _("No creation sink fee provided"); + + if (!nem_validate_address(mosaic_creation->sink, network)) + return _("Invalid creation sink address"); + + if (mosaic_creation->definition.has_name) + return _("Name not allowed in mosaic creation transactions"); + if (mosaic_creation->definition.has_ticker) + return _("Ticker not allowed in mosaic creation transactions"); + if (mosaic_creation->definition.networks_count) + return _("Networks not allowed in mosaic creation transactions"); + + if (!mosaic_creation->definition.has_namespace) + return _("No mosaic namespace provided"); + if (!mosaic_creation->definition.has_mosaic) + return _("No mosaic name provided"); + + if (mosaic_creation->definition.has_levy) { + if (!mosaic_creation->definition.has_fee) + return _("No levy address provided"); + if (!mosaic_creation->definition.has_levy_address) + return _("No levy address provided"); + if (!mosaic_creation->definition.has_levy_namespace) + return _("No levy namespace provided"); + if (!mosaic_creation->definition.has_levy_mosaic) + return _("No levy mosaic name provided"); + + if (!mosaic_creation->definition.has_divisibility) + return _("No divisibility provided"); + if (!mosaic_creation->definition.has_supply) return _("No supply provided"); + if (!mosaic_creation->definition.has_mutable_supply) + return _("No supply mutability provided"); + if (!mosaic_creation->definition.has_transferable) + return _("No mosaic transferability provided"); + if (!mosaic_creation->definition.has_description) + return _("No description provided"); + + if (mosaic_creation->definition.divisibility > NEM_MAX_DIVISIBILITY) + return _("Invalid divisibility provided"); + if (mosaic_creation->definition.supply > NEM_MAX_SUPPLY) + return _("Invalid supply provided"); + + if (!nem_validate_address(mosaic_creation->definition.levy_address, + network)) + return _("Invalid levy address"); + } + + return NULL; } -const char *nem_validate_supply_change(const NEMMosaicSupplyChange *supply_change) { - if (!supply_change->has_namespace) return _("No namespace provided"); - if (!supply_change->has_mosaic) return _("No mosaic provided"); - if (!supply_change->has_type) return _("No type provided"); - if (!supply_change->has_delta) return _("No delta provided"); +const char *nem_validate_supply_change( + const NEMMosaicSupplyChange *supply_change) { + if (!supply_change->has_namespace) return _("No namespace provided"); + if (!supply_change->has_mosaic) return _("No mosaic provided"); + if (!supply_change->has_type) return _("No type provided"); + if (!supply_change->has_delta) return _("No delta provided"); - return NULL; + return NULL; } -const char *nem_validate_aggregate_modification(const NEMAggregateModification *aggregate_modification, bool creation) { - if (creation && aggregate_modification->modifications_count == 0) { - return _("No modifications provided"); - } - - for (size_t i = 0; i < aggregate_modification->modifications_count; i++) { - const NEMCosignatoryModification *modification = &aggregate_modification->modifications[i]; - - if (!modification->has_type) return _("No modification type provided"); - if (!modification->has_public_key) return _("No cosignatory public key provided"); - if (modification->public_key.size != 32) return _("Invalid cosignatory public key provided"); - - if (creation && modification->type == NEMModificationType_CosignatoryModification_Delete) { - return _("Cannot remove cosignatory when converting account"); - } - } - - return NULL; +const char *nem_validate_aggregate_modification( + const NEMAggregateModification *aggregate_modification, bool creation) { + if (creation && aggregate_modification->modifications_count == 0) { + return _("No modifications provided"); + } + + for (size_t i = 0; i < aggregate_modification->modifications_count; i++) { + const NEMCosignatoryModification *modification = + &aggregate_modification->modifications[i]; + + if (!modification->has_type) return _("No modification type provided"); + if (!modification->has_public_key) + return _("No cosignatory public key provided"); + if (modification->public_key.size != 32) + return _("Invalid cosignatory public key provided"); + + if (creation && modification->type == + NEMModificationType_CosignatoryModification_Delete) { + return _("Cannot remove cosignatory when converting account"); + } + } + + return NULL; } -const char *nem_validate_importance_transfer(const NEMImportanceTransfer *importance_transfer) { - if (!importance_transfer->has_mode) return _("No mode provided"); - if (!importance_transfer->has_public_key) return _("No remote account provided"); - if (importance_transfer->public_key.size != 32) return _("Invalid remote account provided"); +const char *nem_validate_importance_transfer( + const NEMImportanceTransfer *importance_transfer) { + if (!importance_transfer->has_mode) return _("No mode provided"); + if (!importance_transfer->has_public_key) + return _("No remote account provided"); + if (importance_transfer->public_key.size != 32) + return _("Invalid remote account provided"); - return NULL; + return NULL; } -bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer, const char *desc) { - if (transfer->mosaics_count) { - const NEMMosaic *xem = NULL; - bool unknownMosaic = false; - - const NEMMosaicDefinition *definitions[transfer->mosaics_count]; - - for (size_t i = 0; i < transfer->mosaics_count; i++) { - const NEMMosaic *mosaic = &transfer->mosaics[i]; - - definitions[i] = nem_mosaicByName(mosaic->namespace, mosaic->mosaic, common->network); - - if (definitions[i] == NEM_MOSAIC_DEFINITION_XEM) { - xem = mosaic; - } else if (definitions[i] == NULL) { - unknownMosaic = true; - } - } - - bignum256 multiplier; - bn_read_uint64(transfer->amount, &multiplier); - - if (unknownMosaic) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("I take the risk"), - _("Unknown Mosaics"), - _("Divisibility and levy"), - _("cannot be shown for"), - _("unknown mosaics!"), - NULL, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - layoutNEMTransferXEM(desc, xem ? xem->quantity : 0, &multiplier, common->fee); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - for (size_t i = 0; i < transfer->mosaics_count; i++) { - const NEMMosaic *mosaic = &transfer->mosaics[i]; - - if (mosaic == xem) { - continue; - } - - if (definitions[i]) { - layoutNEMTransferMosaic(definitions[i], mosaic->quantity, &multiplier, common->network); - } else { - layoutNEMTransferUnknownMosaic(mosaic->namespace, mosaic->mosaic, mosaic->quantity, &multiplier); - } - - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - } else { - layoutNEMTransferXEM(desc, transfer->amount, NULL, common->fee); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - if (transfer->has_payload) { - layoutNEMTransferPayload(transfer->payload.bytes, transfer->payload.size, transfer->has_public_key); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - layoutNEMDialog(&bmp_icon_question, - _("Cancel"), - _("Confirm"), - desc, - _("Confirm transfer to"), - transfer->recipient); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - return false; - } - - return true; +bool nem_askTransfer(const NEMTransactionCommon *common, + const NEMTransfer *transfer, const char *desc) { + if (transfer->mosaics_count) { + const NEMMosaic *xem = NULL; + bool unknownMosaic = false; + + const NEMMosaicDefinition *definitions[transfer->mosaics_count]; + + for (size_t i = 0; i < transfer->mosaics_count; i++) { + const NEMMosaic *mosaic = &transfer->mosaics[i]; + + definitions[i] = + nem_mosaicByName(mosaic->namespace, mosaic->mosaic, common->network); + + if (definitions[i] == NEM_MOSAIC_DEFINITION_XEM) { + xem = mosaic; + } else if (definitions[i] == NULL) { + unknownMosaic = true; + } + } + + bignum256 multiplier; + bn_read_uint64(transfer->amount, &multiplier); + + if (unknownMosaic) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), + _("Unknown Mosaics"), _("Divisibility and levy"), + _("cannot be shown for"), _("unknown mosaics!"), NULL, + NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, + false)) { + return false; + } + } + + layoutNEMTransferXEM(desc, xem ? xem->quantity : 0, &multiplier, + common->fee); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + for (size_t i = 0; i < transfer->mosaics_count; i++) { + const NEMMosaic *mosaic = &transfer->mosaics[i]; + + if (mosaic == xem) { + continue; + } + + if (definitions[i]) { + layoutNEMTransferMosaic(definitions[i], mosaic->quantity, &multiplier, + common->network); + } else { + layoutNEMTransferUnknownMosaic(mosaic->namespace, mosaic->mosaic, + mosaic->quantity, &multiplier); + } + + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, + false)) { + return false; + } + } + } else { + layoutNEMTransferXEM(desc, transfer->amount, NULL, common->fee); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + } + + if (transfer->has_payload) { + layoutNEMTransferPayload(transfer->payload.bytes, transfer->payload.size, + transfer->has_public_key); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + } + + layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Confirm"), desc, + _("Confirm transfer to"), transfer->recipient); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + return false; + } + + return true; } -bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, const NEMTransactionCommon *common, const NEMTransfer *transfer) { - static uint8_t encrypted[NEM_ENCRYPTED_PAYLOAD_SIZE(sizeof(transfer->payload.bytes))]; - - const uint8_t *payload = transfer->payload.bytes; - size_t size = transfer->payload.size; - - if (transfer->has_public_key) { - if (node == NULL) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Private key unavailable for encrypted message")); - return false; - } - - random_buffer(encrypted, NEM_SALT_SIZE + AES_BLOCK_SIZE); - - const uint8_t *salt = encrypted; - const uint8_t *iv = &encrypted[NEM_SALT_SIZE]; - uint8_t *buffer = &encrypted[NEM_SALT_SIZE + AES_BLOCK_SIZE]; - - bool ret = hdnode_nem_encrypt(node, - transfer->public_key.bytes, - iv, - salt, - payload, - size, - buffer); - - if (!ret) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to encrypt payload")); - return false; - } - - payload = encrypted; - size = NEM_ENCRYPTED_PAYLOAD_SIZE(size); - } - - bool ret = nem_transaction_create_transfer(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - transfer->recipient, - transfer->amount, - payload, - size, - transfer->has_public_key, - transfer->mosaics_count); - - if (!ret) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to create transfer transaction")); - return false; - } - - for (size_t i = 0; i < transfer->mosaics_count; i++) { - const NEMMosaic *mosaic = &transfer->mosaics[i]; - - ret = nem_transaction_write_mosaic(context, - mosaic->namespace, - mosaic->mosaic, - mosaic->quantity); - - if (!ret) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to attach mosaics")); - return false; - } - } - - return true; +bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, + const NEMTransactionCommon *common, + const NEMTransfer *transfer) { + static uint8_t + encrypted[NEM_ENCRYPTED_PAYLOAD_SIZE(sizeof(transfer->payload.bytes))]; + + const uint8_t *payload = transfer->payload.bytes; + size_t size = transfer->payload.size; + + if (transfer->has_public_key) { + if (node == NULL) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Private key unavailable for encrypted message")); + return false; + } + + random_buffer(encrypted, NEM_SALT_SIZE + AES_BLOCK_SIZE); + + const uint8_t *salt = encrypted; + const uint8_t *iv = &encrypted[NEM_SALT_SIZE]; + uint8_t *buffer = &encrypted[NEM_SALT_SIZE + AES_BLOCK_SIZE]; + + bool ret = hdnode_nem_encrypt(node, transfer->public_key.bytes, iv, salt, + payload, size, buffer); + + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to encrypt payload")); + return false; + } + + payload = encrypted; + size = NEM_ENCRYPTED_PAYLOAD_SIZE(size); + } + + bool ret = nem_transaction_create_transfer( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, transfer->recipient, transfer->amount, payload, size, + transfer->has_public_key, transfer->mosaics_count); + + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to create transfer transaction")); + return false; + } + + for (size_t i = 0; i < transfer->mosaics_count; i++) { + const NEMMosaic *mosaic = &transfer->mosaics[i]; + + ret = nem_transaction_write_mosaic(context, mosaic->namespace, + mosaic->mosaic, mosaic->quantity); + + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to attach mosaics")); + return false; + } + } + + return true; } -bool nem_askProvisionNamespace(const NEMTransactionCommon *common, const NEMProvisionNamespace *provision_namespace, const char *desc) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - _("Create namespace"), - provision_namespace->namespace, - provision_namespace->has_parent ? _("under namespace") : NULL, - provision_namespace->has_parent ? provision_namespace->parent : NULL, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - layoutNEMNetworkFee(desc, true, _("Confirm rental fee of"), provision_namespace->fee, _("and network fee of"), common->fee); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - return false; - } - - return true; +bool nem_askProvisionNamespace(const NEMTransactionCommon *common, + const NEMProvisionNamespace *provision_namespace, + const char *desc) { + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), desc, _("Create namespace"), + provision_namespace->namespace, + provision_namespace->has_parent ? _("under namespace") : NULL, + provision_namespace->has_parent ? provision_namespace->parent : NULL, + NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + layoutNEMNetworkFee(desc, true, _("Confirm rental fee of"), + provision_namespace->fee, _("and network fee of"), + common->fee); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + return false; + } + + return true; } -bool nem_fsmProvisionNamespace(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMProvisionNamespace *provision_namespace) { - return nem_transaction_create_provision_namespace(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - provision_namespace->namespace, - provision_namespace->has_parent ? provision_namespace->parent : NULL, - provision_namespace->sink, - provision_namespace->fee); +bool nem_fsmProvisionNamespace( + nem_transaction_ctx *context, const NEMTransactionCommon *common, + const NEMProvisionNamespace *provision_namespace) { + return nem_transaction_create_provision_namespace( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, provision_namespace->namespace, + provision_namespace->has_parent ? provision_namespace->parent : NULL, + provision_namespace->sink, provision_namespace->fee); } -bool nem_askMosaicCreation(const NEMTransactionCommon *common, const NEMMosaicCreation *mosaic_creation, const char *desc, const char *address) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - _("Create mosaic"), - mosaic_creation->definition.mosaic, - _("under namespace"), - mosaic_creation->definition.namespace, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - layoutNEMMosaicDescription(mosaic_creation->definition.description); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - char str_out[32]; - - bn_format_uint64(mosaic_creation->definition.supply, - NULL, - NULL, - mosaic_creation->definition.divisibility, - mosaic_creation->definition.divisibility, - true, - str_out, - sizeof(str_out)); - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - _("Properties"), - mosaic_creation->definition.mutable_supply ? _("Mutable supply:") : _("Immutable supply:"), - str_out, - _("Mosaic will be"), - mosaic_creation->definition.transferable ? _("transferable") : _("non-transferable"), - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - if (mosaic_creation->definition.has_levy) { - layoutNEMLevy(&mosaic_creation->definition, common->network); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - if (strcmp(address, mosaic_creation->definition.levy_address) == 0) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - _("Levy Recipient"), - _("Levy will be paid to"), - _("yourself"), - NULL, - NULL, - NULL, - NULL); - } else { - layoutNEMDialog(&bmp_icon_question, - _("Cancel"), - _("Next"), - _("Levy Recipient"), - _("Levy will be paid to"), - mosaic_creation->definition.levy_address); - } - - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - layoutNEMNetworkFee(desc, true, _("Confirm creation fee"), mosaic_creation->fee, _("and network fee of"), common->fee); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - return false; - } - - return true; +bool nem_askMosaicCreation(const NEMTransactionCommon *common, + const NEMMosaicCreation *mosaic_creation, + const char *desc, const char *address) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc, + _("Create mosaic"), mosaic_creation->definition.mosaic, + _("under namespace"), mosaic_creation->definition.namespace, + NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + layoutNEMMosaicDescription(mosaic_creation->definition.description); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + char str_out[32]; + + bn_format_uint64(mosaic_creation->definition.supply, NULL, NULL, + mosaic_creation->definition.divisibility, + mosaic_creation->definition.divisibility, true, str_out, + sizeof(str_out)); + + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), _("Properties"), + mosaic_creation->definition.mutable_supply ? _("Mutable supply:") + : _("Immutable supply:"), + str_out, _("Mosaic will be"), + mosaic_creation->definition.transferable ? _("transferable") + : _("non-transferable"), + NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + if (mosaic_creation->definition.has_levy) { + layoutNEMLevy(&mosaic_creation->definition, common->network); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + if (strcmp(address, mosaic_creation->definition.levy_address) == 0) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), + _("Levy Recipient"), _("Levy will be paid to"), + _("yourself"), NULL, NULL, NULL, NULL); + } else { + layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Next"), + _("Levy Recipient"), _("Levy will be paid to"), + mosaic_creation->definition.levy_address); + } + + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + } + + layoutNEMNetworkFee(desc, true, _("Confirm creation fee"), + mosaic_creation->fee, _("and network fee of"), + common->fee); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + return false; + } + + return true; } -bool nem_fsmMosaicCreation(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMMosaicCreation *mosaic_creation) { - return nem_transaction_create_mosaic_creation(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - mosaic_creation->definition.namespace, - mosaic_creation->definition.mosaic, - mosaic_creation->definition.description, - mosaic_creation->definition.divisibility, - mosaic_creation->definition.supply, - mosaic_creation->definition.mutable_supply, - mosaic_creation->definition.transferable, - mosaic_creation->definition.levy, - mosaic_creation->definition.fee, - mosaic_creation->definition.levy_address, - mosaic_creation->definition.levy_namespace, - mosaic_creation->definition.levy_mosaic, - mosaic_creation->sink, - mosaic_creation->fee); +bool nem_fsmMosaicCreation(nem_transaction_ctx *context, + const NEMTransactionCommon *common, + const NEMMosaicCreation *mosaic_creation) { + return nem_transaction_create_mosaic_creation( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, mosaic_creation->definition.namespace, + mosaic_creation->definition.mosaic, + mosaic_creation->definition.description, + mosaic_creation->definition.divisibility, + mosaic_creation->definition.supply, + mosaic_creation->definition.mutable_supply, + mosaic_creation->definition.transferable, + mosaic_creation->definition.levy, mosaic_creation->definition.fee, + mosaic_creation->definition.levy_address, + mosaic_creation->definition.levy_namespace, + mosaic_creation->definition.levy_mosaic, mosaic_creation->sink, + mosaic_creation->fee); } -bool nem_askSupplyChange(const NEMTransactionCommon *common, const NEMMosaicSupplyChange *supply_change, const char *desc) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - _("Modify supply for"), - supply_change->mosaic, - _("under namespace"), - supply_change->namespace, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - char str_out[32]; - bn_format_uint64(supply_change->delta, NULL, NULL, 0, 0, false, str_out, sizeof(str_out)); - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - supply_change->type == NEMSupplyChangeType_SupplyChange_Increase ? _("Increase supply by") : _("Decrease supply by"), - str_out, - _("whole units"), - NULL, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL, 0); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - return false; - } - - return true; +bool nem_askSupplyChange(const NEMTransactionCommon *common, + const NEMMosaicSupplyChange *supply_change, + const char *desc) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc, + _("Modify supply for"), supply_change->mosaic, + _("under namespace"), supply_change->namespace, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + char str_out[32]; + bn_format_uint64(supply_change->delta, NULL, NULL, 0, 0, false, str_out, + sizeof(str_out)); + + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), desc, + supply_change->type == NEMSupplyChangeType_SupplyChange_Increase + ? _("Increase supply by") + : _("Decrease supply by"), + str_out, _("whole units"), NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL, + 0); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + return false; + } + + return true; } -bool nem_fsmSupplyChange(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMMosaicSupplyChange *supply_change) { - return nem_transaction_create_mosaic_supply_change(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - supply_change->namespace, - supply_change->mosaic, - supply_change->type, - supply_change->delta); +bool nem_fsmSupplyChange(nem_transaction_ctx *context, + const NEMTransactionCommon *common, + const NEMMosaicSupplyChange *supply_change) { + return nem_transaction_create_mosaic_supply_change( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, supply_change->namespace, supply_change->mosaic, + supply_change->type, supply_change->delta); } -bool nem_askAggregateModification(const NEMTransactionCommon *common, const NEMAggregateModification *aggregate_modification, const char *desc, bool creation) { - if (creation) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - _("Convert account to"), - _("multisig account?"), - NULL, - NULL, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - char address[NEM_ADDRESS_SIZE + 1]; - - for (size_t i = 0; i < aggregate_modification->modifications_count; i++) { - const NEMCosignatoryModification *modification = &aggregate_modification->modifications[i]; - nem_get_address(modification->public_key.bytes, common->network, address); - - layoutNEMDialog(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - modification->type == NEMModificationType_CosignatoryModification_Add ? _("Add cosignatory") : _("Remove cosignatory"), - address); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - int32_t relative_change = aggregate_modification->relative_change; - if (relative_change) { - char str_out[32]; - bn_format_uint64(relative_change < 0 ? -relative_change : relative_change, - NULL, - NULL, - 0, - 0, - false, - str_out, - sizeof(str_out)); - - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - creation ? _("Set minimum") : (relative_change < 0 ? _("Decrease minimum") : _("Increase minimum")), - creation ? _("cosignatories to") : _("cosignatories by"), - str_out, - NULL, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - } - - layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL, 0); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - return false; - } - - return true; +bool nem_askAggregateModification( + const NEMTransactionCommon *common, + const NEMAggregateModification *aggregate_modification, const char *desc, + bool creation) { + if (creation) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc, + _("Convert account to"), _("multisig account?"), NULL, + NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + } + + char address[NEM_ADDRESS_SIZE + 1]; + + for (size_t i = 0; i < aggregate_modification->modifications_count; i++) { + const NEMCosignatoryModification *modification = + &aggregate_modification->modifications[i]; + nem_get_address(modification->public_key.bytes, common->network, address); + + layoutNEMDialog( + &bmp_icon_question, _("Cancel"), _("Next"), desc, + modification->type == NEMModificationType_CosignatoryModification_Add + ? _("Add cosignatory") + : _("Remove cosignatory"), + address); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + } + + int32_t relative_change = aggregate_modification->relative_change; + if (relative_change) { + char str_out[32]; + bn_format_uint64(relative_change < 0 ? -relative_change : relative_change, + NULL, NULL, 0, 0, false, str_out, sizeof(str_out)); + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc, + creation ? _("Set minimum") + : (relative_change < 0 ? _("Decrease minimum") + : _("Increase minimum")), + creation ? _("cosignatories to") : _("cosignatories by"), + str_out, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + } + + layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL, + 0); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + return false; + } + + return true; } -bool nem_fsmAggregateModification(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMAggregateModification *aggregate_modification) { - bool ret = nem_transaction_create_aggregate_modification(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - aggregate_modification->modifications_count, - aggregate_modification->relative_change != 0); - if (!ret) return false; - - for (size_t i = 0; i < aggregate_modification->modifications_count; i++) { - const NEMCosignatoryModification *modification = &aggregate_modification->modifications[i]; - - ret = nem_transaction_write_cosignatory_modification(context, - modification->type, - modification->public_key.bytes); - if (!ret) return false; - } - - if (aggregate_modification->relative_change) { - ret = nem_transaction_write_minimum_cosignatories(context, aggregate_modification->relative_change); - if (!ret) return false; - } - - return true; +bool nem_fsmAggregateModification( + nem_transaction_ctx *context, const NEMTransactionCommon *common, + const NEMAggregateModification *aggregate_modification) { + bool ret = nem_transaction_create_aggregate_modification( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, aggregate_modification->modifications_count, + aggregate_modification->relative_change != 0); + if (!ret) return false; + + for (size_t i = 0; i < aggregate_modification->modifications_count; i++) { + const NEMCosignatoryModification *modification = + &aggregate_modification->modifications[i]; + + ret = nem_transaction_write_cosignatory_modification( + context, modification->type, modification->public_key.bytes); + if (!ret) return false; + } + + if (aggregate_modification->relative_change) { + ret = nem_transaction_write_minimum_cosignatories( + context, aggregate_modification->relative_change); + if (!ret) return false; + } + + return true; } -bool nem_askImportanceTransfer(const NEMTransactionCommon *common, const NEMImportanceTransfer *importance_transfer, const char *desc) { - layoutDialogSwipe(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - importance_transfer->mode == NEMImportanceTransferMode_ImportanceTransfer_Activate ? _("Activate remote") : _("Deactivate remote"), - _("harvesting?"), - NULL, - NULL, - NULL, - NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL, 0); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - return false; - } - - return true; +bool nem_askImportanceTransfer(const NEMTransactionCommon *common, + const NEMImportanceTransfer *importance_transfer, + const char *desc) { + layoutDialogSwipe( + &bmp_icon_question, _("Cancel"), _("Next"), desc, + importance_transfer->mode == + NEMImportanceTransferMode_ImportanceTransfer_Activate + ? _("Activate remote") + : _("Deactivate remote"), + _("harvesting?"), NULL, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL, + 0); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + return false; + } + + return true; } -bool nem_fsmImportanceTransfer(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMImportanceTransfer *importance_transfer) { - return nem_transaction_create_importance_transfer(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - importance_transfer->mode, - importance_transfer->public_key.bytes); +bool nem_fsmImportanceTransfer( + nem_transaction_ctx *context, const NEMTransactionCommon *common, + const NEMImportanceTransfer *importance_transfer) { + return nem_transaction_create_importance_transfer( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, importance_transfer->mode, + importance_transfer->public_key.bytes); } -bool nem_askMultisig(const char *address, const char *desc, bool cosigning, uint64_t fee) { - layoutNEMDialog(&bmp_icon_question, - _("Cancel"), - _("Next"), - desc, - cosigning ? _("Cosign transaction for") : _("Initiate transaction for"), - address); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - layoutNEMNetworkFee(desc, false, _("Confirm multisig fee"), fee, NULL, 0); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return false; - } - - return true; +bool nem_askMultisig(const char *address, const char *desc, bool cosigning, + uint64_t fee) { + layoutNEMDialog( + &bmp_icon_question, _("Cancel"), _("Next"), desc, + cosigning ? _("Cosign transaction for") : _("Initiate transaction for"), + address); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + layoutNEMNetworkFee(desc, false, _("Confirm multisig fee"), fee, NULL, 0); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return false; + } + + return true; } -bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *common, const nem_transaction_ctx *inner, bool cosigning) { - bool ret; - if (cosigning) { - ret = nem_transaction_create_multisig_signature(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - inner); - } else { - ret = nem_transaction_create_multisig(context, - common->network, - common->timestamp, - NULL, - common->fee, - common->deadline, - inner); - } - - if (!ret) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to create multisig transaction")); - return false; - } - - return true; +bool nem_fsmMultisig(nem_transaction_ctx *context, + const NEMTransactionCommon *common, + const nem_transaction_ctx *inner, bool cosigning) { + bool ret; + if (cosigning) { + ret = nem_transaction_create_multisig_signature( + context, common->network, common->timestamp, NULL, common->fee, + common->deadline, inner); + } else { + ret = nem_transaction_create_multisig(context, common->network, + common->timestamp, NULL, common->fee, + common->deadline, inner); + } + + if (!ret) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to create multisig transaction")); + return false; + } + + return true; } -const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, const char *mosaic, uint8_t network) { - for (size_t i = 0; i < NEM_MOSAIC_DEFINITIONS_COUNT; i++) { - const NEMMosaicDefinition *definition = &NEM_MOSAIC_DEFINITIONS[i]; +const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, + const char *mosaic, + uint8_t network) { + for (size_t i = 0; i < NEM_MOSAIC_DEFINITIONS_COUNT; i++) { + const NEMMosaicDefinition *definition = &NEM_MOSAIC_DEFINITIONS[i]; - if (nem_mosaicMatches(definition, namespace, mosaic, network)) { - return definition; - } - } + if (nem_mosaicMatches(definition, namespace, mosaic, network)) { + return definition; + } + } - return NULL; + return NULL; } -static inline size_t format_amount(const NEMMosaicDefinition *definition, const bignum256 *amnt, const bignum256 *multiplier, int divisor, char *str_out, size_t size) { - bignum256 val; - memcpy(&val, amnt, sizeof(bignum256)); - - if (multiplier) { - bn_multiply(multiplier, &val, &secp256k1.prime); - divisor += NEM_MOSAIC_DEFINITION_XEM->divisibility; - } - - return bn_format(&val, - NULL, - definition && definition->has_ticker ? definition->ticker : NULL, - definition && definition->has_divisibility ? definition->divisibility : 0, - -divisor, - false, - str_out, - size); +static inline size_t format_amount(const NEMMosaicDefinition *definition, + const bignum256 *amnt, + const bignum256 *multiplier, int divisor, + char *str_out, size_t size) { + bignum256 val; + memcpy(&val, amnt, sizeof(bignum256)); + + if (multiplier) { + bn_multiply(multiplier, &val, &secp256k1.prime); + divisor += NEM_MOSAIC_DEFINITION_XEM->divisibility; + } + + return bn_format( + &val, NULL, + definition && definition->has_ticker ? definition->ticker : NULL, + definition && definition->has_divisibility ? definition->divisibility : 0, + -divisor, false, str_out, size); } size_t nem_canonicalizeMosaics(NEMMosaic *mosaics, size_t mosaics_count) { - if (mosaics_count <= 1) { - return mosaics_count; - } + if (mosaics_count <= 1) { + return mosaics_count; + } - size_t actual_count = 0; + size_t actual_count = 0; - bool skip[mosaics_count]; - memzero(skip, sizeof(skip)); + bool skip[mosaics_count]; + memzero(skip, sizeof(skip)); - // Merge duplicates - for (size_t i = 0; i < mosaics_count; i++) { - if (skip[i]) continue; + // Merge duplicates + for (size_t i = 0; i < mosaics_count; i++) { + if (skip[i]) continue; - NEMMosaic *mosaic = &mosaics[actual_count]; + NEMMosaic *mosaic = &mosaics[actual_count]; - if (actual_count++ != i) { - memcpy(mosaic, &mosaics[i], sizeof(NEMMosaic)); - } + if (actual_count++ != i) { + memcpy(mosaic, &mosaics[i], sizeof(NEMMosaic)); + } - for (size_t j = i + 1; j < mosaics_count; j++) { - if (skip[j]) continue; + for (size_t j = i + 1; j < mosaics_count; j++) { + if (skip[j]) continue; - const NEMMosaic *new_mosaic = &mosaics[j]; + const NEMMosaic *new_mosaic = &mosaics[j]; - if (nem_mosaicCompare(mosaic, new_mosaic) == 0) { - skip[j] = true; - mosaic->quantity += new_mosaic->quantity; - } - } - } + if (nem_mosaicCompare(mosaic, new_mosaic) == 0) { + skip[j] = true; + mosaic->quantity += new_mosaic->quantity; + } + } + } - NEMMosaic temp; + NEMMosaic temp; - // Sort mosaics - for (size_t i = 0; i < actual_count - 1; i++) { - NEMMosaic *a = &mosaics[i]; + // Sort mosaics + for (size_t i = 0; i < actual_count - 1; i++) { + NEMMosaic *a = &mosaics[i]; - for (size_t j = i + 1; j < actual_count; j++) { - NEMMosaic *b = &mosaics[j]; + for (size_t j = i + 1; j < actual_count; j++) { + NEMMosaic *b = &mosaics[j]; - if (nem_mosaicCompare(a, b) > 0) { - memcpy(&temp, a, sizeof(NEMMosaic)); - memcpy(a, b, sizeof(NEMMosaic)); - memcpy(b, &temp, sizeof(NEMMosaic)); - } - } - } + if (nem_mosaicCompare(a, b) > 0) { + memcpy(&temp, a, sizeof(NEMMosaic)); + memcpy(a, b, sizeof(NEMMosaic)); + memcpy(b, &temp, sizeof(NEMMosaic)); + } + } + } - return actual_count; + return actual_count; } -void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size) { - bignum256 amnt; - bn_read_uint64(quantity, &amnt); +void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, + uint64_t quantity, const bignum256 *multiplier, + char *str_out, size_t size) { + bignum256 amnt; + bn_read_uint64(quantity, &amnt); - format_amount(definition, &amnt, multiplier, 0, str_out, size); + format_amount(definition, &amnt, multiplier, 0, str_out, size); } -bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, uint8_t network, char *str_out, size_t size) { - if (!definition->has_levy || !definition->has_fee) { - return false; - } +bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, + uint64_t quantity, const bignum256 *multiplier, + uint8_t network, char *str_out, size_t size) { + if (!definition->has_levy || !definition->has_fee) { + return false; + } - bignum256 amnt, fee; - bn_read_uint64(quantity, &amnt); - bn_read_uint64(definition->fee, &fee); + bignum256 amnt, fee; + bn_read_uint64(quantity, &amnt); + bn_read_uint64(definition->fee, &fee); - const NEMMosaicDefinition *mosaic = nem_mosaicByName(definition->levy_namespace, definition->levy_mosaic, network); + const NEMMosaicDefinition *mosaic = nem_mosaicByName( + definition->levy_namespace, definition->levy_mosaic, network); - switch (definition->levy) { - case NEMMosaicLevy_MosaicLevy_Absolute: - return format_amount(mosaic, &fee, NULL, 0, str_out, size); + switch (definition->levy) { + case NEMMosaicLevy_MosaicLevy_Absolute: + return format_amount(mosaic, &fee, NULL, 0, str_out, size); - case NEMMosaicLevy_MosaicLevy_Percentile: - bn_multiply(&fee, &amnt, &secp256k1.prime); - return format_amount(mosaic, &amnt, multiplier, NEM_LEVY_PERCENTILE_DIVISOR, str_out, size); + case NEMMosaicLevy_MosaicLevy_Percentile: + bn_multiply(&fee, &amnt, &secp256k1.prime); + return format_amount(mosaic, &amnt, multiplier, + NEM_LEVY_PERCENTILE_DIVISOR, str_out, size); - default: - return false; - } + default: + return false; + } } diff --git a/firmware/nem2.h b/firmware/nem2.h index 13f0390fa..a4a9597a1 100644 --- a/firmware/nem2.h +++ b/firmware/nem2.h @@ -29,82 +29,125 @@ const char *nem_validate_common(NEMTransactionCommon *common, bool inner); const char *nem_validate_transfer(const NEMTransfer *transfer, uint8_t network); -const char *nem_validate_provision_namespace(const NEMProvisionNamespace *provision_namespace, uint8_t network); -const char *nem_validate_mosaic_creation(const NEMMosaicCreation *mosaic_creation, uint8_t network); -const char *nem_validate_supply_change(const NEMMosaicSupplyChange *supply_change); -const char *nem_validate_aggregate_modification(const NEMAggregateModification *aggregate_modification, bool creation); -const char *nem_validate_importance_transfer(const NEMImportanceTransfer *importance_transfer); - -bool nem_askTransfer(const NEMTransactionCommon *common, const NEMTransfer *transfer, const char *desc); -bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, const NEMTransactionCommon *common, const NEMTransfer *transfer); - -bool nem_askProvisionNamespace(const NEMTransactionCommon *common, const NEMProvisionNamespace *provision_namespace, const char *desc); -bool nem_fsmProvisionNamespace(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMProvisionNamespace *provision_namespace); - -bool nem_askMosaicCreation(const NEMTransactionCommon *common, const NEMMosaicCreation *mosaic_creation, const char *desc, const char *address); -bool nem_fsmMosaicCreation(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMMosaicCreation *mosaic_creation); - -bool nem_askSupplyChange(const NEMTransactionCommon *common, const NEMMosaicSupplyChange *supply_change, const char *desc); -bool nem_fsmSupplyChange(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMMosaicSupplyChange *supply_change); - -bool nem_askAggregateModification(const NEMTransactionCommon *common, const NEMAggregateModification *aggregate_modification, const char *desc, bool creation); -bool nem_fsmAggregateModification(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMAggregateModification *aggregate_modification); - -bool nem_askImportanceTransfer(const NEMTransactionCommon *common, const NEMImportanceTransfer *importance_transfer, const char *desc); -bool nem_fsmImportanceTransfer(nem_transaction_ctx *context, const NEMTransactionCommon *common, const NEMImportanceTransfer *importance_transfer); - -bool nem_askMultisig(const char *address, const char *desc, bool cosigning, uint64_t fee); -bool nem_fsmMultisig(nem_transaction_ctx *context, const NEMTransactionCommon *common, const nem_transaction_ctx *inner, bool cosigning); - -const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, const char *mosaic, uint8_t network); +const char *nem_validate_provision_namespace( + const NEMProvisionNamespace *provision_namespace, uint8_t network); +const char *nem_validate_mosaic_creation( + const NEMMosaicCreation *mosaic_creation, uint8_t network); +const char *nem_validate_supply_change( + const NEMMosaicSupplyChange *supply_change); +const char *nem_validate_aggregate_modification( + const NEMAggregateModification *aggregate_modification, bool creation); +const char *nem_validate_importance_transfer( + const NEMImportanceTransfer *importance_transfer); + +bool nem_askTransfer(const NEMTransactionCommon *common, + const NEMTransfer *transfer, const char *desc); +bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node, + const NEMTransactionCommon *common, + const NEMTransfer *transfer); + +bool nem_askProvisionNamespace(const NEMTransactionCommon *common, + const NEMProvisionNamespace *provision_namespace, + const char *desc); +bool nem_fsmProvisionNamespace( + nem_transaction_ctx *context, const NEMTransactionCommon *common, + const NEMProvisionNamespace *provision_namespace); + +bool nem_askMosaicCreation(const NEMTransactionCommon *common, + const NEMMosaicCreation *mosaic_creation, + const char *desc, const char *address); +bool nem_fsmMosaicCreation(nem_transaction_ctx *context, + const NEMTransactionCommon *common, + const NEMMosaicCreation *mosaic_creation); + +bool nem_askSupplyChange(const NEMTransactionCommon *common, + const NEMMosaicSupplyChange *supply_change, + const char *desc); +bool nem_fsmSupplyChange(nem_transaction_ctx *context, + const NEMTransactionCommon *common, + const NEMMosaicSupplyChange *supply_change); + +bool nem_askAggregateModification( + const NEMTransactionCommon *common, + const NEMAggregateModification *aggregate_modification, const char *desc, + bool creation); +bool nem_fsmAggregateModification( + nem_transaction_ctx *context, const NEMTransactionCommon *common, + const NEMAggregateModification *aggregate_modification); + +bool nem_askImportanceTransfer(const NEMTransactionCommon *common, + const NEMImportanceTransfer *importance_transfer, + const char *desc); +bool nem_fsmImportanceTransfer( + nem_transaction_ctx *context, const NEMTransactionCommon *common, + const NEMImportanceTransfer *importance_transfer); + +bool nem_askMultisig(const char *address, const char *desc, bool cosigning, + uint64_t fee); +bool nem_fsmMultisig(nem_transaction_ctx *context, + const NEMTransactionCommon *common, + const nem_transaction_ctx *inner, bool cosigning); + +const NEMMosaicDefinition *nem_mosaicByName(const char *namespace, + const char *mosaic, + uint8_t network); size_t nem_canonicalizeMosaics(NEMMosaic *mosaics, size_t mosaics_count); -void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, char *str_out, size_t size); -bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, uint64_t quantity, const bignum256 *multiplier, uint8_t network, char *str_out, size_t size); - -static inline void nem_mosaicFormatName(const char *namespace, const char *mosaic, char *str_out, size_t size) { - strlcpy(str_out, namespace, size); - strlcat(str_out, ".", size); - strlcat(str_out, mosaic, size); +void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition, + uint64_t quantity, const bignum256 *multiplier, + char *str_out, size_t size); +bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition, + uint64_t quantity, const bignum256 *multiplier, + uint8_t network, char *str_out, size_t size); + +static inline void nem_mosaicFormatName(const char *namespace, + const char *mosaic, char *str_out, + size_t size) { + strlcpy(str_out, namespace, size); + strlcat(str_out, ".", size); + strlcat(str_out, mosaic, size); } -static inline bool nem_mosaicMatches(const NEMMosaicDefinition *definition, const char *namespace, const char *mosaic, uint8_t network) { - if (strcmp(namespace, definition->namespace) == 0 && strcmp(mosaic, definition->mosaic) == 0) { - if (definition->networks_count == 0) { - return true; - } - - for (size_t i = 0; i < definition->networks_count; i++) { - if (definition->networks[i] == network) { - return true; - } - } - } - - return false; +static inline bool nem_mosaicMatches(const NEMMosaicDefinition *definition, + const char *namespace, const char *mosaic, + uint8_t network) { + if (strcmp(namespace, definition->namespace) == 0 && + strcmp(mosaic, definition->mosaic) == 0) { + if (definition->networks_count == 0) { + return true; + } + + for (size_t i = 0; i < definition->networks_count; i++) { + if (definition->networks[i] == network) { + return true; + } + } + } + + return false; } static inline int nem_mosaicCompare(const NEMMosaic *a, const NEMMosaic *b) { - size_t namespace_length = strlen(a->namespace); + size_t namespace_length = strlen(a->namespace); - // Ensure that strlen(a->namespace) <= strlen(b->namespace) - if (namespace_length > strlen(b->namespace)) { - return -nem_mosaicCompare(b, a); - } + // Ensure that strlen(a->namespace) <= strlen(b->namespace) + if (namespace_length > strlen(b->namespace)) { + return -nem_mosaicCompare(b, a); + } - int r = strncmp(a->namespace, b->namespace, namespace_length); + int r = strncmp(a->namespace, b->namespace, namespace_length); - if (r == 0 && b->namespace[namespace_length] != '\0') { - // The next character would be the separator - r = (':' - b->namespace[namespace_length]); - } + if (r == 0 && b->namespace[namespace_length] != '\0') { + // The next character would be the separator + r = (':' - b->namespace[namespace_length]); + } - if (r == 0) { - // Finally compare the mosaic - r = strcmp(a->mosaic, b->mosaic); - } + if (r == 0) { + // Finally compare the mosaic + r = strcmp(a->mosaic, b->mosaic); + } - return r; + return r; } #endif diff --git a/firmware/otp.c b/firmware/otp.c index 74810f668..195b97769 100644 --- a/firmware/otp.c +++ b/firmware/otp.c @@ -17,49 +17,51 @@ * along with this library. If not, see . */ -#include #include "otp.h" +#include -#define FLASH_OTP_BASE 0x1FFF7800U -#define FLASH_OTP_LOCK_BASE 0x1FFF7A00U +#define FLASH_OTP_BASE 0x1FFF7800U +#define FLASH_OTP_LOCK_BASE 0x1FFF7A00U -bool flash_otp_is_locked(uint8_t block) -{ - return 0x00 == *(volatile uint8_t *)(FLASH_OTP_LOCK_BASE + block); +bool flash_otp_is_locked(uint8_t block) { + return 0x00 == *(volatile uint8_t *)(FLASH_OTP_LOCK_BASE + block); } -bool flash_otp_lock(uint8_t block) -{ - if (block >= FLASH_OTP_NUM_BLOCKS) { - return false; - } - flash_unlock(); - flash_program_byte(FLASH_OTP_LOCK_BASE + block, 0x00); - flash_lock(); - return true; +bool flash_otp_lock(uint8_t block) { + if (block >= FLASH_OTP_NUM_BLOCKS) { + return false; + } + flash_unlock(); + flash_program_byte(FLASH_OTP_LOCK_BASE + block, 0x00); + flash_lock(); + return true; } -bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen) -{ - if (block >= FLASH_OTP_NUM_BLOCKS || offset + datalen > FLASH_OTP_BLOCK_SIZE) { - return false; - } - for (uint8_t i = 0; i < datalen; i++) { - data[i] = *(volatile uint8_t *)(FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i); - } - return true; +bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, + uint8_t datalen) { + if (block >= FLASH_OTP_NUM_BLOCKS || + offset + datalen > FLASH_OTP_BLOCK_SIZE) { + return false; + } + for (uint8_t i = 0; i < datalen; i++) { + data[i] = *(volatile uint8_t *)(FLASH_OTP_BASE + + block * FLASH_OTP_BLOCK_SIZE + offset + i); + } + return true; } -bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, uint8_t datalen) -{ - if (block >= FLASH_OTP_NUM_BLOCKS || offset + datalen > FLASH_OTP_BLOCK_SIZE) { - return false; - } - flash_unlock(); - for (uint8_t i = 0; i < datalen; i++) { - uint32_t address = FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i; - flash_program_byte(address, data[i]); - } - flash_lock(); - return true; +bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, + uint8_t datalen) { + if (block >= FLASH_OTP_NUM_BLOCKS || + offset + datalen > FLASH_OTP_BLOCK_SIZE) { + return false; + } + flash_unlock(); + for (uint8_t i = 0; i < datalen; i++) { + uint32_t address = + FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i; + flash_program_byte(address, data[i]); + } + flash_lock(); + return true; } diff --git a/firmware/otp.h b/firmware/otp.h index 471e230c0..d329f9425 100644 --- a/firmware/otp.h +++ b/firmware/otp.h @@ -20,17 +20,19 @@ #ifndef __OTP_H__ #define __OTP_H__ -#include #include +#include -#define FLASH_OTP_NUM_BLOCKS 16 -#define FLASH_OTP_BLOCK_SIZE 32 +#define FLASH_OTP_NUM_BLOCKS 16 +#define FLASH_OTP_BLOCK_SIZE 32 -#define FLASH_OTP_BLOCK_RANDOMNESS 3 +#define FLASH_OTP_BLOCK_RANDOMNESS 3 bool flash_otp_is_locked(uint8_t block); bool flash_otp_lock(uint8_t block); -bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, uint8_t datalen); -bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, uint8_t datalen); +bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data, + uint8_t datalen); +bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data, + uint8_t datalen); #endif diff --git a/firmware/pinmatrix.c b/firmware/pinmatrix.c index 94f8a06cc..f45fa2024 100644 --- a/firmware/pinmatrix.c +++ b/firmware/pinmatrix.c @@ -19,64 +19,60 @@ #include -#include "pinmatrix.h" #include "layout2.h" #include "oled.h" +#include "pinmatrix.h" #include "rng.h" static char pinmatrix_perm[10] = "XXXXXXXXX"; -void pinmatrix_draw(const char *text) -{ - const BITMAP *bmp_digits[10] = { - &bmp_digit0, &bmp_digit1, &bmp_digit2, &bmp_digit3, &bmp_digit4, - &bmp_digit5, &bmp_digit6, &bmp_digit7, &bmp_digit8, &bmp_digit9, - }; - layoutSwipe(); - const int w = bmp_digit0.width, h = bmp_digit0.height, pad = 2; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - // use (2 - j) instead of j to achieve 789456123 layout - int k = pinmatrix_perm[i + (2 - j) * 3] - '0'; - if (text) { - oledDrawStringCenter(OLED_WIDTH / 2, 0, text, FONT_STANDARD); - } - oledDrawBitmap((OLED_WIDTH - 3 * w - 2 * pad) / 2 + i * (w + pad), OLED_HEIGHT - 3 * h - 2 * pad + j * (h + pad), bmp_digits[k]); - } - } - oledRefresh(); +void pinmatrix_draw(const char *text) { + const BITMAP *bmp_digits[10] = { + &bmp_digit0, &bmp_digit1, &bmp_digit2, &bmp_digit3, &bmp_digit4, + &bmp_digit5, &bmp_digit6, &bmp_digit7, &bmp_digit8, &bmp_digit9, + }; + layoutSwipe(); + const int w = bmp_digit0.width, h = bmp_digit0.height, pad = 2; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + // use (2 - j) instead of j to achieve 789456123 layout + int k = pinmatrix_perm[i + (2 - j) * 3] - '0'; + if (text) { + oledDrawStringCenter(OLED_WIDTH / 2, 0, text, FONT_STANDARD); + } + oledDrawBitmap((OLED_WIDTH - 3 * w - 2 * pad) / 2 + i * (w + pad), + OLED_HEIGHT - 3 * h - 2 * pad + j * (h + pad), + bmp_digits[k]); + } + } + oledRefresh(); } -void pinmatrix_start(const char *text) -{ - for (int i = 0; i < 9; i++) { - pinmatrix_perm[i] = '1' + i; - } - pinmatrix_perm[9] = 0; - random_permute(pinmatrix_perm, 9); - pinmatrix_draw(text); +void pinmatrix_start(const char *text) { + for (int i = 0; i < 9; i++) { + pinmatrix_perm[i] = '1' + i; + } + pinmatrix_perm[9] = 0; + random_permute(pinmatrix_perm, 9); + pinmatrix_draw(text); } -void pinmatrix_done(char *pin) -{ - int k, i = 0; - while (pin && pin[i]) { - k = pin[i] - '1'; - if (k >= 0 && k <= 8) { - pin[i] = pinmatrix_perm[k]; - } else { - pin[i] = 'X'; - } - i++; - } - memset(pinmatrix_perm, 'X', sizeof(pinmatrix_perm) - 1); +void pinmatrix_done(char *pin) { + int k, i = 0; + while (pin && pin[i]) { + k = pin[i] - '1'; + if (k >= 0 && k <= 8) { + pin[i] = pinmatrix_perm[k]; + } else { + pin[i] = 'X'; + } + i++; + } + memset(pinmatrix_perm, 'X', sizeof(pinmatrix_perm) - 1); } #if DEBUG_LINK -const char *pinmatrix_get(void) -{ - return pinmatrix_perm; -} +const char *pinmatrix_get(void) { return pinmatrix_perm; } #endif diff --git a/firmware/protect.c b/firmware/protect.c index 548504b8b..7617d2ab2 100644 --- a/firmware/protect.c +++ b/firmware/protect.c @@ -18,296 +18,303 @@ */ #include "protect.h" +#include "buttons.h" #include "config.h" +#include "debug.h" +#include "fsm.h" +#include "gettext.h" +#include "layout2.h" #include "memory.h" +#include "memzero.h" #include "messages.h" -#include "usb.h" +#include "messages.pb.h" #include "oled.h" -#include "buttons.h" #include "pinmatrix.h" -#include "fsm.h" -#include "layout2.h" +#include "usb.h" #include "util.h" -#include "debug.h" -#include "gettext.h" -#include "memzero.h" -#include "messages.pb.h" #define MAX_WRONG_PINS 15 bool protectAbortedByCancel = false; bool protectAbortedByInitialize = false; -bool protectButton(ButtonRequestType type, bool confirm_only) -{ - ButtonRequest resp; - bool result = false; - bool acked = false; +bool protectButton(ButtonRequestType type, bool confirm_only) { + ButtonRequest resp; + bool result = false; + bool acked = false; #if DEBUG_LINK - bool debug_decided = false; + bool debug_decided = false; #endif - memzero(&resp, sizeof(ButtonRequest)); - resp.has_code = true; - resp.code = type; - usbTiny(1); - buttonUpdate(); // Clear button state - msg_write(MessageType_MessageType_ButtonRequest, &resp); - - for (;;) { - usbPoll(); - - // check for ButtonAck - if (msg_tiny_id == MessageType_MessageType_ButtonAck) { - msg_tiny_id = 0xFFFF; - acked = true; - } - - // button acked - check buttons - if (acked) { - usbSleep(5); - buttonUpdate(); - if (button.YesUp) { - result = true; - break; - } - if (!confirm_only && button.NoUp) { - result = false; - break; - } - } - - // check for Cancel / Initialize - protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); - protectAbortedByInitialize = (msg_tiny_id == MessageType_MessageType_Initialize); - if (protectAbortedByCancel || protectAbortedByInitialize) { - msg_tiny_id = 0xFFFF; - result = false; - break; - } + memzero(&resp, sizeof(ButtonRequest)); + resp.has_code = true; + resp.code = type; + usbTiny(1); + buttonUpdate(); // Clear button state + msg_write(MessageType_MessageType_ButtonRequest, &resp); + + for (;;) { + usbPoll(); + + // check for ButtonAck + if (msg_tiny_id == MessageType_MessageType_ButtonAck) { + msg_tiny_id = 0xFFFF; + acked = true; + } + + // button acked - check buttons + if (acked) { + usbSleep(5); + buttonUpdate(); + if (button.YesUp) { + result = true; + break; + } + if (!confirm_only && button.NoUp) { + result = false; + break; + } + } + + // check for Cancel / Initialize + protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); + protectAbortedByInitialize = + (msg_tiny_id == MessageType_MessageType_Initialize); + if (protectAbortedByCancel || protectAbortedByInitialize) { + msg_tiny_id = 0xFFFF; + result = false; + break; + } #if DEBUG_LINK - // check DebugLink - if (msg_tiny_id == MessageType_MessageType_DebugLinkDecision) { - msg_tiny_id = 0xFFFF; - DebugLinkDecision *dld = (DebugLinkDecision *)msg_tiny; - result = dld->yes_no; - debug_decided = true; - } - - if (acked && debug_decided) { - break; - } - - if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) { - msg_tiny_id = 0xFFFF; - fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny); - } + // check DebugLink + if (msg_tiny_id == MessageType_MessageType_DebugLinkDecision) { + msg_tiny_id = 0xFFFF; + DebugLinkDecision *dld = (DebugLinkDecision *)msg_tiny; + result = dld->yes_no; + debug_decided = true; + } + + if (acked && debug_decided) { + break; + } + + if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) { + msg_tiny_id = 0xFFFF; + fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny); + } #endif - } + } - usbTiny(0); + usbTiny(0); - return result; + return result; } -const char *requestPin(PinMatrixRequestType type, const char *text) -{ - PinMatrixRequest resp; - memzero(&resp, sizeof(PinMatrixRequest)); - resp.has_type = true; - resp.type = type; - usbTiny(1); - msg_write(MessageType_MessageType_PinMatrixRequest, &resp); - pinmatrix_start(text); - for (;;) { - usbPoll(); - if (msg_tiny_id == MessageType_MessageType_PinMatrixAck) { - msg_tiny_id = 0xFFFF; - PinMatrixAck *pma = (PinMatrixAck *)msg_tiny; - pinmatrix_done(pma->pin); // convert via pinmatrix - usbTiny(0); - return pma->pin; - } - // check for Cancel / Initialize - protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); - protectAbortedByInitialize = (msg_tiny_id == MessageType_MessageType_Initialize); - if (protectAbortedByCancel || protectAbortedByInitialize) { - pinmatrix_done(0); - msg_tiny_id = 0xFFFF; - usbTiny(0); - return 0; - } +const char *requestPin(PinMatrixRequestType type, const char *text) { + PinMatrixRequest resp; + memzero(&resp, sizeof(PinMatrixRequest)); + resp.has_type = true; + resp.type = type; + usbTiny(1); + msg_write(MessageType_MessageType_PinMatrixRequest, &resp); + pinmatrix_start(text); + for (;;) { + usbPoll(); + if (msg_tiny_id == MessageType_MessageType_PinMatrixAck) { + msg_tiny_id = 0xFFFF; + PinMatrixAck *pma = (PinMatrixAck *)msg_tiny; + pinmatrix_done(pma->pin); // convert via pinmatrix + usbTiny(0); + return pma->pin; + } + // check for Cancel / Initialize + protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); + protectAbortedByInitialize = + (msg_tiny_id == MessageType_MessageType_Initialize); + if (protectAbortedByCancel || protectAbortedByInitialize) { + pinmatrix_done(0); + msg_tiny_id = 0xFFFF; + usbTiny(0); + return 0; + } #if DEBUG_LINK - if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) { - msg_tiny_id = 0xFFFF; - fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny); - } + if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) { + msg_tiny_id = 0xFFFF; + fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny); + } #endif - } + } } -secbool protectPinUiCallback(uint32_t wait, uint32_t progress, const char* message) -{ - // Convert wait to secstr string. - char secstrbuf[] = _("________0 seconds"); - char *secstr = secstrbuf + 9; - uint32_t secs = wait; - do { - secstr--; - *secstr = (secs % 10) + '0'; - secs /= 10; - } while (secs > 0 && secstr >= secstrbuf); - if (wait == 1) { - // Change "seconds" to "second". - secstrbuf[16] = 0; - } - oledClear(); - oledDrawStringCenter(OLED_WIDTH / 2, 0 * 9, message, FONT_STANDARD); - oledDrawStringCenter(OLED_WIDTH / 2, 2 * 9, _("Please wait"), FONT_STANDARD); - oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD); - oledDrawStringCenter(OLED_WIDTH / 2, 4 * 9, _("to continue ..."), FONT_STANDARD); - // progressbar - oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1); - oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0); - progress = progress * (OLED_WIDTH - 4) / 1000; - if (progress > OLED_WIDTH - 4) { - progress = OLED_WIDTH - 4; - } - oledBox(2, OLED_HEIGHT - 6, 1 + progress, OLED_HEIGHT - 3, 1); - oledRefresh(); - // Check for Cancel / Initialize. - protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); - protectAbortedByInitialize = (msg_tiny_id == MessageType_MessageType_Initialize); - if (protectAbortedByCancel || protectAbortedByInitialize) { - msg_tiny_id = 0xFFFF; - usbTiny(0); - fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); - return sectrue; - } - - return secfalse; +secbool protectPinUiCallback(uint32_t wait, uint32_t progress, + const char *message) { + // Convert wait to secstr string. + char secstrbuf[] = _("________0 seconds"); + char *secstr = secstrbuf + 9; + uint32_t secs = wait; + do { + secstr--; + *secstr = (secs % 10) + '0'; + secs /= 10; + } while (secs > 0 && secstr >= secstrbuf); + if (wait == 1) { + // Change "seconds" to "second". + secstrbuf[16] = 0; + } + oledClear(); + oledDrawStringCenter(OLED_WIDTH / 2, 0 * 9, message, FONT_STANDARD); + oledDrawStringCenter(OLED_WIDTH / 2, 2 * 9, _("Please wait"), FONT_STANDARD); + oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD); + oledDrawStringCenter(OLED_WIDTH / 2, 4 * 9, _("to continue ..."), + FONT_STANDARD); + // progressbar + oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1); + oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0); + progress = progress * (OLED_WIDTH - 4) / 1000; + if (progress > OLED_WIDTH - 4) { + progress = OLED_WIDTH - 4; + } + oledBox(2, OLED_HEIGHT - 6, 1 + progress, OLED_HEIGHT - 3, 1); + oledRefresh(); + // Check for Cancel / Initialize. + protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); + protectAbortedByInitialize = + (msg_tiny_id == MessageType_MessageType_Initialize); + if (protectAbortedByCancel || protectAbortedByInitialize) { + msg_tiny_id = 0xFFFF; + usbTiny(0); + fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); + return sectrue; + } + + return secfalse; } -bool protectPin(bool use_cached) -{ - if (use_cached && session_isUnlocked()) { - return true; - } - - const char *pin = ""; - if (config_hasPin()) { - pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:")); - if (!pin) { - fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); - return false; - } - } - - bool ret = config_unlock(pin); - if (!ret) { - fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); - } - return ret; +bool protectPin(bool use_cached) { + if (use_cached && session_isUnlocked()) { + return true; + } + + const char *pin = ""; + if (config_hasPin()) { + pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, + _("Please enter current PIN:")); + if (!pin) { + fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); + return false; + } + } + + bool ret = config_unlock(pin); + if (!ret) { + fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); + } + return ret; } -bool protectChangePin(bool removal) -{ - static CONFIDENTIAL char old_pin[MAX_PIN_LEN + 1] = ""; - static CONFIDENTIAL char new_pin[MAX_PIN_LEN + 1] = ""; - const char* pin = NULL; - - if (config_hasPin()) { - pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, _("Please enter current PIN:")); - if (pin == NULL) { - fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); - return false; - } - - // If removing, defer the check to config_changePin(). - if (!removal) { - usbTiny(1); - bool ret = config_unlock(pin); - usbTiny(0); - if (ret == false) { - fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); - return false; - } - } - - strlcpy(old_pin, pin, sizeof(old_pin)); - } - - if (!removal) { - pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst, _("Please enter new PIN:")); - if (pin == NULL) { - memzero(old_pin, sizeof(old_pin)); - fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); - return false; - } - strlcpy(new_pin, pin, sizeof(new_pin)); - - pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, _("Please re-enter new PIN:")); - if (pin == NULL) { - memzero(old_pin, sizeof(old_pin)); - memzero(new_pin, sizeof(new_pin)); - fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); - return false; - } - - if (strncmp(new_pin, pin, sizeof(new_pin)) != 0) { - memzero(old_pin, sizeof(old_pin)); - memzero(new_pin, sizeof(new_pin)); - fsm_sendFailure(FailureType_Failure_PinMismatch, NULL); - return false; - } - } - - bool ret = config_changePin(old_pin, new_pin); - memzero(old_pin, sizeof(old_pin)); - memzero(new_pin, sizeof(new_pin)); - if (ret == false) { - fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); - } - return ret; +bool protectChangePin(bool removal) { + static CONFIDENTIAL char old_pin[MAX_PIN_LEN + 1] = ""; + static CONFIDENTIAL char new_pin[MAX_PIN_LEN + 1] = ""; + const char *pin = NULL; + + if (config_hasPin()) { + pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, + _("Please enter current PIN:")); + if (pin == NULL) { + fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); + return false; + } + + // If removing, defer the check to config_changePin(). + if (!removal) { + usbTiny(1); + bool ret = config_unlock(pin); + usbTiny(0); + if (ret == false) { + fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); + return false; + } + } + + strlcpy(old_pin, pin, sizeof(old_pin)); + } + + if (!removal) { + pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst, + _("Please enter new PIN:")); + if (pin == NULL) { + memzero(old_pin, sizeof(old_pin)); + fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); + return false; + } + strlcpy(new_pin, pin, sizeof(new_pin)); + + pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, + _("Please re-enter new PIN:")); + if (pin == NULL) { + memzero(old_pin, sizeof(old_pin)); + memzero(new_pin, sizeof(new_pin)); + fsm_sendFailure(FailureType_Failure_PinCancelled, NULL); + return false; + } + + if (strncmp(new_pin, pin, sizeof(new_pin)) != 0) { + memzero(old_pin, sizeof(old_pin)); + memzero(new_pin, sizeof(new_pin)); + fsm_sendFailure(FailureType_Failure_PinMismatch, NULL); + return false; + } + } + + bool ret = config_changePin(old_pin, new_pin); + memzero(old_pin, sizeof(old_pin)); + memzero(new_pin, sizeof(new_pin)); + if (ret == false) { + fsm_sendFailure(FailureType_Failure_PinInvalid, NULL); + } + return ret; } -bool protectPassphrase(void) -{ - bool passphrase_protection = false; - config_getPassphraseProtection(&passphrase_protection); - if (!passphrase_protection || session_isPassphraseCached()) { - return true; - } - - PassphraseRequest resp; - memzero(&resp, sizeof(PassphraseRequest)); - usbTiny(1); - msg_write(MessageType_MessageType_PassphraseRequest, &resp); - - layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter your"), _("passphrase using"), _("the computer's"), _("keyboard."), NULL, NULL); - - 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; - session_cachePassphrase(ppa->has_passphrase ? ppa->passphrase : ""); - result = true; - break; - } - // check for Cancel / Initialize - protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); - protectAbortedByInitialize = (msg_tiny_id == MessageType_MessageType_Initialize); - if (protectAbortedByCancel || protectAbortedByInitialize) { - msg_tiny_id = 0xFFFF; - result = false; - break; - } - } - usbTiny(0); - layoutHome(); - return result; +bool protectPassphrase(void) { + bool passphrase_protection = false; + config_getPassphraseProtection(&passphrase_protection); + if (!passphrase_protection || session_isPassphraseCached()) { + return true; + } + + PassphraseRequest resp; + memzero(&resp, sizeof(PassphraseRequest)); + usbTiny(1); + msg_write(MessageType_MessageType_PassphraseRequest, &resp); + + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter your"), + _("passphrase using"), _("the computer's"), _("keyboard."), + NULL, NULL); + + 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; + session_cachePassphrase(ppa->has_passphrase ? ppa->passphrase : ""); + result = true; + break; + } + // check for Cancel / Initialize + protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel); + protectAbortedByInitialize = + (msg_tiny_id == MessageType_MessageType_Initialize); + if (protectAbortedByCancel || protectAbortedByInitialize) { + msg_tiny_id = 0xFFFF; + result = false; + break; + } + } + usbTiny(0); + layoutHome(); + return result; } diff --git a/firmware/protect.h b/firmware/protect.h index fb269468f..b948d8e6f 100644 --- a/firmware/protect.h +++ b/firmware/protect.h @@ -25,7 +25,8 @@ #include "secbool.h" bool protectButton(ButtonRequestType type, bool confirm_only); -secbool protectPinUiCallback(uint32_t wait, uint32_t progress, const char* message); +secbool protectPinUiCallback(uint32_t wait, uint32_t progress, + const char* message); bool protectPin(bool use_cached); bool protectChangePin(bool removal); bool protectPassphrase(void); diff --git a/firmware/recovery-table.h b/firmware/recovery-table.h index 0efb11455..05cb2334b 100644 --- a/firmware/recovery-table.h +++ b/firmware/recovery-table.h @@ -1,3 +1,4 @@ +// clang-format off /* * This file is part of the TREZOR project, https://trezor.io/ * diff --git a/firmware/recovery.c b/firmware/recovery.c index dd3b7edb0..e9f1c5e0c 100644 --- a/firmware/recovery.c +++ b/firmware/recovery.c @@ -18,21 +18,21 @@ * along with this library. If not, see . */ -#include #include "recovery.h" -#include "fsm.h" +#include +#include "bip39.h" #include "config.h" +#include "fsm.h" +#include "gettext.h" #include "layout2.h" -#include "protect.h" +#include "memzero.h" #include "messages.h" -#include "rng.h" -#include "bip39.h" +#include "messages.pb.h" #include "oled.h" -#include "usb.h" -#include "gettext.h" +#include "protect.h" #include "recovery-table.h" -#include "memzero.h" -#include "messages.pb.h" +#include "rng.h" +#include "usb.h" /* number of words expected in the new seed */ static uint32_t word_count; @@ -112,7 +112,7 @@ static uint8_t word_matrix[9]; * prefix length or table for the very first level, as the prefix length * is always one and there are always nine choices on the second level. */ -#define MASK_IDX(x) ((x) & 0xfff) +#define MASK_IDX(x) ((x)&0xfff) #define TABLE1(x) MASK_IDX(word_table1[x]) #define TABLE2(x) MASK_IDX(word_table2[x]) @@ -122,93 +122,102 @@ static uint8_t word_matrix[9]; * Parameter number gives the number that we should format. */ static void format_number(char *dest, int number) { - if (number < 10) { - dest[0] = ' '; - } else { - dest[0] = '0' + number / 10; - } - dest[1] = '0' + number % 10; - if (number == 1 || number == 21) { - dest[2] = 's'; dest[3] = 't'; - } else if (number == 2 || number == 22) { - dest[2] = 'n'; dest[3] = 'd'; - } else if (number == 3 || number == 23) { - dest[2] = 'r'; dest[3] = 'd'; - } + if (number < 10) { + dest[0] = ' '; + } else { + dest[0] = '0' + number / 10; + } + dest[1] = '0' + number % 10; + if (number == 1 || number == 21) { + dest[2] = 's'; + dest[3] = 't'; + } else if (number == 2 || number == 22) { + dest[2] = 'n'; + dest[3] = 'd'; + } else if (number == 3 || number == 23) { + dest[2] = 'r'; + dest[3] = 'd'; + } } /* Send a request for a new word/matrix code to the PC. */ static void recovery_request(void) { - WordRequest resp; - memzero(&resp, sizeof(WordRequest)); - resp.has_type = true; - resp.type = awaiting_word == 1 ? WordRequestType_WordRequestType_Plain - : (word_index % 4 == 3) ? WordRequestType_WordRequestType_Matrix6 - : WordRequestType_WordRequestType_Matrix9; - msg_write(MessageType_MessageType_WordRequest, &resp); + WordRequest resp; + memzero(&resp, sizeof(WordRequest)); + resp.has_type = true; + resp.type = awaiting_word == 1 + ? WordRequestType_WordRequestType_Plain + : (word_index % 4 == 3) + ? WordRequestType_WordRequestType_Matrix6 + : WordRequestType_WordRequestType_Matrix9; + msg_write(MessageType_MessageType_WordRequest, &resp); } /* Called when the last word was entered. * Check mnemonic and send success/failure. */ static void recovery_done(void) { - char new_mnemonic[MAX_MNEMONIC_LEN + 1] = {0}; - - strlcpy(new_mnemonic, words[0], sizeof(new_mnemonic)); - for (uint32_t i = 1; i < word_count; i++) { - strlcat(new_mnemonic, " ", sizeof(new_mnemonic)); - strlcat(new_mnemonic, words[i], sizeof(new_mnemonic)); - } - if (!enforce_wordlist || mnemonic_check(new_mnemonic)) { - // New mnemonic is valid. - if (!dry_run) { - // Update mnemonic on config. - if (config_setMnemonic(new_mnemonic)) { - if (!enforce_wordlist) { - // not enforcing => mark config as imported - config_setImported(true); - } - fsm_sendSuccess(_("Device recovered")); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to store mnemonic")); - } - memzero(new_mnemonic, sizeof(new_mnemonic)); - } else { - // Inform the user about new mnemonic correctness (as well as whether it is the same as the current one). - bool match = (config_isInitialized() && config_containsMnemonic(new_mnemonic)); - memzero(new_mnemonic, sizeof(new_mnemonic)); - if (match) { - layoutDialog(&bmp_icon_ok, NULL, _("Confirm"), NULL, - _("The seed is valid"), - _("and MATCHES"), - _("the one in the device."), NULL, NULL, NULL); - protectButton(ButtonRequestType_ButtonRequest_Other, true); - fsm_sendSuccess(_("The seed is valid and matches the one in the device")); - } else { - layoutDialog(&bmp_icon_error, NULL, _("Confirm"), NULL, - _("The seed is valid"), - _("but does NOT MATCH"), - _("the one in the device."), NULL, NULL, NULL); - protectButton(ButtonRequestType_ButtonRequest_Other, true); - fsm_sendFailure(FailureType_Failure_DataError, - _("The seed is valid but does not match the one in the device")); - } - } - } else { - // New mnemonic is invalid. - memzero(new_mnemonic, sizeof(new_mnemonic)); - if (!dry_run) { - session_clear(true); - } else { - layoutDialog(&bmp_icon_error, NULL, _("Confirm"), NULL, - _("The seed is"), _("INVALID!"), NULL, NULL, NULL, NULL); - protectButton(ButtonRequestType_ButtonRequest_Other, true); - } - fsm_sendFailure(FailureType_Failure_DataError, _("Invalid seed, are words in correct order?")); - } - awaiting_word = 0; - layoutHome(); + char new_mnemonic[MAX_MNEMONIC_LEN + 1] = {0}; + + strlcpy(new_mnemonic, words[0], sizeof(new_mnemonic)); + for (uint32_t i = 1; i < word_count; i++) { + strlcat(new_mnemonic, " ", sizeof(new_mnemonic)); + strlcat(new_mnemonic, words[i], sizeof(new_mnemonic)); + } + if (!enforce_wordlist || mnemonic_check(new_mnemonic)) { + // New mnemonic is valid. + if (!dry_run) { + // Update mnemonic on config. + if (config_setMnemonic(new_mnemonic)) { + if (!enforce_wordlist) { + // not enforcing => mark config as imported + config_setImported(true); + } + fsm_sendSuccess(_("Device recovered")); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to store mnemonic")); + } + memzero(new_mnemonic, sizeof(new_mnemonic)); + } else { + // Inform the user about new mnemonic correctness (as well as whether it + // is the same as the current one). + bool match = + (config_isInitialized() && config_containsMnemonic(new_mnemonic)); + memzero(new_mnemonic, sizeof(new_mnemonic)); + if (match) { + layoutDialog(&bmp_icon_ok, NULL, _("Confirm"), NULL, + _("The seed is valid"), _("and MATCHES"), + _("the one in the device."), NULL, NULL, NULL); + protectButton(ButtonRequestType_ButtonRequest_Other, true); + fsm_sendSuccess( + _("The seed is valid and matches the one in the device")); + } else { + layoutDialog(&bmp_icon_error, NULL, _("Confirm"), NULL, + _("The seed is valid"), _("but does NOT MATCH"), + _("the one in the device."), NULL, NULL, NULL); + protectButton(ButtonRequestType_ButtonRequest_Other, true); + fsm_sendFailure( + FailureType_Failure_DataError, + _("The seed is valid but does not match the one in the device")); + } + } + } else { + // New mnemonic is invalid. + memzero(new_mnemonic, sizeof(new_mnemonic)); + if (!dry_run) { + session_clear(true); + } else { + layoutDialog(&bmp_icon_error, NULL, _("Confirm"), NULL, _("The seed is"), + _("INVALID!"), NULL, NULL, NULL, NULL); + protectButton(ButtonRequestType_ButtonRequest_Other, true); + } + fsm_sendFailure(FailureType_Failure_DataError, + _("Invalid seed, are words in correct order?")); + } + awaiting_word = 0; + layoutHome(); } /* Helper function for matrix recovery: @@ -222,36 +231,37 @@ static void recovery_done(void) { * memcmp(last, "last + 1", prefixlen) != 0 * first[prefixlen-2] == last[prefixlen-2] except for range WI-Z. */ -static void add_choice(char choice[12], int prefixlen, const char *first, const char *last) { - // assert 1 <= prefixlen <= 4 - char *dest = choice; - for (int i = 0; i < prefixlen; i++) { - *dest++ = toupper((int) first[i]); - } - if (first[0] != last[0]) { - /* special case WI-Z; also used for T-Z, etc. */ - *dest++ = '-'; - *dest++ = toupper((int) last[0]); - } else if (last[prefixlen-1] == first[prefixlen-1]) { - /* single prefix */ - } else if (prefixlen < 3) { - /* AB-AC, etc. */ - *dest++ = '-'; - for (int i = 0; i < prefixlen; i++) { - *dest++ = toupper((int) last[i]); - } - } else { - /* RE[A-M] etc. */ - /* remove last and replace with space */ - dest[-1] = ' '; - if (first[prefixlen - 1]) { - /* handle special case: CAN[-D] */ - *dest++ = toupper((int)first[prefixlen - 1]); - } - *dest++ = '-'; - *dest++ = toupper((int) last[prefixlen - 1]); - } - *dest++ = 0; +static void add_choice(char choice[12], int prefixlen, const char *first, + const char *last) { + // assert 1 <= prefixlen <= 4 + char *dest = choice; + for (int i = 0; i < prefixlen; i++) { + *dest++ = toupper((int)first[i]); + } + if (first[0] != last[0]) { + /* special case WI-Z; also used for T-Z, etc. */ + *dest++ = '-'; + *dest++ = toupper((int)last[0]); + } else if (last[prefixlen - 1] == first[prefixlen - 1]) { + /* single prefix */ + } else if (prefixlen < 3) { + /* AB-AC, etc. */ + *dest++ = '-'; + for (int i = 0; i < prefixlen; i++) { + *dest++ = toupper((int)last[i]); + } + } else { + /* RE[A-M] etc. */ + /* remove last and replace with space */ + dest[-1] = ' '; + if (first[prefixlen - 1]) { + /* handle special case: CAN[-D] */ + *dest++ = toupper((int)first[prefixlen - 1]); + } + *dest++ = '-'; + *dest++ = toupper((int)last[prefixlen - 1]); + } + *dest++ = 0; } /* Helper function for matrix recovery: @@ -259,122 +269,121 @@ static void add_choice(char choice[12], int prefixlen, const char *first, const * use 2x3 layout, otherwise 3x3 layout. Also generates a random * scrambling and stores it in word_matrix. */ -static void display_choices(bool twoColumn, char choices[9][12], int num) -{ - const int nColumns = twoColumn ? 2 : 3; - const int displayedChoices = nColumns * 3; - for (int i = 0; i < displayedChoices; i++) { - word_matrix[i] = i; - } - /* scramble matrix */ - random_permute((char*)word_matrix, displayedChoices); - - if (word_index % 4 == 0) { - char desc[] = "##th word"; - int nr = (word_index / 4) + 1; - format_number(desc, nr); - layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter the"), (nr < 10 ? desc + 1 : desc), _("of your mnemonic"), NULL, NULL, NULL); - } else { - oledBox(0, 27, 127, 63, false); - } - - for (int row = 0; row < 3; row ++) { - int y = 55 - row * 11; - for (int col = 0; col < nColumns; col++) { - int x = twoColumn ? 64 * col + 32 : 42 * col + 22; - int choice = word_matrix[nColumns*row + col]; - const char *text = choice < num ? choices[choice] : "-"; - oledDrawString(x - oledStringWidth(text, FONT_STANDARD)/2, y, text, FONT_STANDARD); - if (twoColumn) { - oledInvert(x - 32 + 1, y - 1, x - 32 + 63 - 1, y + 8); - } else { - oledInvert(x - 22 + 1, y - 1, x - 22 + 41 - 1, y + 8); - } - } - } - oledRefresh(); - - /* avoid picking out of range numbers */ - for (int i = 0; i < displayedChoices; i++) { - if (word_matrix[i] >= num) - word_matrix[i] = 0; - } - /* two column layout: middle column = right column */ - if (twoColumn) { - static const uint8_t twolayout[9] = { 0, 1, 1, 2, 3, 3, 4, 5, 5 }; - for (int i = 8; i >= 2; i--) { - word_matrix[i] = word_matrix[twolayout[i]]; - } - } +static void display_choices(bool twoColumn, char choices[9][12], int num) { + const int nColumns = twoColumn ? 2 : 3; + const int displayedChoices = nColumns * 3; + for (int i = 0; i < displayedChoices; i++) { + word_matrix[i] = i; + } + /* scramble matrix */ + random_permute((char *)word_matrix, displayedChoices); + + if (word_index % 4 == 0) { + char desc[] = "##th word"; + int nr = (word_index / 4) + 1; + format_number(desc, nr); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter the"), + (nr < 10 ? desc + 1 : desc), _("of your mnemonic"), NULL, + NULL, NULL); + } else { + oledBox(0, 27, 127, 63, false); + } + + for (int row = 0; row < 3; row++) { + int y = 55 - row * 11; + for (int col = 0; col < nColumns; col++) { + int x = twoColumn ? 64 * col + 32 : 42 * col + 22; + int choice = word_matrix[nColumns * row + col]; + const char *text = choice < num ? choices[choice] : "-"; + oledDrawString(x - oledStringWidth(text, FONT_STANDARD) / 2, y, text, + FONT_STANDARD); + if (twoColumn) { + oledInvert(x - 32 + 1, y - 1, x - 32 + 63 - 1, y + 8); + } else { + oledInvert(x - 22 + 1, y - 1, x - 22 + 41 - 1, y + 8); + } + } + } + oledRefresh(); + + /* avoid picking out of range numbers */ + for (int i = 0; i < displayedChoices; i++) { + if (word_matrix[i] >= num) word_matrix[i] = 0; + } + /* two column layout: middle column = right column */ + if (twoColumn) { + static const uint8_t twolayout[9] = {0, 1, 1, 2, 3, 3, 4, 5, 5}; + for (int i = 8; i >= 2; i--) { + word_matrix[i] = word_matrix[twolayout[i]]; + } + } } /* Helper function for matrix recovery: * Generates a new matrix and requests the next pin. */ static void next_matrix(void) { - const char * const *wl = mnemonic_wordlist(); - char word_choices[9][12]; - uint32_t idx, num; - bool last = (word_index % 4) == 3; - - /* Build the matrix: - * num: number of choices - * word_choices[][]: the strings containing the choices - */ - switch (word_index % 4) { - case 3: - /* last level: show up to six words */ - /* idx: index in table2 for the entered choice. */ - /* first: the first word. */ - /* num: the number of words to choose from. */ - idx = TABLE1(word_pincode / 9) + word_pincode % 9; - const uint32_t first = TABLE2(idx); - num = TABLE2(idx + 1) - first; - for (uint32_t i = 0; i < num; i++) { - strlcpy(word_choices[i], wl[first + i], sizeof(word_choices[i])); - } - break; - - case 2: - /* third level: show up to nine ranges (using table2) */ - /* idx: first index in table2 corresponding to pin code. */ - /* num: the number of choices. */ - idx = TABLE1(word_pincode); - num = TABLE1(word_pincode + 1) - idx; - for (uint32_t i = 0; i < num; i++) { - add_choice(word_choices[i], (word_table2[idx + i] >> 12), - wl[TABLE2(idx + i)], - wl[TABLE2(idx + i + 1) - 1]); - } - break; - - case 1: - /* second level: exactly nine ranges (using table1) */ - /* idx: first index in table1 corresponding to pin code. */ - /* num: the number of choices. */ - idx = word_pincode * 9; - num = 9; - for (uint32_t i = 0; i < num; i++) { - add_choice(word_choices[i], (word_table1[idx + i] >> 12), - wl[TABLE2(TABLE1(idx + i))], - wl[TABLE2(TABLE1(idx + i + 1)) - 1]); - } - break; - - case 0: - /* first level: exactly nine ranges */ - /* num: the number of choices. */ - num = 9; - for (uint32_t i = 0; i < num; i++) { - add_choice(word_choices[i], 1, - wl[TABLE2(TABLE1(9*i))], - wl[TABLE2(TABLE1(9*(i+1)))-1]); - } - break; - } - display_choices(last, word_choices, num); - - recovery_request(); + const char *const *wl = mnemonic_wordlist(); + char word_choices[9][12]; + uint32_t idx, num; + bool last = (word_index % 4) == 3; + + /* Build the matrix: + * num: number of choices + * word_choices[][]: the strings containing the choices + */ + switch (word_index % 4) { + case 3: + /* last level: show up to six words */ + /* idx: index in table2 for the entered choice. */ + /* first: the first word. */ + /* num: the number of words to choose from. */ + idx = TABLE1(word_pincode / 9) + word_pincode % 9; + const uint32_t first = TABLE2(idx); + num = TABLE2(idx + 1) - first; + for (uint32_t i = 0; i < num; i++) { + strlcpy(word_choices[i], wl[first + i], sizeof(word_choices[i])); + } + break; + + case 2: + /* third level: show up to nine ranges (using table2) */ + /* idx: first index in table2 corresponding to pin code. */ + /* num: the number of choices. */ + idx = TABLE1(word_pincode); + num = TABLE1(word_pincode + 1) - idx; + for (uint32_t i = 0; i < num; i++) { + add_choice(word_choices[i], (word_table2[idx + i] >> 12), + wl[TABLE2(idx + i)], wl[TABLE2(idx + i + 1) - 1]); + } + break; + + case 1: + /* second level: exactly nine ranges (using table1) */ + /* idx: first index in table1 corresponding to pin code. */ + /* num: the number of choices. */ + idx = word_pincode * 9; + num = 9; + for (uint32_t i = 0; i < num; i++) { + add_choice(word_choices[i], (word_table1[idx + i] >> 12), + wl[TABLE2(TABLE1(idx + i))], + wl[TABLE2(TABLE1(idx + i + 1)) - 1]); + } + break; + + case 0: + /* first level: exactly nine ranges */ + /* num: the number of choices. */ + num = 9; + for (uint32_t i = 0; i < num; i++) { + add_choice(word_choices[i], 1, wl[TABLE2(TABLE1(9 * i))], + wl[TABLE2(TABLE1(9 * (i + 1))) - 1]); + } + break; + } + display_choices(last, word_choices, num); + + recovery_request(); } /* Function called when a digit was entered by user. @@ -382,202 +391,203 @@ static void next_matrix(void) { * '\x08' for backspace. */ static void recovery_digit(const char digit) { - if (digit == 8) { - /* backspace: undo */ - if ((word_index % 4) == 0) { - /* undo complete word */ - if (word_index > 0) - word_index -= 4; - } else { - word_index--; - word_pincode /= 9; - } - next_matrix(); - return; - } - - if (digit < '1' || digit > '9') { - recovery_request(); - return; - } - - int choice = word_matrix[digit - '1']; - if ((word_index % 4) == 3) { - /* received final word */ - - /* Mark the chosen word for 250 ms */ - int y = 54 - ((digit - '1') / 3) * 11; - int x = 64 * (((digit - '1') % 3) > 0); - oledInvert(x + 1, y, x + 62, y + 9); - oledRefresh(); - usbTiny(1); - usbSleep(250); - usbTiny(0); - - /* index of the chosen word */ - int idx = TABLE2(TABLE1(word_pincode / 9) + (word_pincode % 9)) + choice; - uint32_t widx = word_index / 4; - - word_pincode = 0; - strlcpy(words[widx], mnemonic_wordlist()[idx], sizeof(words[widx])); - if (widx + 1 == word_count) { - recovery_done(); - return; - } - /* next word */ - } else { - word_pincode = word_pincode * 9 + choice; - } - word_index++; - next_matrix(); + if (digit == 8) { + /* backspace: undo */ + if ((word_index % 4) == 0) { + /* undo complete word */ + if (word_index > 0) word_index -= 4; + } else { + word_index--; + word_pincode /= 9; + } + next_matrix(); + return; + } + + if (digit < '1' || digit > '9') { + recovery_request(); + return; + } + + int choice = word_matrix[digit - '1']; + if ((word_index % 4) == 3) { + /* received final word */ + + /* Mark the chosen word for 250 ms */ + int y = 54 - ((digit - '1') / 3) * 11; + int x = 64 * (((digit - '1') % 3) > 0); + oledInvert(x + 1, y, x + 62, y + 9); + oledRefresh(); + usbTiny(1); + usbSleep(250); + usbTiny(0); + + /* index of the chosen word */ + int idx = TABLE2(TABLE1(word_pincode / 9) + (word_pincode % 9)) + choice; + uint32_t widx = word_index / 4; + + word_pincode = 0; + strlcpy(words[widx], mnemonic_wordlist()[idx], sizeof(words[widx])); + if (widx + 1 == word_count) { + recovery_done(); + return; + } + /* next word */ + } else { + word_pincode = word_pincode * 9 + choice; + } + word_index++; + next_matrix(); } /* Helper function for scrambled recovery: * Ask the user for the next word. */ void next_word(void) { - word_pos = word_order[word_index]; - if (word_pos == 0) { - const char * const *wl = mnemonic_wordlist(); - strlcpy(fake_word, wl[random_uniform(2048)], sizeof(fake_word)); - layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter the word"), NULL, fake_word, NULL, _("on your computer"), NULL); - } else { - fake_word[0] = 0; - char desc[] = "##th word"; - format_number(desc, word_pos); - layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter the"), NULL, (word_pos < 10 ? desc + 1 : desc), NULL, _("of your mnemonic"), NULL); - } - recovery_request(); + word_pos = word_order[word_index]; + if (word_pos == 0) { + const char *const *wl = mnemonic_wordlist(); + strlcpy(fake_word, wl[random_uniform(2048)], sizeof(fake_word)); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, + _("Please enter the word"), NULL, fake_word, NULL, + _("on your computer"), NULL); + } else { + fake_word[0] = 0; + char desc[] = "##th word"; + format_number(desc, word_pos); + layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter the"), + NULL, (word_pos < 10 ? desc + 1 : desc), NULL, + _("of your mnemonic"), NULL); + } + recovery_request(); } -void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist, uint32_t type, uint32_t u2f_counter, bool _dry_run) -{ - if (_word_count != 12 && _word_count != 18 && _word_count != 24) return; - - word_count = _word_count; - enforce_wordlist = _enforce_wordlist; - dry_run = _dry_run; - - if (!dry_run) { - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("recover the device?"), NULL, NULL, NULL, NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (!dry_run) { - if (pin_protection && !protectChangePin(false)) { - layoutHome(); - return; - } - - config_setPassphraseProtection(passphrase_protection); - config_setLanguage(language); - config_setLabel(label); - config_setU2FCounter(u2f_counter); - } - - if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) { - awaiting_word = 2; - word_index = 0; - word_pincode = 0; - next_matrix(); - } else { - for (uint32_t i = 0; i < word_count; i++) { - word_order[i] = i + 1; - } - for (uint32_t i = word_count; i < 24; i++) { - word_order[i] = 0; - } - random_permute(word_order, 24); - awaiting_word = 1; - word_index = 0; - next_word(); - } +void recovery_init(uint32_t _word_count, bool passphrase_protection, + bool pin_protection, const char *language, const char *label, + bool _enforce_wordlist, uint32_t type, uint32_t u2f_counter, + bool _dry_run) { + if (_word_count != 12 && _word_count != 18 && _word_count != 24) return; + + word_count = _word_count; + enforce_wordlist = _enforce_wordlist; + dry_run = _dry_run; + + if (!dry_run) { + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("recover the device?"), + NULL, NULL, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (!dry_run) { + if (pin_protection && !protectChangePin(false)) { + layoutHome(); + return; + } + + config_setPassphraseProtection(passphrase_protection); + config_setLanguage(language); + config_setLabel(label); + config_setU2FCounter(u2f_counter); + } + + if ((type & RecoveryDeviceType_RecoveryDeviceType_Matrix) != 0) { + awaiting_word = 2; + word_index = 0; + word_pincode = 0; + next_matrix(); + } else { + for (uint32_t i = 0; i < word_count; i++) { + word_order[i] = i + 1; + } + for (uint32_t i = word_count; i < 24; i++) { + word_order[i] = 0; + } + random_permute(word_order, 24); + awaiting_word = 1; + word_index = 0; + next_word(); + } } -static void recovery_scrambledword(const char *word) -{ - if (word_pos == 0) { // fake word - if (strcmp(word, fake_word) != 0) { - if (!dry_run) { - session_clear(true); - } - fsm_sendFailure(FailureType_Failure_ProcessError, _("Wrong word retyped")); - layoutHome(); - return; - } - } else { // real word - if (enforce_wordlist) { // check if word is valid - const char * const *wl = mnemonic_wordlist(); - bool found = false; - while (*wl) { - if (strcmp(word, *wl) == 0) { - found = true; - break; - } - wl++; - } - if (!found) { - if (!dry_run) { - session_clear(true); - } - fsm_sendFailure(FailureType_Failure_DataError, _("Word not found in a wordlist")); - layoutHome(); - return; - } - } - strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1])); - } - - if (word_index + 1 == 24) { // last one - recovery_done(); - } else { - word_index++; - next_word(); - } +static void recovery_scrambledword(const char *word) { + if (word_pos == 0) { // fake word + if (strcmp(word, fake_word) != 0) { + if (!dry_run) { + session_clear(true); + } + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Wrong word retyped")); + layoutHome(); + return; + } + } else { // real word + if (enforce_wordlist) { // check if word is valid + const char *const *wl = mnemonic_wordlist(); + bool found = false; + while (*wl) { + if (strcmp(word, *wl) == 0) { + found = true; + break; + } + wl++; + } + if (!found) { + if (!dry_run) { + session_clear(true); + } + fsm_sendFailure(FailureType_Failure_DataError, + _("Word not found in a wordlist")); + layoutHome(); + return; + } + } + strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1])); + } + + if (word_index + 1 == 24) { // last one + recovery_done(); + } else { + word_index++; + next_word(); + } } /* Function called when a word was entered by user. Used * for scrambled recovery. */ -void recovery_word(const char *word) -{ - switch (awaiting_word) { - case 2: - recovery_digit(word[0]); - break; - case 1: - recovery_scrambledword(word); - break; - default: - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Recovery mode")); - break; - } +void recovery_word(const char *word) { + switch (awaiting_word) { + case 2: + recovery_digit(word[0]); + break; + case 1: + recovery_scrambledword(word); + break; + default: + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Not in Recovery mode")); + break; + } } /* Abort recovery. */ -void recovery_abort(void) -{ - if (awaiting_word) { - layoutHome(); - awaiting_word = 0; - } +void recovery_abort(void) { + if (awaiting_word) { + layoutHome(); + awaiting_word = 0; + } } #if DEBUG_LINK -const char *recovery_get_fake_word(void) -{ - return fake_word; -} +const char *recovery_get_fake_word(void) { return fake_word; } -uint32_t recovery_get_word_pos(void) -{ - return word_pos; -} +uint32_t recovery_get_word_pos(void) { return word_pos; } #endif diff --git a/firmware/recovery.h b/firmware/recovery.h index d0a63508e..84a261aa0 100644 --- a/firmware/recovery.h +++ b/firmware/recovery.h @@ -20,10 +20,13 @@ #ifndef __RECOVERY_H__ #define __RECOVERY_H__ -#include #include +#include -void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist, uint32_t type, uint32_t u2f_counter, bool _dry_run); +void recovery_init(uint32_t _word_count, bool passphrase_protection, + bool pin_protection, const char *language, const char *label, + bool _enforce_wordlist, uint32_t type, uint32_t u2f_counter, + bool _dry_run); void recovery_word(const char *word); void recovery_abort(void); const char *recovery_get_fake_word(void); diff --git a/firmware/reset.c b/firmware/reset.c index 82bf7191d..1faef79ee 100644 --- a/firmware/reset.c +++ b/firmware/reset.c @@ -18,180 +18,189 @@ */ #include "reset.h" +#include "bip39.h" #include "config.h" -#include "rng.h" -#include "sha2.h" -#include "messages.h" #include "fsm.h" +#include "gettext.h" #include "layout2.h" +#include "memzero.h" +#include "messages.h" +#include "messages.pb.h" #include "protect.h" -#include "bip39.h" +#include "rng.h" +#include "sha2.h" #include "util.h" -#include "gettext.h" -#include "messages.pb.h" -#include "memzero.h" static uint32_t strength; -static uint8_t int_entropy[32]; -static bool awaiting_entropy = false; -static bool skip_backup = false; -static bool no_backup = false; - -void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label, uint32_t u2f_counter, bool _skip_backup, bool _no_backup) -{ - if (_strength != 128 && _strength != 192 && _strength != 256) return; - - strength = _strength; - skip_backup = _skip_backup; - no_backup = _no_backup; - - if (display_random && (skip_backup || no_backup)) { - fsm_sendFailure(FailureType_Failure_ProcessError, "Can't show internal entropy when backup is skipped"); - layoutHome(); - return; - } - - layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, _("Do you really want to"), _("create a new wallet?"), NULL, _("By continuing you"), _("agree to trezor.io/tos"), NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - - random_buffer(int_entropy, 32); - - char ent_str[4][17]; - data2hex(int_entropy , 8, ent_str[0]); - data2hex(int_entropy + 8, 8, ent_str[1]); - data2hex(int_entropy + 16, 8, ent_str[2]); - data2hex(int_entropy + 24, 8, ent_str[3]); - - if (display_random) { - layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Continue"), NULL, _("Internal entropy:"), ent_str[0], ent_str[1], ent_str[2], ent_str[3], NULL); - if (!protectButton(ButtonRequestType_ButtonRequest_ResetDevice, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - layoutHome(); - return; - } - } - - if (pin_protection && !protectChangePin(false)) { - layoutHome(); - return; - } - - config_setPassphraseProtection(passphrase_protection); - config_setLanguage(language); - config_setLabel(label); - config_setU2FCounter(u2f_counter); - - EntropyRequest resp; - memzero(&resp, sizeof(EntropyRequest)); - msg_write(MessageType_MessageType_EntropyRequest, &resp); - awaiting_entropy = true; +static uint8_t int_entropy[32]; +static bool awaiting_entropy = false; +static bool skip_backup = false; +static bool no_backup = false; + +void reset_init(bool display_random, uint32_t _strength, + bool passphrase_protection, bool pin_protection, + const char *language, const char *label, uint32_t u2f_counter, + bool _skip_backup, bool _no_backup) { + if (_strength != 128 && _strength != 192 && _strength != 256) return; + + strength = _strength; + skip_backup = _skip_backup; + no_backup = _no_backup; + + if (display_random && (skip_backup || no_backup)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + "Can't show internal entropy when backup is skipped"); + layoutHome(); + return; + } + + layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL, + _("Do you really want to"), _("create a new wallet?"), NULL, + _("By continuing you"), _("agree to trezor.io/tos"), NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + + random_buffer(int_entropy, 32); + + char ent_str[4][17]; + data2hex(int_entropy, 8, ent_str[0]); + data2hex(int_entropy + 8, 8, ent_str[1]); + data2hex(int_entropy + 16, 8, ent_str[2]); + data2hex(int_entropy + 24, 8, ent_str[3]); + + if (display_random) { + layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Continue"), NULL, + _("Internal entropy:"), ent_str[0], ent_str[1], + ent_str[2], ent_str[3], NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ResetDevice, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + layoutHome(); + return; + } + } + + if (pin_protection && !protectChangePin(false)) { + layoutHome(); + return; + } + + config_setPassphraseProtection(passphrase_protection); + config_setLanguage(language); + config_setLabel(label); + config_setU2FCounter(u2f_counter); + + EntropyRequest resp; + memzero(&resp, sizeof(EntropyRequest)); + msg_write(MessageType_MessageType_EntropyRequest, &resp); + awaiting_entropy = true; } -void reset_entropy(const uint8_t *ext_entropy, uint32_t len) -{ - if (!awaiting_entropy) { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Reset mode")); - return; - } - awaiting_entropy = false; - - SHA256_CTX ctx; - sha256_Init(&ctx); - sha256_Update(&ctx, int_entropy, 32); - sha256_Update(&ctx, ext_entropy, len); - sha256_Final(&ctx, int_entropy); - const char* mnemonic = mnemonic_from_data(int_entropy, strength / 8); - memzero(int_entropy, 32); - - if (skip_backup || no_backup) { - if (no_backup) { - config_setNoBackup(); - } else { - config_setNeedsBackup(true); - } - if (config_setMnemonic(mnemonic)) { - fsm_sendSuccess(_("Device successfully initialized")); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to store mnemonic")); - } - layoutHome(); - } else { - reset_backup(false, mnemonic); - } - mnemonic_clear(); +void reset_entropy(const uint8_t *ext_entropy, uint32_t len) { + if (!awaiting_entropy) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Not in Reset mode")); + return; + } + awaiting_entropy = false; + + SHA256_CTX ctx; + sha256_Init(&ctx); + sha256_Update(&ctx, int_entropy, 32); + sha256_Update(&ctx, ext_entropy, len); + sha256_Final(&ctx, int_entropy); + const char *mnemonic = mnemonic_from_data(int_entropy, strength / 8); + memzero(int_entropy, 32); + + if (skip_backup || no_backup) { + if (no_backup) { + config_setNoBackup(); + } else { + config_setNeedsBackup(true); + } + if (config_setMnemonic(mnemonic)) { + fsm_sendSuccess(_("Device successfully initialized")); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to store mnemonic")); + } + layoutHome(); + } else { + reset_backup(false, mnemonic); + } + mnemonic_clear(); } static char current_word[10]; // separated == true if called as a separate workflow via BackupMessage -void reset_backup(bool separated, const char* mnemonic) -{ - if (separated) { - bool needs_backup = false; - config_getNeedsBackup(&needs_backup); - if (!needs_backup) { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Seed already backed up")); - return; - } - - config_setUnfinishedBackup(true); - config_setNeedsBackup(false); - } - - for (int pass = 0; pass < 2; pass++) { - int i = 0, word_pos = 1; - while (mnemonic[i] != 0) { - // copy current_word - int j = 0; - while (mnemonic[i] != ' ' && mnemonic[i] != 0 && j + 1 < (int)sizeof(current_word)) { - current_word[j] = mnemonic[i]; - i++; j++; - } - current_word[j] = 0; - if (mnemonic[i] != 0) { - i++; - } - layoutResetWord(current_word, pass, word_pos, mnemonic[i] == 0); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) { - if (!separated) { - session_clear(true); - } - layoutHome(); - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - return; - } - word_pos++; - } - } - - config_setUnfinishedBackup(false); - - if (separated) { - fsm_sendSuccess(_("Seed successfully backed up")); - } else { - config_setNeedsBackup(false); - if (config_setMnemonic(mnemonic)) { - fsm_sendSuccess(_("Device successfully initialized")); - } else { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to store mnemonic")); - } - } - layoutHome(); +void reset_backup(bool separated, const char *mnemonic) { + if (separated) { + bool needs_backup = false; + config_getNeedsBackup(&needs_backup); + if (!needs_backup) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Seed already backed up")); + return; + } + + config_setUnfinishedBackup(true); + config_setNeedsBackup(false); + } + + for (int pass = 0; pass < 2; pass++) { + int i = 0, word_pos = 1; + while (mnemonic[i] != 0) { + // copy current_word + int j = 0; + while (mnemonic[i] != ' ' && mnemonic[i] != 0 && + j + 1 < (int)sizeof(current_word)) { + current_word[j] = mnemonic[i]; + i++; + j++; + } + current_word[j] = 0; + if (mnemonic[i] != 0) { + i++; + } + layoutResetWord(current_word, pass, word_pos, mnemonic[i] == 0); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) { + if (!separated) { + session_clear(true); + } + layoutHome(); + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + return; + } + word_pos++; + } + } + + config_setUnfinishedBackup(false); + + if (separated) { + fsm_sendSuccess(_("Seed successfully backed up")); + } else { + config_setNeedsBackup(false); + if (config_setMnemonic(mnemonic)) { + fsm_sendSuccess(_("Device successfully initialized")); + } else { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to store mnemonic")); + } + } + layoutHome(); } #if DEBUG_LINK uint32_t reset_get_int_entropy(uint8_t *entropy) { - memcpy(entropy, int_entropy, 32); - return 32; + memcpy(entropy, int_entropy, 32); + return 32; } -const char *reset_get_word(void) { - return current_word; -} +const char *reset_get_word(void) { return current_word; } #endif diff --git a/firmware/reset.h b/firmware/reset.h index 3ceea7533..e9aba0b6d 100644 --- a/firmware/reset.h +++ b/firmware/reset.h @@ -20,12 +20,15 @@ #ifndef __RESET_H__ #define __RESET_H__ -#include #include +#include -void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label, uint32_t u2f_counter, bool _skip_backup, bool _no_backup); +void reset_init(bool display_random, uint32_t _strength, + bool passphrase_protection, bool pin_protection, + const char *language, const char *label, uint32_t u2f_counter, + bool _skip_backup, bool _no_backup); void reset_entropy(const uint8_t *ext_entropy, uint32_t len); -void reset_backup(bool separated, const char* mnemonic); +void reset_backup(bool separated, const char *mnemonic); uint32_t reset_get_int_entropy(uint8_t *entropy); const char *reset_get_word(void); diff --git a/firmware/signing.c b/firmware/signing.c index 035ab05c7..c69cccef5 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -18,17 +18,17 @@ */ #include "signing.h" +#include "crypto.h" +#include "ecdsa.h" #include "fsm.h" +#include "gettext.h" #include "layout2.h" +#include "memzero.h" #include "messages.h" -#include "transaction.h" -#include "ecdsa.h" +#include "messages.pb.h" #include "protect.h" -#include "crypto.h" #include "secp256k1.h" -#include "gettext.h" -#include "messages.pb.h" -#include "memzero.h" +#include "transaction.h" static uint32_t inputs_count; static uint32_t outputs_count; @@ -37,18 +37,18 @@ static CONFIDENTIAL HDNode root; static CONFIDENTIAL HDNode node; static bool signing = false; enum { - STAGE_REQUEST_1_INPUT, - STAGE_REQUEST_2_PREV_META, - STAGE_REQUEST_2_PREV_INPUT, - STAGE_REQUEST_2_PREV_OUTPUT, - STAGE_REQUEST_2_PREV_EXTRADATA, - STAGE_REQUEST_3_OUTPUT, - STAGE_REQUEST_4_INPUT, - STAGE_REQUEST_4_OUTPUT, - STAGE_REQUEST_SEGWIT_INPUT, - STAGE_REQUEST_5_OUTPUT, - STAGE_REQUEST_SEGWIT_WITNESS, - STAGE_REQUEST_DECRED_WITNESS + STAGE_REQUEST_1_INPUT, + STAGE_REQUEST_2_PREV_META, + STAGE_REQUEST_2_PREV_INPUT, + STAGE_REQUEST_2_PREV_OUTPUT, + STAGE_REQUEST_2_PREV_EXTRADATA, + STAGE_REQUEST_3_OUTPUT, + STAGE_REQUEST_4_INPUT, + STAGE_REQUEST_4_OUTPUT, + STAGE_REQUEST_SEGWIT_INPUT, + STAGE_REQUEST_5_OUTPUT, + STAGE_REQUEST_SEGWIT_WITNESS, + STAGE_REQUEST_DECRED_WITNESS } signing_stage; static uint32_t idx1, idx2; static uint32_t signatures; @@ -59,7 +59,7 @@ static TxStruct to, tp, ti; static Hasher hasher_prevouts, hasher_sequence, hasher_outputs, hasher_check; static uint8_t CONFIDENTIAL privkey[32]; static uint8_t pubkey[33], sig[64]; -static uint8_t hash_prevouts[32], hash_sequence[32],hash_outputs[32]; +static uint8_t hash_prevouts[32], hash_sequence[32], hash_outputs[32]; static uint8_t hash_prefix[32]; static uint8_t hash_check[32]; static uint64_t to_spend, authorized_amount, spending, change_spend; @@ -81,9 +81,9 @@ static uint32_t tx_weight; input */ #define BIP32_NOCHANGEALLOWED 1 /* The number of bip32 levels used in a wallet (chain and address) */ -#define BIP32_WALLET_DEPTH 2 +#define BIP32_WALLET_DEPTH 2 /* The chain id used for change */ -#define BIP32_CHANGE_CHAIN 1 +#define BIP32_CHANGE_CHAIN 1 /* The maximum allowed change address. This should be large enough for normal use and still allow to quickly brute-force the correct bip32 path. */ #define BIP32_MAX_LAST_ELEMENT 1000000 @@ -96,17 +96,16 @@ static uint32_t tx_weight; #define TXSIZE_SEGWIT_OVERHEAD 2 enum { - SIGHASH_ALL = 1, - SIGHASH_FORKID = 0x40, + SIGHASH_ALL = 1, + SIGHASH_FORKID = 0x40, }; enum { - DECRED_SERIALIZE_FULL = 0, - DECRED_SERIALIZE_NO_WITNESS = 1, - DECRED_SERIALIZE_WITNESS_SIGNING = 3, + DECRED_SERIALIZE_FULL = 0, + DECRED_SERIALIZE_NO_WITNESS = 1, + DECRED_SERIALIZE_WITNESS_SIGNING = 3, }; - /* progress_step/meta_step are fixed point numbers, giving the * progress per input in permille with these many additional bits. */ @@ -126,29 +125,20 @@ Phase1 - check inputs, previous transactions, and outputs ========================================================= foreach I (idx1): - Request I STAGE_REQUEST_1_INPUT - Add I to segwit hash_prevouts, hash_sequence + Request I STAGE_REQUEST_1_INPUT Add I to segwit hash_prevouts, hash_sequence Add I to Decred hash_prefix Add I to TransactionChecksum (prevout and type) if (Decred) Return I If not segwit, Calculate amount of I: - Request prevhash I, META STAGE_REQUEST_2_PREV_META - foreach prevhash I (idx2): - Request prevhash I STAGE_REQUEST_2_PREV_INPUT - foreach prevhash O (idx2): - Request prevhash O STAGE_REQUEST_2_PREV_OUTPUT - Add amount of prevhash O (which is amount of I) - Request prevhash extra data (if applicable) STAGE_REQUEST_2_PREV_EXTRADATA - Calculate hash of streamed tx, compare to prevhash I -foreach O (idx1): - Request O STAGE_REQUEST_3_OUTPUT - Add O to Decred hash_prefix - Add O to TransactionChecksum - if (Decred) - Return O - Display output - Ask for confirmation + Request prevhash I, META STAGE_REQUEST_2_PREV_META foreach prevhash I +(idx2): Request prevhash I STAGE_REQUEST_2_PREV_INPUT foreach prevhash O (idx2): + Request prevhash O STAGE_REQUEST_2_PREV_OUTPUT Add amount of +prevhash O (which is amount of I) Request prevhash extra data (if applicable) +STAGE_REQUEST_2_PREV_EXTRADATA Calculate hash of streamed tx, compare to +prevhash I foreach O (idx1): Request O STAGE_REQUEST_3_OUTPUT Add O to Decred +hash_prefix Add O to TransactionChecksum if (Decred) Return O Display output Ask +for confirmation Check tx fee Ask for confirmation @@ -161,21 +151,17 @@ if (Decred) foreach I (idx1): // input to sign if (idx1 is segwit) - Request I STAGE_REQUEST_SEGWIT_INPUT - Return serialized input chunk + Request I STAGE_REQUEST_SEGWIT_INPUT Return serialized input chunk else foreach I (idx2): - Request I STAGE_REQUEST_4_INPUT - If idx1 == idx2 - Fill scriptsig + Request I STAGE_REQUEST_4_INPUT If idx1 == idx2 Fill scriptsig Remember key for signing Add I to StreamTransactionSign Add I to TransactionChecksum foreach O (idx2): - Request O STAGE_REQUEST_4_OUTPUT - Add O to StreamTransactionSign - Add O to TransactionChecksum + Request O STAGE_REQUEST_4_OUTPUT Add O to StreamTransactionSign Add +O to TransactionChecksum Compare TransactionChecksum with checksum computed in Phase 1 If different: @@ -184,1270 +170,1369 @@ foreach I (idx1): // input to sign Return signed chunk foreach O (idx1): - Request O STAGE_REQUEST_5_OUTPUT - Rewrite change address - Return O + Request O STAGE_REQUEST_5_OUTPUT Rewrite change address Return O Phase3: sign segwit inputs, check that nothing changed =============================================== foreach I (idx1): // input to sign - Request I STAGE_REQUEST_SEGWIT_WITNESS - Check amount - Sign segwit prevhash, sequence, amount, outputs - Return witness + Request I STAGE_REQUEST_SEGWIT_WITNESS Check amount Sign segwit prevhash, +sequence, amount, outputs Return witness Phase3: sign Decred inputs ========================== -foreach I (idx1): // input to sign STAGE_REQUEST_DECRED_WITNESS - Request I - Fill scriptSig - Compute hash_witness +foreach I (idx1): // input to sign STAGE_REQUEST_DECRED_WITNESS Request I Fill +scriptSig Compute hash_witness Sign (hash_type || hash_prefix || hash_witness) Return witness */ -void send_req_1_input(void) -{ - signing_stage = STAGE_REQUEST_1_INPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXINPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx1; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_1_input(void) { + signing_stage = STAGE_REQUEST_1_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_2_prev_meta(void) -{ - signing_stage = STAGE_REQUEST_2_PREV_META; - resp.has_request_type = true; - resp.request_type = RequestType_TXMETA; - resp.has_details = true; - resp.details.has_tx_hash = true; - resp.details.tx_hash.size = input.prev_hash.size; - memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, input.prev_hash.size); - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_2_prev_meta(void) { + signing_stage = STAGE_REQUEST_2_PREV_META; + resp.has_request_type = true; + resp.request_type = RequestType_TXMETA; + resp.has_details = true; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = input.prev_hash.size; + memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, + input.prev_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_2_prev_input(void) -{ - signing_stage = STAGE_REQUEST_2_PREV_INPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXINPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx2; - resp.details.has_tx_hash = true; - resp.details.tx_hash.size = input.prev_hash.size; - memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, resp.details.tx_hash.size); - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_2_prev_input(void) { + signing_stage = STAGE_REQUEST_2_PREV_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx2; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = input.prev_hash.size; + memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, + resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_2_prev_output(void) -{ - signing_stage = STAGE_REQUEST_2_PREV_OUTPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXOUTPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx2; - resp.details.has_tx_hash = true; - resp.details.tx_hash.size = input.prev_hash.size; - memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, resp.details.tx_hash.size); - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_2_prev_output(void) { + signing_stage = STAGE_REQUEST_2_PREV_OUTPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXOUTPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx2; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = input.prev_hash.size; + memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, + resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_2_prev_extradata(uint32_t chunk_offset, uint32_t chunk_len) -{ - signing_stage = STAGE_REQUEST_2_PREV_EXTRADATA; - resp.has_request_type = true; - resp.request_type = RequestType_TXEXTRADATA; - resp.has_details = true; - resp.details.has_extra_data_offset = true; - resp.details.extra_data_offset = chunk_offset; - resp.details.has_extra_data_len = true; - resp.details.extra_data_len = chunk_len; - resp.details.has_tx_hash = true; - resp.details.tx_hash.size = input.prev_hash.size; - memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, resp.details.tx_hash.size); - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_2_prev_extradata(uint32_t chunk_offset, uint32_t chunk_len) { + signing_stage = STAGE_REQUEST_2_PREV_EXTRADATA; + resp.has_request_type = true; + resp.request_type = RequestType_TXEXTRADATA; + resp.has_details = true; + resp.details.has_extra_data_offset = true; + resp.details.extra_data_offset = chunk_offset; + resp.details.has_extra_data_len = true; + resp.details.extra_data_len = chunk_len; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = input.prev_hash.size; + memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, + resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_3_output(void) -{ - signing_stage = STAGE_REQUEST_3_OUTPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXOUTPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx1; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_3_output(void) { + signing_stage = STAGE_REQUEST_3_OUTPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXOUTPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_4_input(void) -{ - signing_stage = STAGE_REQUEST_4_INPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXINPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx2; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_4_input(void) { + signing_stage = STAGE_REQUEST_4_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx2; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_4_output(void) -{ - signing_stage = STAGE_REQUEST_4_OUTPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXOUTPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx2; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_4_output(void) { + signing_stage = STAGE_REQUEST_4_OUTPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXOUTPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx2; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_segwit_input(void) -{ - signing_stage = STAGE_REQUEST_SEGWIT_INPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXINPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx1; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_segwit_input(void) { + signing_stage = STAGE_REQUEST_SEGWIT_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_segwit_witness(void) -{ - signing_stage = STAGE_REQUEST_SEGWIT_WITNESS; - resp.has_request_type = true; - resp.request_type = RequestType_TXINPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx1; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_segwit_witness(void) { + signing_stage = STAGE_REQUEST_SEGWIT_WITNESS; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_decred_witness(void) -{ - signing_stage = STAGE_REQUEST_DECRED_WITNESS; - resp.has_request_type = true; - resp.request_type = RequestType_TXINPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx1; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_decred_witness(void) { + signing_stage = STAGE_REQUEST_DECRED_WITNESS; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_5_output(void) -{ - signing_stage = STAGE_REQUEST_5_OUTPUT; - resp.has_request_type = true; - resp.request_type = RequestType_TXOUTPUT; - resp.has_details = true; - resp.details.has_request_index = true; - resp.details.request_index = idx1; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_5_output(void) { + signing_stage = STAGE_REQUEST_5_OUTPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXOUTPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void send_req_finished(void) -{ - resp.has_request_type = true; - resp.request_type = RequestType_TXFINISHED; - msg_write(MessageType_MessageType_TxRequest, &resp); +void send_req_finished(void) { + resp.has_request_type = true; + resp.request_type = RequestType_TXFINISHED; + msg_write(MessageType_MessageType_TxRequest, &resp); } -void phase1_request_next_input(void) -{ - if (idx1 < inputs_count - 1) { - idx1++; - send_req_1_input(); - } else { - // compute segwit hashPrevouts & hashSequence - hasher_Final(&hasher_prevouts, hash_prevouts); - hasher_Final(&hasher_sequence, hash_sequence); - hasher_Final(&hasher_check, hash_check); - // init hashOutputs - hasher_Reset(&hasher_outputs); - idx1 = 0; - send_req_3_output(); - } +void phase1_request_next_input(void) { + if (idx1 < inputs_count - 1) { + idx1++; + send_req_1_input(); + } else { + // compute segwit hashPrevouts & hashSequence + hasher_Final(&hasher_prevouts, hash_prevouts); + hasher_Final(&hasher_sequence, hash_sequence); + hasher_Final(&hasher_check, hash_check); + // init hashOutputs + hasher_Reset(&hasher_outputs); + idx1 = 0; + send_req_3_output(); + } } -void phase2_request_next_input(void) -{ - if (idx1 == next_nonsegwit_input) { - idx2 = 0; - send_req_4_input(); - } else { - send_req_segwit_input(); - } +void phase2_request_next_input(void) { + if (idx1 == next_nonsegwit_input) { + idx2 = 0; + send_req_4_input(); + } else { + send_req_segwit_input(); + } } -void extract_input_bip32_path(const TxInputType *tinput) -{ - if (in_address_n_count == BIP32_NOCHANGEALLOWED) { - return; - } - size_t count = tinput->address_n_count; - if (count < BIP32_WALLET_DEPTH) { - // no change address allowed - in_address_n_count = BIP32_NOCHANGEALLOWED; - return; - } - if (in_address_n_count == 0) { - // initialize in_address_n on first input seen - in_address_n_count = count; - // store the bip32 path up to the account - memcpy(in_address_n, tinput->address_n, - (count - BIP32_WALLET_DEPTH) * sizeof(uint32_t)); - return; - } - // check that all addresses use a path of same length - if (in_address_n_count != count) { - in_address_n_count = BIP32_NOCHANGEALLOWED; - return; - } - // check that the bip32 path up to the account matches - if (memcmp(in_address_n, tinput->address_n, - (count - BIP32_WALLET_DEPTH) * sizeof(uint32_t)) != 0) { - // mismatch -> no change address allowed - in_address_n_count = BIP32_NOCHANGEALLOWED; - return; - } +void extract_input_bip32_path(const TxInputType *tinput) { + if (in_address_n_count == BIP32_NOCHANGEALLOWED) { + return; + } + size_t count = tinput->address_n_count; + if (count < BIP32_WALLET_DEPTH) { + // no change address allowed + in_address_n_count = BIP32_NOCHANGEALLOWED; + return; + } + if (in_address_n_count == 0) { + // initialize in_address_n on first input seen + in_address_n_count = count; + // store the bip32 path up to the account + memcpy(in_address_n, tinput->address_n, + (count - BIP32_WALLET_DEPTH) * sizeof(uint32_t)); + return; + } + // check that all addresses use a path of same length + if (in_address_n_count != count) { + in_address_n_count = BIP32_NOCHANGEALLOWED; + return; + } + // check that the bip32 path up to the account matches + if (memcmp(in_address_n, tinput->address_n, + (count - BIP32_WALLET_DEPTH) * sizeof(uint32_t)) != 0) { + // mismatch -> no change address allowed + in_address_n_count = BIP32_NOCHANGEALLOWED; + return; + } } -bool check_change_bip32_path(const TxOutputType *toutput) -{ - size_t count = toutput->address_n_count; - - // Check that the change path has the same bip32 path length, - // the same path up to the account, and that the wallet components - // (chain id and address) are as expected. - // Note: count >= BIP32_WALLET_DEPTH and count == in_address_n_count - // imply that in_address_n_count != BIP32_NOCHANGEALLOWED - return (count >= BIP32_WALLET_DEPTH - && count == in_address_n_count - && 0 == memcmp(in_address_n, toutput->address_n, - (count - BIP32_WALLET_DEPTH) * sizeof(uint32_t)) - && toutput->address_n[count - 2] <= BIP32_CHANGE_CHAIN - && toutput->address_n[count - 1] <= BIP32_MAX_LAST_ELEMENT); +bool check_change_bip32_path(const TxOutputType *toutput) { + size_t count = toutput->address_n_count; + + // Check that the change path has the same bip32 path length, + // the same path up to the account, and that the wallet components + // (chain id and address) are as expected. + // Note: count >= BIP32_WALLET_DEPTH and count == in_address_n_count + // imply that in_address_n_count != BIP32_NOCHANGEALLOWED + return (count >= BIP32_WALLET_DEPTH && count == in_address_n_count && + 0 == memcmp(in_address_n, toutput->address_n, + (count - BIP32_WALLET_DEPTH) * sizeof(uint32_t)) && + toutput->address_n[count - 2] <= BIP32_CHANGE_CHAIN && + toutput->address_n[count - 1] <= BIP32_MAX_LAST_ELEMENT); } -bool compile_input_script_sig(TxInputType *tinput) -{ - if (!multisig_fp_mismatch) { - // check that this is still multisig - uint8_t h[32]; - if (!tinput->has_multisig - || cryptoMultisigFingerprint(&(tinput->multisig), h) == 0 - || memcmp(multisig_fp, h, 32) != 0) { - // Transaction has changed during signing - return false; - } - } - if (in_address_n_count != BIP32_NOCHANGEALLOWED) { - // check that input address didn't change - size_t count = tinput->address_n_count; - if (count < 2 - || count != in_address_n_count - || 0 != memcmp(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t))) { - return false; - } - } - memcpy(&node, &root, sizeof(HDNode)); - if (hdnode_private_ckd_cached(&node, tinput->address_n, tinput->address_n_count, NULL) == 0) { - // Failed to derive private key - return false; - } - hdnode_fill_public_key(&node); - if (tinput->has_multisig) { - tinput->script_sig.size = compile_script_multisig(coin, &(tinput->multisig), tinput->script_sig.bytes); - } else { // SPENDADDRESS - uint8_t hash[20]; - ecdsa_get_pubkeyhash(node.public_key, coin->curve->hasher_pubkey, hash); - tinput->script_sig.size = compile_script_sig(coin->address_type, hash, tinput->script_sig.bytes); - } - return tinput->script_sig.size > 0; +bool compile_input_script_sig(TxInputType *tinput) { + if (!multisig_fp_mismatch) { + // check that this is still multisig + uint8_t h[32]; + if (!tinput->has_multisig || + cryptoMultisigFingerprint(&(tinput->multisig), h) == 0 || + memcmp(multisig_fp, h, 32) != 0) { + // Transaction has changed during signing + return false; + } + } + if (in_address_n_count != BIP32_NOCHANGEALLOWED) { + // check that input address didn't change + size_t count = tinput->address_n_count; + if (count < 2 || count != in_address_n_count || + 0 != memcmp(in_address_n, tinput->address_n, + (count - 2) * sizeof(uint32_t))) { + return false; + } + } + memcpy(&node, &root, sizeof(HDNode)); + if (hdnode_private_ckd_cached(&node, tinput->address_n, + tinput->address_n_count, NULL) == 0) { + // Failed to derive private key + return false; + } + hdnode_fill_public_key(&node); + if (tinput->has_multisig) { + tinput->script_sig.size = compile_script_multisig(coin, &(tinput->multisig), + tinput->script_sig.bytes); + } else { // SPENDADDRESS + uint8_t hash[20]; + ecdsa_get_pubkeyhash(node.public_key, coin->curve->hasher_pubkey, hash); + tinput->script_sig.size = + compile_script_sig(coin->address_type, hash, tinput->script_sig.bytes); + } + return tinput->script_sig.size > 0; } -void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root) -{ - inputs_count = msg->inputs_count; - outputs_count = msg->outputs_count; - coin = _coin; - memcpy(&root, _root, sizeof(HDNode)); - version = msg->version; - lock_time = msg->lock_time; - expiry = msg->expiry; - overwintered = msg->has_overwintered && msg->overwintered; - version_group_id = msg->version_group_id; - branch_id = msg->branch_id; - // set default values for Zcash if branch_id is unset - if (overwintered && (branch_id == 0)) { - switch (version) { - case 3: - branch_id = 0x5BA81B19; // Overwinter - break; - case 4: - branch_id = 0x76B809BB; // Sapling - break; - } - } - - uint32_t size = TXSIZE_HEADER + TXSIZE_FOOTER + ser_length_size(inputs_count) + ser_length_size(outputs_count); - if (coin->decred) { - size += 4; // Decred expiry - size += ser_length_size(inputs_count); // Witness inputs count - } - - tx_weight = 4 * size; - - signatures = 0; - idx1 = 0; - to_spend = 0; - spending = 0; - change_spend = 0; - authorized_amount = 0; - memzero(&input, sizeof(TxInputType)); - memzero(&resp, sizeof(TxRequest)); - - signing = true; - progress = 0; - // we step by 500/inputs_count per input in phase1 and phase2 - // this means 50 % per phase. - progress_step = (500 << PROGRESS_PRECISION) / inputs_count; - - in_address_n_count = 0; - multisig_fp_set = false; - multisig_fp_mismatch = false; - next_nonsegwit_input = 0xffffffff; - - tx_init(&to, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered, version_group_id); - - if (coin->decred) { - to.version |= (DECRED_SERIALIZE_FULL << 16); - to.is_decred = true; - - tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered, version_group_id); - ti.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); - ti.is_decred = true; - } - - // segwit hashes for hashPrevouts and hashSequence - if (overwintered) { - hasher_InitParam(&hasher_prevouts, HASHER_BLAKE2B_PERSONAL, "ZcashPrevoutHash", 16); - hasher_InitParam(&hasher_sequence, HASHER_BLAKE2B_PERSONAL, "ZcashSequencHash", 16); - hasher_InitParam(&hasher_outputs, HASHER_BLAKE2B_PERSONAL, "ZcashOutputsHash", 16); - hasher_Init(&hasher_check, coin->curve->hasher_sign); - } else { - hasher_Init(&hasher_prevouts, coin->curve->hasher_sign); - hasher_Init(&hasher_sequence, coin->curve->hasher_sign); - hasher_Init(&hasher_outputs, coin->curve->hasher_sign); - hasher_Init(&hasher_check, coin->curve->hasher_sign); - } - - layoutProgressSwipe(_("Signing transaction"), 0); - - send_req_1_input(); +void signing_init(const SignTx *msg, const CoinInfo *_coin, + const HDNode *_root) { + inputs_count = msg->inputs_count; + outputs_count = msg->outputs_count; + coin = _coin; + memcpy(&root, _root, sizeof(HDNode)); + version = msg->version; + lock_time = msg->lock_time; + expiry = msg->expiry; + overwintered = msg->has_overwintered && msg->overwintered; + version_group_id = msg->version_group_id; + branch_id = msg->branch_id; + // set default values for Zcash if branch_id is unset + if (overwintered && (branch_id == 0)) { + switch (version) { + case 3: + branch_id = 0x5BA81B19; // Overwinter + break; + case 4: + branch_id = 0x76B809BB; // Sapling + break; + } + } + + uint32_t size = TXSIZE_HEADER + TXSIZE_FOOTER + + ser_length_size(inputs_count) + + ser_length_size(outputs_count); + if (coin->decred) { + size += 4; // Decred expiry + size += ser_length_size(inputs_count); // Witness inputs count + } + + tx_weight = 4 * size; + + signatures = 0; + idx1 = 0; + to_spend = 0; + spending = 0; + change_spend = 0; + authorized_amount = 0; + memzero(&input, sizeof(TxInputType)); + memzero(&resp, sizeof(TxRequest)); + + signing = true; + progress = 0; + // we step by 500/inputs_count per input in phase1 and phase2 + // this means 50 % per phase. + progress_step = (500 << PROGRESS_PRECISION) / inputs_count; + + in_address_n_count = 0; + multisig_fp_set = false; + multisig_fp_mismatch = false; + next_nonsegwit_input = 0xffffffff; + + tx_init(&to, inputs_count, outputs_count, version, lock_time, expiry, 0, + coin->curve->hasher_sign, overwintered, version_group_id); + + if (coin->decred) { + to.version |= (DECRED_SERIALIZE_FULL << 16); + to.is_decred = true; + + tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, + coin->curve->hasher_sign, overwintered, version_group_id); + ti.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); + ti.is_decred = true; + } + + // segwit hashes for hashPrevouts and hashSequence + if (overwintered) { + hasher_InitParam(&hasher_prevouts, HASHER_BLAKE2B_PERSONAL, + "ZcashPrevoutHash", 16); + hasher_InitParam(&hasher_sequence, HASHER_BLAKE2B_PERSONAL, + "ZcashSequencHash", 16); + hasher_InitParam(&hasher_outputs, HASHER_BLAKE2B_PERSONAL, + "ZcashOutputsHash", 16); + hasher_Init(&hasher_check, coin->curve->hasher_sign); + } else { + hasher_Init(&hasher_prevouts, coin->curve->hasher_sign); + hasher_Init(&hasher_sequence, coin->curve->hasher_sign); + hasher_Init(&hasher_outputs, coin->curve->hasher_sign); + hasher_Init(&hasher_check, coin->curve->hasher_sign); + } + + layoutProgressSwipe(_("Signing transaction"), 0); + + send_req_1_input(); } -#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) static bool signing_check_input(const TxInputType *txinput) { - /* compute multisig fingerprint */ - /* (if all input share the same fingerprint, outputs having the same fingerprint will be considered as change outputs) */ - if (txinput->has_multisig && !multisig_fp_mismatch) { - uint8_t h[32]; - if (cryptoMultisigFingerprint(&txinput->multisig, h) == 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Error computing multisig fingerprint")); - signing_abort(); - return false; - } - if (multisig_fp_set) { - if (memcmp(multisig_fp, h, 32) != 0) { - multisig_fp_mismatch = true; - } - } else { - memcpy(multisig_fp, h, 32); - multisig_fp_set = true; - } - } else { // single signature - multisig_fp_mismatch = true; - } - // remember the input bip32 path - // change addresses must use the same bip32 path as all inputs - extract_input_bip32_path(txinput); - // compute segwit hashPrevouts & hashSequence - tx_prevout_hash(&hasher_prevouts, txinput); - tx_sequence_hash(&hasher_sequence, txinput); - if (coin->decred) { - if (txinput->decred_script_version > 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Decred v1+ scripts are not supported")); - signing_abort(); - return false; - } - - // serialize Decred prefix in Phase 1 - resp.has_serialized = true; - resp.serialized.has_serialized_tx = true; - resp.serialized.serialized_tx.size = tx_serialize_input(&to, txinput, resp.serialized.serialized_tx.bytes); - - // compute Decred hashPrefix - tx_serialize_input_hash(&ti, txinput); - } - // hash prevout and script type to check it later (relevant for fee computation) - tx_prevout_hash(&hasher_check, txinput); - hasher_Update(&hasher_check, (const uint8_t *)&txinput->script_type, sizeof(&txinput->script_type)); - return true; + /* compute multisig fingerprint */ + /* (if all input share the same fingerprint, outputs having the same + * fingerprint will be considered as change outputs) */ + if (txinput->has_multisig && !multisig_fp_mismatch) { + uint8_t h[32]; + if (cryptoMultisigFingerprint(&txinput->multisig, h) == 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Error computing multisig fingerprint")); + signing_abort(); + return false; + } + if (multisig_fp_set) { + if (memcmp(multisig_fp, h, 32) != 0) { + multisig_fp_mismatch = true; + } + } else { + memcpy(multisig_fp, h, 32); + multisig_fp_set = true; + } + } else { // single signature + multisig_fp_mismatch = true; + } + // remember the input bip32 path + // change addresses must use the same bip32 path as all inputs + extract_input_bip32_path(txinput); + // compute segwit hashPrevouts & hashSequence + tx_prevout_hash(&hasher_prevouts, txinput); + tx_sequence_hash(&hasher_sequence, txinput); + if (coin->decred) { + if (txinput->decred_script_version > 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Decred v1+ scripts are not supported")); + signing_abort(); + return false; + } + + // serialize Decred prefix in Phase 1 + resp.has_serialized = true; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.size = + tx_serialize_input(&to, txinput, resp.serialized.serialized_tx.bytes); + + // compute Decred hashPrefix + tx_serialize_input_hash(&ti, txinput); + } + // hash prevout and script type to check it later (relevant for fee + // computation) + tx_prevout_hash(&hasher_check, txinput); + hasher_Update(&hasher_check, (const uint8_t *)&txinput->script_type, + sizeof(&txinput->script_type)); + return true; } // check if the hash of the prevtx matches static bool signing_check_prevtx_hash(void) { - uint8_t hash[32]; - tx_hash_final(&tp, hash, true); - if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Encountered invalid prevhash")); - signing_abort(); - return false; - } - phase1_request_next_input(); - return true; + uint8_t hash[32]; + tx_hash_final(&tp, hash, true); + if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Encountered invalid prevhash")); + signing_abort(); + return false; + } + phase1_request_next_input(); + return true; } static bool signing_check_output(TxOutputType *txoutput) { - // Phase1: Check outputs - // add it to hash_outputs - // ask user for permission - - // check for change address - bool is_change = false; - if (txoutput->address_n_count > 0) { - if (txoutput->has_address) { - fsm_sendFailure(FailureType_Failure_DataError, _("Address in change output")); - signing_abort(); - return false; - } - /* - * For multisig check that all inputs are multisig - */ - if (txoutput->has_multisig) { - uint8_t h[32]; - if (multisig_fp_set && !multisig_fp_mismatch - && cryptoMultisigFingerprint(&(txoutput->multisig), h) - && memcmp(multisig_fp, h, 32) == 0) { - is_change = check_change_bip32_path(txoutput); - } - } else { - is_change = check_change_bip32_path(txoutput); - } - /* - * only allow segwit change if amount is smaller than what segwit inputs paid. - * this was added during the times segwit was not yet fully activated - * to make sure the user is not tricked to use witness change output - * instead of regular one therefore creating ANYONECANSPEND output - */ - if ((txoutput->script_type == OutputScriptType_PAYTOWITNESS - || txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) - && txoutput->amount > authorized_amount) { - is_change = false; - } - } - - if (is_change) { - if (change_spend == 0) { // not set - change_spend = txoutput->amount; - } else { - /* We only skip confirmation for the first change output */ - is_change = false; - } - } - - if (spending + txoutput->amount < spending) { - fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); - signing_abort(); - return false; - } - spending += txoutput->amount; - int co = compile_output(coin, &root, txoutput, &bin_output, !is_change); - if (!is_change) { - layoutProgress(_("Signing transaction"), progress); - } - if (co < 0) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - signing_abort(); - return false; - } else if (co == 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile output")); - signing_abort(); - return false; - } - if (coin->decred) { - // serialize Decred prefix in Phase 1 - resp.has_serialized = true; - resp.serialized.has_serialized_tx = true; - resp.serialized.serialized_tx.size = tx_serialize_output(&to, &bin_output, resp.serialized.serialized_tx.bytes); - - // compute Decred hashPrefix - tx_serialize_output_hash(&ti, &bin_output); - } - // compute segwit hashOuts - tx_output_hash(&hasher_outputs, &bin_output, coin->decred); - return true; + // Phase1: Check outputs + // add it to hash_outputs + // ask user for permission + + // check for change address + bool is_change = false; + if (txoutput->address_n_count > 0) { + if (txoutput->has_address) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Address in change output")); + signing_abort(); + return false; + } + /* + * For multisig check that all inputs are multisig + */ + if (txoutput->has_multisig) { + uint8_t h[32]; + if (multisig_fp_set && !multisig_fp_mismatch && + cryptoMultisigFingerprint(&(txoutput->multisig), h) && + memcmp(multisig_fp, h, 32) == 0) { + is_change = check_change_bip32_path(txoutput); + } + } else { + is_change = check_change_bip32_path(txoutput); + } + /* + * only allow segwit change if amount is smaller than what segwit inputs + * paid. this was added during the times segwit was not yet fully activated + * to make sure the user is not tricked to use witness change output + * instead of regular one therefore creating ANYONECANSPEND output + */ + if ((txoutput->script_type == OutputScriptType_PAYTOWITNESS || + txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) && + txoutput->amount > authorized_amount) { + is_change = false; + } + } + + if (is_change) { + if (change_spend == 0) { // not set + change_spend = txoutput->amount; + } else { + /* We only skip confirmation for the first change output */ + is_change = false; + } + } + + if (spending + txoutput->amount < spending) { + fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); + signing_abort(); + return false; + } + spending += txoutput->amount; + int co = compile_output(coin, &root, txoutput, &bin_output, !is_change); + if (!is_change) { + layoutProgress(_("Signing transaction"), progress); + } + if (co < 0) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + signing_abort(); + return false; + } else if (co == 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return false; + } + if (coin->decred) { + // serialize Decred prefix in Phase 1 + resp.has_serialized = true; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.size = tx_serialize_output( + &to, &bin_output, resp.serialized.serialized_tx.bytes); + + // compute Decred hashPrefix + tx_serialize_output_hash(&ti, &bin_output); + } + // compute segwit hashOuts + tx_output_hash(&hasher_outputs, &bin_output, coin->decred); + return true; } static bool signing_check_fee(void) { - // check fees - if (spending > to_spend) { - fsm_sendFailure(FailureType_Failure_NotEnoughFunds, _("Not enough funds")); - signing_abort(); - return false; - } - uint64_t fee = to_spend - spending; - if (fee > ((uint64_t) tx_weight * coin->maxfee_kb)/4000) { - layoutFeeOverThreshold(coin, fee); - if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - signing_abort(); - return false; - } - } - // last confirmation - layoutConfirmTx(coin, to_spend - change_spend, fee); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); - signing_abort(); - return false; - } - return true; + // check fees + if (spending > to_spend) { + fsm_sendFailure(FailureType_Failure_NotEnoughFunds, _("Not enough funds")); + signing_abort(); + return false; + } + uint64_t fee = to_spend - spending; + if (fee > ((uint64_t)tx_weight * coin->maxfee_kb) / 4000) { + layoutFeeOverThreshold(coin, fee); + if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, + false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + signing_abort(); + return false; + } + } + // last confirmation + layoutConfirmTx(coin, to_spend - change_spend, fee); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL); + signing_abort(); + return false; + } + return true; } static uint32_t signing_hash_type(void) { - uint32_t hash_type = SIGHASH_ALL; + uint32_t hash_type = SIGHASH_ALL; - if (coin->has_fork_id) { - hash_type |= (coin->fork_id << 8) | SIGHASH_FORKID; - } + if (coin->has_fork_id) { + hash_type |= (coin->fork_id << 8) | SIGHASH_FORKID; + } - return hash_type; + return hash_type; } static void phase1_request_next_output(void) { - if (idx1 < outputs_count - 1) { - idx1++; - send_req_3_output(); - } else { - if (coin->decred) { - // compute Decred hashPrefix - tx_hash_final(&ti, hash_prefix, false); - } - hasher_Final(&hasher_outputs, hash_outputs); - if (!signing_check_fee()) { - return; - } - // Everything was checked, now phase 2 begins and the transaction is signed. - progress_meta_step = progress_step / (inputs_count + outputs_count); - layoutProgress(_("Signing transaction"), progress); - idx1 = 0; - if (coin->decred) { - // Decred prefix serialized in Phase 1, skip Phase 2 - send_req_decred_witness(); - } else { - phase2_request_next_input(); - } - } + if (idx1 < outputs_count - 1) { + idx1++; + send_req_3_output(); + } else { + if (coin->decred) { + // compute Decred hashPrefix + tx_hash_final(&ti, hash_prefix, false); + } + hasher_Final(&hasher_outputs, hash_outputs); + if (!signing_check_fee()) { + return; + } + // Everything was checked, now phase 2 begins and the transaction is signed. + progress_meta_step = progress_step / (inputs_count + outputs_count); + layoutProgress(_("Signing transaction"), progress); + idx1 = 0; + if (coin->decred) { + // Decred prefix serialized in Phase 1, skip Phase 2 + send_req_decred_witness(); + } else { + phase2_request_next_input(); + } + } } static void signing_hash_bip143(const TxInputType *txinput, uint8_t *hash) { - uint32_t hash_type = signing_hash_type(); - Hasher hasher_preimage; - hasher_Init(&hasher_preimage, coin->curve->hasher_sign); - hasher_Update(&hasher_preimage, (const uint8_t *)&version, 4); // nVersion - hasher_Update(&hasher_preimage, hash_prevouts, 32); // hashPrevouts - hasher_Update(&hasher_preimage, hash_sequence, 32); // hashSequence - tx_prevout_hash(&hasher_preimage, txinput); // outpoint - tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // scriptCode - hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, 8); // amount - tx_sequence_hash(&hasher_preimage, txinput); // nSequence - hasher_Update(&hasher_preimage, hash_outputs, 32); // hashOutputs - hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, 4); // nLockTime - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); // nHashType - hasher_Final(&hasher_preimage, hash); + uint32_t hash_type = signing_hash_type(); + Hasher hasher_preimage; + hasher_Init(&hasher_preimage, coin->curve->hasher_sign); + hasher_Update(&hasher_preimage, (const uint8_t *)&version, 4); // nVersion + hasher_Update(&hasher_preimage, hash_prevouts, 32); // hashPrevouts + hasher_Update(&hasher_preimage, hash_sequence, 32); // hashSequence + tx_prevout_hash(&hasher_preimage, txinput); // outpoint + tx_script_hash(&hasher_preimage, txinput->script_sig.size, + txinput->script_sig.bytes); // scriptCode + hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, + 8); // amount + tx_sequence_hash(&hasher_preimage, txinput); // nSequence + hasher_Update(&hasher_preimage, hash_outputs, 32); // hashOutputs + hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, 4); // nLockTime + hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); // nHashType + hasher_Final(&hasher_preimage, hash); } static void signing_hash_zip143(const TxInputType *txinput, uint8_t *hash) { - uint32_t hash_type = signing_hash_type(); - uint8_t personal[16]; - memcpy(personal, "ZcashSigHash", 12); - memcpy(personal + 12, &branch_id, 4); - Hasher hasher_preimage; - hasher_InitParam(&hasher_preimage, HASHER_BLAKE2B_PERSONAL, personal, sizeof(personal)); - uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered - hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); - hasher_Update(&hasher_preimage, (const uint8_t *)&version_group_id, 4); // 2. nVersionGroupId - hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts - hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence - hasher_Update(&hasher_preimage, hash_outputs, 32); // 5. hashOutputs - // 6. hashJoinSplits - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); - hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, 4); // 7. nLockTime - hasher_Update(&hasher_preimage, (const uint8_t *)&expiry, 4); // 8. expiryHeight - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); // 9. nHashType - - tx_prevout_hash(&hasher_preimage, txinput); // 10a. outpoint - tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // 10b. scriptCode - hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, 8); // 10c. value - tx_sequence_hash(&hasher_preimage, txinput); // 10d. nSequence - - hasher_Final(&hasher_preimage, hash); + uint32_t hash_type = signing_hash_type(); + uint8_t personal[16]; + memcpy(personal, "ZcashSigHash", 12); + memcpy(personal + 12, &branch_id, 4); + Hasher hasher_preimage; + hasher_InitParam(&hasher_preimage, HASHER_BLAKE2B_PERSONAL, personal, + sizeof(personal)); + uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered + hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); + hasher_Update(&hasher_preimage, (const uint8_t *)&version_group_id, + 4); // 2. nVersionGroupId + hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts + hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence + hasher_Update(&hasher_preimage, hash_outputs, 32); // 5. hashOutputs + // 6. hashJoinSplits + hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, + 4); // 7. nLockTime + hasher_Update(&hasher_preimage, (const uint8_t *)&expiry, + 4); // 8. expiryHeight + hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, + 4); // 9. nHashType + + tx_prevout_hash(&hasher_preimage, txinput); // 10a. outpoint + tx_script_hash(&hasher_preimage, txinput->script_sig.size, + txinput->script_sig.bytes); // 10b. scriptCode + hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, + 8); // 10c. value + tx_sequence_hash(&hasher_preimage, txinput); // 10d. nSequence + + hasher_Final(&hasher_preimage, hash); } static void signing_hash_zip243(const TxInputType *txinput, uint8_t *hash) { - uint32_t hash_type = signing_hash_type(); - uint8_t personal[16]; - memcpy(personal, "ZcashSigHash", 12); - memcpy(personal + 12, &branch_id, 4); - Hasher hasher_preimage; - hasher_InitParam(&hasher_preimage, HASHER_BLAKE2B_PERSONAL, personal, sizeof(personal)); - uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered - hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); - hasher_Update(&hasher_preimage, (const uint8_t *)&version_group_id, 4); // 2. nVersionGroupId - hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts - hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence - hasher_Update(&hasher_preimage, hash_outputs, 32); // 5. hashOutputs - // 6. hashJoinSplits - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); - // 7. hashShieldedSpends - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); - // 8. hashShieldedOutputs - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); - hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, 4); // 9. nLockTime - hasher_Update(&hasher_preimage, (const uint8_t *)&expiry, 4); // 10. expiryHeight - hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00", 8); // 11. valueBalance - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); // 12. nHashType - - tx_prevout_hash(&hasher_preimage, txinput); // 13a. outpoint - tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // 13b. scriptCode - hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, 8); // 13c. value - tx_sequence_hash(&hasher_preimage, txinput); // 13d. nSequence - - hasher_Final(&hasher_preimage, hash); + uint32_t hash_type = signing_hash_type(); + uint8_t personal[16]; + memcpy(personal, "ZcashSigHash", 12); + memcpy(personal + 12, &branch_id, 4); + Hasher hasher_preimage; + hasher_InitParam(&hasher_preimage, HASHER_BLAKE2B_PERSONAL, personal, + sizeof(personal)); + uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered + hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); + hasher_Update(&hasher_preimage, (const uint8_t *)&version_group_id, + 4); // 2. nVersionGroupId + hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts + hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence + hasher_Update(&hasher_preimage, hash_outputs, 32); // 5. hashOutputs + // 6. hashJoinSplits + hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + // 7. hashShieldedSpends + hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + // 8. hashShieldedOutputs + hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + hasher_Update(&hasher_preimage, (const uint8_t *)&lock_time, + 4); // 9. nLockTime + hasher_Update(&hasher_preimage, (const uint8_t *)&expiry, + 4); // 10. expiryHeight + hasher_Update(&hasher_preimage, + (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00", + 8); // 11. valueBalance + hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, + 4); // 12. nHashType + + tx_prevout_hash(&hasher_preimage, txinput); // 13a. outpoint + tx_script_hash(&hasher_preimage, txinput->script_sig.size, + txinput->script_sig.bytes); // 13b. scriptCode + hasher_Update(&hasher_preimage, (const uint8_t *)&txinput->amount, + 8); // 13c. value + tx_sequence_hash(&hasher_preimage, txinput); // 13d. nSequence + + hasher_Final(&hasher_preimage, hash); } static void signing_hash_decred(const uint8_t *hash_witness, uint8_t *hash) { - uint32_t hash_type = signing_hash_type(); - Hasher hasher_preimage; - hasher_Init(&hasher_preimage, coin->curve->hasher_sign); - hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); - hasher_Update(&hasher_preimage, hash_prefix, 32); - hasher_Update(&hasher_preimage, hash_witness, 32); - hasher_Final(&hasher_preimage, hash); + uint32_t hash_type = signing_hash_type(); + Hasher hasher_preimage; + hasher_Init(&hasher_preimage, coin->curve->hasher_sign); + hasher_Update(&hasher_preimage, (const uint8_t *)&hash_type, 4); + hasher_Update(&hasher_preimage, hash_prefix, 32); + hasher_Update(&hasher_preimage, hash_witness, 32); + hasher_Final(&hasher_preimage, hash); } -static bool signing_sign_hash(TxInputType *txinput, const uint8_t *private_key, const uint8_t *public_key, const uint8_t *hash) { - resp.serialized.has_signature_index = true; - resp.serialized.signature_index = idx1; - resp.serialized.has_signature = true; - resp.serialized.has_serialized_tx = true; - if (ecdsa_sign_digest(coin->curve->params, private_key, hash, sig, NULL, NULL) != 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); - signing_abort(); - return false; - } - resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - - uint8_t sighash = signing_hash_type() & 0xff; - if (txinput->has_multisig) { - // fill in the signature - int pubkey_idx = cryptoMultisigPubkeyIndex(coin, &(txinput->multisig), public_key); - if (pubkey_idx < 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Pubkey not found in multisig script")); - signing_abort(); - return false; - } - memcpy(txinput->multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); - txinput->multisig.signatures[pubkey_idx].size = resp.serialized.signature.size; - txinput->script_sig.size = serialize_script_multisig(coin, &(txinput->multisig), sighash, txinput->script_sig.bytes); - if (txinput->script_sig.size == 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize multisig script")); - signing_abort(); - return false; - } - } else { // SPENDADDRESS - txinput->script_sig.size = serialize_script_sig(resp.serialized.signature.bytes, resp.serialized.signature.size, public_key, 33, sighash, txinput->script_sig.bytes); - } - return true; +static bool signing_sign_hash(TxInputType *txinput, const uint8_t *private_key, + const uint8_t *public_key, const uint8_t *hash) { + resp.serialized.has_signature_index = true; + resp.serialized.signature_index = idx1; + resp.serialized.has_signature = true; + resp.serialized.has_serialized_tx = true; + if (ecdsa_sign_digest(coin->curve->params, private_key, hash, sig, NULL, + NULL) != 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); + signing_abort(); + return false; + } + resp.serialized.signature.size = + ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); + + uint8_t sighash = signing_hash_type() & 0xff; + if (txinput->has_multisig) { + // fill in the signature + int pubkey_idx = + cryptoMultisigPubkeyIndex(coin, &(txinput->multisig), public_key); + if (pubkey_idx < 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Pubkey not found in multisig script")); + signing_abort(); + return false; + } + memcpy(txinput->multisig.signatures[pubkey_idx].bytes, + resp.serialized.signature.bytes, resp.serialized.signature.size); + txinput->multisig.signatures[pubkey_idx].size = + resp.serialized.signature.size; + txinput->script_sig.size = serialize_script_multisig( + coin, &(txinput->multisig), sighash, txinput->script_sig.bytes); + if (txinput->script_sig.size == 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize multisig script")); + signing_abort(); + return false; + } + } else { // SPENDADDRESS + txinput->script_sig.size = serialize_script_sig( + resp.serialized.signature.bytes, resp.serialized.signature.size, + public_key, 33, sighash, txinput->script_sig.bytes); + } + return true; } static bool signing_sign_input(void) { - uint8_t hash[32]; - hasher_Final(&hasher_check, hash); - if (memcmp(hash, hash_outputs, 32) != 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); - signing_abort(); - return false; - } - - uint32_t hash_type = signing_hash_type(); - hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); - tx_hash_final(&ti, hash, false); - resp.has_serialized = true; - if (!signing_sign_hash(&input, privkey, pubkey, hash)) - return false; - resp.serialized.serialized_tx.size = tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); - return true; + uint8_t hash[32]; + hasher_Final(&hasher_check, hash); + if (memcmp(hash, hash_outputs, 32) != 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Transaction has changed during signing")); + signing_abort(); + return false; + } + + uint32_t hash_type = signing_hash_type(); + hasher_Update(&ti.hasher, (const uint8_t *)&hash_type, 4); + tx_hash_final(&ti, hash, false); + resp.has_serialized = true; + if (!signing_sign_hash(&input, privkey, pubkey, hash)) return false; + resp.serialized.serialized_tx.size = + tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); + return true; } static bool signing_sign_segwit_input(TxInputType *txinput) { - // idx1: index to sign - uint8_t hash[32]; - - if (txinput->script_type == InputScriptType_SPENDWITNESS - || txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { - if (!compile_input_script_sig(txinput)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); - signing_abort(); - return false; - } - if (txinput->amount > authorized_amount) { - fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); - signing_abort(); - return false; - } - authorized_amount -= txinput->amount; - - signing_hash_bip143(txinput, hash); - - resp.has_serialized = true; - if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) - return false; - - uint8_t sighash = signing_hash_type() & 0xff; - if (txinput->has_multisig) { - uint32_t r = 1; // skip number of items (filled in later) - resp.serialized.serialized_tx.bytes[r] = 0; r++; - int nwitnesses = 2; - for (uint32_t i = 0; i < txinput->multisig.signatures_count; i++) { - if (txinput->multisig.signatures[i].size == 0) { - continue; - } - nwitnesses++; - txinput->multisig.signatures[i].bytes[txinput->multisig.signatures[i].size] = sighash; - r += tx_serialize_script(txinput->multisig.signatures[i].size + 1, txinput->multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); - } - uint32_t script_len = compile_script_multisig(coin, &txinput->multisig, 0); - r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); - r += compile_script_multisig(coin, &txinput->multisig, resp.serialized.serialized_tx.bytes + r); - resp.serialized.serialized_tx.bytes[0] = nwitnesses; - resp.serialized.serialized_tx.size = r; - } else { // single signature - uint32_t r = 0; - r += ser_length(2, resp.serialized.serialized_tx.bytes + r); - resp.serialized.signature.bytes[resp.serialized.signature.size] = sighash; - r += tx_serialize_script(resp.serialized.signature.size + 1, resp.serialized.signature.bytes, resp.serialized.serialized_tx.bytes + r); - r += tx_serialize_script(33, node.public_key, resp.serialized.serialized_tx.bytes + r); - resp.serialized.serialized_tx.size = r; - } - } else { - // empty witness - resp.has_serialized = true; - resp.serialized.has_signature_index = false; - resp.serialized.has_signature = false; - resp.serialized.has_serialized_tx = true; - resp.serialized.serialized_tx.bytes[0] = 0; - resp.serialized.serialized_tx.size = 1; - } - // if last witness add tx footer - if (idx1 == inputs_count - 1) { - uint32_t r = resp.serialized.serialized_tx.size; - r += tx_serialize_footer(&to, resp.serialized.serialized_tx.bytes + r); - resp.serialized.serialized_tx.size = r; - } - return true; + // idx1: index to sign + uint8_t hash[32]; + + if (txinput->script_type == InputScriptType_SPENDWITNESS || + txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { + if (!compile_input_script_sig(txinput)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile input")); + signing_abort(); + return false; + } + if (txinput->amount > authorized_amount) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Transaction has changed during signing")); + signing_abort(); + return false; + } + authorized_amount -= txinput->amount; + + signing_hash_bip143(txinput, hash); + + resp.has_serialized = true; + if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) + return false; + + uint8_t sighash = signing_hash_type() & 0xff; + if (txinput->has_multisig) { + uint32_t r = 1; // skip number of items (filled in later) + resp.serialized.serialized_tx.bytes[r] = 0; + r++; + int nwitnesses = 2; + for (uint32_t i = 0; i < txinput->multisig.signatures_count; i++) { + if (txinput->multisig.signatures[i].size == 0) { + continue; + } + nwitnesses++; + txinput->multisig.signatures[i] + .bytes[txinput->multisig.signatures[i].size] = sighash; + r += tx_serialize_script(txinput->multisig.signatures[i].size + 1, + txinput->multisig.signatures[i].bytes, + resp.serialized.serialized_tx.bytes + r); + } + uint32_t script_len = + compile_script_multisig(coin, &txinput->multisig, 0); + r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); + r += compile_script_multisig(coin, &txinput->multisig, + resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.bytes[0] = nwitnesses; + resp.serialized.serialized_tx.size = r; + } else { // single signature + uint32_t r = 0; + r += ser_length(2, resp.serialized.serialized_tx.bytes + r); + resp.serialized.signature.bytes[resp.serialized.signature.size] = sighash; + r += tx_serialize_script(resp.serialized.signature.size + 1, + resp.serialized.signature.bytes, + resp.serialized.serialized_tx.bytes + r); + r += tx_serialize_script(33, node.public_key, + resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.size = r; + } + } else { + // empty witness + resp.has_serialized = true; + resp.serialized.has_signature_index = false; + resp.serialized.has_signature = false; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.bytes[0] = 0; + resp.serialized.serialized_tx.size = 1; + } + // if last witness add tx footer + if (idx1 == inputs_count - 1) { + uint32_t r = resp.serialized.serialized_tx.size; + r += tx_serialize_footer(&to, resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.size = r; + } + return true; } static bool signing_sign_decred_input(TxInputType *txinput) { - uint8_t hash[32], hash_witness[32]; - tx_hash_final(&ti, hash_witness, false); - signing_hash_decred(hash_witness, hash); - resp.has_serialized = true; - if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) - return false; - resp.serialized.serialized_tx.size = tx_serialize_decred_witness(&to, txinput, resp.serialized.serialized_tx.bytes); - return true; + uint8_t hash[32], hash_witness[32]; + tx_hash_final(&ti, hash_witness, false); + signing_hash_decred(hash_witness, hash); + resp.has_serialized = true; + if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) + return false; + resp.serialized.serialized_tx.size = tx_serialize_decred_witness( + &to, txinput, resp.serialized.serialized_tx.bytes); + return true; } -#define ENABLE_SEGWIT_NONSEGWIT_MIXING 1 +#define ENABLE_SEGWIT_NONSEGWIT_MIXING 1 -void signing_txack(TransactionType *tx) -{ - if (!signing) { - fsm_sendFailure(FailureType_Failure_UnexpectedMessage, _("Not in Signing mode")); - layoutHome(); - return; - } +void signing_txack(TransactionType *tx) { + if (!signing) { + fsm_sendFailure(FailureType_Failure_UnexpectedMessage, + _("Not in Signing mode")); + layoutHome(); + return; + } - static int update_ctr = 0; - if (update_ctr++ == 20) { - layoutProgress(_("Signing transaction"), progress); - update_ctr = 0; - } + static int update_ctr = 0; + if (update_ctr++ == 20) { + layoutProgress(_("Signing transaction"), progress); + update_ctr = 0; + } - memzero(&resp, sizeof(TxRequest)); + memzero(&resp, sizeof(TxRequest)); - switch (signing_stage) { - case STAGE_REQUEST_1_INPUT: - signing_check_input(&tx->inputs[0]); + switch (signing_stage) { + case STAGE_REQUEST_1_INPUT: + signing_check_input(&tx->inputs[0]); - tx_weight += tx_input_weight(coin, &tx->inputs[0]); - if (coin->decred) { - tx_weight += tx_decred_witness_weight(&tx->inputs[0]); - } + tx_weight += tx_input_weight(coin, &tx->inputs[0]); + if (coin->decred) { + tx_weight += tx_decred_witness_weight(&tx->inputs[0]); + } - if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG - || tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { - memcpy(&input, tx->inputs, sizeof(TxInputType)); + if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG || + tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { + memcpy(&input, tx->inputs, sizeof(TxInputType)); #if !ENABLE_SEGWIT_NONSEGWIT_MIXING - // don't mix segwit and non-segwit inputs - if (idx1 > 0 && to.is_segwit == true) { - fsm_sendFailure(FailureType_Failure_DataError, _("Mixing segwit and non-segwit inputs is not allowed")); - signing_abort(); - return; - } + // don't mix segwit and non-segwit inputs + if (idx1 > 0 && to.is_segwit == true) { + fsm_sendFailure( + FailureType_Failure_DataError, + _("Mixing segwit and non-segwit inputs is not allowed")); + signing_abort(); + return; + } #endif - if (coin->force_bip143 || overwintered) { - if (!tx->inputs[0].has_amount) { - fsm_sendFailure(FailureType_Failure_DataError, _("Expected input with amount")); - signing_abort(); - return; - } - if (to_spend + tx->inputs[0].amount < to_spend) { - fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); - signing_abort(); - return; - } - to_spend += tx->inputs[0].amount; - authorized_amount += tx->inputs[0].amount; - phase1_request_next_input(); - } else { - // remember the first non-segwit input -- this is the first input - // we need to sign during phase2 - if (next_nonsegwit_input == 0xffffffff) - next_nonsegwit_input = idx1; - send_req_2_prev_meta(); - } - } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { - if (coin->decred) { - fsm_sendFailure(FailureType_Failure_DataError, _("Decred does not support Segwit")); - signing_abort(); - return; - } - if (!coin->has_segwit) { - fsm_sendFailure(FailureType_Failure_DataError, _("Segwit not enabled on this coin")); - signing_abort(); - return; - } - if (!tx->inputs[0].has_amount) { - fsm_sendFailure(FailureType_Failure_DataError, _("Segwit input without amount")); - signing_abort(); - return; - } - if (to_spend + tx->inputs[0].amount < to_spend) { - fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); - signing_abort(); - return; - } - if (!to.is_segwit) { - tx_weight += TXSIZE_SEGWIT_OVERHEAD + to.inputs_len; - } + if (coin->force_bip143 || overwintered) { + if (!tx->inputs[0].has_amount) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Expected input with amount")); + signing_abort(); + return; + } + if (to_spend + tx->inputs[0].amount < to_spend) { + fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); + signing_abort(); + return; + } + to_spend += tx->inputs[0].amount; + authorized_amount += tx->inputs[0].amount; + phase1_request_next_input(); + } else { + // remember the first non-segwit input -- this is the first input + // we need to sign during phase2 + if (next_nonsegwit_input == 0xffffffff) next_nonsegwit_input = idx1; + send_req_2_prev_meta(); + } + } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS || + tx->inputs[0].script_type == + InputScriptType_SPENDP2SHWITNESS) { + if (coin->decred) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Decred does not support Segwit")); + signing_abort(); + return; + } + if (!coin->has_segwit) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Segwit not enabled on this coin")); + signing_abort(); + return; + } + if (!tx->inputs[0].has_amount) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Segwit input without amount")); + signing_abort(); + return; + } + if (to_spend + tx->inputs[0].amount < to_spend) { + fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); + signing_abort(); + return; + } + if (!to.is_segwit) { + tx_weight += TXSIZE_SEGWIT_OVERHEAD + to.inputs_len; + } #if !ENABLE_SEGWIT_NONSEGWIT_MIXING - // don't mix segwit and non-segwit inputs - if (idx1 == 0) { - to.is_segwit = true; - } else if (to.is_segwit == false) { - fsm_sendFailure(FailureType_Failure_DataError, _("Mixing segwit and non-segwit inputs is not allowed")); - signing_abort(); - return; - } + // don't mix segwit and non-segwit inputs + if (idx1 == 0) { + to.is_segwit = true; + } else if (to.is_segwit == false) { + fsm_sendFailure( + FailureType_Failure_DataError, + _("Mixing segwit and non-segwit inputs is not allowed")); + signing_abort(); + return; + } #else - to.is_segwit = true; + to.is_segwit = true; #endif - to_spend += tx->inputs[0].amount; - authorized_amount += tx->inputs[0].amount; - phase1_request_next_input(); - } else { - fsm_sendFailure(FailureType_Failure_DataError, _("Wrong input script type")); - signing_abort(); - return; - } - return; - case STAGE_REQUEST_2_PREV_META: - if (tx->outputs_cnt <= input.prev_index) { - fsm_sendFailure(FailureType_Failure_DataError, _("Not enough outputs in previous transaction.")); - signing_abort(); - return; - } - if (tx->inputs_cnt + tx->outputs_cnt < tx->inputs_cnt) { - fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); - signing_abort(); - return; - } - tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->expiry, tx->extra_data_len, coin->curve->hasher_sign, overwintered, version_group_id); - if (coin->decred) { - tp.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); - tp.is_decred = true; - } - progress_meta_step = progress_step / (tp.inputs_len + tp.outputs_len); - idx2 = 0; - if (tp.inputs_len > 0) { - send_req_2_prev_input(); - } else { - tx_serialize_header_hash(&tp); - send_req_2_prev_output(); - } - return; - case STAGE_REQUEST_2_PREV_INPUT: - progress = (idx1 * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION; - if (!tx_serialize_input_hash(&tp, tx->inputs)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize input")); - signing_abort(); - return; - } - if (idx2 < tp.inputs_len - 1) { - idx2++; - send_req_2_prev_input(); - } else { - idx2 = 0; - send_req_2_prev_output(); - } - return; - case STAGE_REQUEST_2_PREV_OUTPUT: - progress = (idx1 * progress_step + (tp.inputs_len + idx2) * progress_meta_step) >> PROGRESS_PRECISION; - if (!tx_serialize_output_hash(&tp, tx->bin_outputs)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize output")); - signing_abort(); - return; - } - if (idx2 == input.prev_index) { - if (to_spend + tx->bin_outputs[0].amount < to_spend) { - fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); - signing_abort(); - return; - } - if (coin->decred && tx->bin_outputs[0].decred_script_version > 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Decred script version does not match previous output")); - signing_abort(); - return; - } - to_spend += tx->bin_outputs[0].amount; - } - if (idx2 < tp.outputs_len - 1) { - /* Check prevtx of next input */ - idx2++; - send_req_2_prev_output(); - } else if (tp.extra_data_len > 0) { // has extra data - send_req_2_prev_extradata(0, MIN(1024, tp.extra_data_len)); - return; - } else { - /* prevtx is done */ - signing_check_prevtx_hash(); - } - return; - case STAGE_REQUEST_2_PREV_EXTRADATA: - if (!tx_serialize_extra_data_hash(&tp, tx->extra_data.bytes, tx->extra_data.size)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize extra data")); - signing_abort(); - return; - } - if (tp.extra_data_received < tp.extra_data_len) { // still some data remanining - send_req_2_prev_extradata(tp.extra_data_received, MIN(1024, tp.extra_data_len - tp.extra_data_received)); - } else { - signing_check_prevtx_hash(); - } - return; - case STAGE_REQUEST_3_OUTPUT: - if (!signing_check_output(&tx->outputs[0])) { - return; - } - tx_weight += tx_output_weight(coin, &tx->outputs[0]); - phase1_request_next_output(); - return; - case STAGE_REQUEST_4_INPUT: - progress = 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); - if (idx2 == 0) { - tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered, version_group_id); - hasher_Reset(&hasher_check); - } - // check prevouts and script type - tx_prevout_hash(&hasher_check, tx->inputs); - hasher_Update(&hasher_check, (const uint8_t *)&tx->inputs[0].script_type, sizeof(&tx->inputs[0].script_type)); - if (idx2 == idx1) { - if (!compile_input_script_sig(&tx->inputs[0])) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); - signing_abort(); - return; - } - memcpy(&input, &tx->inputs[0], sizeof(input)); - memcpy(privkey, node.private_key, 32); - memcpy(pubkey, node.public_key, 33); - } else { - if (next_nonsegwit_input == idx1 && idx2 > idx1 - && (tx->inputs[0].script_type == InputScriptType_SPENDADDRESS - || tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) { - next_nonsegwit_input = idx2; - } - tx->inputs[0].script_sig.size = 0; - } - if (!tx_serialize_input_hash(&ti, tx->inputs)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize input")); - signing_abort(); - return; - } - if (idx2 < inputs_count - 1) { - idx2++; - send_req_4_input(); - } else { - uint8_t hash[32]; - hasher_Final(&hasher_check, hash); - if (memcmp(hash, hash_check, 32) != 0) { - fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); - signing_abort(); - return; - } - hasher_Reset(&hasher_check); - idx2 = 0; - send_req_4_output(); - } - return; - case STAGE_REQUEST_4_OUTPUT: - progress = 500 + ((signatures * progress_step + (inputs_count + idx2) * progress_meta_step) >> PROGRESS_PRECISION); - if (compile_output(coin, &root, tx->outputs, &bin_output, false) <= 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile output")); - signing_abort(); - return; - } - // check hashOutputs - tx_output_hash(&hasher_check, &bin_output, coin->decred); - if (!tx_serialize_output_hash(&ti, &bin_output)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize output")); - signing_abort(); - return; - } - if (idx2 < outputs_count - 1) { - idx2++; - send_req_4_output(); - } else { - if (!signing_sign_input()) { - return; - } - // since this took a longer time, update progress - signatures++; - progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); - layoutProgress(_("Signing transaction"), progress); - update_ctr = 0; - if (idx1 < inputs_count - 1) { - idx1++; - phase2_request_next_input(); - } else { - idx1 = 0; - send_req_5_output(); - } - } - return; - - case STAGE_REQUEST_SEGWIT_INPUT: - resp.has_serialized = true; - resp.serialized.has_signature_index = false; - resp.serialized.has_signature = false; - resp.serialized.has_serialized_tx = true; - if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG - || tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { - if (!(coin->force_bip143 || overwintered)) { - fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); - signing_abort(); - return; - } - if (!compile_input_script_sig(&tx->inputs[0])) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); - signing_abort(); - return; - } - if (tx->inputs[0].amount > authorized_amount) { - fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); - signing_abort(); - return; - } - authorized_amount -= tx->inputs[0].amount; - - uint8_t hash[32]; - if (overwintered) { - switch (version) { - case 3: - signing_hash_zip143(&tx->inputs[0], hash); - break; - case 4: - signing_hash_zip243(&tx->inputs[0], hash); - break; - default: - fsm_sendFailure(FailureType_Failure_DataError, _("Unsupported version for overwintered transaction")); - signing_abort(); - return; - } - } else { - signing_hash_bip143(&tx->inputs[0], hash); - } - if (!signing_sign_hash(&tx->inputs[0], node.private_key, node.public_key, hash)) - return; - // since this took a longer time, update progress - signatures++; - progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); - layoutProgress(_("Signing transaction"), progress); - update_ctr = 0; - } else if (tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS - && !tx->inputs[0].has_multisig) { - if (!compile_input_script_sig(&tx->inputs[0])) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); - signing_abort(); - return; - } - // fixup normal p2pkh script into witness 0 p2wpkh script for p2sh - // we convert 76 A9 14 88 AC to 16 00 14 - // P2SH input pushes witness 0 script - tx->inputs[0].script_sig.size = 0x17; // drops last 2 bytes. - tx->inputs[0].script_sig.bytes[0] = 0x16; // push 22 bytes; replaces OP_DUP - tx->inputs[0].script_sig.bytes[1] = 0x00; // witness 0 script ; replaces OP_HASH160 - // digest is already in right place. - } else if (tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { - // Prepare P2SH witness script. - tx->inputs[0].script_sig.size = 0x23; // 35 bytes long: - tx->inputs[0].script_sig.bytes[0] = 0x22; // push 34 bytes (full witness script) - tx->inputs[0].script_sig.bytes[1] = 0x00; // witness 0 script - tx->inputs[0].script_sig.bytes[2] = 0x20; // push 32 bytes (digest) - // compute digest of multisig script - if (!compile_script_multisig_hash(coin, &tx->inputs[0].multisig, tx->inputs[0].script_sig.bytes + 3)) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); - signing_abort(); - return; - } - } else { - // direct witness scripts require zero scriptSig - tx->inputs[0].script_sig.size = 0; - } - resp.serialized.serialized_tx.size = tx_serialize_input(&to, &tx->inputs[0], resp.serialized.serialized_tx.bytes); - if (idx1 < inputs_count - 1) { - idx1++; - phase2_request_next_input(); - } else { - idx1 = 0; - send_req_5_output(); - } - return; - - case STAGE_REQUEST_5_OUTPUT: - if (compile_output(coin, &root, tx->outputs, &bin_output,false) <= 0) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile output")); - signing_abort(); - return; - } - resp.has_serialized = true; - resp.serialized.has_serialized_tx = true; - resp.serialized.serialized_tx.size = tx_serialize_output(&to, &bin_output, resp.serialized.serialized_tx.bytes); - if (idx1 < outputs_count - 1) { - idx1++; - send_req_5_output(); - } else if (to.is_segwit) { - idx1 = 0; - send_req_segwit_witness(); - } else { - send_req_finished(); - signing_abort(); - } - return; - - case STAGE_REQUEST_SEGWIT_WITNESS: - if (!signing_sign_segwit_input(&tx->inputs[0])) { - return; - } - signatures++; - progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); - layoutProgress(_("Signing transaction"), progress); - update_ctr = 0; - if (idx1 < inputs_count - 1) { - idx1++; - send_req_segwit_witness(); - } else { - send_req_finished(); - signing_abort(); - } - return; - - case STAGE_REQUEST_DECRED_WITNESS: - progress = 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); - if (idx1 == 0) { - // witness - tx_init(&to, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered, version_group_id); - to.is_decred = true; - } - - // witness hash - tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered, version_group_id); - ti.version |= (DECRED_SERIALIZE_WITNESS_SIGNING << 16); - ti.is_decred = true; - if (!compile_input_script_sig(&tx->inputs[0])) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); - signing_abort(); - return; - } - - for (idx2 = 0; idx2 < inputs_count; idx2++) { - uint32_t r; - if (idx2 == idx1) { - r = tx_serialize_decred_witness_hash(&ti, &tx->inputs[0]); - } else { - r = tx_serialize_decred_witness_hash(&ti, NULL); - } - - if (!r) { - fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize input")); - signing_abort(); - return; - } - } - - if (!signing_sign_decred_input(&tx->inputs[0])) { - return; - } - // since this took a longer time, update progress - signatures++; - progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); - layoutProgress(_("Signing transaction"), progress); - update_ctr = 0; - if (idx1 < inputs_count - 1) { - idx1++; - send_req_decred_witness(); - } else { - send_req_finished(); - signing_abort(); - } - return; - } - - fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing error")); - signing_abort(); + to_spend += tx->inputs[0].amount; + authorized_amount += tx->inputs[0].amount; + phase1_request_next_input(); + } else { + fsm_sendFailure(FailureType_Failure_DataError, + _("Wrong input script type")); + signing_abort(); + return; + } + return; + case STAGE_REQUEST_2_PREV_META: + if (tx->outputs_cnt <= input.prev_index) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Not enough outputs in previous transaction.")); + signing_abort(); + return; + } + if (tx->inputs_cnt + tx->outputs_cnt < tx->inputs_cnt) { + fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); + signing_abort(); + return; + } + tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, + tx->expiry, tx->extra_data_len, coin->curve->hasher_sign, + overwintered, version_group_id); + if (coin->decred) { + tp.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); + tp.is_decred = true; + } + progress_meta_step = progress_step / (tp.inputs_len + tp.outputs_len); + idx2 = 0; + if (tp.inputs_len > 0) { + send_req_2_prev_input(); + } else { + tx_serialize_header_hash(&tp); + send_req_2_prev_output(); + } + return; + case STAGE_REQUEST_2_PREV_INPUT: + progress = (idx1 * progress_step + idx2 * progress_meta_step) >> + PROGRESS_PRECISION; + if (!tx_serialize_input_hash(&tp, tx->inputs)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize input")); + signing_abort(); + return; + } + if (idx2 < tp.inputs_len - 1) { + idx2++; + send_req_2_prev_input(); + } else { + idx2 = 0; + send_req_2_prev_output(); + } + return; + case STAGE_REQUEST_2_PREV_OUTPUT: + progress = (idx1 * progress_step + + (tp.inputs_len + idx2) * progress_meta_step) >> + PROGRESS_PRECISION; + if (!tx_serialize_output_hash(&tp, tx->bin_outputs)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize output")); + signing_abort(); + return; + } + if (idx2 == input.prev_index) { + if (to_spend + tx->bin_outputs[0].amount < to_spend) { + fsm_sendFailure(FailureType_Failure_DataError, _("Value overflow")); + signing_abort(); + return; + } + if (coin->decred && tx->bin_outputs[0].decred_script_version > 0) { + fsm_sendFailure( + FailureType_Failure_DataError, + _("Decred script version does not match previous output")); + signing_abort(); + return; + } + to_spend += tx->bin_outputs[0].amount; + } + if (idx2 < tp.outputs_len - 1) { + /* Check prevtx of next input */ + idx2++; + send_req_2_prev_output(); + } else if (tp.extra_data_len > 0) { // has extra data + send_req_2_prev_extradata(0, MIN(1024, tp.extra_data_len)); + return; + } else { + /* prevtx is done */ + signing_check_prevtx_hash(); + } + return; + case STAGE_REQUEST_2_PREV_EXTRADATA: + if (!tx_serialize_extra_data_hash(&tp, tx->extra_data.bytes, + tx->extra_data.size)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize extra data")); + signing_abort(); + return; + } + if (tp.extra_data_received < + tp.extra_data_len) { // still some data remanining + send_req_2_prev_extradata( + tp.extra_data_received, + MIN(1024, tp.extra_data_len - tp.extra_data_received)); + } else { + signing_check_prevtx_hash(); + } + return; + case STAGE_REQUEST_3_OUTPUT: + if (!signing_check_output(&tx->outputs[0])) { + return; + } + tx_weight += tx_output_weight(coin, &tx->outputs[0]); + phase1_request_next_output(); + return; + case STAGE_REQUEST_4_INPUT: + progress = + 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> + PROGRESS_PRECISION); + if (idx2 == 0) { + tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, + coin->curve->hasher_sign, overwintered, version_group_id); + hasher_Reset(&hasher_check); + } + // check prevouts and script type + tx_prevout_hash(&hasher_check, tx->inputs); + hasher_Update(&hasher_check, (const uint8_t *)&tx->inputs[0].script_type, + sizeof(&tx->inputs[0].script_type)); + if (idx2 == idx1) { + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile input")); + signing_abort(); + return; + } + memcpy(&input, &tx->inputs[0], sizeof(input)); + memcpy(privkey, node.private_key, 32); + memcpy(pubkey, node.public_key, 33); + } else { + if (next_nonsegwit_input == idx1 && idx2 > idx1 && + (tx->inputs[0].script_type == InputScriptType_SPENDADDRESS || + tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) { + next_nonsegwit_input = idx2; + } + tx->inputs[0].script_sig.size = 0; + } + if (!tx_serialize_input_hash(&ti, tx->inputs)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize input")); + signing_abort(); + return; + } + if (idx2 < inputs_count - 1) { + idx2++; + send_req_4_input(); + } else { + uint8_t hash[32]; + hasher_Final(&hasher_check, hash); + if (memcmp(hash, hash_check, 32) != 0) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Transaction has changed during signing")); + signing_abort(); + return; + } + hasher_Reset(&hasher_check); + idx2 = 0; + send_req_4_output(); + } + return; + case STAGE_REQUEST_4_OUTPUT: + progress = 500 + ((signatures * progress_step + + (inputs_count + idx2) * progress_meta_step) >> + PROGRESS_PRECISION); + if (compile_output(coin, &root, tx->outputs, &bin_output, false) <= 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return; + } + // check hashOutputs + tx_output_hash(&hasher_check, &bin_output, coin->decred); + if (!tx_serialize_output_hash(&ti, &bin_output)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize output")); + signing_abort(); + return; + } + if (idx2 < outputs_count - 1) { + idx2++; + send_req_4_output(); + } else { + if (!signing_sign_input()) { + return; + } + // since this took a longer time, update progress + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); + layoutProgress(_("Signing transaction"), progress); + update_ctr = 0; + if (idx1 < inputs_count - 1) { + idx1++; + phase2_request_next_input(); + } else { + idx1 = 0; + send_req_5_output(); + } + } + return; + + case STAGE_REQUEST_SEGWIT_INPUT: + resp.has_serialized = true; + resp.serialized.has_signature_index = false; + resp.serialized.has_signature = false; + resp.serialized.has_serialized_tx = true; + if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG || + tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { + if (!(coin->force_bip143 || overwintered)) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Transaction has changed during signing")); + signing_abort(); + return; + } + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile input")); + signing_abort(); + return; + } + if (tx->inputs[0].amount > authorized_amount) { + fsm_sendFailure(FailureType_Failure_DataError, + _("Transaction has changed during signing")); + signing_abort(); + return; + } + authorized_amount -= tx->inputs[0].amount; + + uint8_t hash[32]; + if (overwintered) { + switch (version) { + case 3: + signing_hash_zip143(&tx->inputs[0], hash); + break; + case 4: + signing_hash_zip243(&tx->inputs[0], hash); + break; + default: + fsm_sendFailure( + FailureType_Failure_DataError, + _("Unsupported version for overwintered transaction")); + signing_abort(); + return; + } + } else { + signing_hash_bip143(&tx->inputs[0], hash); + } + if (!signing_sign_hash(&tx->inputs[0], node.private_key, + node.public_key, hash)) + return; + // since this took a longer time, update progress + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); + layoutProgress(_("Signing transaction"), progress); + update_ctr = 0; + } else if (tx->inputs[0].script_type == + InputScriptType_SPENDP2SHWITNESS && + !tx->inputs[0].has_multisig) { + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile input")); + signing_abort(); + return; + } + // fixup normal p2pkh script into witness 0 p2wpkh script for p2sh + // we convert 76 A9 14 88 AC to 16 00 14 + // P2SH input pushes witness 0 script + tx->inputs[0].script_sig.size = 0x17; // drops last 2 bytes. + tx->inputs[0].script_sig.bytes[0] = + 0x16; // push 22 bytes; replaces OP_DUP + tx->inputs[0].script_sig.bytes[1] = + 0x00; // witness 0 script ; replaces OP_HASH160 + // digest is already in right place. + } else if (tx->inputs[0].script_type == + InputScriptType_SPENDP2SHWITNESS) { + // Prepare P2SH witness script. + tx->inputs[0].script_sig.size = 0x23; // 35 bytes long: + tx->inputs[0].script_sig.bytes[0] = + 0x22; // push 34 bytes (full witness script) + tx->inputs[0].script_sig.bytes[1] = 0x00; // witness 0 script + tx->inputs[0].script_sig.bytes[2] = 0x20; // push 32 bytes (digest) + // compute digest of multisig script + if (!compile_script_multisig_hash(coin, &tx->inputs[0].multisig, + tx->inputs[0].script_sig.bytes + 3)) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile input")); + signing_abort(); + return; + } + } else { + // direct witness scripts require zero scriptSig + tx->inputs[0].script_sig.size = 0; + } + resp.serialized.serialized_tx.size = tx_serialize_input( + &to, &tx->inputs[0], resp.serialized.serialized_tx.bytes); + if (idx1 < inputs_count - 1) { + idx1++; + phase2_request_next_input(); + } else { + idx1 = 0; + send_req_5_output(); + } + return; + + case STAGE_REQUEST_5_OUTPUT: + if (compile_output(coin, &root, tx->outputs, &bin_output, false) <= 0) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile output")); + signing_abort(); + return; + } + resp.has_serialized = true; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.size = tx_serialize_output( + &to, &bin_output, resp.serialized.serialized_tx.bytes); + if (idx1 < outputs_count - 1) { + idx1++; + send_req_5_output(); + } else if (to.is_segwit) { + idx1 = 0; + send_req_segwit_witness(); + } else { + send_req_finished(); + signing_abort(); + } + return; + + case STAGE_REQUEST_SEGWIT_WITNESS: + if (!signing_sign_segwit_input(&tx->inputs[0])) { + return; + } + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); + layoutProgress(_("Signing transaction"), progress); + update_ctr = 0; + if (idx1 < inputs_count - 1) { + idx1++; + send_req_segwit_witness(); + } else { + send_req_finished(); + signing_abort(); + } + return; + + case STAGE_REQUEST_DECRED_WITNESS: + progress = + 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> + PROGRESS_PRECISION); + if (idx1 == 0) { + // witness + tx_init(&to, inputs_count, outputs_count, version, lock_time, expiry, 0, + coin->curve->hasher_sign, overwintered, version_group_id); + to.is_decred = true; + } + + // witness hash + tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, + coin->curve->hasher_sign, overwintered, version_group_id); + ti.version |= (DECRED_SERIALIZE_WITNESS_SIGNING << 16); + ti.is_decred = true; + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to compile input")); + signing_abort(); + return; + } + + for (idx2 = 0; idx2 < inputs_count; idx2++) { + uint32_t r; + if (idx2 == idx1) { + r = tx_serialize_decred_witness_hash(&ti, &tx->inputs[0]); + } else { + r = tx_serialize_decred_witness_hash(&ti, NULL); + } + + if (!r) { + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Failed to serialize input")); + signing_abort(); + return; + } + } + + if (!signing_sign_decred_input(&tx->inputs[0])) { + return; + } + // since this took a longer time, update progress + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); + layoutProgress(_("Signing transaction"), progress); + update_ctr = 0; + if (idx1 < inputs_count - 1) { + idx1++; + send_req_decred_witness(); + } else { + send_req_finished(); + signing_abort(); + } + return; + } + + fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing error")); + signing_abort(); } -void signing_abort(void) -{ - if (signing) { - layoutHome(); - signing = false; - } - memzero(&root, sizeof(root)); - memzero(&node, sizeof(node)); +void signing_abort(void) { + if (signing) { + layoutHome(); + signing = false; + } + memzero(&root, sizeof(root)); + memzero(&node, sizeof(node)); } diff --git a/firmware/signing.h b/firmware/signing.h index d27ebf7cf..af74d4a6b 100644 --- a/firmware/signing.h +++ b/firmware/signing.h @@ -20,14 +20,15 @@ #ifndef __SIGNING_H__ #define __SIGNING_H__ -#include #include +#include #include "bip32.h" #include "coins.h" #include "hasher.h" #include "messages-bitcoin.pb.h" -void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root); +void signing_init(const SignTx *msg, const CoinInfo *_coin, + const HDNode *_root); void signing_abort(void); void signing_txack(TransactionType *tx); diff --git a/firmware/stellar.c b/firmware/stellar.c index 5ee64d857..d4f46e4e8 100644 --- a/firmware/stellar.c +++ b/firmware/stellar.c @@ -19,32 +19,34 @@ // Stellar signing workflow: // -// 1. Client sends a StellarSignTx method to the device with transaction header information -// 2. Device confirms transaction details with the user and requests first operation +// 1. Client sends a StellarSignTx method to the device with transaction header +// information +// 2. Device confirms transaction details with the user and requests first +// operation // 3. Client sends protobuf message with details about the operation to sign // 4. Device confirms operation with user -// 5a. If there are more operations in the transaction, device responds with StellarTxOpRequest. Go to 3 -// 5b. If the operation is the last one, device responds with StellarSignedTx +// 5a. If there are more operations in the transaction, device responds with +// StellarTxOpRequest. Go to 3 5b. If the operation is the last one, device +// responds with StellarSignedTx +#include "stellar.h" #include #include -#include "messages.h" -#include "messages.pb.h" -#include "stellar.h" +#include "base32.h" +#include "bignum.h" #include "bip32.h" +#include "config.h" #include "crypto.h" -#include "layout2.h" +#include "fonts.h" +#include "fsm.h" #include "gettext.h" -#include "bignum.h" +#include "layout2.h" +#include "memzero.h" +#include "messages.h" +#include "messages.pb.h" #include "oled.h" -#include "base32.h" -#include "config.h" -#include "fsm.h" #include "protect.h" #include "util.h" -#include "layout2.h" -#include "fonts.h" -#include "memzero.h" static bool stellar_signing = false; static StellarTransaction stellar_activeTx; @@ -52,1216 +54,1141 @@ static StellarTransaction stellar_activeTx; /* * Starts the signing process and parses the transaction header */ -bool stellar_signingInit(const StellarSignTx *msg) -{ - memzero(&stellar_activeTx, sizeof(StellarTransaction)); - stellar_signing = true; - // Initialize signing context - sha256_Init(&(stellar_activeTx.sha256_ctx)); - - // Calculate sha256 for network passphrase - // max length defined in messages.options - uint8_t network_hash[32]; - sha256_Raw((uint8_t *)msg->network_passphrase, strnlen(msg->network_passphrase, 1024), network_hash); - - uint8_t tx_type_bytes[4] = { 0x00, 0x00, 0x00, 0x02 }; - - // Copy some data into the active tx - stellar_activeTx.num_operations = msg->num_operations; - - // Start building what will be signed: - // sha256 of: - // sha256(network passphrase) - // 4-byte unsigned big-endian int type constant (2 for tx) - // remaining bytes are operations added in subsequent messages - stellar_hashupdate_bytes(network_hash, sizeof(network_hash)); - stellar_hashupdate_bytes(tx_type_bytes, sizeof(tx_type_bytes)); - - // Public key comes from deriving the specified account path - const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count); - if (!node) { - return false; - } - memcpy(&(stellar_activeTx.signing_pubkey), node->public_key + 1, sizeof(stellar_activeTx.signing_pubkey)); - - stellar_activeTx.address_n_count = msg->address_n_count; - // todo: fix sizeof check - memcpy(&(stellar_activeTx.address_n), &(msg->address_n), sizeof(stellar_activeTx.address_n)); - - // Hash: public key - stellar_hashupdate_address(node->public_key + 1); - - // Hash: fee - stellar_hashupdate_uint32(msg->fee); - - // Hash: sequence number - stellar_hashupdate_uint64(msg->sequence_number); - - // Timebounds are only present if timebounds_start or timebounds_end is non-zero - uint8_t has_timebounds = (msg->timebounds_start > 0 || msg->timebounds_end > 0); - if (has_timebounds) { - // Hash: the "has timebounds?" boolean - stellar_hashupdate_bool(true); - - // Timebounds are sent as uint32s since that's all we can display, but they must be hashed as - // 64-bit values - stellar_hashupdate_uint32(0); - stellar_hashupdate_uint32(msg->timebounds_start); - - stellar_hashupdate_uint32(0); - stellar_hashupdate_uint32(msg->timebounds_end); - } - // No timebounds, hash a false boolean - else { - stellar_hashupdate_bool(false); - } - - // Hash: memo - stellar_hashupdate_uint32(msg->memo_type); - switch (msg->memo_type) { - // None, nothing else to do - case 0: - break; - // Text: 4 bytes (size) + up to 28 bytes - case 1: - stellar_hashupdate_string((unsigned char*)&(msg->memo_text), strnlen(msg->memo_text, 28)); - break; - // ID (8 bytes, uint64) - case 2: - stellar_hashupdate_uint64(msg->memo_id); - break; - // Hash and return are the same data structure (32 byte tx hash) - case 3: - case 4: - stellar_hashupdate_bytes(msg->memo_hash.bytes, msg->memo_hash.size); - break; - default: - break; - } - - // Hash: number of operations - stellar_hashupdate_uint32(msg->num_operations); - - // Determine what type of network this transaction is for - if (strncmp("Public Global Stellar Network ; September 2015", msg->network_passphrase, 1024) == 0) { - stellar_activeTx.network_type = 1; - } - else if (strncmp("Test SDF Network ; September 2015", msg->network_passphrase, 1024) == 0) { - stellar_activeTx.network_type = 2; - } - else { - stellar_activeTx.network_type = 3; - } +bool stellar_signingInit(const StellarSignTx *msg) { + memzero(&stellar_activeTx, sizeof(StellarTransaction)); + stellar_signing = true; + // Initialize signing context + sha256_Init(&(stellar_activeTx.sha256_ctx)); + + // Calculate sha256 for network passphrase + // max length defined in messages.options + uint8_t network_hash[32]; + sha256_Raw((uint8_t *)msg->network_passphrase, + strnlen(msg->network_passphrase, 1024), network_hash); + + uint8_t tx_type_bytes[4] = {0x00, 0x00, 0x00, 0x02}; + + // Copy some data into the active tx + stellar_activeTx.num_operations = msg->num_operations; + + // Start building what will be signed: + // sha256 of: + // sha256(network passphrase) + // 4-byte unsigned big-endian int type constant (2 for tx) + // remaining bytes are operations added in subsequent messages + stellar_hashupdate_bytes(network_hash, sizeof(network_hash)); + stellar_hashupdate_bytes(tx_type_bytes, sizeof(tx_type_bytes)); + + // Public key comes from deriving the specified account path + const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count); + if (!node) { + return false; + } + memcpy(&(stellar_activeTx.signing_pubkey), node->public_key + 1, + sizeof(stellar_activeTx.signing_pubkey)); + + stellar_activeTx.address_n_count = msg->address_n_count; + // todo: fix sizeof check + memcpy(&(stellar_activeTx.address_n), &(msg->address_n), + sizeof(stellar_activeTx.address_n)); + + // Hash: public key + stellar_hashupdate_address(node->public_key + 1); + + // Hash: fee + stellar_hashupdate_uint32(msg->fee); + + // Hash: sequence number + stellar_hashupdate_uint64(msg->sequence_number); + + // Timebounds are only present if timebounds_start or timebounds_end is + // non-zero + uint8_t has_timebounds = + (msg->timebounds_start > 0 || msg->timebounds_end > 0); + if (has_timebounds) { + // Hash: the "has timebounds?" boolean + stellar_hashupdate_bool(true); + + // Timebounds are sent as uint32s since that's all we can display, but they + // must be hashed as 64-bit values + stellar_hashupdate_uint32(0); + stellar_hashupdate_uint32(msg->timebounds_start); - return true; + stellar_hashupdate_uint32(0); + stellar_hashupdate_uint32(msg->timebounds_end); + } + // No timebounds, hash a false boolean + else { + stellar_hashupdate_bool(false); + } + + // Hash: memo + stellar_hashupdate_uint32(msg->memo_type); + switch (msg->memo_type) { + // None, nothing else to do + case 0: + break; + // Text: 4 bytes (size) + up to 28 bytes + case 1: + stellar_hashupdate_string((unsigned char *)&(msg->memo_text), + strnlen(msg->memo_text, 28)); + break; + // ID (8 bytes, uint64) + case 2: + stellar_hashupdate_uint64(msg->memo_id); + break; + // Hash and return are the same data structure (32 byte tx hash) + case 3: + case 4: + stellar_hashupdate_bytes(msg->memo_hash.bytes, msg->memo_hash.size); + break; + default: + break; + } + + // Hash: number of operations + stellar_hashupdate_uint32(msg->num_operations); + + // Determine what type of network this transaction is for + if (strncmp("Public Global Stellar Network ; September 2015", + msg->network_passphrase, 1024) == 0) { + stellar_activeTx.network_type = 1; + } else if (strncmp("Test SDF Network ; September 2015", + msg->network_passphrase, 1024) == 0) { + stellar_activeTx.network_type = 2; + } else { + stellar_activeTx.network_type = 3; + } + + return true; } -bool stellar_confirmSourceAccount(bool has_source_account, const char *str_account) -{ - if (!has_source_account) { - stellar_hashupdate_bool(false); - return true; - } - - // Convert account string to public key bytes - uint8_t bytes[32]; - if (!stellar_getAddressBytes(str_account, bytes)) { - return false; - } - - const char **str_addr_rows = stellar_lineBreakAddress(bytes); - - stellar_layoutTransactionDialog( - _("Op src account OK?"), - NULL, - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - - // Hash: source account - stellar_hashupdate_address(bytes); - +bool stellar_confirmSourceAccount(bool has_source_account, + const char *str_account) { + if (!has_source_account) { + stellar_hashupdate_bool(false); return true; -} + } -bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } + // Convert account string to public key bytes + uint8_t bytes[32]; + if (!stellar_getAddressBytes(str_account, bytes)) { + return false; + } - // Hash: operation type - stellar_hashupdate_uint32(0); + const char **str_addr_rows = stellar_lineBreakAddress(bytes); - // Validate new account and convert to bytes - uint8_t new_account_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->new_account, new_account_bytes)) { - stellar_signingAbort(_("Invalid new account address")); - return false; - } + stellar_layoutTransactionDialog(_("Op src account OK?"), NULL, + str_addr_rows[0], str_addr_rows[1], + str_addr_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } - const char **str_addr_rows = stellar_lineBreakAddress(new_account_bytes); + // Hash: source account + stellar_hashupdate_address(bytes); - // Amount being funded - char str_amount_line[32]; - char str_amount[32]; - stellar_format_stroops(msg->starting_balance, str_amount, sizeof(str_amount)); - - strlcpy(str_amount_line, _("With "), sizeof(str_amount_line)); - strlcat(str_amount_line, str_amount, sizeof(str_amount_line)); - strlcat(str_amount_line, _(" XLM"), sizeof(str_amount_line)); - - stellar_layoutTransactionDialog( - _("Create account: "), - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2], - str_amount_line - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - - // Hash: address - stellar_hashupdate_address(new_account_bytes); - // Hash: starting amount - stellar_hashupdate_uint64(msg->starting_balance); - - stellar_activeTx.confirmed_operations++; - return true; + return true; } -bool stellar_confirmPaymentOp(const StellarPaymentOp *msg) -{ - if (!stellar_signing) return false; +bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(0); + + // Validate new account and convert to bytes + uint8_t new_account_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->new_account, new_account_bytes)) { + stellar_signingAbort(_("Invalid new account address")); + return false; + } + + const char **str_addr_rows = stellar_lineBreakAddress(new_account_bytes); + + // Amount being funded + char str_amount_line[32]; + char str_amount[32]; + stellar_format_stroops(msg->starting_balance, str_amount, sizeof(str_amount)); + + strlcpy(str_amount_line, _("With "), sizeof(str_amount_line)); + strlcat(str_amount_line, str_amount, sizeof(str_amount_line)); + strlcat(str_amount_line, _(" XLM"), sizeof(str_amount_line)); + + stellar_layoutTransactionDialog(_("Create account: "), str_addr_rows[0], + str_addr_rows[1], str_addr_rows[2], + str_amount_line); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash: address + stellar_hashupdate_address(new_account_bytes); + // Hash: starting amount + stellar_hashupdate_uint64(msg->starting_balance); + + stellar_activeTx.confirmed_operations++; + return true; +} - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } +bool stellar_confirmPaymentOp(const StellarPaymentOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(1); + + // Validate destination account and convert to bytes + uint8_t destination_account_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->destination_account, + destination_account_bytes)) { + stellar_signingAbort(_("Invalid destination account")); + return false; + } + + const char **str_addr_rows = + stellar_lineBreakAddress(destination_account_bytes); + + // To: G... + char str_to[32]; + strlcpy(str_to, _("To: "), sizeof(str_to)); + strlcat(str_to, str_addr_rows[0], sizeof(str_to)); + + char str_asset_row[32]; + memzero(str_asset_row, sizeof(str_asset_row)); + stellar_format_asset(&(msg->asset), str_asset_row, sizeof(str_asset_row)); + + char str_pay_amount[32]; + char str_amount[32]; + stellar_format_stroops(msg->amount, str_amount, sizeof(str_amount)); + + strlcpy(str_pay_amount, _("Pay "), sizeof(str_pay_amount)); + strlcat(str_pay_amount, str_amount, sizeof(str_pay_amount)); + + stellar_layoutTransactionDialog(str_pay_amount, str_asset_row, str_to, + str_addr_rows[1], str_addr_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash destination + stellar_hashupdate_address(destination_account_bytes); + // asset + stellar_hashupdate_asset(&(msg->asset)); + // amount (even though amount is signed it doesn't matter for hashing) + stellar_hashupdate_uint64(msg->amount); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} - // Hash: operation type - stellar_hashupdate_uint32(1); +bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(2); + + // Validate destination account and convert to bytes + uint8_t destination_account_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->destination_account, + destination_account_bytes)) { + stellar_signingAbort(_("Invalid destination account")); + return false; + } + const char **str_dest_rows = + stellar_lineBreakAddress(destination_account_bytes); + + // To: G... + char str_to[32]; + strlcpy(str_to, _("To: "), sizeof(str_to)); + strlcat(str_to, str_dest_rows[0], sizeof(str_to)); + + char str_send_asset[32]; + char str_dest_asset[32]; + stellar_format_asset(&(msg->send_asset), str_send_asset, + sizeof(str_send_asset)); + stellar_format_asset(&(msg->destination_asset), str_dest_asset, + sizeof(str_dest_asset)); + + char str_pay_amount[32]; + char str_amount[32]; + stellar_format_stroops(msg->destination_amount, str_amount, + sizeof(str_amount)); + + strlcpy(str_pay_amount, _("Path Pay "), sizeof(str_pay_amount)); + strlcat(str_pay_amount, str_amount, sizeof(str_pay_amount)); + + // Confirm what the receiver will get + /* + Path Pay 100 + JPY (G1234ABCDEF) + To: G.... + .... + .... + */ + stellar_layoutTransactionDialog(str_pay_amount, str_dest_asset, str_to, + str_dest_rows[1], str_dest_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Confirm what the sender is using to pay + char str_source_amount[32]; + char str_source_number[32]; + stellar_format_stroops(msg->send_max, str_source_number, + sizeof(str_source_number)); + + strlcpy(str_source_amount, _("Pay Using "), sizeof(str_source_amount)); + strlcat(str_source_amount, str_source_number, sizeof(str_source_amount)); + + stellar_layoutTransactionDialog(str_source_amount, str_send_asset, NULL, + _("This is the amount debited"), + _("from your account.")); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + // Note: no confirmation for intermediate steps since they don't impact the + // user + + // Hash send asset + stellar_hashupdate_asset(&(msg->send_asset)); + // send max (signed vs. unsigned doesn't matter wrt hashing) + stellar_hashupdate_uint64(msg->send_max); + // destination account + stellar_hashupdate_address(destination_account_bytes); + // destination asset + stellar_hashupdate_asset(&(msg->destination_asset)); + // destination amount + stellar_hashupdate_uint64(msg->destination_amount); + + // paths are stored as an array so hash the number of elements as a uint32 + stellar_hashupdate_uint32(msg->paths_count); + for (uint8_t i = 0; i < msg->paths_count; i++) { + stellar_hashupdate_asset(&(msg->paths[i])); + } + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} - // Validate destination account and convert to bytes - uint8_t destination_account_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->destination_account, destination_account_bytes)) { - stellar_signingAbort(_("Invalid destination account")); - return false; - } +bool stellar_confirmManageOfferOp(const StellarManageOfferOp *msg) { + if (!stellar_signing) return false; - const char **str_addr_rows = stellar_lineBreakAddress(destination_account_bytes); + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } - // To: G... - char str_to[32]; - strlcpy(str_to, _("To: "), sizeof(str_to)); - strlcat(str_to, str_addr_rows[0], sizeof(str_to)); + // Hash: operation type + stellar_hashupdate_uint32(3); - char str_asset_row[32]; - memzero(str_asset_row, sizeof(str_asset_row)); - stellar_format_asset(&(msg->asset), str_asset_row, sizeof(str_asset_row)); + // New Offer / Delete #123 / Update #123 + char str_offer[32]; + if (msg->offer_id == 0) { + strlcpy(str_offer, _("New Offer"), sizeof(str_offer)); + } else { + char str_offer_id[20]; + stellar_format_uint64(msg->offer_id, str_offer_id, sizeof(str_offer_id)); - char str_pay_amount[32]; - char str_amount[32]; - stellar_format_stroops(msg->amount, str_amount, sizeof(str_amount)); - - strlcpy(str_pay_amount, _("Pay "), sizeof(str_pay_amount)); - strlcat(str_pay_amount, str_amount, sizeof(str_pay_amount)); - - stellar_layoutTransactionDialog( - str_pay_amount, - str_asset_row, - str_to, - str_addr_rows[1], - str_addr_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - - // Hash destination - stellar_hashupdate_address(destination_account_bytes); - // asset - stellar_hashupdate_asset(&(msg->asset)); - // amount (even though amount is signed it doesn't matter for hashing) - stellar_hashupdate_uint64(msg->amount); + if (msg->amount == 0) { + strlcpy(str_offer, _("Delete #"), sizeof(str_offer)); + } else { + strlcpy(str_offer, _("Update #"), sizeof(str_offer)); + } + + strlcat(str_offer, str_offer_id, sizeof(str_offer)); + } + + char str_selling[32]; + char str_sell_amount[32]; + char str_selling_asset[32]; + + stellar_format_asset(&(msg->selling_asset), str_selling_asset, + sizeof(str_selling_asset)); + stellar_format_stroops(msg->amount, str_sell_amount, sizeof(str_sell_amount)); + + /* + Sell 200 + XLM (Native Asset) + */ + strlcpy(str_selling, _("Sell "), sizeof(str_selling)); + strlcat(str_selling, str_sell_amount, sizeof(str_selling)); + + char str_buying[32]; + char str_buying_asset[32]; + char str_price[32]; + + stellar_format_asset(&(msg->buying_asset), str_buying_asset, + sizeof(str_buying_asset)); + stellar_format_price(msg->price_n, msg->price_d, str_price, + sizeof(str_price)); + + /* + For 0.675952 Per + USD (G12345678) + */ + strlcpy(str_buying, _("For "), sizeof(str_buying)); + strlcat(str_buying, str_price, sizeof(str_buying)); + strlcat(str_buying, _(" Per"), sizeof(str_buying)); + + stellar_layoutTransactionDialog(str_offer, str_selling, str_selling_asset, + str_buying, str_buying_asset); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash selling asset + stellar_hashupdate_asset(&(msg->selling_asset)); + // buying asset + stellar_hashupdate_asset(&(msg->buying_asset)); + // amount to sell (signed vs. unsigned doesn't matter wrt hashing) + stellar_hashupdate_uint64(msg->amount); + // numerator + stellar_hashupdate_uint32(msg->price_n); + // denominator + stellar_hashupdate_uint32(msg->price_d); + // offer ID + stellar_hashupdate_uint64(msg->offer_id); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; +bool stellar_confirmCreatePassiveOfferOp( + const StellarCreatePassiveOfferOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(4); + + // New Offer / Delete #123 / Update #123 + char str_offer[32]; + if (msg->amount == 0) { + strlcpy(str_offer, _("Delete Passive Offer"), sizeof(str_offer)); + } else { + strlcpy(str_offer, _("New Passive Offer"), sizeof(str_offer)); + } + + char str_selling[32]; + char str_sell_amount[32]; + char str_selling_asset[32]; + + stellar_format_asset(&(msg->selling_asset), str_selling_asset, + sizeof(str_selling_asset)); + stellar_format_stroops(msg->amount, str_sell_amount, sizeof(str_sell_amount)); + + /* + Sell 200 + XLM (Native Asset) + */ + strlcpy(str_selling, _("Sell "), sizeof(str_selling)); + strlcat(str_selling, str_sell_amount, sizeof(str_selling)); + + char str_buying[32]; + char str_buying_asset[32]; + char str_price[32]; + + stellar_format_asset(&(msg->buying_asset), str_buying_asset, + sizeof(str_buying_asset)); + stellar_format_price(msg->price_n, msg->price_d, str_price, + sizeof(str_price)); + + /* + For 0.675952 Per + USD (G12345678) + */ + strlcpy(str_buying, _("For "), sizeof(str_buying)); + strlcat(str_buying, str_price, sizeof(str_buying)); + strlcat(str_buying, _(" Per"), sizeof(str_buying)); + + stellar_layoutTransactionDialog(str_offer, str_selling, str_selling_asset, + str_buying, str_buying_asset); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash selling asset + stellar_hashupdate_asset(&(msg->selling_asset)); + // buying asset + stellar_hashupdate_asset(&(msg->buying_asset)); + // amount to sell (signed vs. unsigned doesn't matter wrt hashing) + stellar_hashupdate_uint64(msg->amount); + // numerator + stellar_hashupdate_uint32(msg->price_n); + // denominator + stellar_hashupdate_uint32(msg->price_d); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; } -bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg) -{ - if (!stellar_signing) return false; +bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) { + if (!stellar_signing) return false; - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } - // Hash: operation type - stellar_hashupdate_uint32(2); + // Hash: operation type + stellar_hashupdate_uint32(5); - // Validate destination account and convert to bytes - uint8_t destination_account_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->destination_account, destination_account_bytes)) { - stellar_signingAbort(_("Invalid destination account")); - return false; - } - const char **str_dest_rows = stellar_lineBreakAddress(destination_account_bytes); + // Something like Set Inflation Destination + char str_title[32]; + char rows[4][32]; + int row_idx = 0; + memzero(rows, sizeof(rows)); - // To: G... - char str_to[32]; - strlcpy(str_to, _("To: "), sizeof(str_to)); - strlcat(str_to, str_dest_rows[0], sizeof(str_to)); + // Inflation destination + stellar_hashupdate_bool(msg->has_inflation_destination_account); + if (msg->has_inflation_destination_account) { + strlcpy(str_title, _("Set Inflation Destination"), sizeof(str_title)); - char str_send_asset[32]; - char str_dest_asset[32]; - stellar_format_asset(&(msg->send_asset), str_send_asset, sizeof(str_send_asset)); - stellar_format_asset(&(msg->destination_asset), str_dest_asset, sizeof(str_dest_asset)); - - char str_pay_amount[32]; - char str_amount[32]; - stellar_format_stroops(msg->destination_amount, str_amount, sizeof(str_amount)); - - strlcpy(str_pay_amount, _("Path Pay "), sizeof(str_pay_amount)); - strlcat(str_pay_amount, str_amount, sizeof(str_pay_amount)); - - // Confirm what the receiver will get - /* - Path Pay 100 - JPY (G1234ABCDEF) - To: G.... - .... - .... - */ - stellar_layoutTransactionDialog( - str_pay_amount, - str_dest_asset, - str_to, - str_dest_rows[1], - str_dest_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; + // Validate account and convert to bytes + uint8_t inflation_destination_account_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->inflation_destination_account, + inflation_destination_account_bytes)) { + stellar_signingAbort(_("Invalid inflation destination account")); + return false; } + const char **str_addr_rows = + stellar_lineBreakAddress(inflation_destination_account_bytes); - // Confirm what the sender is using to pay - char str_source_amount[32]; - char str_source_number[32]; - stellar_format_stroops(msg->send_max, str_source_number, sizeof(str_source_number)); - - strlcpy(str_source_amount, _("Pay Using "), sizeof(str_source_amount)); - strlcat(str_source_amount, str_source_number, sizeof(str_source_amount)); - - stellar_layoutTransactionDialog( - str_source_amount, - str_send_asset, - NULL, - _("This is the amount debited"), - _("from your account.") - ); + stellar_layoutTransactionDialog(str_title, NULL, str_addr_rows[0], + str_addr_rows[1], str_addr_rows[2]); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - // Note: no confirmation for intermediate steps since they don't impact the user - - // Hash send asset - stellar_hashupdate_asset(&(msg->send_asset)); - // send max (signed vs. unsigned doesn't matter wrt hashing) - stellar_hashupdate_uint64(msg->send_max); - // destination account - stellar_hashupdate_address(destination_account_bytes); - // destination asset - stellar_hashupdate_asset(&(msg->destination_asset)); - // destination amount - stellar_hashupdate_uint64(msg->destination_amount); - - // paths are stored as an array so hash the number of elements as a uint32 - stellar_hashupdate_uint32(msg->paths_count); - for (uint8_t i=0; i < msg->paths_count; i++) { - stellar_hashupdate_asset(&(msg->paths[i])); + stellar_signingAbort(_("User canceled")); + return false; } - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; -} + // address + stellar_hashupdate_address(inflation_destination_account_bytes); + } -bool stellar_confirmManageOfferOp(const StellarManageOfferOp *msg) -{ - if (!stellar_signing) return false; + // Clear flags + stellar_hashupdate_bool(msg->has_clear_flags); + if (msg->has_clear_flags) { + strlcpy(str_title, _("Clear Flag(s)"), sizeof(str_title)); - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; + // Auth required + if (msg->clear_flags & 0x01) { + strlcpy(rows[row_idx], _("AUTH_REQUIRED"), sizeof(rows[row_idx])); + row_idx++; } - - // Hash: operation type - stellar_hashupdate_uint32(3); - - // New Offer / Delete #123 / Update #123 - char str_offer[32]; - if (msg->offer_id == 0) { - strlcpy(str_offer, _("New Offer"), sizeof(str_offer)); - } - else { - char str_offer_id[20]; - stellar_format_uint64(msg->offer_id, str_offer_id, sizeof(str_offer_id)); - - if (msg->amount == 0) { - strlcpy(str_offer, _("Delete #"), sizeof(str_offer)); - } - else { - strlcpy(str_offer, _("Update #"), sizeof(str_offer)); - } - - strlcat(str_offer, str_offer_id, sizeof(str_offer)); + // Auth revocable + if (msg->clear_flags & 0x02) { + strlcpy(rows[row_idx], _("AUTH_REVOCABLE"), sizeof(rows[row_idx])); + row_idx++; } - char str_selling[32]; - char str_sell_amount[32]; - char str_selling_asset[32]; - - stellar_format_asset(&(msg->selling_asset), str_selling_asset, sizeof(str_selling_asset)); - stellar_format_stroops(msg->amount, str_sell_amount, sizeof(str_sell_amount)); - - /* - Sell 200 - XLM (Native Asset) - */ - strlcpy(str_selling, _("Sell "), sizeof(str_selling)); - strlcat(str_selling, str_sell_amount, sizeof(str_selling)); - - char str_buying[32]; - char str_buying_asset[32]; - char str_price[32]; - - stellar_format_asset(&(msg->buying_asset), str_buying_asset, sizeof(str_buying_asset)); - stellar_format_price(msg->price_n, msg->price_d, str_price, sizeof(str_price)); - - /* - For 0.675952 Per - USD (G12345678) - */ - strlcpy(str_buying, _("For "), sizeof(str_buying)); - strlcat(str_buying, str_price, sizeof(str_buying)); - strlcat(str_buying, _(" Per"), sizeof(str_buying)); - - stellar_layoutTransactionDialog( - str_offer, - str_selling, - str_selling_asset, - str_buying, - str_buying_asset - ); + stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2], + rows[3]); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; + stellar_signingAbort(_("User canceled")); + return false; } + memzero(rows, sizeof(rows)); + row_idx = 0; - // Hash selling asset - stellar_hashupdate_asset(&(msg->selling_asset)); - // buying asset - stellar_hashupdate_asset(&(msg->buying_asset)); - // amount to sell (signed vs. unsigned doesn't matter wrt hashing) - stellar_hashupdate_uint64(msg->amount); - // numerator - stellar_hashupdate_uint32(msg->price_n); - // denominator - stellar_hashupdate_uint32(msg->price_d); - // offer ID - stellar_hashupdate_uint64(msg->offer_id); - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; -} - -bool stellar_confirmCreatePassiveOfferOp(const StellarCreatePassiveOfferOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } + // Hash flags + stellar_hashupdate_uint32(msg->clear_flags); + } - // Hash: operation type - stellar_hashupdate_uint32(4); + // Set flags + stellar_hashupdate_bool(msg->has_set_flags); + if (msg->has_set_flags) { + strlcpy(str_title, _("Set Flag(s)"), sizeof(str_title)); - // New Offer / Delete #123 / Update #123 - char str_offer[32]; - if (msg->amount == 0) { - strlcpy(str_offer, _("Delete Passive Offer"), sizeof(str_offer)); + // Auth required + if (msg->set_flags & 0x01) { + strlcpy(rows[row_idx], _("AUTH_REQUIRED"), sizeof(rows[row_idx])); + row_idx++; } - else { - strlcpy(str_offer, _("New Passive Offer"), sizeof(str_offer)); + // Auth revocable + if (msg->set_flags & 0x02) { + strlcpy(rows[row_idx], _("AUTH_REVOCABLE"), sizeof(rows[row_idx])); + row_idx++; } - char str_selling[32]; - char str_sell_amount[32]; - char str_selling_asset[32]; - - stellar_format_asset(&(msg->selling_asset), str_selling_asset, sizeof(str_selling_asset)); - stellar_format_stroops(msg->amount, str_sell_amount, sizeof(str_sell_amount)); - - /* - Sell 200 - XLM (Native Asset) - */ - strlcpy(str_selling, _("Sell "), sizeof(str_selling)); - strlcat(str_selling, str_sell_amount, sizeof(str_selling)); - - char str_buying[32]; - char str_buying_asset[32]; - char str_price[32]; - - stellar_format_asset(&(msg->buying_asset), str_buying_asset, sizeof(str_buying_asset)); - stellar_format_price(msg->price_n, msg->price_d, str_price, sizeof(str_price)); - - /* - For 0.675952 Per - USD (G12345678) - */ - strlcpy(str_buying, _("For "), sizeof(str_buying)); - strlcat(str_buying, str_price, sizeof(str_buying)); - strlcat(str_buying, _(" Per"), sizeof(str_buying)); - - stellar_layoutTransactionDialog( - str_offer, - str_selling, - str_selling_asset, - str_buying, - str_buying_asset - ); + stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2], + rows[3]); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; + stellar_signingAbort(_("User canceled")); + return false; } - - // Hash selling asset - stellar_hashupdate_asset(&(msg->selling_asset)); - // buying asset - stellar_hashupdate_asset(&(msg->buying_asset)); - // amount to sell (signed vs. unsigned doesn't matter wrt hashing) - stellar_hashupdate_uint64(msg->amount); - // numerator - stellar_hashupdate_uint32(msg->price_n); - // denominator - stellar_hashupdate_uint32(msg->price_d); - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; -} - -bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } - - // Hash: operation type - stellar_hashupdate_uint32(5); - - // Something like Set Inflation Destination - char str_title[32]; - char rows[4][32]; - int row_idx = 0; memzero(rows, sizeof(rows)); - - // Inflation destination - stellar_hashupdate_bool(msg->has_inflation_destination_account); - if (msg->has_inflation_destination_account) { - strlcpy(str_title, _("Set Inflation Destination"), sizeof(str_title)); - - // Validate account and convert to bytes - uint8_t inflation_destination_account_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->inflation_destination_account, inflation_destination_account_bytes)) { - stellar_signingAbort(_("Invalid inflation destination account")); - return false; - } - const char **str_addr_rows = stellar_lineBreakAddress(inflation_destination_account_bytes); - - stellar_layoutTransactionDialog( - str_title, - NULL, - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - - // address - stellar_hashupdate_address(inflation_destination_account_bytes); - } - - // Clear flags - stellar_hashupdate_bool(msg->has_clear_flags); - if (msg->has_clear_flags) { - strlcpy(str_title, _("Clear Flag(s)"), sizeof(str_title)); - - // Auth required - if (msg->clear_flags & 0x01) { - strlcpy(rows[row_idx], _("AUTH_REQUIRED"), sizeof(rows[row_idx])); - row_idx++; - } - // Auth revocable - if (msg->clear_flags & 0x02) { - strlcpy(rows[row_idx], _("AUTH_REVOCABLE"), sizeof(rows[row_idx])); - row_idx++; - } - - stellar_layoutTransactionDialog( - str_title, - rows[0], - rows[1], - rows[2], - rows[3] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - memzero(rows, sizeof(rows)); - row_idx = 0; - - // Hash flags - stellar_hashupdate_uint32(msg->clear_flags); - } - - // Set flags - stellar_hashupdate_bool(msg->has_set_flags); - if (msg->has_set_flags) { - strlcpy(str_title, _("Set Flag(s)"), sizeof(str_title)); - - // Auth required - if (msg->set_flags & 0x01) { - strlcpy(rows[row_idx], _("AUTH_REQUIRED"), sizeof(rows[row_idx])); - row_idx++; - } - // Auth revocable - if (msg->set_flags & 0x02) { - strlcpy(rows[row_idx], _("AUTH_REVOCABLE"), sizeof(rows[row_idx])); - row_idx++; - } - - stellar_layoutTransactionDialog( - str_title, - rows[0], - rows[1], - rows[2], - rows[3] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - memzero(rows, sizeof(rows)); - row_idx = 0; - - // Hash flags - stellar_hashupdate_uint32(msg->set_flags); - } - - // Account thresholds - bool show_thresholds_confirm = false; row_idx = 0; - stellar_hashupdate_bool(msg->has_master_weight); - if (msg->has_master_weight) { - char str_master_weight[10+1]; - show_thresholds_confirm = true; - stellar_format_uint32(msg->master_weight, str_master_weight, sizeof(str_master_weight)); - strlcpy(rows[row_idx], _("Master Weight: "), sizeof(rows[row_idx])); - strlcat(rows[row_idx], str_master_weight, sizeof(rows[row_idx])); - row_idx++; - - // Hash master weight - stellar_hashupdate_uint32(msg->master_weight); - } - - stellar_hashupdate_bool(msg->has_low_threshold); - if (msg->has_low_threshold) { - char str_low_threshold[10+1]; - show_thresholds_confirm = true; - stellar_format_uint32(msg->low_threshold, str_low_threshold, sizeof(str_low_threshold)); - strlcpy(rows[row_idx], _("Low: "), sizeof(rows[row_idx])); - strlcat(rows[row_idx], str_low_threshold, sizeof(rows[row_idx])); - row_idx++; - - // Hash low threshold - stellar_hashupdate_uint32(msg->low_threshold); - } - stellar_hashupdate_bool(msg->has_medium_threshold); - if (msg->has_medium_threshold) { - char str_med_threshold[10+1]; - show_thresholds_confirm = true; - stellar_format_uint32(msg->medium_threshold, str_med_threshold, sizeof(str_med_threshold)); - strlcpy(rows[row_idx], _("Medium: "), sizeof(rows[row_idx])); - strlcat(rows[row_idx], str_med_threshold, sizeof(rows[row_idx])); - row_idx++; - - // Hash medium threshold - stellar_hashupdate_uint32(msg->medium_threshold); - } - stellar_hashupdate_bool(msg->has_high_threshold); - if (msg->has_high_threshold) { - char str_high_threshold[10+1]; - show_thresholds_confirm = true; - stellar_format_uint32(msg->high_threshold, str_high_threshold, sizeof(str_high_threshold)); - strlcpy(rows[row_idx], _("High: "), sizeof(rows[row_idx])); - strlcat(rows[row_idx], str_high_threshold, sizeof(rows[row_idx])); - row_idx++; - - // Hash high threshold - stellar_hashupdate_uint32(msg->high_threshold); - } - - if (show_thresholds_confirm) { - strlcpy(str_title, _("Account Thresholds"), sizeof(str_title)); - stellar_layoutTransactionDialog( - str_title, - rows[0], - rows[1], - rows[2], - rows[3] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - memzero(rows, sizeof(rows)); - row_idx = 0; - } - - // Home domain - stellar_hashupdate_bool(msg->has_home_domain); - if (msg->has_home_domain) { - strlcpy(str_title, _("Home Domain"), sizeof(str_title)); - - // Split home domain if longer than 22 characters - int home_domain_len = strnlen(msg->home_domain, 32); - if (home_domain_len > 22) { - strlcpy(rows[0], msg->home_domain, 22); - strlcpy(rows[1], msg->home_domain + 21, sizeof(rows[1])); - } - else { - strlcpy(rows[0], msg->home_domain, sizeof(rows[0])); - } - - stellar_layoutTransactionDialog( - str_title, - rows[0], - rows[1], - NULL, - NULL - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - memzero(rows, sizeof(rows)); - row_idx = 0; - - stellar_hashupdate_string((unsigned char*)&(msg->home_domain), strnlen(msg->home_domain, 32)); - } - // Signer - stellar_hashupdate_bool(msg->has_signer_type); - if (msg->has_signer_type) { - if (msg->signer_weight > 0) { - strlcpy(str_title, _("Add Signer: "), sizeof(str_title)); - } - else { - strlcpy(str_title, _("REMOVE Signer: "), sizeof(str_title)); - } - - // Format weight as a string - char str_weight[16]; - stellar_format_uint32(msg->signer_weight, str_weight, sizeof(str_weight)); - char str_weight_row[32]; - strlcpy(str_weight_row, _("Weight: "), sizeof(str_weight_row)); - strlcat(str_weight_row, str_weight, sizeof(str_weight_row)); - - // 0 = account, 1 = pre-auth, 2 = hash(x) - char str_signer_type[16]; - bool needs_hash_confirm = false; - if (msg->signer_type == 0) { - strlcpy(str_signer_type, _("account"), sizeof(str_signer_type)); - strlcat(str_title, str_signer_type, sizeof(str_title)); - - const char **str_addr_rows = stellar_lineBreakAddress(msg->signer_key.bytes); - stellar_layoutTransactionDialog( - str_title, - str_weight_row, - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - } - if (msg->signer_type == 1) { - needs_hash_confirm = true; - strlcpy(str_signer_type, _("pre-auth hash"), sizeof(str_signer_type)); - strlcat(str_title, str_signer_type, sizeof(str_title)); - - stellar_layoutTransactionDialog( - str_title, - str_weight_row, - NULL, - _("(confirm hash on next"), - _("screen)") - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - } - if (msg->signer_type == 2) { - needs_hash_confirm = true; - strlcpy(str_signer_type, _("hash(x)"), sizeof(str_signer_type)); - strlcat(str_title, str_signer_type, sizeof(str_title)); - - stellar_layoutTransactionDialog( - str_title, - str_weight_row, - NULL, - _("(confirm hash on next"), - _("screen)") - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - } - - // Extra confirmation step for hash signers - if (needs_hash_confirm) { - data2hex(msg->signer_key.bytes + 0, 8, rows[row_idx++]); - data2hex(msg->signer_key.bytes + 8, 8, rows[row_idx++]); - data2hex(msg->signer_key.bytes + 16, 8, rows[row_idx++]); - data2hex(msg->signer_key.bytes + 24, 8, rows[row_idx++]); - - stellar_layoutTransactionDialog( - _("Confirm Hash"), - rows[0], - rows[1], - rows[2], - rows[3] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - memzero(rows, sizeof(rows)); - row_idx = 0; - } - - // Hash: signer type - stellar_hashupdate_uint32(msg->signer_type); - // key - stellar_hashupdate_bytes(msg->signer_key.bytes, msg->signer_key.size); - // weight - stellar_hashupdate_uint32(msg->signer_weight); + // Hash flags + stellar_hashupdate_uint32(msg->set_flags); + } + + // Account thresholds + bool show_thresholds_confirm = false; + row_idx = 0; + stellar_hashupdate_bool(msg->has_master_weight); + if (msg->has_master_weight) { + char str_master_weight[10 + 1]; + show_thresholds_confirm = true; + stellar_format_uint32(msg->master_weight, str_master_weight, + sizeof(str_master_weight)); + strlcpy(rows[row_idx], _("Master Weight: "), sizeof(rows[row_idx])); + strlcat(rows[row_idx], str_master_weight, sizeof(rows[row_idx])); + row_idx++; + + // Hash master weight + stellar_hashupdate_uint32(msg->master_weight); + } + + stellar_hashupdate_bool(msg->has_low_threshold); + if (msg->has_low_threshold) { + char str_low_threshold[10 + 1]; + show_thresholds_confirm = true; + stellar_format_uint32(msg->low_threshold, str_low_threshold, + sizeof(str_low_threshold)); + strlcpy(rows[row_idx], _("Low: "), sizeof(rows[row_idx])); + strlcat(rows[row_idx], str_low_threshold, sizeof(rows[row_idx])); + row_idx++; + + // Hash low threshold + stellar_hashupdate_uint32(msg->low_threshold); + } + stellar_hashupdate_bool(msg->has_medium_threshold); + if (msg->has_medium_threshold) { + char str_med_threshold[10 + 1]; + show_thresholds_confirm = true; + stellar_format_uint32(msg->medium_threshold, str_med_threshold, + sizeof(str_med_threshold)); + strlcpy(rows[row_idx], _("Medium: "), sizeof(rows[row_idx])); + strlcat(rows[row_idx], str_med_threshold, sizeof(rows[row_idx])); + row_idx++; + + // Hash medium threshold + stellar_hashupdate_uint32(msg->medium_threshold); + } + stellar_hashupdate_bool(msg->has_high_threshold); + if (msg->has_high_threshold) { + char str_high_threshold[10 + 1]; + show_thresholds_confirm = true; + stellar_format_uint32(msg->high_threshold, str_high_threshold, + sizeof(str_high_threshold)); + strlcpy(rows[row_idx], _("High: "), sizeof(rows[row_idx])); + strlcat(rows[row_idx], str_high_threshold, sizeof(rows[row_idx])); + row_idx++; + + // Hash high threshold + stellar_hashupdate_uint32(msg->high_threshold); + } + + if (show_thresholds_confirm) { + strlcpy(str_title, _("Account Thresholds"), sizeof(str_title)); + stellar_layoutTransactionDialog(str_title, rows[0], rows[1], rows[2], + rows[3]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; } - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; -} - -bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; + memzero(rows, sizeof(rows)); + row_idx = 0; + } + + // Home domain + stellar_hashupdate_bool(msg->has_home_domain); + if (msg->has_home_domain) { + strlcpy(str_title, _("Home Domain"), sizeof(str_title)); + + // Split home domain if longer than 22 characters + int home_domain_len = strnlen(msg->home_domain, 32); + if (home_domain_len > 22) { + strlcpy(rows[0], msg->home_domain, 22); + strlcpy(rows[1], msg->home_domain + 21, sizeof(rows[1])); + } else { + strlcpy(rows[0], msg->home_domain, sizeof(rows[0])); } - // Hash: operation type - stellar_hashupdate_uint32(6); - - // Add Trust: USD - char str_title[32]; - if (msg->limit == 0) { - strlcpy(str_title, _("DELETE Trust: "), sizeof(str_title)); - } - else { - strlcpy(str_title, _("Add Trust: "), sizeof(str_title)); + stellar_layoutTransactionDialog(str_title, rows[0], rows[1], NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; } - strlcat(str_title, msg->asset.code, sizeof(str_title)); - - // Amount: MAX (or a number) - char str_amount_row[32]; - strlcpy(str_amount_row, _("Amount: "), sizeof(str_amount_row)); + memzero(rows, sizeof(rows)); + row_idx = 0; - if (msg->limit == 9223372036854775807) { - strlcat(str_amount_row, _("[Maximum]"), sizeof(str_amount_row)); - } - else { - char str_amount[32]; - stellar_format_stroops(msg->limit, str_amount, sizeof(str_amount)); - strlcat(str_amount_row, str_amount, sizeof(str_amount_row)); - } + stellar_hashupdate_string((unsigned char *)&(msg->home_domain), + strnlen(msg->home_domain, 32)); + } - // Validate destination account and convert to bytes - uint8_t asset_issuer_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->asset.issuer, asset_issuer_bytes)) { + // Signer + stellar_hashupdate_bool(msg->has_signer_type); + if (msg->has_signer_type) { + if (msg->signer_weight > 0) { + strlcpy(str_title, _("Add Signer: "), sizeof(str_title)); + } else { + strlcpy(str_title, _("REMOVE Signer: "), sizeof(str_title)); + } + + // Format weight as a string + char str_weight[16]; + stellar_format_uint32(msg->signer_weight, str_weight, sizeof(str_weight)); + char str_weight_row[32]; + strlcpy(str_weight_row, _("Weight: "), sizeof(str_weight_row)); + strlcat(str_weight_row, str_weight, sizeof(str_weight_row)); + + // 0 = account, 1 = pre-auth, 2 = hash(x) + char str_signer_type[16]; + bool needs_hash_confirm = false; + if (msg->signer_type == 0) { + strlcpy(str_signer_type, _("account"), sizeof(str_signer_type)); + strlcat(str_title, str_signer_type, sizeof(str_title)); + + const char **str_addr_rows = + stellar_lineBreakAddress(msg->signer_key.bytes); + stellar_layoutTransactionDialog(str_title, str_weight_row, + str_addr_rows[0], str_addr_rows[1], + str_addr_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { stellar_signingAbort(_("User canceled")); - fsm_sendFailure(FailureType_Failure_ProcessError, _("Invalid asset issuer")); return false; + } } + if (msg->signer_type == 1) { + needs_hash_confirm = true; + strlcpy(str_signer_type, _("pre-auth hash"), sizeof(str_signer_type)); + strlcat(str_title, str_signer_type, sizeof(str_title)); - // Display full issuer address - const char **str_addr_rows = stellar_lineBreakAddress(asset_issuer_bytes); - - stellar_layoutTransactionDialog( - str_title, - str_amount_row, - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_layoutTransactionDialog(str_title, str_weight_row, NULL, + _("(confirm hash on next"), _("screen)")); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { stellar_signingAbort(_("User canceled")); return false; + } } + if (msg->signer_type == 2) { + needs_hash_confirm = true; + strlcpy(str_signer_type, _("hash(x)"), sizeof(str_signer_type)); + strlcat(str_title, str_signer_type, sizeof(str_title)); - // Hash: asset - stellar_hashupdate_asset(&(msg->asset)); - // limit - stellar_hashupdate_uint64(msg->limit); - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; -} - -bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } - - // Hash: operation type - stellar_hashupdate_uint32(7); - - // Add Trust: USD - char str_title[32]; - if (msg->is_authorized) { - strlcpy(str_title, _("Allow Trust of"), sizeof(str_title)); - } - else { - strlcpy(str_title, _("REVOKE Trust of"), sizeof(str_title)); - } - - // Asset code - char str_asset_row[32]; - strlcpy(str_asset_row, msg->asset_code, sizeof(str_asset_row)); - - // Validate account and convert to bytes - uint8_t trusted_account_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->trusted_account, trusted_account_bytes)) { - stellar_signingAbort(_("Invalid trusted account")); - return false; - } - - const char **str_trustor_rows = stellar_lineBreakAddress(trusted_account_bytes); - - // By: G... - char str_by[32]; - strlcpy(str_by, _("By: "), sizeof(str_by)); - strlcat(str_by, str_trustor_rows[0], sizeof(str_by)); - - stellar_layoutTransactionDialog( - str_title, - str_asset_row, - str_by, - str_trustor_rows[1], - str_trustor_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_layoutTransactionDialog(str_title, str_weight_row, NULL, + _("(confirm hash on next"), _("screen)")); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { stellar_signingAbort(_("User canceled")); return false; + } } - // Hash: trustor account (the account being allowed to access the asset) - stellar_hashupdate_address(trusted_account_bytes); - // asset type - stellar_hashupdate_uint32(msg->asset_type); - // asset code - if (msg->asset_type == 1) { - char code4[4+1]; - memzero(code4, sizeof(code4)); - strlcpy(code4, msg->asset_code, sizeof(code4)); - stellar_hashupdate_bytes((uint8_t *)code4, 4); - } - if (msg->asset_type == 2) { - char code12[12+1]; - memzero(code12, sizeof(code12)); - strlcpy(code12, msg->asset_code, sizeof(code12)); - stellar_hashupdate_bytes((uint8_t *)code12, 12); - } - // is authorized - stellar_hashupdate_bool(msg->is_authorized); - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; -} - -bool stellar_confirmAccountMergeOp(const StellarAccountMergeOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } - - // Hash: operation type - stellar_hashupdate_uint32(8); - - // Validate account and convert to bytes - uint8_t destination_account_bytes[STELLAR_KEY_SIZE]; - if (!stellar_getAddressBytes(msg->destination_account, destination_account_bytes)) { - stellar_signingAbort(_("Invalid destination account")); - return false; - } - - const char **str_destination_rows = stellar_lineBreakAddress(destination_account_bytes); + // Extra confirmation step for hash signers + if (needs_hash_confirm) { + data2hex(msg->signer_key.bytes + 0, 8, rows[row_idx++]); + data2hex(msg->signer_key.bytes + 8, 8, rows[row_idx++]); + data2hex(msg->signer_key.bytes + 16, 8, rows[row_idx++]); + data2hex(msg->signer_key.bytes + 24, 8, rows[row_idx++]); - stellar_layoutTransactionDialog( - _("Merge Account"), - _("All XLM will be sent to:"), - str_destination_rows[0], - str_destination_rows[1], - str_destination_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_layoutTransactionDialog(_("Confirm Hash"), rows[0], rows[1], + rows[2], rows[3]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { stellar_signingAbort(_("User canceled")); return false; - } - - // Hash: destination account - stellar_hashupdate_address(destination_account_bytes); - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; + } + memzero(rows, sizeof(rows)); + row_idx = 0; + } + + // Hash: signer type + stellar_hashupdate_uint32(msg->signer_type); + // key + stellar_hashupdate_bytes(msg->signer_key.bytes, msg->signer_key.size); + // weight + stellar_hashupdate_uint32(msg->signer_weight); + } + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; } -bool stellar_confirmManageDataOp(const StellarManageDataOp *msg) -{ - if (!stellar_signing) return false; - - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } - - // Hash: operation type - stellar_hashupdate_uint32(10); +bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(6); + + // Add Trust: USD + char str_title[32]; + if (msg->limit == 0) { + strlcpy(str_title, _("DELETE Trust: "), sizeof(str_title)); + } else { + strlcpy(str_title, _("Add Trust: "), sizeof(str_title)); + } + strlcat(str_title, msg->asset.code, sizeof(str_title)); + + // Amount: MAX (or a number) + char str_amount_row[32]; + strlcpy(str_amount_row, _("Amount: "), sizeof(str_amount_row)); + + if (msg->limit == 9223372036854775807) { + strlcat(str_amount_row, _("[Maximum]"), sizeof(str_amount_row)); + } else { + char str_amount[32]; + stellar_format_stroops(msg->limit, str_amount, sizeof(str_amount)); + strlcat(str_amount_row, str_amount, sizeof(str_amount_row)); + } + + // Validate destination account and convert to bytes + uint8_t asset_issuer_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->asset.issuer, asset_issuer_bytes)) { + stellar_signingAbort(_("User canceled")); + fsm_sendFailure(FailureType_Failure_ProcessError, + _("Invalid asset issuer")); + return false; + } + + // Display full issuer address + const char **str_addr_rows = stellar_lineBreakAddress(asset_issuer_bytes); + + stellar_layoutTransactionDialog(str_title, str_amount_row, str_addr_rows[0], + str_addr_rows[1], str_addr_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash: asset + stellar_hashupdate_asset(&(msg->asset)); + // limit + stellar_hashupdate_uint64(msg->limit); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} - char str_title[32]; - if (msg->has_value) { - strlcpy(str_title, _("Set data value key:"), sizeof(str_title)); - } - else { - strlcpy(str_title, _("CLEAR data value key:"), sizeof(str_title)); - } +bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(7); + + // Add Trust: USD + char str_title[32]; + if (msg->is_authorized) { + strlcpy(str_title, _("Allow Trust of"), sizeof(str_title)); + } else { + strlcpy(str_title, _("REVOKE Trust of"), sizeof(str_title)); + } + + // Asset code + char str_asset_row[32]; + strlcpy(str_asset_row, msg->asset_code, sizeof(str_asset_row)); + + // Validate account and convert to bytes + uint8_t trusted_account_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->trusted_account, trusted_account_bytes)) { + stellar_signingAbort(_("Invalid trusted account")); + return false; + } + + const char **str_trustor_rows = + stellar_lineBreakAddress(trusted_account_bytes); + + // By: G... + char str_by[32]; + strlcpy(str_by, _("By: "), sizeof(str_by)); + strlcat(str_by, str_trustor_rows[0], sizeof(str_by)); + + stellar_layoutTransactionDialog(str_title, str_asset_row, str_by, + str_trustor_rows[1], str_trustor_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash: trustor account (the account being allowed to access the asset) + stellar_hashupdate_address(trusted_account_bytes); + // asset type + stellar_hashupdate_uint32(msg->asset_type); + // asset code + if (msg->asset_type == 1) { + char code4[4 + 1]; + memzero(code4, sizeof(code4)); + strlcpy(code4, msg->asset_code, sizeof(code4)); + stellar_hashupdate_bytes((uint8_t *)code4, 4); + } + if (msg->asset_type == 2) { + char code12[12 + 1]; + memzero(code12, sizeof(code12)); + strlcpy(code12, msg->asset_code, sizeof(code12)); + stellar_hashupdate_bytes((uint8_t *)code12, 12); + } + // is authorized + stellar_hashupdate_bool(msg->is_authorized); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} - // Confirm key - const char **str_key_lines = split_message((const uint8_t*)(msg->key), strnlen(msg->key, 64), 16); +bool stellar_confirmAccountMergeOp(const StellarAccountMergeOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(8); + + // Validate account and convert to bytes + uint8_t destination_account_bytes[STELLAR_KEY_SIZE]; + if (!stellar_getAddressBytes(msg->destination_account, + destination_account_bytes)) { + stellar_signingAbort(_("Invalid destination account")); + return false; + } + + const char **str_destination_rows = + stellar_lineBreakAddress(destination_account_bytes); + + stellar_layoutTransactionDialog( + _("Merge Account"), _("All XLM will be sent to:"), + str_destination_rows[0], str_destination_rows[1], + str_destination_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Hash: destination account + stellar_hashupdate_address(destination_account_bytes); + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; +} - stellar_layoutTransactionDialog( - str_title, - str_key_lines[0], - str_key_lines[1], - str_key_lines[2], - str_key_lines[3] - ); +bool stellar_confirmManageDataOp(const StellarManageDataOp *msg) { + if (!stellar_signing) return false; + + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } + + // Hash: operation type + stellar_hashupdate_uint32(10); + + char str_title[32]; + if (msg->has_value) { + strlcpy(str_title, _("Set data value key:"), sizeof(str_title)); + } else { + strlcpy(str_title, _("CLEAR data value key:"), sizeof(str_title)); + } + + // Confirm key + const char **str_key_lines = + split_message((const uint8_t *)(msg->key), strnlen(msg->key, 64), 16); + + stellar_layoutTransactionDialog(str_title, str_key_lines[0], str_key_lines[1], + str_key_lines[2], str_key_lines[3]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } + + // Confirm value by displaying sha256 hash since this can contain + // non-printable characters + if (msg->has_value) { + strlcpy(str_title, _("Confirm sha256 of value:"), sizeof(str_title)); + + char str_hash_digest[SHA256_DIGEST_STRING_LENGTH]; + sha256_Data(msg->value.bytes, msg->value.size, str_hash_digest); + const char **str_hash_lines = split_message( + (const uint8_t *)str_hash_digest, sizeof(str_hash_digest), 16); + + stellar_layoutTransactionDialog(str_title, str_hash_lines[0], + str_hash_lines[1], str_hash_lines[2], + str_hash_lines[3]); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - - // Confirm value by displaying sha256 hash since this can contain non-printable characters - if (msg->has_value) { - strlcpy(str_title, _("Confirm sha256 of value:"), sizeof(str_title)); - - char str_hash_digest[SHA256_DIGEST_STRING_LENGTH]; - sha256_Data(msg->value.bytes, msg->value.size, str_hash_digest); - const char **str_hash_lines = split_message((const uint8_t*)str_hash_digest, sizeof(str_hash_digest), 16); - - stellar_layoutTransactionDialog( - str_title, - str_hash_lines[0], - str_hash_lines[1], - str_hash_lines[2], - str_hash_lines[3] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } - } - - // Hash: key - stellar_hashupdate_string((unsigned char*)&(msg->key), strnlen(msg->key, 64)); - // value - if (msg->has_value) { - stellar_hashupdate_bool(true); - // Variable opaque field is length + raw bytes - stellar_hashupdate_uint32(msg->value.size); - stellar_hashupdate_bytes(msg->value.bytes, msg->value.size); - } - else { - stellar_hashupdate_bool(false); - } - - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; + stellar_signingAbort(_("User canceled")); + return false; + } + } + + // Hash: key + stellar_hashupdate_string((unsigned char *)&(msg->key), + strnlen(msg->key, 64)); + // value + if (msg->has_value) { + stellar_hashupdate_bool(true); + // Variable opaque field is length + raw bytes + stellar_hashupdate_uint32(msg->value.size); + stellar_hashupdate_bytes(msg->value.bytes, msg->value.size); + } else { + stellar_hashupdate_bool(false); + } + + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; } -bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg) -{ - if (!stellar_signing) return false; +bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg) { + if (!stellar_signing) return false; - if (!stellar_confirmSourceAccount(msg->has_source_account, msg->source_account)) { - stellar_signingAbort(_("Source account error")); - return false; - } + if (!stellar_confirmSourceAccount(msg->has_source_account, + msg->source_account)) { + stellar_signingAbort(_("Source account error")); + return false; + } - // Hash: operation type - stellar_hashupdate_uint32(11); + // Hash: operation type + stellar_hashupdate_uint32(11); - char str_bump_to[20]; - stellar_format_uint64(msg->bump_to, str_bump_to, sizeof(str_bump_to)); + char str_bump_to[20]; + stellar_format_uint64(msg->bump_to, str_bump_to, sizeof(str_bump_to)); - stellar_layoutTransactionDialog( - _("Bump Sequence"), - _("Set sequence to:"), - str_bump_to, - NULL, - NULL - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return false; - } + stellar_layoutTransactionDialog(_("Bump Sequence"), _("Set sequence to:"), + str_bump_to, NULL, NULL); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return false; + } - // Hash: bump to - stellar_hashupdate_uint64(msg->bump_to); + // Hash: bump to + stellar_hashupdate_uint64(msg->bump_to); - // At this point, the operation is confirmed - stellar_activeTx.confirmed_operations++; - return true; + // At this point, the operation is confirmed + stellar_activeTx.confirmed_operations++; + return true; } -void stellar_signingAbort(const char *reason) -{ - if (!reason) { - reason = _("Unknown error"); - } +void stellar_signingAbort(const char *reason) { + if (!reason) { + reason = _("Unknown error"); + } - stellar_signing = false; - fsm_sendFailure(FailureType_Failure_ProcessError, reason); - layoutHome(); + stellar_signing = false; + fsm_sendFailure(FailureType_Failure_ProcessError, reason); + layoutHome(); } /** * Populates the fields of resp with the signature of the active transaction */ -void stellar_fillSignedTx(StellarSignedTx *resp) -{ - // Finalize the transaction by hashing 4 null bytes representing a (currently unused) empty union - stellar_hashupdate_uint32(0); - - // Add the public key for verification that the right account was used for signing - memcpy(resp->public_key.bytes, stellar_activeTx.signing_pubkey, 32); - resp->public_key.size = 32; - resp->has_public_key = true; - - // Add the signature (note that this does not include the 4-byte hint since it can be calculated from the public key) - uint8_t signature[64]; - // Note: this calls sha256_Final on the hash context - stellar_getSignatureForActiveTx(signature); - memcpy(resp->signature.bytes, signature, sizeof(signature)); - resp->signature.size = sizeof(signature); - resp->has_signature = true; +void stellar_fillSignedTx(StellarSignedTx *resp) { + // Finalize the transaction by hashing 4 null bytes representing a (currently + // unused) empty union + stellar_hashupdate_uint32(0); + + // Add the public key for verification that the right account was used for + // signing + memcpy(resp->public_key.bytes, stellar_activeTx.signing_pubkey, 32); + resp->public_key.size = 32; + resp->has_public_key = true; + + // Add the signature (note that this does not include the 4-byte hint since it + // can be calculated from the public key) + uint8_t signature[64]; + // Note: this calls sha256_Final on the hash context + stellar_getSignatureForActiveTx(signature); + memcpy(resp->signature.bytes, signature, sizeof(signature)); + resp->signature.size = sizeof(signature); + resp->has_signature = true; } -bool stellar_allOperationsConfirmed() -{ - return stellar_activeTx.confirmed_operations == stellar_activeTx.num_operations; +bool stellar_allOperationsConfirmed() { + return stellar_activeTx.confirmed_operations == + stellar_activeTx.num_operations; } /* * Calculates and sets the signature for the active transaction */ -void stellar_getSignatureForActiveTx(uint8_t *out_signature) -{ - const HDNode *node = stellar_deriveNode(stellar_activeTx.address_n, stellar_activeTx.address_n_count); - if (!node) { - // return empty signature when we can't derive node - memzero(out_signature, 64); - return; - } - - // Signature is the ed25519 detached signature of the sha256 of all the bytes - // that have been read so far - uint8_t to_sign[32]; - sha256_Final(&(stellar_activeTx.sha256_ctx), to_sign); - - uint8_t signature[64]; - ed25519_sign(to_sign, sizeof(to_sign), node->private_key, node->public_key + 1, signature); - - memcpy(out_signature, signature, sizeof(signature)); +void stellar_getSignatureForActiveTx(uint8_t *out_signature) { + const HDNode *node = stellar_deriveNode(stellar_activeTx.address_n, + stellar_activeTx.address_n_count); + if (!node) { + // return empty signature when we can't derive node + memzero(out_signature, 64); + return; + } + + // Signature is the ed25519 detached signature of the sha256 of all the bytes + // that have been read so far + uint8_t to_sign[32]; + sha256_Final(&(stellar_activeTx.sha256_ctx), to_sign); + + uint8_t signature[64]; + ed25519_sign(to_sign, sizeof(to_sign), node->private_key, + node->public_key + 1, signature); + + memcpy(out_signature, signature, sizeof(signature)); } /* * Returns number (representing stroops) formatted as XLM - * For example, if number has value 1000000000 then it will be returned as "100.0" + * For example, if number has value 1000000000 then it will be returned as + * "100.0" */ -void stellar_format_stroops(uint64_t number, char *out, size_t outlen) -{ - bn_format_uint64(number, NULL, NULL, 7, 0, false, out, outlen); +void stellar_format_stroops(uint64_t number, char *out, size_t outlen) { + bn_format_uint64(number, NULL, NULL, 7, 0, false, out, outlen); } /* * Formats a price represented as a uint32 numerator and uint32 denominator * - * Note that there may be a loss of precision between the real price value and what - * is shown to the user + * Note that there may be a loss of precision between the real price value and + * what is shown to the user * * Smallest possible price is 1 / 4294967296 which is: * 0.00000000023283064365386962890625 @@ -1269,77 +1196,74 @@ void stellar_format_stroops(uint64_t number, char *out, size_t outlen) * largest possible price is: * 4294967296 */ -void stellar_format_price(uint32_t numerator, uint32_t denominator, char *out, size_t outlen) -{ - memzero(out, outlen); - - // early exit for invalid denominator - if (denominator == 0) { - strlcpy(out, _("[Invalid Price]"), outlen); - return; - } - - // early exit for zero - if (numerator == 0) { - strlcpy(out, "0", outlen); - return; - } - - int scale = 0; - uint64_t value = numerator; - while (value < (UINT64_MAX / 10)) { - value *= 10; - scale++; - } - value /= denominator; - while (value < (UINT64_MAX / 10)) { - value *= 10; - scale++; - } - - // Format with bn_format_uint64 - bn_format_uint64(value, NULL, NULL, scale, 0, false, out, outlen); +void stellar_format_price(uint32_t numerator, uint32_t denominator, char *out, + size_t outlen) { + memzero(out, outlen); + + // early exit for invalid denominator + if (denominator == 0) { + strlcpy(out, _("[Invalid Price]"), outlen); + return; + } + + // early exit for zero + if (numerator == 0) { + strlcpy(out, "0", outlen); + return; + } + + int scale = 0; + uint64_t value = numerator; + while (value < (UINT64_MAX / 10)) { + value *= 10; + scale++; + } + value /= denominator; + while (value < (UINT64_MAX / 10)) { + value *= 10; + scale++; + } + + // Format with bn_format_uint64 + bn_format_uint64(value, NULL, NULL, scale, 0, false, out, outlen); } /* * Returns a uint32 formatted as a string */ -void stellar_format_uint32(uint32_t number, char *out, size_t outlen) -{ - bignum256 bn_number; - bn_read_uint32(number, &bn_number); - bn_format(&bn_number, NULL, NULL, 0, 0, false, out, outlen); +void stellar_format_uint32(uint32_t number, char *out, size_t outlen) { + bignum256 bn_number; + bn_read_uint32(number, &bn_number); + bn_format(&bn_number, NULL, NULL, 0, 0, false, out, outlen); } /* * Returns a uint64 formatted as a string */ -void stellar_format_uint64(uint64_t number, char *out, size_t outlen) -{ - bn_format_uint64(number, NULL, NULL, 0, 0, false, out, outlen); +void stellar_format_uint64(uint64_t number, char *out, size_t outlen) { + bn_format_uint64(number, NULL, NULL, 0, 0, false, out, outlen); } /* * Breaks a 56 character address into 3 lines of lengths 16, 20, 20 * This is to allow a small label to be prepended to the first line */ -const char **stellar_lineBreakAddress(const uint8_t *addrbytes) -{ - char str_fulladdr[56+1]; - static char rows[3][20+1]; +const char **stellar_lineBreakAddress(const uint8_t *addrbytes) { + char str_fulladdr[56 + 1]; + static char rows[3][20 + 1]; - memzero(rows, sizeof(rows)); + memzero(rows, sizeof(rows)); - // get full address string - stellar_publicAddressAsStr(addrbytes, str_fulladdr, sizeof(str_fulladdr)); + // get full address string + stellar_publicAddressAsStr(addrbytes, str_fulladdr, sizeof(str_fulladdr)); - // Break it into 3 lines - strlcpy(rows[0], str_fulladdr + 0, 17); - strlcpy(rows[1], str_fulladdr + 16, 21); - strlcpy(rows[2], str_fulladdr + 16 + 20, 21); + // Break it into 3 lines + strlcpy(rows[0], str_fulladdr + 0, 17); + strlcpy(rows[1], str_fulladdr + 16, 21); + strlcpy(rows[2], str_fulladdr + 16 + 20, 21); - static const char *ret[3] = { rows[0], rows[1], rows[2] }; - return ret; + static const char *ret[3] = {rows[0], rows[1], rows[2]}; + return ret; } /* @@ -1350,450 +1274,429 @@ const char **stellar_lineBreakAddress(const uint8_t *addrbytes) * MOBI (G123456789000) * ALPHA12EXAMP (G0987) */ -void stellar_format_asset(const StellarAssetType *asset, char *str_formatted, size_t len) -{ - char str_asset_code[12 + 1]; - // truncated asset issuer, final length depends on length of asset code - char str_asset_issuer_trunc[13 + 1]; - - memzero(str_formatted, len); - memzero(str_asset_code, sizeof(str_asset_code)); - memzero(str_asset_issuer_trunc, sizeof(str_asset_issuer_trunc)); - - // Validate issuer account for non-native assets - if (asset->type != 0 && !stellar_validateAddress(asset->issuer)) { - stellar_signingAbort(_("Invalid asset issuer")); - return; - } - - // Native asset - if (asset->type == 0) { - strlcpy(str_formatted, _("XLM (native asset)"), len); - } - // 4-character custom - if (asset->type == 1) { - memcpy(str_asset_code, asset->code, 4); - strlcpy(str_formatted, str_asset_code, len); - - // Truncate issuer to 13 chars - memcpy(str_asset_issuer_trunc, asset->issuer, 13); - } - // 12-character custom - if (asset->type == 2) { - memcpy(str_asset_code, asset->code, 12); - strlcpy(str_formatted, str_asset_code, len); - - // Truncate issuer to 5 characters - memcpy(str_asset_issuer_trunc, asset->issuer, 5); - } - // Issuer is read the same way for both types of custom assets - if (asset->type == 1 || asset->type == 2) { - strlcat(str_formatted, _(" ("), len); - strlcat(str_formatted, str_asset_issuer_trunc, len); - strlcat(str_formatted, _(")"), len); - } +void stellar_format_asset(const StellarAssetType *asset, char *str_formatted, + size_t len) { + char str_asset_code[12 + 1]; + // truncated asset issuer, final length depends on length of asset code + char str_asset_issuer_trunc[13 + 1]; + + memzero(str_formatted, len); + memzero(str_asset_code, sizeof(str_asset_code)); + memzero(str_asset_issuer_trunc, sizeof(str_asset_issuer_trunc)); + + // Validate issuer account for non-native assets + if (asset->type != 0 && !stellar_validateAddress(asset->issuer)) { + stellar_signingAbort(_("Invalid asset issuer")); + return; + } + + // Native asset + if (asset->type == 0) { + strlcpy(str_formatted, _("XLM (native asset)"), len); + } + // 4-character custom + if (asset->type == 1) { + memcpy(str_asset_code, asset->code, 4); + strlcpy(str_formatted, str_asset_code, len); + + // Truncate issuer to 13 chars + memcpy(str_asset_issuer_trunc, asset->issuer, 13); + } + // 12-character custom + if (asset->type == 2) { + memcpy(str_asset_code, asset->code, 12); + strlcpy(str_formatted, str_asset_code, len); + + // Truncate issuer to 5 characters + memcpy(str_asset_issuer_trunc, asset->issuer, 5); + } + // Issuer is read the same way for both types of custom assets + if (asset->type == 1 || asset->type == 2) { + strlcat(str_formatted, _(" ("), len); + strlcat(str_formatted, str_asset_issuer_trunc, len); + strlcat(str_formatted, _(")"), len); + } } -size_t stellar_publicAddressAsStr(const uint8_t *bytes, char *out, size_t outlen) -{ - // version + key bytes + checksum - uint8_t keylen = 1 + 32 + 2; - uint8_t bytes_full[keylen]; - bytes_full[0] = 6 << 3; // 'G' +size_t stellar_publicAddressAsStr(const uint8_t *bytes, char *out, + size_t outlen) { + // version + key bytes + checksum + uint8_t keylen = 1 + 32 + 2; + uint8_t bytes_full[keylen]; + bytes_full[0] = 6 << 3; // 'G' - memcpy(bytes_full + 1, bytes, 32); + memcpy(bytes_full + 1, bytes, 32); - // Last two bytes are the checksum - uint16_t checksum = stellar_crc16(bytes_full, 33); - bytes_full[keylen-2] = checksum & 0x00ff; - bytes_full[keylen-1] = (checksum>>8) & 0x00ff; + // Last two bytes are the checksum + uint16_t checksum = stellar_crc16(bytes_full, 33); + bytes_full[keylen - 2] = checksum & 0x00ff; + bytes_full[keylen - 1] = (checksum >> 8) & 0x00ff; - base32_encode(bytes_full, keylen, out, outlen, BASE32_ALPHABET_RFC4648); + base32_encode(bytes_full, keylen, out, outlen, BASE32_ALPHABET_RFC4648); - // Public key will always be 56 characters - return 56; + // Public key will always be 56 characters + return 56; } /** * Stellar account string is a base32-encoded string that starts with "G" * * It decodes to the following format: - * Byte 0 - always 0x30 ("G" when base32 encoded), version byte indicating a public key - * Bytes 1-33 - 32-byte public key bytes - * Bytes 34-35 - 2-byte CRC16 checksum of the version byte + public key bytes (first 33 bytes) + * Byte 0 - always 0x30 ("G" when base32 encoded), version byte indicating a + * public key Bytes 1-33 - 32-byte public key bytes Bytes 34-35 - 2-byte CRC16 + * checksum of the version byte + public key bytes (first 33 bytes) * - * Note that the stellar "seed" (private key) also uses this format except the version byte - * is 0xC0 which encodes to "S" in base32 + * Note that the stellar "seed" (private key) also uses this format except the + * version byte is 0xC0 which encodes to "S" in base32 */ -bool stellar_validateAddress(const char *str_address) -{ - bool valid = false; - uint8_t decoded[STELLAR_ADDRESS_SIZE_RAW]; - - if (strlen(str_address) != STELLAR_ADDRESS_SIZE) { - return false; - } - - // Check that it decodes correctly - uint8_t *ret = base32_decode(str_address, STELLAR_ADDRESS_SIZE, decoded, sizeof(decoded), BASE32_ALPHABET_RFC4648); - valid = (ret != NULL); - - // ... and that version byte is 0x30 - if (valid && decoded[0] != 0x30) { - valid = false; - } - - // ... and that checksums match - uint16_t checksum_expected = stellar_crc16(decoded, 33); - uint16_t checksum_actual = (decoded[34] << 8) | decoded[33]; // unsigned short (little endian) - if (valid && checksum_expected != checksum_actual) { - valid = false; - } - - memzero(decoded, sizeof(decoded)); - return valid; +bool stellar_validateAddress(const char *str_address) { + bool valid = false; + uint8_t decoded[STELLAR_ADDRESS_SIZE_RAW]; + + if (strlen(str_address) != STELLAR_ADDRESS_SIZE) { + return false; + } + + // Check that it decodes correctly + uint8_t *ret = base32_decode(str_address, STELLAR_ADDRESS_SIZE, decoded, + sizeof(decoded), BASE32_ALPHABET_RFC4648); + valid = (ret != NULL); + + // ... and that version byte is 0x30 + if (valid && decoded[0] != 0x30) { + valid = false; + } + + // ... and that checksums match + uint16_t checksum_expected = stellar_crc16(decoded, 33); + uint16_t checksum_actual = + (decoded[34] << 8) | decoded[33]; // unsigned short (little endian) + if (valid && checksum_expected != checksum_actual) { + valid = false; + } + + memzero(decoded, sizeof(decoded)); + return valid; } /** * Converts a string address (G...) to the 32-byte raw address */ -bool stellar_getAddressBytes(const char* str_address, uint8_t *out_bytes) -{ - uint8_t decoded[STELLAR_ADDRESS_SIZE_RAW]; +bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes) { + uint8_t decoded[STELLAR_ADDRESS_SIZE_RAW]; - // Ensure address is valid - if (!stellar_validateAddress(str_address)) return false; + // Ensure address is valid + if (!stellar_validateAddress(str_address)) return false; - base32_decode(str_address, STELLAR_ADDRESS_SIZE, decoded, sizeof(decoded), BASE32_ALPHABET_RFC4648); + base32_decode(str_address, STELLAR_ADDRESS_SIZE, decoded, sizeof(decoded), + BASE32_ALPHABET_RFC4648); - // The 32 bytes with offset 1-33 represent the public key - memcpy(out_bytes, &decoded[1], 32); + // The 32 bytes with offset 1-33 represent the public key + memcpy(out_bytes, &decoded[1], 32); - memzero(decoded, sizeof(decoded)); - return true; + memzero(decoded, sizeof(decoded)); + return true; } /* * CRC16 implementation compatible with the Stellar version - * Ported from this implementation: http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java.html - * Initial value changed to 0x0000 to match Stellar + * Ported from this implementation: + * http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java.html Initial + * value changed to 0x0000 to match Stellar */ -uint16_t stellar_crc16(uint8_t *bytes, uint32_t length) -{ - // Calculate checksum for existing bytes - uint16_t crc = 0x0000; - uint16_t polynomial = 0x1021; - uint32_t i; - uint8_t bit; - uint8_t byte; - uint8_t bitidx; - uint8_t c15; - - for (i=0; i < length; i++) { - byte = bytes[i]; - for (bitidx=0; bitidx < 8; bitidx++) { - bit = ((byte >> (7 - bitidx) & 1) == 1); - c15 = ((crc >> 15 & 1) == 1); - crc <<= 1; - if (c15 ^ bit) crc ^= polynomial; - } - } - - return crc & 0xffff; +uint16_t stellar_crc16(uint8_t *bytes, uint32_t length) { + // Calculate checksum for existing bytes + uint16_t crc = 0x0000; + uint16_t polynomial = 0x1021; + uint32_t i; + uint8_t bit; + uint8_t byte; + uint8_t bitidx; + uint8_t c15; + + for (i = 0; i < length; i++) { + byte = bytes[i]; + for (bitidx = 0; bitidx < 8; bitidx++) { + bit = ((byte >> (7 - bitidx) & 1) == 1); + c15 = ((crc >> 15 & 1) == 1); + crc <<= 1; + if (c15 ^ bit) crc ^= polynomial; + } + } + + return crc & 0xffff; } /* * Derives the HDNode at the given index - * Standard Stellar prefix is m/44'/148'/ and the default account is m/44'/148'/0' + * Standard Stellar prefix is m/44'/148'/ and the default account is + * m/44'/148'/0' * * All paths must be hardened */ -const HDNode *stellar_deriveNode(const uint32_t *address_n, size_t address_n_count) -{ - static CONFIDENTIAL HDNode node; - const char *curve = "ed25519"; - - // Device not initialized, passphrase request cancelled, or unsupported curve - if (!config_getRootNode(&node, curve, true)) { - return 0; - } - // Failed to derive private key - if (hdnode_private_ckd_cached(&node, address_n, address_n_count, NULL) == 0) { - return 0; - } - - hdnode_fill_public_key(&node); - - return &node; +const HDNode *stellar_deriveNode(const uint32_t *address_n, + size_t address_n_count) { + static CONFIDENTIAL HDNode node; + const char *curve = "ed25519"; + + // Device not initialized, passphrase request cancelled, or unsupported curve + if (!config_getRootNode(&node, curve, true)) { + return 0; + } + // Failed to derive private key + if (hdnode_private_ckd_cached(&node, address_n, address_n_count, NULL) == 0) { + return 0; + } + + hdnode_fill_public_key(&node); + + return &node; } -void stellar_hashupdate_uint32(uint32_t value) -{ - // Ensure uint32 is big endian +void stellar_hashupdate_uint32(uint32_t value) { + // Ensure uint32 is big endian #if BYTE_ORDER == LITTLE_ENDIAN - REVERSE32(value, value); + REVERSE32(value, value); #endif - // Byte values must be hashed as big endian - uint8_t data[4]; - data[3] = (value >> 24) & 0xFF; - data[2] = (value >> 16) & 0xFF; - data[1] = (value >> 8) & 0xFF; - data[0] = value & 0xFF; + // Byte values must be hashed as big endian + uint8_t data[4]; + data[3] = (value >> 24) & 0xFF; + data[2] = (value >> 16) & 0xFF; + data[1] = (value >> 8) & 0xFF; + data[0] = value & 0xFF; - stellar_hashupdate_bytes(data, sizeof(data)); + stellar_hashupdate_bytes(data, sizeof(data)); } -void stellar_hashupdate_uint64(uint64_t value) -{ - // Ensure uint64 is big endian +void stellar_hashupdate_uint64(uint64_t value) { + // Ensure uint64 is big endian #if BYTE_ORDER == LITTLE_ENDIAN - REVERSE64(value, value); + REVERSE64(value, value); #endif - // Byte values must be hashed as big endian - uint8_t data[8]; - data[7] = (value >> 56) & 0xFF; - data[6] = (value >> 48) & 0xFF; - data[5] = (value >> 40) & 0xFF; - data[4] = (value >> 32) & 0xFF; - data[3] = (value >> 24) & 0xFF; - data[2] = (value >> 16) & 0xFF; - data[1] = (value >> 8) & 0xFF; - data[0] = value & 0xFF; - - stellar_hashupdate_bytes(data, sizeof(data)); + // Byte values must be hashed as big endian + uint8_t data[8]; + data[7] = (value >> 56) & 0xFF; + data[6] = (value >> 48) & 0xFF; + data[5] = (value >> 40) & 0xFF; + data[4] = (value >> 32) & 0xFF; + data[3] = (value >> 24) & 0xFF; + data[2] = (value >> 16) & 0xFF; + data[1] = (value >> 8) & 0xFF; + data[0] = value & 0xFF; + + stellar_hashupdate_bytes(data, sizeof(data)); } -void stellar_hashupdate_bool(bool value) -{ - if (value) { - stellar_hashupdate_uint32(1); - } - else { - stellar_hashupdate_uint32(0); - } +void stellar_hashupdate_bool(bool value) { + if (value) { + stellar_hashupdate_uint32(1); + } else { + stellar_hashupdate_uint32(0); + } } -void stellar_hashupdate_string(const uint8_t *data, size_t len) -{ - // Hash the length of the string - stellar_hashupdate_uint32((uint32_t)len); - - // Hash the raw bytes of the string - stellar_hashupdate_bytes(data, len); - - // If len isn't a multiple of 4, add padding bytes - int remainder = len % 4; - uint8_t null_byte[1] = { 0x00 }; - if (remainder) { - while (remainder < 4) { - stellar_hashupdate_bytes(null_byte, 1); - remainder++; - } +void stellar_hashupdate_string(const uint8_t *data, size_t len) { + // Hash the length of the string + stellar_hashupdate_uint32((uint32_t)len); + + // Hash the raw bytes of the string + stellar_hashupdate_bytes(data, len); + + // If len isn't a multiple of 4, add padding bytes + int remainder = len % 4; + uint8_t null_byte[1] = {0x00}; + if (remainder) { + while (remainder < 4) { + stellar_hashupdate_bytes(null_byte, 1); + remainder++; } + } } -void stellar_hashupdate_address(const uint8_t *address_bytes) -{ - // First 4 bytes of an address are the type. There's only one type (0) - stellar_hashupdate_uint32(0); +void stellar_hashupdate_address(const uint8_t *address_bytes) { + // First 4 bytes of an address are the type. There's only one type (0) + stellar_hashupdate_uint32(0); - // Remaining part of the address is 32 bytes - stellar_hashupdate_bytes(address_bytes, 32); + // Remaining part of the address is 32 bytes + stellar_hashupdate_bytes(address_bytes, 32); } /* - * Note about string handling below: this field is an XDR "opaque" field and not a typical string, - * so if "TEST" is the asset code then the hashed value needs to be 4 bytes and not include the null - * at the end of the string + * Note about string handling below: this field is an XDR "opaque" field and not + * a typical string, so if "TEST" is the asset code then the hashed value needs + * to be 4 bytes and not include the null at the end of the string */ -void stellar_hashupdate_asset(const StellarAssetType *asset) -{ - stellar_hashupdate_uint32(asset->type); - - // For non-native assets, validate issuer account and convert to bytes - uint8_t issuer_bytes[STELLAR_KEY_SIZE]; - if (asset->type != 0 && !stellar_getAddressBytes(asset->issuer, issuer_bytes)) { - stellar_signingAbort(_("Invalid asset issuer")); - return; - } - - // 4-character asset code - if (asset->type == 1) { - char code4[4 + 1]; - memzero(code4, sizeof(code4)); - strlcpy(code4, asset->code, sizeof(code4)); - - stellar_hashupdate_bytes((uint8_t *)code4, 4); - stellar_hashupdate_address(issuer_bytes); - } - - // 12-character asset code - if (asset->type == 2) { - char code12[12 + 1]; - memzero(code12, sizeof(code12)); - strlcpy(code12, asset->code, sizeof(code12)); - - stellar_hashupdate_bytes((uint8_t *)code12, 12); - stellar_hashupdate_address(issuer_bytes); - } +void stellar_hashupdate_asset(const StellarAssetType *asset) { + stellar_hashupdate_uint32(asset->type); + + // For non-native assets, validate issuer account and convert to bytes + uint8_t issuer_bytes[STELLAR_KEY_SIZE]; + if (asset->type != 0 && + !stellar_getAddressBytes(asset->issuer, issuer_bytes)) { + stellar_signingAbort(_("Invalid asset issuer")); + return; + } + + // 4-character asset code + if (asset->type == 1) { + char code4[4 + 1]; + memzero(code4, sizeof(code4)); + strlcpy(code4, asset->code, sizeof(code4)); + + stellar_hashupdate_bytes((uint8_t *)code4, 4); + stellar_hashupdate_address(issuer_bytes); + } + + // 12-character asset code + if (asset->type == 2) { + char code12[12 + 1]; + memzero(code12, sizeof(code12)); + strlcpy(code12, asset->code, sizeof(code12)); + + stellar_hashupdate_bytes((uint8_t *)code12, 12); + stellar_hashupdate_address(issuer_bytes); + } } -void stellar_hashupdate_bytes(const uint8_t *data, size_t len) -{ - sha256_Update(&(stellar_activeTx.sha256_ctx), data, len); +void stellar_hashupdate_bytes(const uint8_t *data, size_t len) { + sha256_Update(&(stellar_activeTx.sha256_ctx), data, len); } /* * Displays a summary of the overall transaction */ -void stellar_layoutTransactionSummary(const StellarSignTx *msg) -{ - char str_lines[5][32]; - memzero(str_lines, sizeof(str_lines)); - - char str_fee[12]; - char str_num_ops[12]; - - // Will be set to true for some large hashes that don't fit on one screen - uint8_t needs_memo_hash_confirm = 0; - - // Format the fee - stellar_format_stroops(msg->fee, str_fee, sizeof(str_fee)); - - strlcpy(str_lines[0], _("Fee: "), sizeof(str_lines[0])); - strlcat(str_lines[0], str_fee, sizeof(str_lines[0])); - strlcat(str_lines[0], _(" XLM"), sizeof(str_lines[0])); - - // add in numOperations - stellar_format_uint32(msg->num_operations, str_num_ops, sizeof(str_num_ops)); - - strlcat(str_lines[0], _(" ("), sizeof(str_lines[0])); - strlcat(str_lines[0], str_num_ops, sizeof(str_lines[0])); - if (msg->num_operations == 1) { - strlcat(str_lines[0], _(" op)"), sizeof(str_lines[0])); +void stellar_layoutTransactionSummary(const StellarSignTx *msg) { + char str_lines[5][32]; + memzero(str_lines, sizeof(str_lines)); + + char str_fee[12]; + char str_num_ops[12]; + + // Will be set to true for some large hashes that don't fit on one screen + uint8_t needs_memo_hash_confirm = 0; + + // Format the fee + stellar_format_stroops(msg->fee, str_fee, sizeof(str_fee)); + + strlcpy(str_lines[0], _("Fee: "), sizeof(str_lines[0])); + strlcat(str_lines[0], str_fee, sizeof(str_lines[0])); + strlcat(str_lines[0], _(" XLM"), sizeof(str_lines[0])); + + // add in numOperations + stellar_format_uint32(msg->num_operations, str_num_ops, sizeof(str_num_ops)); + + strlcat(str_lines[0], _(" ("), sizeof(str_lines[0])); + strlcat(str_lines[0], str_num_ops, sizeof(str_lines[0])); + if (msg->num_operations == 1) { + strlcat(str_lines[0], _(" op)"), sizeof(str_lines[0])); + } else { + strlcat(str_lines[0], _(" ops)"), sizeof(str_lines[0])); + } + + // Display full address being used to sign transaction + const char **str_addr_rows = + stellar_lineBreakAddress(stellar_activeTx.signing_pubkey); + + stellar_layoutTransactionDialog(str_lines[0], _("Signing with:"), + str_addr_rows[0], str_addr_rows[1], + str_addr_rows[2]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return; + } + + // Reset lines for displaying memo + memzero(str_lines, sizeof(str_lines)); + + // Memo: none + if (msg->memo_type == 0) { + strlcpy(str_lines[0], _("[No Memo Set]"), sizeof(str_lines[0])); + strlcpy(str_lines[1], _("Important:"), sizeof(str_lines[0])); + strlcpy(str_lines[2], _("Many exchanges require"), sizeof(str_lines[0])); + strlcpy(str_lines[3], _("a memo when depositing."), sizeof(str_lines[0])); + } + // Memo: text + if (msg->memo_type == 1) { + strlcpy(str_lines[0], _("Memo (TEXT)"), sizeof(str_lines[0])); + + // Split 28-character string into two lines of 19 / 9 + // todo: word wrap method? + strlcpy(str_lines[1], (const char *)msg->memo_text, 19 + 1); + strlcpy(str_lines[2], (const char *)(msg->memo_text + 19), 9 + 1); + } + // Memo: ID + if (msg->memo_type == 2) { + strlcpy(str_lines[0], _("Memo (ID)"), sizeof(str_lines[0])); + stellar_format_uint64(msg->memo_id, str_lines[1], sizeof(str_lines[1])); + } + // Memo: hash + if (msg->memo_type == 3) { + needs_memo_hash_confirm = 1; + strlcpy(str_lines[0], _("Memo (HASH)"), sizeof(str_lines[0])); + } + // Memo: return + if (msg->memo_type == 4) { + needs_memo_hash_confirm = 1; + strlcpy(str_lines[0], _("Memo (RETURN)"), sizeof(str_lines[0])); + } + + if (needs_memo_hash_confirm) { + data2hex(msg->memo_hash.bytes + 0, 8, str_lines[1]); + data2hex(msg->memo_hash.bytes + 8, 8, str_lines[2]); + data2hex(msg->memo_hash.bytes + 16, 8, str_lines[3]); + data2hex(msg->memo_hash.bytes + 24, 8, str_lines[4]); + } + + stellar_layoutTransactionDialog(str_lines[0], str_lines[1], str_lines[2], + str_lines[3], str_lines[4]); + if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { + stellar_signingAbort(_("User canceled")); + return; + } + + // Verify timebounds, if present + memzero(str_lines, sizeof(str_lines)); + + // Timebound: lower + if (msg->timebounds_start || msg->timebounds_end) { + time_t timebound; + char str_timebound[32]; + const struct tm *tm; + + timebound = (time_t)msg->timebounds_start; + strlcpy(str_lines[0], _("Valid from:"), sizeof(str_lines[0])); + if (timebound) { + tm = gmtime(&timebound); + strftime(str_timebound, sizeof(str_timebound), "%F %T (UTC)", tm); + strlcpy(str_lines[1], str_timebound, sizeof(str_lines[1])); } else { - strlcat(str_lines[0], _(" ops)"), sizeof(str_lines[0])); - } - - // Display full address being used to sign transaction - const char **str_addr_rows = stellar_lineBreakAddress(stellar_activeTx.signing_pubkey); - - stellar_layoutTransactionDialog( - str_lines[0], - _("Signing with:"), - str_addr_rows[0], - str_addr_rows[1], - str_addr_rows[2] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return; + strlcpy(str_lines[1], _("[no restriction]"), sizeof(str_lines[1])); } - // Reset lines for displaying memo - memzero(str_lines, sizeof(str_lines)); + // Reset for timebound_max + memzero(str_timebound, sizeof(str_timebound)); - // Memo: none - if (msg->memo_type == 0) { - strlcpy(str_lines[0], _("[No Memo Set]"), sizeof(str_lines[0])); - strlcpy(str_lines[1], _("Important:"), sizeof(str_lines[0])); - strlcpy(str_lines[2], _("Many exchanges require"), sizeof(str_lines[0])); - strlcpy(str_lines[3], _("a memo when depositing."), sizeof(str_lines[0])); - } - // Memo: text - if (msg->memo_type == 1) { - strlcpy(str_lines[0], _("Memo (TEXT)"), sizeof(str_lines[0])); - - // Split 28-character string into two lines of 19 / 9 - // todo: word wrap method? - strlcpy(str_lines[1], (const char*)msg->memo_text, 19 + 1); - strlcpy(str_lines[2], (const char*)(msg->memo_text + 19), 9 + 1); - } - // Memo: ID - if (msg->memo_type == 2) { - strlcpy(str_lines[0], _("Memo (ID)"), sizeof(str_lines[0])); - stellar_format_uint64(msg->memo_id, str_lines[1], sizeof(str_lines[1])); - } - // Memo: hash - if (msg->memo_type == 3) { - needs_memo_hash_confirm = 1; - strlcpy(str_lines[0], _("Memo (HASH)"), sizeof(str_lines[0])); - } - // Memo: return - if (msg->memo_type == 4) { - needs_memo_hash_confirm = 1; - strlcpy(str_lines[0], _("Memo (RETURN)"), sizeof(str_lines[0])); - } - - if (needs_memo_hash_confirm) { - data2hex(msg->memo_hash.bytes + 0, 8, str_lines[1]); - data2hex(msg->memo_hash.bytes + 8, 8, str_lines[2]); - data2hex(msg->memo_hash.bytes + 16, 8, str_lines[3]); - data2hex(msg->memo_hash.bytes + 24, 8, str_lines[4]); + timebound = (time_t)msg->timebounds_end; + strlcpy(str_lines[0], _("Valid from:"), sizeof(str_lines[0])); + if (timebound) { + tm = gmtime(&timebound); + strftime(str_timebound, sizeof(str_timebound), "%F %T (UTC)", tm); + strlcpy(str_lines[1], str_timebound, sizeof(str_lines[1])); + } else { + strlcpy(str_lines[1], _("[no restriction]"), sizeof(str_lines[1])); } + } - stellar_layoutTransactionDialog( - str_lines[0], - str_lines[1], - str_lines[2], - str_lines[3], - str_lines[4] - ); + if (msg->timebounds_start || msg->timebounds_end) { + stellar_layoutTransactionDialog(_("Confirm Time Bounds"), str_lines[0], + str_lines[1], str_lines[2], str_lines[3]); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return; - } - - // Verify timebounds, if present - memzero(str_lines, sizeof(str_lines)); - - // Timebound: lower - if (msg->timebounds_start || msg->timebounds_end) { - time_t timebound; - char str_timebound[32]; - const struct tm *tm; - - timebound = (time_t)msg->timebounds_start; - strlcpy(str_lines[0], _("Valid from:"), sizeof(str_lines[0])); - if (timebound) { - tm = gmtime(&timebound); - strftime(str_timebound, sizeof(str_timebound), "%F %T (UTC)", tm); - strlcpy(str_lines[1], str_timebound, sizeof(str_lines[1])); - } - else { - strlcpy(str_lines[1], _("[no restriction]"), sizeof(str_lines[1])); - } - - // Reset for timebound_max - memzero(str_timebound, sizeof(str_timebound)); - - timebound = (time_t)msg->timebounds_end; - strlcpy(str_lines[0], _("Valid from:"), sizeof(str_lines[0])); - if (timebound) { - tm = gmtime(&timebound); - strftime(str_timebound, sizeof(str_timebound), "%F %T (UTC)", tm); - strlcpy(str_lines[1], str_timebound, sizeof(str_lines[1])); - } - else { - strlcpy(str_lines[1], _("[no restriction]"), sizeof(str_lines[1])); - } - } - - if (msg->timebounds_start || msg->timebounds_end) { - stellar_layoutTransactionDialog( - _("Confirm Time Bounds"), - str_lines[0], - str_lines[1], - str_lines[2], - str_lines[3] - ); - if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { - stellar_signingAbort(_("User canceled")); - return; - } + stellar_signingAbort(_("User canceled")); + return; } + } } /* @@ -1803,119 +1706,126 @@ void stellar_layoutTransactionSummary(const StellarSignTx *msg) * - Cancel / Next buttons * - Warning message can appear between cancel/next buttons */ -void stellar_layoutSigningDialog(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, uint32_t *address_n, size_t address_n_count, const char *warning, bool is_final_step) -{ - // Start with some initial padding and use these to track position as rendering moves down the screen - int offset_x = 1; - int offset_y = 1; - int line_height = 9; - - const HDNode *node = stellar_deriveNode(address_n, address_n_count); - if (!node) { - // abort on error - return; - } - - char str_pubaddr_truncated[12]; // G???? + null - memzero(str_pubaddr_truncated, sizeof(str_pubaddr_truncated)); - - layoutLast = layoutDialogSwipe; - layoutSwipe(); - oledClear(); - - // Load up public address - char str_pubaddr[56 + 1]; - memzero(str_pubaddr, sizeof(str_pubaddr)); - stellar_publicAddressAsStr(node->public_key + 1, str_pubaddr, sizeof(str_pubaddr)); - memcpy(str_pubaddr_truncated, str_pubaddr, sizeof(str_pubaddr_truncated) - 1); - - // Header - // Ends up as: Signing with GABCDEFGHIJKL - char str_header[32]; - memzero(str_header, sizeof(str_header)); - strlcpy(str_header, _("Signing with "), sizeof(str_header)); - strlcat(str_header, str_pubaddr_truncated, sizeof(str_header)); - - oledDrawString(offset_x, offset_y, str_header, FONT_STANDARD); - offset_y += line_height; - // Invert color on header - oledInvert(0, 0, OLED_WIDTH, offset_y - 2); - - // Dialog contents begin - if (line1) { - oledDrawString(offset_x, offset_y, line1, FONT_STANDARD); - } - offset_y += line_height; - if (line2) { - oledDrawString(offset_x, offset_y, line2, FONT_STANDARD); - } - offset_y += line_height; - if (line3) { - oledDrawString(offset_x, offset_y, line3, FONT_STANDARD); - } - offset_y += line_height; - if (line4) { - oledDrawString(offset_x, offset_y, line4, FONT_STANDARD); - } - offset_y += line_height; - if (line5) { - oledDrawString(offset_x, offset_y, line5, FONT_STANDARD); - } - offset_y += line_height; - - // Cancel button - oledDrawString(1, OLED_HEIGHT - 8, "\x15", FONT_STANDARD); - oledDrawString(fontCharWidth(FONT_STANDARD, '\x15') + 3, OLED_HEIGHT - 8, "Cancel", FONT_STANDARD); - oledInvert(0, OLED_HEIGHT - 9, fontCharWidth(FONT_STANDARD, '\x15') + oledStringWidth("Cancel", FONT_STANDARD) + 2, OLED_HEIGHT - 1); - - // Warnings (drawn centered between the buttons - if (warning) { - oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 8, warning, FONT_STANDARD); - } - - // Next / sign button - char str_next_label[8]; - if (is_final_step) { - strlcpy(str_next_label, _("SIGN"), sizeof(str_next_label)); - } - else { - strlcpy(str_next_label, _("Next"), sizeof(str_next_label)); - } - - oledDrawString(OLED_WIDTH - fontCharWidth(FONT_STANDARD, '\x06') - 1, OLED_HEIGHT - 8, "\x06", FONT_STANDARD); - oledDrawString(OLED_WIDTH - oledStringWidth(str_next_label, FONT_STANDARD) - fontCharWidth(FONT_STANDARD, '\x06') - 3, OLED_HEIGHT - 8, str_next_label, FONT_STANDARD); - oledInvert(OLED_WIDTH - oledStringWidth(str_next_label, FONT_STANDARD) - fontCharWidth(FONT_STANDARD, '\x06') - 4, OLED_HEIGHT - 9, OLED_WIDTH - 1, OLED_HEIGHT - 1); - - oledRefresh(); +void stellar_layoutSigningDialog(const char *line1, const char *line2, + const char *line3, const char *line4, + const char *line5, uint32_t *address_n, + size_t address_n_count, const char *warning, + bool is_final_step) { + // Start with some initial padding and use these to track position as + // rendering moves down the screen + int offset_x = 1; + int offset_y = 1; + int line_height = 9; + + const HDNode *node = stellar_deriveNode(address_n, address_n_count); + if (!node) { + // abort on error + return; + } + + char str_pubaddr_truncated[12]; // G???? + null + memzero(str_pubaddr_truncated, sizeof(str_pubaddr_truncated)); + + layoutLast = layoutDialogSwipe; + layoutSwipe(); + oledClear(); + + // Load up public address + char str_pubaddr[56 + 1]; + memzero(str_pubaddr, sizeof(str_pubaddr)); + stellar_publicAddressAsStr(node->public_key + 1, str_pubaddr, + sizeof(str_pubaddr)); + memcpy(str_pubaddr_truncated, str_pubaddr, sizeof(str_pubaddr_truncated) - 1); + + // Header + // Ends up as: Signing with GABCDEFGHIJKL + char str_header[32]; + memzero(str_header, sizeof(str_header)); + strlcpy(str_header, _("Signing with "), sizeof(str_header)); + strlcat(str_header, str_pubaddr_truncated, sizeof(str_header)); + + oledDrawString(offset_x, offset_y, str_header, FONT_STANDARD); + offset_y += line_height; + // Invert color on header + oledInvert(0, 0, OLED_WIDTH, offset_y - 2); + + // Dialog contents begin + if (line1) { + oledDrawString(offset_x, offset_y, line1, FONT_STANDARD); + } + offset_y += line_height; + if (line2) { + oledDrawString(offset_x, offset_y, line2, FONT_STANDARD); + } + offset_y += line_height; + if (line3) { + oledDrawString(offset_x, offset_y, line3, FONT_STANDARD); + } + offset_y += line_height; + if (line4) { + oledDrawString(offset_x, offset_y, line4, FONT_STANDARD); + } + offset_y += line_height; + if (line5) { + oledDrawString(offset_x, offset_y, line5, FONT_STANDARD); + } + offset_y += line_height; + + // Cancel button + oledDrawString(1, OLED_HEIGHT - 8, "\x15", FONT_STANDARD); + oledDrawString(fontCharWidth(FONT_STANDARD, '\x15') + 3, OLED_HEIGHT - 8, + "Cancel", FONT_STANDARD); + oledInvert(0, OLED_HEIGHT - 9, + fontCharWidth(FONT_STANDARD, '\x15') + + oledStringWidth("Cancel", FONT_STANDARD) + 2, + OLED_HEIGHT - 1); + + // Warnings (drawn centered between the buttons + if (warning) { + oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 8, warning, + FONT_STANDARD); + } + + // Next / sign button + char str_next_label[8]; + if (is_final_step) { + strlcpy(str_next_label, _("SIGN"), sizeof(str_next_label)); + } else { + strlcpy(str_next_label, _("Next"), sizeof(str_next_label)); + } + + oledDrawString(OLED_WIDTH - fontCharWidth(FONT_STANDARD, '\x06') - 1, + OLED_HEIGHT - 8, "\x06", FONT_STANDARD); + oledDrawString(OLED_WIDTH - oledStringWidth(str_next_label, FONT_STANDARD) - + fontCharWidth(FONT_STANDARD, '\x06') - 3, + OLED_HEIGHT - 8, str_next_label, FONT_STANDARD); + oledInvert(OLED_WIDTH - oledStringWidth(str_next_label, FONT_STANDARD) - + fontCharWidth(FONT_STANDARD, '\x06') - 4, + OLED_HEIGHT - 9, OLED_WIDTH - 1, OLED_HEIGHT - 1); + + oledRefresh(); } /* * Main dialog helper method. Allows displaying 5 lines. * A title showing the account being used to sign is always displayed. */ -void stellar_layoutTransactionDialog(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5) -{ - char str_warning[16]; - memzero(str_warning, sizeof(str_warning)); - - if (stellar_activeTx.network_type == 2) { - // Warning: testnet - strlcpy(str_warning, _("WRN:TN"), sizeof(str_warning)); - } - if (stellar_activeTx.network_type == 3) { - // Warning: private network - strlcpy(str_warning, _("WRN:PN"), sizeof(str_warning)); - } - - stellar_layoutSigningDialog( - line1, - line2, - line3, - line4, - line5, - stellar_activeTx.address_n, - stellar_activeTx.address_n_count, - str_warning, - false - ); +void stellar_layoutTransactionDialog(const char *line1, const char *line2, + const char *line3, const char *line4, + const char *line5) { + char str_warning[16]; + memzero(str_warning, sizeof(str_warning)); + + if (stellar_activeTx.network_type == 2) { + // Warning: testnet + strlcpy(str_warning, _("WRN:TN"), sizeof(str_warning)); + } + if (stellar_activeTx.network_type == 3) { + // Warning: private network + strlcpy(str_warning, _("WRN:PN"), sizeof(str_warning)); + } + + stellar_layoutSigningDialog( + line1, line2, line3, line4, line5, stellar_activeTx.address_n, + stellar_activeTx.address_n_count, str_warning, false); } diff --git a/firmware/stellar.h b/firmware/stellar.h index 14ce551f9..92eda1d7b 100644 --- a/firmware/stellar.h +++ b/firmware/stellar.h @@ -21,11 +21,11 @@ #define __STELLAR_H__ #include +#include "base32.h" #include "bip32.h" #include "crypto.h" -#include "messages.pb.h" #include "fsm.h" -#include "base32.h" +#include "messages.pb.h" // 56 character base-32 encoded string #define STELLAR_ADDRESS_SIZE 56 @@ -35,32 +35,34 @@ #define STELLAR_KEY_SIZE 32 typedef struct { - // BIP32 path to the address being used for signing - uint32_t address_n[10]; - size_t address_n_count; - uint8_t signing_pubkey[32]; + // BIP32 path to the address being used for signing + uint32_t address_n[10]; + size_t address_n_count; + uint8_t signing_pubkey[32]; - // 1 - public network, 2 - official testnet, 3 - other private network - uint8_t network_type; + // 1 - public network, 2 - official testnet, 3 - other private network + uint8_t network_type; - // Total number of operations expected - uint32_t num_operations; - // Number that have been confirmed by the user - uint32_t confirmed_operations; + // Total number of operations expected + uint32_t num_operations; + // Number that have been confirmed by the user + uint32_t confirmed_operations; - // sha256 context that will eventually be signed - SHA256_CTX sha256_ctx; + // sha256 context that will eventually be signed + SHA256_CTX sha256_ctx; } StellarTransaction; // Signing process bool stellar_signingInit(const StellarSignTx *tx); void stellar_signingAbort(const char *reason); -bool stellar_confirmSourceAccount(bool has_source_account, const char *str_account); +bool stellar_confirmSourceAccount(bool has_source_account, + const char *str_account); bool stellar_confirmCreateAccountOp(const StellarCreateAccountOp *msg); bool stellar_confirmPaymentOp(const StellarPaymentOp *msg); bool stellar_confirmPathPaymentOp(const StellarPathPaymentOp *msg); bool stellar_confirmManageOfferOp(const StellarManageOfferOp *msg); -bool stellar_confirmCreatePassiveOfferOp(const StellarCreatePassiveOfferOp *msg); +bool stellar_confirmCreatePassiveOfferOp( + const StellarCreatePassiveOfferOp *msg); bool stellar_confirmSetOptionsOp(const StellarSetOptionsOp *msg); bool stellar_confirmChangeTrustOp(const StellarChangeTrustOp *msg); bool stellar_confirmAllowTrustOp(const StellarAllowTrustOp *msg); @@ -69,14 +71,22 @@ bool stellar_confirmManageDataOp(const StellarManageDataOp *msg); bool stellar_confirmBumpSequenceOp(const StellarBumpSequenceOp *msg); // Layout -void stellar_layoutTransactionDialog(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5); +void stellar_layoutTransactionDialog(const char *line1, const char *line2, + const char *line3, const char *line4, + const char *line5); void stellar_layoutTransactionSummary(const StellarSignTx *msg); -void stellar_layoutSigningDialog(const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, uint32_t *address_n, size_t address_n_count, const char *warning, bool is_final_step); +void stellar_layoutSigningDialog(const char *line1, const char *line2, + const char *line3, const char *line4, + const char *line5, uint32_t *address_n, + size_t address_n_count, const char *warning, + bool is_final_step); // Helpers -const HDNode *stellar_deriveNode(const uint32_t *address_n, size_t address_n_count); +const HDNode *stellar_deriveNode(const uint32_t *address_n, + size_t address_n_count); -size_t stellar_publicAddressAsStr(const uint8_t *bytes, char *out, size_t outlen); +size_t stellar_publicAddressAsStr(const uint8_t *bytes, char *out, + size_t outlen); const char **stellar_lineBreakAddress(const uint8_t *addrbytes); void stellar_hashupdate_uint32(uint32_t value); @@ -94,11 +104,13 @@ void stellar_getSignatureForActiveTx(uint8_t *out_signature); void stellar_format_uint32(uint32_t number, char *out, size_t outlen); void stellar_format_uint64(uint64_t number, char *out, size_t outlen); void stellar_format_stroops(uint64_t number, char *out, size_t outlen); -void stellar_format_asset(const StellarAssetType *asset, char *str_formatted, size_t len); -void stellar_format_price(uint32_t numerator, uint32_t denominator, char *out, size_t outlen); +void stellar_format_asset(const StellarAssetType *asset, char *str_formatted, + size_t len); +void stellar_format_price(uint32_t numerator, uint32_t denominator, char *out, + size_t outlen); bool stellar_validateAddress(const char *str_address); -bool stellar_getAddressBytes(const char* str_address, uint8_t *out_bytes); +bool stellar_getAddressBytes(const char *str_address, uint8_t *out_bytes); uint16_t stellar_crc16(uint8_t *bytes, uint32_t length); #endif diff --git a/firmware/transaction.c b/firmware/transaction.c index fbfd5c2f7..dd917291a 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -17,28 +17,28 @@ * along with this library. If not, see . */ -#include #include "transaction.h" -#include "ecdsa.h" +#include +#include "address.h" +#include "base58.h" +#include "cash_addr.h" #include "coins.h" -#include "util.h" +#include "crypto.h" #include "debug.h" -#include "protect.h" +#include "ecdsa.h" #include "layout2.h" -#include "crypto.h" -#include "ripemd160.h" -#include "base58.h" -#include "address.h" +#include "memzero.h" #include "messages.pb.h" +#include "protect.h" +#include "ripemd160.h" #include "segwit_addr.h" -#include "cash_addr.h" -#include "memzero.h" +#include "util.h" #define SEGWIT_VERSION_0 0 #define CASHADDR_P2KH (0) #define CASHADDR_P2SH (8) -#define CASHADDR_160 (0) +#define CASHADDR_160 (0) /* transaction input size (without script): 32 prevhash, 4 idx, 4 sequence */ #define TXSIZE_INPUT 40 @@ -58,798 +58,843 @@ #define TXSIZE_P2PKHASH 25 /* size of a p2sh script (hash, push, 20 scripthash, equal) */ #define TXSIZE_P2SCRIPT 23 -/* size of a Decred witness (without script): 8 amount, 4 block height, 4 block index */ +/* size of a Decred witness (without script): 8 amount, 4 block height, 4 block + * index */ #define TXSIZE_DECRED_WITNESS 16 -static const uint8_t segwit_header[2] = {0,1}; +static const uint8_t segwit_header[2] = {0, 1}; static inline uint32_t op_push_size(uint32_t i) { - if (i < 0x4C) { - return 1; - } - if (i < 0x100) { - return 2; - } - if (i < 0x10000) { - return 3; - } - return 5; + if (i < 0x4C) { + return 1; + } + if (i < 0x100) { + return 2; + } + if (i < 0x10000) { + return 3; + } + return 5; } uint32_t op_push(uint32_t i, uint8_t *out) { - if (i < 0x4C) { - out[0] = i & 0xFF; - return 1; - } - if (i < 0x100) { - out[0] = 0x4C; - out[1] = i & 0xFF; - return 2; - } - if (i < 0x10000) { - out[0] = 0x4D; - out[1] = i & 0xFF; - out[2] = (i >> 8) & 0xFF; - return 3; - } - out[0] = 0x4E; - out[1] = i & 0xFF; - out[2] = (i >> 8) & 0xFF; - out[3] = (i >> 16) & 0xFF; - out[4] = (i >> 24) & 0xFF; - return 5; -} - -bool compute_address(const CoinInfo *coin, - InputScriptType script_type, - const HDNode *node, - bool has_multisig, const MultisigRedeemScriptType *multisig, - char address[MAX_ADDR_SIZE]) { - - uint8_t raw[MAX_ADDR_RAW_SIZE]; - uint8_t digest[32]; - size_t prelen; - - if (has_multisig) { - if (cryptoMultisigPubkeyIndex(coin, multisig, node->public_key) < 0) { - return 0; - } - if (compile_script_multisig_hash(coin, multisig, digest) == 0) { - return 0; - } - if (script_type == InputScriptType_SPENDWITNESS) { - // segwit p2wsh: script hash is single sha256 - if (!coin->has_segwit || !coin->bech32_prefix) { - return 0; - } - if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 32)) { - return 0; - } - } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { - // segwit p2wsh encapsuled in p2sh address - if (!coin->has_segwit) { - return 0; - } - if (!coin->has_address_type_p2sh) { - return 0; - } - raw[0] = 0; // push version - raw[1] = 32; // push 32 bytes - memcpy(raw + 2, digest, 32); // push hash - hasher_Raw(coin->curve->hasher_pubkey, raw, 34, digest); - prelen = address_prefix_bytes_len(coin->address_type_p2sh); - address_write_prefix_bytes(coin->address_type_p2sh, raw); - memcpy(raw + prelen, digest, 32); - if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, address, MAX_ADDR_SIZE)) { - return 0; - } - } else if (coin->cashaddr_prefix) { - raw[0] = CASHADDR_P2SH | CASHADDR_160; - ripemd160(digest, 32, raw + 1); - if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { - return 0; - } - } else { - // non-segwit p2sh multisig - prelen = address_prefix_bytes_len(coin->address_type_p2sh); - address_write_prefix_bytes(coin->address_type_p2sh, raw); - ripemd160(digest, 32, raw + prelen); - if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, address, MAX_ADDR_SIZE)) { - return 0; - } - } - } else if (script_type == InputScriptType_SPENDWITNESS) { - // segwit p2wpkh: pubkey hash is ripemd160 of sha256 - if (!coin->has_segwit || !coin->bech32_prefix) { - return 0; - } - ecdsa_get_pubkeyhash(node->public_key, coin->curve->hasher_pubkey, digest); - if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 20)) { - return 0; - } - } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { - // segwit p2wpkh embedded in p2sh - if (!coin->has_segwit) { - return 0; - } - if (!coin->has_address_type_p2sh) { - return 0; - } - ecdsa_get_address_segwit_p2sh(node->public_key, coin->address_type_p2sh, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); - } else if (coin->cashaddr_prefix) { - ecdsa_get_address_raw(node->public_key, CASHADDR_P2KH | CASHADDR_160, coin->curve->hasher_pubkey, raw); - if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { - return 0; - } - } else { - ecdsa_get_address(node->public_key, coin->address_type, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); - } - return 1; -} - -int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm) -{ - memzero(out, sizeof(TxOutputBinType)); - out->amount = in->amount; - out->decred_script_version = in->decred_script_version; - uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - size_t addr_raw_len; - - if (in->script_type == OutputScriptType_PAYTOOPRETURN) { - // only 0 satoshi allowed for OP_RETURN - if (in->amount != 0) { - return 0; // failed to compile output - } - if (needs_confirm) { - if (in->op_return_data.size >= 8 && memcmp(in->op_return_data.bytes, "omni", 4) == 0) { // OMNI transaction - layoutConfirmOmni(in->op_return_data.bytes, in->op_return_data.size); - } else { - layoutConfirmOpReturn(in->op_return_data.bytes, in->op_return_data.size); - } - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; // user aborted - } - } - uint32_t r = 0; - out->script_pubkey.bytes[0] = 0x6A; r++; // OP_RETURN - r += op_push(in->op_return_data.size, out->script_pubkey.bytes + r); - memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size; - out->script_pubkey.size = r; - return r; - } - - if (in->address_n_count > 0) { - static CONFIDENTIAL HDNode node; - InputScriptType input_script_type; - - switch (in->script_type) { - case OutputScriptType_PAYTOADDRESS: - input_script_type = InputScriptType_SPENDADDRESS; - break; - case OutputScriptType_PAYTOMULTISIG: - input_script_type = InputScriptType_SPENDMULTISIG; - break; - case OutputScriptType_PAYTOWITNESS: - input_script_type = InputScriptType_SPENDWITNESS; - break; - case OutputScriptType_PAYTOP2SHWITNESS: - input_script_type = InputScriptType_SPENDP2SHWITNESS; - break; - default: - return 0; // failed to compile output - } - memcpy(&node, root, sizeof(HDNode)); - if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, NULL) == 0) { - return 0; // failed to compile output - } - hdnode_fill_public_key(&node); - if (!compute_address(coin, input_script_type, &node, - in->has_multisig, &in->multisig, - in->address)) { - return 0; // failed to compile output - } - } else if (!in->has_address) { - return 0; // failed to compile output - } - - addr_raw_len = base58_decode_check(in->address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); - size_t prefix_len; - if (coin->has_address_type // p2pkh - && addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type)) - && address_check_prefix(addr_raw, coin->address_type)) { - out->script_pubkey.bytes[0] = 0x76; // OP_DUP - out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 3, addr_raw + prefix_len, 20); - out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY - out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG - out->script_pubkey.size = 25; - } else if (coin->has_address_type_p2sh // p2sh - && addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2sh)) - && address_check_prefix(addr_raw, coin->address_type_p2sh)) { - out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len, 20); - out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL - out->script_pubkey.size = 23; - } else if (coin->cashaddr_prefix - && cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, in->address)) { - if (addr_raw_len == 21 - && addr_raw[0] == (CASHADDR_P2KH | CASHADDR_160)) { - out->script_pubkey.bytes[0] = 0x76; // OP_DUP - out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 3, addr_raw + 1, 20); - out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY - out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG - out->script_pubkey.size = 25; - - } else if (addr_raw_len == 21 - && addr_raw[0] == (CASHADDR_P2SH | CASHADDR_160)) { - out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + 1, 20); - out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL - out->script_pubkey.size = 23; - } else { - return 0; - } - } else if (coin->bech32_prefix) { - int witver; - if (!segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, in->address)) { - return 0; - } - // segwit: - // push 1 byte version id (opcode OP_0 = 0, OP_i = 80+i) - // push addr_raw (segwit_addr_decode makes sure addr_raw_len is at most 40) - out->script_pubkey.bytes[0] = witver == 0 ? 0 : 80 + witver; - out->script_pubkey.bytes[1] = addr_raw_len; - memcpy(out->script_pubkey.bytes + 2, addr_raw, addr_raw_len); - out->script_pubkey.size = addr_raw_len + 2; - } else { - return 0; - } - - if (needs_confirm) { - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; // user aborted - } - } - - return out->script_pubkey.size; -} - -uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out) -{ - if (coinByAddressType(address_type)) { // valid coin type - out[0] = 0x76; // OP_DUP - out[1] = 0xA9; // OP_HASH_160 - out[2] = 0x14; // pushing 20 bytes - memcpy(out + 3, pubkeyhash, 20); - out[23] = 0x88; // OP_EQUALVERIFY - out[24] = 0xAC; // OP_CHECKSIG - return 25; - } else { - return 0; // unsupported - } + if (i < 0x4C) { + out[0] = i & 0xFF; + return 1; + } + if (i < 0x100) { + out[0] = 0x4C; + out[1] = i & 0xFF; + return 2; + } + if (i < 0x10000) { + out[0] = 0x4D; + out[1] = i & 0xFF; + out[2] = (i >> 8) & 0xFF; + return 3; + } + out[0] = 0x4E; + out[1] = i & 0xFF; + out[2] = (i >> 8) & 0xFF; + out[3] = (i >> 16) & 0xFF; + out[4] = (i >> 24) & 0xFF; + return 5; +} + +bool compute_address(const CoinInfo *coin, InputScriptType script_type, + const HDNode *node, bool has_multisig, + const MultisigRedeemScriptType *multisig, + char address[MAX_ADDR_SIZE]) { + uint8_t raw[MAX_ADDR_RAW_SIZE]; + uint8_t digest[32]; + size_t prelen; + + if (has_multisig) { + if (cryptoMultisigPubkeyIndex(coin, multisig, node->public_key) < 0) { + return 0; + } + if (compile_script_multisig_hash(coin, multisig, digest) == 0) { + return 0; + } + if (script_type == InputScriptType_SPENDWITNESS) { + // segwit p2wsh: script hash is single sha256 + if (!coin->has_segwit || !coin->bech32_prefix) { + return 0; + } + if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, + digest, 32)) { + return 0; + } + } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { + // segwit p2wsh encapsuled in p2sh address + if (!coin->has_segwit) { + return 0; + } + if (!coin->has_address_type_p2sh) { + return 0; + } + raw[0] = 0; // push version + raw[1] = 32; // push 32 bytes + memcpy(raw + 2, digest, 32); // push hash + hasher_Raw(coin->curve->hasher_pubkey, raw, 34, digest); + prelen = address_prefix_bytes_len(coin->address_type_p2sh); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + memcpy(raw + prelen, digest, 32); + if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, + address, MAX_ADDR_SIZE)) { + return 0; + } + } else if (coin->cashaddr_prefix) { + raw[0] = CASHADDR_P2SH | CASHADDR_160; + ripemd160(digest, 32, raw + 1); + if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { + return 0; + } + } else { + // non-segwit p2sh multisig + prelen = address_prefix_bytes_len(coin->address_type_p2sh); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + ripemd160(digest, 32, raw + prelen); + if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, + address, MAX_ADDR_SIZE)) { + return 0; + } + } + } else if (script_type == InputScriptType_SPENDWITNESS) { + // segwit p2wpkh: pubkey hash is ripemd160 of sha256 + if (!coin->has_segwit || !coin->bech32_prefix) { + return 0; + } + ecdsa_get_pubkeyhash(node->public_key, coin->curve->hasher_pubkey, digest); + if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, + digest, 20)) { + return 0; + } + } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { + // segwit p2wpkh embedded in p2sh + if (!coin->has_segwit) { + return 0; + } + if (!coin->has_address_type_p2sh) { + return 0; + } + ecdsa_get_address_segwit_p2sh( + node->public_key, coin->address_type_p2sh, coin->curve->hasher_pubkey, + coin->curve->hasher_base58, address, MAX_ADDR_SIZE); + } else if (coin->cashaddr_prefix) { + ecdsa_get_address_raw(node->public_key, CASHADDR_P2KH | CASHADDR_160, + coin->curve->hasher_pubkey, raw); + if (!cash_addr_encode(address, coin->cashaddr_prefix, raw, 21)) { + return 0; + } + } else { + ecdsa_get_address(node->public_key, coin->address_type, + coin->curve->hasher_pubkey, coin->curve->hasher_base58, + address, MAX_ADDR_SIZE); + } + return 1; +} + +int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, + TxOutputBinType *out, bool needs_confirm) { + memzero(out, sizeof(TxOutputBinType)); + out->amount = in->amount; + out->decred_script_version = in->decred_script_version; + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + size_t addr_raw_len; + + if (in->script_type == OutputScriptType_PAYTOOPRETURN) { + // only 0 satoshi allowed for OP_RETURN + if (in->amount != 0) { + return 0; // failed to compile output + } + if (needs_confirm) { + if (in->op_return_data.size >= 8 && + memcmp(in->op_return_data.bytes, "omni", 4) == + 0) { // OMNI transaction + layoutConfirmOmni(in->op_return_data.bytes, in->op_return_data.size); + } else { + layoutConfirmOpReturn(in->op_return_data.bytes, + in->op_return_data.size); + } + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, + false)) { + return -1; // user aborted + } + } + uint32_t r = 0; + out->script_pubkey.bytes[0] = 0x6A; + r++; // OP_RETURN + r += op_push(in->op_return_data.size, out->script_pubkey.bytes + r); + memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, + in->op_return_data.size); + r += in->op_return_data.size; + out->script_pubkey.size = r; + return r; + } + + if (in->address_n_count > 0) { + static CONFIDENTIAL HDNode node; + InputScriptType input_script_type; + + switch (in->script_type) { + case OutputScriptType_PAYTOADDRESS: + input_script_type = InputScriptType_SPENDADDRESS; + break; + case OutputScriptType_PAYTOMULTISIG: + input_script_type = InputScriptType_SPENDMULTISIG; + break; + case OutputScriptType_PAYTOWITNESS: + input_script_type = InputScriptType_SPENDWITNESS; + break; + case OutputScriptType_PAYTOP2SHWITNESS: + input_script_type = InputScriptType_SPENDP2SHWITNESS; + break; + default: + return 0; // failed to compile output + } + memcpy(&node, root, sizeof(HDNode)); + if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, + NULL) == 0) { + return 0; // failed to compile output + } + hdnode_fill_public_key(&node); + if (!compute_address(coin, input_script_type, &node, in->has_multisig, + &in->multisig, in->address)) { + return 0; // failed to compile output + } + } else if (!in->has_address) { + return 0; // failed to compile output + } + + addr_raw_len = base58_decode_check(in->address, coin->curve->hasher_base58, + addr_raw, MAX_ADDR_RAW_SIZE); + size_t prefix_len; + if (coin->has_address_type // p2pkh + && addr_raw_len == + 20 + (prefix_len = address_prefix_bytes_len(coin->address_type)) && + address_check_prefix(addr_raw, coin->address_type)) { + out->script_pubkey.bytes[0] = 0x76; // OP_DUP + out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 3, addr_raw + prefix_len, 20); + out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY + out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG + out->script_pubkey.size = 25; + } else if (coin->has_address_type_p2sh // p2sh + && addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len( + coin->address_type_p2sh)) && + address_check_prefix(addr_raw, coin->address_type_p2sh)) { + out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len, 20); + out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL + out->script_pubkey.size = 23; + } else if (coin->cashaddr_prefix && + cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, + in->address)) { + if (addr_raw_len == 21 && addr_raw[0] == (CASHADDR_P2KH | CASHADDR_160)) { + out->script_pubkey.bytes[0] = 0x76; // OP_DUP + out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 3, addr_raw + 1, 20); + out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY + out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG + out->script_pubkey.size = 25; + + } else if (addr_raw_len == 21 && + addr_raw[0] == (CASHADDR_P2SH | CASHADDR_160)) { + out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + 1, 20); + out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL + out->script_pubkey.size = 23; + } else { + return 0; + } + } else if (coin->bech32_prefix) { + int witver; + if (!segwit_addr_decode(&witver, addr_raw, &addr_raw_len, + coin->bech32_prefix, in->address)) { + return 0; + } + // segwit: + // push 1 byte version id (opcode OP_0 = 0, OP_i = 80+i) + // push addr_raw (segwit_addr_decode makes sure addr_raw_len is at most 40) + out->script_pubkey.bytes[0] = witver == 0 ? 0 : 80 + witver; + out->script_pubkey.bytes[1] = addr_raw_len; + memcpy(out->script_pubkey.bytes + 2, addr_raw, addr_raw_len); + out->script_pubkey.size = addr_raw_len + 2; + } else { + return 0; + } + + if (needs_confirm) { + layoutConfirmOutput(coin, in); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return -1; // user aborted + } + } + + return out->script_pubkey.size; +} + +uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, + uint8_t *out) { + if (coinByAddressType(address_type)) { // valid coin type + out[0] = 0x76; // OP_DUP + out[1] = 0xA9; // OP_HASH_160 + out[2] = 0x14; // pushing 20 bytes + memcpy(out + 3, pubkeyhash, 20); + out[23] = 0x88; // OP_EQUALVERIFY + out[24] = 0xAC; // OP_CHECKSIG + return 25; + } else { + return 0; // unsupported + } } // if out == NULL just compute the length -uint32_t compile_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *out) -{ - if (!multisig->has_m) return 0; - const uint32_t m = multisig->m; - const uint32_t n = cryptoMultisigPubkeyCount(multisig); - if (m < 1 || m > 15) return 0; - if (n < 1 || n > 15) return 0; - uint32_t r = 0; - if (out) { - out[r] = 0x50 + m; r++; - for (uint32_t i = 0; i < n; i++) { - out[r] = 33; r++; // OP_PUSH 33 - const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); - if (!pubnode) return 0; - memcpy(out + r, pubnode->public_key, 33); r += 33; - } - out[r] = 0x50 + n; r++; - out[r] = 0xAE; r++; // OP_CHECKMULTISIG - } else { - r = 1 + 34 * n + 2; - } - return r; -} - -uint32_t compile_script_multisig_hash(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *hash) -{ - if (!multisig->has_m) return 0; - const uint32_t m = multisig->m; - const uint32_t n = cryptoMultisigPubkeyCount(multisig); - if (m < 1 || m > 15) return 0; - if (n < 1 || n > 15) return 0; - - Hasher hasher; - hasher_Init(&hasher, coin->curve->hasher_script); - - uint8_t d[2]; - d[0] = 0x50 + m; hasher_Update(&hasher, d, 1); - for (uint32_t i = 0; i < n; i++) { - d[0] = 33; hasher_Update(&hasher, d, 1); // OP_PUSH 33 - const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); - if (!pubnode) return 0; - hasher_Update(&hasher, pubnode->public_key, 33); - } - d[0] = 0x50 + n; - d[1] = 0xAE; - hasher_Update(&hasher, d, 2); - - hasher_Final(&hasher, hash); - - return 1; -} - -uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t sighash, uint8_t *out) -{ - uint32_t r = 0; - r += op_push(signature_len + 1, out + r); - memcpy(out + r, signature, signature_len); r += signature_len; - out[r] = sighash; r++; - r += op_push(pubkey_len, out + r); - memcpy(out + r, pubkey, pubkey_len); r += pubkey_len; - return r; -} - -uint32_t serialize_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out) -{ - uint32_t r = 0; - if (!coin->decred) { - // Decred fixed the off-by-one bug - out[r] = 0x00; r++; - } - for (uint32_t i = 0; i < multisig->signatures_count; i++) { - if (multisig->signatures[i].size == 0) { - continue; - } - r += op_push(multisig->signatures[i].size + 1, out + r); - memcpy(out + r, multisig->signatures[i].bytes, multisig->signatures[i].size); r += multisig->signatures[i].size; - out[r] = sighash; r++; - } - uint32_t script_len = compile_script_multisig(coin, multisig, 0); - if (script_len == 0) { - return 0; - } - r += op_push(script_len, out + r); - r += compile_script_multisig(coin, multisig, out + r); - return r; +uint32_t compile_script_multisig(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint8_t *out) { + if (!multisig->has_m) return 0; + const uint32_t m = multisig->m; + const uint32_t n = cryptoMultisigPubkeyCount(multisig); + if (m < 1 || m > 15) return 0; + if (n < 1 || n > 15) return 0; + uint32_t r = 0; + if (out) { + out[r] = 0x50 + m; + r++; + for (uint32_t i = 0; i < n; i++) { + out[r] = 33; + r++; // OP_PUSH 33 + const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); + if (!pubnode) return 0; + memcpy(out + r, pubnode->public_key, 33); + r += 33; + } + out[r] = 0x50 + n; + r++; + out[r] = 0xAE; + r++; // OP_CHECKMULTISIG + } else { + r = 1 + 34 * n + 2; + } + return r; +} + +uint32_t compile_script_multisig_hash(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint8_t *hash) { + if (!multisig->has_m) return 0; + const uint32_t m = multisig->m; + const uint32_t n = cryptoMultisigPubkeyCount(multisig); + if (m < 1 || m > 15) return 0; + if (n < 1 || n > 15) return 0; + + Hasher hasher; + hasher_Init(&hasher, coin->curve->hasher_script); + + uint8_t d[2]; + d[0] = 0x50 + m; + hasher_Update(&hasher, d, 1); + for (uint32_t i = 0; i < n; i++) { + d[0] = 33; + hasher_Update(&hasher, d, 1); // OP_PUSH 33 + const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i); + if (!pubnode) return 0; + hasher_Update(&hasher, pubnode->public_key, 33); + } + d[0] = 0x50 + n; + d[1] = 0xAE; + hasher_Update(&hasher, d, 2); + + hasher_Final(&hasher, hash); + + return 1; +} + +uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, + const uint8_t *pubkey, uint32_t pubkey_len, + uint8_t sighash, uint8_t *out) { + uint32_t r = 0; + r += op_push(signature_len + 1, out + r); + memcpy(out + r, signature, signature_len); + r += signature_len; + out[r] = sighash; + r++; + r += op_push(pubkey_len, out + r); + memcpy(out + r, pubkey, pubkey_len); + r += pubkey_len; + return r; +} + +uint32_t serialize_script_multisig(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint8_t sighash, uint8_t *out) { + uint32_t r = 0; + if (!coin->decred) { + // Decred fixed the off-by-one bug + out[r] = 0x00; + r++; + } + for (uint32_t i = 0; i < multisig->signatures_count; i++) { + if (multisig->signatures[i].size == 0) { + continue; + } + r += op_push(multisig->signatures[i].size + 1, out + r); + memcpy(out + r, multisig->signatures[i].bytes, + multisig->signatures[i].size); + r += multisig->signatures[i].size; + out[r] = sighash; + r++; + } + uint32_t script_len = compile_script_multisig(coin, multisig, 0); + if (script_len == 0) { + return 0; + } + r += op_push(script_len, out + r); + r += compile_script_multisig(coin, multisig, out + r); + return r; } // tx methods -uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input) -{ - for (int i = 0; i < 32; i++) { - hasher_Update(hasher, &(input->prev_hash.bytes[31 - i]), 1); - } - hasher_Update(hasher, (const uint8_t *)&input->prev_index, 4); - return 36; -} - -uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data) -{ - int r = ser_length_hash(hasher, size); - hasher_Update(hasher, data, size); - return r + size; -} - -uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input) -{ - hasher_Update(hasher, (const uint8_t *)&input->sequence, 4); - return 4; -} - -uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, bool decred) -{ - uint32_t r = 0; - hasher_Update(hasher, (const uint8_t *)&output->amount, 8); r += 8; - if (decred) { - uint16_t script_version = output->decred_script_version & 0xFFFF; - hasher_Update(hasher, (const uint8_t *)&script_version, 2); r += 2; - } - r += tx_script_hash(hasher, output->script_pubkey.size, output->script_pubkey.bytes); - return r; -} - -uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) -{ - int r = ser_length(size, out); - memcpy(out + r, data, size); - return r + size; -} - -uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) -{ - int r = 4; - if (tx->overwintered) { - uint32_t ver = tx->version | TX_OVERWINTERED; - memcpy(out, &ver, 4); - memcpy(out + 4, &(tx->version_group_id), 4); - r += 4; - } else { - memcpy(out, &(tx->version), 4); - if (tx->is_segwit) { - memcpy(out + r, segwit_header, 2); - r += 2; - } - } - return r + ser_length(tx->inputs_len, out + r); -} - -uint32_t tx_serialize_header_hash(TxStruct *tx) -{ - int r = 4; - if (tx->overwintered) { - uint32_t ver = tx->version | TX_OVERWINTERED; - hasher_Update(&(tx->hasher), (const uint8_t *)&ver, 4); - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version_group_id), 4); - r += 4; - } else { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version), 4); - if (tx->is_segwit) { - hasher_Update(&(tx->hasher), segwit_header, 2); - r += 2; - } - } - return r + ser_length_hash(&(tx->hasher), tx->inputs_len); -} - -uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out) -{ - if (tx->have_inputs >= tx->inputs_len) { - // already got all inputs - return 0; - } - uint32_t r = 0; - if (tx->have_inputs == 0) { - r += tx_serialize_header(tx, out + r); - } - for (int i = 0; i < 32; i++) { - *(out + r + i) = input->prev_hash.bytes[31 - i]; - } - r += 32; - memcpy(out + r, &input->prev_index, 4); r += 4; - if (tx->is_decred) { - uint8_t tree = input->decred_tree & 0xFF; - out[r++] = tree; - } else { - r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); - } - memcpy(out + r, &input->sequence, 4); r += 4; - - tx->have_inputs++; - tx->size += r; - - return r; -} - -uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) -{ - if (tx->have_inputs >= tx->inputs_len) { - // already got all inputs - return 0; - } - uint32_t r = 0; - if (tx->have_inputs == 0) { - r += tx_serialize_header_hash(tx); - } - r += tx_prevout_hash(&(tx->hasher), input); - if (tx->is_decred) { - uint8_t tree = input->decred_tree & 0xFF; - hasher_Update(&(tx->hasher), (const uint8_t *)&(tree), 1); r++; - } else { - r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); - } - r += tx_sequence_hash(&(tx->hasher), input); - - tx->have_inputs++; - tx->size += r; - - return r; -} - -uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, uint8_t *out) -{ - static const uint64_t amount = 0; - static const uint32_t block_height = 0x00000000; - static const uint32_t block_index = 0xFFFFFFFF; - - if (tx->have_inputs >= tx->inputs_len) { - // already got all inputs - return 0; - } - uint32_t r = 0; - if (tx->have_inputs == 0) { - r += ser_length(tx->inputs_len, out + r); - } - memcpy(out + r, &amount, 8); r += 8; - memcpy(out + r, &block_height, 4); r += 4; - memcpy(out + r, &block_index, 4); r += 4; - r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); - - tx->have_inputs++; - tx->size += r; - - return r; -} - -uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, const TxInputType *input) -{ - if (tx->have_inputs >= tx->inputs_len) { - // already got all inputs - return 0; - } - uint32_t r = 0; - if (tx->have_inputs == 0) { - r += tx_serialize_header_hash(tx); - } - if (input == NULL) { - r += ser_length_hash(&(tx->hasher), 0); - } else { - r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); - } - - tx->have_inputs++; - tx->size += r; - - return r; -} - -uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out) -{ - return ser_length(tx->outputs_len, out); -} - -uint32_t tx_serialize_middle_hash(TxStruct *tx) -{ - return ser_length_hash(&(tx->hasher), tx->outputs_len); -} - -uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) -{ - memcpy(out, &(tx->lock_time), 4); - if (tx->overwintered) { - if (tx->version == 3) { - memcpy(out + 4, &(tx->expiry), 4); - out[8] = 0x00; // nJoinSplit - return 9; - } else - if (tx->version == 4) { - memcpy(out + 4, &(tx->expiry), 4); - memzero(out + 8, 8); // valueBalance - out[16] = 0x00; // nShieldedSpend - out[17] = 0x00; // nShieldedOutput - out[18] = 0x00; // nJoinSplit - return 19; - } - } - if (tx->is_decred) { - memcpy(out + 4, &(tx->expiry), 4); - return 8; - } - return 4; -} - -uint32_t tx_serialize_footer_hash(TxStruct *tx) -{ - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->lock_time), 4); - if (tx->overwintered) { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); - hasher_Update(&(tx->hasher), (const uint8_t *)"\x00", 1); // nJoinSplit - return 9; - } - if (tx->is_decred) { - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); - return 8; - } - return 4; -} - -uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_t *out) -{ - if (tx->have_inputs < tx->inputs_len) { - // not all inputs provided - return 0; - } - if (tx->have_outputs >= tx->outputs_len) { - // already got all outputs - return 0; - } - uint32_t r = 0; - if (tx->have_outputs == 0) { - r += tx_serialize_middle(tx, out + r); - } - memcpy(out + r, &output->amount, 8); r += 8; - if (tx->is_decred) { - uint16_t script_version = output->decred_script_version & 0xFFFF; - memcpy(out + r, &script_version, 2); r += 2; - } - r += tx_serialize_script(output->script_pubkey.size, output->script_pubkey.bytes, out + r); - tx->have_outputs++; - if (tx->have_outputs == tx->outputs_len - && !tx->is_segwit) { - r += tx_serialize_footer(tx, out + r); - } - tx->size += r; - return r; -} - -uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) -{ - if (tx->have_inputs < tx->inputs_len) { - // not all inputs provided - return 0; - } - if (tx->have_outputs >= tx->outputs_len) { - // already got all outputs - return 0; - } - uint32_t r = 0; - if (tx->have_outputs == 0) { - r += tx_serialize_middle_hash(tx); - } - r += tx_output_hash(&(tx->hasher), output, tx->is_decred); - tx->have_outputs++; - if (tx->have_outputs == tx->outputs_len - && !tx->is_segwit) { - r += tx_serialize_footer_hash(tx); - } - tx->size += r; - return r; -} - -uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_t datalen) -{ - if (tx->have_inputs < tx->inputs_len) { - // not all inputs provided - return 0; - } - if (tx->have_outputs < tx->outputs_len) { - // not all inputs provided - return 0; - } - if (tx->extra_data_received + datalen > tx->extra_data_len) { - // we are receiving too much data - return 0; - } - hasher_Update(&(tx->hasher), data, datalen); - tx->extra_data_received += datalen; - tx->size += datalen; - return datalen; -} - -void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t extra_data_len, HasherType hasher_sign, bool overwintered, uint32_t version_group_id) -{ - tx->inputs_len = inputs_len; - tx->outputs_len = outputs_len; - tx->version = version; - tx->lock_time = lock_time; - tx->expiry = expiry; - tx->have_inputs = 0; - tx->have_outputs = 0; - tx->extra_data_len = extra_data_len; - tx->extra_data_received = 0; - tx->size = 0; - tx->is_segwit = false; - tx->is_decred = false; - tx->overwintered = overwintered; - tx->version_group_id = version_group_id; - hasher_Init(&(tx->hasher), hasher_sign); -} - -void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) -{ - hasher_Final(&(t->hasher), hash); - if (!reverse) return; - for (uint8_t i = 0; i < 16; i++) { - uint8_t k = hash[31 - i]; - hash[31 - i] = hash[i]; - hash[i] = k; - } +uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input) { + for (int i = 0; i < 32; i++) { + hasher_Update(hasher, &(input->prev_hash.bytes[31 - i]), 1); + } + hasher_Update(hasher, (const uint8_t *)&input->prev_index, 4); + return 36; +} + +uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data) { + int r = ser_length_hash(hasher, size); + hasher_Update(hasher, data, size); + return r + size; +} + +uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input) { + hasher_Update(hasher, (const uint8_t *)&input->sequence, 4); + return 4; +} + +uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, + bool decred) { + uint32_t r = 0; + hasher_Update(hasher, (const uint8_t *)&output->amount, 8); + r += 8; + if (decred) { + uint16_t script_version = output->decred_script_version & 0xFFFF; + hasher_Update(hasher, (const uint8_t *)&script_version, 2); + r += 2; + } + r += tx_script_hash(hasher, output->script_pubkey.size, + output->script_pubkey.bytes); + return r; +} + +uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) { + int r = ser_length(size, out); + memcpy(out + r, data, size); + return r + size; +} + +uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { + int r = 4; + if (tx->overwintered) { + uint32_t ver = tx->version | TX_OVERWINTERED; + memcpy(out, &ver, 4); + memcpy(out + 4, &(tx->version_group_id), 4); + r += 4; + } else { + memcpy(out, &(tx->version), 4); + if (tx->is_segwit) { + memcpy(out + r, segwit_header, 2); + r += 2; + } + } + return r + ser_length(tx->inputs_len, out + r); +} + +uint32_t tx_serialize_header_hash(TxStruct *tx) { + int r = 4; + if (tx->overwintered) { + uint32_t ver = tx->version | TX_OVERWINTERED; + hasher_Update(&(tx->hasher), (const uint8_t *)&ver, 4); + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version_group_id), 4); + r += 4; + } else { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version), 4); + if (tx->is_segwit) { + hasher_Update(&(tx->hasher), segwit_header, 2); + r += 2; + } + } + return r + ser_length_hash(&(tx->hasher), tx->inputs_len); +} + +uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, + uint8_t *out) { + if (tx->have_inputs >= tx->inputs_len) { + // already got all inputs + return 0; + } + uint32_t r = 0; + if (tx->have_inputs == 0) { + r += tx_serialize_header(tx, out + r); + } + for (int i = 0; i < 32; i++) { + *(out + r + i) = input->prev_hash.bytes[31 - i]; + } + r += 32; + memcpy(out + r, &input->prev_index, 4); + r += 4; + if (tx->is_decred) { + uint8_t tree = input->decred_tree & 0xFF; + out[r++] = tree; + } else { + r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, + out + r); + } + memcpy(out + r, &input->sequence, 4); + r += 4; + + tx->have_inputs++; + tx->size += r; + + return r; +} + +uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) { + if (tx->have_inputs >= tx->inputs_len) { + // already got all inputs + return 0; + } + uint32_t r = 0; + if (tx->have_inputs == 0) { + r += tx_serialize_header_hash(tx); + } + r += tx_prevout_hash(&(tx->hasher), input); + if (tx->is_decred) { + uint8_t tree = input->decred_tree & 0xFF; + hasher_Update(&(tx->hasher), (const uint8_t *)&(tree), 1); + r++; + } else { + r += tx_script_hash(&(tx->hasher), input->script_sig.size, + input->script_sig.bytes); + } + r += tx_sequence_hash(&(tx->hasher), input); + + tx->have_inputs++; + tx->size += r; + + return r; +} + +uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, + uint8_t *out) { + static const uint64_t amount = 0; + static const uint32_t block_height = 0x00000000; + static const uint32_t block_index = 0xFFFFFFFF; + + if (tx->have_inputs >= tx->inputs_len) { + // already got all inputs + return 0; + } + uint32_t r = 0; + if (tx->have_inputs == 0) { + r += ser_length(tx->inputs_len, out + r); + } + memcpy(out + r, &amount, 8); + r += 8; + memcpy(out + r, &block_height, 4); + r += 4; + memcpy(out + r, &block_index, 4); + r += 4; + r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, + out + r); + + tx->have_inputs++; + tx->size += r; + + return r; +} + +uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, + const TxInputType *input) { + if (tx->have_inputs >= tx->inputs_len) { + // already got all inputs + return 0; + } + uint32_t r = 0; + if (tx->have_inputs == 0) { + r += tx_serialize_header_hash(tx); + } + if (input == NULL) { + r += ser_length_hash(&(tx->hasher), 0); + } else { + r += tx_script_hash(&(tx->hasher), input->script_sig.size, + input->script_sig.bytes); + } + + tx->have_inputs++; + tx->size += r; + + return r; +} + +uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out) { + return ser_length(tx->outputs_len, out); +} + +uint32_t tx_serialize_middle_hash(TxStruct *tx) { + return ser_length_hash(&(tx->hasher), tx->outputs_len); +} + +uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { + memcpy(out, &(tx->lock_time), 4); + if (tx->overwintered) { + if (tx->version == 3) { + memcpy(out + 4, &(tx->expiry), 4); + out[8] = 0x00; // nJoinSplit + return 9; + } else if (tx->version == 4) { + memcpy(out + 4, &(tx->expiry), 4); + memzero(out + 8, 8); // valueBalance + out[16] = 0x00; // nShieldedSpend + out[17] = 0x00; // nShieldedOutput + out[18] = 0x00; // nJoinSplit + return 19; + } + } + if (tx->is_decred) { + memcpy(out + 4, &(tx->expiry), 4); + return 8; + } + return 4; +} + +uint32_t tx_serialize_footer_hash(TxStruct *tx) { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->lock_time), 4); + if (tx->overwintered) { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); + hasher_Update(&(tx->hasher), (const uint8_t *)"\x00", 1); // nJoinSplit + return 9; + } + if (tx->is_decred) { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); + return 8; + } + return 4; +} + +uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, + uint8_t *out) { + if (tx->have_inputs < tx->inputs_len) { + // not all inputs provided + return 0; + } + if (tx->have_outputs >= tx->outputs_len) { + // already got all outputs + return 0; + } + uint32_t r = 0; + if (tx->have_outputs == 0) { + r += tx_serialize_middle(tx, out + r); + } + memcpy(out + r, &output->amount, 8); + r += 8; + if (tx->is_decred) { + uint16_t script_version = output->decred_script_version & 0xFFFF; + memcpy(out + r, &script_version, 2); + r += 2; + } + r += tx_serialize_script(output->script_pubkey.size, + output->script_pubkey.bytes, out + r); + tx->have_outputs++; + if (tx->have_outputs == tx->outputs_len && !tx->is_segwit) { + r += tx_serialize_footer(tx, out + r); + } + tx->size += r; + return r; +} + +uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) { + if (tx->have_inputs < tx->inputs_len) { + // not all inputs provided + return 0; + } + if (tx->have_outputs >= tx->outputs_len) { + // already got all outputs + return 0; + } + uint32_t r = 0; + if (tx->have_outputs == 0) { + r += tx_serialize_middle_hash(tx); + } + r += tx_output_hash(&(tx->hasher), output, tx->is_decred); + tx->have_outputs++; + if (tx->have_outputs == tx->outputs_len && !tx->is_segwit) { + r += tx_serialize_footer_hash(tx); + } + tx->size += r; + return r; +} + +uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, + uint32_t datalen) { + if (tx->have_inputs < tx->inputs_len) { + // not all inputs provided + return 0; + } + if (tx->have_outputs < tx->outputs_len) { + // not all inputs provided + return 0; + } + if (tx->extra_data_received + datalen > tx->extra_data_len) { + // we are receiving too much data + return 0; + } + hasher_Update(&(tx->hasher), data, datalen); + tx->extra_data_received += datalen; + tx->size += datalen; + return datalen; +} + +void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, + uint32_t version, uint32_t lock_time, uint32_t expiry, + uint32_t extra_data_len, HasherType hasher_sign, bool overwintered, + uint32_t version_group_id) { + tx->inputs_len = inputs_len; + tx->outputs_len = outputs_len; + tx->version = version; + tx->lock_time = lock_time; + tx->expiry = expiry; + tx->have_inputs = 0; + tx->have_outputs = 0; + tx->extra_data_len = extra_data_len; + tx->extra_data_received = 0; + tx->size = 0; + tx->is_segwit = false; + tx->is_decred = false; + tx->overwintered = overwintered; + tx->version_group_id = version_group_id; + hasher_Init(&(tx->hasher), hasher_sign); +} + +void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { + hasher_Final(&(t->hasher), hash); + if (!reverse) return; + for (uint8_t i = 0; i < 16; i++) { + uint8_t k = hash[31 - i]; + hash[31 - i] = hash[i]; + hash[i] = k; + } } static uint32_t tx_input_script_size(const TxInputType *txinput) { - uint32_t input_script_size; - if (txinput->has_multisig) { - uint32_t multisig_script_size = TXSIZE_MULTISIGSCRIPT - + txinput->multisig.pubkeys_count * (1 + TXSIZE_PUBKEY); - input_script_size = 1 // the OP_FALSE bug in multisig - + txinput->multisig.m * (1 + TXSIZE_SIGNATURE) - + op_push_size(multisig_script_size) + multisig_script_size; - } else { - input_script_size = (1 + TXSIZE_SIGNATURE + 1 + TXSIZE_PUBKEY); - } - - return input_script_size; + uint32_t input_script_size; + if (txinput->has_multisig) { + uint32_t multisig_script_size = + TXSIZE_MULTISIGSCRIPT + + txinput->multisig.pubkeys_count * (1 + TXSIZE_PUBKEY); + input_script_size = 1 // the OP_FALSE bug in multisig + + txinput->multisig.m * (1 + TXSIZE_SIGNATURE) + + op_push_size(multisig_script_size) + + multisig_script_size; + } else { + input_script_size = (1 + TXSIZE_SIGNATURE + 1 + TXSIZE_PUBKEY); + } + + return input_script_size; } uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput) { - if (coin->decred) { - return 4 * (TXSIZE_INPUT + 1); // Decred tree - } - - uint32_t input_script_size = tx_input_script_size(txinput); - uint32_t weight = 4 * TXSIZE_INPUT; - if (txinput->script_type == InputScriptType_SPENDADDRESS - || txinput->script_type == InputScriptType_SPENDMULTISIG) { - input_script_size += ser_length_size(input_script_size); - weight += 4 * input_script_size; - } else if (txinput->script_type == InputScriptType_SPENDWITNESS - || txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { - if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { - weight += 4 * (2 + (txinput->has_multisig - ? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH)); - } else { - weight += 4; // empty input script - } - weight += input_script_size; // discounted witness - } - return weight; + if (coin->decred) { + return 4 * (TXSIZE_INPUT + 1); // Decred tree + } + + uint32_t input_script_size = tx_input_script_size(txinput); + uint32_t weight = 4 * TXSIZE_INPUT; + if (txinput->script_type == InputScriptType_SPENDADDRESS || + txinput->script_type == InputScriptType_SPENDMULTISIG) { + input_script_size += ser_length_size(input_script_size); + weight += 4 * input_script_size; + } else if (txinput->script_type == InputScriptType_SPENDWITNESS || + txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { + if (txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { + weight += 4 * (2 + (txinput->has_multisig ? TXSIZE_WITNESSSCRIPT + : TXSIZE_WITNESSPKHASH)); + } else { + weight += 4; // empty input script + } + weight += input_script_size; // discounted witness + } + return weight; } uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput) { - uint32_t output_script_size = 0; - if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) { - output_script_size = 1 + op_push_size(txoutput->op_return_data.size) - + txoutput->op_return_data.size; - } else if (txoutput->address_n_count > 0) { - if (txoutput->script_type == OutputScriptType_PAYTOWITNESS) { - output_script_size = txoutput->has_multisig - ? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH; - } else if (txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) { - output_script_size = TXSIZE_P2SCRIPT; - } else { - output_script_size = txoutput->has_multisig - ? TXSIZE_P2SCRIPT : TXSIZE_P2PKHASH; - } - } else { - uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - int witver; - size_t addr_raw_len; - if (coin->cashaddr_prefix - && cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, txoutput->address)) { - if (addr_raw_len == 21 - && addr_raw[0] == (CASHADDR_P2KH | CASHADDR_160)) { - output_script_size = TXSIZE_P2PKHASH; - } else if (addr_raw_len == 21 - && addr_raw[0] == (CASHADDR_P2SH | CASHADDR_160)) { - output_script_size = TXSIZE_P2SCRIPT; - } - } else if (coin->bech32_prefix - && segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, txoutput->address)) { - output_script_size = 2 + addr_raw_len; - } else { - addr_raw_len = base58_decode_check(txoutput->address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); - if (coin->has_address_type - && address_check_prefix(addr_raw, coin->address_type)) { - output_script_size = TXSIZE_P2PKHASH; - } else if (coin->has_address_type_p2sh - && address_check_prefix(addr_raw, coin->address_type_p2sh)) { - output_script_size = TXSIZE_P2SCRIPT; - } - } - } - output_script_size += ser_length_size(output_script_size); - - uint32_t size = TXSIZE_OUTPUT; - if (coin->decred) { - size += 2; // Decred script version - } - - return 4 * (size + output_script_size); + uint32_t output_script_size = 0; + if (txoutput->script_type == OutputScriptType_PAYTOOPRETURN) { + output_script_size = 1 + op_push_size(txoutput->op_return_data.size) + + txoutput->op_return_data.size; + } else if (txoutput->address_n_count > 0) { + if (txoutput->script_type == OutputScriptType_PAYTOWITNESS) { + output_script_size = + txoutput->has_multisig ? TXSIZE_WITNESSSCRIPT : TXSIZE_WITNESSPKHASH; + } else if (txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) { + output_script_size = TXSIZE_P2SCRIPT; + } else { + output_script_size = + txoutput->has_multisig ? TXSIZE_P2SCRIPT : TXSIZE_P2PKHASH; + } + } else { + uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + int witver; + size_t addr_raw_len; + if (coin->cashaddr_prefix && + cash_addr_decode(addr_raw, &addr_raw_len, coin->cashaddr_prefix, + txoutput->address)) { + if (addr_raw_len == 21 && addr_raw[0] == (CASHADDR_P2KH | CASHADDR_160)) { + output_script_size = TXSIZE_P2PKHASH; + } else if (addr_raw_len == 21 && + addr_raw[0] == (CASHADDR_P2SH | CASHADDR_160)) { + output_script_size = TXSIZE_P2SCRIPT; + } + } else if (coin->bech32_prefix && + segwit_addr_decode(&witver, addr_raw, &addr_raw_len, + coin->bech32_prefix, txoutput->address)) { + output_script_size = 2 + addr_raw_len; + } else { + addr_raw_len = + base58_decode_check(txoutput->address, coin->curve->hasher_base58, + addr_raw, MAX_ADDR_RAW_SIZE); + if (coin->has_address_type && + address_check_prefix(addr_raw, coin->address_type)) { + output_script_size = TXSIZE_P2PKHASH; + } else if (coin->has_address_type_p2sh && + address_check_prefix(addr_raw, coin->address_type_p2sh)) { + output_script_size = TXSIZE_P2SCRIPT; + } + } + } + output_script_size += ser_length_size(output_script_size); + + uint32_t size = TXSIZE_OUTPUT; + if (coin->decred) { + size += 2; // Decred script version + } + + return 4 * (size + output_script_size); } uint32_t tx_decred_witness_weight(const TxInputType *txinput) { - uint32_t input_script_size = tx_input_script_size(txinput); - uint32_t size = TXSIZE_DECRED_WITNESS + ser_length_size(input_script_size) + input_script_size; + uint32_t input_script_size = tx_input_script_size(txinput); + uint32_t size = TXSIZE_DECRED_WITNESS + ser_length_size(input_script_size) + + input_script_size; - return 4 * size; + return 4 * size; } diff --git a/firmware/transaction.h b/firmware/transaction.h index 5180a58e5..3a13f351d 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -22,62 +22,84 @@ #include #include -#include "sha2.h" #include "bip32.h" #include "coins.h" #include "hasher.h" #include "messages-bitcoin.pb.h" +#include "sha2.h" #define TX_OVERWINTERED 0x80000000 typedef struct { - uint32_t inputs_len; - uint32_t outputs_len; + uint32_t inputs_len; + uint32_t outputs_len; - uint32_t version; - uint32_t version_group_id; - uint32_t lock_time; - uint32_t expiry; - bool is_segwit; - bool is_decred; + uint32_t version; + uint32_t version_group_id; + uint32_t lock_time; + uint32_t expiry; + bool is_segwit; + bool is_decred; - uint32_t have_inputs; - uint32_t have_outputs; + uint32_t have_inputs; + uint32_t have_outputs; - bool overwintered; - uint32_t extra_data_len; - uint32_t extra_data_received; + bool overwintered; + uint32_t extra_data_len; + uint32_t extra_data_received; - uint32_t size; + uint32_t size; - Hasher hasher; + Hasher hasher; } TxStruct; -bool compute_address(const CoinInfo *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE]); -uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out); -uint32_t compile_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *out); -uint32_t compile_script_multisig_hash(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *hash); -uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t sighash, uint8_t *out); -uint32_t serialize_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out); -int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm); +bool compute_address(const CoinInfo *coin, InputScriptType script_type, + const HDNode *node, bool has_multisig, + const MultisigRedeemScriptType *multisig, + char address[MAX_ADDR_SIZE]); +uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, + uint8_t *out); +uint32_t compile_script_multisig(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint8_t *out); +uint32_t compile_script_multisig_hash(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint8_t *hash); +uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, + const uint8_t *pubkey, uint32_t pubkey_len, + uint8_t sighash, uint8_t *out); +uint32_t serialize_script_multisig(const CoinInfo *coin, + const MultisigRedeemScriptType *multisig, + uint8_t sighash, uint8_t *out); +int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, + TxOutputBinType *out, bool needs_confirm); uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input); uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data); uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input); -uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, bool decred); +uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, + bool decred); uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out); uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out); -uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out); -uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_t *out); -uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, uint8_t *out); +uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, + uint8_t *out); +uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, + uint8_t *out); +uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, + uint8_t *out); -void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t extra_data_len, HasherType hasher_sign, bool overwintered, uint32_t version_group_id); +void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, + uint32_t version, uint32_t lock_time, uint32_t expiry, + uint32_t extra_data_len, HasherType hasher_sign, bool overwintered, + uint32_t version_group_id); uint32_t tx_serialize_header_hash(TxStruct *tx); uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input); uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output); -uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_t datalen); -uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, const TxInputType *input); +uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, + uint32_t datalen); +uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, + const TxInputType *input); void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse); uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput); diff --git a/firmware/trezor.c b/firmware/trezor.c index 7a6832932..b112a87e7 100644 --- a/firmware/trezor.c +++ b/firmware/trezor.c @@ -17,140 +17,143 @@ * along with this library. If not, see . */ -#include "common.h" #include "trezor.h" -#include "oled.h" #include "bitmaps.h" -#include "util.h" -#include "usb.h" -#include "setup.h" +#include "bl_check.h" +#include "buttons.h" +#include "common.h" #include "config.h" +#include "gettext.h" #include "layout.h" #include "layout2.h" +#include "memzero.h" +#include "oled.h" #include "rng.h" +#include "setup.h" #include "timer.h" -#include "buttons.h" -#include "gettext.h" -#include "bl_check.h" -#include "memzero.h" +#include "usb.h" +#include "util.h" #if !EMULATOR -#include "otp.h" #include +#include "otp.h" #endif /* Screen timeout */ uint32_t system_millis_lock_start; -void check_lock_screen(void) -{ - buttonUpdate(); - - // wake from screensaver on any button - if (layoutLast == layoutScreensaver && (button.NoUp || button.YesUp)) { - layoutHome(); - return; - } - - // button held for long enough (2 seconds) - if (layoutLast == layoutHome && button.NoDown >= 285000 * 2) { - - layoutDialog(&bmp_icon_question, _("Cancel"), _("Lock Device"), NULL, _("Do you really want to"), _("lock your TREZOR?"), NULL, NULL, NULL, NULL); - - // wait until NoButton is released - usbTiny(1); - do { - usbSleep(5); - buttonUpdate(); - } while (!button.NoUp); - - // wait for confirmation/cancellation of the dialog - do { - usbSleep(5); - buttonUpdate(); - } while (!button.YesUp && !button.NoUp); - usbTiny(0); - - if (button.YesUp) { - // lock the screen - session_clear(true); - layoutScreensaver(); - } else { - // resume homescreen - layoutHome(); - } - } - - // if homescreen is shown for too long - if (layoutLast == layoutHome) { - if ((timer_ms() - system_millis_lock_start) >= config_getAutoLockDelayMs()) { - // lock the screen - session_clear(true); - layoutScreensaver(); - } - } +void check_lock_screen(void) { + buttonUpdate(); + + // wake from screensaver on any button + if (layoutLast == layoutScreensaver && (button.NoUp || button.YesUp)) { + layoutHome(); + return; + } + + // button held for long enough (2 seconds) + if (layoutLast == layoutHome && button.NoDown >= 285000 * 2) { + layoutDialog(&bmp_icon_question, _("Cancel"), _("Lock Device"), NULL, + _("Do you really want to"), _("lock your TREZOR?"), NULL, NULL, + NULL, NULL); + + // wait until NoButton is released + usbTiny(1); + do { + usbSleep(5); + buttonUpdate(); + } while (!button.NoUp); + + // wait for confirmation/cancellation of the dialog + do { + usbSleep(5); + buttonUpdate(); + } while (!button.YesUp && !button.NoUp); + usbTiny(0); + + if (button.YesUp) { + // lock the screen + session_clear(true); + layoutScreensaver(); + } else { + // resume homescreen + layoutHome(); + } + } + + // if homescreen is shown for too long + if (layoutLast == layoutHome) { + if ((timer_ms() - system_millis_lock_start) >= + config_getAutoLockDelayMs()) { + // lock the screen + session_clear(true); + layoutScreensaver(); + } + } } -static void collect_hw_entropy(bool privileged) -{ +static void collect_hw_entropy(bool privileged) { #if EMULATOR - (void)privileged; - memzero(HW_ENTROPY_DATA, HW_ENTROPY_LEN); + (void)privileged; + memzero(HW_ENTROPY_DATA, HW_ENTROPY_LEN); #else - if (privileged) { - desig_get_unique_id((uint32_t *)HW_ENTROPY_DATA); - // set entropy in the OTP randomness block - if (!flash_otp_is_locked(FLASH_OTP_BLOCK_RANDOMNESS)) { - uint8_t entropy[FLASH_OTP_BLOCK_SIZE]; - random_buffer(entropy, FLASH_OTP_BLOCK_SIZE); - flash_otp_write(FLASH_OTP_BLOCK_RANDOMNESS, 0, entropy, FLASH_OTP_BLOCK_SIZE); - flash_otp_lock(FLASH_OTP_BLOCK_RANDOMNESS); - } - // collect entropy from OTP randomness block - flash_otp_read(FLASH_OTP_BLOCK_RANDOMNESS, 0, HW_ENTROPY_DATA + 12, FLASH_OTP_BLOCK_SIZE); - } else { - // unprivileged mode => use fixed HW_ENTROPY - memset(HW_ENTROPY_DATA, 0x3C, HW_ENTROPY_LEN); - } + if (privileged) { + desig_get_unique_id((uint32_t *)HW_ENTROPY_DATA); + // set entropy in the OTP randomness block + if (!flash_otp_is_locked(FLASH_OTP_BLOCK_RANDOMNESS)) { + uint8_t entropy[FLASH_OTP_BLOCK_SIZE]; + random_buffer(entropy, FLASH_OTP_BLOCK_SIZE); + flash_otp_write(FLASH_OTP_BLOCK_RANDOMNESS, 0, entropy, + FLASH_OTP_BLOCK_SIZE); + flash_otp_lock(FLASH_OTP_BLOCK_RANDOMNESS); + } + // collect entropy from OTP randomness block + flash_otp_read(FLASH_OTP_BLOCK_RANDOMNESS, 0, HW_ENTROPY_DATA + 12, + FLASH_OTP_BLOCK_SIZE); + } else { + // unprivileged mode => use fixed HW_ENTROPY + memset(HW_ENTROPY_DATA, 0x3C, HW_ENTROPY_LEN); + } #endif } -int main(void) -{ +int main(void) { #ifndef APPVER - setup(); - __stack_chk_guard = random32(); // this supports compiler provided unpredictable stack protection checks - oledInit(); + setup(); + __stack_chk_guard = random32(); // this supports compiler provided + // unpredictable stack protection checks + oledInit(); #else - check_bootloader(); - setupApp(); - __stack_chk_guard = random32(); // this supports compiler provided unpredictable stack protection checks + check_bootloader(); + setupApp(); + __stack_chk_guard = random32(); // this supports compiler provided + // unpredictable stack protection checks #endif - if (!is_mode_unprivileged()) { - collect_hw_entropy(true); - timer_init(); + if (!is_mode_unprivileged()) { + collect_hw_entropy(true); + timer_init(); #ifdef APPVER - // enable MPU (Memory Protection Unit) - mpu_config_firmware(); + // enable MPU (Memory Protection Unit) + mpu_config_firmware(); #endif - } else { - collect_hw_entropy(false); - } + } else { + collect_hw_entropy(false); + } #if DEBUG_LINK - oledSetDebugLink(1); - config_wipe(); + oledSetDebugLink(1); + config_wipe(); #endif - oledDrawBitmap(40, 0, &bmp_logo64); - oledRefresh(); + oledDrawBitmap(40, 0, &bmp_logo64); + oledRefresh(); - config_init(); - layoutHome(); - usbInit(); - for (;;) { - usbPoll(); - check_lock_screen(); - } + config_init(); + layoutHome(); + usbInit(); + for (;;) { + usbPoll(); + check_lock_screen(); + } - return 0; + return 0; } diff --git a/firmware/u2f.c b/firmware/u2f.c index 1b856aa70..1c799dc49 100644 --- a/firmware/u2f.c +++ b/firmware/u2f.c @@ -17,32 +17,32 @@ * along with this library. If not, see . */ -#include #include +#include -#include "debug.h" -#include "config.h" #include "bip32.h" -#include "layout2.h" -#include "usb.h" #include "buttons.h" -#include "trezor.h" +#include "config.h" #include "curves.h" +#include "debug.h" +#include "gettext.h" +#include "hmac.h" +#include "layout2.h" +#include "memzero.h" #include "nist256p1.h" #include "rng.h" -#include "hmac.h" +#include "trezor.h" +#include "usb.h" #include "util.h" -#include "gettext.h" -#include "memzero.h" +#include "u2f.h" #include "u2f/u2f.h" #include "u2f/u2f_hid.h" #include "u2f/u2f_keys.h" #include "u2f_knownapps.h" -#include "u2f.h" // About 1/2 Second according to values used in protect.c -#define U2F_TIMEOUT (800000/2) +#define U2F_TIMEOUT (800000 / 2) #define U2F_OUT_PKT_BUFFER_LEN 130 // Initialise without a cid @@ -61,44 +61,44 @@ static uint8_t u2f_out_packets[U2F_OUT_PKT_BUFFER_LEN][HID_RPT_SIZE]; #define KEY_PATH_ENTRIES (KEY_PATH_LEN / sizeof(uint32_t)) // Defined as UsbSignHandler.BOGUS_APP_ID_HASH -// in https://github.com/google/u2f-ref-code/blob/master/u2f-chrome-extension/usbsignhandler.js#L118 +// in +// https://github.com/google/u2f-ref-code/blob/master/u2f-chrome-extension/usbsignhandler.js#L118 #define BOGUS_APPID "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" // Auth/Register request state machine typedef enum { - INIT = 0, - AUTH = 10, - AUTH_PASS = 11, - REG = 20, - REG_PASS = 21 + INIT = 0, + AUTH = 10, + AUTH_PASS = 11, + REG = 20, + REG_PASS = 21 } U2F_STATE; static U2F_STATE last_req_state = INIT; typedef struct { - uint8_t reserved; - uint8_t appId[U2F_APPID_SIZE]; - uint8_t chal[U2F_CHAL_SIZE]; - uint8_t keyHandle[KEY_HANDLE_LEN]; - uint8_t pubKey[U2F_PUBKEY_LEN]; + uint8_t reserved; + uint8_t appId[U2F_APPID_SIZE]; + uint8_t chal[U2F_CHAL_SIZE]; + uint8_t keyHandle[KEY_HANDLE_LEN]; + uint8_t pubKey[U2F_PUBKEY_LEN]; } U2F_REGISTER_SIG_STR; typedef struct { - uint8_t appId[U2F_APPID_SIZE]; - uint8_t flags; - uint8_t ctr[4]; - uint8_t chal[U2F_CHAL_SIZE]; + uint8_t appId[U2F_APPID_SIZE]; + uint8_t flags; + uint8_t ctr[4]; + uint8_t chal[U2F_CHAL_SIZE]; } U2F_AUTHENTICATE_SIG_STR; static uint32_t dialog_timeout = 0; -uint32_t next_cid(void) -{ - // extremely unlikely but hey - do { - cid = random32(); - } while (cid == 0 || cid == CID_BROADCAST); - return cid; +uint32_t next_cid(void) { + // extremely unlikely but hey + do { + cid = random32(); + } while (cid == 0 || cid == CID_BROADCAST); + return cid; } // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-hid-protocol-v1.2-ps-20170411.html#message--and-packet-structure @@ -107,667 +107,649 @@ uint32_t next_cid(void) // the maximum message payload length is 64 - 7 + 128 * (64 - 5) = 7609 bytes. #define U2F_MAXIMUM_PAYLOAD_LENGTH 7609 typedef struct { - uint8_t buf[U2F_MAXIMUM_PAYLOAD_LENGTH]; - uint8_t *buf_ptr; - uint32_t len; - uint8_t seq; - uint8_t cmd; + uint8_t buf[U2F_MAXIMUM_PAYLOAD_LENGTH]; + uint8_t *buf_ptr; + uint32_t len; + uint8_t seq; + uint8_t cmd; } U2F_ReadBuffer; U2F_ReadBuffer *reader; -void u2fhid_read(char tiny, const U2FHID_FRAME *f) -{ - // Always handle init packets directly - if (f->init.cmd == U2FHID_INIT) { - u2fhid_init(f); - if (tiny && reader && f->cid == cid) { - // abort current channel - reader->cmd = 0; - reader->len = 0; - reader->seq = 255; - } - return; - } - - if (tiny) { - // read continue packet - if (reader == 0 || cid != f->cid) { - send_u2fhid_error(f->cid, ERR_CHANNEL_BUSY); - return; - } - - if ((f->type & TYPE_INIT) && reader->seq == 255) { - u2fhid_init_cmd(f); - return; - } - - if (reader->seq != f->cont.seq) { - send_u2fhid_error(f->cid, ERR_INVALID_SEQ); - reader->cmd = 0; - reader->len = 0; - reader->seq = 255; - return; - } - - // check out of bounds - if ((reader->buf_ptr - reader->buf) >= (signed) reader->len - || (reader->buf_ptr + sizeof(f->cont.data) - reader->buf) > (signed) sizeof(reader->buf)) - return; - reader->seq++; - memcpy(reader->buf_ptr, f->cont.data, sizeof(f->cont.data)); - reader->buf_ptr += sizeof(f->cont.data); - return; - } - - u2fhid_read_start(f); +void u2fhid_read(char tiny, const U2FHID_FRAME *f) { + // Always handle init packets directly + if (f->init.cmd == U2FHID_INIT) { + u2fhid_init(f); + if (tiny && reader && f->cid == cid) { + // abort current channel + reader->cmd = 0; + reader->len = 0; + reader->seq = 255; + } + return; + } + + if (tiny) { + // read continue packet + if (reader == 0 || cid != f->cid) { + send_u2fhid_error(f->cid, ERR_CHANNEL_BUSY); + return; + } + + if ((f->type & TYPE_INIT) && reader->seq == 255) { + u2fhid_init_cmd(f); + return; + } + + if (reader->seq != f->cont.seq) { + send_u2fhid_error(f->cid, ERR_INVALID_SEQ); + reader->cmd = 0; + reader->len = 0; + reader->seq = 255; + return; + } + + // check out of bounds + if ((reader->buf_ptr - reader->buf) >= (signed)reader->len || + (reader->buf_ptr + sizeof(f->cont.data) - reader->buf) > + (signed)sizeof(reader->buf)) + return; + reader->seq++; + memcpy(reader->buf_ptr, f->cont.data, sizeof(f->cont.data)); + reader->buf_ptr += sizeof(f->cont.data); + return; + } + + u2fhid_read_start(f); } void u2fhid_init_cmd(const U2FHID_FRAME *f) { - reader->seq = 0; - reader->buf_ptr = reader->buf; - reader->len = MSG_LEN(*f); - reader->cmd = f->type; - memcpy(reader->buf_ptr, f->init.data, sizeof(f->init.data)); - reader->buf_ptr += sizeof(f->init.data); - cid = f->cid; + reader->seq = 0; + reader->buf_ptr = reader->buf; + reader->len = MSG_LEN(*f); + reader->cmd = f->type; + memcpy(reader->buf_ptr, f->init.data, sizeof(f->init.data)); + reader->buf_ptr += sizeof(f->init.data); + cid = f->cid; } void u2fhid_read_start(const U2FHID_FRAME *f) { - U2F_ReadBuffer readbuffer; - memzero(&readbuffer, sizeof(readbuffer)); - - if (!(f->type & TYPE_INIT)) { - return; - } - - // Broadcast is reserved for init - if (f->cid == CID_BROADCAST || f->cid == 0) { - send_u2fhid_error(f->cid, ERR_INVALID_CID); - return; - } - - if ((unsigned)MSG_LEN(*f) > sizeof(reader->buf)) { - send_u2fhid_error(f->cid, ERR_INVALID_LEN); - return; - } - - reader = &readbuffer; - u2fhid_init_cmd(f); - - usbTiny(1); - for(;;) { - // Do we need to wait for more data - while ((reader->buf_ptr - reader->buf) < (signed)reader->len) { - uint8_t lastseq = reader->seq; - uint8_t lastcmd = reader->cmd; - int counter = U2F_TIMEOUT; - while (reader->seq == lastseq && reader->cmd == lastcmd) { - if (counter-- == 0) { - // timeout - send_u2fhid_error(cid, ERR_MSG_TIMEOUT); - cid = 0; - reader = 0; - usbTiny(0); - layoutHome(); - return; - } - usbPoll(); - } - } - - // We have all the data - switch (reader->cmd) { - case 0: - // message was aborted by init - break; - case U2FHID_PING: - u2fhid_ping(reader->buf, reader->len); - break; - case U2FHID_MSG: - u2fhid_msg((APDU *)reader->buf, reader->len); - break; - case U2FHID_WINK: - u2fhid_wink(reader->buf, reader->len); - break; - default: - send_u2fhid_error(cid, ERR_INVALID_CMD); - break; - } - - // wait for next commmand/ button press - reader->cmd = 0; - reader->seq = 255; - while (dialog_timeout > 0 && reader->cmd == 0) { - dialog_timeout--; - usbPoll(); // may trigger new request - buttonUpdate(); - if (button.YesUp && - (last_req_state == AUTH || last_req_state == REG)) { - last_req_state++; - // standard requires to remember button press for 10 seconds. - dialog_timeout = 10 * U2F_TIMEOUT; - } - } - - if (reader->cmd == 0) { - last_req_state = INIT; - cid = 0; - reader = 0; - usbTiny(0); - layoutHome(); - return; - } - } + U2F_ReadBuffer readbuffer; + memzero(&readbuffer, sizeof(readbuffer)); + + if (!(f->type & TYPE_INIT)) { + return; + } + + // Broadcast is reserved for init + if (f->cid == CID_BROADCAST || f->cid == 0) { + send_u2fhid_error(f->cid, ERR_INVALID_CID); + return; + } + + if ((unsigned)MSG_LEN(*f) > sizeof(reader->buf)) { + send_u2fhid_error(f->cid, ERR_INVALID_LEN); + return; + } + + reader = &readbuffer; + u2fhid_init_cmd(f); + + usbTiny(1); + for (;;) { + // Do we need to wait for more data + while ((reader->buf_ptr - reader->buf) < (signed)reader->len) { + uint8_t lastseq = reader->seq; + uint8_t lastcmd = reader->cmd; + int counter = U2F_TIMEOUT; + while (reader->seq == lastseq && reader->cmd == lastcmd) { + if (counter-- == 0) { + // timeout + send_u2fhid_error(cid, ERR_MSG_TIMEOUT); + cid = 0; + reader = 0; + usbTiny(0); + layoutHome(); + return; + } + usbPoll(); + } + } + + // We have all the data + switch (reader->cmd) { + case 0: + // message was aborted by init + break; + case U2FHID_PING: + u2fhid_ping(reader->buf, reader->len); + break; + case U2FHID_MSG: + u2fhid_msg((APDU *)reader->buf, reader->len); + break; + case U2FHID_WINK: + u2fhid_wink(reader->buf, reader->len); + break; + default: + send_u2fhid_error(cid, ERR_INVALID_CMD); + break; + } + + // wait for next commmand/ button press + reader->cmd = 0; + reader->seq = 255; + while (dialog_timeout > 0 && reader->cmd == 0) { + dialog_timeout--; + usbPoll(); // may trigger new request + buttonUpdate(); + if (button.YesUp && (last_req_state == AUTH || last_req_state == REG)) { + last_req_state++; + // standard requires to remember button press for 10 seconds. + dialog_timeout = 10 * U2F_TIMEOUT; + } + } + + if (reader->cmd == 0) { + last_req_state = INIT; + cid = 0; + reader = 0; + usbTiny(0); + layoutHome(); + return; + } + } } -void u2fhid_ping(const uint8_t *buf, uint32_t len) -{ - debugLog(0, "", "u2fhid_ping"); - send_u2fhid_msg(U2FHID_PING, buf, len); +void u2fhid_ping(const uint8_t *buf, uint32_t len) { + debugLog(0, "", "u2fhid_ping"); + send_u2fhid_msg(U2FHID_PING, buf, len); } -void u2fhid_wink(const uint8_t *buf, uint32_t len) -{ - debugLog(0, "", "u2fhid_wink"); - (void)buf; +void u2fhid_wink(const uint8_t *buf, uint32_t len) { + debugLog(0, "", "u2fhid_wink"); + (void)buf; - if (len > 0) - return send_u2fhid_error(cid, ERR_INVALID_LEN); + if (len > 0) return send_u2fhid_error(cid, ERR_INVALID_LEN); - if (dialog_timeout > 0) - dialog_timeout = U2F_TIMEOUT; + if (dialog_timeout > 0) dialog_timeout = U2F_TIMEOUT; - U2FHID_FRAME f; - memzero(&f, sizeof(f)); - f.cid = cid; - f.init.cmd = U2FHID_WINK; - f.init.bcntl = 0; - queue_u2f_pkt(&f); + U2FHID_FRAME f; + memzero(&f, sizeof(f)); + f.cid = cid; + f.init.cmd = U2FHID_WINK; + f.init.bcntl = 0; + queue_u2f_pkt(&f); } -void u2fhid_init(const U2FHID_FRAME *in) -{ - const U2FHID_INIT_REQ *init_req = (const U2FHID_INIT_REQ *)&in->init.data; - U2FHID_FRAME f; - U2FHID_INIT_RESP resp; - memzero(&resp, sizeof(resp)); - - debugLog(0, "", "u2fhid_init"); - - if (in->cid == 0) { - send_u2fhid_error(in->cid, ERR_INVALID_CID); - return; - } - - memzero(&f, sizeof(f)); - f.cid = in->cid; - f.init.cmd = U2FHID_INIT; - f.init.bcnth = 0; - f.init.bcntl = sizeof(resp); - - memcpy(resp.nonce, init_req->nonce, sizeof(init_req->nonce)); - resp.cid = in->cid == CID_BROADCAST ? next_cid() : in->cid; - resp.versionInterface = U2FHID_IF_VERSION; - resp.versionMajor = VERSION_MAJOR; - resp.versionMinor = VERSION_MINOR; - resp.versionBuild = VERSION_PATCH; - resp.capFlags = CAPFLAG_WINK; - memcpy(&f.init.data, &resp, sizeof(resp)); - - queue_u2f_pkt(&f); +void u2fhid_init(const U2FHID_FRAME *in) { + const U2FHID_INIT_REQ *init_req = (const U2FHID_INIT_REQ *)&in->init.data; + U2FHID_FRAME f; + U2FHID_INIT_RESP resp; + memzero(&resp, sizeof(resp)); + + debugLog(0, "", "u2fhid_init"); + + if (in->cid == 0) { + send_u2fhid_error(in->cid, ERR_INVALID_CID); + return; + } + + memzero(&f, sizeof(f)); + f.cid = in->cid; + f.init.cmd = U2FHID_INIT; + f.init.bcnth = 0; + f.init.bcntl = sizeof(resp); + + memcpy(resp.nonce, init_req->nonce, sizeof(init_req->nonce)); + resp.cid = in->cid == CID_BROADCAST ? next_cid() : in->cid; + resp.versionInterface = U2FHID_IF_VERSION; + resp.versionMajor = VERSION_MAJOR; + resp.versionMinor = VERSION_MINOR; + resp.versionBuild = VERSION_PATCH; + resp.capFlags = CAPFLAG_WINK; + memcpy(&f.init.data, &resp, sizeof(resp)); + + queue_u2f_pkt(&f); } -void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt) -{ - // debugLog(0, "", "u2f_write_pkt"); - uint32_t next = (u2f_out_end + 1) % U2F_OUT_PKT_BUFFER_LEN; - if (u2f_out_start == next) { - debugLog(0, "", "u2f_write_pkt full"); - return; // Buffer full :( - } - memcpy(u2f_out_packets[u2f_out_end], u2f_pkt, HID_RPT_SIZE); - u2f_out_end = next; +void queue_u2f_pkt(const U2FHID_FRAME *u2f_pkt) { + // debugLog(0, "", "u2f_write_pkt"); + uint32_t next = (u2f_out_end + 1) % U2F_OUT_PKT_BUFFER_LEN; + if (u2f_out_start == next) { + debugLog(0, "", "u2f_write_pkt full"); + return; // Buffer full :( + } + memcpy(u2f_out_packets[u2f_out_end], u2f_pkt, HID_RPT_SIZE); + u2f_out_end = next; } -uint8_t *u2f_out_data(void) -{ - if (u2f_out_start == u2f_out_end) - return NULL; // No data - // debugLog(0, "", "u2f_out_data"); - uint32_t t = u2f_out_start; - u2f_out_start = (u2f_out_start + 1) % U2F_OUT_PKT_BUFFER_LEN; - return u2f_out_packets[t]; +uint8_t *u2f_out_data(void) { + if (u2f_out_start == u2f_out_end) return NULL; // No data + // debugLog(0, "", "u2f_out_data"); + uint32_t t = u2f_out_start; + u2f_out_start = (u2f_out_start + 1) % U2F_OUT_PKT_BUFFER_LEN; + return u2f_out_packets[t]; } -void u2fhid_msg(const APDU *a, uint32_t len) -{ - if ((APDU_LEN(*a) + sizeof(APDU)) > len) { - debugLog(0, "", "BAD APDU LENGTH"); - debugInt(APDU_LEN(*a)); - debugInt(len); - return; - } - - if (a->cla != 0) { - send_u2f_error(U2F_SW_CLA_NOT_SUPPORTED); - return; - } - - switch (a->ins) { - case U2F_REGISTER: - u2f_register(a); - break; - case U2F_AUTHENTICATE: - u2f_authenticate(a); - break; - case U2F_VERSION: - u2f_version(a); - break; - default: - debugLog(0, "", "u2f unknown cmd"); - send_u2f_error(U2F_SW_INS_NOT_SUPPORTED); - } +void u2fhid_msg(const APDU *a, uint32_t len) { + if ((APDU_LEN(*a) + sizeof(APDU)) > len) { + debugLog(0, "", "BAD APDU LENGTH"); + debugInt(APDU_LEN(*a)); + debugInt(len); + return; + } + + if (a->cla != 0) { + send_u2f_error(U2F_SW_CLA_NOT_SUPPORTED); + return; + } + + switch (a->ins) { + case U2F_REGISTER: + u2f_register(a); + break; + case U2F_AUTHENTICATE: + u2f_authenticate(a); + break; + case U2F_VERSION: + u2f_version(a); + break; + default: + debugLog(0, "", "u2f unknown cmd"); + send_u2f_error(U2F_SW_INS_NOT_SUPPORTED); + } } -void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, const uint32_t len) -{ - if (len > U2F_MAXIMUM_PAYLOAD_LENGTH) { - debugLog(0, "", "send_u2fhid_msg failed"); - return; - } - - U2FHID_FRAME f; - uint8_t *p = (uint8_t *)data; - uint32_t l = len; - uint32_t psz; - uint8_t seq = 0; - - // debugLog(0, "", "send_u2fhid_msg"); - - memzero(&f, sizeof(f)); - f.cid = cid; - f.init.cmd = cmd; - f.init.bcnth = len >> 8; - f.init.bcntl = len & 0xff; - - // Init packet - psz = MIN(sizeof(f.init.data), l); - memcpy(f.init.data, p, psz); - queue_u2f_pkt(&f); - l -= psz; - p += psz; - - // Cont packet(s) - for (; l > 0; l -= psz, p += psz) { - // debugLog(0, "", "send_u2fhid_msg con"); - memzero(&f.cont.data, sizeof(f.cont.data)); - f.cont.seq = seq++; - psz = MIN(sizeof(f.cont.data), l); - memcpy(f.cont.data, p, psz); - queue_u2f_pkt(&f); - } - - if (data + len != p) { - debugLog(0, "", "send_u2fhid_msg is bad"); - debugInt(data + len - p); - } +void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, + const uint32_t len) { + if (len > U2F_MAXIMUM_PAYLOAD_LENGTH) { + debugLog(0, "", "send_u2fhid_msg failed"); + return; + } + + U2FHID_FRAME f; + uint8_t *p = (uint8_t *)data; + uint32_t l = len; + uint32_t psz; + uint8_t seq = 0; + + // debugLog(0, "", "send_u2fhid_msg"); + + memzero(&f, sizeof(f)); + f.cid = cid; + f.init.cmd = cmd; + f.init.bcnth = len >> 8; + f.init.bcntl = len & 0xff; + + // Init packet + psz = MIN(sizeof(f.init.data), l); + memcpy(f.init.data, p, psz); + queue_u2f_pkt(&f); + l -= psz; + p += psz; + + // Cont packet(s) + for (; l > 0; l -= psz, p += psz) { + // debugLog(0, "", "send_u2fhid_msg con"); + memzero(&f.cont.data, sizeof(f.cont.data)); + f.cont.seq = seq++; + psz = MIN(sizeof(f.cont.data), l); + memcpy(f.cont.data, p, psz); + queue_u2f_pkt(&f); + } + + if (data + len != p) { + debugLog(0, "", "send_u2fhid_msg is bad"); + debugInt(data + len - p); + } } -void send_u2fhid_error(uint32_t fcid, uint8_t err) -{ - U2FHID_FRAME f; +void send_u2fhid_error(uint32_t fcid, uint8_t err) { + U2FHID_FRAME f; - memzero(&f, sizeof(f)); - f.cid = fcid; - f.init.cmd = U2FHID_ERROR; - f.init.bcntl = 1; - f.init.data[0] = err; - queue_u2f_pkt(&f); + memzero(&f, sizeof(f)); + f.cid = fcid; + f.init.cmd = U2FHID_ERROR; + f.init.bcntl = 1; + f.init.data[0] = err; + queue_u2f_pkt(&f); } -void u2f_version(const APDU *a) -{ - if (APDU_LEN(*a) != 0) { - debugLog(0, "", "u2f version - badlen"); - send_u2f_error(U2F_SW_WRONG_LENGTH); - return; - } - - // INCLUDES SW_NO_ERROR - static const uint8_t version_response[] = {'U', '2', 'F', '_', - 'V', '2', 0x90, 0x00}; - debugLog(0, "", "u2f version"); - send_u2f_msg(version_response, sizeof(version_response)); +void u2f_version(const APDU *a) { + if (APDU_LEN(*a) != 0) { + debugLog(0, "", "u2f version - badlen"); + send_u2f_error(U2F_SW_WRONG_LENGTH); + return; + } + + // INCLUDES SW_NO_ERROR + static const uint8_t version_response[] = {'U', '2', 'F', '_', + 'V', '2', 0x90, 0x00}; + debugLog(0, "", "u2f version"); + send_u2f_msg(version_response, sizeof(version_response)); } -static void getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], const char **appname) { - static char buf[8+2+8+1]; - - for (unsigned int i = 0; i < sizeof(u2f_well_known)/sizeof(U2FWellKnown); i++) { - if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) { - *appname = u2f_well_known[i].appname; - return; - } - } - - data2hex(appid, 4, &buf[0]); - buf[8] = buf[9] = '.'; - data2hex(appid + (U2F_APPID_SIZE - 4), 4, &buf[10]); - *appname = buf; +static void getReadableAppId(const uint8_t appid[U2F_APPID_SIZE], + const char **appname) { + static char buf[8 + 2 + 8 + 1]; + + for (unsigned int i = 0; i < sizeof(u2f_well_known) / sizeof(U2FWellKnown); + i++) { + if (memcmp(appid, u2f_well_known[i].appid, U2F_APPID_SIZE) == 0) { + *appname = u2f_well_known[i].appname; + return; + } + } + + data2hex(appid, 4, &buf[0]); + buf[8] = buf[9] = '.'; + data2hex(appid + (U2F_APPID_SIZE - 4), 4, &buf[10]); + *appname = buf; } -static const HDNode *getDerivedNode(uint32_t *address_n, size_t address_n_count) -{ - static CONFIDENTIAL HDNode node; - if (!config_getU2FRoot(&node)) { - layoutHome(); - debugLog(0, "", "ERR: Device not init"); - return 0; - } - if (!address_n || address_n_count == 0) { - return &node; - } - for (size_t i = 0; i < address_n_count; i++) { - if (hdnode_private_ckd(&node, address_n[i]) == 0) { - layoutHome(); - debugLog(0, "", "ERR: Derive private failed"); - return 0; - } - } - return &node; +static const HDNode *getDerivedNode(uint32_t *address_n, + size_t address_n_count) { + static CONFIDENTIAL HDNode node; + if (!config_getU2FRoot(&node)) { + layoutHome(); + debugLog(0, "", "ERR: Device not init"); + return 0; + } + if (!address_n || address_n_count == 0) { + return &node; + } + for (size_t i = 0; i < address_n_count; i++) { + if (hdnode_private_ckd(&node, address_n[i]) == 0) { + layoutHome(); + debugLog(0, "", "ERR: Derive private failed"); + return 0; + } + } + return &node; } -static const HDNode *generateKeyHandle(const uint8_t app_id[], uint8_t key_handle[]) -{ - uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; - - // Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' - uint32_t key_path[KEY_PATH_ENTRIES]; - for (uint32_t i = 0; i < KEY_PATH_ENTRIES; i++) { - // high bit for hardened keys - key_path[i]= 0x80000000 | random32(); - } - - // First half of keyhandle is key_path - memcpy(key_handle, key_path, KEY_PATH_LEN); - - // prepare keypair from /random data - const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); - if (!node) - return NULL; - - // For second half of keyhandle - // Signature of app_id and random data - memcpy(&keybase[0], app_id, U2F_APPID_SIZE); - memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); - hmac_sha256(node->private_key, sizeof(node->private_key), - keybase, sizeof(keybase), &key_handle[KEY_PATH_LEN]); - - // Done! - return node; +static const HDNode *generateKeyHandle(const uint8_t app_id[], + uint8_t key_handle[]) { + uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; + + // Derivation path is m/U2F'/r'/r'/r'/r'/r'/r'/r'/r' + uint32_t key_path[KEY_PATH_ENTRIES]; + for (uint32_t i = 0; i < KEY_PATH_ENTRIES; i++) { + // high bit for hardened keys + key_path[i] = 0x80000000 | random32(); + } + + // First half of keyhandle is key_path + memcpy(key_handle, key_path, KEY_PATH_LEN); + + // prepare keypair from /random data + const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + if (!node) return NULL; + + // For second half of keyhandle + // Signature of app_id and random data + memcpy(&keybase[0], app_id, U2F_APPID_SIZE); + memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); + hmac_sha256(node->private_key, sizeof(node->private_key), keybase, + sizeof(keybase), &key_handle[KEY_PATH_LEN]); + + // Done! + return node; } - -static const HDNode *validateKeyHandle(const uint8_t app_id[], const uint8_t key_handle[]) -{ - uint32_t key_path[KEY_PATH_ENTRIES]; - memcpy(key_path, key_handle, KEY_PATH_LEN); - for (unsigned int i = 0; i < KEY_PATH_ENTRIES; i++) { - // check high bit for hardened keys - if (! (key_path[i] & 0x80000000)) { - return NULL; - } - } - - const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); - if (!node) - return NULL; - - uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; - memcpy(&keybase[0], app_id, U2F_APPID_SIZE); - memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); - - - uint8_t hmac[SHA256_DIGEST_LENGTH]; - hmac_sha256(node->private_key, sizeof(node->private_key), - keybase, sizeof(keybase), hmac); - - if (memcmp(&key_handle[KEY_PATH_LEN], hmac, SHA256_DIGEST_LENGTH) != 0) - return NULL; - - // Done! - return node; +static const HDNode *validateKeyHandle(const uint8_t app_id[], + const uint8_t key_handle[]) { + uint32_t key_path[KEY_PATH_ENTRIES]; + memcpy(key_path, key_handle, KEY_PATH_LEN); + for (unsigned int i = 0; i < KEY_PATH_ENTRIES; i++) { + // check high bit for hardened keys + if (!(key_path[i] & 0x80000000)) { + return NULL; + } + } + + const HDNode *node = getDerivedNode(key_path, KEY_PATH_ENTRIES); + if (!node) return NULL; + + uint8_t keybase[U2F_APPID_SIZE + KEY_PATH_LEN]; + memcpy(&keybase[0], app_id, U2F_APPID_SIZE); + memcpy(&keybase[U2F_APPID_SIZE], key_handle, KEY_PATH_LEN); + + uint8_t hmac[SHA256_DIGEST_LENGTH]; + hmac_sha256(node->private_key, sizeof(node->private_key), keybase, + sizeof(keybase), hmac); + + if (memcmp(&key_handle[KEY_PATH_LEN], hmac, SHA256_DIGEST_LENGTH) != 0) + return NULL; + + // Done! + return node; } - -void u2f_register(const APDU *a) -{ - static U2F_REGISTER_REQ last_req; - const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data; - - if (!config_isInitialized()) { - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - return; - } - - // Validate basic request parameters - debugLog(0, "", "u2f register"); - if (APDU_LEN(*a) != sizeof(U2F_REGISTER_REQ)) { - debugLog(0, "", "u2f register - badlen"); - send_u2f_error(U2F_SW_WRONG_LENGTH); - return; - } - - // If this request is different from last request, reset state machine - if (memcmp(&last_req, req, sizeof(last_req)) != 0) { - memcpy(&last_req, req, sizeof(last_req)); - last_req_state = INIT; - } - - // First Time request, return not present and display request dialog - if (last_req_state == INIT) { - // error: testof-user-presence is required - buttonUpdate(); // Clear button state - if (0 == memcmp(req->appId, BOGUS_APPID, U2F_APPID_SIZE)) { - layoutDialog(&bmp_icon_warning, NULL, _("OK"), NULL, _("Another U2F device"), _("was used to register"), _("in this application."), NULL, NULL, NULL); - } else { - const char *appname; - getReadableAppId(req->appId, &appname); - layoutU2FDialog(_("Register"), appname); - } - last_req_state = REG; - } - - // Still awaiting Keypress - if (last_req_state == REG) { - // error: testof-user-presence is required - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - dialog_timeout = U2F_TIMEOUT; - return; - } - - // Buttons said yes - if (last_req_state == REG_PASS) { - uint8_t data[sizeof(U2F_REGISTER_RESP) + 2]; - U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)&data; - memzero(data, sizeof(data)); - - resp->registerId = U2F_REGISTER_ID; - resp->keyHandleLen = KEY_HANDLE_LEN; - // Generate keypair for this appId - const HDNode *node = - generateKeyHandle(req->appId, (uint8_t*)&resp->keyHandleCertSig); - - if (!node) { - debugLog(0, "", "getDerivedNode Fail"); - send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle - return; - } - - ecdsa_get_public_key65(node->curve->params, node->private_key, - (uint8_t *)&resp->pubKey); - - memcpy(resp->keyHandleCertSig + resp->keyHandleLen, - U2F_ATT_CERT, sizeof(U2F_ATT_CERT)); - - uint8_t sig[64]; - U2F_REGISTER_SIG_STR sig_base; - sig_base.reserved = 0; - memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE); - memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); - memcpy(sig_base.keyHandle, &resp->keyHandleCertSig, KEY_HANDLE_LEN); - memcpy(sig_base.pubKey, &resp->pubKey, U2F_PUBKEY_LEN); - if (ecdsa_sign(&nist256p1, HASHER_SHA2, U2F_ATT_PRIV_KEY, (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, NULL) != 0) { - send_u2f_error(U2F_SW_WRONG_DATA); - return; - } - - // Where to write the signature in the response - uint8_t *resp_sig = resp->keyHandleCertSig + - resp->keyHandleLen + sizeof(U2F_ATT_CERT); - // Convert to der for the response - const uint8_t sig_len = ecdsa_sig_to_der(sig, resp_sig); - - // Append success bytes - memcpy(resp->keyHandleCertSig + resp->keyHandleLen + - sizeof(U2F_ATT_CERT) + sig_len, - "\x90\x00", 2); - - int l = 1 /* registerId */ + U2F_PUBKEY_LEN + - 1 /* keyhandleLen */ + resp->keyHandleLen + - sizeof(U2F_ATT_CERT) + sig_len + 2; - - last_req_state = INIT; - dialog_timeout = 0; - send_u2f_msg(data, l); - return; - } - - // Didnt expect to get here - dialog_timeout = 0; +void u2f_register(const APDU *a) { + static U2F_REGISTER_REQ last_req; + const U2F_REGISTER_REQ *req = (U2F_REGISTER_REQ *)a->data; + + if (!config_isInitialized()) { + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + return; + } + + // Validate basic request parameters + debugLog(0, "", "u2f register"); + if (APDU_LEN(*a) != sizeof(U2F_REGISTER_REQ)) { + debugLog(0, "", "u2f register - badlen"); + send_u2f_error(U2F_SW_WRONG_LENGTH); + return; + } + + // If this request is different from last request, reset state machine + if (memcmp(&last_req, req, sizeof(last_req)) != 0) { + memcpy(&last_req, req, sizeof(last_req)); + last_req_state = INIT; + } + + // First Time request, return not present and display request dialog + if (last_req_state == INIT) { + // error: testof-user-presence is required + buttonUpdate(); // Clear button state + if (0 == memcmp(req->appId, BOGUS_APPID, U2F_APPID_SIZE)) { + layoutDialog(&bmp_icon_warning, NULL, _("OK"), NULL, + _("Another U2F device"), _("was used to register"), + _("in this application."), NULL, NULL, NULL); + } else { + const char *appname; + getReadableAppId(req->appId, &appname); + layoutU2FDialog(_("Register"), appname); + } + last_req_state = REG; + } + + // Still awaiting Keypress + if (last_req_state == REG) { + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + dialog_timeout = U2F_TIMEOUT; + return; + } + + // Buttons said yes + if (last_req_state == REG_PASS) { + uint8_t data[sizeof(U2F_REGISTER_RESP) + 2]; + U2F_REGISTER_RESP *resp = (U2F_REGISTER_RESP *)&data; + memzero(data, sizeof(data)); + + resp->registerId = U2F_REGISTER_ID; + resp->keyHandleLen = KEY_HANDLE_LEN; + // Generate keypair for this appId + const HDNode *node = + generateKeyHandle(req->appId, (uint8_t *)&resp->keyHandleCertSig); + + if (!node) { + debugLog(0, "", "getDerivedNode Fail"); + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + ecdsa_get_public_key65(node->curve->params, node->private_key, + (uint8_t *)&resp->pubKey); + + memcpy(resp->keyHandleCertSig + resp->keyHandleLen, U2F_ATT_CERT, + sizeof(U2F_ATT_CERT)); + + uint8_t sig[64]; + U2F_REGISTER_SIG_STR sig_base; + sig_base.reserved = 0; + memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE); + memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); + memcpy(sig_base.keyHandle, &resp->keyHandleCertSig, KEY_HANDLE_LEN); + memcpy(sig_base.pubKey, &resp->pubKey, U2F_PUBKEY_LEN); + if (ecdsa_sign(&nist256p1, HASHER_SHA2, U2F_ATT_PRIV_KEY, + (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, + NULL) != 0) { + send_u2f_error(U2F_SW_WRONG_DATA); + return; + } + + // Where to write the signature in the response + uint8_t *resp_sig = + resp->keyHandleCertSig + resp->keyHandleLen + sizeof(U2F_ATT_CERT); + // Convert to der for the response + const uint8_t sig_len = ecdsa_sig_to_der(sig, resp_sig); + + // Append success bytes + memcpy(resp->keyHandleCertSig + resp->keyHandleLen + sizeof(U2F_ATT_CERT) + + sig_len, + "\x90\x00", 2); + + int l = 1 /* registerId */ + U2F_PUBKEY_LEN + 1 /* keyhandleLen */ + + resp->keyHandleLen + sizeof(U2F_ATT_CERT) + sig_len + 2; + + last_req_state = INIT; + dialog_timeout = 0; + send_u2f_msg(data, l); + return; + } + + // Didnt expect to get here + dialog_timeout = 0; } -void u2f_authenticate(const APDU *a) -{ - const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data; - static U2F_AUTHENTICATE_REQ last_req; - - if (!config_isInitialized()) { - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - return; - } - - if (APDU_LEN(*a) < 64) { /// FIXME: decent value - debugLog(0, "", "u2f authenticate - badlen"); - send_u2f_error(U2F_SW_WRONG_LENGTH); - return; - } - - if (req->keyHandleLen != KEY_HANDLE_LEN) { - debugLog(0, "", "u2f auth - bad keyhandle len"); - send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle - return; - } - - const HDNode *node = - validateKeyHandle(req->appId, req->keyHandle); - - if (!node) { - debugLog(0, "", "u2f auth - bad keyhandle len"); - send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle - return; - } - - if (a->p1 == U2F_AUTH_CHECK_ONLY) { - debugLog(0, "", "u2f authenticate check"); - // This is a success for a good keyhandle - // A failed check would have happened earlier - // error: testof-user-presence is required - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - return; - } - - if (a->p1 != U2F_AUTH_ENFORCE) { - debugLog(0, "", "u2f authenticate unknown"); - // error:bad key handle - send_u2f_error(U2F_SW_WRONG_DATA); - return; - } - - debugLog(0, "", "u2f authenticate enforce"); - - if (memcmp(&last_req, req, sizeof(last_req)) != 0) { - memcpy(&last_req, req, sizeof(last_req)); - last_req_state = INIT; - } - - if (last_req_state == INIT) { - // error: testof-user-presence is required - buttonUpdate(); // Clear button state - const char *appname; - getReadableAppId(req->appId, &appname); - layoutU2FDialog(_("Authenticate"), appname); - last_req_state = AUTH; - } - - // Awaiting Keypress - if (last_req_state == AUTH) { - // error: testof-user-presence is required - send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); - dialog_timeout = U2F_TIMEOUT; - return; - } - - // Buttons said yes - if (last_req_state == AUTH_PASS) { - uint8_t buf[sizeof(U2F_AUTHENTICATE_RESP) + 2]; - U2F_AUTHENTICATE_RESP *resp = - (U2F_AUTHENTICATE_RESP *)&buf; - - const uint32_t ctr = config_nextU2FCounter(); - resp->flags = U2F_AUTH_FLAG_TUP; - resp->ctr[0] = ctr >> 24 & 0xff; - resp->ctr[1] = ctr >> 16 & 0xff; - resp->ctr[2] = ctr >> 8 & 0xff; - resp->ctr[3] = ctr & 0xff; - - // Build and sign response - U2F_AUTHENTICATE_SIG_STR sig_base; - uint8_t sig[64]; - memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE); - sig_base.flags = resp->flags; - memcpy(sig_base.ctr, resp->ctr, 4); - memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); - if (ecdsa_sign(&nist256p1, HASHER_SHA2, node->private_key, (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, NULL) != 0) { - send_u2f_error(U2F_SW_WRONG_DATA); - return; - } - - // Copy DER encoded signature into response - const uint8_t sig_len = ecdsa_sig_to_der(sig, resp->sig); - - // Append OK - memcpy(buf + sizeof(U2F_AUTHENTICATE_RESP) - - U2F_MAX_EC_SIG_SIZE + sig_len, - "\x90\x00", 2); - last_req_state = INIT; - dialog_timeout = 0; - send_u2f_msg(buf, sizeof(U2F_AUTHENTICATE_RESP) - - U2F_MAX_EC_SIG_SIZE + sig_len + - 2); - } +void u2f_authenticate(const APDU *a) { + const U2F_AUTHENTICATE_REQ *req = (U2F_AUTHENTICATE_REQ *)a->data; + static U2F_AUTHENTICATE_REQ last_req; + + if (!config_isInitialized()) { + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + return; + } + + if (APDU_LEN(*a) < 64) { /// FIXME: decent value + debugLog(0, "", "u2f authenticate - badlen"); + send_u2f_error(U2F_SW_WRONG_LENGTH); + return; + } + + if (req->keyHandleLen != KEY_HANDLE_LEN) { + debugLog(0, "", "u2f auth - bad keyhandle len"); + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + const HDNode *node = validateKeyHandle(req->appId, req->keyHandle); + + if (!node) { + debugLog(0, "", "u2f auth - bad keyhandle len"); + send_u2f_error(U2F_SW_WRONG_DATA); // error:bad key handle + return; + } + + if (a->p1 == U2F_AUTH_CHECK_ONLY) { + debugLog(0, "", "u2f authenticate check"); + // This is a success for a good keyhandle + // A failed check would have happened earlier + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + return; + } + + if (a->p1 != U2F_AUTH_ENFORCE) { + debugLog(0, "", "u2f authenticate unknown"); + // error:bad key handle + send_u2f_error(U2F_SW_WRONG_DATA); + return; + } + + debugLog(0, "", "u2f authenticate enforce"); + + if (memcmp(&last_req, req, sizeof(last_req)) != 0) { + memcpy(&last_req, req, sizeof(last_req)); + last_req_state = INIT; + } + + if (last_req_state == INIT) { + // error: testof-user-presence is required + buttonUpdate(); // Clear button state + const char *appname; + getReadableAppId(req->appId, &appname); + layoutU2FDialog(_("Authenticate"), appname); + last_req_state = AUTH; + } + + // Awaiting Keypress + if (last_req_state == AUTH) { + // error: testof-user-presence is required + send_u2f_error(U2F_SW_CONDITIONS_NOT_SATISFIED); + dialog_timeout = U2F_TIMEOUT; + return; + } + + // Buttons said yes + if (last_req_state == AUTH_PASS) { + uint8_t buf[sizeof(U2F_AUTHENTICATE_RESP) + 2]; + U2F_AUTHENTICATE_RESP *resp = (U2F_AUTHENTICATE_RESP *)&buf; + + const uint32_t ctr = config_nextU2FCounter(); + resp->flags = U2F_AUTH_FLAG_TUP; + resp->ctr[0] = ctr >> 24 & 0xff; + resp->ctr[1] = ctr >> 16 & 0xff; + resp->ctr[2] = ctr >> 8 & 0xff; + resp->ctr[3] = ctr & 0xff; + + // Build and sign response + U2F_AUTHENTICATE_SIG_STR sig_base; + uint8_t sig[64]; + memcpy(sig_base.appId, req->appId, U2F_APPID_SIZE); + sig_base.flags = resp->flags; + memcpy(sig_base.ctr, resp->ctr, 4); + memcpy(sig_base.chal, req->chal, U2F_CHAL_SIZE); + if (ecdsa_sign(&nist256p1, HASHER_SHA2, node->private_key, + (uint8_t *)&sig_base, sizeof(sig_base), sig, NULL, + NULL) != 0) { + send_u2f_error(U2F_SW_WRONG_DATA); + return; + } + + // Copy DER encoded signature into response + const uint8_t sig_len = ecdsa_sig_to_der(sig, resp->sig); + + // Append OK + memcpy(buf + sizeof(U2F_AUTHENTICATE_RESP) - U2F_MAX_EC_SIG_SIZE + sig_len, + "\x90\x00", 2); + last_req_state = INIT; + dialog_timeout = 0; + send_u2f_msg( + buf, sizeof(U2F_AUTHENTICATE_RESP) - U2F_MAX_EC_SIG_SIZE + sig_len + 2); + } } -void send_u2f_error(const uint16_t err) -{ - uint8_t data[2]; - data[0] = err >> 8 & 0xFF; - data[1] = err & 0xFF; - send_u2f_msg(data, 2); +void send_u2f_error(const uint16_t err) { + uint8_t data[2]; + data[0] = err >> 8 & 0xFF; + data[1] = err & 0xFF; + send_u2f_msg(data, 2); } -void send_u2f_msg(const uint8_t *data, const uint32_t len) -{ - send_u2fhid_msg(U2FHID_MSG, data, len); +void send_u2f_msg(const uint8_t *data, const uint32_t len) { + send_u2fhid_msg(U2FHID_MSG, data, len); } diff --git a/firmware/u2f.h b/firmware/u2f.h index 9eb0cca62..e1e3a4197 100644 --- a/firmware/u2f.h +++ b/firmware/u2f.h @@ -20,17 +20,17 @@ #ifndef __U2F_H__ #define __U2F_H__ -#include #include -#include "u2f/u2f_hid.h" +#include #include "trezor.h" +#include "u2f/u2f_hid.h" #define U2F_KEY_PATH 0x80553246 typedef struct { - uint8_t cla, ins, p1, p2; - uint8_t lc1, lc2, lc3; - uint8_t data[]; + uint8_t cla, ins, p1, p2; + uint8_t lc1, lc2, lc3; + uint8_t data[]; } APDU; #define APDU_LEN(A) (uint32_t)(((A).lc1 << 16) + ((A).lc2 << 8) + ((A).lc3)) @@ -56,7 +56,7 @@ void send_u2f_msg(const uint8_t *data, uint32_t len); void send_u2f_error(uint16_t err); void send_u2fhid_msg(const uint8_t cmd, const uint8_t *data, - const uint32_t len); + const uint32_t len); void send_u2fhid_error(uint32_t fcid, uint8_t err); #endif diff --git a/firmware/u2f/u2f_keys.h b/firmware/u2f/u2f_keys.h index 680a730e2..578b435c0 100644 --- a/firmware/u2f/u2f_keys.h +++ b/firmware/u2f/u2f_keys.h @@ -4,37 +4,35 @@ #include const uint8_t U2F_ATT_PRIV_KEY[] = { - 0x71, 0x26, 0xac, 0x2b, 0xf6, 0x44, 0xdc, 0x61, - 0x86, 0xad, 0x83, 0xef, 0x1f, 0xcd, 0xf1, 0x2a, - 0x57, 0xb5, 0xcf, 0xa2, 0x00, 0x0b, 0x8a, 0xd0, - 0x27, 0xe9, 0x56, 0xe8, 0x54, 0xc5, 0x0a, 0x8b -}; + 0x71, 0x26, 0xac, 0x2b, 0xf6, 0x44, 0xdc, 0x61, 0x86, 0xad, 0x83, + 0xef, 0x1f, 0xcd, 0xf1, 0x2a, 0x57, 0xb5, 0xcf, 0xa2, 0x00, 0x0b, + 0x8a, 0xd0, 0x27, 0xe9, 0x56, 0xe8, 0x54, 0xc5, 0x0a, 0x8b}; const uint8_t U2F_ATT_CERT[] = { - 0x30, 0x82, 0x01, 0x18, 0x30, 0x81, 0xc0, 0x02, 0x09, 0x00, 0xb1, 0xd9, - 0x8f, 0x42, 0x64, 0x72, 0xd3, 0x2c, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, - 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, - 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x54, 0x72, 0x65, 0x7a, 0x6f, - 0x72, 0x20, 0x55, 0x32, 0x46, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, - 0x34, 0x32, 0x39, 0x31, 0x33, 0x33, 0x31, 0x35, 0x33, 0x5a, 0x17, 0x0d, - 0x32, 0x36, 0x30, 0x34, 0x32, 0x37, 0x31, 0x33, 0x33, 0x31, 0x35, 0x33, - 0x5a, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, - 0x0c, 0x0a, 0x54, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x20, 0x55, 0x32, 0x46, - 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, - 0x42, 0x00, 0x04, 0xd9, 0x18, 0xbd, 0xfa, 0x8a, 0x54, 0xac, 0x92, 0xe9, - 0x0d, 0xa9, 0x1f, 0xca, 0x7a, 0xa2, 0x64, 0x54, 0xc0, 0xd1, 0x73, 0x36, - 0x31, 0x4d, 0xde, 0x83, 0xa5, 0x4b, 0x86, 0xb5, 0xdf, 0x4e, 0xf0, 0x52, - 0x65, 0x9a, 0x1d, 0x6f, 0xfc, 0xb7, 0x46, 0x7f, 0x1a, 0xcd, 0xdb, 0x8a, - 0x33, 0x08, 0x0b, 0x5e, 0xed, 0x91, 0x89, 0x13, 0xf4, 0x43, 0xa5, 0x26, - 0x1b, 0xc7, 0x7b, 0x68, 0x60, 0x6f, 0xc1, 0x30, 0x0a, 0x06, 0x08, 0x2a, - 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, - 0x02, 0x20, 0x24, 0x1e, 0x81, 0xff, 0xd2, 0xe5, 0xe6, 0x15, 0x36, 0x94, - 0xc3, 0x55, 0x2e, 0x8f, 0xeb, 0xd7, 0x1e, 0x89, 0x35, 0x92, 0x1c, 0xb4, - 0x83, 0x41, 0x43, 0x71, 0x1c, 0x76, 0xea, 0xee, 0xf3, 0x95, 0x02, 0x20, - 0x5f, 0x80, 0xeb, 0x10, 0xf2, 0x5c, 0xcc, 0x39, 0x8b, 0x3c, 0xa8, 0xa9, - 0xad, 0xa4, 0x02, 0x7f, 0x93, 0x13, 0x20, 0x77, 0xb7, 0xab, 0xce, 0x77, - 0x46, 0x5a, 0x27, 0xf5, 0x3d, 0x33, 0xa1, 0x1d, + 0x30, 0x82, 0x01, 0x18, 0x30, 0x81, 0xc0, 0x02, 0x09, 0x00, 0xb1, 0xd9, + 0x8f, 0x42, 0x64, 0x72, 0xd3, 0x2c, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x54, 0x72, 0x65, 0x7a, 0x6f, + 0x72, 0x20, 0x55, 0x32, 0x46, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x36, 0x30, + 0x34, 0x32, 0x39, 0x31, 0x33, 0x33, 0x31, 0x35, 0x33, 0x5a, 0x17, 0x0d, + 0x32, 0x36, 0x30, 0x34, 0x32, 0x37, 0x31, 0x33, 0x33, 0x31, 0x35, 0x33, + 0x5a, 0x30, 0x15, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x0a, 0x54, 0x72, 0x65, 0x7a, 0x6f, 0x72, 0x20, 0x55, 0x32, 0x46, + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, + 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, + 0x42, 0x00, 0x04, 0xd9, 0x18, 0xbd, 0xfa, 0x8a, 0x54, 0xac, 0x92, 0xe9, + 0x0d, 0xa9, 0x1f, 0xca, 0x7a, 0xa2, 0x64, 0x54, 0xc0, 0xd1, 0x73, 0x36, + 0x31, 0x4d, 0xde, 0x83, 0xa5, 0x4b, 0x86, 0xb5, 0xdf, 0x4e, 0xf0, 0x52, + 0x65, 0x9a, 0x1d, 0x6f, 0xfc, 0xb7, 0x46, 0x7f, 0x1a, 0xcd, 0xdb, 0x8a, + 0x33, 0x08, 0x0b, 0x5e, 0xed, 0x91, 0x89, 0x13, 0xf4, 0x43, 0xa5, 0x26, + 0x1b, 0xc7, 0x7b, 0x68, 0x60, 0x6f, 0xc1, 0x30, 0x0a, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x47, 0x00, 0x30, 0x44, + 0x02, 0x20, 0x24, 0x1e, 0x81, 0xff, 0xd2, 0xe5, 0xe6, 0x15, 0x36, 0x94, + 0xc3, 0x55, 0x2e, 0x8f, 0xeb, 0xd7, 0x1e, 0x89, 0x35, 0x92, 0x1c, 0xb4, + 0x83, 0x41, 0x43, 0x71, 0x1c, 0x76, 0xea, 0xee, 0xf3, 0x95, 0x02, 0x20, + 0x5f, 0x80, 0xeb, 0x10, 0xf2, 0x5c, 0xcc, 0x39, 0x8b, 0x3c, 0xa8, 0xa9, + 0xad, 0xa4, 0x02, 0x7f, 0x93, 0x13, 0x20, 0x77, 0xb7, 0xab, 0xce, 0x77, + 0x46, 0x5a, 0x27, 0xf5, 0x3d, 0x33, 0xa1, 0x1d, }; -#endif // __U2F_KEYS_H_INCLUDED__ +#endif // __U2F_KEYS_H_INCLUDED__ diff --git a/firmware/u2f_knownapps.h b/firmware/u2f_knownapps.h index 2f945e937..f01a03510 100644 --- a/firmware/u2f_knownapps.h +++ b/firmware/u2f_knownapps.h @@ -24,14 +24,15 @@ #include "u2f/u2f.h" typedef struct { - const uint8_t appid[U2F_APPID_SIZE]; - const char *appname; + const uint8_t appid[U2F_APPID_SIZE]; + const char *appname; } U2FWellKnown; // contents generated via script in // trezor-common/defs/webauthn/gen.py // do not edit manually +// clang-format off static const U2FWellKnown u2f_well_known[] = { { // U2F: https://bitbucket.org @@ -144,5 +145,6 @@ static const U2FWellKnown u2f_well_known[] = { "demo.yubico.com" }, }; +// clang-format on -#endif // U2F_KNOWNAPPS_INCLUDED +#endif // U2F_KNOWNAPPS_INCLUDED diff --git a/firmware/udp.c b/firmware/udp.c index 75c3e08a3..e9161261d 100644 --- a/firmware/udp.c +++ b/firmware/udp.c @@ -21,15 +21,13 @@ #include "usb.h" +#include "debug.h" #include "messages.h" #include "timer.h" -#include "debug.h" static volatile char tiny = 0; -void usbInit(void) { - emulatorSocketInit(); -} +void usbInit(void) { emulatorSocketInit(); } #if DEBUG_LINK #define _ISDBG (((iface == 1) ? 'd' : 'n')) @@ -38,42 +36,42 @@ void usbInit(void) { #endif void usbPoll(void) { - emulatorPoll(); + emulatorPoll(); - static uint8_t buffer[64]; + static uint8_t buffer[64]; - int iface = 0; - if (emulatorSocketRead(&iface, buffer, sizeof(buffer)) > 0) { - if (!tiny) { - msg_read_common(_ISDBG, buffer, sizeof(buffer)); - } else { - msg_read_tiny(buffer, sizeof(buffer)); - } - } + int iface = 0; + if (emulatorSocketRead(&iface, buffer, sizeof(buffer)) > 0) { + if (!tiny) { + msg_read_common(_ISDBG, buffer, sizeof(buffer)); + } else { + msg_read_tiny(buffer, sizeof(buffer)); + } + } - const uint8_t *data = msg_out_data(); - if (data != NULL) { - emulatorSocketWrite(0, data, 64); - } + const uint8_t *data = msg_out_data(); + if (data != NULL) { + emulatorSocketWrite(0, data, 64); + } #if DEBUG_LINK - data = msg_debug_out_data(); - if (data != NULL) { - emulatorSocketWrite(1, data, 64); - } + data = msg_debug_out_data(); + if (data != NULL) { + emulatorSocketWrite(1, data, 64); + } #endif } char usbTiny(char set) { - char old = tiny; - tiny = set; - return old; + char old = tiny; + tiny = set; + return old; } void usbSleep(uint32_t millis) { - uint32_t start = timer_ms(); + uint32_t start = timer_ms(); - while ((timer_ms() - start) < millis) { - usbPoll(); - } + while ((timer_ms() - start) < millis) { + usbPoll(); + } } diff --git a/firmware/usb.c b/firmware/usb.c index 4830da662..09fb45a56 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -17,23 +17,22 @@ * along with this library. If not, see . */ -#include #include +#include -#include "trezor.h" -#include "usb.h" +#include "config.h" #include "debug.h" #include "messages.h" +#include "timer.h" +#include "trezor.h" #include "u2f.h" -#include "config.h" +#include "usb.h" #include "util.h" -#include "timer.h" #include "usb21_standard.h" #include "webusb.h" #include "winusb.h" - #define USB_INTERFACE_INDEX_MAIN 0 #if DEBUG_LINK #define USB_INTERFACE_INDEX_DEBUG 1 @@ -44,370 +43,381 @@ #define USB_INTERFACE_COUNT 2 #endif -#define ENDPOINT_ADDRESS_MAIN_IN (0x81) -#define ENDPOINT_ADDRESS_MAIN_OUT (0x01) +#define ENDPOINT_ADDRESS_MAIN_IN (0x81) +#define ENDPOINT_ADDRESS_MAIN_OUT (0x01) #if DEBUG_LINK -#define ENDPOINT_ADDRESS_DEBUG_IN (0x82) -#define ENDPOINT_ADDRESS_DEBUG_OUT (0x02) +#define ENDPOINT_ADDRESS_DEBUG_IN (0x82) +#define ENDPOINT_ADDRESS_DEBUG_OUT (0x02) #endif -#define ENDPOINT_ADDRESS_U2F_IN (0x83) -#define ENDPOINT_ADDRESS_U2F_OUT (0x03) +#define ENDPOINT_ADDRESS_U2F_IN (0x83) +#define ENDPOINT_ADDRESS_U2F_OUT (0x03) -#define USB_STRINGS \ - X(MANUFACTURER, "SatoshiLabs") \ - X(PRODUCT, "TREZOR") \ - X(SERIAL_NUMBER, config_uuid_str) \ - X(INTERFACE_MAIN, "TREZOR Interface") \ - X(INTERFACE_DEBUG, "TREZOR Debug Link Interface") \ - X(INTERFACE_U2F, "TREZOR U2F Interface") \ +#define USB_STRINGS \ + X(MANUFACTURER, "SatoshiLabs") \ + X(PRODUCT, "TREZOR") \ + X(SERIAL_NUMBER, config_uuid_str) \ + X(INTERFACE_MAIN, "TREZOR Interface") \ + X(INTERFACE_DEBUG, "TREZOR Debug Link Interface") \ + X(INTERFACE_U2F, "TREZOR U2F Interface") #define X(name, value) USB_STRING_##name, enum { - USB_STRING_LANGID_CODES, // LANGID code array - USB_STRINGS + USB_STRING_LANGID_CODES, // LANGID code array + USB_STRINGS }; #undef X #define X(name, value) value, -static const char *usb_strings[] = { - USB_STRINGS -}; +static const char *usb_strings[] = {USB_STRINGS}; #undef X static const struct usb_device_descriptor dev_descr = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0210, - .bDeviceClass = 0, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - .bMaxPacketSize0 = 64, - .idVendor = 0x1209, - .idProduct = 0x53c1, - .bcdDevice = 0x0100, - .iManufacturer = USB_STRING_MANUFACTURER, - .iProduct = USB_STRING_PRODUCT, - .iSerialNumber = USB_STRING_SERIAL_NUMBER, - .bNumConfigurations = 1, + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0210, + .bDeviceClass = 0, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0x1209, + .idProduct = 0x53c1, + .bcdDevice = 0x0100, + .iManufacturer = USB_STRING_MANUFACTURER, + .iProduct = USB_STRING_PRODUCT, + .iSerialNumber = USB_STRING_SERIAL_NUMBER, + .bNumConfigurations = 1, }; static const uint8_t hid_report_descriptor_u2f[] = { - 0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance) - 0x09, 0x01, // USAGE (U2F HID Authenticator Device) - 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x20, // USAGE (Input Report Data) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x40, // REPORT_COUNT (64) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x09, 0x21, // USAGE (Output Report Data) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x40, // REPORT_COUNT (64) - 0x91, 0x02, // OUTPUT (Data,Var,Abs) - 0xc0 // END_COLLECTION + 0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance) + 0x09, 0x01, // USAGE (U2F HID Authenticator Device) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x20, // USAGE (Input Report Data) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x40, // REPORT_COUNT (64) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x09, 0x21, // USAGE (Output Report Data) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x40, // REPORT_COUNT (64) + 0x91, 0x02, // OUTPUT (Data,Var,Abs) + 0xc0 // END_COLLECTION }; static const struct { - struct usb_hid_descriptor hid_descriptor_u2f; - struct { - uint8_t bReportDescriptorType; - uint16_t wDescriptorLength; - } __attribute__((packed)) hid_report_u2f; -} __attribute__((packed)) hid_function_u2f = { - .hid_descriptor_u2f = { - .bLength = sizeof(hid_function_u2f), - .bDescriptorType = USB_DT_HID, - .bcdHID = 0x0111, - .bCountryCode = 0, - .bNumDescriptors = 1, - }, - .hid_report_u2f = { - .bReportDescriptorType = USB_DT_REPORT, - .wDescriptorLength = sizeof(hid_report_descriptor_u2f), - } -}; - -static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = {{ - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_U2F_IN, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}, { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_U2F_OUT, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}}; + struct usb_hid_descriptor hid_descriptor_u2f; + struct { + uint8_t bReportDescriptorType; + uint16_t wDescriptorLength; + } __attribute__((packed)) hid_report_u2f; +} __attribute__((packed)) +hid_function_u2f = {.hid_descriptor_u2f = + { + .bLength = sizeof(hid_function_u2f), + .bDescriptorType = USB_DT_HID, + .bcdHID = 0x0111, + .bCountryCode = 0, + .bNumDescriptors = 1, + }, + .hid_report_u2f = { + .bReportDescriptorType = USB_DT_REPORT, + .wDescriptorLength = sizeof(hid_report_descriptor_u2f), + }}; + +static const struct usb_endpoint_descriptor hid_endpoints_u2f[2] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_U2F_IN, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_U2F_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }}; static const struct usb_interface_descriptor hid_iface_u2f[] = {{ - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = USB_INTERFACE_INDEX_U2F, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_HID, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = USB_STRING_INTERFACE_U2F, - .endpoint = hid_endpoints_u2f, - .extra = &hid_function_u2f, - .extralen = sizeof(hid_function_u2f), + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_INTERFACE_INDEX_U2F, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = USB_STRING_INTERFACE_U2F, + .endpoint = hid_endpoints_u2f, + .extra = &hid_function_u2f, + .extralen = sizeof(hid_function_u2f), }}; #if DEBUG_LINK -static const struct usb_endpoint_descriptor webusb_endpoints_debug[2] = {{ - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_DEBUG_IN, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}, { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_DEBUG_OUT, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}}; +static const struct usb_endpoint_descriptor webusb_endpoints_debug[2] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_DEBUG_IN, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_DEBUG_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }}; static const struct usb_interface_descriptor webusb_iface_debug[] = {{ - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = USB_INTERFACE_INDEX_DEBUG, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = USB_STRING_INTERFACE_DEBUG, - .endpoint = webusb_endpoints_debug, - .extra = NULL, - .extralen = 0, + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_INTERFACE_INDEX_DEBUG, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = USB_STRING_INTERFACE_DEBUG, + .endpoint = webusb_endpoints_debug, + .extra = NULL, + .extralen = 0, }}; #endif -static const struct usb_endpoint_descriptor webusb_endpoints_main[2] = {{ - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_MAIN_IN, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}, { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = ENDPOINT_ADDRESS_MAIN_OUT, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 64, - .bInterval = 1, -}}; +static const struct usb_endpoint_descriptor webusb_endpoints_main[2] = { + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_MAIN_IN, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }, + { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = ENDPOINT_ADDRESS_MAIN_OUT, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 64, + .bInterval = 1, + }}; static const struct usb_interface_descriptor webusb_iface_main[] = {{ - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = USB_INTERFACE_INDEX_MAIN, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = USB_STRING_INTERFACE_MAIN, - .endpoint = webusb_endpoints_main, - .extra = NULL, - .extralen = 0, + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_INTERFACE_INDEX_MAIN, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = USB_STRING_INTERFACE_MAIN, + .endpoint = webusb_endpoints_main, + .extra = NULL, + .extralen = 0, }}; - // Windows are strict about interfaces appearing // in correct order -static const struct usb_interface ifaces[] = {{ - .num_altsetting = 1, - .altsetting = webusb_iface_main, +static const struct usb_interface ifaces[] = { + { + .num_altsetting = 1, + .altsetting = webusb_iface_main, #if DEBUG_LINK -}, { - .num_altsetting = 1, - .altsetting = webusb_iface_debug, + }, + { + .num_altsetting = 1, + .altsetting = webusb_iface_debug, #endif -}, { - .num_altsetting = 1, - .altsetting = hid_iface_u2f, -}}; + }, + { + .num_altsetting = 1, + .altsetting = hid_iface_u2f, + }}; static const struct usb_config_descriptor config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = USB_INTERFACE_COUNT, - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80, - .bMaxPower = 0x32, - .interface = ifaces, + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = USB_INTERFACE_COUNT, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + .interface = ifaces, }; -static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len, usbd_control_complete_callback *complete) -{ - (void)complete; - (void)dev; +static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, + uint8_t **buf, uint16_t *len, + usbd_control_complete_callback *complete) { + (void)complete; + (void)dev; - wait_random(); + wait_random(); - if ((req->bmRequestType != 0x81) || - (req->bRequest != USB_REQ_GET_DESCRIPTOR) || - (req->wValue != 0x2200)) - return 0; + if ((req->bmRequestType != 0x81) || + (req->bRequest != USB_REQ_GET_DESCRIPTOR) || (req->wValue != 0x2200)) + return 0; - debugLog(0, "", "hid_control_request u2f"); - *buf = (uint8_t *)hid_report_descriptor_u2f; - *len = MIN_8bits(*len, sizeof(hid_report_descriptor_u2f)); - return 1; + debugLog(0, "", "hid_control_request u2f"); + *buf = (uint8_t *)hid_report_descriptor_u2f; + *len = MIN_8bits(*len, sizeof(hid_report_descriptor_u2f)); + return 1; } static volatile char tiny = 0; -static void main_rx_callback(usbd_device *dev, uint8_t ep) -{ - (void)ep; - static CONFIDENTIAL uint8_t buf[64] __attribute__ ((aligned(4))); - if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_MAIN_OUT, buf, 64) != 64) return; - debugLog(0, "", "main_rx_callback"); - if (!tiny) { - msg_read(buf, 64); - } else { - msg_read_tiny(buf, 64); - } +static void main_rx_callback(usbd_device *dev, uint8_t ep) { + (void)ep; + static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); + if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_MAIN_OUT, buf, 64) != 64) + return; + debugLog(0, "", "main_rx_callback"); + if (!tiny) { + msg_read(buf, 64); + } else { + msg_read_tiny(buf, 64); + } } -static void u2f_rx_callback(usbd_device *dev, uint8_t ep) -{ - (void)ep; - static CONFIDENTIAL uint8_t buf[64] __attribute__ ((aligned(4))); +static void u2f_rx_callback(usbd_device *dev, uint8_t ep) { + (void)ep; + static CONFIDENTIAL uint8_t buf[64] __attribute__((aligned(4))); - debugLog(0, "", "u2f_rx_callback"); - if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; - u2fhid_read(tiny, (const U2FHID_FRAME *) (void*) buf); + debugLog(0, "", "u2f_rx_callback"); + if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_U2F_OUT, buf, 64) != 64) return; + u2fhid_read(tiny, (const U2FHID_FRAME *)(void *)buf); } #if DEBUG_LINK -static void debug_rx_callback(usbd_device *dev, uint8_t ep) -{ - (void)ep; - static uint8_t buf[64] __attribute__ ((aligned(4))); - if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_DEBUG_OUT, buf, 64) != 64) return; - debugLog(0, "", "debug_rx_callback"); - if (!tiny) { - msg_debug_read(buf, 64); - } else { - msg_read_tiny(buf, 64); - } +static void debug_rx_callback(usbd_device *dev, uint8_t ep) { + (void)ep; + static uint8_t buf[64] __attribute__((aligned(4))); + if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_DEBUG_OUT, buf, 64) != 64) + return; + debugLog(0, "", "debug_rx_callback"); + if (!tiny) { + msg_debug_read(buf, 64); + } else { + msg_read_tiny(buf, 64); + } } #endif -static void set_config(usbd_device *dev, uint16_t wValue) -{ - (void)wValue; - - usbd_ep_setup(dev, ENDPOINT_ADDRESS_MAIN_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); - usbd_ep_setup(dev, ENDPOINT_ADDRESS_MAIN_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, main_rx_callback); - usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); - usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, u2f_rx_callback); +static void set_config(usbd_device *dev, uint16_t wValue) { + (void)wValue; + + usbd_ep_setup(dev, ENDPOINT_ADDRESS_MAIN_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, + 0); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_MAIN_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, + main_rx_callback); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, + 0); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_U2F_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, + u2f_rx_callback); #if DEBUG_LINK - usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0); - usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, debug_rx_callback); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, + 0); + usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_OUT, USB_ENDPOINT_ATTR_INTERRUPT, + 64, debug_rx_callback); #endif - usbd_register_control_callback( - dev, - USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - hid_control_request); + usbd_register_control_callback( + dev, USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, hid_control_request); } static usbd_device *usbd_dev = NULL; -static uint8_t usbd_control_buffer[256] __attribute__ ((aligned (2))); +static uint8_t usbd_control_buffer[256] __attribute__((aligned(2))); -static const struct usb_device_capability_descriptor* capabilities[] = { - (const struct usb_device_capability_descriptor*)&webusb_platform_capability_descriptor_no_landing_page, +static const struct usb_device_capability_descriptor *capabilities[] = { + (const struct usb_device_capability_descriptor + *)&webusb_platform_capability_descriptor_no_landing_page, }; static const struct usb_bos_descriptor bos_descriptor = { - .bLength = USB_DT_BOS_SIZE, - .bDescriptorType = USB_DT_BOS, - .bNumDeviceCaps = sizeof(capabilities)/sizeof(capabilities[0]), - .capabilities = capabilities -}; - -void usbInit(void) -{ - usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, sizeof(usb_strings) / sizeof(*usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer)); - usbd_register_set_config_callback(usbd_dev, set_config); - usb21_setup(usbd_dev, &bos_descriptor); - static const char* origin_url = "trezor.io/start"; - webusb_setup(usbd_dev, origin_url); - // Debug link interface does not have WinUSB set; - // if you really need debug link on windows, edit the descriptor in winusb.c - winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN); + .bLength = USB_DT_BOS_SIZE, + .bDescriptorType = USB_DT_BOS, + .bNumDeviceCaps = sizeof(capabilities) / sizeof(capabilities[0]), + .capabilities = capabilities}; + +void usbInit(void) { + usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, + sizeof(usb_strings) / sizeof(*usb_strings), + usbd_control_buffer, sizeof(usbd_control_buffer)); + usbd_register_set_config_callback(usbd_dev, set_config); + usb21_setup(usbd_dev, &bos_descriptor); + static const char *origin_url = "trezor.io/start"; + webusb_setup(usbd_dev, origin_url); + // Debug link interface does not have WinUSB set; + // if you really need debug link on windows, edit the descriptor in winusb.c + winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN); } -void usbPoll(void) -{ - if (usbd_dev == NULL) { - return; - } - - static const uint8_t *data; - // poll read buffer - usbd_poll(usbd_dev); - // write pending data - data = msg_out_data(); - if (data) { - while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_MAIN_IN, data, 64) != 64 ) {} - } - data = u2f_out_data(); - if (data) { - while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_U2F_IN, data, 64) != 64 ) {} - } +void usbPoll(void) { + if (usbd_dev == NULL) { + return; + } + + static const uint8_t *data; + // poll read buffer + usbd_poll(usbd_dev); + // write pending data + data = msg_out_data(); + if (data) { + while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_MAIN_IN, data, 64) != + 64) { + } + } + data = u2f_out_data(); + if (data) { + while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_U2F_IN, data, 64) != + 64) { + } + } #if DEBUG_LINK - // write pending debug data - data = msg_debug_out_data(); - if (data) { - while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_DEBUG_IN, data, 64) != 64 ) {} - } + // write pending debug data + data = msg_debug_out_data(); + if (data) { + while (usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_DEBUG_IN, data, + 64) != 64) { + } + } #endif } -void usbReconnect(void) -{ - if (usbd_dev != NULL) { - usbd_disconnect(usbd_dev, 1); - delay(1000); - usbd_disconnect(usbd_dev, 0); - } +void usbReconnect(void) { + if (usbd_dev != NULL) { + usbd_disconnect(usbd_dev, 1); + delay(1000); + usbd_disconnect(usbd_dev, 0); + } } -char usbTiny(char set) -{ - char old = tiny; - tiny = set; - return old; +char usbTiny(char set) { + char old = tiny; + tiny = set; + return old; } -void usbSleep(uint32_t millis) -{ - uint32_t start = timer_ms(); +void usbSleep(uint32_t millis) { + uint32_t start = timer_ms(); - while ((timer_ms() - start) < millis) { - if (usbd_dev != NULL) { - usbd_poll(usbd_dev); - } - } + while ((timer_ms() - start) < millis) { + if (usbd_dev != NULL) { + usbd_poll(usbd_dev); + } + } } diff --git a/flash.c b/flash.c index 1501b6aa2..fc5bbafa2 100644 --- a/flash.c +++ b/flash.c @@ -17,8 +17,8 @@ * along with this program. If not, see . */ -#include #include +#include #include "common.h" #include "flash.h" @@ -26,112 +26,105 @@ #include "supervise.h" static const uint32_t FLASH_SECTOR_TABLE[FLASH_SECTOR_COUNT + 1] = { - [ 0] = 0x08000000, // - 0x08003FFF | 16 KiB - [ 1] = 0x08004000, // - 0x08007FFF | 16 KiB - [ 2] = 0x08008000, // - 0x0800BFFF | 16 KiB - [ 3] = 0x0800C000, // - 0x0800FFFF | 16 KiB - [ 4] = 0x08010000, // - 0x0801FFFF | 64 KiB - [ 5] = 0x08020000, // - 0x0803FFFF | 128 KiB - [ 6] = 0x08040000, // - 0x0805FFFF | 128 KiB - [ 7] = 0x08060000, // - 0x0807FFFF | 128 KiB - [ 8] = 0x08080000, // - 0x0809FFFF | 128 KiB - [ 9] = 0x080A0000, // - 0x080BFFFF | 128 KiB - [10] = 0x080C0000, // - 0x080DFFFF | 128 KiB - [11] = 0x080E0000, // - 0x080FFFFF | 128 KiB - [12] = 0x08100000, // last element - not a valid sector + [0] = 0x08000000, // - 0x08003FFF | 16 KiB + [1] = 0x08004000, // - 0x08007FFF | 16 KiB + [2] = 0x08008000, // - 0x0800BFFF | 16 KiB + [3] = 0x0800C000, // - 0x0800FFFF | 16 KiB + [4] = 0x08010000, // - 0x0801FFFF | 64 KiB + [5] = 0x08020000, // - 0x0803FFFF | 128 KiB + [6] = 0x08040000, // - 0x0805FFFF | 128 KiB + [7] = 0x08060000, // - 0x0807FFFF | 128 KiB + [8] = 0x08080000, // - 0x0809FFFF | 128 KiB + [9] = 0x080A0000, // - 0x080BFFFF | 128 KiB + [10] = 0x080C0000, // - 0x080DFFFF | 128 KiB + [11] = 0x080E0000, // - 0x080FFFFF | 128 KiB + [12] = 0x08100000, // last element - not a valid sector }; -static secbool flash_check_success(uint32_t status) -{ - return (status & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | FLASH_SR_WRPERR)) ? secfalse : sectrue; +static secbool flash_check_success(uint32_t status) { + return (status & (FLASH_SR_PGAERR | FLASH_SR_PGPERR | FLASH_SR_PGSERR | + FLASH_SR_WRPERR)) + ? secfalse + : sectrue; } -void flash_init(void) -{ -} +void flash_init(void) {} -secbool flash_unlock_write(void) -{ - svc_flash_unlock(); - return sectrue; +secbool flash_unlock_write(void) { + svc_flash_unlock(); + return sectrue; } -secbool flash_lock_write(void) -{ - return flash_check_success(svc_flash_lock()); -} - -const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) -{ - if (sector >= FLASH_SECTOR_COUNT) { - return NULL; - } - const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset; - const uint32_t next = FLASH_SECTOR_TABLE[sector + 1]; - if (addr + size > next) { - return NULL; - } - return (const void *) FLASH_PTR(addr); +secbool flash_lock_write(void) { return flash_check_success(svc_flash_lock()); } + +const void *flash_get_address(uint8_t sector, uint32_t offset, uint32_t size) { + if (sector >= FLASH_SECTOR_COUNT) { + return NULL; + } + const uint32_t addr = FLASH_SECTOR_TABLE[sector] + offset; + const uint32_t next = FLASH_SECTOR_TABLE[sector + 1]; + if (addr + size > next) { + return NULL; + } + return (const void *)FLASH_PTR(addr); } -secbool flash_erase(uint8_t sector) -{ - ensure(flash_unlock_write(), NULL); - svc_flash_erase_sector(sector); - ensure(flash_lock_write(), NULL); - - // Check whether the sector was really deleted (contains only 0xFF). - const uint32_t addr_start = FLASH_SECTOR_TABLE[sector], addr_end = FLASH_SECTOR_TABLE[sector + 1]; - for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { - if (*((const uint32_t *)FLASH_PTR(addr)) != 0xFFFFFFFF) { - return secfalse; - } +secbool flash_erase(uint8_t sector) { + ensure(flash_unlock_write(), NULL); + svc_flash_erase_sector(sector); + ensure(flash_lock_write(), NULL); + + // Check whether the sector was really deleted (contains only 0xFF). + const uint32_t addr_start = FLASH_SECTOR_TABLE[sector], + addr_end = FLASH_SECTOR_TABLE[sector + 1]; + for (uint32_t addr = addr_start; addr < addr_end; addr += 4) { + if (*((const uint32_t *)FLASH_PTR(addr)) != 0xFFFFFFFF) { + return secfalse; } - return sectrue; + } + return sectrue; } -secbool flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data) -{ - uint8_t *address = (uint8_t *) flash_get_address(sector, offset, 1); - if (address == NULL) { - return secfalse; - } +secbool flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data) { + uint8_t *address = (uint8_t *)flash_get_address(sector, offset, 1); + if (address == NULL) { + return secfalse; + } - if ((*address & data) != data) { - return secfalse; - } + if ((*address & data) != data) { + return secfalse; + } - svc_flash_program(FLASH_CR_PROGRAM_X8); - *(volatile uint8_t *) address = data; + svc_flash_program(FLASH_CR_PROGRAM_X8); + *(volatile uint8_t *)address = data; - if (*address != data) { - return secfalse; - } + if (*address != data) { + return secfalse; + } - return sectrue; + return sectrue; } -secbool flash_write_word(uint8_t sector, uint32_t offset, uint32_t data) -{ - uint32_t *address = (uint32_t *) flash_get_address(sector, offset, 4); - if (address == NULL) { - return secfalse; - } +secbool flash_write_word(uint8_t sector, uint32_t offset, uint32_t data) { + uint32_t *address = (uint32_t *)flash_get_address(sector, offset, 4); + if (address == NULL) { + return secfalse; + } - if (offset % 4 != 0) { - return secfalse; - } + if (offset % 4 != 0) { + return secfalse; + } - if ((*address & data) != data) { - return secfalse; - } + if ((*address & data) != data) { + return secfalse; + } - svc_flash_program(FLASH_CR_PROGRAM_X32); - *(volatile uint32_t *) address = data; + svc_flash_program(FLASH_CR_PROGRAM_X32); + *(volatile uint32_t *)address = data; - if (*address != data) { - return secfalse; - } + if (*address != data) { + return secfalse; + } - return sectrue; + return sectrue; } diff --git a/flash.h b/flash.h index 24e1a3ad6..c0c7289d1 100644 --- a/flash.h +++ b/flash.h @@ -26,12 +26,15 @@ #define FLASH_SECTOR_COUNT 24 -// note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427) (reference RM0090 section 3.7.5) +// note: FLASH_SR_RDERR is STM32F42xxx and STM32F43xxx specific (STM32F427) +// (reference RM0090 section 3.7.5) #ifndef STM32F427xx #define FLASH_SR_RDERR 0 #endif -#define FLASH_STATUS_ALL_FLAGS (FLASH_SR_RDERR | FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR | FLASH_SR_WRPERR | FLASH_SR_SOP | FLASH_SR_EOP) +#define FLASH_STATUS_ALL_FLAGS \ + (FLASH_SR_RDERR | FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR | \ + FLASH_SR_WRPERR | FLASH_SR_SOP | FLASH_SR_EOP) void flash_init(void); @@ -44,4 +47,4 @@ secbool __wur flash_erase(uint8_t sector); secbool __wur flash_write_byte(uint8_t sector, uint32_t offset, uint8_t data); secbool __wur flash_write_word(uint8_t sector, uint32_t offset, uint32_t data); -#endif // FLASH_H +#endif // FLASH_H diff --git a/layout.c b/layout.c index 6b3e5538a..b2a37e177 100644 --- a/layout.c +++ b/layout.c @@ -22,95 +22,102 @@ #include "layout.h" #include "oled.h" -void layoutButtonNo(const char *btnNo) -{ - oledDrawString(1, OLED_HEIGHT - 8, "\x15", FONT_STANDARD); - oledDrawString(fontCharWidth(FONT_STANDARD, '\x15') + 3, OLED_HEIGHT - 8, btnNo, FONT_STANDARD); - oledInvert(0, OLED_HEIGHT - 9, fontCharWidth(FONT_STANDARD, '\x15') + oledStringWidth(btnNo, FONT_STANDARD) + 2, OLED_HEIGHT - 1); +void layoutButtonNo(const char *btnNo) { + oledDrawString(1, OLED_HEIGHT - 8, "\x15", FONT_STANDARD); + oledDrawString(fontCharWidth(FONT_STANDARD, '\x15') + 3, OLED_HEIGHT - 8, + btnNo, FONT_STANDARD); + oledInvert(0, OLED_HEIGHT - 9, + fontCharWidth(FONT_STANDARD, '\x15') + + oledStringWidth(btnNo, FONT_STANDARD) + 2, + OLED_HEIGHT - 1); } -void layoutButtonYes(const char *btnYes) -{ - oledDrawString(OLED_WIDTH - fontCharWidth(FONT_STANDARD, '\x06') - 1, OLED_HEIGHT - 8, "\x06", FONT_STANDARD); - oledDrawStringRight(OLED_WIDTH - fontCharWidth(FONT_STANDARD, '\x06') - 3, OLED_HEIGHT - 8, btnYes, FONT_STANDARD); - oledInvert(OLED_WIDTH - oledStringWidth(btnYes, FONT_STANDARD) - fontCharWidth(FONT_STANDARD, '\x06') - 4, OLED_HEIGHT - 9, OLED_WIDTH - 1, OLED_HEIGHT - 1); +void layoutButtonYes(const char *btnYes) { + oledDrawString(OLED_WIDTH - fontCharWidth(FONT_STANDARD, '\x06') - 1, + OLED_HEIGHT - 8, "\x06", FONT_STANDARD); + oledDrawStringRight(OLED_WIDTH - fontCharWidth(FONT_STANDARD, '\x06') - 3, + OLED_HEIGHT - 8, btnYes, FONT_STANDARD); + oledInvert(OLED_WIDTH - oledStringWidth(btnYes, FONT_STANDARD) - + fontCharWidth(FONT_STANDARD, '\x06') - 4, + OLED_HEIGHT - 9, OLED_WIDTH - 1, OLED_HEIGHT - 1); } -void layoutDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6) -{ - int left = 0; - oledClear(); - if (icon) { - oledDrawBitmap(0, 0, icon); - left = icon->width + 4; - } - if (line1) oledDrawString(left, 0 * 9, line1, FONT_STANDARD); - if (line2) oledDrawString(left, 1 * 9, line2, FONT_STANDARD); - if (line3) oledDrawString(left, 2 * 9, line3, FONT_STANDARD); - if (line4) oledDrawString(left, 3 * 9, line4, FONT_STANDARD); - if (desc) { - oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 2 * 9 - 1, desc, FONT_STANDARD); - if (btnYes || btnNo) { - oledHLine(OLED_HEIGHT - 21); - } - } else { - if (line5) oledDrawString(left, 4 * 9, line5, FONT_STANDARD); - if (line6) oledDrawString(left, 5 * 9, line6, FONT_STANDARD); - if (btnYes || btnNo) { - oledHLine(OLED_HEIGHT - 13); - } - } - if (btnNo) { - layoutButtonNo(btnNo); - } - if (btnYes) { - layoutButtonYes(btnYes); - } - oledRefresh(); +void layoutDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, + const char *desc, const char *line1, const char *line2, + const char *line3, const char *line4, const char *line5, + const char *line6) { + int left = 0; + oledClear(); + if (icon) { + oledDrawBitmap(0, 0, icon); + left = icon->width + 4; + } + if (line1) oledDrawString(left, 0 * 9, line1, FONT_STANDARD); + if (line2) oledDrawString(left, 1 * 9, line2, FONT_STANDARD); + if (line3) oledDrawString(left, 2 * 9, line3, FONT_STANDARD); + if (line4) oledDrawString(left, 3 * 9, line4, FONT_STANDARD); + if (desc) { + oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 2 * 9 - 1, desc, + FONT_STANDARD); + if (btnYes || btnNo) { + oledHLine(OLED_HEIGHT - 21); + } + } else { + if (line5) oledDrawString(left, 4 * 9, line5, FONT_STANDARD); + if (line6) oledDrawString(left, 5 * 9, line6, FONT_STANDARD); + if (btnYes || btnNo) { + oledHLine(OLED_HEIGHT - 13); + } + } + if (btnNo) { + layoutButtonNo(btnNo); + } + if (btnYes) { + layoutButtonYes(btnYes); + } + oledRefresh(); } -void layoutProgressUpdate(bool refresh) -{ - static uint8_t step = 0; - switch (step) { - case 0: - oledDrawBitmap(40, 0, &bmp_gears0); - break; - case 1: - oledDrawBitmap(40, 0, &bmp_gears1); - break; - case 2: - oledDrawBitmap(40, 0, &bmp_gears2); - break; - case 3: - oledDrawBitmap(40, 0, &bmp_gears3); - break; - } - step = (step + 1) % 4; - if (refresh) { - oledRefresh(); - } +void layoutProgressUpdate(bool refresh) { + static uint8_t step = 0; + switch (step) { + case 0: + oledDrawBitmap(40, 0, &bmp_gears0); + break; + case 1: + oledDrawBitmap(40, 0, &bmp_gears1); + break; + case 2: + oledDrawBitmap(40, 0, &bmp_gears2); + break; + case 3: + oledDrawBitmap(40, 0, &bmp_gears3); + break; + } + step = (step + 1) % 4; + if (refresh) { + oledRefresh(); + } } -void layoutProgress(const char *desc, int permil) -{ - oledClear(); - layoutProgressUpdate(false); - // progressbar - oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1); - oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0); - permil = permil * (OLED_WIDTH - 4) / 1000; - if (permil < 0) { - permil = 0; - } - if (permil > OLED_WIDTH - 4) { - permil = OLED_WIDTH - 4; - } - oledBox(2, OLED_HEIGHT - 6, 1 + permil, OLED_HEIGHT - 3, 1); - // text - oledBox(0, OLED_HEIGHT - 16, OLED_WIDTH - 1, OLED_HEIGHT - 16 + 7, 0); - if (desc) { - oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 16, desc, FONT_STANDARD); - } - oledRefresh(); +void layoutProgress(const char *desc, int permil) { + oledClear(); + layoutProgressUpdate(false); + // progressbar + oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1); + oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0); + permil = permil * (OLED_WIDTH - 4) / 1000; + if (permil < 0) { + permil = 0; + } + if (permil > OLED_WIDTH - 4) { + permil = OLED_WIDTH - 4; + } + oledBox(2, OLED_HEIGHT - 6, 1 + permil, OLED_HEIGHT - 3, 1); + // text + oledBox(0, OLED_HEIGHT - 16, OLED_WIDTH - 1, OLED_HEIGHT - 16 + 7, 0); + if (desc) { + oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 16, desc, FONT_STANDARD); + } + oledRefresh(); } diff --git a/layout.h b/layout.h index eea043277..2f662c657 100644 --- a/layout.h +++ b/layout.h @@ -20,13 +20,16 @@ #ifndef __LAYOUT_H__ #define __LAYOUT_H__ -#include #include +#include #include "bitmaps.h" void layoutButtonNo(const char *btnNo); void layoutButtonYes(const char *btnYes); -void layoutDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, const char *desc, const char *line1, const char *line2, const char *line3, const char *line4, const char *line5, const char *line6); +void layoutDialog(const BITMAP *icon, const char *btnNo, const char *btnYes, + const char *desc, const char *line1, const char *line2, + const char *line3, const char *line4, const char *line5, + const char *line6); void layoutProgressUpdate(bool refresh); void layoutProgress(const char *desc, int permil); diff --git a/memory.c b/memory.c index b11e046b2..2eaa4368f 100644 --- a/memory.c +++ b/memory.c @@ -17,38 +17,43 @@ * along with this library. If not, see . */ +#include "memory.h" #include #include -#include "memory.h" #include "sha2.h" #define FLASH_OPTION_BYTES_1 (*(const uint64_t *)0x1FFFC000) #define FLASH_OPTION_BYTES_2 (*(const uint64_t *)0x1FFFC008) -void memory_protect(void) -{ +void memory_protect(void) { #if MEMORY_PROTECT - // Reference STM32F205 Flash programming manual revision 5 http://www.st.com/resource/en/programming_manual/cd00233952.pdf - // Section 2.6 Option bytes - // set RDP level 2 WRP for sectors 0 and 1 flash option control register matches - if (((FLASH_OPTION_BYTES_1 & 0xFFEC) == 0xCCEC) && ((FLASH_OPTION_BYTES_2 & 0xFFF) == 0xFFC) && (FLASH_OPTCR == 0x0FFCCCED)) { - return; // already set up correctly - bail out - } - for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; i++) { - flash_erase_sector(i, FLASH_CR_PROGRAM_X32); - } - flash_unlock_option_bytes(); - // Section 2.8.6 Flash option control register (FLASH_OPTCR) - // Bits 31:28 Reserved, must be kept cleared. - // Bits 27:16 nWRP: Not write protect: write protect bootloader code in flash main memory sectors 0 and 1 (Section 2.3; table 2) - // Bits 15:8 RDP: Read protect: level 2 chip read protection active - // Bits 7:5 USER: User option bytes: no reset on standby, no reset on stop, software watchdog - // Bit 4 Reserved, must be kept cleared. - // Bits 3:2 BOR_LEV: BOR reset Level: BOR off - // Bit 1 OPTSTRT: Option start: ignored by flash_program_option_bytes - // Bit 0 OPTLOCK: Option lock: ignored by flash_program_option_bytes - flash_program_option_bytes(0x0FFCCCEC); - flash_lock_option_bytes(); + // Reference STM32F205 Flash programming manual revision 5 + // http://www.st.com/resource/en/programming_manual/cd00233952.pdf Section 2.6 + // Option bytes + // set RDP level 2 WRP for sectors 0 and + // 1 flash option control register matches + if (((FLASH_OPTION_BYTES_1 & 0xFFEC) == 0xCCEC) && + ((FLASH_OPTION_BYTES_2 & 0xFFF) == 0xFFC) && + (FLASH_OPTCR == 0x0FFCCCED)) { + return; // already set up correctly - bail out + } + for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST; + i++) { + flash_erase_sector(i, FLASH_CR_PROGRAM_X32); + } + flash_unlock_option_bytes(); + // Section 2.8.6 Flash option control register (FLASH_OPTCR) + // Bits 31:28 Reserved, must be kept cleared. + // Bits 27:16 nWRP: Not write protect: write protect bootloader code in + // flash main memory sectors 0 and 1 (Section 2.3; table 2) Bits 15:8 RDP: + // Read protect: level 2 chip read protection active Bits 7:5 USER: User + // option bytes: no reset on standby, no reset on stop, software watchdog + // Bit 4 Reserved, must be kept cleared. + // Bits 3:2 BOR_LEV: BOR reset Level: BOR off + // Bit 1 OPTSTRT: Option start: ignored by flash_program_option_bytes + // Bit 0 OPTLOCK: Option lock: ignored by flash_program_option_bytes + flash_program_option_bytes(0x0FFCCCEC); + flash_lock_option_bytes(); #endif } @@ -62,16 +67,14 @@ void memory_protect(void) // from OPTION_BYTES and not form FLASH_OPCTR register. // // Read protection is unaffected and always stays locked to the desired value. -void memory_write_unlock(void) -{ - flash_unlock_option_bytes(); - flash_program_option_bytes(0x0FFFCCEC); - flash_lock_option_bytes(); +void memory_write_unlock(void) { + flash_unlock_option_bytes(); + flash_program_option_bytes(0x0FFFCCEC); + flash_lock_option_bytes(); } -int memory_bootloader_hash(uint8_t *hash) -{ - sha256_Raw(FLASH_PTR(FLASH_BOOT_START), FLASH_BOOT_LEN, hash); - sha256_Raw(hash, 32, hash); - return 32; +int memory_bootloader_hash(uint8_t *hash) { + sha256_Raw(FLASH_PTR(FLASH_BOOT_START), FLASH_BOOT_LEN, hash); + sha256_Raw(hash, 32, hash); + return 32; } diff --git a/memory.h b/memory.h index e47fc2917..2494eff76 100644 --- a/memory.h +++ b/memory.h @@ -58,47 +58,47 @@ */ -#define FLASH_ORIGIN (0x08000000) +#define FLASH_ORIGIN (0x08000000) #if EMULATOR extern uint8_t *emulator_flash_base; -#define FLASH_PTR(x) (emulator_flash_base + (x - FLASH_ORIGIN)) +#define FLASH_PTR(x) (emulator_flash_base + (x - FLASH_ORIGIN)) #else -#define FLASH_PTR(x) (const uint8_t*) (x) +#define FLASH_PTR(x) (const uint8_t *)(x) #endif -#define FLASH_TOTAL_SIZE (1024 * 1024) +#define FLASH_TOTAL_SIZE (1024 * 1024) -#define FLASH_BOOT_START (FLASH_ORIGIN) -#define FLASH_BOOT_LEN (0x8000) +#define FLASH_BOOT_START (FLASH_ORIGIN) +#define FLASH_BOOT_LEN (0x8000) -#define FLASH_STORAGE_START (FLASH_BOOT_START + FLASH_BOOT_LEN) -#define FLASH_STORAGE_LEN (0x8000) +#define FLASH_STORAGE_START (FLASH_BOOT_START + FLASH_BOOT_LEN) +#define FLASH_STORAGE_LEN (0x8000) #define FLASH_FWHEADER_START (FLASH_STORAGE_START + FLASH_STORAGE_LEN) -#define FLASH_FWHEADER_LEN (0x400) +#define FLASH_FWHEADER_LEN (0x400) -#define FLASH_APP_START (FLASH_FWHEADER_START + FLASH_FWHEADER_LEN) -#define FLASH_APP_LEN (FLASH_TOTAL_SIZE - (FLASH_APP_START - FLASH_ORIGIN)) +#define FLASH_APP_START (FLASH_FWHEADER_START + FLASH_FWHEADER_LEN) +#define FLASH_APP_LEN (FLASH_TOTAL_SIZE - (FLASH_APP_START - FLASH_ORIGIN)) -#define FLASH_BOOT_SECTOR_FIRST 0 -#define FLASH_BOOT_SECTOR_LAST 1 +#define FLASH_BOOT_SECTOR_FIRST 0 +#define FLASH_BOOT_SECTOR_LAST 1 -#define FLASH_STORAGE_SECTOR_FIRST 2 -#define FLASH_STORAGE_SECTOR_LAST 3 +#define FLASH_STORAGE_SECTOR_FIRST 2 +#define FLASH_STORAGE_SECTOR_LAST 3 -#define FLASH_CODE_SECTOR_FIRST 4 -#define FLASH_CODE_SECTOR_LAST 11 +#define FLASH_CODE_SECTOR_FIRST 4 +#define FLASH_CODE_SECTOR_LAST 11 void memory_protect(void); void memory_write_unlock(void); int memory_bootloader_hash(uint8_t *hash); static inline void flash_write32(uint32_t addr, uint32_t word) { - *(volatile uint32_t *) FLASH_PTR(addr) = word; + *(volatile uint32_t *)FLASH_PTR(addr) = word; } static inline void flash_write8(uint32_t addr, uint8_t byte) { - *(volatile uint8_t *) FLASH_PTR(addr) = byte; + *(volatile uint8_t *)FLASH_PTR(addr) = byte; } #endif diff --git a/norcow_config.h b/norcow_config.h index 1ea894c2b..14fc3d666 100644 --- a/norcow_config.h +++ b/norcow_config.h @@ -23,11 +23,13 @@ #include "flash.h" #define NORCOW_SECTOR_COUNT 2 -#define NORCOW_SECTOR_SIZE (16*1024) -#define NORCOW_SECTORS {2, 3} +#define NORCOW_SECTOR_SIZE (16 * 1024) +#define NORCOW_SECTORS \ + { 2, 3 } /* - * The length of the sector header in bytes. The header is preserved between sector erasures. + * The length of the sector header in bytes. The header is preserved between + * sector erasures. */ #define NORCOW_HEADER_LEN (0) diff --git a/oled.c b/oled.c index 00a03a27e..d59d880ac 100644 --- a/oled.c +++ b/oled.c @@ -22,39 +22,39 @@ #include +#include "memzero.h" #include "oled.h" #include "util.h" -#include "memzero.h" -#define OLED_SETCONTRAST 0x81 -#define OLED_DISPLAYALLON_RESUME 0xA4 -#define OLED_DISPLAYALLON 0xA5 -#define OLED_NORMALDISPLAY 0xA6 -#define OLED_INVERTDISPLAY 0xA7 -#define OLED_DISPLAYOFF 0xAE -#define OLED_DISPLAYON 0xAF -#define OLED_SETDISPLAYOFFSET 0xD3 -#define OLED_SETCOMPINS 0xDA -#define OLED_SETVCOMDETECT 0xDB -#define OLED_SETDISPLAYCLOCKDIV 0xD5 -#define OLED_SETPRECHARGE 0xD9 -#define OLED_SETMULTIPLEX 0xA8 -#define OLED_SETLOWCOLUMN 0x00 -#define OLED_SETHIGHCOLUMN 0x10 -#define OLED_SETSTARTLINE 0x40 -#define OLED_MEMORYMODE 0x20 -#define OLED_COMSCANINC 0xC0 -#define OLED_COMSCANDEC 0xC8 -#define OLED_SEGREMAP 0xA0 -#define OLED_CHARGEPUMP 0x8D - -#define SPI_BASE SPI1 -#define OLED_DC_PORT GPIOB -#define OLED_DC_PIN GPIO0 // PB0 | Data/Command -#define OLED_CS_PORT GPIOA -#define OLED_CS_PIN GPIO4 // PA4 | SPI Select -#define OLED_RST_PORT GPIOB -#define OLED_RST_PIN GPIO1 // PB1 | Reset display +#define OLED_SETCONTRAST 0x81 +#define OLED_DISPLAYALLON_RESUME 0xA4 +#define OLED_DISPLAYALLON 0xA5 +#define OLED_NORMALDISPLAY 0xA6 +#define OLED_INVERTDISPLAY 0xA7 +#define OLED_DISPLAYOFF 0xAE +#define OLED_DISPLAYON 0xAF +#define OLED_SETDISPLAYOFFSET 0xD3 +#define OLED_SETCOMPINS 0xDA +#define OLED_SETVCOMDETECT 0xDB +#define OLED_SETDISPLAYCLOCKDIV 0xD5 +#define OLED_SETPRECHARGE 0xD9 +#define OLED_SETMULTIPLEX 0xA8 +#define OLED_SETLOWCOLUMN 0x00 +#define OLED_SETHIGHCOLUMN 0x10 +#define OLED_SETSTARTLINE 0x40 +#define OLED_MEMORYMODE 0x20 +#define OLED_COMSCANINC 0xC0 +#define OLED_COMSCANDEC 0xC8 +#define OLED_SEGREMAP 0xA0 +#define OLED_CHARGEPUMP 0x8D + +#define SPI_BASE SPI1 +#define OLED_DC_PORT GPIOB +#define OLED_DC_PIN GPIO0 // PB0 | Data/Command +#define OLED_CS_PORT GPIOA +#define OLED_CS_PIN GPIO4 // PA4 | SPI Select +#define OLED_RST_PORT GPIOB +#define OLED_RST_PIN GPIO1 // PB1 | Reset display /* TREZOR has a display of size OLED_WIDTH x OLED_HEIGHT (128x64). * The contents of this display are buffered in _oledbuffer. This is @@ -70,126 +70,127 @@ static bool is_debug_link = 0; /* * macros to convert coordinate to bit position */ -#define OLED_OFFSET(x, y) (OLED_BUFSIZE - 1 - (x) - ((y)/8)*OLED_WIDTH) -#define OLED_MASK(x, y) (1 << (7 - (y) % 8)) +#define OLED_OFFSET(x, y) (OLED_BUFSIZE - 1 - (x) - ((y) / 8) * OLED_WIDTH) +#define OLED_MASK(x, y) (1 << (7 - (y) % 8)) /* * Draws a white pixel at x, y */ -void oledDrawPixel(int x, int y) -{ - if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) { - return; - } - _oledbuffer[OLED_OFFSET(x, y)] |= OLED_MASK(x, y); +void oledDrawPixel(int x, int y) { + if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) { + return; + } + _oledbuffer[OLED_OFFSET(x, y)] |= OLED_MASK(x, y); } /* * Clears pixel at x, y */ -void oledClearPixel(int x, int y) -{ - if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) { - return; - } - _oledbuffer[OLED_OFFSET(x, y)] &= ~OLED_MASK(x, y); +void oledClearPixel(int x, int y) { + if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) { + return; + } + _oledbuffer[OLED_OFFSET(x, y)] &= ~OLED_MASK(x, y); } /* * Inverts pixel at x, y */ -void oledInvertPixel(int x, int y) -{ - if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) { - return; - } - _oledbuffer[OLED_OFFSET(x, y)] ^= OLED_MASK(x, y); +void oledInvertPixel(int x, int y) { + if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) { + return; + } + _oledbuffer[OLED_OFFSET(x, y)] ^= OLED_MASK(x, y); } #if !EMULATOR /* * Send a block of data via the SPI bus. */ -static inline void SPISend(uint32_t base, const uint8_t *data, int len) -{ - delay(1); - for (int i = 0; i < len; i++) { - spi_send(base, data[i]); - } - while (!(SPI_SR(base) & SPI_SR_TXE)); - while ((SPI_SR(base) & SPI_SR_BSY)); +static inline void SPISend(uint32_t base, const uint8_t *data, int len) { + delay(1); + for (int i = 0; i < len; i++) { + spi_send(base, data[i]); + } + while (!(SPI_SR(base) & SPI_SR_TXE)) + ; + while ((SPI_SR(base) & SPI_SR_BSY)) + ; } /* * Initialize the display. */ -void oledInit() -{ - static const uint8_t s[25] = { - OLED_DISPLAYOFF, - OLED_SETDISPLAYCLOCKDIV, - 0x80, - OLED_SETMULTIPLEX, - 0x3F, // 128x64 - OLED_SETDISPLAYOFFSET, - 0x00, - OLED_SETSTARTLINE | 0x00, - OLED_CHARGEPUMP, - 0x14, - OLED_MEMORYMODE, - 0x00, - OLED_SEGREMAP | 0x01, - OLED_COMSCANDEC, - OLED_SETCOMPINS, - 0x12, // 128x64 - OLED_SETCONTRAST, - 0xCF, - OLED_SETPRECHARGE, - 0xF1, - OLED_SETVCOMDETECT, - 0x40, - OLED_DISPLAYALLON_RESUME, - OLED_NORMALDISPLAY, - OLED_DISPLAYON - }; - - gpio_clear(OLED_DC_PORT, OLED_DC_PIN); // set to CMD - gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect - - // Reset the LCD - gpio_set(OLED_RST_PORT, OLED_RST_PIN); - delay(40); - gpio_clear(OLED_RST_PORT, OLED_RST_PIN); - delay(400); - gpio_set(OLED_RST_PORT, OLED_RST_PIN); - - // init - gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select - SPISend(SPI_BASE, s, 25); - gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect - - oledClear(); - oledRefresh(); +void oledInit() { + static const uint8_t s[25] = {OLED_DISPLAYOFF, + OLED_SETDISPLAYCLOCKDIV, + 0x80, + OLED_SETMULTIPLEX, + 0x3F, // 128x64 + OLED_SETDISPLAYOFFSET, + 0x00, + OLED_SETSTARTLINE | 0x00, + OLED_CHARGEPUMP, + 0x14, + OLED_MEMORYMODE, + 0x00, + OLED_SEGREMAP | 0x01, + OLED_COMSCANDEC, + OLED_SETCOMPINS, + 0x12, // 128x64 + OLED_SETCONTRAST, + 0xCF, + OLED_SETPRECHARGE, + 0xF1, + OLED_SETVCOMDETECT, + 0x40, + OLED_DISPLAYALLON_RESUME, + OLED_NORMALDISPLAY, + OLED_DISPLAYON}; + + gpio_clear(OLED_DC_PORT, OLED_DC_PIN); // set to CMD + gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect + + // Reset the LCD + gpio_set(OLED_RST_PORT, OLED_RST_PIN); + delay(40); + gpio_clear(OLED_RST_PORT, OLED_RST_PIN); + delay(400); + gpio_set(OLED_RST_PORT, OLED_RST_PIN); + + // init + gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select + SPISend(SPI_BASE, s, 25); + gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect + + oledClear(); + oledRefresh(); } #endif /* * Clears the display buffer (sets all pixels to black) */ -void oledClear() -{ - memzero(_oledbuffer, sizeof(_oledbuffer)); -} - -void oledInvertDebugLink() -{ - if (is_debug_link) { - oledInvertPixel(OLED_WIDTH - 5, 0); oledInvertPixel(OLED_WIDTH - 4, 0); oledInvertPixel(OLED_WIDTH - 3, 0); oledInvertPixel(OLED_WIDTH - 2, 0); oledInvertPixel(OLED_WIDTH - 1, 0); - oledInvertPixel(OLED_WIDTH - 4, 1); oledInvertPixel(OLED_WIDTH - 3, 1); oledInvertPixel(OLED_WIDTH - 2, 1); oledInvertPixel(OLED_WIDTH - 1, 1); - oledInvertPixel(OLED_WIDTH - 3, 2); oledInvertPixel(OLED_WIDTH - 2, 2); oledInvertPixel(OLED_WIDTH - 1, 2); - oledInvertPixel(OLED_WIDTH - 2, 3); oledInvertPixel(OLED_WIDTH - 1, 3); - oledInvertPixel(OLED_WIDTH - 1, 4); - } +void oledClear() { memzero(_oledbuffer, sizeof(_oledbuffer)); } + +void oledInvertDebugLink() { + if (is_debug_link) { + oledInvertPixel(OLED_WIDTH - 5, 0); + oledInvertPixel(OLED_WIDTH - 4, 0); + oledInvertPixel(OLED_WIDTH - 3, 0); + oledInvertPixel(OLED_WIDTH - 2, 0); + oledInvertPixel(OLED_WIDTH - 1, 0); + oledInvertPixel(OLED_WIDTH - 4, 1); + oledInvertPixel(OLED_WIDTH - 3, 1); + oledInvertPixel(OLED_WIDTH - 2, 1); + oledInvertPixel(OLED_WIDTH - 1, 1); + oledInvertPixel(OLED_WIDTH - 3, 2); + oledInvertPixel(OLED_WIDTH - 2, 2); + oledInvertPixel(OLED_WIDTH - 1, 2); + oledInvertPixel(OLED_WIDTH - 2, 3); + oledInvertPixel(OLED_WIDTH - 1, 3); + oledInvertPixel(OLED_WIDTH - 1, 4); + } } /* @@ -199,225 +200,216 @@ void oledInvertDebugLink() * not the content of the display. */ #if !EMULATOR -void oledRefresh() -{ - static const uint8_t s[3] = {OLED_SETLOWCOLUMN | 0x00, OLED_SETHIGHCOLUMN | 0x00, OLED_SETSTARTLINE | 0x00}; - - // draw triangle in upper right corner - oledInvertDebugLink(); - - gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select - SPISend(SPI_BASE, s, 3); - gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect - - gpio_set(OLED_DC_PORT, OLED_DC_PIN); // set to DATA - gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select - SPISend(SPI_BASE, _oledbuffer, sizeof(_oledbuffer)); - gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect - gpio_clear(OLED_DC_PORT, OLED_DC_PIN); // set to CMD - - // return it back - oledInvertDebugLink(); +void oledRefresh() { + static const uint8_t s[3] = {OLED_SETLOWCOLUMN | 0x00, + OLED_SETHIGHCOLUMN | 0x00, + OLED_SETSTARTLINE | 0x00}; + + // draw triangle in upper right corner + oledInvertDebugLink(); + + gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select + SPISend(SPI_BASE, s, 3); + gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect + + gpio_set(OLED_DC_PORT, OLED_DC_PIN); // set to DATA + gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select + SPISend(SPI_BASE, _oledbuffer, sizeof(_oledbuffer)); + gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect + gpio_clear(OLED_DC_PORT, OLED_DC_PIN); // set to CMD + + // return it back + oledInvertDebugLink(); } #endif -const uint8_t *oledGetBuffer() -{ - return _oledbuffer; -} +const uint8_t *oledGetBuffer() { return _oledbuffer; } -void oledSetDebugLink(bool set) -{ - is_debug_link = set; - oledRefresh(); +void oledSetDebugLink(bool set) { + is_debug_link = set; + oledRefresh(); } -void oledSetBuffer(uint8_t *buf) -{ - memcpy(_oledbuffer, buf, sizeof(_oledbuffer)); +void oledSetBuffer(uint8_t *buf) { + memcpy(_oledbuffer, buf, sizeof(_oledbuffer)); } -void oledDrawChar(int x, int y, char c, int font) -{ - if (x >= OLED_WIDTH || y >= OLED_HEIGHT || y <= -FONT_HEIGHT) { - return; - } - - int zoom = (font & FONT_DOUBLE ? 2 : 1); - int char_width = fontCharWidth(font & 0x7f, c); - const uint8_t *char_data = fontCharData(font & 0x7f, c); - - if (x <= -char_width * zoom) { - return; - } - - for (int xo = 0; xo < char_width; xo++) { - for (int yo = 0; yo < FONT_HEIGHT; yo++) { - if (char_data[xo] & (1 << (FONT_HEIGHT - 1 - yo))) { - if (zoom <= 1) { - oledDrawPixel(x + xo, y + yo); - } else { - oledBox(x + xo * zoom, y + yo * zoom, x + (xo + 1) * zoom - 1, y + (yo + 1) * zoom - 1, true); - } - } - } - } +void oledDrawChar(int x, int y, char c, int font) { + if (x >= OLED_WIDTH || y >= OLED_HEIGHT || y <= -FONT_HEIGHT) { + return; + } + + int zoom = (font & FONT_DOUBLE ? 2 : 1); + int char_width = fontCharWidth(font & 0x7f, c); + const uint8_t *char_data = fontCharData(font & 0x7f, c); + + if (x <= -char_width * zoom) { + return; + } + + for (int xo = 0; xo < char_width; xo++) { + for (int yo = 0; yo < FONT_HEIGHT; yo++) { + if (char_data[xo] & (1 << (FONT_HEIGHT - 1 - yo))) { + if (zoom <= 1) { + oledDrawPixel(x + xo, y + yo); + } else { + oledBox(x + xo * zoom, y + yo * zoom, x + (xo + 1) * zoom - 1, + y + (yo + 1) * zoom - 1, true); + } + } + } + } } char oledConvertChar(const char c) { - uint8_t a = c; - if (a < 0x80) return c; - // UTF-8 handling: https://en.wikipedia.org/wiki/UTF-8#Description - // bytes 11xxxxxx are first byte of UTF-8 characters - // bytes 10xxxxxx are successive UTF-8 characters - if (a >= 0xC0) return '_'; - return 0; + uint8_t a = c; + if (a < 0x80) return c; + // UTF-8 handling: https://en.wikipedia.org/wiki/UTF-8#Description + // bytes 11xxxxxx are first byte of UTF-8 characters + // bytes 10xxxxxx are successive UTF-8 characters + if (a >= 0xC0) return '_'; + return 0; } int oledStringWidth(const char *text, int font) { - if (!text) return 0; - int size = (font & FONT_DOUBLE ? 2 : 1); - int l = 0; - for (; *text; text++) { - char c = oledConvertChar(*text); - if (c) { - l += size * (fontCharWidth(font & 0x7f, c) + 1); - } - } - return l; + if (!text) return 0; + int size = (font & FONT_DOUBLE ? 2 : 1); + int l = 0; + for (; *text; text++) { + char c = oledConvertChar(*text); + if (c) { + l += size * (fontCharWidth(font & 0x7f, c) + 1); + } + } + return l; } -void oledDrawString(int x, int y, const char* text, int font) -{ - if (!text) return; - int l = 0; - int size = (font & FONT_DOUBLE ? 2 : 1); - for (; *text; text++) { - char c = oledConvertChar(*text); - if (c) { - oledDrawChar(x + l, y, c, font); - l += size * (fontCharWidth(font & 0x7f, c) + 1); - } - } +void oledDrawString(int x, int y, const char *text, int font) { + if (!text) return; + int l = 0; + int size = (font & FONT_DOUBLE ? 2 : 1); + for (; *text; text++) { + char c = oledConvertChar(*text); + if (c) { + oledDrawChar(x + l, y, c, font); + l += size * (fontCharWidth(font & 0x7f, c) + 1); + } + } } -void oledDrawStringCenter(int x, int y, const char* text, int font) -{ - x = x - oledStringWidth(text, font) / 2; - oledDrawString(x, y, text, font); +void oledDrawStringCenter(int x, int y, const char *text, int font) { + x = x - oledStringWidth(text, font) / 2; + oledDrawString(x, y, text, font); } -void oledDrawStringRight(int x, int y, const char* text, int font) -{ - x -= oledStringWidth(text, font); - oledDrawString(x, y, text, font); +void oledDrawStringRight(int x, int y, const char *text, int font) { + x -= oledStringWidth(text, font); + oledDrawString(x, y, text, font); } -void oledDrawBitmap(int x, int y, const BITMAP *bmp) -{ - for (int i = 0; i < bmp->width; i++) { - for (int j = 0; j < bmp->height; j++) { - if (bmp->data[(i / 8) + j * bmp->width / 8] & (1 << (7 - i % 8))) { - oledDrawPixel(x + i, y + j); - } else { - oledClearPixel(x + i, y + j); - } - } - } +void oledDrawBitmap(int x, int y, const BITMAP *bmp) { + for (int i = 0; i < bmp->width; i++) { + for (int j = 0; j < bmp->height; j++) { + if (bmp->data[(i / 8) + j * bmp->width / 8] & (1 << (7 - i % 8))) { + oledDrawPixel(x + i, y + j); + } else { + oledClearPixel(x + i, y + j); + } + } + } } /* * Inverts box between (x1,y1) and (x2,y2) inclusive. */ -void oledInvert(int x1, int y1, int x2, int y2) -{ - x1 = MAX(x1, 0); - y1 = MAX(y1, 0); - x2 = MIN(x2, OLED_WIDTH - 1); - y2 = MIN(y2, OLED_HEIGHT - 1); - for (int x = x1; x <= x2; x++) { - for (int y = y1; y <= y2; y++) { - oledInvertPixel(x,y); - } - } +void oledInvert(int x1, int y1, int x2, int y2) { + x1 = MAX(x1, 0); + y1 = MAX(y1, 0); + x2 = MIN(x2, OLED_WIDTH - 1); + y2 = MIN(y2, OLED_HEIGHT - 1); + for (int x = x1; x <= x2; x++) { + for (int y = y1; y <= y2; y++) { + oledInvertPixel(x, y); + } + } } /* * Draw a filled rectangle. */ -void oledBox(int x1, int y1, int x2, int y2, bool set) -{ - x1 = MAX(x1, 0); - y1 = MAX(y1, 0); - x2 = MIN(x2, OLED_WIDTH - 1); - y2 = MIN(y2, OLED_HEIGHT - 1); - for (int x = x1; x <= x2; x++) { - for (int y = y1; y <= y2; y++) { - set ? oledDrawPixel(x, y) : oledClearPixel(x, y); - } - } +void oledBox(int x1, int y1, int x2, int y2, bool set) { + x1 = MAX(x1, 0); + y1 = MAX(y1, 0); + x2 = MIN(x2, OLED_WIDTH - 1); + y2 = MIN(y2, OLED_HEIGHT - 1); + for (int x = x1; x <= x2; x++) { + for (int y = y1; y <= y2; y++) { + set ? oledDrawPixel(x, y) : oledClearPixel(x, y); + } + } } void oledHLine(int y) { - if (y < 0 || y >= OLED_HEIGHT) { - return; - } - for (int x = 0; x < OLED_WIDTH; x++) { - oledDrawPixel(x, y); - } + if (y < 0 || y >= OLED_HEIGHT) { + return; + } + for (int x = 0; x < OLED_WIDTH; x++) { + oledDrawPixel(x, y); + } } /* * Draw a rectangle frame. */ -void oledFrame(int x1, int y1, int x2, int y2) -{ - for (int x = x1; x <= x2; x++) { - oledDrawPixel(x, y1); - oledDrawPixel(x, y2); - } - for (int y = y1 + 1; y < y2; y++) { - oledDrawPixel(x1, y); - oledDrawPixel(x2, y); - } +void oledFrame(int x1, int y1, int x2, int y2) { + for (int x = x1; x <= x2; x++) { + oledDrawPixel(x, y1); + oledDrawPixel(x, y2); + } + for (int y = y1 + 1; y < y2; y++) { + oledDrawPixel(x1, y); + oledDrawPixel(x2, y); + } } /* * Animates the display, swiping the current contents out to the left. * This clears the display. */ -void oledSwipeLeft(void) -{ - for (int i = 0; i < OLED_WIDTH; i++) { - for (int j = 0; j < OLED_HEIGHT / 8; j++) { - for (int k = OLED_WIDTH-1; k > 0; k--) { - _oledbuffer[j * OLED_WIDTH + k] = _oledbuffer[j * OLED_WIDTH + k - 1]; - } - _oledbuffer[j * OLED_WIDTH] = 0; - } - oledRefresh(); - } +void oledSwipeLeft(void) { + for (int i = 0; i < OLED_WIDTH; i++) { + for (int j = 0; j < OLED_HEIGHT / 8; j++) { + for (int k = OLED_WIDTH - 1; k > 0; k--) { + _oledbuffer[j * OLED_WIDTH + k] = _oledbuffer[j * OLED_WIDTH + k - 1]; + } + _oledbuffer[j * OLED_WIDTH] = 0; + } + oledRefresh(); + } } /* * Animates the display, swiping the current contents out to the right. * This clears the display. */ -void oledSwipeRight(void) -{ - for (int i = 0; i < OLED_WIDTH / 4; i++) { - for (int j = 0; j < OLED_HEIGHT / 8; j++) { - for (int k = 0; k < OLED_WIDTH / 4 - 1; k++) { - _oledbuffer[k * 4 + 0 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 4 + j * OLED_WIDTH]; - _oledbuffer[k * 4 + 1 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 5 + j * OLED_WIDTH]; - _oledbuffer[k * 4 + 2 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 6 + j * OLED_WIDTH]; - _oledbuffer[k * 4 + 3 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 7 + j * OLED_WIDTH]; - } - _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 1] = 0; - _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 2] = 0; - _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 3] = 0; - _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 4] = 0; - } - oledRefresh(); - } +void oledSwipeRight(void) { + for (int i = 0; i < OLED_WIDTH / 4; i++) { + for (int j = 0; j < OLED_HEIGHT / 8; j++) { + for (int k = 0; k < OLED_WIDTH / 4 - 1; k++) { + _oledbuffer[k * 4 + 0 + j * OLED_WIDTH] = + _oledbuffer[k * 4 + 4 + j * OLED_WIDTH]; + _oledbuffer[k * 4 + 1 + j * OLED_WIDTH] = + _oledbuffer[k * 4 + 5 + j * OLED_WIDTH]; + _oledbuffer[k * 4 + 2 + j * OLED_WIDTH] = + _oledbuffer[k * 4 + 6 + j * OLED_WIDTH]; + _oledbuffer[k * 4 + 3 + j * OLED_WIDTH] = + _oledbuffer[k * 4 + 7 + j * OLED_WIDTH]; + } + _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 1] = 0; + _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 2] = 0; + _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 3] = 0; + _oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 4] = 0; + } + oledRefresh(); + } } diff --git a/oled.h b/oled.h index afc09829b..a4c9c91ac 100644 --- a/oled.h +++ b/oled.h @@ -20,14 +20,14 @@ #ifndef __OLED_H__ #define __OLED_H__ -#include #include +#include #include "bitmaps.h" #include "fonts.h" -#define OLED_WIDTH 128 -#define OLED_HEIGHT 64 +#define OLED_WIDTH 128 +#define OLED_HEIGHT 64 #define OLED_BUFSIZE (OLED_WIDTH * OLED_HEIGHT / 8) void oledInit(void); @@ -45,9 +45,9 @@ void oledInvertPixel(int x, int y); void oledDrawChar(int x, int y, char c, int zoom); int oledStringWidth(const char *text, int font); -void oledDrawString(int x, int y, const char* text, int font); -void oledDrawStringCenter(int x, int y, const char* text, int font); -void oledDrawStringRight(int x, int y, const char* text, int font); +void oledDrawString(int x, int y, const char *text, int font); +void oledDrawStringCenter(int x, int y, const char *text, int font); +void oledDrawStringRight(int x, int y, const char *text, int font); void oledDrawBitmap(int x, int y, const BITMAP *bmp); void oledInvert(int x1, int y1, int x2, int y2); void oledBox(int x1, int y1, int x2, int y2, bool set); diff --git a/rng.c b/rng.c index 6576d8bb4..6266a82ea 100644 --- a/rng.c +++ b/rng.c @@ -18,21 +18,20 @@ */ #include -#include #include +#include #include "rng.h" #if !EMULATOR -uint32_t random32(void) -{ - static uint32_t last = 0, new = 0; - while (new == last) { - if ((RNG_SR & (RNG_SR_SECS | RNG_SR_CECS | RNG_SR_DRDY)) == RNG_SR_DRDY) { - new = RNG_DR; - } - } - last = new; - return new; +uint32_t random32(void) { + static uint32_t last = 0, new = 0; + while (new == last) { + if ((RNG_SR & (RNG_SR_SECS | RNG_SR_CECS | RNG_SR_DRDY)) == RNG_SR_DRDY) { + new = RNG_DR; + } + } + last = new; + return new; } #endif diff --git a/secbool.h b/secbool.h index 76dfb38dc..65860dd76 100644 --- a/secbool.h +++ b/secbool.h @@ -23,11 +23,11 @@ #include typedef uint32_t secbool; -#define sectrue 0xAAAAAAAAU +#define sectrue 0xAAAAAAAAU #define secfalse 0x00000000U #ifndef __wur -#define __wur __attribute__ ((warn_unused_result)) +#define __wur __attribute__((warn_unused_result)) #endif #endif diff --git a/setup.c b/setup.c index 135301300..62cedf7ef 100644 --- a/setup.c +++ b/setup.c @@ -20,249 +20,264 @@ #include #include #include -#include #include -#include +#include #include +#include -#include "rng.h" #include "layout.h" +#include "rng.h" #include "util.h" uint32_t __stack_chk_guard; static inline void __attribute__((noreturn)) fault_handler(const char *line1) { - layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, "detected.", NULL, "Please unplug", "the device.", NULL); - shutdown(); + layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, "detected.", NULL, + "Please unplug", "the device.", NULL); + shutdown(); } void __attribute__((noreturn)) __stack_chk_fail(void) { - fault_handler("Stack smashing"); -} - -void nmi_handler(void) -{ - // Clock Security System triggered NMI - if ((RCC_CIR & RCC_CIR_CSSF) != 0) { - fault_handler("Clock instability"); - } -} - -void hard_fault_handler(void) { - fault_handler("Hard fault"); + fault_handler("Stack smashing"); } -void mem_manage_handler(void) { - fault_handler("Memory fault"); +void nmi_handler(void) { + // Clock Security System triggered NMI + if ((RCC_CIR & RCC_CIR_CSSF) != 0) { + fault_handler("Clock instability"); + } } -void setup(void) -{ - // set SCB_CCR STKALIGN bit to make sure 8-byte stack alignment on exception entry is in effect. - // This is not strictly necessary for the current TREZOR system. - // This is here to comply with guidance from section 3.3.3 "Binary compatibility with other Cortex processors" - // of the ARM Cortex-M3 Processor Technical Reference Manual. - // According to section 4.4.2 and 4.4.7 of the "STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual", - // STM32F2 series MCUs are r2p0 and always have this bit set on reset already. - SCB_CCR |= SCB_CCR_STKALIGN; - - // setup clock - struct rcc_clock_scale clock = rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_120MHZ]; - rcc_clock_setup_hse_3v3(&clock); - - // enable GPIO clock - A (oled), B(oled), C (buttons) - rcc_periph_clock_enable(RCC_GPIOA); - rcc_periph_clock_enable(RCC_GPIOB); - rcc_periph_clock_enable(RCC_GPIOC); - - // enable SPI clock - rcc_periph_clock_enable(RCC_SPI1); - - // enable RNG - rcc_periph_clock_enable(RCC_RNG); - RNG_CR |= RNG_CR_RNGEN; - // to be extra careful and heed the STM32F205xx Reference manual, Section 20.3.1 - // we don't use the first random number generated after setting the RNGEN bit in setup - random32(); - - // enable CSS (Clock Security System) - RCC_CR |= RCC_CR_CSSON; - - // set GPIO for buttons - gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO2 | GPIO5); - - // set GPIO for OLED display - gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4); - gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0 | GPIO1); - - // enable SPI 1 for OLED display - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO7); - gpio_set_af(GPIOA, GPIO_AF5, GPIO5 | GPIO7); - -// spi_disable_crc(SPI1); - spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); - spi_enable_ss_output(SPI1); -// spi_enable_software_slave_management(SPI1); -// spi_set_nss_high(SPI1); -// spi_clear_mode_fault(SPI1); - spi_enable(SPI1); - - // enable OTG_FS - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO10); - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); - gpio_set_af(GPIOA, GPIO_AF10, GPIO10 | GPIO11 | GPIO12); - - // enable OTG FS clock - rcc_periph_clock_enable(RCC_OTGFS); - // clear USB OTG_FS peripheral dedicated RAM - memset_reg((void *) 0x50020000, (void *) 0x50020500, 0); +void hard_fault_handler(void) { fault_handler("Hard fault"); } + +void mem_manage_handler(void) { fault_handler("Memory fault"); } + +void setup(void) { + // set SCB_CCR STKALIGN bit to make sure 8-byte stack alignment on exception + // entry is in effect. This is not strictly necessary for the current TREZOR + // system. This is here to comply with guidance from section 3.3.3 "Binary + // compatibility with other Cortex processors" of the ARM Cortex-M3 Processor + // Technical Reference Manual. According to section 4.4.2 and 4.4.7 of the + // "STM32F10xxx/20xxx/21xxx/L1xxxx Cortex-M3 programming manual", STM32F2 + // series MCUs are r2p0 and always have this bit set on reset already. + SCB_CCR |= SCB_CCR_STKALIGN; + + // setup clock + struct rcc_clock_scale clock = rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_120MHZ]; + rcc_clock_setup_hse_3v3(&clock); + + // enable GPIO clock - A (oled), B(oled), C (buttons) + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + + // enable SPI clock + rcc_periph_clock_enable(RCC_SPI1); + + // enable RNG + rcc_periph_clock_enable(RCC_RNG); + RNG_CR |= RNG_CR_RNGEN; + // to be extra careful and heed the STM32F205xx Reference manual, + // Section 20.3.1 we don't use the first random number generated after setting + // the RNGEN bit in setup + random32(); + + // enable CSS (Clock Security System) + RCC_CR |= RCC_CR_CSSON; + + // set GPIO for buttons + gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO2 | GPIO5); + + // set GPIO for OLED display + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4); + gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0 | GPIO1); + + // enable SPI 1 for OLED display + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO7); + gpio_set_af(GPIOA, GPIO_AF5, GPIO5 | GPIO7); + + // spi_disable_crc(SPI1); + spi_init_master( + SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); + spi_enable_ss_output(SPI1); + // spi_enable_software_slave_management(SPI1); + // spi_set_nss_high(SPI1); + // spi_clear_mode_fault(SPI1); + spi_enable(SPI1); + + // enable OTG_FS + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO10); + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO10 | GPIO11 | GPIO12); + + // enable OTG FS clock + rcc_periph_clock_enable(RCC_OTGFS); + // clear USB OTG_FS peripheral dedicated RAM + memset_reg((void *)0x50020000, (void *)0x50020500, 0); } -void setupApp(void) -{ - // for completeness, disable RNG peripheral interrupts for old bootloaders that had - // enabled them in RNG control register (the RNG interrupt was never enabled in the NVIC) - RNG_CR &= ~RNG_CR_IE; - // the static variables in random32 are separate between the bootloader and firmware. - // therefore, they need to be initialized here so that we can be sure to avoid dupes. - // this is to try to comply with STM32F205xx Reference manual - Section 20.3.1: - // "Each subsequent generated random number has to be compared with the previously generated - // number. The test fails if any two compared numbers are equal (continuous random number generator test)." - random32(); - - // enable CSS (Clock Security System) - RCC_CR |= RCC_CR_CSSON; - - // hotfix for old bootloader - gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO9); - spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); - - gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO10); - gpio_set_af(GPIOA, GPIO_AF10, GPIO10); +void setupApp(void) { + // for completeness, disable RNG peripheral interrupts for old bootloaders + // that had enabled them in RNG control register (the RNG interrupt was never + // enabled in the NVIC) + RNG_CR &= ~RNG_CR_IE; + // the static variables in random32 are separate between the bootloader and + // firmware. therefore, they need to be initialized here so that we can be + // sure to avoid dupes. this is to try to comply with STM32F205xx Reference + // manual - Section 20.3.1: "Each subsequent generated random number has to be + // compared with the previously generated number. The test fails if any two + // compared numbers are equal (continuous random number generator test)." + random32(); + + // enable CSS (Clock Security System) + RCC_CR |= RCC_CR_CSSON; + + // hotfix for old bootloader + gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO9); + spi_init_master( + SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, + SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); + + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO10); + gpio_set_af(GPIOA, GPIO_AF10, GPIO10); } -#define MPU_RASR_SIZE_32B (0x04UL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_1KB (0x09UL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_4KB (0x0BUL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_8KB (0x0CUL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_16KB (0x0DUL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_32KB (0x0EUL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_64KB (0x0FUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_32B (0x04UL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_1KB (0x09UL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_4KB (0x0BUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_8KB (0x0CUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_16KB (0x0DUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_32KB (0x0EUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_64KB (0x0FUL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_128KB (0x10UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_256KB (0x11UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_512KB (0x12UL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_1MB (0x13UL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_1MB (0x13UL << MPU_RASR_SIZE_LSB) #define MPU_RASR_SIZE_512MB (0x1CUL << MPU_RASR_SIZE_LSB) -#define MPU_RASR_SIZE_4GB (0x1FUL << MPU_RASR_SIZE_LSB) +#define MPU_RASR_SIZE_4GB (0x1FUL << MPU_RASR_SIZE_LSB) // http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/BABDJJGF.html -#define MPU_RASR_ATTR_FLASH (MPU_RASR_ATTR_C) -#define MPU_RASR_ATTR_SRAM (MPU_RASR_ATTR_C | MPU_RASR_ATTR_S) +#define MPU_RASR_ATTR_FLASH (MPU_RASR_ATTR_C) +#define MPU_RASR_ATTR_SRAM (MPU_RASR_ATTR_C | MPU_RASR_ATTR_S) #define MPU_RASR_ATTR_PERIPH (MPU_RASR_ATTR_B | MPU_RASR_ATTR_S) -#define FLASH_BASE (0x08000000U) -#define SRAM_BASE (0x20000000U) +#define FLASH_BASE (0x08000000U) +#define SRAM_BASE (0x20000000U) -void mpu_config_off(void) -{ - // Disable MPU - MPU_CTRL = 0; +void mpu_config_off(void) { + // Disable MPU + MPU_CTRL = 0; - __asm__ volatile("dsb"); - __asm__ volatile("isb"); + __asm__ volatile("dsb"); + __asm__ volatile("isb"); } -void mpu_config_bootloader(void) -{ - // Disable MPU - MPU_CTRL = 0; - - // Note: later entries overwrite previous ones - - // Everything (0x00000000 - 0xFFFFFFFF, 4 GiB, read-write) - MPU_RBAR = 0 | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_4GB | MPU_RASR_ATTR_AP_PRW_URW; - - // Flash (0x8007FE0 - 0x08007FFF, 32 B, no-access) - MPU_RBAR = (FLASH_BASE + 0x7FE0) | MPU_RBAR_VALID | (1 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_32B | MPU_RASR_ATTR_AP_PNO_UNO; - - // SRAM (0x20000000 - 0x2001FFFF, read-write, execute never) - MPU_RBAR = SRAM_BASE | MPU_RBAR_VALID | (2 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_SRAM | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - - // Peripherals (0x40000000 - 0x4001FFFF, read-write, execute never) - MPU_RBAR = PERIPH_BASE | MPU_RBAR_VALID | (3 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - // Peripherals (0x40020000 - 0x40023FFF, read-write, execute never) - MPU_RBAR = 0x40020000 | MPU_RBAR_VALID | (4 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_16KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - // Don't enable DMA controller access - // Peripherals (0x50000000 - 0x5007ffff, read-write, execute never) - MPU_RBAR = 0x50000000 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_512KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - - // Enable MPU - MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_HFNMIENA; - - // Enable memory fault handler - SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA; - - __asm__ volatile("dsb"); - __asm__ volatile("isb"); +void mpu_config_bootloader(void) { + // Disable MPU + MPU_CTRL = 0; + + // Note: later entries overwrite previous ones + + // Everything (0x00000000 - 0xFFFFFFFF, 4 GiB, read-write) + MPU_RBAR = 0 | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_4GB | + MPU_RASR_ATTR_AP_PRW_URW; + + // Flash (0x8007FE0 - 0x08007FFF, 32 B, no-access) + MPU_RBAR = + (FLASH_BASE + 0x7FE0) | MPU_RBAR_VALID | (1 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_32B | + MPU_RASR_ATTR_AP_PNO_UNO; + + // SRAM (0x20000000 - 0x2001FFFF, read-write, execute never) + MPU_RBAR = SRAM_BASE | MPU_RBAR_VALID | (2 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_SRAM | MPU_RASR_SIZE_128KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Peripherals (0x40000000 - 0x4001FFFF, read-write, execute never) + MPU_RBAR = PERIPH_BASE | MPU_RBAR_VALID | (3 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_128KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Peripherals (0x40020000 - 0x40023FFF, read-write, execute never) + MPU_RBAR = 0x40020000 | MPU_RBAR_VALID | (4 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_16KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Don't enable DMA controller access + // Peripherals (0x50000000 - 0x5007ffff, read-write, execute never) + MPU_RBAR = 0x50000000 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_512KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Enable MPU + MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_HFNMIENA; + + // Enable memory fault handler + SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA; + + __asm__ volatile("dsb"); + __asm__ volatile("isb"); } // Never use in bootloader! Disables access to PPB (including MPU, NVIC, SCB) -void mpu_config_firmware(void) -{ +void mpu_config_firmware(void) { #if MEMORY_PROTECT - // Disable MPU - MPU_CTRL = 0; - - // Note: later entries overwrite previous ones - - // Flash (0x08000000 - 0x0807FFFF, 1 MiB, read-only) - MPU_RBAR = FLASH_BASE | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_1MB | MPU_RASR_ATTR_AP_PRO_URO; - - // Metadata in Flash is read-write when unlocked - // (0x08008000 - 0x0800FFFF, 32 KiB, read-write, execute never) - MPU_RBAR = (FLASH_BASE + 0x8000) | MPU_RBAR_VALID | (1 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_32KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - - // SRAM (0x20000000 - 0x2001FFFF, read-write, execute never) - MPU_RBAR = SRAM_BASE | MPU_RBAR_VALID | (2 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_SRAM | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - - // Peripherals (0x40000000 - 0x4001FFFF, read-write, execute never) - MPU_RBAR = PERIPH_BASE | MPU_RBAR_VALID | (3 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_128KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - // Peripherals (0x40020000 - 0x40023FFF, read-write, execute never) - MPU_RBAR = 0x40020000 | MPU_RBAR_VALID | (4 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_16KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - // Flash controller is protected - // (0x40023C00 - 0x40023FFF, privileged read-write, user no, execute never) - MPU_RBAR = 0x40023c00 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_1KB | MPU_RASR_ATTR_AP_PRW_UNO | MPU_RASR_ATTR_XN; - // Don't enable DMA controller access - // Peripherals (0x50000000 - 0x5007ffff, read-write, execute never) - MPU_RBAR = 0x50000000 | MPU_RBAR_VALID | (6 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_512KB | MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; - // SYSCFG_* registers are disabled - // (0x40013800 - 0x40013BFF, read-only, execute never) - MPU_RBAR = 0x40013800 | MPU_RBAR_VALID | (7 << MPU_RBAR_REGION_LSB); - MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_1KB | MPU_RASR_ATTR_AP_PRO_URO | MPU_RASR_ATTR_XN; - - // Enable MPU - MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_HFNMIENA; - - // Enable memory fault handler - SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA; - - __asm__ volatile("dsb"); - __asm__ volatile("isb"); - - // Switch to unprivileged software execution to prevent access to MPU - set_mode_unprivileged(); + // Disable MPU + MPU_CTRL = 0; + + // Note: later entries overwrite previous ones + + // Flash (0x08000000 - 0x0807FFFF, 1 MiB, read-only) + MPU_RBAR = FLASH_BASE | MPU_RBAR_VALID | (0 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_1MB | + MPU_RASR_ATTR_AP_PRO_URO; + + // Metadata in Flash is read-write when unlocked + // (0x08008000 - 0x0800FFFF, 32 KiB, read-write, execute never) + MPU_RBAR = + (FLASH_BASE + 0x8000) | MPU_RBAR_VALID | (1 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_FLASH | MPU_RASR_SIZE_32KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // SRAM (0x20000000 - 0x2001FFFF, read-write, execute never) + MPU_RBAR = SRAM_BASE | MPU_RBAR_VALID | (2 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_SRAM | MPU_RASR_SIZE_128KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + + // Peripherals (0x40000000 - 0x4001FFFF, read-write, execute never) + MPU_RBAR = PERIPH_BASE | MPU_RBAR_VALID | (3 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_128KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Peripherals (0x40020000 - 0x40023FFF, read-write, execute never) + MPU_RBAR = 0x40020000 | MPU_RBAR_VALID | (4 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_16KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // Flash controller is protected + // (0x40023C00 - 0x40023FFF, privileged read-write, user no, execute never) + MPU_RBAR = 0x40023c00 | MPU_RBAR_VALID | (5 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_1KB | + MPU_RASR_ATTR_AP_PRW_UNO | MPU_RASR_ATTR_XN; + // Don't enable DMA controller access + // Peripherals (0x50000000 - 0x5007ffff, read-write, execute never) + MPU_RBAR = 0x50000000 | MPU_RBAR_VALID | (6 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_512KB | + MPU_RASR_ATTR_AP_PRW_URW | MPU_RASR_ATTR_XN; + // SYSCFG_* registers are disabled + // (0x40013800 - 0x40013BFF, read-only, execute never) + MPU_RBAR = 0x40013800 | MPU_RBAR_VALID | (7 << MPU_RBAR_REGION_LSB); + MPU_RASR = MPU_RASR_ENABLE | MPU_RASR_ATTR_PERIPH | MPU_RASR_SIZE_1KB | + MPU_RASR_ATTR_AP_PRO_URO | MPU_RASR_ATTR_XN; + + // Enable MPU + MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_HFNMIENA; + + // Enable memory fault handler + SCB_SHCSR |= SCB_SHCSR_MEMFAULTENA; + + __asm__ volatile("dsb"); + __asm__ volatile("isb"); + + // Switch to unprivileged software execution to prevent access to MPU + set_mode_unprivileged(); #endif } diff --git a/shell.nix b/shell.nix index 1404234d5..87f5ce344 100644 --- a/shell.nix +++ b/shell.nix @@ -5,5 +5,5 @@ let in stdenv.mkDerivation { name = "trezor-mcu-dev"; - buildInputs = [ myPython protobuf gnumake gcc gcc-arm-embedded pkgconfig SDL2 SDL2_image ]; + buildInputs = [ myPython protobuf gnumake gcc gcc-arm-embedded pkgconfig SDL2 SDL2_image clang-tools ]; } diff --git a/supervise.c b/supervise.c index 5a86db884..445881405 100644 --- a/supervise.c +++ b/supervise.c @@ -17,77 +17,75 @@ * along with this library. If not, see . */ +#include "supervise.h" #include #include -#include "supervise.h" #include "memory.h" #if !EMULATOR static void svhandler_flash_unlock(void) { - flash_wait_for_last_operation(); - flash_clear_status_flags(); - flash_unlock(); + flash_wait_for_last_operation(); + flash_clear_status_flags(); + flash_unlock(); } static void svhandler_flash_program(uint32_t psize) { - /* Wait for any write operation to complete. */ - flash_wait_for_last_operation(); - /* check program size argument */ - if (psize != FLASH_CR_PROGRAM_X8 - && psize != FLASH_CR_PROGRAM_X16 - && psize != FLASH_CR_PROGRAM_X32 - && psize != FLASH_CR_PROGRAM_X64) - return; - FLASH_CR = (FLASH_CR & ~(FLASH_CR_PROGRAM_MASK << FLASH_CR_PROGRAM_SHIFT)) - | (psize << FLASH_CR_PROGRAM_SHIFT); - FLASH_CR |= FLASH_CR_PG; + /* Wait for any write operation to complete. */ + flash_wait_for_last_operation(); + /* check program size argument */ + if (psize != FLASH_CR_PROGRAM_X8 && psize != FLASH_CR_PROGRAM_X16 && + psize != FLASH_CR_PROGRAM_X32 && psize != FLASH_CR_PROGRAM_X64) + return; + FLASH_CR = (FLASH_CR & ~(FLASH_CR_PROGRAM_MASK << FLASH_CR_PROGRAM_SHIFT)) | + (psize << FLASH_CR_PROGRAM_SHIFT); + FLASH_CR |= FLASH_CR_PG; } static void svhandler_flash_erase_sector(uint16_t sector) { - /* we only allow erasing storage sectors 2 and 3. */ - if (sector < FLASH_STORAGE_SECTOR_FIRST || - sector > FLASH_STORAGE_SECTOR_LAST) { - return; - } - flash_erase_sector(sector, FLASH_CR_PROGRAM_X32); + /* we only allow erasing storage sectors 2 and 3. */ + if (sector < FLASH_STORAGE_SECTOR_FIRST || + sector > FLASH_STORAGE_SECTOR_LAST) { + return; + } + flash_erase_sector(sector, FLASH_CR_PROGRAM_X32); } static uint32_t svhandler_flash_lock(void) { - /* Wait for any write operation to complete. */ - flash_wait_for_last_operation(); - /* Disable writes to flash. */ - FLASH_CR &= ~FLASH_CR_PG; - /* lock flash register */ - FLASH_CR |= FLASH_CR_LOCK; - /* return flash status register */ - return FLASH_SR; + /* Wait for any write operation to complete. */ + flash_wait_for_last_operation(); + /* Disable writes to flash. */ + FLASH_CR &= ~FLASH_CR_PG; + /* lock flash register */ + FLASH_CR |= FLASH_CR_LOCK; + /* return flash status register */ + return FLASH_SR; } extern volatile uint32_t system_millis; void svc_handler_main(uint32_t *stack) { - uint8_t svc_number = ((uint8_t*) stack[6])[-2]; - switch (svc_number) { - case SVC_FLASH_UNLOCK: - svhandler_flash_unlock(); - break; - case SVC_FLASH_PROGRAM: - svhandler_flash_program(stack[0]); - break; - case SVC_FLASH_ERASE: - svhandler_flash_erase_sector(stack[0]); - break; - case SVC_FLASH_LOCK: - stack[0] = svhandler_flash_lock(); - break; - case SVC_TIMER_MS: - stack[0] = system_millis; - break; - default: - stack[0] = 0xffffffff; - break; - } + uint8_t svc_number = ((uint8_t *)stack[6])[-2]; + switch (svc_number) { + case SVC_FLASH_UNLOCK: + svhandler_flash_unlock(); + break; + case SVC_FLASH_PROGRAM: + svhandler_flash_program(stack[0]); + break; + case SVC_FLASH_ERASE: + svhandler_flash_erase_sector(stack[0]); + break; + case SVC_FLASH_LOCK: + stack[0] = svhandler_flash_lock(); + break; + case SVC_TIMER_MS: + stack[0] = system_millis; + break; + default: + stack[0] = 0xffffffff; + break; + } } #endif diff --git a/supervise.h b/supervise.h index 1457568a6..6b7bed1c8 100644 --- a/supervise.h +++ b/supervise.h @@ -22,18 +22,20 @@ #if !EMULATOR -#define SVC_FLASH_UNLOCK 0 -#define SVC_FLASH_ERASE 1 +#include + +#define SVC_FLASH_UNLOCK 0 +#define SVC_FLASH_ERASE 1 #define SVC_FLASH_PROGRAM 2 -#define SVC_FLASH_LOCK 3 -#define SVC_TIMER_MS 4 +#define SVC_FLASH_LOCK 3 +#define SVC_TIMER_MS 4 /* Unlocks flash. This function needs to be called before programming * or erasing. Multiple calls of flash_program and flash_erase can * follow and should be completed with flash_lock(). */ inline void svc_flash_unlock(void) { - __asm__ __volatile__ ("svc %0" :: "i" (SVC_FLASH_UNLOCK) : "memory"); + __asm__ __volatile__("svc %0" ::"i"(SVC_FLASH_UNLOCK) : "memory"); } /* Enable flash write operations. @@ -41,32 +43,32 @@ inline void svc_flash_unlock(void) { * should be one of the FLASH_CR_PROGRAM_X.. constants */ inline void svc_flash_program(uint32_t program_size) { - register uint32_t r0 __asm__("r0") = program_size; - __asm__ __volatile__ ("svc %0" :: "i" (SVC_FLASH_PROGRAM), "r" (r0) : "memory"); + register uint32_t r0 __asm__("r0") = program_size; + __asm__ __volatile__("svc %0" ::"i"(SVC_FLASH_PROGRAM), "r"(r0) : "memory"); } /* Erase a flash sector. - * @param sector sector number 0..11 + * @param sector sector number 0..11 * (this only allows erasing meta sectors 2 and 3 though). */ inline void svc_flash_erase_sector(uint8_t sector) { - register uint32_t r0 __asm__("r0") = sector; - __asm__ __volatile__ ("svc %0" :: "i" (SVC_FLASH_ERASE), "r" (r0) : "memory"); + register uint32_t r0 __asm__("r0") = sector; + __asm__ __volatile__("svc %0" ::"i"(SVC_FLASH_ERASE), "r"(r0) : "memory"); } /* Lock flash after programming or erasing. * @return flash status register (FLASH_SR) */ inline uint32_t svc_flash_lock(void) { - register uint32_t r0 __asm__("r0"); - __asm__ __volatile__ ("svc %1" : "=r" (r0) : "i" (SVC_FLASH_LOCK) : "memory"); - return r0; + register uint32_t r0 __asm__("r0"); + __asm__ __volatile__("svc %1" : "=r"(r0) : "i"(SVC_FLASH_LOCK) : "memory"); + return r0; } inline uint32_t svc_timer_ms(void) { - register uint32_t r0 __asm__("r0"); - __asm__ __volatile__ ("svc %1" : "=r" (r0) : "i" (SVC_TIMER_MS) : "memory"); - return r0; + register uint32_t r0 __asm__("r0"); + __asm__ __volatile__("svc %1" : "=r"(r0) : "i"(SVC_TIMER_MS) : "memory"); + return r0; } #else diff --git a/timer.c b/timer.c index 93f0eb0ca..0fea37d82 100644 --- a/timer.c +++ b/timer.c @@ -17,12 +17,11 @@ * along with this library. If not, see . */ - #include "timer.h" -#include #include #include +#include /* 1 tick = 1 ms */ extern volatile uint32_t system_millis; @@ -31,32 +30,30 @@ extern volatile uint32_t system_millis; * Initialise the Cortex-M3 SysTick timer */ void timer_init(void) { - system_millis = 0; - - /* - * MCU clock (120 MHz) as source - * - * (120 MHz / 8) = 15 clock pulses - * - */ - systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); - STK_CVR = 0; - - /* - * 1 tick = 1 ms @ 120 MHz - * - * (15 clock pulses * 1000 ms) = 15000 clock pulses - * - * Send an interrupt every (N - 1) clock pulses - */ - systick_set_reload(14999); - - /* SysTick as interrupt */ - systick_interrupt_enable(); - - systick_counter_enable(); + system_millis = 0; + + /* + * MCU clock (120 MHz) as source + * + * (120 MHz / 8) = 15 clock pulses + * + */ + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); + STK_CVR = 0; + + /* + * 1 tick = 1 ms @ 120 MHz + * + * (15 clock pulses * 1000 ms) = 15000 clock pulses + * + * Send an interrupt every (N - 1) clock pulses + */ + systick_set_reload(14999); + + /* SysTick as interrupt */ + systick_interrupt_enable(); + + systick_counter_enable(); } -void sys_tick_handler(void) { - system_millis++; -} +void sys_tick_handler(void) { system_millis++; } diff --git a/usb21_standard.c b/usb21_standard.c index d6aab2bfd..9538285aa 100644 --- a/usb21_standard.c +++ b/usb21_standard.c @@ -16,82 +16,79 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "usb21_standard.h" #include #include #include "util.h" -#include "usb21_standard.h" static uint16_t build_bos_descriptor(const struct usb_bos_descriptor *bos, - uint8_t *buf, uint16_t len) -{ - uint8_t *tmpbuf = buf; - uint16_t count, total = 0, totallen = 0; - uint16_t i; - - memcpy(buf, bos, count = MIN(len, bos->bLength)); - buf += count; - len -= count; - total += count; - totallen += bos->bLength; - - /* For each device capability */ - for (i = 0; i < bos->bNumDeviceCaps; i++) { - /* Copy device capability descriptor. */ - const struct usb_device_capability_descriptor *cap = - bos->capabilities[i]; - - memcpy(buf, cap, count = MIN(len, cap->bLength)); - buf += count; - len -= count; - total += count; - totallen += cap->bLength; - } - - /* Fill in wTotalLength. */ - *(uint16_t *)(tmpbuf + 2) = totallen; - - return total; + uint8_t *buf, uint16_t len) { + uint8_t *tmpbuf = buf; + uint16_t count, total = 0, totallen = 0; + uint16_t i; + + memcpy(buf, bos, count = MIN(len, bos->bLength)); + buf += count; + len -= count; + total += count; + totallen += bos->bLength; + + /* For each device capability */ + for (i = 0; i < bos->bNumDeviceCaps; i++) { + /* Copy device capability descriptor. */ + const struct usb_device_capability_descriptor *cap = bos->capabilities[i]; + + memcpy(buf, cap, count = MIN(len, cap->bLength)); + buf += count; + len -= count; + total += count; + totallen += cap->bLength; + } + + /* Fill in wTotalLength. */ + *(uint16_t *)(tmpbuf + 2) = totallen; + + return total; } -static const struct usb_bos_descriptor* usb21_bos; - -static int usb21_standard_get_descriptor(usbd_device* usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { - (void)complete; - (void)usbd_dev; - - wait_random(); - - if (req->bRequest == USB_REQ_GET_DESCRIPTOR) { - int descr_type = req->wValue >> 8; - if (descr_type == USB_DT_BOS) { - if (!usb21_bos) { - return USBD_REQ_NOTSUPP; - } - *len = MIN_8bits(*len, build_bos_descriptor(usb21_bos, *buf, *len)); - return USBD_REQ_HANDLED; - } - } - - return USBD_REQ_NEXT_CALLBACK; +static const struct usb_bos_descriptor *usb21_bos; + +static int usb21_standard_get_descriptor( + usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, usbd_control_complete_callback *complete) { + (void)complete; + (void)usbd_dev; + + wait_random(); + + if (req->bRequest == USB_REQ_GET_DESCRIPTOR) { + int descr_type = req->wValue >> 8; + if (descr_type == USB_DT_BOS) { + if (!usb21_bos) { + return USBD_REQ_NOTSUPP; + } + *len = MIN_8bits(*len, build_bos_descriptor(usb21_bos, *buf, *len)); + return USBD_REQ_HANDLED; + } + } + + return USBD_REQ_NEXT_CALLBACK; } -static void usb21_set_config(usbd_device* usbd_dev, uint16_t wValue) { - (void)wValue; +static void usb21_set_config(usbd_device *usbd_dev, uint16_t wValue) { + (void)wValue; - usbd_register_control_callback( - usbd_dev, - USB_REQ_TYPE_IN | USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE, - USB_REQ_TYPE_DIRECTION | USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - &usb21_standard_get_descriptor); + usbd_register_control_callback( + usbd_dev, USB_REQ_TYPE_IN | USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_DEVICE, + USB_REQ_TYPE_DIRECTION | USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + &usb21_standard_get_descriptor); } -void usb21_setup(usbd_device* usbd_dev, const struct usb_bos_descriptor* binary_object_store) { - usb21_bos = binary_object_store; +void usb21_setup(usbd_device *usbd_dev, + const struct usb_bos_descriptor *binary_object_store) { + usb21_bos = binary_object_store; - /* Register the control request handler _before_ the config is set */ - usb21_set_config(usbd_dev, 0x0000); - usbd_register_set_config_callback(usbd_dev, usb21_set_config); + /* Register the control request handler _before_ the config is set */ + usb21_set_config(usbd_dev, 0x0000); + usbd_register_set_config_callback(usbd_dev, usb21_set_config); } diff --git a/usb21_standard.h b/usb21_standard.h index e5358bcd5..41637db91 100644 --- a/usb21_standard.h +++ b/usb21_standard.h @@ -22,29 +22,30 @@ #include /* USB 3.1 Descriptor Types - Table 9-6 */ -#define USB_DT_BOS 15 -#define USB_DT_DEVICE_CAPABILITY 16 +#define USB_DT_BOS 15 +#define USB_DT_DEVICE_CAPABILITY 16 struct usb_device_capability_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; } __attribute__((packed)); struct usb_bos_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t wTotalLength; - uint8_t bNumDeviceCaps; - /* Descriptor ends here. The following are used internally: */ - const struct usb_device_capability_descriptor **capabilities; + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; + /* Descriptor ends here. The following are used internally: */ + const struct usb_device_capability_descriptor** capabilities; } __attribute__((packed)); #define USB_DT_BOS_SIZE 5 /* USB Device Capability Types - USB 3.1 Table 9-14 */ -#define USB_DC_PLATFORM 5 +#define USB_DC_PLATFORM 5 -extern void usb21_setup(usbd_device* usbd_dev, const struct usb_bos_descriptor* binary_object_store); +extern void usb21_setup(usbd_device* usbd_dev, + const struct usb_bos_descriptor* binary_object_store); #endif diff --git a/usb_private.h b/usb_private.h index bb553be32..862e77067 100644 --- a/usb_private.h +++ b/usb_private.h @@ -1,3 +1,4 @@ +// clang-format off /** @defgroup usb_private_defines USB Private Structures @brief Defined Constants and Types for the USB Private Structures diff --git a/usb_standard.c b/usb_standard.c index 3576c289c..8ac179236 100644 --- a/usb_standard.c +++ b/usb_standard.c @@ -1,3 +1,4 @@ +// clang-format off /** @defgroup usb_standard_file Generic USB Standard Request Interface @ingroup USB diff --git a/util.c b/util.c index 7d32092a8..5a96381de 100644 --- a/util.c +++ b/util.c @@ -17,71 +17,66 @@ * along with this library. If not, see . */ -#include "rng.h" #include "util.h" +#include "rng.h" -inline void delay(uint32_t wait) -{ - while (--wait > 0) __asm__("nop"); +inline void delay(uint32_t wait) { + while (--wait > 0) __asm__("nop"); } -void wait_random(void) -{ - int wait = random32() & 0xff; - volatile int i = 0; - volatile int j = wait; - while (i < wait) { - if (i + j != wait) { - shutdown(); - } - ++i; - --j; - } - // Double-check loop completion. - if (i != wait || j != 0) { - shutdown(); +void wait_random(void) { + int wait = random32() & 0xff; + volatile int i = 0; + volatile int j = wait; + while (i < wait) { + if (i + j != wait) { + shutdown(); } + ++i; + --j; + } + // Double-check loop completion. + if (i != wait || j != 0) { + shutdown(); + } } static const char *hexdigits = "0123456789ABCDEF"; -void uint32hex(uint32_t num, char *str) -{ - for (uint32_t i = 0; i < 8; i++) { - str[i] = hexdigits[(num >> (28 - i * 4)) & 0xF]; - } +void uint32hex(uint32_t num, char *str) { + for (uint32_t i = 0; i < 8; i++) { + str[i] = hexdigits[(num >> (28 - i * 4)) & 0xF]; + } } // converts data to hexa -void data2hex(const void *data, uint32_t len, char *str) -{ - const uint8_t *cdata = (uint8_t *)data; - for (uint32_t i = 0; i < len; i++) { - str[i * 2 ] = hexdigits[(cdata[i] >> 4) & 0xF]; - str[i * 2 + 1] = hexdigits[cdata[i] & 0xF]; - } - str[len * 2] = 0; +void data2hex(const void *data, uint32_t len, char *str) { + const uint8_t *cdata = (uint8_t *)data; + for (uint32_t i = 0; i < len; i++) { + str[i * 2] = hexdigits[(cdata[i] >> 4) & 0xF]; + str[i * 2 + 1] = hexdigits[cdata[i] & 0xF]; + } + str[len * 2] = 0; } -uint32_t readprotobufint(const uint8_t **ptr) -{ - uint32_t result = (**ptr & 0x7F); - if (**ptr & 0x80) { - (*ptr)++; - result += (**ptr & 0x7F) * 128; - if (**ptr & 0x80) { - (*ptr)++; - result += (**ptr & 0x7F) * 128 * 128; - if (**ptr & 0x80) { - (*ptr)++; - result += (**ptr & 0x7F) * 128 * 128 * 128; - if (**ptr & 0x80) { - (*ptr)++; - result += (**ptr & 0x7F) * 128 * 128 * 128 * 128; - } - } - } - } - (*ptr)++; - return result; +uint32_t readprotobufint(const uint8_t **ptr) { + uint32_t result = (**ptr & 0x7F); + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128; + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128 * 128; + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128 * 128 * 128; + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128 * 128 * 128 * 128; + } + } + } + } + (*ptr)++; + return result; } diff --git a/util.h b/util.h index c96a79b6e..75db5a866 100644 --- a/util.h +++ b/util.h @@ -20,9 +20,9 @@ #ifndef __UTIL_H_ #define __UTIL_H_ -#include -#include #include +#include +#include #if !EMULATOR #include @@ -31,9 +31,24 @@ #endif // Statement expressions make these macros side-effect safe -#define MIN_8bits(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? (_a & 0xFF) : (_b & 0xFF); }) -#define MIN(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a < _b ? _a : _b; }) -#define MAX(a, b) ({ typeof(a) _a = (a); typeof(b) _b = (b); _a > _b ? _a : _b; }) +#define MIN_8bits(a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? (_a & 0xFF) : (_b & 0xFF); \ + }) +#define MIN(a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) +#define MAX(a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) void delay(uint32_t wait); @@ -59,47 +74,44 @@ extern uint8_t _stack[]; // defined in startup.s extern void memset_reg(void *start, void *stop, uint32_t val); -#define FW_SIGNED 0x5A3CA5C3 -#define FW_UNTRUSTED 0x00000000 - -static inline void __attribute__((noreturn)) jump_to_firmware(const vector_table_t *vector_table, int trust) -{ - if (FW_SIGNED == trust) { // trusted signed firmware - SCB_VTOR = (uint32_t)vector_table; // * relocate vector table - // Set stack pointer - __asm__ volatile("msr msp, %0" :: "r" (vector_table->initial_sp_value)); - } else { // untrusted firmware - timer_init(); - mpu_config_firmware(); // * configure MPU for the firmware - __asm__ volatile("msr msp, %0" :: "r" (_stack)); - } - - // Jump to address - vector_table->reset(); - - // Prevent compiler from generating stack protector code (which causes CPU fault because the stack is moved) - for (;;); +#define FW_SIGNED 0x5A3CA5C3 +#define FW_UNTRUSTED 0x00000000 + +static inline void __attribute__((noreturn)) +jump_to_firmware(const vector_table_t *vector_table, int trust) { + if (FW_SIGNED == trust) { // trusted signed firmware + SCB_VTOR = (uint32_t)vector_table; // * relocate vector table + // Set stack pointer + __asm__ volatile("msr msp, %0" ::"r"(vector_table->initial_sp_value)); + } else { // untrusted firmware + timer_init(); + mpu_config_firmware(); // * configure MPU for the firmware + __asm__ volatile("msr msp, %0" ::"r"(_stack)); + } + + // Jump to address + vector_table->reset(); + + // Prevent compiler from generating stack protector code (which causes CPU + // fault because the stack is moved) + for (;;) + ; } -static inline void set_mode_unprivileged(void) -{ - // http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/CHDBIBGJ.html - __asm__ volatile("msr control, %0" :: "r" (0x1)); +static inline void set_mode_unprivileged(void) { + // http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/CHDBIBGJ.html + __asm__ volatile("msr control, %0" ::"r"(0x1)); } -static inline bool is_mode_unprivileged(void) -{ - uint32_t r0; - __asm__ volatile("mrs %0, control" : "=r" (r0)); - return r0 & 1; +static inline bool is_mode_unprivileged(void) { + uint32_t r0; + __asm__ volatile("mrs %0, control" : "=r"(r0)); + return r0 & 1; } #else /* EMULATOR */ -static inline bool is_mode_unprivileged(void) -{ - return true; -} +static inline bool is_mode_unprivileged(void) { return true; } #endif #endif diff --git a/webusb.c b/webusb.c index 21c6d3b5e..24ca5ee19 100644 --- a/webusb.c +++ b/webusb.c @@ -18,90 +18,87 @@ #include +#include "usb21_standard.h" #include "util.h" #include "webusb.h" -#include "usb21_standard.h" -const struct webusb_platform_descriptor webusb_platform_capability_descriptor = { - .bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_DC_PLATFORM, - .bReserved = 0, - .platformCapabilityUUID = WEBUSB_UUID, - .bcdVersion = 0x0100, - .bVendorCode = WEBUSB_VENDOR_CODE, - .iLandingPage = 1 -}; +const struct webusb_platform_descriptor webusb_platform_capability_descriptor = + {.bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DC_PLATFORM, + .bReserved = 0, + .platformCapabilityUUID = WEBUSB_UUID, + .bcdVersion = 0x0100, + .bVendorCode = WEBUSB_VENDOR_CODE, + .iLandingPage = 1}; -const struct webusb_platform_descriptor webusb_platform_capability_descriptor_no_landing_page = { - .bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, - .bDescriptorType = USB_DT_DEVICE_CAPABILITY, - .bDevCapabilityType = USB_DC_PLATFORM, - .bReserved = 0, - .platformCapabilityUUID = WEBUSB_UUID, - .bcdVersion = 0x0100, - .bVendorCode = WEBUSB_VENDOR_CODE, - .iLandingPage = 0 -}; +const struct webusb_platform_descriptor + webusb_platform_capability_descriptor_no_landing_page = { + .bLength = WEBUSB_PLATFORM_DESCRIPTOR_SIZE, + .bDescriptorType = USB_DT_DEVICE_CAPABILITY, + .bDevCapabilityType = USB_DC_PLATFORM, + .bReserved = 0, + .platformCapabilityUUID = WEBUSB_UUID, + .bcdVersion = 0x0100, + .bVendorCode = WEBUSB_VENDOR_CODE, + .iLandingPage = 0}; static const char* webusb_https_url; -static int webusb_control_vendor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { - (void)complete; - (void)usbd_dev; +static int webusb_control_vendor_request( + usbd_device* usbd_dev, struct usb_setup_data* req, uint8_t** buf, + uint16_t* len, usbd_control_complete_callback* complete) { + (void)complete; + (void)usbd_dev; - wait_random(); + wait_random(); - if (req->bRequest != WEBUSB_VENDOR_CODE) { - return USBD_REQ_NEXT_CALLBACK; - } + if (req->bRequest != WEBUSB_VENDOR_CODE) { + return USBD_REQ_NEXT_CALLBACK; + } - int status = USBD_REQ_NOTSUPP; - switch (req->wIndex) { - case WEBUSB_REQ_GET_URL: { - struct webusb_url_descriptor* url = (struct webusb_url_descriptor*)(*buf); - uint16_t index = req->wValue; - if (index == 0) { - return USBD_REQ_NOTSUPP; - } + int status = USBD_REQ_NOTSUPP; + switch (req->wIndex) { + case WEBUSB_REQ_GET_URL: { + struct webusb_url_descriptor* url = (struct webusb_url_descriptor*)(*buf); + uint16_t index = req->wValue; + if (index == 0) { + return USBD_REQ_NOTSUPP; + } - if (index == 1) { - size_t url_len = strlen(webusb_https_url); - url->bLength = WEBUSB_DT_URL_DESCRIPTOR_SIZE + url_len; - url->bDescriptorType = WEBUSB_DT_URL; - url->bScheme = WEBUSB_URL_SCHEME_HTTPS; - memcpy(&url->URL, webusb_https_url, url_len); - *len = MIN_8bits(*len, url->bLength); - status = USBD_REQ_HANDLED; - } else { - // TODO: stall instead? - status = USBD_REQ_NOTSUPP; - } - break; - } - default: { - status = USBD_REQ_NOTSUPP; - break; - } - } + if (index == 1) { + size_t url_len = strlen(webusb_https_url); + url->bLength = WEBUSB_DT_URL_DESCRIPTOR_SIZE + url_len; + url->bDescriptorType = WEBUSB_DT_URL; + url->bScheme = WEBUSB_URL_SCHEME_HTTPS; + memcpy(&url->URL, webusb_https_url, url_len); + *len = MIN_8bits(*len, url->bLength); + status = USBD_REQ_HANDLED; + } else { + // TODO: stall instead? + status = USBD_REQ_NOTSUPP; + } + break; + } + default: { + status = USBD_REQ_NOTSUPP; + break; + } + } - return status; + return status; } static void webusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { - (void)wValue; - usbd_register_control_callback( - usbd_dev, - USB_REQ_TYPE_VENDOR | USB_REQ_TYPE_DEVICE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - webusb_control_vendor_request); + (void)wValue; + usbd_register_control_callback(usbd_dev, + USB_REQ_TYPE_VENDOR | USB_REQ_TYPE_DEVICE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + webusb_control_vendor_request); } void webusb_setup(usbd_device* usbd_dev, const char* https_url) { - webusb_https_url = https_url; + webusb_https_url = https_url; - usbd_register_set_config_callback(usbd_dev, webusb_set_config); + usbd_register_set_config_callback(usbd_dev, webusb_set_config); } diff --git a/webusb.h b/webusb.h index c7f5dc78f..01feda4e5 100644 --- a/webusb.h +++ b/webusb.h @@ -25,8 +25,10 @@ // Arbitrary #define WEBUSB_VENDOR_CODE 0x01 -extern const struct webusb_platform_descriptor webusb_platform_capability_descriptor; -extern const struct webusb_platform_descriptor webusb_platform_capability_descriptor_no_landing_page; +extern const struct webusb_platform_descriptor + webusb_platform_capability_descriptor; +extern const struct webusb_platform_descriptor + webusb_platform_capability_descriptor_no_landing_page; extern void webusb_setup(usbd_device* usbd_dev, const char* https_url); diff --git a/webusb_defs.h b/webusb_defs.h index 45b5f2478..b8566f12c 100644 --- a/webusb_defs.h +++ b/webusb_defs.h @@ -21,7 +21,7 @@ #include -#define WEBUSB_REQ_GET_URL 0x02 +#define WEBUSB_REQ_GET_URL 0x02 #define WEBUSB_DT_DESCRIPTOR_SET_HEADER 0 #define WEBUSB_DT_CONFIGURATION_SUBSET_HEADER 1 @@ -32,28 +32,33 @@ #define WEBUSB_URL_SCHEME_HTTPS 1 struct webusb_platform_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bDevCapabilityType; - uint8_t bReserved; - uint8_t platformCapabilityUUID[16]; - uint16_t bcdVersion; - uint8_t bVendorCode; - uint8_t iLandingPage; + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDevCapabilityType; + uint8_t bReserved; + uint8_t platformCapabilityUUID[16]; + uint16_t bcdVersion; + uint8_t bVendorCode; + uint8_t iLandingPage; } __attribute__((packed)); -#define WEBUSB_PLATFORM_DESCRIPTOR_SIZE sizeof(struct webusb_platform_descriptor) +#define WEBUSB_PLATFORM_DESCRIPTOR_SIZE \ + sizeof(struct webusb_platform_descriptor) // from https://wicg.github.io/webusb/#webusb-platform-capability-descriptor // see also this (for endianness explanation) // https://github.com/WICG/webusb/issues/115#issuecomment-352206549 -#define WEBUSB_UUID {0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65} +#define WEBUSB_UUID \ + { \ + 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, 0x8B, 0xFD, 0xA0, 0x76, \ + 0x88, 0x15, 0xB6, 0x65 \ + } struct webusb_url_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint8_t bScheme; - char URL[]; + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bScheme; + char URL[]; } __attribute__((packed)); #define WEBUSB_DT_URL_DESCRIPTOR_SIZE 3 diff --git a/winusb.c b/winusb.c index 06321fd3d..ebd807246 100644 --- a/winusb.c +++ b/winusb.c @@ -16,150 +16,143 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "winusb.h" #include #include "util.h" -#include "winusb.h" -static int usb_descriptor_type(uint16_t wValue) { - return wValue >> 8; -} +static int usb_descriptor_type(uint16_t wValue) { return wValue >> 8; } -static int usb_descriptor_index(uint16_t wValue) { - return wValue & 0xFF; -} +static int usb_descriptor_index(uint16_t wValue) { return wValue & 0xFF; } static struct winusb_compatible_id_descriptor winusb_wcid = { - .header = { - .dwLength = sizeof(struct winusb_compatible_id_descriptor_header) + - 1 * sizeof(struct winusb_compatible_id_function_section), - .bcdVersion = WINUSB_BCD_VERSION, - .wIndex = WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR, - .bNumSections = 1, - .reserved = { 0, 0, 0, 0, 0, 0, 0 }, - }, - .functions = { - { - // note - bInterfaceNumber is rewritten in winusb_setup with the correct interface number - .bInterfaceNumber = 0, - .reserved0 = { 1 }, - .compatibleId = "WINUSB", - .subCompatibleId = "", - .reserved1 = { 0, 0, 0, 0, 0, 0} - }, - } -}; + .header = + { + .dwLength = + sizeof(struct winusb_compatible_id_descriptor_header) + + 1 * sizeof(struct winusb_compatible_id_function_section), + .bcdVersion = WINUSB_BCD_VERSION, + .wIndex = WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR, + .bNumSections = 1, + .reserved = {0, 0, 0, 0, 0, 0, 0}, + }, + .functions = { + {// note - bInterfaceNumber is rewritten in winusb_setup with the + // correct interface number + .bInterfaceNumber = 0, + .reserved0 = {1}, + .compatibleId = "WINUSB", + .subCompatibleId = "", + .reserved1 = {0, 0, 0, 0, 0, 0}}, + }}; static const struct usb_string_descriptor winusb_string_descriptor = { - .bLength = 0x12, - .bDescriptorType = USB_DT_STRING, - .wData = WINUSB_EXTRA_STRING -}; + .bLength = 0x12, + .bDescriptorType = USB_DT_STRING, + .wData = WINUSB_EXTRA_STRING}; static const struct winusb_extended_properties_descriptor guid = { - .header = { - .dwLength = sizeof(struct winusb_extended_properties_descriptor_header) - + 1 * sizeof (struct winusb_extended_properties_feature_descriptor), - .bcdVersion = WINUSB_BCD_VERSION, - .wIndex = WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR, - .wNumFeatures = 1, - }, - .features = { - { - .dwLength = sizeof(struct winusb_extended_properties_feature_descriptor), - .dwPropertyDataType = WINUSB_EXTENDED_PROPERTIES_MULTISZ_DATA_TYPE, - .wNameLength = WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_C, - .name = WINUSB_EXTENDED_PROPERTIES_GUID_NAME, - .dwPropertyDataLength = WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_C, - .propertyData = WINUSB_EXTENDED_PROPERTIES_GUID_DATA, - }, - } -}; + .header = + { + .dwLength = + sizeof(struct winusb_extended_properties_descriptor_header) + + 1 * sizeof( + struct winusb_extended_properties_feature_descriptor), + .bcdVersion = WINUSB_BCD_VERSION, + .wIndex = WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR, + .wNumFeatures = 1, + }, + .features = { + { + .dwLength = + sizeof(struct winusb_extended_properties_feature_descriptor), + .dwPropertyDataType = WINUSB_EXTENDED_PROPERTIES_MULTISZ_DATA_TYPE, + .wNameLength = WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_C, + .name = WINUSB_EXTENDED_PROPERTIES_GUID_NAME, + .dwPropertyDataLength = WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_C, + .propertyData = WINUSB_EXTENDED_PROPERTIES_GUID_DATA, + }, + }}; static int winusb_descriptor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { - (void)complete; - (void)usbd_dev; - - wait_random(); - - if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) { - return USBD_REQ_NEXT_CALLBACK; - } - - if (req->bRequest == USB_REQ_GET_DESCRIPTOR && usb_descriptor_type(req->wValue) == USB_DT_STRING) { - if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) { - *buf = (uint8_t*)(&winusb_string_descriptor); - *len = MIN_8bits(*len, winusb_string_descriptor.bLength); - return USBD_REQ_HANDLED; - } - } - return USBD_REQ_NEXT_CALLBACK; + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, + usbd_control_complete_callback *complete) { + (void)complete; + (void)usbd_dev; + + wait_random(); + + if ((req->bmRequestType & USB_REQ_TYPE_TYPE) != USB_REQ_TYPE_STANDARD) { + return USBD_REQ_NEXT_CALLBACK; + } + + if (req->bRequest == USB_REQ_GET_DESCRIPTOR && + usb_descriptor_type(req->wValue) == USB_DT_STRING) { + if (usb_descriptor_index(req->wValue) == WINUSB_EXTRA_STRING_INDEX) { + *buf = (uint8_t *)(&winusb_string_descriptor); + *len = MIN_8bits(*len, winusb_string_descriptor.bLength); + return USBD_REQ_HANDLED; + } + } + return USBD_REQ_NEXT_CALLBACK; } -static int winusb_control_vendor_request(usbd_device *usbd_dev, - struct usb_setup_data *req, - uint8_t **buf, uint16_t *len, - usbd_control_complete_callback* complete) { - (void)complete; - (void)usbd_dev; - - wait_random(); - - if (req->bRequest != WINUSB_MS_VENDOR_CODE) { - return USBD_REQ_NEXT_CALLBACK; - } - - int status = USBD_REQ_NOTSUPP; - if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && - (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { - *buf = (uint8_t*)(&winusb_wcid); - *len = MIN_8bits(*len, winusb_wcid.header.dwLength); - status = USBD_REQ_HANDLED; - - } else if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_INTERFACE) && - (req->wIndex == WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR) && - (usb_descriptor_index(req->wValue) == winusb_wcid.functions[0].bInterfaceNumber)) { - - *buf = (uint8_t*)(&guid); - *len = MIN_8bits(*len, guid.header.dwLength); - status = USBD_REQ_HANDLED; - - } else { - status = USBD_REQ_NOTSUPP; - } - - return status; +static int winusb_control_vendor_request( + usbd_device *usbd_dev, struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, usbd_control_complete_callback *complete) { + (void)complete; + (void)usbd_dev; + + wait_random(); + + if (req->bRequest != WINUSB_MS_VENDOR_CODE) { + return USBD_REQ_NEXT_CALLBACK; + } + + int status = USBD_REQ_NOTSUPP; + if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == USB_REQ_TYPE_DEVICE) && + (req->wIndex == WINUSB_REQ_GET_COMPATIBLE_ID_FEATURE_DESCRIPTOR)) { + *buf = (uint8_t *)(&winusb_wcid); + *len = MIN_8bits(*len, winusb_wcid.header.dwLength); + status = USBD_REQ_HANDLED; + + } else if (((req->bmRequestType & USB_REQ_TYPE_RECIPIENT) == + USB_REQ_TYPE_INTERFACE) && + (req->wIndex == + WINUSB_REQ_GET_EXTENDED_PROPERTIES_OS_FEATURE_DESCRIPTOR) && + (usb_descriptor_index(req->wValue) == + winusb_wcid.functions[0].bInterfaceNumber)) { + *buf = (uint8_t *)(&guid); + *len = MIN_8bits(*len, guid.header.dwLength); + status = USBD_REQ_HANDLED; + + } else { + status = USBD_REQ_NOTSUPP; + } + + return status; } -static void winusb_set_config(usbd_device* usbd_dev, uint16_t wValue) { - (void)wValue; - usbd_register_control_callback( - usbd_dev, - USB_REQ_TYPE_VENDOR, - USB_REQ_TYPE_TYPE, - winusb_control_vendor_request); +static void winusb_set_config(usbd_device *usbd_dev, uint16_t wValue) { + (void)wValue; + usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR, + USB_REQ_TYPE_TYPE, + winusb_control_vendor_request); } -void winusb_setup(usbd_device* usbd_dev, uint8_t interface) { - winusb_wcid.functions[0].bInterfaceNumber = interface; +void winusb_setup(usbd_device *usbd_dev, uint8_t interface) { + winusb_wcid.functions[0].bInterfaceNumber = interface; - usbd_register_set_config_callback(usbd_dev, winusb_set_config); + usbd_register_set_config_callback(usbd_dev, winusb_set_config); - /* Windows probes the compatible ID before setting the configuration, - so also register the callback now */ + /* Windows probes the compatible ID before setting the configuration, + so also register the callback now */ - usbd_register_control_callback( - usbd_dev, - USB_REQ_TYPE_VENDOR, - USB_REQ_TYPE_TYPE, - winusb_control_vendor_request); + usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_VENDOR, + USB_REQ_TYPE_TYPE, + winusb_control_vendor_request); - usbd_register_control_callback( - usbd_dev, - USB_REQ_TYPE_DEVICE, - USB_REQ_TYPE_RECIPIENT, - winusb_descriptor_request); + usbd_register_control_callback(usbd_dev, USB_REQ_TYPE_DEVICE, + USB_REQ_TYPE_RECIPIENT, + winusb_descriptor_request); } - diff --git a/winusb.h b/winusb.h index 16a6df115..071f262fc 100644 --- a/winusb.h +++ b/winusb.h @@ -19,11 +19,13 @@ #ifndef WINUSB_H_INCLUDED #define WINUSB_H_INCLUDED +#include #include "winusb_defs.h" // Arbitrary, but must be equivalent to the last character in extra string #define WINUSB_MS_VENDOR_CODE '!' -#define WINUSB_EXTRA_STRING {'M', 'S', 'F', 'T', '1', '0', '0', WINUSB_MS_VENDOR_CODE} +#define WINUSB_EXTRA_STRING \ + { 'M', 'S', 'F', 'T', '1', '0', '0', WINUSB_MS_VENDOR_CODE } extern void winusb_setup(usbd_device* usbd_dev, uint8_t interface); diff --git a/winusb_defs.h b/winusb_defs.h index 830e6b154..dd779b857 100644 --- a/winusb_defs.h +++ b/winusb_defs.h @@ -30,60 +30,65 @@ // Apparently using DeviceInterfaceGUID does not always work on Windows 7. // DeviceInterfaceGUIDs does seem to work. -#define WINUSB_EXTENDED_PROPERTIES_GUID_NAME u"DeviceInterfaceGUIDs" -#define WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_C sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_NAME) -#define WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_U (sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_NAME) / 2) +#define WINUSB_EXTENDED_PROPERTIES_GUID_NAME u"DeviceInterfaceGUIDs" +#define WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_C \ + sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_NAME) +#define WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_U \ + (sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_NAME) / 2) // extra null is intentional - it's an array of GUIDs with 1 item -#define WINUSB_EXTENDED_PROPERTIES_GUID_DATA u"{0263b512-88cb-4136-9613-5c8e109d8ef5}\x00" -#define WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_C sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_DATA) -#define WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_U (sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_DATA) / 2) -#define WINUSB_EXTENDED_PROPERTIES_MULTISZ_DATA_TYPE 7 +#define WINUSB_EXTENDED_PROPERTIES_GUID_DATA \ + u"{0263b512-88cb-4136-9613-5c8e109d8ef5}\x00" +#define WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_C \ + sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_DATA) +#define WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_U \ + (sizeof(WINUSB_EXTENDED_PROPERTIES_GUID_DATA) / 2) +#define WINUSB_EXTENDED_PROPERTIES_MULTISZ_DATA_TYPE 7 #define WINUSB_EXTRA_STRING_INDEX 0xee /* Table 2. Function Section */ struct winusb_compatible_id_function_section { - uint8_t bInterfaceNumber; - uint8_t reserved0[1]; - char compatibleId[8]; - char subCompatibleId[8]; - uint8_t reserved1[6]; + uint8_t bInterfaceNumber; + uint8_t reserved0[1]; + char compatibleId[8]; + char subCompatibleId[8]; + uint8_t reserved1[6]; } __attribute__((packed)); /* Table 1. Header Section */ struct winusb_compatible_id_descriptor_header { - uint32_t dwLength; - uint16_t bcdVersion; - uint16_t wIndex; - uint8_t bNumSections; - uint8_t reserved[7]; + uint32_t dwLength; + uint16_t bcdVersion; + uint16_t wIndex; + uint8_t bNumSections; + uint8_t reserved[7]; } __attribute__((packed)); struct winusb_compatible_id_descriptor { - struct winusb_compatible_id_descriptor_header header; - struct winusb_compatible_id_function_section functions[]; + struct winusb_compatible_id_descriptor_header header; + struct winusb_compatible_id_function_section functions[]; } __attribute__((packed)); struct winusb_extended_properties_feature_descriptor { - uint32_t dwLength; - uint32_t dwPropertyDataType; - uint16_t wNameLength; - uint16_t name[WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_U]; - uint32_t dwPropertyDataLength; - uint16_t propertyData[WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_U]; + uint32_t dwLength; + uint32_t dwPropertyDataType; + uint16_t wNameLength; + uint16_t name[WINUSB_EXTENDED_PROPERTIES_GUID_NAME_SIZE_U]; + uint32_t dwPropertyDataLength; + uint16_t propertyData[WINUSB_EXTENDED_PROPERTIES_GUID_DATA_SIZE_U]; } __attribute__((packed)); struct winusb_extended_properties_descriptor_header { - uint32_t dwLength; - uint16_t bcdVersion; - uint16_t wIndex; - uint16_t wNumFeatures; + uint32_t dwLength; + uint16_t bcdVersion; + uint16_t wIndex; + uint16_t wNumFeatures; } __attribute__((packed)); struct winusb_extended_properties_descriptor { - struct winusb_extended_properties_descriptor_header header; - struct winusb_extended_properties_feature_descriptor features[]; + struct winusb_extended_properties_descriptor_header header; + struct winusb_extended_properties_feature_descriptor features[]; } __attribute__((packed)); #endif