diff --git a/.gitmodules b/.gitmodules index 97aef8fa1c..4fe0f75397 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,12 @@ [submodule "trezor-crypto"] - path = trezor-crypto + path = vendor/trezor-crypto url = https://github.com/trezor/trezor-crypto.git [submodule "trezor-common"] - path = trezor-common + path = vendor/trezor-common url = https://github.com/trezor/trezor-common.git [submodule "trezor-qrenc"] - path = trezor-qrenc + path = vendor/trezor-qrenc url = https://github.com/trezor/trezor-qrenc.git +[submodule "libopencm3"] + path = vendor/libopencm3 + url = https://github.com/libopencm3/libopencm3.git diff --git a/.travis.yml b/.travis.yml index 06d332fd7a..8bb34ee731 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,10 @@ install: - sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded -y - sudo apt-get update - sudo apt-get install -y build-essential git gcc-arm-none-eabi - - git clone https://github.com/libopencm3/libopencm3 script: - - make -C libopencm3 - - TOOLCHAIN_DIR=libopencm3 make - - TOOLCHAIN_DIR=../libopencm3 make -C firmware - - TOOLCHAIN_DIR=../libopencm3 make -C bootloader - - TOOLCHAIN_DIR=../libopencm3 make -C demo + - make -C vendor/libopencm3 + - make + - make -C firmware + - make -C bootloader + - make -C demo diff --git a/Dockerfile b/Dockerfile index adb7f76fbf..29efb93146 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,12 +9,3 @@ RUN apt-get update # install build tools and dependencies RUN apt-get install -y build-essential git python gcc-arm-none-eabi - -# clone the source code - -RUN git clone https://github.com/libopencm3/libopencm3 - -# build libopencm3 - -ENV LIBOPENCM3_GITREV 7b29caed1a726b5cef4c269b6a6ef7a1f1dd105c -RUN cd libopencm3 && git checkout $LIBOPENCM3_GITREV && make diff --git a/Makefile.include b/Makefile.include index 8505ffc865..5e48ba04f6 100644 --- a/Makefile.include +++ b/Makefile.include @@ -1,5 +1,5 @@ TOP_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) -TOOLCHAIN_DIR ?= $(TOP_DIR)/../libopencm3 +TOOLCHAIN_DIR ?= $(TOP_DIR)vendor/libopencm3 PREFIX ?= arm-none-eabi- CC = $(PREFIX)gcc @@ -9,9 +9,12 @@ OBJDUMP = $(PREFIX)objdump FLASH = st-flash OPENOCD = openocd -OPTFLAGS = -O3 -g -DNDEBUG +OPTFLAGS ?= -O3 +DBGFLAGS ?= -g -DNDEBUG CFLAGS += $(OPTFLAGS) \ + $(DBGFLAGS) \ + -std=c99 \ -W \ -Wall \ -Wextra \ @@ -42,9 +45,10 @@ CFLAGS += $(OPTFLAGS) \ -DSTM32F2 \ -I$(TOOLCHAIN_DIR)/include \ -I$(TOP_DIR) \ - -I$(TOP_DIR)/gen \ - -I$(TOP_DIR)/trezor-crypto \ - -I$(TOP_DIR)/trezor-qrenc + -I$(TOP_DIR)gen \ + -I$(TOP_DIR)vendor/trezor-crypto \ + -I$(TOP_DIR)vendor/trezor-crypto/ed25519-donna \ + -I$(TOP_DIR)vendor/trezor-qrenc ifdef APPVER CFLAGS += -DAPPVER=$(APPVER) diff --git a/README.md b/README.md index 5c5786626c..c52f67cb26 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,23 @@ http://bitcointrezor.com/ How to build TREZOR firmware? ----------------------------- -1. Install Docker (from docker.com or from your distribution repositories) +1. Install Docker 2. `git clone https://github.com/trezor/trezor-mcu.git` 3. `cd trezor-mcu` 4. `./firmware-docker-build.sh TAG` (where TAG is v1.3.2 for example, if left blank the script builds latest commit) This creates file `output/trezor-TAG.bin` and prints its fingerprint at the last line of the build log. +How to build TREZOR bootloader? +----------------------------- + +1. Install Docker +2. `git clone https://github.com/trezor/trezor-mcu.git` +3. `cd trezor-mcu` +4. `./bootloader-docker-build.sh` + +This creates file `output/bootloader.bin` and prints its fingerprint and size at the last line of the build log. + How to get fingerprint of firmware signed and distributed by SatoshiLabs? ------------------------------------------------------------------------- diff --git a/bootloader-docker-build.sh b/bootloader-docker-build.sh new file mode 100755 index 0000000000..636a095aa0 --- /dev/null +++ b/bootloader-docker-build.sh @@ -0,0 +1,22 @@ +#!/bin/bash +IMAGETAG=trezor-mcu-build +FIRMWARETAG=${1:-master} + +docker build -t $IMAGETAG . +docker run -t -v $(pwd)/output:/output $IMAGETAG /bin/sh -c "\ + git clone https://github.com/trezor/trezor-mcu && \ + cd trezor-mcu && \ + git checkout $FIRMWARETAG && \ + git submodule update --init && \ + make -C vendor/libopencm3 && \ + export OPTFLAGS=-Os + make && \ + make -C bootloader && \ + cp bootloader/bootloader.bin /output/bootloader-$FIRMWARETAG.bin" + +echo "---------------------" +echo "Bootloader fingerprint:" +FILENAME=output/bootloader-$FIRMWARETAG.bin +sha256sum "$FILENAME" +FILESIZE=$(stat -c%s "$FILENAME") +echo "Bootloader size: $FILESIZE bytes (out of 32768 maximum)" diff --git a/bootloader/Makefile b/bootloader/Makefile index 1d87042ca2..85b9aa0480 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -4,12 +4,12 @@ OBJS += bootloader.o OBJS += signatures.o OBJS += usb.o -OBJS += ../trezor-crypto/bignum.o -OBJS += ../trezor-crypto/ecdsa.small.o -OBJS += ../trezor-crypto/hmac.o -OBJS += ../trezor-crypto/ripemd160.o -OBJS += ../trezor-crypto/secp256k1.small.o -OBJS += ../trezor-crypto/sha2.o +OBJS += ../vendor/trezor-crypto/bignum.o +OBJS += ../vendor/trezor-crypto/ecdsa.small.o +OBJS += ../vendor/trezor-crypto/hmac.o +OBJS += ../vendor/trezor-crypto/ripemd160.o +OBJS += ../vendor/trezor-crypto/secp256k1.small.o +OBJS += ../vendor/trezor-crypto/sha2.o CFLAGS += -DUSE_PRECOMPUTED_IV=0 CFLAGS += -DUSE_PRECOMPUTED_CP=0 diff --git a/bootloader/bootloader.c b/bootloader/bootloader.c index 065b83a097..b6b389ad99 100644 --- a/bootloader/bootloader.c +++ b/bootloader/bootloader.c @@ -38,7 +38,23 @@ #error Bootloader cannot be used in app mode #endif -void show_unofficial_warning(void) +void layoutFirmwareHash(uint8_t *hash) +{ + char str[4][17]; + int i; + for (i = 0; i < 4; i++) { + data2hex(hash + i * 8, 8, str[i]); + } + layoutDialog(DIALOG_ICON_QUESTION, "Abort", "Continue", "Compare fingerprints", str[0], str[1], str[2], str[3], NULL, NULL); +} + +void show_halt(void) +{ + layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Unofficial firmware", "aborted.", NULL, "Unplug your TREZOR", "and see our support", "page at mytrezor.com"); + system_halt(); +} + +void show_unofficial_warning(uint8_t *hash) { layoutDialog(DIALOG_ICON_WARNING, "Abort", "I'll take the risk", NULL, "WARNING!", NULL, "Unofficial firmware", "detected.", NULL, NULL); @@ -47,19 +63,29 @@ void show_unofficial_warning(void) buttonUpdate(); } while (!button.YesUp && !button.NoUp); - if (button.YesUp) { - return; // yes button was pressed -> return + if (button.NoUp) { + show_halt(); // no button was pressed -> halt } - layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Unofficial firmware", "aborted.", NULL, "Unplug your TREZOR", "and see our support", "page at mytrezor.com"); - system_halt(); + layoutFirmwareHash(hash); + + do { + delay(100000); + buttonUpdate(); + } while (!button.YesUp && !button.NoUp); + + if (button.NoUp) { + show_halt(); // no button was pressed -> halt + } + + // everything is OK, user pressed 2x Continue -> continue program } void load_app(void) { // jump to app SCB_VTOR = FLASH_APP_START; // & 0xFFFF; - asm volatile("msr msp, %0"::"g" (*(volatile uint32_t *)FLASH_APP_START)); + __asm__ volatile("msr msp, %0"::"g" (*(volatile uint32_t *)FLASH_APP_START)); (*(void (**)())(FLASH_APP_START + 4))(); } @@ -128,8 +154,9 @@ int main(void) oledDrawBitmap(40, 0, &bmp_logo64_empty); oledRefresh(); - if (!signatures_ok()) { - show_unofficial_warning(); + uint8_t hash[32]; + if (!signatures_ok(hash)) { + show_unofficial_warning(hash); } load_app(); diff --git a/bootloader/bootloader.h b/bootloader/bootloader.h index 3546d150d0..3a37d60443 100644 --- a/bootloader/bootloader.h +++ b/bootloader/bootloader.h @@ -22,15 +22,17 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 2 -#define VERSION_PATCH 5 +#define VERSION_PATCH 6 #define STR(X) #X #define VERSTR(X) STR(X) #define VERSION_MAJOR_CHAR "\x01" #define VERSION_MINOR_CHAR "\x02" -#define VERSION_PATCH_CHAR "\x05" +#define VERSION_PATCH_CHAR "\x06" #include "memory.h" +void layoutFirmwareHash(uint8_t *hash); + #endif diff --git a/bootloader/signatures.c b/bootloader/signatures.c index 85e921666f..a4bca418ba 100644 --- a/bootloader/signatures.c +++ b/bootloader/signatures.c @@ -18,10 +18,12 @@ */ #include +#include #include "signatures.h" #include "ecdsa.h" #include "secp256k1.h" +#include "sha2.h" #include "bootloader.h" #define PUBKEYS 5 @@ -36,7 +38,7 @@ static const uint8_t *pubkey[PUBKEYS] = { #define SIGNATURES 3 -int signatures_ok(void) +int signatures_ok(uint8_t *store_hash) { uint32_t codelen = *((uint32_t *)FLASH_META_CODELEN); uint8_t sigindex1, sigindex2, sigindex3; @@ -53,13 +55,19 @@ int signatures_ok(void) if (sigindex1 == sigindex3) return 0; // duplicate use if (sigindex2 == sigindex3) return 0; // duplicate use - if (ecdsa_verify(&secp256k1, pubkey[sigindex1 - 1], (uint8_t *)FLASH_META_SIG1, (uint8_t *)FLASH_APP_START, codelen) != 0) { // failure + uint8_t hash[32]; + sha256_Raw((uint8_t *)FLASH_APP_START, codelen, hash); + if (store_hash) { + memcpy(store_hash, hash, 32); + } + + if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1], (uint8_t *)FLASH_META_SIG1, hash) != 0) { // failure return 0; } - if (ecdsa_verify(&secp256k1, pubkey[sigindex2 - 1], (uint8_t *)FLASH_META_SIG2, (uint8_t *)FLASH_APP_START, codelen) != 0) { // failure + if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex2 - 1], (uint8_t *)FLASH_META_SIG2, hash) != 0) { // failure return 0; } - if (ecdsa_verify(&secp256k1, pubkey[sigindex3 - 1], (uint8_t *)FLASH_META_SIG3, (uint8_t *)FLASH_APP_START, codelen) != 0) { // failture + if (ecdsa_verify_digest(&secp256k1, pubkey[sigindex3 - 1], (uint8_t *)FLASH_META_SIG3, hash) != 0) { // failture return 0; } diff --git a/bootloader/signatures.h b/bootloader/signatures.h index e081be3520..019609346d 100644 --- a/bootloader/signatures.h +++ b/bootloader/signatures.h @@ -20,6 +20,6 @@ #ifndef __SIGNATURES_H__ #define __SIGNATURES_H__ -int signatures_ok(void); +int signatures_ok(uint8_t *store_hash); #endif diff --git a/bootloader/usb.c b/bootloader/usb.c index 94c93fcc2e..0f17df6a0c 100644 --- a/bootloader/usb.c +++ b/bootloader/usb.c @@ -425,15 +425,9 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) if (msg_id != 0x001B) { // ButtonAck message (id 27) return; } - char digest[64]; - sha256_End(&ctx, digest); - char str[4][17]; - strlcpy(str[0], digest, 17); - strlcpy(str[1], digest + 16, 17); - strlcpy(str[2], digest + 32, 17); - strlcpy(str[3], digest + 48, 17); - layoutDialog(DIALOG_ICON_QUESTION, "Abort", "Continue", "Compare fingerprints", str[0], str[1], str[2], str[3], NULL, NULL); - + uint8_t hash[32]; + sha256_Final(&ctx, hash); + layoutFirmwareHash(hash); do { delay(100000); buttonUpdate(); @@ -444,7 +438,7 @@ static void hid_rx_callback(usbd_device *dev, uint8_t ep) layoutProgress("INSTALLING ... Please wait", 1000); uint8_t flags = *((uint8_t *)FLASH_META_FLAGS); // check if to restore old storage area but only if signatures are ok - if ((flags & 0x01) && signatures_ok()) { + if ((flags & 0x01) && signatures_ok(NULL)) { // copy new stuff memcpy(meta_backup, (void *)FLASH_META_START, FLASH_META_DESC_LEN); // replace "TRZR" in header with 0000 when hash not confirmed diff --git a/buttons.h b/buttons.h index 58b7188019..08f0c8e036 100644 --- a/buttons.h +++ b/buttons.h @@ -21,6 +21,7 @@ #define __BUTTONS_H__ #include +#include struct buttonState { volatile bool YesUp; diff --git a/demo/Makefile b/demo/Makefile index 5f16147c7e..57dcb068e3 100644 --- a/demo/Makefile +++ b/demo/Makefile @@ -4,14 +4,14 @@ NAME = demo OBJS += demo.o -OBJS += ../trezor-crypto/bignum.o -OBJS += ../trezor-crypto/bip32.o -OBJS += ../trezor-crypto/ecdsa.o -OBJS += ../trezor-crypto/hmac.o -OBJS += ../trezor-crypto/ripemd160.o -OBJS += ../trezor-crypto/secp256k1.o -OBJS += ../trezor-crypto/sha2.o -OBJS += ../trezor-crypto/bip39.o -OBJS += ../trezor-crypto/pbkdf2.o +OBJS += ../vendor/trezor-crypto/bignum.o +OBJS += ../vendor/trezor-crypto/bip32.o +OBJS += ../vendor/trezor-crypto/ecdsa.o +OBJS += ../vendor/trezor-crypto/hmac.o +OBJS += ../vendor/trezor-crypto/ripemd160.o +OBJS += ../vendor/trezor-crypto/secp256k1.o +OBJS += ../vendor/trezor-crypto/sha2.o +OBJS += ../vendor/trezor-crypto/bip39.o +OBJS += ../vendor/trezor-crypto/pbkdf2.o include ../Makefile.include diff --git a/firmware-docker-build.sh b/firmware-docker-build.sh index f705c8ffbd..cd95ac5b7e 100755 --- a/firmware-docker-build.sh +++ b/firmware-docker-build.sh @@ -8,12 +8,14 @@ docker run -t -v $(pwd)/output:/output $IMAGETAG /bin/sh -c "\ cd trezor-mcu && \ git checkout $FIRMWARETAG && \ git submodule update --init && \ + make -C vendor/libopencm3 && \ make && \ - cd firmware && \ - make && \ - cp trezor.bin /output/trezor-$FIRMWARETAG.bin" + make -C firmware && \ + cp firmware/trezor.bin /output/trezor-$FIRMWARETAG.bin" echo "---------------------" echo "Firmware fingerprint:" - -sha256sum output/trezor-$FIRMWARETAG.bin +FILENAME=output/trezor-$FIRMWARETAG.bin +sha256sum "$FILENAME" +FILESIZE=$(stat -c%s "$FILENAME") +echo "Firmware size: $FILESIZE bytes (out of 491520 maximum)" diff --git a/firmware/Makefile b/firmware/Makefile index a321a77922..36fc371b9d 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -20,25 +20,27 @@ OBJS += crypto.o OBJS += debug.o -OBJS += ../trezor-crypto/bignum.o -OBJS += ../trezor-crypto/ecdsa.o -OBJS += ../trezor-crypto/secp256k1.o -OBJS += ../trezor-crypto/nist256p1.o -OBJS += ../trezor-crypto/hmac.o -OBJS += ../trezor-crypto/bip32.o -OBJS += ../trezor-crypto/bip39.o -OBJS += ../trezor-crypto/pbkdf2.o -OBJS += ../trezor-crypto/base58.o +OBJS += ../vendor/trezor-crypto/bignum.o +OBJS += ../vendor/trezor-crypto/ecdsa.o +OBJS += ../vendor/trezor-crypto/curves.o +OBJS += ../vendor/trezor-crypto/secp256k1.o +OBJS += ../vendor/trezor-crypto/nist256p1.o +OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519.o +OBJS += ../vendor/trezor-crypto/hmac.o +OBJS += ../vendor/trezor-crypto/bip32.o +OBJS += ../vendor/trezor-crypto/bip39.o +OBJS += ../vendor/trezor-crypto/pbkdf2.o +OBJS += ../vendor/trezor-crypto/base58.o -OBJS += ../trezor-crypto/ripemd160.o -OBJS += ../trezor-crypto/sha2.o +OBJS += ../vendor/trezor-crypto/ripemd160.o +OBJS += ../vendor/trezor-crypto/sha2.o -OBJS += ../trezor-crypto/aescrypt.o -OBJS += ../trezor-crypto/aeskey.o -OBJS += ../trezor-crypto/aestab.o -OBJS += ../trezor-crypto/aes_modes.o +OBJS += ../vendor/trezor-crypto/aescrypt.o +OBJS += ../vendor/trezor-crypto/aeskey.o +OBJS += ../vendor/trezor-crypto/aestab.o +OBJS += ../vendor/trezor-crypto/aes_modes.o -OBJS += ../trezor-qrenc/qr_encode.o +OBJS += ../vendor/trezor-qrenc/qr_encode.o # OBJS += protob/pb_common.o OBJS += protob/pb_decode.o @@ -55,3 +57,5 @@ CFLAGS += -DQR_MAX_VERSION=0 CFLAGS += -DDEBUG_LINK=0 CFLAGS += -DDEBUG_LOG=0 CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"' +CFLAGS += -DED25519_CUSTOMRANDOM=1 +CFLAGS += -DED25519_CUSTOMHASH=1 diff --git a/firmware/crypto.c b/firmware/crypto.c index 84d611de68..e49b4b27a0 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -25,8 +25,8 @@ #include "hmac.h" #include "bip32.h" #include "layout.h" +#include "curves.h" #include "secp256k1.h" -#include "nist256p1.h" uint32_t ser_length(uint32_t len, uint8_t *out) { @@ -84,13 +84,23 @@ uint32_t deser_length(const uint8_t *in, uint32_t *out) return 1 + 8; } -int sshMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature) +int sshMessageSign(const 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 ecdsa_sign(&nist256p1, privkey, message, message_len, signature + 1, NULL); + return hdnode_sign(node, message, message_len, signature + 1, NULL); } -int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature) +int gpgMessageSign(const HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature) +{ + // GPG should sign a SHA256 digest of the original message. + if (message_len != 32) { + return 1; + } + signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes + return hdnode_sign_digest(node, message, signature + 1, NULL); +} + +int cryptoMessageSign(const HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature) { SHA256_CTX ctx; sha256_Init(&ctx); @@ -100,10 +110,10 @@ int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t sha256_Update(&ctx, varint, l); sha256_Update(&ctx, message, message_len); uint8_t hash[32]; - sha256_Final(hash, &ctx); + sha256_Final(&ctx, hash); sha256_Raw(hash, 32, hash); uint8_t pby; - int result = ecdsa_sign_digest(&secp256k1, privkey, hash, signature + 1, &pby); + int result = hdnode_sign_digest(node, hash, signature + 1, &pby); if (result == 0) { signature[0] = 27 + pby + 4; } @@ -112,28 +122,9 @@ int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_t *address_raw, const uint8_t *signature) { - bignum256 r, s, e; - curve_point cp, cp2; SHA256_CTX ctx; uint8_t pubkey[65], addr_raw[21], hash[32]; - uint8_t nV = signature[0]; - if (nV < 27 || nV >= 35) { - return 1; - } - bool compressed; - compressed = (nV >= 31); - if (compressed) { - nV -= 4; - } - uint8_t recid = nV - 27; - // read r and s - bn_read_be(signature + 1, &r); - bn_read_be(signature + 33, &s); - // x = r - memcpy(&cp.x, &r, sizeof(bignum256)); - // compute y from x - uncompress_coords(&secp256k1, recid % 2, &cp.x, &cp.y); // calculate hash sha256_Init(&ctx); sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25); @@ -141,43 +132,42 @@ int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_ uint32_t l = ser_length(message_len, varint); sha256_Update(&ctx, varint, l); sha256_Update(&ctx, message, message_len); - sha256_Final(hash, &ctx); + sha256_Final(&ctx, hash); sha256_Raw(hash, 32, hash); - // e = -hash - bn_read_be(hash, &e); - bn_subtract(&secp256k1.order, &e, &e); - // r = r^-1 - bn_inverse(&r, &secp256k1.order); - point_multiply(&secp256k1, &s, &cp, &cp); - scalar_multiply(&secp256k1, &e, &cp2); - point_add(&secp256k1, &cp2, &cp); - point_multiply(&secp256k1, &r, &cp, &cp); - pubkey[0] = 0x04; - bn_write_be(&cp.x, pubkey + 1); - bn_write_be(&cp.y, pubkey + 33); - // check if the address is correct - if (compressed) { - pubkey[0] = 0x02 | (cp.y.val[0] & 0x01); + + uint8_t recid = signature[0] - 27; + if (recid >= 8) { + return 1; } + bool compressed = (recid >= 4); + recid &= 3; + + // check if signature verifies the digest and recover the public key + if (ecdsa_verify_digest_recover(&secp256k1, 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 ecdsa_get_address_raw(pubkey, address_raw[0], addr_raw); if (memcmp(addr_raw, address_raw, 21) != 0) { return 2; } - // check if signature verifies the digest - if (ecdsa_verify_digest(&secp256k1, pubkey, signature + 1, hash) != 0) { - return 3; - } return 0; } 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); - if (cryptoMessageSign(msg, msg_size, privkey, payload + 1 + l + msg_size + 21) != 0) { + 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; @@ -282,7 +272,7 @@ uint8_t *cryptoHDNodePathToPubkey(const HDNodePathType *hdnodepath) { if (!hdnodepath->node.has_public_key || hdnodepath->node.public_key.size != 33) return 0; static HDNode node; - if (hdnode_from_xpub(hdnodepath->node.depth, hdnodepath->node.fingerprint, hdnodepath->node.child_num, hdnodepath->node.chain_code.bytes, hdnodepath->node.public_key.bytes, &node) == 0) { + if (hdnode_from_xpub(hdnodepath->node.depth, hdnodepath->node.fingerprint, hdnodepath->node.child_num, hdnodepath->node.chain_code.bytes, hdnodepath->node.public_key.bytes, SECP256K1_NAME, &node) == 0) { return 0; } layoutProgressUpdate(true); @@ -345,7 +335,7 @@ int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, uint8_t sha256_Update(&ctx, ptr[i]->node.public_key.bytes, 33); } sha256_Update(&ctx, (const uint8_t *)&n, sizeof(uint32_t)); - sha256_Final(hash, &ctx); + sha256_Final(&ctx, hash); layoutProgressUpdate(true); return 1; } @@ -373,6 +363,6 @@ int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash) if (identity->has_path && identity->path[0]) { sha256_Update(&ctx, (const uint8_t *)(identity->path), strlen(identity->path)); } - sha256_Final(hash, &ctx); + sha256_Final(&ctx, hash); return 1; } diff --git a/firmware/crypto.h b/firmware/crypto.h index b9d03fde8a..efde2265dc 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include "types.pb.h" @@ -32,9 +33,11 @@ uint32_t ser_length(uint32_t len, uint8_t *out); uint32_t ser_length_hash(SHA256_CTX *ctx, uint32_t len); -int sshMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature); +int sshMessageSign(const HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); -int cryptoMessageSign(const uint8_t *message, size_t message_len, const uint8_t *privkey, uint8_t *signature); +int gpgMessageSign(const HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); + +int cryptoMessageSign(const HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature); int cryptoMessageVerify(const uint8_t *message, size_t message_len, const uint8_t *address_raw, const uint8_t *signature); diff --git a/firmware/fsm.c b/firmware/fsm.c index 32b25e28a4..930a761810 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -44,8 +44,8 @@ #include "base58.h" #include "bip39.h" #include "ripemd160.h" +#include "curves.h" #include "secp256k1.h" -#include "nist256p1.h" // message methods @@ -93,11 +93,11 @@ const CoinType *fsm_getCoin(const char *name) return coin; } -const HDNode *fsm_getDerivedNode(uint32_t *address_n, size_t address_n_count) +const HDNode *fsm_getDerivedNode(const char *curve, uint32_t *address_n, size_t address_n_count) { static HDNode node; - if (!storage_getRootNode(&node)) { - fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized or passphrase request cancelled"); + if (!storage_getRootNode(&node, curve)) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized or passphrase request cancelled or unsupported curve"); layoutHome(); return 0; } @@ -282,22 +282,29 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) { RESP_INIT(PublicKey); + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + if (!protectPin(true)) { layoutHome(); return; } - const HDNode *node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); + const char *curve = SECP256K1_NAME; + if (msg->has_ecdsa_curve_name) { + curve = msg->ecdsa_curve_name; + } + const HDNode *node = fsm_getDerivedNode(curve, msg->address_n, msg->address_n_count); if (!node) return; - uint8_t public_key[33]; // copy public key to temporary buffer - memcpy(public_key, node->public_key, sizeof(public_key)); - - if (msg->has_ecdsa_curve_name) { - const ecdsa_curve *curve = get_curve_by_name(msg->ecdsa_curve_name); - if (curve) { - // correct public key (since fsm_getDerivedNode uses secp256k1 curve) - ecdsa_get_public_key33(curve, node->private_key, public_key); + if (msg->has_show_display && msg->show_display) { + layoutPublicKey(node->public_key); + if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Show public key cancelled"); + layoutHome(); + return; } } @@ -309,7 +316,7 @@ void fsm_msgGetPublicKey(GetPublicKey *msg) resp->node.has_private_key = false; resp->node.has_public_key = true; resp->node.public_key.size = 33; - memcpy(resp->node.public_key.bytes, public_key, 33); + memcpy(resp->node.public_key.bytes, node->public_key, 33); resp->has_xpub = true; hdnode_serialize_public(node, resp->xpub, sizeof(resp->xpub)); msg_write(MessageType_MessageType_PublicKey, resp); @@ -363,6 +370,11 @@ void fsm_msgResetDevice(ResetDevice *msg) void fsm_msgSignTx(SignTx *msg) { + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + if (msg->inputs_count < 1) { fsm_sendFailure(FailureType_Failure_Other, "Transaction must have at least one input"); layoutHome(); @@ -382,10 +394,10 @@ void fsm_msgSignTx(SignTx *msg) const CoinType *coin = fsm_getCoin(msg->coin_name); if (!coin) return; - const HDNode *node = fsm_getDerivedNode(0, 0); + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, 0, 0); if (!node) return; - signing_init(msg->inputs_count, msg->outputs_count, coin, node); + signing_init(msg->inputs_count, msg->outputs_count, coin, node, msg->version, msg->lock_time); } void fsm_msgCancel(Cancel *msg) @@ -406,6 +418,10 @@ void fsm_msgTxAck(TxAck *msg) void fsm_msgCipherKeyValue(CipherKeyValue *msg) { + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } if (!msg->has_key) { fsm_sendFailure(FailureType_Failure_SyntaxError, "No key provided"); return; @@ -422,7 +438,7 @@ void fsm_msgCipherKeyValue(CipherKeyValue *msg) layoutHome(); return; } - const HDNode *node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; bool encrypt = msg->has_encrypt && msg->encrypt; @@ -531,6 +547,11 @@ void fsm_msgGetAddress(GetAddress *msg) { RESP_INIT(Address); + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + if (!protectPin(true)) { layoutHome(); return; @@ -538,7 +559,7 @@ void fsm_msgGetAddress(GetAddress *msg) const CoinType *coin = fsm_getCoin(msg->coin_name); if (!coin) return; - const HDNode *node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; if (msg->has_multisig) { @@ -599,6 +620,11 @@ void fsm_msgSignMessage(SignMessage *msg) { RESP_INIT(MessageSignature); + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + layoutSignMessage(msg->message.bytes, msg->message.size); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Sign message cancelled"); @@ -613,11 +639,11 @@ void fsm_msgSignMessage(SignMessage *msg) const CoinType *coin = fsm_getCoin(msg->coin_name); if (!coin) return; - const HDNode *node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; layoutProgressSwipe("Signing", 0); - if (cryptoMessageSign(msg->message.bytes, msg->message.size, node->private_key, resp->signature.bytes) == 0) { + if (cryptoMessageSign(node, msg->message.bytes, msg->message.size, resp->signature.bytes) == 0) { resp->has_address = true; uint8_t addr_raw[21]; ecdsa_get_address_raw(node->public_key, coin->address_type, addr_raw); @@ -647,8 +673,18 @@ void fsm_msgVerifyMessage(VerifyMessage *msg) fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid address"); } if (msg->signature.size == 65 && cryptoMessageVerify(msg->message.bytes, msg->message.size, addr_raw, msg->signature.bytes) == 0) { + layoutVerifyAddress(msg->address); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Message verification cancelled"); + layoutHome(); + return; + } layoutVerifyMessage(msg->message.bytes, msg->message.size); - protectButton(ButtonRequestType_ButtonRequest_Other, true); + if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Message verification cancelled"); + layoutHome(); + return; + } fsm_sendSuccess("Message verified"); } else { fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid signature"); @@ -660,6 +696,11 @@ void fsm_msgSignIdentity(SignIdentity *msg) { RESP_INIT(SignedIdentity); + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } + layoutSignIdentity(&(msg->identity), msg->has_challenge_visual ? msg->challenge_visual : 0); if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Sign identity cancelled"); @@ -686,35 +727,31 @@ void fsm_msgSignIdentity(SignIdentity *msg) address_n[3] = 0x80000000 | hash[ 8] | (hash[ 9] << 8) | (hash[10] << 16) | (hash[11] << 24); address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) | (hash[15] << 24); - const HDNode *node = fsm_getDerivedNode(address_n, 5); + 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); if (!node) return; - uint8_t public_key[33]; // copy public key to temporary buffer - memcpy(public_key, node->public_key, sizeof(public_key)); - - if (msg->has_ecdsa_curve_name) { - const ecdsa_curve *curve = get_curve_by_name(msg->ecdsa_curve_name); - if (curve) { - // correct public key (since fsm_getDerivedNode uses secp256k1 curve) - ecdsa_get_public_key33(curve, node->private_key, public_key); - } - } - 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(msg->challenge_hidden.bytes, msg->challenge_hidden.size, node->private_key, resp->signature.bytes); + 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(digest, 64, node->private_key, resp->signature.bytes); + result = cryptoMessageSign(node, digest, 64, resp->signature.bytes); } if (result == 0) { - if (sign_ssh) { + if (strcmp(curve, SECP256K1_NAME) != 0) { resp->has_address = false; } else { resp->has_address = true; @@ -724,7 +761,7 @@ void fsm_msgSignIdentity(SignIdentity *msg) } resp->has_public_key = true; resp->public_key.size = 33; - memcpy(resp->public_key.bytes, public_key, 33); + memcpy(resp->public_key.bytes, node->public_key, 33); resp->has_signature = true; resp->signature.size = 65; msg_write(MessageType_MessageType_SignedIdentity, resp); @@ -736,6 +773,10 @@ void fsm_msgSignIdentity(SignIdentity *msg) void fsm_msgEncryptMessage(EncryptMessage *msg) { + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } if (!msg->has_pubkey) { fsm_sendFailure(FailureType_Failure_SyntaxError, "No public key provided"); return; @@ -765,7 +806,7 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) layoutHome(); return; } - node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); + node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; uint8_t public_key[33]; ecdsa_get_public_key33(&secp256k1, node->private_key, public_key); @@ -792,6 +833,10 @@ void fsm_msgEncryptMessage(EncryptMessage *msg) void fsm_msgDecryptMessage(DecryptMessage *msg) { + if (!storage_isInitialized()) { + fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized"); + return; + } if (!msg->has_nonce) { fsm_sendFailure(FailureType_Failure_SyntaxError, "No nonce provided"); return; @@ -813,7 +858,7 @@ void fsm_msgDecryptMessage(DecryptMessage *msg) layoutHome(); return; } - const HDNode *node = fsm_getDerivedNode(msg->address_n, msg->address_n_count); + const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; layoutProgressSwipe("Decrypting", 0); diff --git a/firmware/layout2.c b/firmware/layout2.c index 2ede4a9e34..4d77c385ac 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -196,10 +196,19 @@ void layoutSignMessage(const uint8_t *msg, uint32_t len) str[0], str[1], str[2], str[3], NULL, NULL); } +void layoutVerifyAddress(const char *address) +{ + const char **str = split_message((const uint8_t *)address, strlen(address), 17); + layoutDialogSwipe(DIALOG_ICON_INFO, "Cancel", "Confirm", + "Confirm address?", + "Message signed by:", + NULL, str[0], str[1], str[2], NULL); +} + void layoutVerifyMessage(const uint8_t *msg, uint32_t len) { const char **str = split_message(msg, len, 16); - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "OK", + layoutDialogSwipe(DIALOG_ICON_INFO, "Cancel", "Confirm", "Verified message", str[0], str[1], str[2], str[3], NULL, NULL); } @@ -270,6 +279,17 @@ void layoutAddress(const char *address, const char *desc) oledRefresh(); } +void layoutPublicKey(const uint8_t *pubkey) +{ + char hex[32*2+1], desc[16]; + strlcpy(desc, "Public Key: 00", sizeof(desc)); + data2hex(pubkey, 1, desc + 12); + data2hex(pubkey + 1, 32, hex); + const char **str = split_message((const uint8_t *)hex, 32*2, 16); + layoutDialogSwipe(DIALOG_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]; diff --git a/firmware/layout2.h b/firmware/layout2.h index 70e35491c0..1b725d5c5c 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -32,11 +32,13 @@ void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out); void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_fee); void layoutFeeOverThreshold(const CoinType *coin, uint64_t fee, uint32_t kb); void layoutSignMessage(const uint8_t *msg, uint32_t len); +void layoutVerifyAddress(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 layoutAddress(const char *address, const char *desc); +void layoutPublicKey(const uint8_t *pubkey); void layoutSignIdentity(const IdentityType *identity, const char *challenge); #endif diff --git a/firmware/messages.c b/firmware/messages.c index 7803efee62..50d37da97e 100644 --- a/firmware/messages.c +++ b/firmware/messages.c @@ -139,7 +139,7 @@ static uint8_t msg_debug_out[MSG_DEBUG_OUT_SIZE]; #endif -inline void msg_out_append(uint8_t c) +static inline void msg_out_append(uint8_t c) { if (msg_out_cur == 0) { msg_out[msg_out_end * 64] = '?'; @@ -155,7 +155,7 @@ inline void msg_out_append(uint8_t c) #if DEBUG_LINK -inline void msg_debug_out_append(uint8_t c) +static inline void msg_debug_out_append(uint8_t c) { if (msg_debug_out_cur == 0) { msg_debug_out[msg_debug_out_end * 64] = '?'; @@ -171,7 +171,7 @@ inline void msg_debug_out_append(uint8_t c) #endif -inline void msg_out_pad(void) +static inline void msg_out_pad(void) { if (msg_out_cur == 0) return; while (msg_out_cur < 64) { diff --git a/firmware/protob/messages.pb.c b/firmware/protob/messages.pb.c index 3a49ebf04c..b0eb77ff23 100644 --- a/firmware/protob/messages.pb.c +++ b/firmware/protob/messages.pb.c @@ -12,7 +12,11 @@ const char SignMessage_coin_name_default[17] = "Bitcoin"; const char EncryptMessage_coin_name_default[17] = "Bitcoin"; const char EstimateTxSize_coin_name_default[17] = "Bitcoin"; const char SignTx_coin_name_default[17] = "Bitcoin"; +const uint32_t SignTx_version_default = 1u; +const uint32_t SignTx_lock_time_default = 0u; const char SimpleSignTx_coin_name_default[17] = "Bitcoin"; +const uint32_t SimpleSignTx_version_default = 1u; +const uint32_t SimpleSignTx_lock_time_default = 0u; const pb_field_t Initialize_fields[1] = { @@ -123,9 +127,10 @@ const pb_field_t Entropy_fields[2] = { PB_LAST_FIELD }; -const pb_field_t GetPublicKey_fields[3] = { +const pb_field_t GetPublicKey_fields[4] = { PB_FIELD2( 1, UINT32 , REPEATED, STATIC , FIRST, GetPublicKey, address_n, address_n, 0), PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, GetPublicKey, ecdsa_curve_name, address_n, 0), + PB_FIELD2( 3, BOOL , OPTIONAL, STATIC , OTHER, GetPublicKey, show_display, ecdsa_curve_name, 0), PB_LAST_FIELD }; @@ -279,18 +284,22 @@ const pb_field_t TxSize_fields[2] = { PB_LAST_FIELD }; -const pb_field_t SignTx_fields[4] = { +const pb_field_t SignTx_fields[6] = { PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, SignTx, outputs_count, outputs_count, 0), PB_FIELD2( 2, UINT32 , REQUIRED, STATIC , OTHER, SignTx, inputs_count, outputs_count, 0), PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, SignTx, coin_name, inputs_count, &SignTx_coin_name_default), + PB_FIELD2( 4, UINT32 , OPTIONAL, STATIC , OTHER, SignTx, version, coin_name, &SignTx_version_default), + PB_FIELD2( 5, UINT32 , OPTIONAL, STATIC , OTHER, SignTx, lock_time, version, &SignTx_lock_time_default), PB_LAST_FIELD }; -const pb_field_t SimpleSignTx_fields[5] = { +const pb_field_t SimpleSignTx_fields[7] = { PB_FIELD2( 1, MESSAGE , REPEATED, STATIC , FIRST, SimpleSignTx, inputs, inputs, &TxInputType_fields), PB_FIELD2( 2, MESSAGE , REPEATED, STATIC , OTHER, SimpleSignTx, outputs, inputs, &TxOutputType_fields), PB_FIELD2( 3, MESSAGE , REPEATED, STATIC , OTHER, SimpleSignTx, transactions, outputs, &TransactionType_fields), PB_FIELD2( 4, STRING , OPTIONAL, STATIC , OTHER, SimpleSignTx, coin_name, transactions, &SimpleSignTx_coin_name_default), + PB_FIELD2( 5, UINT32 , OPTIONAL, STATIC , OTHER, SimpleSignTx, version, coin_name, &SimpleSignTx_version_default), + PB_FIELD2( 6, UINT32 , OPTIONAL, STATIC , OTHER, SimpleSignTx, lock_time, version, &SimpleSignTx_lock_time_default), PB_LAST_FIELD }; diff --git a/firmware/protob/messages.pb.h b/firmware/protob/messages.pb.h index 3f942e5bc5..4049e7f9ed 100644 --- a/firmware/protob/messages.pb.h +++ b/firmware/protob/messages.pb.h @@ -426,6 +426,8 @@ typedef struct _GetPublicKey { uint32_t address_n[8]; bool has_ecdsa_curve_name; char ecdsa_curve_name[32]; + bool has_show_display; + bool show_display; } GetPublicKey; typedef struct _LoadDevice { @@ -551,6 +553,10 @@ typedef struct _SignTx { uint32_t inputs_count; bool has_coin_name; char coin_name[17]; + bool has_version; + uint32_t version; + bool has_lock_time; + uint32_t lock_time; } SignTx; typedef struct { @@ -581,6 +587,10 @@ typedef struct _SimpleSignTx { TransactionType transactions[0]; bool has_coin_name; char coin_name[17]; + bool has_version; + uint32_t version; + bool has_lock_time; + uint32_t lock_time; } SimpleSignTx; typedef struct _Success { @@ -640,7 +650,11 @@ extern const char SignMessage_coin_name_default[17]; extern const char EncryptMessage_coin_name_default[17]; extern const char EstimateTxSize_coin_name_default[17]; extern const char SignTx_coin_name_default[17]; +extern const uint32_t SignTx_version_default; +extern const uint32_t SignTx_lock_time_default; extern const char SimpleSignTx_coin_name_default[17]; +extern const uint32_t SimpleSignTx_version_default; +extern const uint32_t SimpleSignTx_lock_time_default; /* Initializer values for message structs */ #define Initialize_init_default {0} @@ -661,7 +675,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define PassphraseAck_init_default {""} #define GetEntropy_init_default {0} #define Entropy_init_default {{0, {0}}} -#define GetPublicKey_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, ""} +#define GetPublicKey_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0} #define PublicKey_init_default {HDNodeType_init_default, false, ""} #define GetAddress_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "Bitcoin", false, 0, false, MultisigRedeemScriptType_init_default} #define Address_init_default {""} @@ -684,8 +698,8 @@ extern const char SimpleSignTx_coin_name_default[17]; #define CipheredKeyValue_init_default {false, {0, {0}}} #define EstimateTxSize_init_default {0, 0, false, "Bitcoin"} #define TxSize_init_default {false, 0} -#define SignTx_init_default {0, 0, false, "Bitcoin"} -#define SimpleSignTx_init_default {0, {}, 0, {}, 0, {}, false, "Bitcoin"} +#define SignTx_init_default {0, 0, false, "Bitcoin", false, 1u, false, 0u} +#define SimpleSignTx_init_default {0, {}, 0, {}, 0, {}, false, "Bitcoin", false, 1u, false, 0u} #define TxRequest_init_default {false, (RequestType)0, false, TxRequestDetailsType_init_default, false, TxRequestSerializedType_init_default} #define TxAck_init_default {false, TransactionType_init_default} #define SignIdentity_init_default {false, IdentityType_init_default, false, {0, {0}}, false, "", false, ""} @@ -715,7 +729,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define PassphraseAck_init_zero {""} #define GetEntropy_init_zero {0} #define Entropy_init_zero {{0, {0}}} -#define GetPublicKey_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, ""} +#define GetPublicKey_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0} #define PublicKey_init_zero {HDNodeType_init_zero, false, ""} #define GetAddress_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, false, "", false, 0, false, MultisigRedeemScriptType_init_zero} #define Address_init_zero {""} @@ -738,8 +752,8 @@ extern const char SimpleSignTx_coin_name_default[17]; #define CipheredKeyValue_init_zero {false, {0, {0}}} #define EstimateTxSize_init_zero {0, 0, false, ""} #define TxSize_init_zero {false, 0} -#define SignTx_init_zero {0, 0, false, ""} -#define SimpleSignTx_init_zero {0, {}, 0, {}, 0, {}, false, ""} +#define SignTx_init_zero {0, 0, false, "", false, 0, false, 0} +#define SimpleSignTx_init_zero {0, {}, 0, {}, 0, {}, false, "", false, 0, false, 0} #define TxRequest_init_zero {false, (RequestType)0, false, TxRequestDetailsType_init_zero, false, TxRequestSerializedType_init_zero} #define TxAck_init_zero {false, TransactionType_init_zero} #define SignIdentity_init_zero {false, IdentityType_init_zero, false, {0, {0}}, false, "", false, ""} @@ -829,6 +843,7 @@ extern const char SimpleSignTx_coin_name_default[17]; #define GetEntropy_size_tag 1 #define GetPublicKey_address_n_tag 1 #define GetPublicKey_ecdsa_curve_name_tag 2 +#define GetPublicKey_show_display_tag 3 #define LoadDevice_mnemonic_tag 1 #define LoadDevice_node_tag 2 #define LoadDevice_pin_tag 3 @@ -869,6 +884,8 @@ extern const char SimpleSignTx_coin_name_default[17]; #define SignTx_outputs_count_tag 1 #define SignTx_inputs_count_tag 2 #define SignTx_coin_name_tag 3 +#define SignTx_version_tag 4 +#define SignTx_lock_time_tag 5 #define SignedIdentity_address_tag 1 #define SignedIdentity_public_key_tag 2 #define SignedIdentity_signature_tag 3 @@ -876,6 +893,8 @@ extern const char SimpleSignTx_coin_name_default[17]; #define SimpleSignTx_outputs_tag 2 #define SimpleSignTx_transactions_tag 3 #define SimpleSignTx_coin_name_tag 4 +#define SimpleSignTx_version_tag 5 +#define SimpleSignTx_lock_time_tag 6 #define Success_message_tag 1 #define TxAck_tx_tag 1 #define TxRequest_request_type_tag 1 @@ -906,7 +925,7 @@ extern const pb_field_t PassphraseRequest_fields[1]; extern const pb_field_t PassphraseAck_fields[2]; extern const pb_field_t GetEntropy_fields[2]; extern const pb_field_t Entropy_fields[2]; -extern const pb_field_t GetPublicKey_fields[3]; +extern const pb_field_t GetPublicKey_fields[4]; extern const pb_field_t PublicKey_fields[3]; extern const pb_field_t GetAddress_fields[5]; extern const pb_field_t Address_fields[2]; @@ -929,8 +948,8 @@ extern const pb_field_t CipherKeyValue_fields[8]; extern const pb_field_t CipheredKeyValue_fields[2]; extern const pb_field_t EstimateTxSize_fields[4]; extern const pb_field_t TxSize_fields[2]; -extern const pb_field_t SignTx_fields[4]; -extern const pb_field_t SimpleSignTx_fields[5]; +extern const pb_field_t SignTx_fields[6]; +extern const pb_field_t SimpleSignTx_fields[7]; extern const pb_field_t TxRequest_fields[4]; extern const pb_field_t TxAck_fields[2]; extern const pb_field_t SignIdentity_fields[5]; @@ -962,7 +981,7 @@ extern const pb_field_t DebugLinkLog_fields[4]; #define PassphraseAck_size 53 #define GetEntropy_size 6 #define Entropy_size 1027 -#define GetPublicKey_size 82 +#define GetPublicKey_size 84 #define PublicKey_size (121 + HDNodeType_size) #define GetAddress_size (75 + MultisigRedeemScriptType_size) #define Address_size 38 @@ -985,8 +1004,8 @@ extern const pb_field_t DebugLinkLog_fields[4]; #define CipheredKeyValue_size 1027 #define EstimateTxSize_size 31 #define TxSize_size 6 -#define SignTx_size 31 -#define SimpleSignTx_size (19 + 0*TxInputType_size + 0*TxOutputType_size + 0*TransactionType_size) +#define SignTx_size 43 +#define SimpleSignTx_size (31 + 0*TxInputType_size + 0*TxOutputType_size + 0*TransactionType_size) #define TxRequest_size (18 + TxRequestDetailsType_size + TxRequestSerializedType_size) #define TxAck_size (6 + TransactionType_size) #define SignIdentity_size (558 + IdentityType_size) diff --git a/firmware/protob/messages.proto b/firmware/protob/messages.proto index f788ef73ab..4f4140fbd0 120000 --- a/firmware/protob/messages.proto +++ b/firmware/protob/messages.proto @@ -1 +1 @@ -../../trezor-common/protob/messages.proto \ No newline at end of file +../../vendor/trezor-common/protob/messages.proto \ No newline at end of file diff --git a/firmware/protob/storage.proto b/firmware/protob/storage.proto index b7b890d7bf..7502e62f6d 120000 --- a/firmware/protob/storage.proto +++ b/firmware/protob/storage.proto @@ -1 +1 @@ -../../trezor-common/protob/storage.proto \ No newline at end of file +../../vendor/trezor-common/protob/storage.proto \ No newline at end of file diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h index f31812d194..29e8e9ec92 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -53,7 +53,8 @@ typedef enum _ButtonRequestType { ButtonRequestType_ButtonRequest_ProtectCall = 7, ButtonRequestType_ButtonRequest_SignTx = 8, ButtonRequestType_ButtonRequest_FirmwareCheck = 9, - ButtonRequestType_ButtonRequest_Address = 10 + ButtonRequestType_ButtonRequest_Address = 10, + ButtonRequestType_ButtonRequest_PublicKey = 11 } ButtonRequestType; typedef enum _PinMatrixRequestType { diff --git a/firmware/protob/types.proto b/firmware/protob/types.proto index 6f8a7a998f..8eed39a0a4 120000 --- a/firmware/protob/types.proto +++ b/firmware/protob/types.proto @@ -1 +1 @@ -../../trezor-common/protob/types.proto \ No newline at end of file +../../vendor/trezor-common/protob/types.proto \ No newline at end of file diff --git a/firmware/reset.c b/firmware/reset.c index 7e75f96ee6..1ecad15f4d 100644 --- a/firmware/reset.c +++ b/firmware/reset.c @@ -77,7 +77,7 @@ void reset_init(bool display_random, uint32_t _strength, bool passphrase_protect awaiting_entropy = true; } -static char current_word[10]; +static char current_word[10], current_word_display[11]; void reset_entropy(const uint8_t *ext_entropy, uint32_t len) { @@ -89,7 +89,7 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len) sha256_Init(&ctx); sha256_Update(&ctx, int_entropy, 32); sha256_Update(&ctx, ext_entropy, len); - sha256_Final(int_entropy, &ctx); + sha256_Final(&ctx, int_entropy); strlcpy(storage.mnemonic, mnemonic_from_data(int_entropy, strength / 8), sizeof(storage.mnemonic)); memset(int_entropy, 0, 32); awaiting_entropy = false; @@ -122,17 +122,22 @@ void reset_entropy(const uint8_t *ext_entropy, uint32_t len) if (word_pos == 3 || word_pos == 23) { desc[2] = 'r'; desc[3] = 'd'; } + current_word_display[0] = 0x01; + for (j = 0; current_word[j]; j++) { + current_word_display[j + 1] = current_word[j] + 'A' - 'a'; + } + current_word_display[j + 1] = 0; if (word_pos == (int)strength/32*3) { // last word if (pass == 1) { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Finish", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL); + layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Finish", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } else { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Again", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL); + layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Again", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } } else { if (pass == 1) { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL); + layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Please check the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } else { - layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), NULL, current_word, NULL); + layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Write down the seed", NULL, (word_pos < 10 ? desc + 1 : desc), current_word_display, NULL, NULL); } } if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) { diff --git a/firmware/signing.c b/firmware/signing.c index 289315bc37..cfc28d6984 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -51,8 +51,8 @@ static TxStruct to, tp, ti; static SHA256_CTX tc; static uint8_t hash[32], hash_check[32], privkey[32], pubkey[33], sig[64]; static uint64_t to_spend, spending, change_spend; -const uint32_t version = 1; -const uint32_t lock_time = 0; +static uint32_t version = 1; +static uint32_t lock_time = 0; static uint32_t progress, progress_step, progress_meta_step; static bool multisig_fp_set, multisig_fp_mismatch; static uint8_t multisig_fp[32]; @@ -224,12 +224,14 @@ void send_req_finished(void) msg_write(MessageType_MessageType_TxRequest, &resp); } -void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root) +void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time) { inputs_count = _inputs_count; outputs_count = _outputs_count; coin = _coin; root = _root; + version = _version; + lock_time = _lock_time; idx1 = 0; to_spend = 0; @@ -414,7 +416,7 @@ void signing_txack(TransactionType *tx) idx1++; send_req_3_output(); } else { - sha256_Final(hash_check, &tc); + sha256_Final(&tc, hash_check); // check fees if (spending > to_spend) { fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds"); @@ -525,7 +527,7 @@ void signing_txack(TransactionType *tx) idx2++; send_req_4_output(); } else { - sha256_Final(hash, &tc); + sha256_Final(&tc, hash); if (memcmp(hash, hash_check, 32) != 0) { fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); signing_abort(); diff --git a/firmware/signing.h b/firmware/signing.h index bcb66fec6b..ef1b9914ee 100644 --- a/firmware/signing.h +++ b/firmware/signing.h @@ -25,7 +25,7 @@ #include "bip32.h" #include "types.pb.h" -void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root); +void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time); void signing_abort(void); void signing_txack(TransactionType *tx); diff --git a/firmware/storage.c b/firmware/storage.c index f28bce5891..cc9c0ff62a 100644 --- a/firmware/storage.c +++ b/firmware/storage.c @@ -31,6 +31,7 @@ #include "pbkdf2.h" #include "bip32.h" #include "bip39.h" +#include "curves.h" #include "util.h" #include "memory.h" #include "rng.h" @@ -45,8 +46,8 @@ Storage storage; uint8_t storage_uuid[12]; char storage_uuid_str[25]; -static bool sessionRootNodeCached; -static HDNode sessionRootNode; +static bool sessionSeedCached; +static uint8_t sessionSeed[64]; static bool sessionPinCached; @@ -126,8 +127,10 @@ void storage_reset(void) void session_clear(bool clear_pin) { - sessionRootNodeCached = false; memset(&sessionRootNode, 0, sizeof(sessionRootNode)); - sessionPassphraseCached = false; memset(&sessionPassphrase, 0, sizeof(sessionPassphrase)); + sessionSeedCached = false; + memset(&sessionSeed, 0, sizeof(sessionSeed)); + sessionPassphraseCached = false; + memset(&sessionPassphrase, 0, sizeof(sessionPassphrase)); if (clear_pin) { sessionPinCached = false; } @@ -186,14 +189,14 @@ void storage_loadDevice(LoadDevice *msg) storage.has_node = true; storage.has_mnemonic = false; memcpy(&storage.node, &(msg->node), sizeof(HDNodeType)); - sessionRootNodeCached = false; - memset(&sessionRootNode, 0, sizeof(sessionRootNode)); + sessionSeedCached = false; + memset(&sessionSeed, 0, sizeof(sessionSeed)); } else if (msg->has_mnemonic) { storage.has_mnemonic = true; storage.has_node = false; strlcpy(storage.mnemonic, msg->mnemonic, sizeof(storage.mnemonic)); - sessionRootNodeCached = false; - memset(&sessionRootNode, 0, sizeof(sessionRootNode)); + sessionSeedCached = false; + memset(&sessionSeed, 0, sizeof(sessionSeed)); } if (msg->has_language) { @@ -224,7 +227,7 @@ void storage_setLanguage(const char *lang) void storage_setPassphraseProtection(bool passphrase_protection) { - sessionRootNodeCached = false; + sessionSeedCached = false; sessionPassphraseCached = false; storage.has_passphrase_protection = true; @@ -249,56 +252,56 @@ void get_root_node_callback(uint32_t iter, uint32_t total) layoutProgress("Waking up", 1000 * iter / total); } -bool storage_getRootNode(HDNode *node) +const uint8_t *storage_getSeed(void) { // root node is properly cached - if (sessionRootNodeCached) { - memcpy(node, &sessionRootNode, sizeof(HDNode)); - return true; - } - - // if storage has node, decrypt and use it - if (storage.has_node) { - if (!protectPassphrase()) { - return false; - } - if (hdnode_from_xprv(storage.node.depth, storage.node.fingerprint, storage.node.child_num, storage.node.chain_code.bytes, storage.node.private_key.bytes, &sessionRootNode) == 0) { - return false; - } - if (storage.has_passphrase_protection && storage.passphrase_protection && strlen(sessionPassphrase)) { - // decrypt hd node - uint8_t secret[64]; - uint8_t salt[12]; - memcpy(salt, "TREZORHD", 8); - layoutProgressSwipe("Waking up", 0); - pbkdf2_hmac_sha512((const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), salt, 8, BIP39_PBKDF2_ROUNDS, secret, 64, get_root_node_callback); - aes_decrypt_ctx ctx; - aes_decrypt_key256(secret, &ctx); - aes_cbc_decrypt(sessionRootNode.chain_code, sessionRootNode.chain_code, 32, secret + 32, &ctx); - aes_cbc_decrypt(sessionRootNode.private_key, sessionRootNode.private_key, 32, secret + 32, &ctx); - } - memcpy(node, &sessionRootNode, sizeof(HDNode)); - sessionRootNodeCached = true; - return true; + if (sessionSeedCached) { + return sessionSeed; } // if storage has mnemonic, convert it to node and use it if (storage.has_mnemonic) { + if (!protectPassphrase()) { + return NULL; + } + mnemonic_to_seed(storage.mnemonic, sessionPassphrase, sessionSeed, get_root_node_callback); // BIP-0039 + sessionSeedCached = true; + return sessionSeed; + } + + return NULL; +} + +bool storage_getRootNode(HDNode *node, const char *curve) +{ + // if storage has node, decrypt and use it + if (storage.has_node && strcmp(curve, SECP256K1_NAME) == 0) { if (!protectPassphrase()) { return false; } - uint8_t seed[64]; - layoutProgressSwipe("Waking up", 0); - mnemonic_to_seed(storage.mnemonic, sessionPassphrase, seed, get_root_node_callback); // BIP-0039 - if (hdnode_from_seed(seed, sizeof(seed), &sessionRootNode) == 0) { + if (hdnode_from_xprv(storage.node.depth, storage.node.fingerprint, storage.node.child_num, storage.node.chain_code.bytes, storage.node.private_key.bytes, curve, node) == 0) { return false; } - memcpy(node, &sessionRootNode, sizeof(HDNode)); - sessionRootNodeCached = true; + if (storage.has_passphrase_protection && storage.passphrase_protection && sessionPassphraseCached && strlen(sessionPassphrase) > 0) { + // decrypt hd node + uint8_t secret[64]; + uint8_t salt[12]; + memcpy(salt, "TREZORHD", 8); + pbkdf2_hmac_sha512((const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), salt, 8, BIP39_PBKDF2_ROUNDS, secret, 64, get_root_node_callback); + 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; + const uint8_t *seed = storage_getSeed(); + if (seed == NULL) { + return false; + } + + return hdnode_from_seed(seed, 64, curve, node); } const char *storage_getLabel(void) diff --git a/firmware/storage.h b/firmware/storage.h index 793c8c4ea3..8d909e9216 100644 --- a/firmware/storage.h +++ b/firmware/storage.h @@ -33,7 +33,9 @@ void session_clear(bool clear_pin); void storage_loadDevice(LoadDevice *msg); -bool storage_getRootNode(HDNode *node); +const uint8_t *storage_getSeed(void); + +bool storage_getRootNode(HDNode *node, const char *curve); const char *storage_getLabel(void); void storage_setLabel(const char *label); diff --git a/firmware/transaction.c b/firmware/transaction.c index 61b98090bf..45b70da2d0 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -223,7 +223,7 @@ uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, d[1] = 0xAE; sha256_Update(&ctx, d, 2); - sha256_Final(hash, &ctx); + sha256_Final(&ctx, hash); return 1; } @@ -425,7 +425,7 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { - sha256_Final(hash, &(t->ctx)); + sha256_Final(&(t->ctx), hash); sha256_Raw(hash, 32, hash); if (!reverse) return; uint8_t i, k; diff --git a/firmware/trezor.h b/firmware/trezor.h index a9cf8b83e4..a3ea5f4b48 100644 --- a/firmware/trezor.h +++ b/firmware/trezor.h @@ -22,7 +22,7 @@ #define VERSION_MAJOR 1 #define VERSION_MINOR 3 -#define VERSION_PATCH 4 +#define VERSION_PATCH 5 #define STR(X) #X #define VERSTR(X) STR(X) diff --git a/gen/bitmaps/.gitignore b/gen/bitmaps/.gitignore deleted file mode 100644 index 68359a7869..0000000000 --- a/gen/bitmaps/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.c -*.h diff --git a/gitian/gitian.yml b/gitian/gitian.yml new file mode 100644 index 0000000000..2f24b9777f --- /dev/null +++ b/gitian/gitian.yml @@ -0,0 +1,19 @@ +--- +name: "trezor-mcu" +enable_cache: true +suites: +- "trusty" +architectures: +- "amd64" +packages: +- "build-essential" +- "gcc-arm-none-eabi" +reference_datetime: "2015-06-01 00:00:00" +remotes: +- "url": "https://github.com/trezor/trezor-mcu.git" + "dir": "trezor-mcu" +files: [] +script: | + make -C vendor/libopencm3 + make + make -C firmware diff --git a/oled.c b/oled.c index 6b3ef56743..a63d810a9f 100644 --- a/oled.c +++ b/oled.c @@ -55,13 +55,30 @@ #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 + * an array of OLED_WIDTH * OLED_HEIGHT/8 bytes. At byte y*OLED_WIDTH + x + * it stores the column of pixels from (x,8y) to (x,8y+7); the LSB stores + * the top most pixel. The pixel (0,0) is the top left corner of the + * display. + */ + +/* Macros to manipulate a single pixel in _oledbuffer: + * OLED_BUFSET(X,Y) sets pixel X,Y (white) + * OLED_BUFCLR(X,Y) clears pixel X,Y (black) + * OLED_BUFTGL(X,Y) toggles pixel X,Y (inverts it) + */ + #define OLED_BUFSET(X,Y) _oledbuffer[OLED_BUFSIZE - 1 - (X) - ((Y)/8)*OLED_WIDTH] |= (1 << (7 - (Y)%8)) #define OLED_BUFCLR(X,Y) _oledbuffer[OLED_BUFSIZE - 1 - (X) - ((Y)/8)*OLED_WIDTH] &= ~(1 << (7 - (Y)%8)) #define OLED_BUFTGL(X,Y) _oledbuffer[OLED_BUFSIZE - 1 - (X) - ((Y)/8)*OLED_WIDTH] ^= (1 << (7 - (Y)%8)) static uint8_t _oledbuffer[OLED_BUFSIZE]; -static char is_debug_mode = 0; +static bool is_debug_mode = 0; +/* + * Send a block of data via the SPI bus. + */ inline void SPISend(uint32_t base, uint8_t *data, int len) { int i; @@ -72,6 +89,9 @@ inline void SPISend(uint32_t base, uint8_t *data, int len) delay(800); } +/* + * Initialize the display. + */ void oledInit() { static uint8_t s[25] = { @@ -121,11 +141,20 @@ void oledInit() oledRefresh(); } +/* + * Clears the display buffer (sets all pixels to black) + */ void oledClear() { memset(_oledbuffer, 0, sizeof(_oledbuffer)); } +/* + * Refresh the display. This copies the buffer to the display to show the + * contents. This must be called after every operation to the buffer to + * make the change visible. All other operations only change the buffer + * not the content of the display. + */ void oledRefresh() { static uint8_t s[3] = {OLED_SETLOWCOLUMN | 0x00, OLED_SETHIGHCOLUMN | 0x00, OLED_SETSTARTLINE | 0x00}; @@ -164,7 +193,7 @@ const uint8_t *oledGetBuffer() return _oledbuffer; } -void oledSetDebug(char set) +void oledSetDebug(bool set) { is_debug_mode = set; oledRefresh(); @@ -187,7 +216,7 @@ void oledClearPixel(int x, int y) OLED_BUFCLR(x,y); } -void oledDrawChar(int x, int y, char c) +void oledDrawChar(int x, int y, char c, int zoom) { int char_width; const uint8_t *char_data; @@ -197,11 +226,15 @@ void oledDrawChar(int x, int y, char c) char_width = fontCharWidth(c); char_data = fontCharData(c); - int xoffset, yoffset; - for (xoffset = 0; xoffset < char_width; xoffset++) { - for (yoffset = 0; yoffset < FONT_HEIGHT; yoffset++) { - if (char_data[xoffset] & (1 << (FONT_HEIGHT - 1 - yoffset))) { - oledDrawPixel(x + xoffset, y + yoffset); + int xo, yo; + for (xo = 0; xo < char_width; xo++) { + for (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); + } } } } @@ -233,13 +266,18 @@ int oledStringWidth(const char *text) { void oledDrawString(int x, int y, const char* text) { if (!text) return; + int size = 1; + if (*text == 0x01) { // double size + text++; + size = 2; + } int l = 0; char c; for (; *text; text++) { c = oledConvertChar(*text); if (c) { - oledDrawChar(x + l, y, c); - l += fontCharWidth(c) + 1; + oledDrawChar(x + l, y, c, size); + l += size * (fontCharWidth(c) + 1); } } } @@ -283,12 +321,15 @@ void oledInvert(int x1, int y1, int x2, int y2) } } -void oledBox(int x1, int y1, int x2, int y2, char val) +/* + * Draw a filled rectangle. + */ +void oledBox(int x1, int y1, int x2, int y2, bool set) { int x, y; for (x = x1; x <= x2; x++) { for (y = y1; y <= y2; y++) { - val ? oledDrawPixel(x, y) : oledClearPixel(x, y); + set ? oledDrawPixel(x, y) : oledClearPixel(x, y); } } } @@ -300,6 +341,9 @@ void oledHLine(int y) { } } +/* + * Draw a rectangle frame. + */ void oledFrame(int x1, int y1, int x2, int y2) { int x, y; @@ -313,6 +357,10 @@ void oledFrame(int x1, int y1, int x2, int y2) } } +/* + * Animates the display, swiping the current contents out to the left. + * This clears the display. + */ void oledSwipeLeft(void) { int i, j, k; @@ -333,6 +381,10 @@ void oledSwipeLeft(void) } } +/* + * Animates the display, swiping the current contents out to the right. + * This clears the display. + */ void oledSwipeRight(void) { int i, j, k; diff --git a/oled.h b/oled.h index c3b8db2c69..6fccc363ad 100644 --- a/oled.h +++ b/oled.h @@ -21,6 +21,7 @@ #define __OLED_H__ #include +#include #include "bitmaps.h" #include "fonts.h" @@ -33,19 +34,19 @@ void oledInit(void); void oledClear(void); void oledRefresh(void); -void oledSetDebug(char set); +void oledSetDebug(bool set); void oledSetBuffer(uint8_t *buf); const uint8_t *oledGetBuffer(void); void oledDrawPixel(int x, int y); void oledClearPixel(int x, int y); -void oledDrawChar(int x, int y, char c); +void oledDrawChar(int x, int y, char c, int zoom); int oledStringWidth(const char *text); void oledDrawString(int x, int y, const char* text); void oledDrawStringCenter(int y, const char* text); void oledDrawStringRight(int x, int y, const char* text); 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, char val); +void oledBox(int x1, int y1, int x2, int y2, bool set); void oledHLine(int y); void oledFrame(int x1, int y1, int x2, int y2); void oledSwipeLeft(void); diff --git a/setup.c b/setup.c index cb6ad1bf03..c85bd8d0c1 100644 --- a/setup.c +++ b/setup.c @@ -25,7 +25,7 @@ void setup(void) { // setup clock - clock_scale_t clock = hse_8mhz_3v3[CLOCK_3V3_120MHZ]; + 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) diff --git a/trezor-common b/trezor-common deleted file mode 160000 index 72268e816b..0000000000 --- a/trezor-common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 72268e816b8e8e06f698b3729223a255c7c74167 diff --git a/trezor-crypto b/trezor-crypto deleted file mode 160000 index cbbc0bdc71..0000000000 --- a/trezor-crypto +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cbbc0bdc7197e74d647aedcbfd064c43544318cf diff --git a/trezor-qrenc b/trezor-qrenc deleted file mode 160000 index 1183aa7146..0000000000 --- a/trezor-qrenc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1183aa714615dfaa9cfb771bca7ec8c11929a4c2 diff --git a/util.h b/util.h index d7bb5b4244..e31c5801c8 100644 --- a/util.h +++ b/util.h @@ -22,7 +22,7 @@ #include -inline void delay(uint32_t wait); +void delay(uint32_t wait); // converts uint32 to hexa (8 digits) void uint32hex(uint32_t num, char *str); diff --git a/vendor/libopencm3 b/vendor/libopencm3 new file mode 160000 index 0000000000..d3fff11c1f --- /dev/null +++ b/vendor/libopencm3 @@ -0,0 +1 @@ +Subproject commit d3fff11c1f68b706591c0d51c82d18a0bc88dc17 diff --git a/vendor/trezor-common b/vendor/trezor-common new file mode 160000 index 0000000000..0567a429cf --- /dev/null +++ b/vendor/trezor-common @@ -0,0 +1 @@ +Subproject commit 0567a429cfc8c6fdf9e08c79270750c102fc4f70 diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto new file mode 160000 index 0000000000..ed6debf8c4 --- /dev/null +++ b/vendor/trezor-crypto @@ -0,0 +1 @@ +Subproject commit ed6debf8c4ec5ef9c7ef31a1a7eddf76aa33ccd8 diff --git a/vendor/trezor-qrenc b/vendor/trezor-qrenc new file mode 160000 index 0000000000..9e0228f54d --- /dev/null +++ b/vendor/trezor-qrenc @@ -0,0 +1 @@ +Subproject commit 9e0228f54db6241524bb89acd3e89040701e0380