Updated branch u2f

pull/25/head
Jochen Hoenicke 8 years ago
commit cf38291ca4

9
.gitmodules vendored

@ -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

@ -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

@ -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

@ -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)

@ -8,13 +8,23 @@ http://bitcointrezor.com/
How to build TREZOR firmware?
-----------------------------
1. Install Docker (from docker.com or from your distribution repositories)
1. <a href="https://docs.docker.com/engine/installation/">Install Docker</a>
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. <a href="https://docs.docker.com/engine/installation/">Install Docker</a>
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?
-------------------------------------------------------------------------

@ -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)"

@ -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

@ -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();

@ -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

@ -18,10 +18,12 @@
*/
#include <stdint.h>
#include <string.h>
#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;
}

@ -20,6 +20,6 @@
#ifndef __SIGNATURES_H__
#define __SIGNATURES_H__
int signatures_ok(void);
int signatures_ok(uint8_t *store_hash);
#endif

@ -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

@ -21,6 +21,7 @@
#define __BUTTONS_H__
#include <libopencm3/stm32/gpio.h>
#include <stdbool.h>
struct buttonState {
volatile bool YesUp;

@ -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

@ -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)"

@ -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 += ../trezor-crypto/ripemd160.o
OBJS += ../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 += ../trezor-qrenc/qr_encode.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 += ../vendor/trezor-crypto/ripemd160.o
OBJS += ../vendor/trezor-crypto/sha2.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 += ../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

@ -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
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 | (cp.y.val[0] & 0x01);
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;
}

@ -24,6 +24,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <ecdsa.h>
#include <bip32.h>
#include <sha2.h>
#include <pb.h>
#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);

@ -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);
if (!node) return;
uint8_t public_key[33]; // copy public key to temporary buffer
memcpy(public_key, node->public_key, sizeof(public_key));
const char *curve = SECP256K1_NAME;
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);
}
curve = msg->ecdsa_curve_name;
}
const HDNode *node = fsm_getDerivedNode(curve, address_n, 5);
if (!node) return;
bool sign_ssh = msg->identity.has_proto && (strcmp(msg->identity.proto, "ssh") == 0);
bool sign_gpg = msg->identity.has_proto && (strcmp(msg->identity.proto, "gpg") == 0);
int result = 0;
layoutProgressSwipe("Signing", 0);
if (sign_ssh) { // SSH does not sign visual challenge
result = sshMessageSign(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);

@ -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];

@ -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

@ -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) {

@ -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
};

@ -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)

@ -1 +1 @@
../../trezor-common/protob/messages.proto
../../vendor/trezor-common/protob/messages.proto

@ -1 +1 @@
../../trezor-common/protob/storage.proto
../../vendor/trezor-common/protob/storage.proto

@ -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 {

@ -1 +1 @@
../../trezor-common/protob/types.proto
../../vendor/trezor-common/protob/types.proto

@ -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)) {

@ -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();

@ -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);

@ -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 (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) {
if (storage.has_node && strcmp(curve, SECP256K1_NAME) == 0) {
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) {
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;
}
if (storage.has_passphrase_protection && storage.passphrase_protection && strlen(sessionPassphrase)) {
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);
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);
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);
}
memcpy(node, &sessionRootNode, sizeof(HDNode));
sessionRootNodeCached = true;
return true;
}
// if storage has mnemonic, convert it to node and use it
if (storage.has_mnemonic) {
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) {
return false;
}
memcpy(node, &sessionRootNode, sizeof(HDNode));
sessionRootNodeCached = true;
return true;
const uint8_t *seed = storage_getSeed();
if (seed == NULL) {
return false;
}
return false;
return hdnode_from_seed(seed, 64, curve, node);
}
const char *storage_getLabel(void)

@ -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);

@ -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;

@ -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)

@ -1,2 +0,0 @@
*.c
*.h

@ -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

@ -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;

@ -21,6 +21,7 @@
#define __OLED_H__
#include <stdint.h>
#include <stdbool.h>
#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);

@ -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)

@ -1 +0,0 @@
Subproject commit 72268e816b8e8e06f698b3729223a255c7c74167

@ -1 +0,0 @@
Subproject commit cbbc0bdc7197e74d647aedcbfd064c43544318cf

@ -1 +0,0 @@
Subproject commit 1183aa714615dfaa9cfb771bca7ec8c11929a4c2

@ -22,7 +22,7 @@
#include <stdint.h>
inline void delay(uint32_t wait);
void delay(uint32_t wait);
// converts uint32 to hexa (8 digits)
void uint32hex(uint32_t num, char *str);

1
vendor/libopencm3 vendored

@ -0,0 +1 @@
Subproject commit d3fff11c1f68b706591c0d51c82d18a0bc88dc17

@ -0,0 +1 @@
Subproject commit 0567a429cfc8c6fdf9e08c79270750c102fc4f70

@ -0,0 +1 @@
Subproject commit ed6debf8c4ec5ef9c7ef31a1a7eddf76aa33ccd8

@ -0,0 +1 @@
Subproject commit 9e0228f54db6241524bb89acd3e89040701e0380
Loading…
Cancel
Save