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