1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-24 16:38:15 +00:00

MONOREPO MERGE trezor-mcu

This commit is contained in:
matejcik 2019-04-15 19:14:58 +02:00
commit 6aa05f8a6f
239 changed files with 26541 additions and 0 deletions

19
.gitmodules vendored
View File

@ -26,3 +26,22 @@
[submodule "crypto/tests/wycheproof"]
path = crypto/tests/wycheproof
url = https://github.com/google/wycheproof
[submodule "legacy/trezor-crypto"]
path = legacy/vendor/trezor-crypto
url = https://github.com/trezor/trezor-crypto.git
[submodule "legacy/trezor-common"]
path = legacy/vendor/trezor-common
url = https://github.com/trezor/trezor-common.git
[submodule "legacy/libopencm3"]
path = legacy/vendor/libopencm3
url = https://github.com/libopencm3/libopencm3.git
[submodule "legacy/vendor/nanopb"]
path = legacy/vendor/nanopb
url = https://github.com/nanopb/nanopb.git
[submodule "legacy/vendor/trezor-storage"]
path = legacy/vendor/trezor-storage
url = https://github.com/trezor/trezor-storage.git
[submodule "legacy/vendor/QR-Code-generator"]
path = legacy/vendor/QR-Code-generator
url = https://github.com/nayuki/QR-Code-generator.git
ignore = untracked

2
legacy/.clang-format Normal file
View File

@ -0,0 +1,2 @@
---
BasedOnStyle: Google

1
legacy/.dockerignore Normal file
View File

@ -0,0 +1 @@
_attic/

14
legacy/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
.cache/
.vscode/
_attic/
build/
*.o
*.a
*.d
*.bin
*.elf
*.hex
*.img
*.list
*.srec
*.log

63
legacy/.travis.yml Normal file
View File

@ -0,0 +1,63 @@
sudo: false
dist: trusty
language: c
addons:
apt:
sources:
- deadsnakes
packages:
- build-essential
- python3.6
- python3.6-dev
- python3.6-venv
env:
global:
- MAKEFLAGS=-j2
- PYTHON=python3.6
- PROTOBUF_VERSION=3.4.0
- TOOLCHAIN_SHORTVER=8-2018q4
- TOOLCHAIN_LONGVER=gcc-arm-none-eabi-8-2018-q4-major
matrix:
- DEBUG_LINK=0
- DEBUG_LINK=1
matrix:
include:
- name: "Emulator GCC"
env: EMULATOR=1 HEADLESS=1 DEBUG_LINK=1
compiler: gcc
script: pipenv run ./script/cibuild && pipenv run script/test
- name: "Emulator Clang"
env: EMULATOR=1 HEADLESS=1 DEBUG_LINK=1
compiler: clang
script: pipenv run ./script/cibuild && pipenv run script/test
before_install:
- $PYTHON -m ensurepip --user
- $PYTHON -m pip install --user pipenv
install:
- wget "https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip"
- unzip "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" -d protoc
- export PATH="$(pwd)/protoc/bin:$PATH"
- pipenv install
before_script:
- test "$EMULATOR" = "1" || wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/$TOOLCHAIN_SHORTVER/$TOOLCHAIN_LONGVER-linux.tar.bz2
- test "$EMULATOR" = "1" || tar xfj $TOOLCHAIN_LONGVER-linux.tar.bz2
- test "$EMULATOR" = "1" || export PATH=$PWD/$TOOLCHAIN_LONGVER/bin:$PATH
script:
- pipenv run script/cibuild
- pipenv run make -C bootloader
- pipenv run make -C demo
notifications:
webhooks:
urls:
- http://ci-bot.satoshilabs.com:5000/travis
on_success: always
on_failure: always
on_start: always

5
legacy/CONTRIBUTING.md Normal file
View File

@ -0,0 +1,5 @@
# Contribute to Trezor MCU
Please read the general instructions you can find on our [wiki](https://wiki.trezor.io/Developers_guide:Contributing).
If you are working on a new feature, you probably want to contribute this to the [trezor-core](https://github.com/trezor/trezor-core) repository instead.

165
legacy/COPYING Normal file
View File

@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

77
legacy/Dockerfile Normal file
View File

@ -0,0 +1,77 @@
# initialize from the image
FROM debian:9
ARG TOOLCHAIN_FLAVOR=linux
ENV TOOLCHAIN_FLAVOR=$TOOLCHAIN_FLAVOR
# install build tools and dependencies
ARG EMULATOR=0
ENV EMULATOR=$EMULATOR
RUN apt-get update && apt-get install -y \
build-essential wget git python3-pip
# install dependencies from toolchain source build
RUN if [ "$TOOLCHAIN_FLAVOR" = "src" ]; then \
apt-get install -y autoconf autogen bison dejagnu \
flex flip gawk git gperf gzip nsis \
openssh-client p7zip-full perl python-dev \
libisl-dev tcl tofrodos zip \
texinfo texlive texlive-extra-utils; \
fi
# download toolchain
ENV TOOLCHAIN_SHORTVER=8-2018q4
ENV TOOLCHAIN_LONGVER=gcc-arm-none-eabi-8-2018-q4-major
ENV TOOLCHAIN_URL=https://developer.arm.com/-/media/Files/downloads/gnu-rm/$TOOLCHAIN_SHORTVER/$TOOLCHAIN_LONGVER-$TOOLCHAIN_FLAVOR.tar.bz2
ENV TOOLCHAIN_HASH_linux=fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52
ENV TOOLCHAIN_HASH_src=bc228325dbbfaf643f2ee5d19e01d8b1873fcb9c31781b5e1355d40a68704ce7
RUN if [ "$EMULATOR" = 1 ]; then \
apt-get install -y libsdl2-dev libsdl2-image-dev; \
fi
# extract toolchain
RUN cd /opt && wget $TOOLCHAIN_URL
RUN cd /opt && echo "$TOOLCHAIN_HASH_linux $TOOLCHAIN_LONGVER-linux.tar.bz2\n$TOOLCHAIN_HASH_src $TOOLCHAIN_LONGVER-src.tar.bz2" | sha256sum -c --ignore-missing
RUN cd /opt && tar xfj $TOOLCHAIN_LONGVER-$TOOLCHAIN_FLAVOR.tar.bz2
# build toolchain (if required)
RUN if [ "$TOOLCHAIN_FLAVOR" = "src" ]; then \
pushd /opt/$TOOLCHAIN_LONGVER ; \
./install-sources.sh --skip_steps=mingw32 ; \
./build-prerequisites.sh --skip_steps=mingw32 ; \
./build-toolchain.sh --skip_steps=mingw32,manual ; \
popd ; \
fi
# download protobuf
ENV PROTOBUF_VERSION=3.4.0
ENV PROTOBUF_HASH=e4b51de1b75813e62d6ecdde582efa798586e09b5beaebfb866ae7c9eaadace4
RUN wget "https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip"
RUN echo "${PROTOBUF_HASH} protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" | sha256sum -c
# setup toolchain
ENV PATH=/opt/$TOOLCHAIN_LONGVER/bin:$PATH
ENV PYTHON=python3
ENV LC_ALL=C.UTF-8 LANG=C.UTF-8
# use zipfile module to extract files world-readable
RUN $PYTHON -m zipfile -e "protoc-${PROTOBUF_VERSION}-linux-x86_64.zip" /usr/local && chmod 755 /usr/local/bin/protoc
ENV WORKON_HOME=/tmp/.venvs
# install python dependencies
RUN $PYTHON -m pip install pipenv

43
legacy/Makefile Normal file
View File

@ -0,0 +1,43 @@
ifneq ($(EMULATOR),1)
OBJS += startup.o
endif
OBJS += buttons.o
OBJS += common.o
OBJS += flash.o
OBJS += layout.o
OBJS += oled.o
OBJS += rng.o
ifneq ($(EMULATOR),1)
OBJS += setup.o
endif
OBJS += util.o
OBJS += memory.o
OBJS += supervise.o
ifneq ($(EMULATOR),1)
OBJS += timer.o
endif
OBJS += usb_standard.o
OBJS += usb21_standard.o
OBJS += webusb.o
OBJS += winusb.o
OBJS += gen/bitmaps.o
OBJS += gen/fonts.o
libtrezor.a: $(OBJS)
include Makefile.include
libtrezor.a:
@printf " AR $@\n"
$(Q)$(AR) rcs $@ $^
.PHONY: vendor
vendor:
git submodule update --init --recursive

226
legacy/Makefile.include Normal file
View File

@ -0,0 +1,226 @@
TOP_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
TOOLCHAIN_DIR ?= $(TOP_DIR)vendor/libopencm3
ifneq ($(V),1)
Q := @
endif
PYTHON ?= python
ifeq ($(EMULATOR),1)
CC ?= gcc
LD := $(CC)
OBJCOPY := objcopy
OBJDUMP := objdump
AR := ar
AS := as
OPTFLAGS ?= -O3
DBGFLAGS ?= -g3 -ggdb3
CPUFLAGS ?=
FPUFLAGS ?=
else
PREFIX ?= arm-none-eabi-
CC := $(PREFIX)gcc
LD := $(PREFIX)gcc
OBJCOPY := $(PREFIX)objcopy
OBJDUMP := $(PREFIX)objdump
AR := $(PREFIX)ar
AS := $(PREFIX)as
OPENOCD := openocd -f interface/stlink-v2.cfg -c "transport select hla_swd" -f target/stm32f2x.cfg
GDB := $(PREFIX)gdb --nx -ex 'set remotetimeout unlimited' -ex 'set confirm off' -ex 'target remote 127.0.0.1:3333' -ex 'monitor reset halt'
OPTFLAGS ?= -O3
DBGFLAGS ?= -g -DNDEBUG
CPUFLAGS ?= -mcpu=cortex-m3 -mthumb
FPUFLAGS ?= -msoft-float
endif
CFLAGS += $(OPTFLAGS) \
$(DBGFLAGS) \
-std=gnu11 \
-W \
-Wall \
-Wextra \
-Wimplicit-function-declaration \
-Wredundant-decls \
-Wstrict-prototypes \
-Wundef \
-Wshadow \
-Wpointer-arith \
-Wformat \
-Wreturn-type \
-Wsign-compare \
-Wmultichar \
-Wformat-nonliteral \
-Winit-self \
-Wuninitialized \
-Wformat-security \
-Werror \
-fno-common \
-fno-exceptions \
-fvisibility=internal \
-ffunction-sections \
-fdata-sections \
-fstack-protector-all \
$(CPUFLAGS) \
$(FPUFLAGS) \
-DSTM32F2 \
-DCONFIDENTIAL='__attribute__((section("confidential")))' \
-DRAND_PLATFORM_INDEPENDENT=1 \
-I$(TOOLCHAIN_DIR)/include \
-I$(TOP_DIR) \
-I$(TOP_DIR)gen \
-I$(TOP_DIR)vendor/trezor-crypto \
-I$(TOP_DIR)vendor/QR-Code-generator/c \
-I$(TOP_DIR)vendor/trezor-storage
LDFLAGS += -L$(TOP_DIR) \
$(DBGFLAGS) \
$(CPUFLAGS) \
$(FPUFLAGS)
ifeq ($(EMULATOR),1)
CFLAGS += -DEMULATOR=1
CFLAGS += -include $(TOP_DIR)emulator/emulator.h
CFLAGS += -include stdio.h
LDFLAGS += -L$(TOP_DIR)emulator
LDLIBS += -ltrezor -lemulator
LIBDEPS += $(TOP_DIR)/libtrezor.a $(TOP_DIR)emulator/libemulator.a
ifeq ($(HEADLESS),1)
CFLAGS += -DHEADLESS=1
else
CFLAGS += -DHEADLESS=0
CFLAGS += $(shell pkg-config --cflags sdl2)
LDLIBS += $(shell pkg-config --libs sdl2)
endif
ifdef RANDOM_DEV_FILE
CFLAGS += -DRANDOM_DEV_FILE=\"$(RANDOM_DEV_FILE)\"
endif
else
ifdef APPVER
CFLAGS += -DAPPVER=$(APPVER)
LDSCRIPT = $(TOP_DIR)/memory_app_$(APPVER).ld
else
LDSCRIPT = $(TOP_DIR)/memory.ld
endif
CFLAGS += -DEMULATOR=0
LDFLAGS += --static \
-Wl,--start-group \
-lc \
-lgcc \
-lnosys \
-Wl,--end-group \
-L$(TOOLCHAIN_DIR)/lib \
-T$(LDSCRIPT) \
-nostartfiles \
-Wl,--gc-sections
LDLIBS += -ltrezor
LIBDEPS += $(TOP_DIR)/libtrezor.a
LDLIBS += -lopencm3_stm32f2
LIBDEPS += $(TOOLCHAIN_DIR)/lib/libopencm3_stm32f2.a
endif
ifeq ($(MEMORY_PROTECT), 0)
CFLAGS += -DMEMORY_PROTECT=0
$(info MEMORY_PROTECT=0)
else
CFLAGS += -DMEMORY_PROTECT=1
$(info MEMORY_PROTECT=1)
endif
ifeq ($(DEBUG_RNG), 1)
CFLAGS += -DDEBUG_RNG=1
else
CFLAGS += -DDEBUG_RNG=0
endif
all: $(NAME).bin
openocd:
$(OPENOCD)
gdb_bootloader: bootloader/bootloader.elf
$(GDB) $<
gdb_firmware: firmware/trezor.elf
$(GDB) $<
flash: $(NAME).bin
$(OPENOCD) -c "init; reset halt; flash write_image erase $(NAME).bin 0x8000000; exit"
upload: sign
trezorctl firmware_update -f $(NAME).bin -s
sign: $(NAME).bin
$(PYTHON) ../bootloader/firmware_sign.py -f $(NAME).bin
release: $(NAME).bin
$(PYTHON) ../bootloader/firmware_sign.py -f $(NAME).bin
cp $(NAME).bin $(NAME)-$(APPVER).bin
chmod -x $(NAME)-$(APPVER).bin
xxd -p $(NAME)-$(APPVER).bin | tr -d '\n' > $(NAME)-$(APPVER).bin.hex
$(NAME).bin: $(NAME).elf
@printf " OBJCOPY $@\n"
$(Q)$(OBJCOPY) -Obinary $(NAME).elf $(NAME).bin
$(NAME).hex: $(NAME).elf
@printf " OBJCOPY $@\n"
$(Q)$(OBJCOPY) -Oihex $(NAME).elf $(NAME).hex
$(NAME).srec: $(NAME).elf
@printf " OBJCOPY $@\n"
$(Q)$(OBJCOPY) -Osrec $(NAME).elf $(NAME).srec
$(NAME).list: $(NAME).elf
@printf " OBJDUMP $@\n"
$(Q)$(OBJDUMP) -S $(NAME).elf > $(NAME).list
$(NAME).elf: $(OBJS) $(LDSCRIPT) $(LIBDEPS)
@printf " LD $@\n"
$(Q)$(LD) -o $(NAME).elf $(OBJS) $(LDLIBS) $(LDFLAGS)
%.o: %.s Makefile
@printf " AS $@\n"
$(Q)$(AS) $(CPUFLAGS) -o $@ $<
%.o: %.c Makefile
@printf " CC $@\n"
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
%.small.o: %.c Makefile
@printf " CC $@\n"
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
%.d: %.c Makefile
@printf " DEP $@\n"
$(Q)$(CC) $(CFLAGS) -MM -MP -MG -o $@ $<
%.small.d: %.c Makefile
@printf " DEP $@\n"
$(Q)$(CC) $(CFLAGS) -MM -MP -MG -o $@ $<
clean::
rm -f $(OBJS)
rm -f *.a
rm -f *.bin
rm -f *.d
rm -f *.elf
rm -f *.hex
rm -f *.list
rm -f *.log
rm -f *.srec
-include $(OBJS:.o=.d)

14
legacy/Pipfile Normal file
View File

@ -0,0 +1,14 @@
[[source]]
url = "https://pypi.org/simple"
name = "pypi"
verify_ssl = true
[packages]
setuptools = ">=24.2.0"
trezor = {git = "https://github.com/trezor/python-trezor", editable = true, ref = "master"}
pytest = "*"
mock = "*"
typing = "*"
protobuf = "==3.4.0"
mako = "*"
munch = "*"

214
legacy/Pipfile.lock generated Normal file
View File

@ -0,0 +1,214 @@
{
"_meta": {
"hash": {
"sha256": "0c77aa21c1e385d7c3833a2f95bc6129394f6d9ce67e1181700a76a5e15074cb"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"atomicwrites": {
"hashes": [
"sha256:240831ea22da9ab882b551b31d4225591e5e447a68c5e188db5b89ca1d487585",
"sha256:a24da68318b08ac9c9c45029f4a10371ab5b20e4226738e150e6e7c571630ae6"
],
"version": "==1.1.5"
},
"attrs": {
"hashes": [
"sha256:4b90b09eeeb9b88c35bc642cbac057e45a5fd85367b985bd2809c62b7b939265",
"sha256:e0d0eb91441a3b53dab4d9b743eafc1ac44476296a2053b6ca3af0b139faf87b"
],
"version": "==18.1.0"
},
"certifi": {
"hashes": [
"sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7",
"sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0"
],
"version": "==2018.4.16"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
"version": "==3.0.4"
},
"click": {
"hashes": [
"sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d",
"sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b"
],
"version": "==6.7"
},
"ecdsa": {
"hashes": [
"sha256:40d002cf360d0e035cf2cb985e1308d41aaa087cbfc135b2dc2d844296ea546c",
"sha256:64cf1ee26d1cde3c73c6d7d107f835fed7c6a2904aef9eac223d57ad800c43fa"
],
"version": "==0.13"
},
"idna": {
"hashes": [
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
],
"version": "==2.7"
},
"libusb1": {
"hashes": [
"sha256:4707f81e933a97fed1c5bf7d4957f07bae1139cb8084bdee1f50201a40e3fd7c"
],
"version": "==1.6.5"
},
"mako": {
"hashes": [
"sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae"
],
"index": "pypi",
"version": "==1.0.7"
},
"markupsafe": {
"hashes": [
"sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665"
],
"version": "==1.0"
},
"mnemonic": {
"hashes": [
"sha256:02a7306a792370f4a0c106c2cf1ce5a0c84b9dbd7e71c6792fdb9ad88a727f1d"
],
"version": "==0.18"
},
"mock": {
"hashes": [
"sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1",
"sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba"
],
"index": "pypi",
"version": "==2.0.0"
},
"more-itertools": {
"hashes": [
"sha256:2b6b9893337bfd9166bee6a62c2b0c9fe7735dcf85948b387ec8cba30e85d8e8",
"sha256:6703844a52d3588f951883005efcf555e49566a48afd4db4e965d69b883980d3",
"sha256:a18d870ef2ffca2b8463c0070ad17b5978056f403fb64e3f15fe62a52db21cc0"
],
"version": "==4.2.0"
},
"munch": {
"hashes": [
"sha256:6ae3d26b837feacf732fb8aa5b842130da1daf221f5af9f9d4b2a0a6414b0d51"
],
"index": "pypi",
"version": "==2.3.2"
},
"pbkdf2": {
"hashes": [
"sha256:ac6397369f128212c43064a2b4878038dab78dab41875364554aaf2a684e6979"
],
"version": "==1.3"
},
"pbr": {
"hashes": [
"sha256:1b8be50d938c9bb75d0eaf7eda111eec1bf6dc88a62a6412e33bf077457e0f45",
"sha256:b486975c0cafb6beeb50ca0e17ba047647f229087bd74e37f4a7e2cac17d2caa"
],
"version": "==4.2.0"
},
"pluggy": {
"hashes": [
"sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1",
"sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1"
],
"markers": "python_version != '3.2.*' and python_version != '3.1.*' and python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.3.*'",
"version": "==0.7.1"
},
"protobuf": {
"hashes": [
"sha256:1fcb9b704bc2e30767352d86b2664d8f65f8ed49654d7a80e7a150739724e80a",
"sha256:41c4555d9754b985352ce5289fa3ba6b21ed715f595111e46e2b90ca53112475",
"sha256:4d4815467f8a61b06d648699842b233017b201f7a16275d680ec5480f10e30e9",
"sha256:5b816951df388f4ab2adbd3f9ae5619b9a5d7033d14b005c345dc3ee88a7faf4",
"sha256:61dbf86993a9312c3a0816b5252079a3943856003bf0380fea3098c929084ad4",
"sha256:9f3be25ad48b051186ee88f9567a3f3f548facd360e0cb62568e2736d9cfda11",
"sha256:ef02609ef445987976a3a26bff77119c518e0915c96661c3a3b17856d0ef6374"
],
"index": "pypi",
"version": "==3.4.0"
},
"py": {
"hashes": [
"sha256:3fd59af7435864e1a243790d322d763925431213b6b8529c6ca71081ace3bbf7",
"sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e"
],
"version": "==1.5.4"
},
"pyblake2": {
"hashes": [
"sha256:3757f7ad709b0e1b2a6b3919fa79fe3261f166fc375cd521f2be480f8319dde9",
"sha256:407e02c7f8f36fcec1b7aa114ddca0c1060c598142ea6f6759d03710b946a7e3",
"sha256:4d47b4a2c1d292b1e460bde1dda4d13aa792ed2ed70fcc263b6bc24632c8e902",
"sha256:5ccc7eb02edb82fafb8adbb90746af71460fbc29aa0f822526fc976dff83e93f",
"sha256:8043267fbc0b2f3748c6920591cd0b8b5609dcce60c504c32858aa36206386f2",
"sha256:982295a87907d50f4723db6bc724660da76b6547826d52160171d54f95b919ac",
"sha256:baa2190bfe549e36163aa44664d4ee3a9080b236fc5d42f50dc6fd36bbdc749e",
"sha256:c53417ee0bbe77db852d5fd1036749f03696ebc2265de359fe17418d800196c4",
"sha256:fbc9fcde75713930bc2a91b149e97be2401f7c9c56d735b46a109210f58d7358"
],
"version": "==1.1.2"
},
"pytest": {
"hashes": [
"sha256:341ec10361b64a24accaec3c7ba5f7d5ee1ca4cebea30f76fad3dd12db9f0541",
"sha256:952c0389db115437f966c4c2079ae9d54714b9455190e56acebe14e8c38a7efa"
],
"index": "pypi",
"version": "==3.6.4"
},
"requests": {
"hashes": [
"sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1",
"sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a"
],
"version": "==2.19.1"
},
"six": {
"hashes": [
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
],
"version": "==1.11.0"
},
"trezor": {
"editable": true,
"git": "https://github.com/trezor/python-trezor",
"ref": "master"
},
"typing": {
"hashes": [
"sha256:3a887b021a77b292e151afb75323dea88a7bc1b3dfa92176cff8e44c8b68bddf",
"sha256:b2c689d54e1144bbcfd191b0832980a21c2dbcf7b5ff7a66248a60c90e951eb8",
"sha256:d400a9344254803a2368533e4533a4200d21eb7b6b729c173bc38201a74db3f2"
],
"index": "pypi",
"version": "==3.6.4"
},
"urllib3": {
"hashes": [
"sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf",
"sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5"
],
"version": "==1.23"
}
},
"develop": {}
}

74
legacy/README.md Normal file
View File

@ -0,0 +1,74 @@
# TREZOR One Bootloader and Firmware
[![Build Status](https://travis-ci.org/trezor/trezor-mcu.svg?branch=master)](https://travis-ci.org/trezor/trezor-mcu) [![gitter](https://badges.gitter.im/trezor/community.svg)](https://gitter.im/trezor/community)
https://trezor.io/
## How to build the TREZOR bootloader, firmware and emulator
Ensure that you have Docker installed. You can follow [Docker's installation instructions](https://docs.docker.com/engine/installation/).
Clone this repository:
```sh
git clone https://github.com/trezor/trezor-mcu.git`
cd trezor-mcu
```
Use the `build.sh` command to build the images.
* to build bootloader 1.6.0 and firmware 1.7.0:
```sh
./build.sh bl1.6.0 v1.7.0
```
* to build latest firmware from master:
```sh
./build.sh
```
* to build the emulator from master:
```sh
./build.sh EMU
```
* to build the emulator for version 1.7.0:
```sh
./build.sh EMU v1.7.0
```
Build results are stored in the `build/` directory. File `bootloader-<tag>.bin` represents
the bootloader, `trezor-<tag>.bin` is the firmware image, and `trezor-emulator-<tag>.elf`
is the emulator executable.
You can use `TREZOR_OLED_SCALE` environment variable to make emulator screen bigger.
## How to get fingerprint of firmware signed and distributed by SatoshiLabs?
1. Pick version of firmware binary listed on https://wallet.trezor.io/data/firmware/1/releases.json
2. Download it: `wget -O trezor.signed.bin https://wallet.trezor.io/data/firmware/1/trezor-1.6.1.bin`
3. Compute fingerprint: `tail -c +257 trezor.signed.bin | sha256sum`
Step 3 should produce the same sha256 fingerprint like your local build (for the same version tag). Firmware has a special header (of length 256 bytes) holding signatures themselves, which must be avoided while calculating the fingerprint, that's why tail command has to be used.
## How to install custom built firmware?
**WARNING: This will erase the recovery seed stored on the device! You should never do this on TREZOR that contains coins!**
1. Install python-trezor: `pip install trezor` ([more info](https://github.com/trezor/python-trezor))
2. `trezorctl firmware_update -f build/trezor-TAG.bin`
## Building for development
If you want to build device firmware, make sure you have the
[GNU ARM Embedded toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) installed.
You will also need Python 3.5 or later and [pipenv](https://pipenv.readthedocs.io/en/latest/install/).
* If you want to build the emulator instead of the firmware, run `export EMULATOR=1 TREZOR_TRANSPORT_V1=1`
* If you want to build with the debug link, run `export DEBUG_LINK=1`. Use this if you want to run the device tests.
* When you change these variables, use `script/setup` to clean the repository
1. To initialize the repository, run `script/setup`
2. To initialize a Python environment, run `pipenv install`
3. To build the firmware or emulator, run `pipenv run script/cibuild`
If you are building device firmware, the firmware will be in `firmware/trezor.bin`.
You can launch the emulator using `firmware/trezor.elf`. To use `trezorctl` with the emulator, use
`trezorctl -p udp` (for example, `trezorctl -p udp get_features`).

9
legacy/bootloader/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.o
*.a
*.d
*.bin
*.elf
*.hex
*.list
*.srec
*.log

View File

@ -0,0 +1,50 @@
Version 1.6.1
* Fix USB issue on some Windows 10 installations
Version 1.6.0
* Switch from HID to WebUSB
Version 1.5.1
* Improve MPU configuration
Version 1.5.0
* Make unofficial firmwares work again
Version 1.4.0
* More flash-write tests
* Don't restore storage from unofficial firmware
* Support WipeDevice message
* Activate MPU and don't switch VTOR table for unofficial firmware
Version 1.3.3
* Add self-test
* Erase metadata backup after usage
* Erase SRAM on application start
Version 1.3.2
* Don't show recovery seed warning if firmware is flashed for the first time
* Don't show fingerprint if firmware is flashed for the first time
* Compute firmware hash before checking signatures
* Add self-test
* Fix usage of RNG before setup
* Fix stack protector fault
Version 1.3.1
* Fix button testing so it does not break USB communication
Version 1.3.0
* Add test for buttons
* Clean USB descriptor
* Return firmware_present in Features response
* Don't halt on broken firware, stay in bootloader
Version 1.2.7
* Optimize speed of firmware update
Version 1.2.6
* Show hash of unofficial firmware
* Use stack protector
* Clean USB descriptor
Version 1.2.5
* Initial import of code

View File

@ -0,0 +1,21 @@
NAME = bootloader
OBJS += bootloader.o
OBJS += signatures.o
OBJS += usb.o
OBJS += ../vendor/trezor-crypto/bignum.small.o
OBJS += ../vendor/trezor-crypto/ecdsa.small.o
OBJS += ../vendor/trezor-crypto/secp256k1.small.o
OBJS += ../vendor/trezor-crypto/sha2.small.o
OBJS += ../vendor/trezor-crypto/memzero.small.o
CFLAGS += -DUSE_PRECOMPUTED_IV=0
CFLAGS += -DUSE_PRECOMPUTED_CP=0
OPTFLAGS ?= -Os
include ../Makefile.include
align: $(NAME).bin
./firmware_align.py $(NAME).bin

View File

@ -0,0 +1,153 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <libopencm3/cm3/scb.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/rcc.h>
#include "bootloader.h"
#include "buttons.h"
#include "layout.h"
#include "memory.h"
#include "oled.h"
#include "rng.h"
#include "setup.h"
#include "signatures.h"
#include "usb.h"
#include "util.h"
void layoutFirmwareFingerprint(const uint8_t *hash) {
char str[4][17];
for (int i = 0; i < 4; i++) {
data2hex(hash + i * 8, 8, str[i]);
}
layoutDialog(&bmp_icon_question, "Abort", "Continue", "Compare fingerprints",
str[0], str[1], str[2], str[3], NULL, NULL);
}
bool get_button_response(void) {
do {
delay(100000);
buttonUpdate();
} while (!button.YesUp && !button.NoUp);
return button.YesUp;
}
void show_halt(const char *line1, const char *line2) {
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, NULL,
"Unplug your TREZOR,", "reinstall firmware.", NULL);
shutdown();
}
static void show_unofficial_warning(const uint8_t *hash) {
layoutDialog(&bmp_icon_warning, "Abort", "I'll take the risk", NULL,
"WARNING!", NULL, "Unofficial firmware", "detected.", NULL,
NULL);
bool but = get_button_response();
if (!but) { // no button was pressed -> halt
show_halt("Unofficial firmware", "aborted.");
}
layoutFirmwareFingerprint(hash);
but = get_button_response();
if (!but) { // no button was pressed -> halt
show_halt("Unofficial firmware", "aborted.");
}
// everything is OK, user pressed 2x Continue -> continue program
}
static void __attribute__((noreturn)) load_app(int signed_firmware) {
// zero out SRAM
memset_reg(_ram_start, _ram_end, 0);
jump_to_firmware((const vector_table_t *)FLASH_PTR(FLASH_APP_START),
signed_firmware);
}
static void bootloader_loop(void) {
oledClear();
oledDrawBitmap(0, 0, &bmp_logo64);
if (firmware_present_new()) {
oledDrawStringCenter(90, 10, "TREZOR", FONT_STANDARD);
oledDrawStringCenter(90, 30, "Bootloader", FONT_STANDARD);
oledDrawStringCenter(90, 50,
VERSTR(VERSION_MAJOR) "." VERSTR(
VERSION_MINOR) "." VERSTR(VERSION_PATCH),
FONT_STANDARD);
} else {
oledDrawStringCenter(90, 10, "Welcome!", FONT_STANDARD);
oledDrawStringCenter(90, 30, "Please visit", FONT_STANDARD);
oledDrawStringCenter(90, 50, "trezor.io/start", FONT_STANDARD);
}
oledRefresh();
usbLoop();
}
int main(void) {
#ifndef APPVER
setup();
#endif
__stack_chk_guard = random32(); // this supports compiler provided
// unpredictable stack protection checks
#ifndef APPVER
memory_protect();
oledInit();
#endif
mpu_config_bootloader();
#ifndef APPVER
bool left_pressed = (buttonRead() & BTN_PIN_NO) == 0;
if (firmware_present_new() && !left_pressed) {
oledClear();
oledDrawBitmap(40, 0, &bmp_logo64_empty);
oledRefresh();
const image_header *hdr =
(const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
uint8_t fingerprint[32];
int signed_firmware = signatures_new_ok(hdr, fingerprint);
if (SIG_OK != signed_firmware) {
show_unofficial_warning(fingerprint);
}
if (SIG_OK != check_firmware_hashes(hdr)) {
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, "Broken firmware",
"detected.", NULL, "Unplug your TREZOR,",
"reinstall firmware.", NULL);
shutdown();
}
mpu_config_off();
load_app(signed_firmware);
}
#endif
bootloader_loop();
return 0;
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BOOTLOADER_H__
#define __BOOTLOADER_H__
#define VERSION_MAJOR 1
#define VERSION_MINOR 8
#define VERSION_PATCH 0
#define STR(X) #X
#define VERSTR(X) STR(X)
#define VERSION_MAJOR_CHAR "\x01"
#define VERSION_MINOR_CHAR "\x08"
#define VERSION_PATCH_CHAR "\x00"
#include <stdbool.h>
#include <stdint.h>
void show_halt(const char *line1, const char *line2);
void layoutFirmwareFingerprint(const uint8_t *hash);
bool get_button_response(void);
#endif

View File

@ -0,0 +1,12 @@
#!/usr/bin/env python
from __future__ import print_function
bl = open('bl.bin').read()
fw = open('fw.bin').read()
combined = bl + fw[:256] + (32768-256)*'\x00' + fw[256:]
open('combined.bin', 'w').write(combined)
print('bootloader : %d bytes' % len(bl))
print('firmware : %d bytes' % len(fw))
print('combined : %d bytes' % len(combined))

View File

@ -0,0 +1,2 @@
#!/bin/bash
st-flash write combined.bin 0x8000000

View File

@ -0,0 +1,14 @@
#!/usr/bin/env python3
import sys
import os
TOTALSIZE = 32768
MAXSIZE = TOTALSIZE - 32
fn = sys.argv[1]
fs = os.stat(fn).st_size
if fs > MAXSIZE:
raise Exception('bootloader has to be smaller than %d bytes (current size is %d)' % (MAXSIZE, fs))
with open(fn, 'ab') as f:
f.write(b'\x00' * (TOTALSIZE - fs))
f.close()

View File

@ -0,0 +1,259 @@
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import hashlib
import struct
import ecdsa
SLOTS = 3
pubkeys = {
1: "04d571b7f148c5e4232c3814f777d8faeaf1a84216c78d569b71041ffc768a5b2d810fc3bb134dd026b57e65005275aedef43e155f48fc11a32ec790a93312bd58",
2: "0463279c0c0866e50c05c799d32bd6bab0188b6de06536d1109d2ed9ce76cb335c490e55aee10cc901215132e853097d5432eda06b792073bd7740c94ce4516cb1",
3: "0443aedbb6f7e71c563f8ed2ef64ec9981482519e7ef4f4aa98b27854e8c49126d4956d300ab45fdc34cd26bc8710de0a31dbdf6de7435fd0b492be70ac75fde58",
4: "04877c39fd7c62237e038235e9c075dab261630f78eeb8edb92487159fffedfdf6046c6f8b881fa407c4a4ce6c28de0b19c1f4e29f1fcbc5a58ffd1432a3e0938a",
5: "047384c51ae81add0a523adbb186c91b906ffb64c2c765802bf26dbd13bdf12c319e80c2213a136c8ee03d7874fd22b70d68e7dee469decfbbb510ee9a460cda45",
}
FWHEADER_SIZE = 1024
SIGNATURES_START = 6 * 4 + 8 + 512
INDEXES_START = SIGNATURES_START + 3 * 64
def parse_args():
parser = argparse.ArgumentParser(
description="Commandline tool for signing Trezor firmware."
)
parser.add_argument("-f", "--file", dest="path", help="Firmware file to modify")
parser.add_argument(
"-s",
"--sign",
dest="sign",
action="store_true",
help="Add signature to firmware slot",
)
parser.add_argument(
"-p", "--pem", dest="pem", action="store_true", help="Use PEM instead of SECEXP"
)
parser.add_argument(
"-g",
"--generate",
dest="generate",
action="store_true",
help="Generate new ECDSA keypair",
)
return parser.parse_args()
def pad_to_size(data, size):
if len(data) > size:
raise ValueError("Chunk too big already")
if len(data) == size:
return data
return data + b"\xFF" * (size - len(data))
# see memory.h for details
def prepare_hashes(data):
# process chunks
start = 0
end = (64 - 1) * 1024
hashes = []
for i in range(16):
sector = data[start:end]
if len(sector) > 0:
chunk = pad_to_size(sector, end - start)
hashes.append(hashlib.sha256(chunk).digest())
else:
hashes.append(b"\x00" * 32)
start = end
end += 64 * 1024
return hashes
def check_hashes(data):
expected_hashes = data[0x20 : 0x20 + 16 * 32]
hashes = b""
for h in prepare_hashes(data[FWHEADER_SIZE:]):
hashes += h
if expected_hashes == hashes:
print("HASHES OK")
else:
print("HASHES NOT OK")
def update_hashes_in_header(data):
# Store hashes in the firmware header
data = bytearray(data)
o = 0
for h in prepare_hashes(data[FWHEADER_SIZE:]):
data[0x20 + o:0x20 + o + 32] = h
o += 32
return bytes(data)
def get_header(data, zero_signatures=False):
if not zero_signatures:
return data[:FWHEADER_SIZE]
else:
data = bytearray(data[:FWHEADER_SIZE])
data[SIGNATURES_START : SIGNATURES_START + 3 * 64 + 3] = b"\x00" * (3 * 64 + 3)
return bytes(data)
def check_size(data):
size = struct.unpack("<L", data[12:16])[0]
assert size == len(data) - 1024
def check_signatures(data):
# Analyses given firmware and prints out
# status of included signatures
indexes = [x for x in data[INDEXES_START : INDEXES_START + SLOTS]]
to_sign = get_header(data, zero_signatures=True)
fingerprint = hashlib.sha256(to_sign).hexdigest()
print("Firmware fingerprint:", fingerprint)
used = []
for x in range(SLOTS):
signature = data[SIGNATURES_START + 64 * x : SIGNATURES_START + 64 * x + 64]
if indexes[x] == 0:
print("Slot #%d" % (x + 1), "is empty")
else:
pk = pubkeys[indexes[x]]
verify = ecdsa.VerifyingKey.from_string(
bytes.fromhex(pk)[1:],
curve=ecdsa.curves.SECP256k1,
hashfunc=hashlib.sha256,
)
try:
verify.verify(signature, to_sign, hashfunc=hashlib.sha256)
if indexes[x] in used:
print("Slot #%d signature: DUPLICATE" % (x + 1), signature.hex())
else:
used.append(indexes[x])
print("Slot #%d signature: VALID" % (x + 1), signature.hex())
except:
print("Slot #%d signature: INVALID" % (x + 1), signature.hex())
def modify(data, slot, index, signature):
data = bytearray(data)
# put index to data
data[INDEXES_START + slot - 1] = index
# put signature to data
data[SIGNATURES_START + 64 * (slot - 1) : SIGNATURES_START + 64 * slot] = signature
return bytes(data)
def sign(data, is_pem):
# Ask for index and private key and signs the firmware
slot = int(input("Enter signature slot (1-%d): " % SLOTS))
if slot < 1 or slot > SLOTS:
raise Exception("Invalid slot")
if is_pem:
print("Paste ECDSA private key in PEM format and press Enter:")
print("(blank private key removes the signature on given index)")
pem_key = ""
while True:
key = input()
pem_key += key + "\n"
if key == "":
break
if pem_key.strip() == "":
# Blank key,let's remove existing signature from slot
return modify(data, slot, 0, b"\x00" * 64)
key = ecdsa.SigningKey.from_pem(pem_key)
else:
print("Paste SECEXP (in hex) and press Enter:")
print("(blank private key removes the signature on given index)")
secexp = input()
if secexp.strip() == "":
# Blank key,let's remove existing signature from slot
return modify(data, slot, 0, b"\x00" * 64)
key = ecdsa.SigningKey.from_secret_exponent(
secexp=int(secexp, 16),
curve=ecdsa.curves.SECP256k1,
hashfunc=hashlib.sha256,
)
to_sign = get_header(data, zero_signatures=True)
# Locate proper index of current signing key
pubkey = "04" + key.get_verifying_key().to_string().hex()
index = None
for i, pk in pubkeys.items():
if pk == pubkey:
index = i
break
if index == None:
raise Exception("Unable to find private key index. Unknown private key?")
signature = key.sign_deterministic(to_sign, hashfunc=hashlib.sha256)
return modify(data, slot, index, signature)
def main(args):
if args.generate:
key = ecdsa.SigningKey.generate(
curve=ecdsa.curves.SECP256k1, hashfunc=hashlib.sha256
)
print("PRIVATE KEY (SECEXP):")
print(key.to_string().hex())
print()
print("PRIVATE KEY (PEM):")
print(key.to_pem())
print("PUBLIC KEY:")
print("04" + key.get_verifying_key().to_string().hex())
return
if not args.path:
raise Exception("-f/--file is required")
data = open(args.path, "rb").read()
assert len(data) % 4 == 0
if data[:4] != b"TRZF":
raise Exception("Firmware header expected")
data = update_hashes_in_header(data)
print("Firmware size %d bytes" % len(data))
check_size(data)
check_signatures(data)
check_hashes(data)
if args.sign:
data = sign(data, args.pem)
check_signatures(data)
check_hashes(data)
fp = open(args.path, "wb")
fp.write(data)
fp.close()
if __name__ == "__main__":
args = parse_args()
main(args)

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python3
import hashlib
import os
import subprocess
import ecdsa
from binascii import hexlify, unhexlify
print('master secret:', end='')
h = input()
if h:
h = unhexlify(h).encode('ascii')
else:
h = hashlib.sha256(os.urandom(1024)).digest()
print()
print('master secret:', hexlify(h))
print()
for i in range(1, 6):
se = hashlib.sha256(h + chr(i).encode('ascii')).hexdigest()
print('seckey', i, ':', se)
sk = ecdsa.SigningKey.from_secret_exponent(secexp = int(se, 16), curve=ecdsa.curves.SECP256k1, hashfunc=hashlib.sha256)
print('pubkey', i, ':', (b'04' + hexlify(sk.get_verifying_key().to_string())).decode('ascii'))
print(sk.to_pem().decode('ascii'))
p = subprocess.Popen('ssss-split -t 3 -n 5 -x'.split(' '), stdin = subprocess.PIPE)
p.communicate(input = hexlify(h) + '\n')
# to recover use:
# $ ssss-combine -t 3 -x

View File

@ -0,0 +1,203 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "bootloader.h"
#include "ecdsa.h"
#include "memory.h"
#include "memzero.h"
#include "secp256k1.h"
#include "sha2.h"
#include "signatures.h"
const uint32_t FIRMWARE_MAGIC_OLD = 0x525a5254; // TRZR
const uint32_t FIRMWARE_MAGIC_NEW = 0x465a5254; // TRZF
#define PUBKEYS 5
static const uint8_t * const pubkey[PUBKEYS] = {
(const uint8_t *)"\x04\xd5\x71\xb7\xf1\x48\xc5\xe4\x23\x2c\x38\x14\xf7\x77\xd8\xfa\xea\xf1\xa8\x42\x16\xc7\x8d\x56\x9b\x71\x04\x1f\xfc\x76\x8a\x5b\x2d\x81\x0f\xc3\xbb\x13\x4d\xd0\x26\xb5\x7e\x65\x00\x52\x75\xae\xde\xf4\x3e\x15\x5f\x48\xfc\x11\xa3\x2e\xc7\x90\xa9\x33\x12\xbd\x58",
(const uint8_t *)"\x04\x63\x27\x9c\x0c\x08\x66\xe5\x0c\x05\xc7\x99\xd3\x2b\xd6\xba\xb0\x18\x8b\x6d\xe0\x65\x36\xd1\x10\x9d\x2e\xd9\xce\x76\xcb\x33\x5c\x49\x0e\x55\xae\xe1\x0c\xc9\x01\x21\x51\x32\xe8\x53\x09\x7d\x54\x32\xed\xa0\x6b\x79\x20\x73\xbd\x77\x40\xc9\x4c\xe4\x51\x6c\xb1",
(const uint8_t *)"\x04\x43\xae\xdb\xb6\xf7\xe7\x1c\x56\x3f\x8e\xd2\xef\x64\xec\x99\x81\x48\x25\x19\xe7\xef\x4f\x4a\xa9\x8b\x27\x85\x4e\x8c\x49\x12\x6d\x49\x56\xd3\x00\xab\x45\xfd\xc3\x4c\xd2\x6b\xc8\x71\x0d\xe0\xa3\x1d\xbd\xf6\xde\x74\x35\xfd\x0b\x49\x2b\xe7\x0a\xc7\x5f\xde\x58",
(const uint8_t *)"\x04\x87\x7c\x39\xfd\x7c\x62\x23\x7e\x03\x82\x35\xe9\xc0\x75\xda\xb2\x61\x63\x0f\x78\xee\xb8\xed\xb9\x24\x87\x15\x9f\xff\xed\xfd\xf6\x04\x6c\x6f\x8b\x88\x1f\xa4\x07\xc4\xa4\xce\x6c\x28\xde\x0b\x19\xc1\xf4\xe2\x9f\x1f\xcb\xc5\xa5\x8f\xfd\x14\x32\xa3\xe0\x93\x8a",
(const uint8_t *)"\x04\x73\x84\xc5\x1a\xe8\x1a\xdd\x0a\x52\x3a\xdb\xb1\x86\xc9\x1b\x90\x6f\xfb\x64\xc2\xc7\x65\x80\x2b\xf2\x6d\xbd\x13\xbd\xf1\x2c\x31\x9e\x80\xc2\x21\x3a\x13\x6c\x8e\xe0\x3d\x78\x74\xfd\x22\xb7\x0d\x68\xe7\xde\xe4\x69\xde\xcf\xbb\xb5\x10\xee\x9a\x46\x0c\xda\x45",
};
#define SIGNATURES 3
#define FLASH_META_START 0x08008000
#define FLASH_META_CODELEN (FLASH_META_START + 0x0004)
#define FLASH_META_SIGINDEX1 (FLASH_META_START + 0x0008)
#define FLASH_META_SIGINDEX2 (FLASH_META_START + 0x0009)
#define FLASH_META_SIGINDEX3 (FLASH_META_START + 0x000A)
#define FLASH_OLD_APP_START 0x08010000
#define FLASH_META_SIG1 (FLASH_META_START + 0x0040)
#define FLASH_META_SIG2 (FLASH_META_START + 0x0080)
#define FLASH_META_SIG3 (FLASH_META_START + 0x00C0)
bool firmware_present_old(void) {
if (memcmp(FLASH_PTR(FLASH_META_START), &FIRMWARE_MAGIC_OLD,
4)) { // magic does not match
return false;
}
if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) <
8192) { // firmware reports smaller size than 8192
return false;
}
if (*((const uint32_t *)FLASH_PTR(FLASH_META_CODELEN)) >
FLASH_APP_LEN) { // firmware reports bigger size than flash size
return false;
}
return true;
}
int signatures_old_ok(void) {
const uint32_t codelen = *((const uint32_t *)FLASH_META_CODELEN);
const uint8_t sigindex1 = *((const uint8_t *)FLASH_META_SIGINDEX1);
const uint8_t sigindex2 = *((const uint8_t *)FLASH_META_SIGINDEX2);
const uint8_t sigindex3 = *((const uint8_t *)FLASH_META_SIGINDEX3);
if (codelen > FLASH_APP_LEN) {
return false;
}
uint8_t hash[32];
sha256_Raw(FLASH_PTR(FLASH_OLD_APP_START), codelen, hash);
if (sigindex1 < 1 || sigindex1 > PUBKEYS) return SIG_FAIL; // invalid index
if (sigindex2 < 1 || sigindex2 > PUBKEYS) return SIG_FAIL; // invalid index
if (sigindex3 < 1 || sigindex3 > PUBKEYS) return SIG_FAIL; // invalid index
if (sigindex1 == sigindex2) return SIG_FAIL; // duplicate use
if (sigindex1 == sigindex3) return SIG_FAIL; // duplicate use
if (sigindex2 == sigindex3) return SIG_FAIL; // duplicate use
if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex1 - 1],
(const uint8_t *)FLASH_META_SIG1,
hash)) { // failure
return SIG_FAIL;
}
if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex2 - 1],
(const uint8_t *)FLASH_META_SIG2,
hash)) { // failure
return SIG_FAIL;
}
if (0 != ecdsa_verify_digest(&secp256k1, pubkey[sigindex3 - 1],
(const uint8_t *)FLASH_META_SIG3,
hash)) { // failture
return SIG_FAIL;
}
return SIG_OK;
}
void compute_firmware_fingerprint(const image_header *hdr, uint8_t hash[32]) {
image_header copy;
memcpy(&copy, hdr, sizeof(image_header));
memzero(copy.sig1, sizeof(copy.sig1));
memzero(copy.sig2, sizeof(copy.sig2));
memzero(copy.sig3, sizeof(copy.sig3));
copy.sigindex1 = 0;
copy.sigindex2 = 0;
copy.sigindex3 = 0;
sha256_Raw((const uint8_t *)&copy, sizeof(image_header), hash);
}
bool firmware_present_new(void) {
const image_header *hdr =
(const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
if (hdr->magic != FIRMWARE_MAGIC_NEW) return false;
// we need to ignore hdrlen for now
// because we keep reset_handler ptr there
// for compatibility with older bootloaders
// after this is no longer necessary, let's uncomment the line below:
// if (hdr->hdrlen != FLASH_FWHEADER_LEN) return false;
if (hdr->codelen > FLASH_APP_LEN) return false;
if (hdr->codelen < 4096) return false;
return true;
}
int signatures_new_ok(const image_header *hdr, uint8_t store_fingerprint[32]) {
uint8_t hash[32];
compute_firmware_fingerprint(hdr, hash);
if (store_fingerprint) {
memcpy(store_fingerprint, hash, 32);
}
if (hdr->sigindex1 < 1 || hdr->sigindex1 > PUBKEYS)
return SIG_FAIL; // invalid index
if (hdr->sigindex2 < 1 || hdr->sigindex2 > PUBKEYS)
return SIG_FAIL; // invalid index
if (hdr->sigindex3 < 1 || hdr->sigindex3 > PUBKEYS)
return SIG_FAIL; // invalid index
if (hdr->sigindex1 == hdr->sigindex2) return SIG_FAIL; // duplicate use
if (hdr->sigindex1 == hdr->sigindex3) return SIG_FAIL; // duplicate use
if (hdr->sigindex2 == hdr->sigindex3) return SIG_FAIL; // duplicate use
if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex1 - 1],
hdr->sig1, hash)) { // failure
return SIG_FAIL;
}
if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex2 - 1],
hdr->sig2, hash)) { // failure
return SIG_FAIL;
}
if (0 != ecdsa_verify_digest(&secp256k1, pubkey[hdr->sigindex3 - 1],
hdr->sig3, hash)) { // failure
return SIG_FAIL;
}
return SIG_OK;
}
int mem_is_empty(const uint8_t *src, uint32_t len) {
for (uint32_t i = 0; i < len; i++) {
if (src[i]) return 0;
}
return 1;
}
int check_firmware_hashes(const image_header *hdr) {
uint8_t hash[32];
// check hash of the first code chunk
sha256_Raw(FLASH_PTR(FLASH_APP_START), (64 - 1) * 1024, hash);
if (0 != memcmp(hash, hdr->hashes, 32)) return SIG_FAIL;
// check remaining used chunks
uint32_t total_len = FLASH_FWHEADER_LEN + hdr->codelen;
int used_chunks = total_len / FW_CHUNK_SIZE;
if (total_len % FW_CHUNK_SIZE > 0) {
used_chunks++;
}
for (int i = 1; i < used_chunks; i++) {
sha256_Raw(FLASH_PTR(FLASH_FWHEADER_START + (64 * i) * 1024), 64 * 1024,
hash);
if (0 != memcmp(hdr->hashes + 32 * i, hash, 32)) return SIG_FAIL;
}
// check unused chunks
for (int i = used_chunks; i < 16; i++) {
if (!mem_is_empty(hdr->hashes + 32 * i, 32)) return SIG_FAIL;
}
// all OK
return SIG_OK;
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __SIGNATURES_H__
#define __SIGNATURES_H__
#include <stdbool.h>
#include <stdint.h>
extern const uint32_t FIRMWARE_MAGIC_OLD; // TRZR
extern const uint32_t FIRMWARE_MAGIC_NEW; // TRZF
#define SIG_OK 0x5A3CA5C3
#define SIG_FAIL 0x00000000
bool firmware_present_old(void);
int signatures_old_ok(void);
// we use the same structure as T2 firmware header
// but we don't use the field sig
// and rather introduce fields sig1, sig2, sig3
// immediately following the chunk hashes
typedef struct {
uint32_t magic;
uint32_t hdrlen;
uint32_t expiry;
uint32_t codelen;
uint32_t version;
uint32_t fix_version;
uint8_t __reserved1[8];
uint8_t hashes[512];
uint8_t sig1[64];
uint8_t sig2[64];
uint8_t sig3[64];
uint8_t sigindex1;
uint8_t sigindex2;
uint8_t sigindex3;
uint8_t __reserved2[220];
uint8_t __sigmask;
uint8_t __sig[64];
} __attribute__((packed)) image_header;
#define FW_CHUNK_SIZE 65536
bool firmware_present_new(void);
void compute_firmware_fingerprint(const image_header *hdr, uint8_t hash[32]);
int signatures_new_ok(const image_header *hdr, uint8_t store_fingerprint[32]);
int check_firmware_hashes(const image_header *hdr);
int mem_is_empty(const uint8_t *src, uint32_t len);
#endif

467
legacy/bootloader/usb.c Normal file
View File

@ -0,0 +1,467 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopencm3/stm32/flash.h>
#include <libopencm3/usb/usbd.h>
#include <string.h>
#include "bootloader.h"
#include "buttons.h"
#include "ecdsa.h"
#include "layout.h"
#include "memory.h"
#include "memzero.h"
#include "oled.h"
#include "rng.h"
#include "secp256k1.h"
#include "sha2.h"
#include "signatures.h"
#include "usb.h"
#include "util.h"
#include "usb21_standard.h"
#include "webusb.h"
#include "winusb.h"
#include "usb_desc.h"
#include "usb_erase.h"
#include "usb_send.h"
enum {
STATE_READY,
STATE_OPEN,
STATE_FLASHSTART,
STATE_FLASHING,
STATE_CHECK,
STATE_END,
};
static uint32_t flash_pos = 0, flash_len = 0;
static uint32_t chunk_idx = 0;
static char flash_state = STATE_READY;
static uint32_t FW_HEADER[FLASH_FWHEADER_LEN / sizeof(uint32_t)];
static uint32_t FW_CHUNK[FW_CHUNK_SIZE / sizeof(uint32_t)];
static void check_and_write_chunk(void) {
uint32_t offset = (chunk_idx == 0) ? FLASH_FWHEADER_LEN : 0;
uint32_t chunk_pos = flash_pos % FW_CHUNK_SIZE;
if (chunk_pos == 0) {
chunk_pos = FW_CHUNK_SIZE;
}
uint8_t hash[32];
SHA256_CTX ctx;
sha256_Init(&ctx);
sha256_Update(&ctx, (const uint8_t *)FW_CHUNK + offset, chunk_pos - offset);
if (chunk_pos < 64 * 1024) {
// pad with FF
for (uint32_t i = chunk_pos; i < 64 * 1024; i += 4) {
sha256_Update(&ctx, (const uint8_t *)"\xFF\xFF\xFF\xFF", 4);
}
}
sha256_Final(&ctx, hash);
const image_header *hdr = (const image_header *)FW_HEADER;
// invalid chunk sent
if (0 != memcmp(hash, hdr->hashes + chunk_idx * 32, 32)) {
// erase storage
erase_storage();
flash_state = STATE_END;
show_halt("Error installing", "firmware.");
return;
}
flash_wait_for_last_operation();
flash_clear_status_flags();
flash_unlock();
for (uint32_t i = offset / sizeof(uint32_t); i < chunk_pos / sizeof(uint32_t);
i++) {
flash_program_word(
FLASH_FWHEADER_START + chunk_idx * FW_CHUNK_SIZE + i * sizeof(uint32_t),
FW_CHUNK[i]);
}
flash_wait_for_last_operation();
flash_lock();
// all done
if (flash_len == flash_pos) {
// check remaining chunks if any
for (uint32_t i = chunk_idx + 1; i < 16; i++) {
// hash should be empty if the chunk is unused
if (!mem_is_empty(hdr->hashes + 32 * i, 32)) {
flash_state = STATE_END;
show_halt("Error installing", "firmware.");
return;
}
}
}
memzero(FW_CHUNK, sizeof(FW_CHUNK));
chunk_idx++;
}
static void rx_callback(usbd_device *dev, uint8_t ep) {
(void)ep;
static uint16_t msg_id = 0xFFFF;
static uint8_t buf[64] __attribute__((aligned(4)));
static uint32_t w;
static int wi;
static int old_was_signed;
if (usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return;
if (flash_state == STATE_END) {
return;
}
if (flash_state == STATE_READY || flash_state == STATE_OPEN ||
flash_state == STATE_FLASHSTART || flash_state == STATE_CHECK) {
if (buf[0] != '?' || buf[1] != '#' ||
buf[2] != '#') { // invalid start - discard
return;
}
// struct.unpack(">HL") => msg, size
msg_id = (buf[3] << 8) + buf[4];
}
if (flash_state == STATE_READY || flash_state == STATE_OPEN) {
if (msg_id == 0x0000) { // Initialize message (id 0)
send_msg_features(dev);
flash_state = STATE_OPEN;
return;
}
if (msg_id == 0x0037) { // GetFeatures message (id 55)
send_msg_features(dev);
return;
}
if (msg_id == 0x0001) { // Ping message (id 1)
send_msg_success(dev);
return;
}
if (msg_id == 0x0005) { // WipeDevice message (id 5)
layoutDialog(&bmp_icon_question, "Cancel", "Confirm", NULL,
"Do you really want to", "wipe the device?", NULL,
"All data will be lost.", NULL, NULL);
bool but = get_button_response();
if (but) {
erase_storage_code_progress();
flash_state = STATE_END;
layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "Device",
"successfully wiped.", NULL, "You may now",
"unplug your TREZOR.", NULL);
send_msg_success(dev);
} else {
flash_state = STATE_END;
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Device wipe",
"aborted.", NULL, "You may now", "unplug your TREZOR.",
NULL);
send_msg_failure(dev);
}
return;
}
}
if (flash_state == STATE_OPEN) {
if (msg_id == 0x0006) { // FirmwareErase message (id 6)
bool proceed = false;
if (firmware_present_new()) {
layoutDialog(&bmp_icon_question, "Abort", "Continue", NULL,
"Install new", "firmware?", NULL, "Never do this without",
"your recovery card!", NULL);
proceed = get_button_response();
} else {
proceed = true;
}
if (proceed) {
// check whether the current firmware is signed (old or new method)
if (firmware_present_new()) {
const image_header *hdr =
(const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
old_was_signed =
signatures_new_ok(hdr, NULL) & check_firmware_hashes(hdr);
} else if (firmware_present_old()) {
old_was_signed = signatures_old_ok();
} else {
old_was_signed = SIG_FAIL;
}
erase_code_progress();
send_msg_success(dev);
flash_state = STATE_FLASHSTART;
} else {
send_msg_failure(dev);
flash_state = STATE_END;
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL,
"Firmware installation", "aborted.", NULL, "You may now",
"unplug your TREZOR.", NULL);
}
return;
}
return;
}
if (flash_state == STATE_FLASHSTART) {
if (msg_id == 0x0007) { // FirmwareUpload message (id 7)
if (buf[9] != 0x0a) { // invalid contents
send_msg_failure(dev);
flash_state = STATE_END;
show_halt("Error installing", "firmware.");
return;
}
// read payload length
const uint8_t *p = buf + 10;
flash_len = readprotobufint(&p);
if (flash_len <= FLASH_FWHEADER_LEN) { // firmware is too small
send_msg_failure(dev);
flash_state = STATE_END;
show_halt("Firmware is too small.", NULL);
return;
}
if (flash_len >
FLASH_FWHEADER_LEN + FLASH_APP_LEN) { // firmware is too big
send_msg_failure(dev);
flash_state = STATE_END;
show_halt("Firmware is too big.", NULL);
return;
}
// check firmware magic
if (memcmp(p, &FIRMWARE_MAGIC_NEW, 4) != 0) {
send_msg_failure(dev);
flash_state = STATE_END;
show_halt("Wrong firmware header.", NULL);
return;
}
memzero(FW_HEADER, sizeof(FW_HEADER));
memzero(FW_CHUNK, sizeof(FW_CHUNK));
flash_state = STATE_FLASHING;
flash_pos = 0;
chunk_idx = 0;
w = 0;
while (p < buf + 64) {
w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w
wi++;
if (wi == 4) {
FW_HEADER[flash_pos / 4] = w;
flash_pos += 4;
wi = 0;
}
p++;
}
return;
}
return;
}
if (flash_state == STATE_FLASHING) {
if (buf[0] != '?') { // invalid contents
send_msg_failure(dev);
flash_state = STATE_END;
show_halt("Error installing", "firmware.");
return;
}
static uint8_t flash_anim = 0;
if (flash_anim % 32 == 4) {
layoutProgress("INSTALLING ... Please wait",
1000 * flash_pos / flash_len);
}
flash_anim++;
const uint8_t *p = buf + 1;
while (p < buf + 64 && flash_pos < flash_len) {
w = (w >> 8) | (*p << 24); // assign byte to first byte of uint32_t w
wi++;
if (wi == 4) {
if (flash_pos < FLASH_FWHEADER_LEN) {
FW_HEADER[flash_pos / 4] = w;
} else {
FW_CHUNK[(flash_pos % FW_CHUNK_SIZE) / 4] = w;
}
flash_pos += 4;
wi = 0;
// finished the whole chunk
if (flash_pos % FW_CHUNK_SIZE == 0) {
check_and_write_chunk();
}
}
p++;
}
// flashing done
if (flash_pos == flash_len) {
// flush remaining data in the last chunk
if (flash_pos % FW_CHUNK_SIZE > 0) {
check_and_write_chunk();
}
flash_state = STATE_CHECK;
const image_header *hdr = (const image_header *)FW_HEADER;
if (SIG_OK != signatures_new_ok(hdr, NULL)) {
send_msg_buttonrequest_firmwarecheck(dev);
return;
}
} else {
return;
}
}
if (flash_state == STATE_CHECK) {
// use the firmware header from RAM
const image_header *hdr = (const image_header *)FW_HEADER;
bool hash_check_ok;
// show fingerprint of unsigned firmware
if (SIG_OK != signatures_new_ok(hdr, NULL)) {
if (msg_id != 0x001B) { // ButtonAck message (id 27)
return;
}
uint8_t hash[32];
compute_firmware_fingerprint(hdr, hash);
layoutFirmwareFingerprint(hash);
hash_check_ok = get_button_response();
} else {
hash_check_ok = true;
}
layoutProgress("INSTALLING ... Please wait", 1000);
// wipe storage if:
// 1) old firmware was unsigned or not present
// 2) signatures are not OK
// 3) hashes are not OK
if (SIG_OK != old_was_signed || SIG_OK != signatures_new_ok(hdr, NULL) ||
SIG_OK != check_firmware_hashes(hdr)) {
// erase storage
erase_storage();
// check erasure
uint8_t hash[32];
sha256_Raw(FLASH_PTR(FLASH_STORAGE_START), FLASH_STORAGE_LEN, hash);
if (memcmp(hash,
"\x2d\x86\x4c\x0b\x78\x9a\x43\x21\x4e\xee\x85\x24\xd3\x18\x20"
"\x75\x12\x5e\x5c\xa2\xcd\x52\x7f\x35\x82\xec\x87\xff\xd9\x40"
"\x76\xbc",
32) != 0) {
send_msg_failure(dev);
show_halt("Error installing", "firmware.");
return;
}
}
flash_wait_for_last_operation();
flash_clear_status_flags();
flash_unlock();
// write firmware header only when hash was confirmed
if (hash_check_ok) {
for (size_t i = 0; i < FLASH_FWHEADER_LEN / sizeof(uint32_t); i++) {
flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t),
FW_HEADER[i]);
}
} else {
for (size_t i = 0; i < FLASH_FWHEADER_LEN / sizeof(uint32_t); i++) {
flash_program_word(FLASH_FWHEADER_START + i * sizeof(uint32_t), 0);
}
}
flash_wait_for_last_operation();
flash_lock();
flash_state = STATE_END;
if (hash_check_ok) {
layoutDialog(&bmp_icon_ok, NULL, NULL, NULL, "New firmware",
"successfully installed.", NULL, "You may now",
"unplug your TREZOR.", NULL);
send_msg_success(dev);
shutdown();
} else {
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, "Firmware installation",
"aborted.", NULL, "You need to repeat", "the procedure with",
"the correct firmware.");
send_msg_failure(dev);
shutdown();
}
return;
}
}
static void set_config(usbd_device *dev, uint16_t wValue) {
(void)wValue;
usbd_ep_setup(dev, ENDPOINT_ADDRESS_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0);
usbd_ep_setup(dev, ENDPOINT_ADDRESS_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64,
rx_callback);
}
static usbd_device *usbd_dev;
static uint8_t usbd_control_buffer[256] __attribute__((aligned(2)));
static const struct usb_device_capability_descriptor *capabilities[] = {
(const struct usb_device_capability_descriptor
*)&webusb_platform_capability_descriptor,
};
static const struct usb_bos_descriptor bos_descriptor = {
.bLength = USB_DT_BOS_SIZE,
.bDescriptorType = USB_DT_BOS,
.bNumDeviceCaps = sizeof(capabilities) / sizeof(capabilities[0]),
.capabilities = capabilities};
static void usbInit(void) {
usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings,
sizeof(usb_strings) / sizeof(const char *),
usbd_control_buffer, sizeof(usbd_control_buffer));
usbd_register_set_config_callback(usbd_dev, set_config);
usb21_setup(usbd_dev, &bos_descriptor);
webusb_setup(usbd_dev, "trezor.io/start");
winusb_setup(usbd_dev, USB_INTERFACE_INDEX_MAIN);
}
static void checkButtons(void) {
static bool btn_left = false, btn_right = false, btn_final = false;
if (btn_final) {
return;
}
uint16_t state = gpio_port_read(BTN_PORT);
if ((state & (BTN_PIN_YES | BTN_PIN_NO)) != (BTN_PIN_YES | BTN_PIN_NO)) {
if ((state & BTN_PIN_NO) != BTN_PIN_NO) {
btn_left = true;
}
if ((state & BTN_PIN_YES) != BTN_PIN_YES) {
btn_right = true;
}
}
if (btn_left) {
oledBox(0, 0, 3, 3, true);
}
if (btn_right) {
oledBox(OLED_WIDTH - 4, 0, OLED_WIDTH - 1, 3, true);
}
if (btn_left || btn_right) {
oledRefresh();
}
if (btn_left && btn_right) {
btn_final = true;
}
}
void usbLoop(void) {
bool firmware_present = firmware_present_new();
usbInit();
for (;;) {
usbd_poll(usbd_dev);
if (!firmware_present &&
(flash_state == STATE_READY || flash_state == STATE_OPEN)) {
checkButtons();
}
}
}

25
legacy/bootloader/usb.h Normal file
View File

@ -0,0 +1,25 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __USB_H__
#define __USB_H__
void usbLoop(void);
#endif

View File

@ -0,0 +1,77 @@
#define USB_INTERFACE_INDEX_MAIN 0
#define ENDPOINT_ADDRESS_IN (0x81)
#define ENDPOINT_ADDRESS_OUT (0x01)
static const struct usb_device_descriptor dev_descr = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0210,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x1209,
.idProduct = 0x53c0,
.bcdDevice = 0x0100,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
static const struct usb_endpoint_descriptor endpoints[2] = {
{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = ENDPOINT_ADDRESS_IN,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 64,
.bInterval = 1,
},
{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = ENDPOINT_ADDRESS_OUT,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 64,
.bInterval = 1,
}};
static const struct usb_interface_descriptor iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = USB_INTERFACE_INDEX_MAIN,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
.endpoint = endpoints,
.extra = NULL,
.extralen = 0,
}};
static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = iface,
}};
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char *usb_strings[] = {
"SatoshiLabs",
"TREZOR",
"000000000000000000000000",
};

View File

@ -0,0 +1,49 @@
static void erase_storage_code_progress(void) {
flash_wait_for_last_operation();
flash_clear_status_flags();
flash_unlock();
// erase storage area
for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST;
i++) {
layoutProgress("WIPING ... Please wait",
1000 * (i - FLASH_STORAGE_SECTOR_FIRST) /
(FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST));
flash_erase_sector(i, FLASH_CR_PROGRAM_X32);
}
// erase code area
for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) {
layoutProgress("WIPING ... Please wait",
1000 * (i - FLASH_STORAGE_SECTOR_FIRST) /
(FLASH_CODE_SECTOR_LAST - FLASH_STORAGE_SECTOR_FIRST));
flash_erase_sector(i, FLASH_CR_PROGRAM_X32);
}
flash_wait_for_last_operation();
flash_lock();
}
static void erase_code_progress(void) {
flash_wait_for_last_operation();
flash_clear_status_flags();
flash_unlock();
for (int i = FLASH_CODE_SECTOR_FIRST; i <= FLASH_CODE_SECTOR_LAST; i++) {
layoutProgress("PREPARING ... Please wait",
1000 * (i - FLASH_CODE_SECTOR_FIRST) /
(FLASH_CODE_SECTOR_LAST - FLASH_CODE_SECTOR_FIRST));
flash_erase_sector(i, FLASH_CR_PROGRAM_X32);
}
layoutProgress("INSTALLING ... Please wait", 0);
flash_wait_for_last_operation();
flash_lock();
}
static void erase_storage(void) {
flash_wait_for_last_operation();
flash_clear_status_flags();
flash_unlock();
for (int i = FLASH_STORAGE_SECTOR_FIRST; i <= FLASH_STORAGE_SECTOR_LAST;
i++) {
flash_erase_sector(i, FLASH_CR_PROGRAM_X32);
}
flash_wait_for_last_operation();
flash_lock();
}

View File

@ -0,0 +1,92 @@
static void send_msg_success(usbd_device *dev) {
uint8_t response[64];
memzero(response, sizeof(response));
// response: Success message (id 2), payload len 0
memcpy(response,
// header
"?##"
// msg_id
"\x00\x02"
// msg_size
"\x00\x00\x00\x00",
9);
while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {
}
}
static void send_msg_failure(usbd_device *dev) {
uint8_t response[64];
memzero(response, sizeof(response));
// response: Failure message (id 3), payload len 2
// - code = 99 (Failure_FirmwareError)
memcpy(response,
// header
"?##"
// msg_id
"\x00\x03"
// msg_size
"\x00\x00\x00\x02"
// data
"\x08"
"\x63",
11);
while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {
}
}
static void send_msg_features(usbd_device *dev) {
uint8_t response[64];
memzero(response, sizeof(response));
// response: Features message (id 17), payload len 25
// - vendor = "trezor.io"
// - major_version = VERSION_MAJOR
// - minor_version = VERSION_MINOR
// - patch_version = VERSION_PATCH
// - bootloader_mode = True
// - firmware_present = True/False
// - model = "1"
memcpy(response,
// header
"?##"
// msg_id
"\x00\x11"
// msg_size
"\x00\x00\x00\x16"
// data
"\x0a"
"\x09"
"trezor.io"
"\x10" VERSION_MAJOR_CHAR "\x18" VERSION_MINOR_CHAR
"\x20" VERSION_PATCH_CHAR
"\x28"
"\x01"
"\x90\x01"
"\x00"
"\xaa"
"\x01"
"1",
34);
response[30] = firmware_present_new() ? 0x01 : 0x00;
while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {
}
}
static void send_msg_buttonrequest_firmwarecheck(usbd_device *dev) {
uint8_t response[64];
memzero(response, sizeof(response));
// response: ButtonRequest message (id 26), payload len 2
// - code = ButtonRequest_FirmwareCheck (9)
memcpy(response,
// header
"?##"
// msg_id
"\x00\x1a"
// msg_size
"\x00\x00\x00\x02"
// data
"\x08"
"\x09",
11);
while (usbd_ep_write_packet(dev, ENDPOINT_ADDRESS_IN, response, 64) != 64) {
}
}

19
legacy/build.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash
set -e
BOOTLOADER_COMMIT=${1:-HEAD}
FIRMWARE_COMMIT=${2:-HEAD}
if [ "$BOOTLOADER_COMMIT" = "EMU" ]; then
export EMULATOR=1
fi
if [ "$EMULATOR" = 1 ]; then
IMAGE=trezor-mcu-emulator
else
IMAGE=trezor-mcu-build
fi
docker build -t "$IMAGE" --build-arg EMULATOR=$EMULATOR .
docker run -it -v $(pwd):/src:z --user="$(stat -c "%u:%g" .)" "$IMAGE" \
/src/script/fullbuild "$BOOTLOADER_COMMIT" "$FIRMWARE_COMMIT"

70
legacy/buttons.c Normal file
View File

@ -0,0 +1,70 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "buttons.h"
struct buttonState button;
#if !EMULATOR
uint16_t buttonRead(void) { return gpio_port_read(BTN_PORT); }
#endif
void buttonUpdate() {
static uint16_t last_state = BTN_PIN_YES | BTN_PIN_NO;
uint16_t state = buttonRead();
if ((state & BTN_PIN_YES) == 0) { // Yes button is down
if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down
if (button.YesDown < 2000000000) button.YesDown++;
button.YesUp = false;
} else { // last Yes was up
button.YesDown = 0;
button.YesUp = false;
}
} else { // Yes button is up
if ((last_state & BTN_PIN_YES) == 0) { // last Yes was down
button.YesDown = 0;
button.YesUp = true;
} else { // last Yes was up
button.YesDown = 0;
button.YesUp = false;
}
}
if ((state & BTN_PIN_NO) == 0) { // No button is down
if ((last_state & BTN_PIN_NO) == 0) { // last No was down
if (button.NoDown < 2000000000) button.NoDown++;
button.NoUp = false;
} else { // last No was up
button.NoDown = 0;
button.NoUp = false;
}
} else { // No button is up
if ((last_state & BTN_PIN_NO) == 0) { // last No was down
button.NoDown = 0;
button.NoUp = true;
} else { // last No was up
button.NoDown = 0;
button.NoUp = false;
}
}
last_state = state;
}

50
legacy/buttons.h Normal file
View File

@ -0,0 +1,50 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BUTTONS_H__
#define __BUTTONS_H__
#include <libopencm3/stm32/gpio.h>
#include <stdbool.h>
struct buttonState {
volatile bool YesUp;
volatile int YesDown;
volatile bool NoUp;
volatile int NoDown;
};
extern struct buttonState button;
uint16_t buttonRead(void);
void buttonUpdate(void);
#ifndef BTN_PORT
#define BTN_PORT GPIOC
#endif
#ifndef BTN_PIN_YES
#define BTN_PIN_YES GPIO2
#endif
#ifndef BTN_PIN_NO
#define BTN_PIN_NO GPIO5
#endif
#endif

83
legacy/common.c Normal file
View File

@ -0,0 +1,83 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <stdio.h>
#include "bitmaps.h"
#include "firmware/usb.h"
#include "layout.h"
#include "oled.h"
#include "rng.h"
#include "util.h"
uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
void __attribute__((noreturn))
__fatal_error(const char *expr, const char *msg, const char *file, int line_num,
const char *func) {
const BITMAP *icon = &bmp_icon_error;
char line[128] = {0};
int y = icon->height + 3;
oledClear();
oledDrawBitmap(0, 0, icon);
oledDrawStringCenter(OLED_WIDTH / 2, (icon->height - FONT_HEIGHT) / 2 + 1,
"FATAL ERROR", FONT_STANDARD);
snprintf(line, sizeof(line), "Expr: %s", expr ? expr : "(null)");
oledDrawString(0, y, line, FONT_STANDARD);
y += FONT_HEIGHT + 1;
snprintf(line, sizeof(line), "Msg: %s", msg ? msg : "(null)");
oledDrawString(0, y, line, FONT_STANDARD);
y += FONT_HEIGHT + 1;
const char *label = "File: ";
snprintf(line, sizeof(line), "%s:%d", file ? file : "(null)", line_num);
oledDrawStringRight(OLED_WIDTH - 1, y, line, FONT_STANDARD);
oledBox(0, y, oledStringWidth(label, FONT_STANDARD), y + FONT_HEIGHT, false);
oledDrawString(0, y, label, FONT_STANDARD);
y += FONT_HEIGHT + 1;
snprintf(line, sizeof(line), "Func: %s", func ? func : "(null)");
oledDrawString(0, y, line, FONT_STANDARD);
y += FONT_HEIGHT + 1;
oledDrawString(0, y, "Contact TREZOR support.", FONT_STANDARD);
oledRefresh();
shutdown();
}
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4) {
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, line1, line2, line3, line4,
"Please unplug", "the device.");
shutdown();
}
#ifndef NDEBUG
void __assert_func(const char *file, int line, const char *func,
const char *expr) {
__fatal_error(expr, "assert failed", file, line, func);
}
#endif
void hal_delay(uint32_t ms) { usbSleep(ms); }

43
legacy/common.h Normal file
View File

@ -0,0 +1,43 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __TREZORHAL_COMMON_H__
#define __TREZORHAL_COMMON_H__
#include <stdint.h>
#include "secbool.h"
#define HW_ENTROPY_LEN (12 + 32)
extern uint8_t HW_ENTROPY_DATA[HW_ENTROPY_LEN];
void __attribute__((noreturn))
__fatal_error(const char *expr, const char *msg, const char *file, int line,
const char *func);
void __attribute__((noreturn))
error_shutdown(const char *line1, const char *line2, const char *line3,
const char *line4);
#define ensure(expr, msg) \
(((expr) == sectrue) \
? (void)0 \
: __fatal_error(#expr, msg, __FILE__, __LINE__, __func__))
void hal_delay(uint32_t ms);
#endif

18
legacy/demo/Makefile Normal file
View File

@ -0,0 +1,18 @@
APPVER = 1.0.0
NAME = demo
OBJS += demo.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
OBJS += ../vendor/trezor-crypto/memzero.o
include ../Makefile.include

309
legacy/demo/demo.c Normal file
View File

@ -0,0 +1,309 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopencm3/usb/hid.h>
#include <libopencm3/usb/usbd.h>
#include <string.h>
#include "bitmaps.h"
#include "buttons.h"
#include "hmac.h"
#include "layout.h"
#include "oled.h"
#include "pbkdf2.h"
#include "rng.h"
#include "setup.h"
const int states = 2;
int state = 0;
int frame = 0;
uint8_t seed[128];
uint8_t *pass = (uint8_t *)"meadow";
uint32_t passlen;
uint8_t *salt = (uint8_t *)"TREZOR";
uint32_t saltlen;
static const struct usb_device_descriptor dev_descr = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0200,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x1209,
.idProduct = 0x53c1,
.bcdDevice = 0x0100,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};
/* got via usbhid-dump from CP2110 */
static const uint8_t hid_report_descriptor[] = {
0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x09, 0x01, 0x75, 0x08, 0x95,
0x40, 0x26, 0xFF, 0x00, 0x15, 0x00, 0x85, 0x01, 0x95, 0x01, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x02, 0x95, 0x02, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x03, 0x95, 0x03, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x04, 0x95, 0x04, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x05, 0x95, 0x05, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x06, 0x95, 0x06, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x07, 0x95, 0x07, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x08, 0x95, 0x08, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x09, 0x95, 0x09, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0A, 0x95, 0x0A, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0B, 0x95, 0x0B, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0C, 0x95, 0x0C, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0D, 0x95, 0x0D, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0E, 0x95, 0x0E, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x0F, 0x95, 0x0F, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x10, 0x95, 0x10, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x11, 0x95, 0x11, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x12, 0x95, 0x12, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x13, 0x95, 0x13, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x14, 0x95, 0x14, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x15, 0x95, 0x15, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x16, 0x95, 0x16, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x17, 0x95, 0x17, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x18, 0x95, 0x18, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x19, 0x95, 0x19, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1A, 0x95, 0x1A, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1B, 0x95, 0x1B, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1C, 0x95, 0x1C, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1D, 0x95, 0x1D, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1E, 0x95, 0x1E, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x1F, 0x95, 0x1F, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x20, 0x95, 0x20, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x21, 0x95, 0x21, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x22, 0x95, 0x22, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x23, 0x95, 0x23, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x24, 0x95, 0x24, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x25, 0x95, 0x25, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x26, 0x95, 0x26, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x27, 0x95, 0x27, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x28, 0x95, 0x28, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x29, 0x95, 0x29, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2A, 0x95, 0x2A, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2B, 0x95, 0x2B, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2C, 0x95, 0x2C, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2D, 0x95, 0x2D, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2E, 0x95, 0x2E, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x2F, 0x95, 0x2F, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x30, 0x95, 0x30, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x31, 0x95, 0x31, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x32, 0x95, 0x32, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x33, 0x95, 0x33, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x34, 0x95, 0x34, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x35, 0x95, 0x35, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x36, 0x95, 0x36, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x37, 0x95, 0x37, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x38, 0x95, 0x38, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x39, 0x95, 0x39, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3A, 0x95, 0x3A, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3B, 0x95, 0x3B, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3C, 0x95, 0x3C, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3D, 0x95, 0x3D, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3E, 0x95, 0x3E, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x3F, 0x95, 0x3F, 0x09, 0x01,
0x81, 0x02, 0x09, 0x01, 0x91, 0x02, 0x85, 0x40, 0x95, 0x01, 0x09, 0x01,
0xB1, 0x02, 0x85, 0x41, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x42,
0x95, 0x06, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x43, 0x95, 0x01, 0x09, 0x01,
0xB1, 0x02, 0x85, 0x44, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x45,
0x95, 0x04, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x46, 0x95, 0x02, 0x09, 0x01,
0xB1, 0x02, 0x85, 0x47, 0x95, 0x02, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x50,
0x95, 0x08, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x51, 0x95, 0x01, 0x09, 0x01,
0xB1, 0x02, 0x85, 0x52, 0x95, 0x01, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x60,
0x95, 0x0A, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x61, 0x95, 0x3F, 0x09, 0x01,
0xB1, 0x02, 0x85, 0x62, 0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x63,
0x95, 0x3F, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x64, 0x95, 0x3F, 0x09, 0x01,
0xB1, 0x02, 0x85, 0x65, 0x95, 0x3E, 0x09, 0x01, 0xB1, 0x02, 0x85, 0x66,
0x95, 0x13, 0x09, 0x01, 0xB1, 0x02, 0xC0,
};
static const struct {
struct usb_hid_descriptor hid_descriptor;
struct {
uint8_t bReportDescriptorType;
uint16_t wDescriptorLength;
} __attribute__((packed)) hid_report;
} __attribute__((packed))
hid_function = {.hid_descriptor =
{
.bLength = sizeof(hid_function),
.bDescriptorType = USB_DT_HID,
.bcdHID = 0x0111,
.bCountryCode = 0,
.bNumDescriptors = 1,
},
.hid_report = {
.bReportDescriptorType = USB_DT_REPORT,
.wDescriptorLength = sizeof(hid_report_descriptor),
}};
static const struct usb_endpoint_descriptor hid_endpoints[2] = {
{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 64,
.bInterval = 1,
},
{
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x02,
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
.wMaxPacketSize = 64,
.bInterval = 1,
}};
static const struct usb_interface_descriptor hid_iface[] = {{
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.iInterface = 0,
.endpoint = hid_endpoints,
.extra = &hid_function,
.extralen = sizeof(hid_function),
}};
static const struct usb_interface ifaces[] = {{
.num_altsetting = 1,
.altsetting = hid_iface,
}};
static const struct usb_config_descriptor config = {
.bLength = USB_DT_CONFIGURATION_SIZE,
.bDescriptorType = USB_DT_CONFIGURATION,
.wTotalLength = 0,
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 0,
.bmAttributes = 0x80,
.bMaxPower = 0x32,
.interface = ifaces,
};
static const char *usb_strings[] = {
"SatoshiLabs",
"TREZOR",
"01234567",
};
static enum usbd_request_return_codes hid_control_request(
usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
usbd_control_complete_callback *complete) {
(void)complete;
(void)dev;
if ((req->bmRequestType != 0x81) ||
(req->bRequest != USB_REQ_GET_DESCRIPTOR) || (req->wValue != 0x2200))
return 0;
/* Handle the HID report descriptor. */
*buf = (uint8_t *)hid_report_descriptor;
*len = sizeof(hid_report_descriptor);
return 1;
}
static void hid_rx_callback(usbd_device *dev, uint8_t ep) {
(void)dev;
(void)ep;
}
static void hid_set_config(usbd_device *dev, uint16_t wValue) {
(void)wValue;
usbd_ep_setup(dev, 0x81, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0);
usbd_ep_setup(dev, 0x02, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_rx_callback);
usbd_register_control_callback(
dev, USB_REQ_TYPE_STANDARD | USB_REQ_TYPE_INTERFACE,
USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, hid_control_request);
}
static usbd_device *usbd_dev;
static uint8_t usbd_control_buffer[128];
void usbInit(void) {
usbd_dev = usbd_init(&otgfs_usb_driver, &dev_descr, &config, usb_strings, 3,
usbd_control_buffer, sizeof(usbd_control_buffer));
usbd_register_set_config_callback(usbd_dev, hid_set_config);
}
int main(void) {
#ifndef APPVER
setup();
__stack_chk_guard = random32(); // this supports compiler provided
// unpredictable stack protection checks
oledInit();
#else
setupApp();
__stack_chk_guard = random32(); // this supports compiler provided
// unpredictable stack protection checks
#endif
usbInit();
passlen = strlen((char *)pass);
saltlen = strlen((char *)salt);
for (;;) {
frame = 0;
switch (state) {
case 0:
oledClear();
oledDrawBitmap(40, 0, &bmp_logo64);
break;
}
oledRefresh();
do {
usbd_poll(usbd_dev);
switch (state) {
case 1:
layoutProgress("WORKING", frame % 41 * 25);
pbkdf2_hmac_sha512(pass, passlen, salt, saltlen, 100, seed, 64);
usbd_ep_write_packet(usbd_dev, 0x81, seed, 64);
break;
}
buttonUpdate();
frame += 1;
} while (!button.YesUp && !button.NoUp);
if (button.YesUp) {
state = (state + 1) % states;
oledSwipeLeft();
} else {
state = (state + states - 1) % states;
oledSwipeRight();
}
}
return 0;
}

17
legacy/emulator/Makefile Normal file
View File

@ -0,0 +1,17 @@
EMULATOR := 1
OBJS += setup.o
OBJS += buttons.o
OBJS += memory.o
OBJS += oled.o
OBJS += rng.o
OBJS += timer.o
OBJS += udp.o
OBJS += strl.o
libemulator.a: $(OBJS)
$(AR) rcs $@ $(OBJS)
include ../Makefile.include

40
legacy/emulator/buttons.c Normal file
View File

@ -0,0 +1,40 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "buttons.h"
#if !HEADLESS
#include <SDL.h>
#endif
uint16_t buttonRead(void) {
uint16_t state = 0;
#if !HEADLESS
const uint8_t *scancodes = SDL_GetKeyboardState(NULL);
if (scancodes[SDL_SCANCODE_LEFT]) {
state |= BTN_PIN_NO;
}
if (scancodes[SDL_SCANCODE_RIGHT]) {
state |= BTN_PIN_YES;
}
#endif
return ~state;
}

View File

@ -0,0 +1,38 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __EMULATOR_H__
#define __EMULATOR_H__
#if EMULATOR
#include "strl.h"
#include <stddef.h>
void emulatorPoll(void);
void emulatorRandom(void *buffer, size_t size);
void emulatorSocketInit(void);
size_t emulatorSocketRead(int *iface, void *buffer, size_t size);
size_t emulatorSocketWrite(int iface, const void *buffer, size_t size);
#endif
#endif

136
legacy/emulator/memory.c Normal file
View File

@ -0,0 +1,136 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include "memory.h"
void flash_lock(void) { sync(); }
void flash_unlock(void) {}
void flash_clear_status_flags(void) {}
void flash_lock_option_bytes(void) {}
void flash_unlock_option_bytes(void) {}
void flash_program_option_bytes(uint32_t data) { (void)data; }
static ssize_t sector_to_offset(uint8_t sector) {
switch (sector) {
case 0:
return 0x0;
case 1:
return 0x4000;
case 2:
return 0x8000;
case 3:
return 0xC000;
case 4:
return 0x10000;
case 5:
return 0x20000;
case 6:
return 0x40000;
case 7:
return 0x60000;
case 8:
return 0x80000;
default:
return -1;
}
}
static void *sector_to_address(uint8_t sector) {
ssize_t offset = sector_to_offset(sector);
if (offset < 0) {
return NULL;
}
return (void *)FLASH_PTR(FLASH_ORIGIN + offset);
}
static ssize_t sector_to_size(uint8_t sector) {
ssize_t start = sector_to_offset(sector);
if (start < 0) {
return -1;
}
ssize_t end = sector_to_offset(sector + 1);
if (end < 0) {
return -1;
}
return end - start;
}
void flash_erase_sector(uint8_t sector, uint32_t program_size) {
(void)program_size;
void *address = sector_to_address(sector);
if (address == NULL) {
return;
}
ssize_t size = sector_to_size(sector);
if (size < 0) {
return;
}
memset(address, 0xFF, size);
}
void flash_erase_all_sectors(uint32_t program_size) {
(void)program_size;
memset(emulator_flash_base, 0xFF, FLASH_TOTAL_SIZE);
}
void flash_program_word(uint32_t address, uint32_t data) {
*(volatile uint32_t *)FLASH_PTR(address) = data;
}
void flash_program_byte(uint32_t address, uint8_t data) {
*(volatile uint8_t *)FLASH_PTR(address) = data;
}
static bool flash_locked = true;
void svc_flash_unlock(void) {
assert(flash_locked);
flash_locked = false;
}
void svc_flash_program(uint32_t size) {
(void)size;
assert(!flash_locked);
}
void svc_flash_erase_sector(uint16_t sector) {
assert(!flash_locked);
assert(sector >= FLASH_STORAGE_SECTOR_FIRST &&
sector <= FLASH_STORAGE_SECTOR_LAST);
flash_erase_sector(sector, 3);
}
uint32_t svc_flash_lock(void) {
assert(!flash_locked);
flash_locked = true;
sync();
return 0;
}

150
legacy/emulator/oled.c Normal file
View File

@ -0,0 +1,150 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "oled.h"
#if HEADLESS
void oledInit(void) {}
void oledRefresh(void) {}
void emulatorPoll(void) {}
#else
#include <SDL.h>
static SDL_Renderer *renderer = NULL;
static SDL_Texture *texture = NULL;
static SDL_Rect dstrect;
#define ENV_OLED_FULLSCREEN "TREZOR_OLED_FULLSCREEN"
#define ENV_OLED_SCALE "TREZOR_OLED_SCALE"
static int emulatorFullscreen(void) {
const char *variable = getenv(ENV_OLED_FULLSCREEN);
if (!variable) {
return 0;
}
return atoi(variable);
}
static int emulatorScale(void) {
const char *variable = getenv(ENV_OLED_SCALE);
if (!variable) {
return 1;
}
int scale = atoi(variable);
if (scale >= 1 && scale <= 16) {
return scale;
}
return 1;
}
void oledInit(void) {
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "Failed to initialize SDL: %s\n", SDL_GetError());
exit(1);
}
atexit(SDL_Quit);
int scale = emulatorScale();
int fullscreen = emulatorFullscreen();
SDL_Window *window = SDL_CreateWindow(
"TREZOR", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
OLED_WIDTH * scale, OLED_HEIGHT * scale,
fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
if (window == NULL) {
fprintf(stderr, "Failed to create window: %s\n", SDL_GetError());
exit(1);
}
renderer = SDL_CreateRenderer(window, -1, 0);
if (!renderer) {
fprintf(stderr, "Failed to create renderer: %s\n", SDL_GetError());
exit(1);
}
if (fullscreen) {
SDL_DisplayMode current_mode;
if (SDL_GetCurrentDisplayMode(0, &current_mode) != 0) {
fprintf(stderr, "Failed to get current display mode: %s\n",
SDL_GetError());
exit(1);
}
dstrect.x = (current_mode.w - OLED_WIDTH * scale) / 2;
dstrect.y = (current_mode.h - OLED_HEIGHT * scale) / 2;
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
SDL_ShowCursor(SDL_DISABLE);
} else {
dstrect.x = 0;
dstrect.y = 0;
}
dstrect.w = OLED_WIDTH * scale;
dstrect.h = OLED_HEIGHT * scale;
texture =
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING, OLED_WIDTH, OLED_HEIGHT);
oledClear();
oledRefresh();
}
void oledRefresh(void) {
/* Draw triangle in upper right corner */
oledInvertDebugLink();
const uint8_t *buffer = oledGetBuffer();
static uint32_t data[OLED_HEIGHT][OLED_WIDTH];
for (size_t i = 0; i < OLED_BUFSIZE; i++) {
int x = (OLED_BUFSIZE - 1 - i) % OLED_WIDTH;
int y = (OLED_BUFSIZE - 1 - i) / OLED_WIDTH * 8 + 7;
for (uint8_t shift = 0; shift < 8; shift++, y--) {
bool set = (buffer[i] >> shift) & 1;
data[y][x] = set ? 0xFFFFFFFF : 0xFF000000;
}
}
SDL_UpdateTexture(texture, NULL, data, OLED_WIDTH * sizeof(uint32_t));
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
SDL_RenderPresent(renderer);
/* Return it back */
oledInvertDebugLink();
}
void emulatorPoll(void) {
SDL_Event event;
if (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
exit(1);
}
}
}
#endif

32
legacy/emulator/rng.c Normal file
View File

@ -0,0 +1,32 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "rng.h"
uint32_t random32(void) {
static uint32_t last = 0;
uint32_t new;
do {
emulatorRandom(&new, sizeof(new));
} while (last == new);
last = new;
return new;
}

108
legacy/emulator/setup.c Normal file
View File

@ -0,0 +1,108 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <libopencm3/stm32/flash.h>
#include "memory.h"
#include "oled.h"
#include "rng.h"
#include "setup.h"
#include "timer.h"
#define EMULATOR_FLASH_FILE "emulator.img"
#ifndef RANDOM_DEV_FILE
#define RANDOM_DEV_FILE "/dev/urandom"
#endif
uint8_t *emulator_flash_base = NULL;
uint32_t __stack_chk_guard;
static int random_fd = -1;
static void setup_urandom(void);
static void setup_flash(void);
void setup(void) {
setup_urandom();
setup_flash();
}
void __attribute__((noreturn)) shutdown(void) {
for (;;) pause();
}
void emulatorRandom(void *buffer, size_t size) {
ssize_t n, len = 0;
do {
n = read(random_fd, (char *)buffer + len, size - len);
if (n < 0) {
perror("Failed to read " RANDOM_DEV_FILE);
exit(1);
}
len += n;
} while (len != (ssize_t)size);
}
static void setup_urandom(void) {
random_fd = open(RANDOM_DEV_FILE, O_RDONLY);
if (random_fd < 0) {
perror("Failed to open " RANDOM_DEV_FILE);
exit(1);
}
}
static void setup_flash(void) {
int fd = open(EMULATOR_FLASH_FILE, O_RDWR | O_SYNC | O_CREAT, 0644);
if (fd < 0) {
perror("Failed to open flash emulation file");
exit(1);
}
off_t length = lseek(fd, 0, SEEK_END);
if (length < 0) {
perror("Failed to read length of flash emulation file");
exit(1);
}
emulator_flash_base =
mmap(NULL, FLASH_TOTAL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (emulator_flash_base == MAP_FAILED) {
perror("Failed to map flash emulation file");
exit(1);
}
if (length < FLASH_TOTAL_SIZE) {
if (ftruncate(fd, FLASH_TOTAL_SIZE) != 0) {
perror("Failed to initialize flash emulation file");
exit(1);
}
/* Initialize the flash */
flash_erase_all_sectors(FLASH_CR_PROGRAM_X32);
}
}

44
legacy/emulator/strl.c Normal file
View File

@ -0,0 +1,44 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "strl.h"
#include "util.h"
#include <string.h>
#if (!defined __APPLE__) && (!defined HAVE_STRLCPY)
size_t strlcpy(char *dst, const char *src, size_t size) {
size_t ret = strlen(src);
if (size) {
size_t len = MIN(ret, size - 1);
memcpy(dst, src, len);
dst[len] = '\0';
}
return ret;
}
size_t strlcat(char *dst, const char *src, size_t size) {
size_t n = strnlen(dst, size);
return n + strlcpy(&dst[n], src, size - n);
}
#endif

30
legacy/emulator/strl.h Normal file
View File

@ -0,0 +1,30 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STRL_H__
#define __STRL_H__
#include <stddef.h>
#if (!defined __APPLE__) && (!defined HAVE_STRLCPY)
size_t strlcpy(char *dst, const char *src, size_t size);
size_t strlcat(char *dst, const char *src, size_t size);
#endif
#endif

32
legacy/emulator/timer.c Normal file
View File

@ -0,0 +1,32 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <time.h>
#include "timer.h"
void timer_init(void) {}
uint32_t timer_ms(void) {
struct timespec t;
clock_gettime(CLOCK_MONOTONIC, &t);
uint32_t msec = t.tv_sec * 1000 + (t.tv_nsec / 1000000);
return msec;
}

127
legacy/emulator/udp.c Normal file
View File

@ -0,0 +1,127 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#define TREZOR_UDP_PORT 21324
struct usb_socket {
int fd;
struct sockaddr_in from;
socklen_t fromlen;
};
static struct usb_socket usb_main;
static struct usb_socket usb_debug;
static int socket_setup(int port) {
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
perror("Failed to create socket");
exit(1);
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
perror("Failed to bind socket");
exit(1);
}
return fd;
}
static size_t socket_write(struct usb_socket *sock, const void *buffer,
size_t size) {
if (sock->fromlen > 0) {
ssize_t n = sendto(sock->fd, buffer, size, MSG_DONTWAIT,
(const struct sockaddr *)&sock->from, sock->fromlen);
if (n < 0 || ((size_t)n) != size) {
perror("Failed to write socket");
return 0;
}
}
return size;
}
static size_t socket_read(struct usb_socket *sock, void *buffer, size_t size) {
sock->fromlen = sizeof(sock->from);
ssize_t n = recvfrom(sock->fd, buffer, size, MSG_DONTWAIT,
(struct sockaddr *)&sock->from, &sock->fromlen);
if (n < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("Failed to read socket");
}
return 0;
}
static const char msg_ping[] = {'P', 'I', 'N', 'G', 'P', 'I', 'N', 'G'};
static const char msg_pong[] = {'P', 'O', 'N', 'G', 'P', 'O', 'N', 'G'};
if (n == sizeof(msg_ping) &&
memcmp(buffer, msg_ping, sizeof(msg_ping)) == 0) {
socket_write(sock, msg_pong, sizeof(msg_pong));
return 0;
}
return n;
}
void emulatorSocketInit(void) {
usb_main.fd = socket_setup(TREZOR_UDP_PORT);
usb_main.fromlen = 0;
usb_debug.fd = socket_setup(TREZOR_UDP_PORT + 1);
usb_debug.fromlen = 0;
}
size_t emulatorSocketRead(int *iface, void *buffer, size_t size) {
size_t n = socket_read(&usb_main, buffer, size);
if (n > 0) {
*iface = 0;
return n;
}
n = socket_read(&usb_debug, buffer, size);
if (n > 0) {
*iface = 1;
return n;
}
return 0;
}
size_t emulatorSocketWrite(int iface, const void *buffer, size_t size) {
if (iface == 0) {
return socket_write(&usb_main, buffer, size);
}
if (iface == 1) {
return socket_write(&usb_debug, buffer, size);
}
return 0;
}

6
legacy/firmware/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
coin_info.[ch]
nem_mosaics.[ch]
ethereum_networks.h
ethereum_tokens.[ch]
bl_data.h

178
legacy/firmware/ChangeLog Normal file
View File

@ -0,0 +1,178 @@
Version 1.8.0
* Stable release, optional update
* Security improvements
* Upgraded to new storage format
* Stellar and NEM fixes
* New coins: ATS, KMD, XPM, XSN, ZCL
* New ETH tokens
Version 1.7.3
* Stable release, optional update
* Fix USB issue on some Windows 10 installations
Version 1.7.2
* Stable release, optional update
* Add support for OMNI layer: OMNI/MAID/USDT
* U2F fixes
* Don't ask for PIN if it has been just set
Version 1.7.1
* Stable release, optional update
* Add support for Lisk
* Add support for Zcash Sapling hardfork
* Implement seedless setup
Version 1.7.0
* Stable release, optional update
* Switch from HID to WebUSB
* Add support for Stellar
* Included bootloader 1.6.0
Version 1.6.3
* Stable release, required update
* Implement RSKIP-60 Ethereum checksum encoding
* Add support for new Ethereum networks (ESN, AKA, ETHO, MUSI, PIRL, ATH, GO)
* Add support for new 80 Ethereum tokens
* Improve MPU configuration
* Included bootloader 1.5.1
Version 1.6.2
* Stable release, optional update
* Add possibility to set custom auto-lock delay
* Bitcoin Cash cashaddr support
* Zcash Overwinter hardfork support
* Support for new coins:
- Decred, Bitcoin Private, Fujicoin, Groestlcoin, Vertcoin, Viacoin, Zcoin
* Support for new Ethereum networks:
- EOS Classic, Ethereum Social, Ellaism, Callisto, EtherGem, Wanchain
* Support for 500+ new Ethereum tokens
* Included bootloader 1.5.0
Version 1.6.1
* Stable release, required update
* Use fixed-width font for addresses
* Lots of under-the-hood improvements
* Fixed issue with write-protection settings
* Included bootloader 1.4.0
Version 1.6.0
* Stable release, optional update
* Native SegWit (Bech32) address support
* Show recognized BIP44/BIP49 paths in GetAddress dialog
* NEM support
* Expanse and UBIQ chains support
* Bitcoin Gold, DigiByte, Monacoin support
* Ed25519 collective signatures (CoSi) support
Version 1.5.2
* Stable release, required update
* Clean memory on start
* Fix storage import from older versions
Version 1.5.1
* Stable release, optional update
* Wipe storage after 16 wrong PIN attempts
* Enable Segwit for Bitcoin
* Bcash aka Bitcoin Cash support
* Message signing/verification for Ethereum and Segwit
* Make address dialog nicer (switch text/QR via button)
* Use checksum for Ethereum addresses
* Add more ERC-20 tokens, handle unrecognized ERC-20 tokens
* Allow "dry run" recovery procedure
* Allow separated backup procedure
Version 1.5.0
* Stable release, optional update
* Enable Segwit for Testnet and Litecoin
* Enable ERC-20 tokens for Ethereum chains
Version 1.4.2
* Stable release, optional update
* New Matrix-based recovery method
* Minor Ethereum fixes (including EIP-155 replay protection)
* Minor USB, U2F and GPG fixes
Version 1.4.1
* Stable release, optional update
* Support for Zcash JoinSplit transactions
* Enable device lock after 10 minutes of inactivity
* Enable device lock by pressing left button for 2 seconds
* Confirm dialog for U2F counter change
Version 1.4.0
* Stable release, optional update
* U2F support
* Ethereum support
* GPG decryption support
* Zcash support
Version 1.3.6
* Stable release, optional update
* Enable advanced transactions such as ones with REPLACE-BY-FEE and CHECKLOCKTIMEVERIFY
* Fix message signing for altcoins
* Message verification now shows address
* Enable GPG signing support
* Enable Ed25519 curve (for SSH and GPG)
* Use separate deterministic hierarchy for NIST256P1 and Ed25519 curves
* Users using SSH already need to regenerate their keys using the new firmware!!!
Version 1.3.5
* Stable release, optional update
* Double size font for recovery words during the device setup
* Optimizations for simultaneous access when more applications try communicate with the device
Version 1.3.4
* Stable release, optional update
* Screensaver active on ClearSession message
* Support for NIST P-256 curve
* Updated SignIdentity to v2 format
* Show seconds counter during PIN lockdown
* Updated maxfee per kb for coins
Version 1.3.3
* Stable release, mandatory update
* Ask for PIN on GetAddress and GetPublicKey
* Signing speed improved
Version 1.3.2
* Stable release, optional update
* Fix check during transaction streaming
* Login feature via SignIdentity message
* GetAddress for multisig shows M of N description
* PIN checking in constant time
Version 1.3.1
* Stable release, optional update
* Optimized signing speed
* Enabled OP_RETURN
* Added option to change home screen
* Moved fee calculation before any signing
* Made PIN delay increase immune against hardware hacking
Version 1.3.0
* Stable release, optional update
* Added multisig support
* Added visual validation of receiving address
* Added ECIES encryption capabilities
Version 1.2.1
* Stable release, mandatory update
* Added stack overflow protection
* Added compatibility with TREZOR Bridge
Version 1.2.0
* Stable release, optional update
* Fix false positives for fee warning
* Better UI for signing/verifying messages
* Smaller firmware size
Version 1.1.0
* Stable release, optional update
* Minor UI fixes
* Better handling of unexpected messages
* Added AES support
Version 1.0.0
* Stable release, mandatory update
* Added support for streaming of transactions into the device
* Removed all current limits on size of signed transaction

139
legacy/firmware/Makefile Normal file
View File

@ -0,0 +1,139 @@
APPVER = 1.8.0
NAME = trezor
ifeq ($(EMULATOR),1)
OBJS += udp.o
else
OBJS += usb.o
OBJS += bl_check.o
OBJS += otp.o
OBJS += header.o
endif
OBJS += u2f.o
OBJS += messages.o
OBJS += config.o
OBJS += trezor.o
OBJS += pinmatrix.o
OBJS += fsm.o
OBJS += coins.o
OBJS += coin_info.o
OBJS += transaction.o
OBJS += protect.o
OBJS += layout2.o
OBJS += recovery.o
OBJS += reset.o
OBJS += signing.o
OBJS += crypto.o
OBJS += ethereum.o
OBJS += ethereum_tokens.o
OBJS += nem2.o
OBJS += nem_mosaics.o
OBJS += stellar.o
OBJS += lisk.o
OBJS += debug.o
OBJS += ../vendor/trezor-crypto/address.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/rand.o
OBJS += ../vendor/trezor-crypto/memzero.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/curve25519-donna-32bit.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/curve25519-donna-helpers.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/modm-donna-32bit.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519-donna-basepoint-table.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519-donna-32bit-tables.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519-donna-impl-base.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/curve25519-donna-scalarmult-base.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519-sha3.o
OBJS += ../vendor/trezor-crypto/ed25519-donna/ed25519-keccak.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/base32.o
OBJS += ../vendor/trezor-crypto/base58.o
OBJS += ../vendor/trezor-crypto/segwit_addr.o
OBJS += ../vendor/trezor-crypto/cash_addr.o
OBJS += ../vendor/trezor-crypto/ripemd160.o
OBJS += ../vendor/trezor-crypto/sha2.o
OBJS += ../vendor/trezor-crypto/sha3.o
OBJS += ../vendor/trezor-crypto/blake256.o
OBJS += ../vendor/trezor-crypto/blake2b.o
OBJS += ../vendor/trezor-crypto/groestl.o
OBJS += ../vendor/trezor-crypto/hasher.o
OBJS += ../vendor/trezor-crypto/aes/aescrypt.o
OBJS += ../vendor/trezor-crypto/aes/aeskey.o
OBJS += ../vendor/trezor-crypto/aes/aestab.o
OBJS += ../vendor/trezor-crypto/aes/aes_modes.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/chacha20poly1305.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/chacha_merged.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/poly1305-donna.o
OBJS += ../vendor/trezor-crypto/chacha20poly1305/rfc7539.o
OBJS += ../vendor/trezor-crypto/nem.o
OBJS += ../vendor/QR-Code-generator/c/qrcodegen.o
OBJS += ../vendor/trezor-storage/storage.o
OBJS += ../vendor/trezor-storage/norcow.o
OBJS += ../vendor/nanopb/pb_common.o
OBJS += ../vendor/nanopb/pb_decode.o
OBJS += ../vendor/nanopb/pb_encode.o
OBJS += protob/messages.pb.o
OBJS += protob/messages-bitcoin.pb.o
OBJS += protob/messages-common.pb.o
OBJS += protob/messages-crypto.pb.o
OBJS += protob/messages-debug.pb.o
OBJS += protob/messages-ethereum.pb.o
OBJS += protob/messages-management.pb.o
OBJS += protob/messages-nem.pb.o
OBJS += protob/messages-stellar.pb.o
OBJS += protob/messages-lisk.pb.o
OPTFLAGS ?= -Os
../vendor/trezor-crypto/bip32.o: OPTFLAGS = -O3
../vendor/trezor-crypto/bip39.o: OPTFLAGS = -O3
../vendor/trezor-crypto/ecdsa.o: OPTFLAGS = -O3
../vendor/trezor-crypto/sha2.o: OPTFLAGS = -O3
../vendor/trezor-crypto/secp256k1.o: OPTFLAGS = -O3
include ../Makefile.include
DEBUG_LINK ?= 0
DEBUG_LOG ?= 0
CFLAGS += -Wno-sequence-point
CFLAGS += -I../vendor/nanopb -Iprotob -DPB_FIELD_16BIT=1
CFLAGS += -DDEBUG_LINK=$(DEBUG_LINK)
CFLAGS += -DDEBUG_LOG=$(DEBUG_LOG)
CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"'
CFLAGS += -DUSE_ETHEREUM=1
CFLAGS += -DUSE_NEM=1
CFLAGS += -DUSE_MONERO=0
%:: %.mako defs
@printf " MAKO $@\n"
$(Q)$(PYTHON) ../vendor/trezor-common/tools/cointool.py render $@.mako
bl_data.h: bl_data.py ../bootloader/bootloader.bin
@printf " PYTHON bl_data.py\n"
$(Q)$(PYTHON) bl_data.py
clean::
rm -f bl_data.h
find -maxdepth 1 -name "*.mako" | sed 's/.mako$$//' | xargs rm -f

192
legacy/firmware/bl_check.c Normal file
View File

@ -0,0 +1,192 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopencm3/stm32/flash.h>
#include <stdint.h>
#include <string.h>
#include "bl_data.h"
#include "gettext.h"
#include "layout.h"
#include "memory.h"
#include "util.h"
static int known_bootloader(int r, const uint8_t *hash) {
if (r != 32) return 0;
if (0 ==
memcmp(hash,
"\xbf\x72\xe2\x5e\x2c\x2f\xc1\xba\x57\x04\x50\xfa\xdf\xb6\x6f\xaa"
"\x5a\x71\x6d\xcd\xc0\x33\x35\x88\x55\x7b\x77\x54\x0a\xb8\x7e\x98",
32))
return 1; // 1.2.0a
if (0 ==
memcmp(hash,
"\x77\xb8\xe2\xf2\x5f\xaa\x8e\x8c\x7d\x9f\x5b\x32\x3b\x27\xce\x05"
"\x6c\xa3\xdb\xc2\x3f\x56\xc3\x7e\xe3\x3f\x97\x7c\xa6\xeb\x4d\x3e",
32))
return 1; // 1.2.0b
if (0 ==
memcmp(hash,
"\xc4\xc3\x25\x39\xb4\xa0\x25\xa8\xe7\x53\xa4\xc4\x62\x64\x28\x59"
"\x11\xa4\x5f\xcb\x14\xf4\x71\x81\x79\xe7\x11\xb1\xce\x99\x05\x24",
32))
return 1; // 1.2.5
if (0 ==
memcmp(hash,
"\x42\x59\x66\x94\xa0\xf2\x9d\x1e\xc2\x35\x71\x29\x2d\x54\x39\xd8"
"\x2f\xa1\x8c\x07\x37\xcb\x10\x7e\x98\xf6\x1e\xf5\x93\x4d\xe7\x16",
32))
return 1; // 1.3.0a
if (0 ==
memcmp(hash,
"\x3a\xcf\x2e\x51\x0b\x0f\xe1\x56\xb5\x58\xbb\xf7\x9c\x7e\x48\x5e"
"\xb0\x26\xe5\xe0\x8c\xb4\x4d\x15\x2d\x44\xd6\x4e\x0c\x6a\x41\x37",
32))
return 1; // 1.3.0b
if (0 ==
memcmp(hash,
"\x15\x85\x21\x5b\xc6\xe5\x5a\x34\x07\xa8\xb3\xee\xe2\x79\x03\x4e"
"\x95\xb9\xc4\x34\x00\x33\xe1\xb6\xae\x16\x0c\xe6\x61\x19\x67\x15",
32))
return 1; // 1.3.1
if (0 ==
memcmp(hash,
"\x76\x51\xb7\xca\xba\x5a\xae\x0c\xc1\xc6\x5c\x83\x04\xf7\x60\x39"
"\x6f\x77\x60\x6c\xd3\x99\x0c\x99\x15\x98\xf0\xe2\x2a\x81\xe0\x07",
32))
return 1; // 1.3.2
// note to those verifying these values: bootloader versions above this
// comment are aligned/padded to 32KiB with trailing 0xFF bytes and versions
// below are padded with 0x00.
// for more info, refer to "make -C
// bootloader align" and
// "firmware/bl_data.py".
if (0 ==
memcmp(hash,
"\x8c\xe8\xd7\x9e\xdf\x43\x0c\x03\x42\x64\x68\x6c\xa9\xb1\xd7\x8d"
"\x26\xed\xb2\xac\xab\x71\x39\xbe\x8f\x98\x5c\x2a\x3c\x6c\xae\x11",
32))
return 1; // 1.3.3
if (0 ==
memcmp(hash,
"\x63\x30\xfc\xec\x16\x72\xfa\xd3\x0b\x42\x1b\x60\xf7\x4f\x83\x9a"
"\x39\x39\x33\x45\x65\xcb\x70\x3b\x2b\xd7\x18\x2e\xa2\xdd\xa0\x19",
32))
return 1; // 1.4.0 shipped with fw 1.6.1
if (0 ==
memcmp(hash,
"\xaf\xb4\xcf\x7a\x4a\x57\x96\x10\x0e\xd5\x41\x6b\x75\x12\x1b\xc7"
"\x10\x08\xc2\xa2\xfd\x54\x49\xbd\x8f\x63\xcc\x22\xa6\xa7\xd6\x80",
32))
return 1; // 1.5.0 shipped with fw 1.6.2
if (0 ==
memcmp(hash,
"\x51\x12\x90\xa8\x72\x3f\xaf\xe7\x34\x15\x25\x9d\x25\x96\x76\x54"
"\x06\x32\x5c\xe2\x4b\x4b\x80\x03\x2c\x0b\x70\xb0\x5d\x98\x46\xe9",
32))
return 1; // 1.5.1 shipped with fw 1.6.3
if (0 ==
memcmp(hash,
"\x3e\xc4\xbd\xd5\x77\xea\x0c\x36\xc7\xba\xb7\xb9\xa3\x5b\x87\x17"
"\xb3\xf1\xfc\x2f\x80\x9e\x69\x0c\x8a\xbe\x5b\x05\xfb\xc2\x43\xc6",
32))
return 1; // 1.6.0 shipped with fw 1.7.0
if (0 ==
memcmp(hash,
"\x8e\x83\x02\x3f\x0d\x4f\x82\x4f\x64\x71\x20\x75\x2b\x6c\x71\x6f"
"\x55\xd7\x95\x70\x66\x8f\xd4\x90\x65\xd5\xb7\x97\x6e\x7a\x6e\x19",
32))
return 1; // 1.6.0 shipped with fw 1.7.1 and 1.7.2
if (0 ==
memcmp(hash,
"\xa2\x36\x6e\x77\xde\x8e\xfd\xfd\xc9\x99\xf4\x72\x20\xc0\x16\xe3"
"\x3f\x6d\x24\x24\xe2\x45\x90\x79\x11\x7a\x90\xb3\xa8\x88\xba\xdd",
32))
return 1; // 1.6.1 shipped with fw 1.7.3
if (0 ==
memcmp(hash,
"\xf7\xfa\x16\x5b\xe6\xd7\x80\xf3\xe1\xaf\x00\xab\xc0\x7d\xf8\xb3"
"\x07\x6b\xcd\xad\x72\xd7\x0d\xa2\x2a\x63\xd8\x89\x6b\x63\x91\xd8",
32))
return 1; // 1.8.0 shipped with fw 1.8.0
return 0;
}
void check_bootloader(void) {
#if MEMORY_PROTECT
uint8_t hash[32];
int r = memory_bootloader_hash(hash);
if (!known_bootloader(r, hash)) {
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Unknown bootloader"),
_("detected."), NULL, _("Unplug your TREZOR"),
_("contact our support."), NULL);
shutdown();
}
if (is_mode_unprivileged()) {
return;
}
if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) {
// all OK -> done
return;
}
// ENABLE THIS AT YOUR OWN RISK
// ATTEMPTING TO OVERWRITE BOOTLOADER WITH UNSIGNED FIRMWARE MAY BRICK
// YOUR DEVICE.
layoutDialog(&bmp_icon_warning, NULL, NULL, NULL, _("Updating bootloader"),
NULL, NULL, _("DO NOT UNPLUG"), _("YOUR TREZOR!"), NULL);
// unlock sectors
memory_write_unlock();
for (int tries = 0; tries < 10; tries++) {
// replace bootloader
flash_wait_for_last_operation();
flash_clear_status_flags();
flash_unlock();
for (int i = FLASH_BOOT_SECTOR_FIRST; i <= FLASH_BOOT_SECTOR_LAST; i++) {
flash_erase_sector(i, FLASH_CR_PROGRAM_X32);
}
for (int i = 0; i < FLASH_BOOT_LEN / 4; i++) {
const uint32_t *w = (const uint32_t *)(bl_data + i * 4);
flash_program_word(FLASH_BOOT_START + i * 4, *w);
}
flash_wait_for_last_operation();
flash_lock();
// check whether the write was OK
r = memory_bootloader_hash(hash);
if (r == 32 && 0 == memcmp(hash, bl_hash, 32)) {
// OK -> show info and halt
layoutDialog(&bmp_icon_info, NULL, NULL, NULL, _("Update finished"),
_("successfully."), NULL, _("Please reconnect"),
_("the device."), NULL);
shutdown();
return;
}
}
// show info and halt
layoutDialog(&bmp_icon_error, NULL, NULL, NULL, _("Bootloader update"),
_("broken."), NULL, _("Unplug your TREZOR"),
_("contact our support."), NULL);
shutdown();
#endif
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __BL_CHECK_H__
#define __BL_CHECK_H__
void check_bootloader(void);
#endif

19
legacy/firmware/bl_data.py Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
from hashlib import sha256
fn = '../bootloader/bootloader.bin'
data = open(fn, 'rb').read()
if len(data) > 32768:
raise Exception('bootloader has to be smaller than 32768 bytes')
data += b'\x00' * (32768 - len(data))
h = sha256(sha256(data).digest()).digest()
bl_hash = ', '.join('0x%02x' % x for x in bytearray(h))
bl_data = ', '.join('0x%02x' % x for x in bytearray(data))
with open('bl_data.h', 'wt') as f:
f.write('static const uint8_t bl_hash[32] = {%s};\n' % bl_hash)
f.write('static const uint8_t bl_data[32768] = {%s};\n' % bl_data)

View File

@ -0,0 +1,51 @@
<%
def signed_message_header(s):
return r'"\x{:02x}" {}'.format(len(s), c_str(s))
def c_bool(b):
return "true" if b else "false"
def c_int(i):
return int(i or 0)
def defined(s):
return c_bool(s is not None)
def hex(x):
return "0x{:08x}".format(c_int(x))
%>\
// This file is automatically generated from coin_info.c.mako
// DO NOT EDIT
#include "coins.h"
#include "curves.h"
#include "secp256k1.h"
const CoinInfo coins[COINS_COUNT] = {
% for c in supported_on("trezor1", bitcoin):
{
.coin_name = ${c_str(c.coin_name)},
.coin_shortcut = ${c_str(" " + c.coin_shortcut)},
.maxfee_kb = ${c_int(c.maxfee_kb)},
.signed_message_header = ${signed_message_header(c.signed_message_header)},
.has_address_type = ${defined(c.address_type)},
.has_address_type_p2sh = ${defined(c.address_type_p2sh)},
.has_segwit = ${c_bool(c.segwit)},
.has_fork_id = ${defined(c.fork_id)},
.force_bip143 = ${c_bool(c.force_bip143)},
.decred = ${c_bool(c.decred)},
.address_type = ${c.address_type},
.address_type_p2sh = ${c.address_type_p2sh},
.xpub_magic = ${hex(c.xpub_magic)},
.xpub_magic_segwit_p2sh = ${hex(c.xpub_magic_segwit_p2sh)},
.xpub_magic_segwit_native = ${hex(c.xpub_magic_segwit_native)},
.fork_id = ${c_int(c.fork_id)},
.bech32_prefix = ${c_str(c.bech32_prefix)},
.cashaddr_prefix = ${c_str(c.cashaddr_prefix)},
.coin_type = (${c_int(c.slip44)} | 0x80000000),
.curve_name = ${c.curve_name.upper()}_NAME,
.curve = &${c.curve_name}_info,
},
% endfor
};

View File

@ -0,0 +1,14 @@
// This file is automatically generated from coin_info.h.mako
// DO NOT EDIT
#ifndef __COIN_INFO_H__
#define __COIN_INFO_H__
#include "coins.h"
<% coins_list = list(supported_on("trezor1", bitcoin)) %>\
#define COINS_COUNT (${len(coins_list)})
extern const CoinInfo coins[COINS_COUNT];
#endif

80
legacy/firmware/coins.c Normal file
View File

@ -0,0 +1,80 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "coins.h"
#include <string.h>
#include "address.h"
#include "base58.h"
#include "ecdsa.h"
const CoinInfo *coinByName(const char *name) {
if (!name) return 0;
for (int i = 0; i < COINS_COUNT; i++) {
if (strcmp(name, coins[i].coin_name) == 0) {
return &(coins[i]);
}
}
return 0;
}
const CoinInfo *coinByAddressType(uint32_t address_type) {
for (int i = 0; i < COINS_COUNT; i++) {
if (address_type == coins[i].address_type) {
return &(coins[i]);
}
}
return 0;
}
const CoinInfo *coinBySlip44(uint32_t coin_type) {
for (int i = 0; i < COINS_COUNT; i++) {
if (coin_type == coins[i].coin_type) {
return &(coins[i]);
}
}
return 0;
}
bool coinExtractAddressType(const CoinInfo *coin, const char *addr,
uint32_t *address_type) {
if (!addr) return false;
uint8_t addr_raw[MAX_ADDR_RAW_SIZE];
int len = base58_decode_check(addr, coin->curve->hasher_base58, addr_raw,
MAX_ADDR_RAW_SIZE);
if (len >= 21) {
return coinExtractAddressTypeRaw(coin, addr_raw, address_type);
}
return false;
}
bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw,
uint32_t *address_type) {
if (coin->has_address_type &&
address_check_prefix(addr_raw, coin->address_type)) {
*address_type = coin->address_type;
return true;
}
if (coin->has_address_type_p2sh &&
address_check_prefix(addr_raw, coin->address_type_p2sh)) {
*address_type = coin->address_type_p2sh;
return true;
}
*address_type = 0;
return false;
}

64
legacy/firmware/coins.h Normal file
View File

@ -0,0 +1,64 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __COINS_H__
#define __COINS_H__
#include <stdbool.h>
#include <stdint.h>
#include "bip32.h"
#include "hasher.h"
typedef struct _CoinInfo {
const char *coin_name;
const char *coin_shortcut;
uint64_t maxfee_kb;
const char *signed_message_header;
bool has_address_type;
bool has_address_type_p2sh;
bool has_segwit;
bool has_fork_id;
bool force_bip143;
bool decred;
// address types > 0xFF represent a two-byte prefix in big-endian order
uint32_t address_type;
uint32_t address_type_p2sh;
uint32_t xpub_magic;
uint32_t xpub_magic_segwit_p2sh;
uint32_t xpub_magic_segwit_native;
uint32_t fork_id;
const char *bech32_prefix;
const char *cashaddr_prefix;
uint32_t coin_type;
const char *curve_name;
const curve_info *curve;
} CoinInfo;
#include "coin_info.h"
const CoinInfo *coinByName(const char *name);
const CoinInfo *coinByAddressType(uint32_t address_type);
const CoinInfo *coinBySlip44(uint32_t coin_type);
bool coinExtractAddressType(const CoinInfo *coin, const char *addr,
uint32_t *address_type);
bool coinExtractAddressTypeRaw(const CoinInfo *coin, const uint8_t *addr_raw,
uint32_t *address_type);
#endif

937
legacy/firmware/config.c Normal file
View File

@ -0,0 +1,937 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopencm3/stm32/flash.h>
#include <stdint.h>
#include <string.h>
#include "messages.pb.h"
#include "aes/aes.h"
#include "bip32.h"
#include "bip39.h"
#include "common.h"
#include "config.h"
#include "curves.h"
#include "debug.h"
#include "gettext.h"
#include "hmac.h"
#include "layout2.h"
#include "memory.h"
#include "memzero.h"
#include "pbkdf2.h"
#include "protect.h"
#include "rng.h"
#include "sha2.h"
#include "storage.h"
#include "supervise.h"
#include "trezor.h"
#include "u2f.h"
#include "usb.h"
#include "util.h"
/* Magic constants to check validity of storage block for storage versions 1
* to 10. */
static const uint32_t CONFIG_MAGIC_V10 = 0x726f7473; // 'stor' as uint32_t
#if !EMULATOR
static const uint32_t META_MAGIC_V10 = 0x525a5254; // 'TRZR' as uint32_t
#else
static const uint32_t META_MAGIC_V10 = 0xFFFFFFFF;
#endif
#define APP 0x0100
#define FLAG_PUBLIC 0x8000
#define FLAGS_WRITE 0xC000
#define KEY_UUID (0 | APP | FLAG_PUBLIC) // bytes(12)
#define KEY_VERSION (1 | APP) // uint32
#define KEY_MNEMONIC (2 | APP) // string(241)
#define KEY_LANGUAGE (3 | APP | FLAG_PUBLIC) // string(17)
#define KEY_LABEL (4 | APP | FLAG_PUBLIC) // string(33)
#define KEY_PASSPHRASE_PROTECTION (5 | APP | FLAG_PUBLIC) // bool
#define KEY_HOMESCREEN (6 | APP | FLAG_PUBLIC) // bytes(1024)
#define KEY_NEEDS_BACKUP (7 | APP) // bool
#define KEY_FLAGS (8 | APP) // uint32
#define KEY_U2F_COUNTER (9 | APP | FLAGS_WRITE) // uint32
#define KEY_UNFINISHED_BACKUP (11 | APP) // bool
#define KEY_AUTO_LOCK_DELAY_MS (12 | APP) // uint32
#define KEY_NO_BACKUP (13 | APP) // bool
#define KEY_INITIALIZED (14 | APP | FLAG_PUBLIC) // uint32
#define KEY_NODE (15 | APP) // node
#define KEY_IMPORTED (16 | APP) // bool
#define KEY_U2F_ROOT (17 | APP | FLAG_PUBLIC) // node
#define KEY_DEBUG_LINK_PIN (255 | APP | FLAG_PUBLIC) // string(10)
// The PIN value corresponding to an empty PIN.
static const uint32_t PIN_EMPTY = 1;
static uint32_t config_uuid[UUID_SIZE / sizeof(uint32_t)];
_Static_assert(sizeof(config_uuid) == UUID_SIZE, "config_uuid has wrong size");
char config_uuid_str[2 * UUID_SIZE + 1];
/*
Old storage layout:
offset | type/length | description
--------+--------------+-------------------------------
0x0000 | 4 bytes | magic = 'stor'
0x0004 | 12 bytes | uuid
0x0010 | ? bytes | Storage structure
--------+--------------+-------------------------------
0x4000 | 4 kbytes | area for pin failures
0x5000 | 256 bytes | area for u2f counter updates
0x5100 | 11.75 kbytes | reserved
The area for pin failures looks like this:
0 ... 0 pinfail 0xffffffff .. 0xffffffff
The pinfail is a binary number of the form 1...10...0,
the number of zeros is the number of pin failures.
This layout is used because we can only clear bits without
erasing the flash.
The area for u2f counter updates is just a sequence of zero-bits
followed by a sequence of one-bits. The bits in a byte are numbered
from LSB to MSB. The number of zero bits is the offset that should
be added to the storage u2f_counter to get the real counter value.
*/
/* Current u2f offset, i.e. u2f counter is
* storage.u2f_counter + config_u2f_offset.
* This corresponds to the number of cleared bits in the U2FAREA.
*/
static secbool sessionSeedCached, sessionSeedUsesPassphrase;
static uint8_t CONFIDENTIAL sessionSeed[64];
static secbool sessionPassphraseCached = secfalse;
static char CONFIDENTIAL sessionPassphrase[51];
#define autoLockDelayMsDefault (10 * 60 * 1000U) // 10 minutes
static secbool autoLockDelayMsCached = secfalse;
static uint32_t autoLockDelayMs = autoLockDelayMsDefault;
static const uint32_t CONFIG_VERSION = 11;
static const uint8_t FALSE_BYTE = '\x00';
static const uint8_t TRUE_BYTE = '\x01';
static uint32_t pin_to_int(const char *pin) {
uint32_t val = 1;
size_t i = 0;
for (i = 0; i < MAX_PIN_LEN && pin[i] != '\0'; ++i) {
if (pin[i] < '0' || pin[i] > '9') {
return 0;
}
val = 10 * val + pin[i] - '0';
}
if (pin[i] != '\0') {
return 0;
}
return val;
}
static secbool config_set_bool(uint16_t key, bool value) {
if (value) {
return storage_set(key, &TRUE_BYTE, sizeof(TRUE_BYTE));
} else {
return storage_set(key, &FALSE_BYTE, sizeof(FALSE_BYTE));
}
}
static secbool config_get_bool(uint16_t key, bool *value) {
uint8_t val = 0;
uint16_t len = 0;
if (sectrue == storage_get(key, &val, sizeof(val), &len) &&
len == sizeof(TRUE_BYTE)) {
*value = (val == TRUE_BYTE);
return sectrue;
} else {
*value = false;
return secfalse;
}
}
static secbool config_get_bytes(uint16_t key, uint8_t *dest, uint16_t dest_size,
uint16_t *real_size) {
if (dest_size == 0) {
return secfalse;
}
if (sectrue != storage_get(key, dest, dest_size, real_size)) {
return secfalse;
}
return sectrue;
}
static secbool config_get_string(uint16_t key, char *dest, uint16_t dest_size) {
if (dest_size == 0) {
return secfalse;
}
uint16_t len = 0;
if (sectrue != storage_get(key, dest, dest_size - 1, &len)) {
dest[0] = '\0';
return secfalse;
}
dest[len] = '\0';
return sectrue;
}
static secbool config_get_uint32(uint16_t key, uint32_t *value) {
uint16_t len = 0;
if (sectrue != storage_get(key, value, sizeof(uint32_t), &len) ||
len != sizeof(uint32_t)) {
*value = 0;
return secfalse;
}
return sectrue;
}
#define FLASH_META_START 0x08008000
#define FLASH_META_LEN 0x100
static secbool config_upgrade_v10(void) {
#define OLD_STORAGE_SIZE(last_member) \
(((offsetof(Storage, last_member) + pb_membersize(Storage, last_member)) + \
3) & \
~3)
if (memcmp(FLASH_PTR(FLASH_META_START), &META_MAGIC_V10,
sizeof(META_MAGIC_V10)) != 0 ||
memcmp(FLASH_PTR(FLASH_META_START + FLASH_META_LEN), &CONFIG_MAGIC_V10,
sizeof(CONFIG_MAGIC_V10)) != 0) {
// wrong magic
return secfalse;
}
Storage config __attribute__((aligned(4)));
_Static_assert((sizeof(config) & 3) == 0, "storage unaligned");
memcpy(
config_uuid,
FLASH_PTR(FLASH_META_START + FLASH_META_LEN + sizeof(CONFIG_MAGIC_V10)),
sizeof(config_uuid));
memcpy(&config,
FLASH_PTR(FLASH_META_START + FLASH_META_LEN +
sizeof(CONFIG_MAGIC_V10) + sizeof(config_uuid)),
sizeof(config));
// version 1: since 1.0.0
// version 2: since 1.2.1
// version 3: since 1.3.1
// version 4: since 1.3.2
// version 5: since 1.3.3
// version 6: since 1.3.6
// version 7: since 1.5.1
// version 8: since 1.5.2
// version 9: since 1.6.1
// version 10: since 1.7.2
if (config.version > CONFIG_VERSION) {
// downgrade -> clear storage
config_wipe();
return secfalse;
}
size_t old_config_size = 0;
if (config.version == 0) {
} else if (config.version <= 2) {
old_config_size = OLD_STORAGE_SIZE(imported);
} else if (config.version <= 5) {
// added homescreen
old_config_size = OLD_STORAGE_SIZE(homescreen);
} else if (config.version <= 7) {
// added u2fcounter
old_config_size = OLD_STORAGE_SIZE(u2f_counter);
} else if (config.version <= 8) {
// added flags and needsBackup
old_config_size = OLD_STORAGE_SIZE(flags);
} else if (config.version <= 9) {
// added u2froot, unfinished_backup and auto_lock_delay_ms
old_config_size = OLD_STORAGE_SIZE(auto_lock_delay_ms);
} else if (config.version <= 10) {
// added no_backup
old_config_size = OLD_STORAGE_SIZE(no_backup);
}
// Erase newly added fields.
if (old_config_size != sizeof(Storage)) {
memzero((char *)&config + old_config_size,
sizeof(Storage) - old_config_size);
}
const uint32_t FLASH_STORAGE_PINAREA = FLASH_META_START + 0x4000;
uint32_t pin_wait = 0;
if (config.version <= 5) {
// Get PIN failure counter from version 5 format.
uint32_t pinctr =
config.has_pin_failed_attempts ? config.pin_failed_attempts : 0;
if (pinctr > 31) {
pinctr = 31;
}
pin_wait = (1 << pinctr) - 1;
} else {
// Get PIN failure counter from version 10 format.
uint32_t flash_pinfails = FLASH_STORAGE_PINAREA;
while (*(const uint32_t *)FLASH_PTR(flash_pinfails) == 0) {
flash_pinfails += sizeof(uint32_t);
}
pin_wait = ~*(const uint32_t *)FLASH_PTR(flash_pinfails);
}
uint32_t u2f_offset = 0;
if (config.has_u2f_counter) {
const uint32_t FLASH_STORAGE_U2FAREA = FLASH_STORAGE_PINAREA + 0x1000;
const uint32_t *u2fptr = (const uint32_t *)FLASH_PTR(FLASH_STORAGE_U2FAREA);
while (*u2fptr == 0) {
u2fptr++;
}
u2f_offset =
32 * (u2fptr - (const uint32_t *)FLASH_PTR(FLASH_STORAGE_U2FAREA));
uint32_t u2fword = *u2fptr;
while ((u2fword & 1) == 0) {
u2f_offset++;
u2fword >>= 1;
}
}
storage_init(NULL, HW_ENTROPY_DATA, HW_ENTROPY_LEN);
storage_unlock(PIN_EMPTY);
if (config.has_pin) {
storage_change_pin(PIN_EMPTY, pin_to_int(config.pin));
}
while (pin_wait != 0) {
storage_pin_fails_increase();
pin_wait >>= 1;
}
storage_set(KEY_UUID, config_uuid, sizeof(config_uuid));
storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION));
if (config.has_node) {
if (sectrue == storage_set(KEY_NODE, &config.node, sizeof(config.node))) {
config_set_bool(KEY_INITIALIZED, true);
}
}
if (config.has_mnemonic) {
config_setMnemonic(config.mnemonic);
}
if (config.has_passphrase_protection) {
config_setPassphraseProtection(config.passphrase_protection);
}
if (config.has_language) {
config_setLanguage(config.language);
}
if (config.has_label) {
config_setLabel(config.label);
}
if (config.has_imported) {
config_setImported(config.imported);
}
if (config.has_homescreen) {
config_setHomescreen(config.homescreen.bytes, config.homescreen.size);
}
if (config.has_u2f_counter) {
config_setU2FCounter(config.u2f_counter + u2f_offset);
}
if (config.has_needs_backup) {
config_setNeedsBackup(config.needs_backup);
}
if (config.has_flags) {
config_applyFlags(config.flags);
}
if (config.has_unfinished_backup) {
config_setUnfinishedBackup(config.unfinished_backup);
}
if (config.has_auto_lock_delay_ms) {
config_setAutoLockDelayMs(config.auto_lock_delay_ms);
}
if (config.has_no_backup && config.no_backup) {
config_setNoBackup();
}
memzero(&config, sizeof(config));
session_clear(true);
return sectrue;
}
void config_init(void) {
char oldTiny = usbTiny(1);
config_upgrade_v10();
storage_init(&protectPinUiCallback, HW_ENTROPY_DATA, HW_ENTROPY_LEN);
memzero(HW_ENTROPY_DATA, sizeof(HW_ENTROPY_DATA));
// Auto-unlock storage if no PIN is set.
if (storage_is_unlocked() == secfalse && storage_has_pin() == secfalse) {
storage_unlock(PIN_EMPTY);
}
uint16_t len = 0;
// If UUID is not set, then the config is uninitialized.
if (sectrue !=
storage_get(KEY_UUID, config_uuid, sizeof(config_uuid), &len) ||
len != sizeof(config_uuid)) {
random_buffer((uint8_t *)config_uuid, sizeof(config_uuid));
storage_set(KEY_UUID, config_uuid, sizeof(config_uuid));
storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION));
}
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
usbTiny(oldTiny);
}
void session_clear(bool lock) {
sessionSeedCached = secfalse;
memzero(&sessionSeed, sizeof(sessionSeed));
sessionPassphraseCached = secfalse;
memzero(&sessionPassphrase, sizeof(sessionPassphrase));
if (lock) {
storage_lock();
}
}
static void get_u2froot_callback(uint32_t iter, uint32_t total) {
layoutProgress(_("Updating"), 1000 * iter / total);
}
static void config_compute_u2froot(const char *mnemonic,
StorageHDNode *u2froot) {
static CONFIDENTIAL HDNode node;
char oldTiny = usbTiny(1);
mnemonic_to_seed(mnemonic, "", sessionSeed,
get_u2froot_callback); // BIP-0039
usbTiny(oldTiny);
hdnode_from_seed(sessionSeed, 64, NIST256P1_NAME, &node);
hdnode_private_ckd(&node, U2F_KEY_PATH);
u2froot->depth = node.depth;
u2froot->child_num = U2F_KEY_PATH;
u2froot->chain_code.size = sizeof(node.chain_code);
memcpy(u2froot->chain_code.bytes, node.chain_code, sizeof(node.chain_code));
u2froot->has_private_key = true;
u2froot->private_key.size = sizeof(node.private_key);
memcpy(u2froot->private_key.bytes, node.private_key,
sizeof(node.private_key));
memzero(&node, sizeof(node));
session_clear(false); // invalidate seed cache
}
static void config_setNode(const HDNodeType *node) {
StorageHDNode storageHDNode;
memzero(&storageHDNode, sizeof(storageHDNode));
storageHDNode.depth = node->depth;
storageHDNode.fingerprint = node->fingerprint;
storageHDNode.child_num = node->child_num;
storageHDNode.chain_code.size = 32;
memcpy(storageHDNode.chain_code.bytes, node->chain_code.bytes, 32);
if (node->has_private_key) {
storageHDNode.has_private_key = true;
storageHDNode.private_key.size = 32;
memcpy(storageHDNode.private_key.bytes, node->private_key.bytes, 32);
}
if (sectrue == storage_set(KEY_NODE, &storageHDNode, sizeof(storageHDNode))) {
config_set_bool(KEY_INITIALIZED, true);
}
memzero(&storageHDNode, sizeof(storageHDNode));
}
#if DEBUG_LINK
bool config_dumpNode(HDNodeType *node) {
memzero(node, sizeof(HDNodeType));
StorageHDNode storageNode;
uint16_t len = 0;
if (sectrue !=
storage_get(KEY_NODE, &storageNode, sizeof(storageNode), &len) ||
len != sizeof(StorageHDNode)) {
memzero(&storageNode, sizeof(storageNode));
return false;
}
node->depth = storageNode.depth;
node->fingerprint = storageNode.fingerprint;
node->child_num = storageNode.child_num;
node->chain_code.size = 32;
memcpy(node->chain_code.bytes, storageNode.chain_code.bytes, 32);
if (storageNode.has_private_key) {
node->has_private_key = true;
node->private_key.size = 32;
memcpy(node->private_key.bytes, storageNode.private_key.bytes, 32);
}
memzero(&storageNode, sizeof(storageNode));
return true;
}
#endif
void config_loadDevice(const LoadDevice *msg) {
session_clear(false);
config_set_bool(KEY_IMPORTED, true);
config_setPassphraseProtection(msg->has_passphrase_protection &&
msg->passphrase_protection);
if (msg->has_pin) {
config_changePin("", msg->pin);
}
if (msg->has_node) {
storage_delete(KEY_MNEMONIC);
config_setNode(&(msg->node));
} else if (msg->has_mnemonic) {
storage_delete(KEY_NODE);
config_setMnemonic(msg->mnemonic);
}
if (msg->has_language) {
config_setLanguage(msg->language);
}
config_setLabel(msg->has_label ? msg->label : "");
if (msg->has_u2f_counter) {
config_setU2FCounter(msg->u2f_counter);
}
}
void config_setLabel(const char *label) {
if (label == NULL || label[0] == '\0') {
storage_delete(KEY_LABEL);
} else {
storage_set(KEY_LABEL, label, strnlen(label, MAX_LABEL_LEN));
}
}
void config_setLanguage(const char *lang) {
if (lang == NULL) {
return;
}
// Sanity check.
if (strcmp(lang, "english") != 0) {
return;
}
storage_set(KEY_LANGUAGE, lang, strnlen(lang, MAX_LANGUAGE_LEN));
}
void config_setPassphraseProtection(bool passphrase_protection) {
sessionSeedCached = secfalse;
sessionPassphraseCached = secfalse;
config_set_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
}
bool config_getPassphraseProtection(bool *passphrase_protection) {
return sectrue ==
config_get_bool(KEY_PASSPHRASE_PROTECTION, passphrase_protection);
}
void config_setHomescreen(const uint8_t *data, uint32_t size) {
if (data != NULL && size == HOMESCREEN_SIZE) {
storage_set(KEY_HOMESCREEN, data, size);
} else {
storage_delete(KEY_HOMESCREEN);
}
}
static void get_root_node_callback(uint32_t iter, uint32_t total) {
usbSleep(1);
layoutProgress(_("Waking up"), 1000 * iter / total);
}
const uint8_t *config_getSeed(bool usePassphrase) {
// root node is properly cached
if (usePassphrase == (sectrue == sessionSeedUsesPassphrase) &&
sectrue == sessionSeedCached) {
return sessionSeed;
}
// if storage has mnemonic, convert it to node and use it
char mnemonic[MAX_MNEMONIC_LEN + 1];
if (config_getMnemonic(mnemonic, sizeof(mnemonic))) {
if (usePassphrase && !protectPassphrase()) {
memzero(mnemonic, sizeof(mnemonic));
return NULL;
}
// if storage was not imported (i.e. it was properly generated or recovered)
bool imported = false;
config_get_bool(KEY_IMPORTED, &imported);
if (!imported) {
// test whether mnemonic is a valid BIP-0039 mnemonic
if (!mnemonic_check(mnemonic)) {
// and if not then halt the device
error_shutdown(_("Storage failure"), _("detected."), NULL, NULL);
}
}
char oldTiny = usbTiny(1);
mnemonic_to_seed(mnemonic, usePassphrase ? sessionPassphrase : "",
sessionSeed, get_root_node_callback); // BIP-0039
memzero(mnemonic, sizeof(mnemonic));
usbTiny(oldTiny);
sessionSeedCached = sectrue;
sessionSeedUsesPassphrase = usePassphrase ? sectrue : secfalse;
return sessionSeed;
}
return NULL;
}
static bool config_loadNode(const StorageHDNode *node, const char *curve,
HDNode *out) {
return hdnode_from_xprv(node->depth, node->child_num, node->chain_code.bytes,
node->private_key.bytes, curve, out);
}
bool config_getU2FRoot(HDNode *node) {
StorageHDNode u2fNode;
uint16_t len = 0;
if (sectrue != storage_get(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode), &len) ||
len != sizeof(StorageHDNode)) {
memzero(&u2fNode, sizeof(u2fNode));
return false;
}
bool ret = config_loadNode(&u2fNode, NIST256P1_NAME, node);
memzero(&u2fNode, sizeof(u2fNode));
return ret;
}
bool config_getRootNode(HDNode *node, const char *curve, bool usePassphrase) {
// if storage has node, decrypt and use it
StorageHDNode storageHDNode;
uint16_t len = 0;
if (strcmp(curve, SECP256K1_NAME) == 0 &&
sectrue ==
storage_get(KEY_NODE, &storageHDNode, sizeof(storageHDNode), &len) &&
len == sizeof(StorageHDNode)) {
if (!protectPassphrase()) {
memzero(&storageHDNode, sizeof(storageHDNode));
return false;
}
if (!config_loadNode(&storageHDNode, curve, node)) {
memzero(&storageHDNode, sizeof(storageHDNode));
return false;
}
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (passphrase_protection && sectrue == sessionPassphraseCached &&
sessionPassphrase[0] != '\0') {
// decrypt hd node
uint8_t secret[64];
PBKDF2_HMAC_SHA512_CTX pctx;
char oldTiny = usbTiny(1);
pbkdf2_hmac_sha512_Init(&pctx, (const uint8_t *)sessionPassphrase,
strlen(sessionPassphrase),
(const uint8_t *)"TREZORHD", 8, 1);
get_root_node_callback(0, BIP39_PBKDF2_ROUNDS);
for (int i = 0; i < 8; i++) {
pbkdf2_hmac_sha512_Update(&pctx, BIP39_PBKDF2_ROUNDS / 8);
get_root_node_callback((i + 1) * BIP39_PBKDF2_ROUNDS / 8,
BIP39_PBKDF2_ROUNDS);
}
pbkdf2_hmac_sha512_Final(&pctx, secret);
usbTiny(oldTiny);
aes_decrypt_ctx ctx;
aes_decrypt_key256(secret, &ctx);
aes_cbc_decrypt(node->chain_code, node->chain_code, 32, secret + 32,
&ctx);
aes_cbc_decrypt(node->private_key, node->private_key, 32, secret + 32,
&ctx);
}
return true;
}
memzero(&storageHDNode, sizeof(storageHDNode));
const uint8_t *seed = config_getSeed(usePassphrase);
if (seed == NULL) {
return false;
}
return hdnode_from_seed(seed, 64, curve, node);
}
bool config_getLabel(char *dest, uint16_t dest_size) {
return sectrue == config_get_string(KEY_LABEL, dest, dest_size);
}
bool config_getLanguage(char *dest, uint16_t dest_size) {
return sectrue == config_get_string(KEY_LANGUAGE, dest, dest_size);
}
bool config_getHomescreen(uint8_t *dest, uint16_t dest_size) {
uint16_t len = 0;
secbool ret = storage_get(KEY_HOMESCREEN, dest, dest_size, &len);
if (sectrue != ret || len != HOMESCREEN_SIZE) {
return false;
}
return true;
}
bool config_setMnemonic(const char *mnemonic) {
if (mnemonic == NULL) {
return false;
}
if (sectrue != storage_set(KEY_MNEMONIC, mnemonic,
strnlen(mnemonic, MAX_MNEMONIC_LEN))) {
return false;
}
StorageHDNode u2fNode;
memzero(&u2fNode, sizeof(u2fNode));
config_compute_u2froot(mnemonic, &u2fNode);
secbool ret = storage_set(KEY_U2F_ROOT, &u2fNode, sizeof(u2fNode));
memzero(&u2fNode, sizeof(u2fNode));
if (sectrue != ret) {
storage_delete(KEY_MNEMONIC);
return false;
}
config_set_bool(KEY_INITIALIZED, true);
return true;
}
bool config_getMnemonicBytes(uint8_t *dest, uint16_t dest_size,
uint16_t *real_size) {
return sectrue == config_get_bytes(KEY_MNEMONIC, dest, dest_size, real_size);
}
bool config_getMnemonic(char *dest, uint16_t dest_size) {
return sectrue == config_get_string(KEY_MNEMONIC, dest, dest_size);
}
/* Check whether mnemonic matches storage. The mnemonic must be
* a null-terminated string.
*/
bool config_containsMnemonic(const char *mnemonic) {
uint16_t len = 0;
uint8_t stored_mnemonic[MAX_MNEMONIC_LEN];
if (sectrue != storage_get(KEY_MNEMONIC, stored_mnemonic,
sizeof(stored_mnemonic), &len)) {
return false;
}
// Compare the digests to mitigate side-channel attacks.
uint8_t digest_stored[SHA256_DIGEST_LENGTH];
sha256_Raw(stored_mnemonic, len, digest_stored);
memzero(stored_mnemonic, sizeof(stored_mnemonic));
uint8_t digest_input[SHA256_DIGEST_LENGTH];
sha256_Raw((const uint8_t *)mnemonic, strnlen(mnemonic, MAX_MNEMONIC_LEN),
digest_input);
uint8_t diff = 0;
for (size_t i = 0; i < sizeof(digest_input); i++) {
diff |= (digest_stored[i] - digest_input[i]);
}
memzero(digest_stored, sizeof(digest_stored));
memzero(digest_input, sizeof(digest_input));
return diff == 0;
}
/* Check whether pin matches storage. The pin must be
* a null-terminated string with at most 9 characters.
*/
bool config_unlock(const char *pin) {
char oldTiny = usbTiny(1);
secbool ret = storage_unlock(pin_to_int(pin));
usbTiny(oldTiny);
return sectrue == ret;
}
bool config_hasPin(void) { return sectrue == storage_has_pin(); }
bool config_changePin(const char *old_pin, const char *new_pin) {
uint32_t new_pin_int = pin_to_int(new_pin);
if (new_pin_int == 0) {
return false;
}
char oldTiny = usbTiny(1);
secbool ret = storage_change_pin(pin_to_int(old_pin), new_pin_int);
usbTiny(oldTiny);
#if DEBUG_LINK
if (sectrue == ret) {
if (new_pin_int != PIN_EMPTY) {
storage_set(KEY_DEBUG_LINK_PIN, new_pin, strnlen(new_pin, MAX_PIN_LEN));
} else {
storage_delete(KEY_DEBUG_LINK_PIN);
}
}
#endif
memzero(&new_pin_int, sizeof(new_pin_int));
return sectrue == ret;
}
#if DEBUG_LINK
bool config_getPin(char *dest, uint16_t dest_size) {
return sectrue == config_get_string(KEY_DEBUG_LINK_PIN, dest, dest_size);
}
#endif
void session_cachePassphrase(const char *passphrase) {
strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase));
sessionPassphraseCached = sectrue;
}
bool session_isPassphraseCached(void) {
return sectrue == sessionPassphraseCached;
}
bool session_getState(const uint8_t *salt, uint8_t *state,
const char *passphrase) {
if (!passphrase && sectrue != sessionPassphraseCached) {
return false;
} else {
passphrase = sessionPassphrase;
}
if (!salt) {
// if salt is not provided fill the first half of the state with random data
random_buffer(state, 32);
} else {
// if salt is provided fill the first half of the state with salt
memcpy(state, salt, 32);
}
// state[0:32] = salt
// state[32:64] = HMAC(passphrase, salt || device_id)
HMAC_SHA256_CTX ctx;
hmac_sha256_Init(&ctx, (const uint8_t *)passphrase, strlen(passphrase));
hmac_sha256_Update(&ctx, state, 32);
hmac_sha256_Update(&ctx, (const uint8_t *)config_uuid, sizeof(config_uuid));
hmac_sha256_Final(&ctx, state + 32);
memzero(&ctx, sizeof(ctx));
return true;
}
bool session_isUnlocked(void) { return sectrue == storage_is_unlocked(); }
bool config_isInitialized(void) {
bool initialized = false;
config_get_bool(KEY_INITIALIZED, &initialized);
return initialized;
}
bool config_getImported(bool *imported) {
return sectrue == config_get_bool(KEY_IMPORTED, imported);
}
void config_setImported(bool imported) {
config_set_bool(KEY_IMPORTED, imported);
}
bool config_getNeedsBackup(bool *needs_backup) {
return sectrue == config_get_bool(KEY_NEEDS_BACKUP, needs_backup);
}
void config_setNeedsBackup(bool needs_backup) {
config_set_bool(KEY_NEEDS_BACKUP, needs_backup);
}
bool config_getUnfinishedBackup(bool *unfinished_backup) {
return sectrue == config_get_bool(KEY_UNFINISHED_BACKUP, unfinished_backup);
}
void config_setUnfinishedBackup(bool unfinished_backup) {
config_set_bool(KEY_UNFINISHED_BACKUP, unfinished_backup);
}
bool config_getNoBackup(bool *no_backup) {
return sectrue == config_get_bool(KEY_NO_BACKUP, no_backup);
}
void config_setNoBackup(void) { config_set_bool(KEY_NO_BACKUP, true); }
void config_applyFlags(uint32_t flags) {
uint32_t old_flags = 0;
config_get_uint32(KEY_FLAGS, &old_flags);
flags |= old_flags;
if (flags == old_flags) {
return; // no new flags
}
storage_set(KEY_FLAGS, &flags, sizeof(flags));
}
bool config_getFlags(uint32_t *flags) {
return sectrue == config_get_uint32(KEY_FLAGS, flags);
}
uint32_t config_nextU2FCounter(void) {
uint32_t u2fcounter = 0;
storage_next_counter(KEY_U2F_COUNTER, &u2fcounter);
return u2fcounter;
}
void config_setU2FCounter(uint32_t u2fcounter) {
storage_set_counter(KEY_U2F_COUNTER, u2fcounter);
}
uint32_t config_getAutoLockDelayMs() {
if (sectrue == autoLockDelayMsCached) {
return autoLockDelayMs;
}
if (sectrue != storage_is_unlocked()) {
return autoLockDelayMsDefault;
}
if (sectrue != config_get_uint32(KEY_AUTO_LOCK_DELAY_MS, &autoLockDelayMs)) {
autoLockDelayMs = autoLockDelayMsDefault;
}
autoLockDelayMsCached = sectrue;
return autoLockDelayMs;
}
void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms) {
const uint32_t min_delay_ms = 10 * 1000U; // 10 seconds
auto_lock_delay_ms = MAX(auto_lock_delay_ms, min_delay_ms);
if (sectrue == storage_set(KEY_AUTO_LOCK_DELAY_MS, &auto_lock_delay_ms,
sizeof(auto_lock_delay_ms))) {
autoLockDelayMs = auto_lock_delay_ms;
autoLockDelayMsCached = sectrue;
}
}
void config_wipe(void) {
char oldTiny = usbTiny(1);
storage_wipe();
if (storage_is_unlocked() != sectrue) {
storage_unlock(PIN_EMPTY);
}
usbTiny(oldTiny);
random_buffer((uint8_t *)config_uuid, sizeof(config_uuid));
data2hex(config_uuid, sizeof(config_uuid), config_uuid_str);
autoLockDelayMsCached = secfalse;
storage_set(KEY_UUID, config_uuid, sizeof(config_uuid));
storage_set(KEY_VERSION, &CONFIG_VERSION, sizeof(CONFIG_VERSION));
session_clear(false);
}

158
legacy/firmware/config.h Normal file
View File

@ -0,0 +1,158 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include "bip32.h"
#include "messages-management.pb.h"
#define STORAGE_FIELD(TYPE, NAME) \
bool has_##NAME; \
TYPE NAME;
#define STORAGE_STRING(NAME, SIZE) \
bool has_##NAME; \
char NAME[SIZE];
#define STORAGE_BYTES(NAME, SIZE) \
bool has_##NAME; \
struct { \
uint32_t size; \
uint8_t bytes[SIZE]; \
} NAME;
#define STORAGE_BOOL(NAME) STORAGE_FIELD(bool, NAME)
#define STORAGE_NODE(NAME) STORAGE_FIELD(StorageHDNode, NAME)
#define STORAGE_UINT32(NAME) STORAGE_FIELD(uint32_t, NAME)
typedef struct {
uint32_t depth;
uint32_t fingerprint;
uint32_t child_num;
struct {
uint32_t size;
uint8_t bytes[32];
} chain_code;
STORAGE_BYTES(private_key, 32);
STORAGE_BYTES(public_key, 33);
} StorageHDNode;
typedef struct _Storage {
uint32_t version;
STORAGE_NODE(node)
STORAGE_STRING(mnemonic, 241)
STORAGE_BOOL(passphrase_protection)
STORAGE_UINT32(pin_failed_attempts)
STORAGE_STRING(pin, 10)
STORAGE_STRING(language, 17)
STORAGE_STRING(label, 33)
STORAGE_BOOL(imported)
STORAGE_BYTES(homescreen, 1024)
STORAGE_UINT32(u2f_counter)
STORAGE_BOOL(needs_backup)
STORAGE_UINT32(flags)
STORAGE_NODE(u2froot)
STORAGE_BOOL(unfinished_backup)
STORAGE_UINT32(auto_lock_delay_ms)
STORAGE_BOOL(no_backup)
} Storage;
extern Storage configUpdate;
#define MAX_PIN_LEN 9
#define MAX_LABEL_LEN 32
#define MAX_LANGUAGE_LEN 16
#define MAX_MNEMONIC_LEN 240
#define HOMESCREEN_SIZE 1024
#define UUID_SIZE 12
void config_init(void);
void session_clear(bool lock);
void config_loadDevice(const LoadDevice *msg);
const uint8_t *config_getSeed(bool usePassphrase);
bool config_getU2FRoot(HDNode *node);
bool config_getRootNode(HDNode *node, const char *curve, bool usePassphrase);
bool config_getLabel(char *dest, uint16_t dest_size);
void config_setLabel(const char *label);
bool config_getLanguage(char *dest, uint16_t dest_size);
void config_setLanguage(const char *lang);
void config_setPassphraseProtection(bool passphrase_protection);
bool config_getPassphraseProtection(bool *passphrase_protection);
bool config_getHomescreen(uint8_t *dest, uint16_t dest_size);
void config_setHomescreen(const uint8_t *data, uint32_t size);
void session_cachePassphrase(const char *passphrase);
bool session_isPassphraseCached(void);
bool session_getState(const uint8_t *salt, uint8_t *state,
const char *passphrase);
bool config_setMnemonic(const char *mnemonic);
bool config_containsMnemonic(const char *mnemonic);
bool config_getMnemonic(char *dest, uint16_t dest_size);
bool config_getMnemonicBytes(uint8_t *dest, uint16_t dest_size,
uint16_t *real_size);
#if DEBUG_LINK
bool config_dumpNode(HDNodeType *node);
bool config_getPin(char *dest, uint16_t dest_size);
#endif
bool config_unlock(const char *pin);
bool config_hasPin(void);
bool config_changePin(const char *old_pin, const char *new_pin);
bool session_isUnlocked(void);
uint32_t config_nextU2FCounter(void);
void config_setU2FCounter(uint32_t u2fcounter);
bool config_isInitialized(void);
bool config_getImported(bool *imported);
void config_setImported(bool imported);
bool config_getNeedsBackup(bool *needs_backup);
void config_setNeedsBackup(bool needs_backup);
bool config_getUnfinishedBackup(bool *unfinished_backup);
void config_setUnfinishedBackup(bool unfinished_backup);
bool config_getNoBackup(bool *no_backup);
void config_setNoBackup(void);
void config_applyFlags(uint32_t flags);
bool config_getFlags(uint32_t *flags);
uint32_t config_getAutoLockDelayMs(void);
void config_setAutoLockDelayMs(uint32_t auto_lock_delay_ms);
void config_wipe(void);
extern char config_uuid_str[2 * UUID_SIZE + 1];
#endif

498
legacy/firmware/crypto.c Normal file
View File

@ -0,0 +1,498 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "crypto.h"
#include <string.h>
#include "address.h"
#include "aes/aes.h"
#include "base58.h"
#include "bip32.h"
#include "cash_addr.h"
#include "coins.h"
#include "curves.h"
#include "hmac.h"
#include "layout.h"
#include "pbkdf2.h"
#include "secp256k1.h"
#include "segwit_addr.h"
#include "sha2.h"
uint32_t ser_length(uint32_t len, uint8_t *out) {
if (len < 253) {
out[0] = len & 0xFF;
return 1;
}
if (len < 0x10000) {
out[0] = 253;
out[1] = len & 0xFF;
out[2] = (len >> 8) & 0xFF;
return 3;
}
out[0] = 254;
out[1] = len & 0xFF;
out[2] = (len >> 8) & 0xFF;
out[3] = (len >> 16) & 0xFF;
out[4] = (len >> 24) & 0xFF;
return 5;
}
uint32_t ser_length_hash(Hasher *hasher, uint32_t len) {
if (len < 253) {
hasher_Update(hasher, (const uint8_t *)&len, 1);
return 1;
}
if (len < 0x10000) {
uint8_t d = 253;
hasher_Update(hasher, &d, 1);
hasher_Update(hasher, (const uint8_t *)&len, 2);
return 3;
}
uint8_t d = 254;
hasher_Update(hasher, &d, 1);
hasher_Update(hasher, (const uint8_t *)&len, 4);
return 5;
}
uint32_t deser_length(const uint8_t *in, uint32_t *out) {
if (in[0] < 253) {
*out = in[0];
return 1;
}
if (in[0] == 253) {
*out = in[1] + (in[2] << 8);
return 1 + 2;
}
if (in[0] == 254) {
*out = in[1] + (in[2] << 8) + (in[3] << 16) + ((uint32_t)in[4] << 24);
return 1 + 4;
}
*out = 0; // ignore 64 bit
return 1 + 8;
}
int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len,
uint8_t *signature) {
signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes
return hdnode_sign(node, message, message_len, HASHER_SHA2, signature + 1,
NULL, NULL);
}
int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len,
uint8_t *signature) {
signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes
const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME);
if (ed25519_curve_info && node->curve == ed25519_curve_info) {
// GPG supports variable size digest for Ed25519 signatures
return hdnode_sign(node, message, message_len, 0, signature + 1, NULL,
NULL);
} else {
// Ensure 256-bit digest before proceeding
if (message_len != 32) {
return 1;
}
return hdnode_sign_digest(node, message, signature + 1, NULL, NULL);
}
}
static void cryptoMessageHash(const CoinInfo *coin, const uint8_t *message,
size_t message_len,
uint8_t hash[HASHER_DIGEST_LENGTH]) {
Hasher hasher;
hasher_Init(&hasher, coin->curve->hasher_sign);
hasher_Update(&hasher, (const uint8_t *)coin->signed_message_header,
strlen(coin->signed_message_header));
uint8_t varint[5];
uint32_t l = ser_length(message_len, varint);
hasher_Update(&hasher, varint, l);
hasher_Update(&hasher, message, message_len);
hasher_Final(&hasher, hash);
}
int cryptoMessageSign(const CoinInfo *coin, HDNode *node,
InputScriptType script_type, const uint8_t *message,
size_t message_len, uint8_t *signature) {
uint8_t hash[HASHER_DIGEST_LENGTH];
cryptoMessageHash(coin, message, message_len, hash);
uint8_t pby;
int result = hdnode_sign_digest(node, hash, signature + 1, &pby, NULL);
if (result == 0) {
switch (script_type) {
case InputScriptType_SPENDP2SHWITNESS:
// segwit-in-p2sh
signature[0] = 35 + pby;
break;
case InputScriptType_SPENDWITNESS:
// segwit
signature[0] = 39 + pby;
break;
default:
// p2pkh
signature[0] = 31 + pby;
break;
}
}
return result;
}
int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
size_t message_len, const char *address,
const uint8_t *signature) {
// check for invalid signature prefix
if (signature[0] < 27 || signature[0] > 43) {
return 1;
}
uint8_t hash[HASHER_DIGEST_LENGTH];
cryptoMessageHash(coin, message, message_len, hash);
uint8_t recid = (signature[0] - 27) % 4;
bool compressed = signature[0] >= 31;
// check if signature verifies the digest and recover the public key
uint8_t pubkey[65];
if (ecdsa_recover_pub_from_sig(coin->curve->params, pubkey, signature + 1,
hash, recid) != 0) {
return 3;
}
// convert public key to compressed pubkey if necessary
if (compressed) {
pubkey[0] = 0x02 | (pubkey[64] & 1);
}
// check if the address is correct
uint8_t addr_raw[MAX_ADDR_RAW_SIZE];
uint8_t recovered_raw[MAX_ADDR_RAW_SIZE];
// p2pkh
if (signature[0] >= 27 && signature[0] <= 34) {
size_t len;
if (coin->cashaddr_prefix) {
if (!cash_addr_decode(addr_raw, &len, coin->cashaddr_prefix, address)) {
return 2;
}
} else {
len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw,
MAX_ADDR_RAW_SIZE);
}
ecdsa_get_address_raw(pubkey, coin->address_type,
coin->curve->hasher_pubkey, recovered_raw);
if (memcmp(recovered_raw, addr_raw, len) != 0 ||
len != address_prefix_bytes_len(coin->address_type) + 20) {
return 2;
}
} else
// segwit-in-p2sh
if (signature[0] >= 35 && signature[0] <= 38) {
size_t len = base58_decode_check(address, coin->curve->hasher_base58,
addr_raw, MAX_ADDR_RAW_SIZE);
ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh,
coin->curve->hasher_pubkey,
recovered_raw);
if (memcmp(recovered_raw, addr_raw, len) != 0 ||
len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) {
return 2;
}
} else
// segwit
if (signature[0] >= 39 && signature[0] <= 42) {
int witver;
size_t len;
if (!coin->bech32_prefix ||
!segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix,
address)) {
return 4;
}
ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw);
if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) {
return 2;
}
} else {
return 4;
}
return 0;
}
/* ECIES disabled
int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t
msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t
*payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t
*privkey, const uint8_t *address_raw)
{
if (privkey && address_raw) { // signing == true
HDNode node;
payload[0] = display_only ? 0x81 : 0x01;
uint32_t l = ser_length(msg_size, payload + 1);
memcpy(payload + 1 + l, msg, msg_size);
memcpy(payload + 1 + l + msg_size, address_raw, 21);
hdnode_from_xprv(0, 0, 0, privkey, privkey, SECP256K1_NAME,
&node); if (cryptoMessageSign(&node, msg, msg_size, payload + 1 + l + msg_size +
21) != 0) { return 1;
}
*payload_len = 1 + l + msg_size + 21 + 65;
} else {
payload[0] = display_only ? 0x80 : 0x00;
uint32_t l = ser_length(msg_size, payload + 1);
memcpy(payload + 1 + l, msg, msg_size);
*payload_len = 1 + l + msg_size;
}
// generate random nonce
curve_point R;
bignum256 k;
if (generate_k_random(&secp256k1, &k) != 0) {
return 2;
}
// compute k*G
scalar_multiply(&secp256k1, &k, &R);
nonce[0] = 0x02 | (R.y.val[0] & 0x01);
bn_write_be(&R.x, nonce + 1);
*nonce_len = 33;
// compute shared secret
point_multiply(&secp256k1, &k, pubkey, &R);
uint8_t shared_secret[33];
shared_secret[0] = 0x02 | (R.y.val[0] & 0x01);
bn_write_be(&R.x, shared_secret + 1);
// generate keying bytes
uint8_t keying_bytes[80];
uint8_t salt[22 + 33];
memcpy(salt, "Bitcoin Secure Message", 22);
memcpy(salt + 22, nonce, 33);
pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes,
80);
// encrypt payload
aes_encrypt_ctx ctx;
aes_encrypt_key256(keying_bytes, &ctx);
aes_cfb_encrypt(payload, payload, *payload_len, keying_bytes + 64,
&ctx);
// compute hmac
uint8_t out[32];
hmac_sha256(keying_bytes + 32, 32, payload, *payload_len, out);
memcpy(hmac, out, 8);
*hmac_len = 8;
return 0;
}
int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t
payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey,
uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t
*address_raw)
{
if (hmac_len != 8) {
return 1;
}
// compute shared secret
curve_point R;
bignum256 k;
bn_read_be(privkey, &k);
point_multiply(&secp256k1, &k, nonce, &R);
uint8_t shared_secret[33];
shared_secret[0] = 0x02 | (R.y.val[0] & 0x01);
bn_write_be(&R.x, shared_secret + 1);
// generate keying bytes
uint8_t keying_bytes[80];
uint8_t salt[22 + 33];
memcpy(salt, "Bitcoin Secure Message", 22);
salt[22] = 0x02 | (nonce->y.val[0] & 0x01);
bn_write_be(&(nonce->x), salt + 23);
pbkdf2_hmac_sha256(shared_secret, 33, salt, 22 + 33, 2048, keying_bytes,
80);
// compute hmac
uint8_t out[32];
hmac_sha256(keying_bytes + 32, 32, payload, payload_len, out);
if (memcmp(hmac, out, 8) != 0) {
return 2;
}
// decrypt payload
aes_encrypt_ctx ctx;
aes_encrypt_key256(keying_bytes, &ctx);
aes_cfb_decrypt(payload, payload, payload_len, keying_bytes + 64, &ctx);
// check first byte
if (payload[0] != 0x00 && payload[0] != 0x01 && payload[0] != 0x80 &&
payload[0] != 0x81) { return 3;
}
*signing = payload[0] & 0x01;
*display_only = payload[0] & 0x80;
uint32_t l, o;
l = deser_length(payload + 1, &o);
if (*signing) {
// FIXME: assumes a raw address is 21 bytes (also below).
if (1 + l + o + 21 + 65 != payload_len) {
return 4;
}
// FIXME: cryptoMessageVerify changed to take the address_type
as a parameter. if (cryptoMessageVerify(payload + 1 + l, o, payload + 1 + l + o,
payload + 1 + l + o + 21) != 0) { return 5;
}
memcpy(address_raw, payload + 1 + l + o, 21);
} else {
if (1 + l + o != payload_len) {
return 4;
}
}
memcpy(msg, payload + 1 + l, o);
*msg_len = o;
return 0;
}
*/
const HDNode *cryptoMultisigPubkey(const CoinInfo *coin,
const MultisigRedeemScriptType *multisig,
uint32_t index) {
const HDNodeType *node_ptr;
const uint32_t *address_n;
uint32_t address_n_count;
if (multisig->nodes_count) { // use multisig->nodes
if (index >= multisig->nodes_count) {
return 0;
}
node_ptr = &(multisig->nodes[index]);
address_n = multisig->address_n;
address_n_count = multisig->address_n_count;
} else if (multisig->pubkeys_count) { // use multisig->pubkeys
if (index >= multisig->pubkeys_count) {
return 0;
}
node_ptr = &(multisig->pubkeys[index].node);
address_n = multisig->pubkeys[index].address_n;
address_n_count = multisig->pubkeys[index].address_n_count;
} else {
return 0;
}
if (node_ptr->chain_code.size != 32) return 0;
if (!node_ptr->has_public_key || node_ptr->public_key.size != 33) return 0;
static HDNode node;
if (!hdnode_from_xpub(node_ptr->depth, node_ptr->child_num,
node_ptr->chain_code.bytes, node_ptr->public_key.bytes,
coin->curve_name, &node)) {
return 0;
}
layoutProgressUpdate(true);
for (uint32_t i = 0; i < address_n_count; i++) {
if (!hdnode_public_ckd(&node, address_n[i])) {
return 0;
}
layoutProgressUpdate(true);
}
return &node;
}
uint32_t cryptoMultisigPubkeyCount(const MultisigRedeemScriptType *multisig) {
return multisig->nodes_count ? multisig->nodes_count
: multisig->pubkeys_count;
}
int cryptoMultisigPubkeyIndex(const CoinInfo *coin,
const MultisigRedeemScriptType *multisig,
const uint8_t *pubkey) {
for (size_t i = 0; i < cryptoMultisigPubkeyCount(multisig); i++) {
const HDNode *pubnode = cryptoMultisigPubkey(coin, multisig, i);
if (pubnode && memcmp(pubnode->public_key, pubkey, 33) == 0) {
return i;
}
}
return -1;
}
int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig,
uint8_t *hash) {
static const HDNodeType *pubnodes[15], *swap;
const uint32_t n = cryptoMultisigPubkeyCount(multisig);
if (n < 1 || n > 15) {
return 0;
}
if (!multisig->has_m || multisig->m < 1 || multisig->m > 15) {
return 0;
}
for (uint32_t i = 0; i < n; i++) {
if (multisig->nodes_count) { // use multisig->nodes
pubnodes[i] = &(multisig->nodes[i]);
} else if (multisig->pubkeys_count) { // use multisig->pubkeys
pubnodes[i] = &(multisig->pubkeys[i].node);
} else {
return 0;
}
}
for (uint32_t i = 0; i < n; i++) {
if (!pubnodes[i]->has_public_key || pubnodes[i]->public_key.size != 33)
return 0;
if (pubnodes[i]->chain_code.size != 32) return 0;
}
// minsort according to pubkey
for (uint32_t i = 0; i < n - 1; i++) {
for (uint32_t j = n - 1; j > i; j--) {
if (memcmp(pubnodes[i]->public_key.bytes, pubnodes[j]->public_key.bytes,
33) > 0) {
swap = pubnodes[i];
pubnodes[i] = pubnodes[j];
pubnodes[j] = swap;
}
}
}
// hash sorted nodes
SHA256_CTX ctx;
sha256_Init(&ctx);
sha256_Update(&ctx, (const uint8_t *)&(multisig->m), sizeof(uint32_t));
for (uint32_t i = 0; i < n; i++) {
sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->depth),
sizeof(uint32_t));
sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->fingerprint),
sizeof(uint32_t));
sha256_Update(&ctx, (const uint8_t *)&(pubnodes[i]->child_num),
sizeof(uint32_t));
sha256_Update(&ctx, pubnodes[i]->chain_code.bytes, 32);
sha256_Update(&ctx, pubnodes[i]->public_key.bytes, 33);
}
sha256_Update(&ctx, (const uint8_t *)&n, sizeof(uint32_t));
sha256_Final(&ctx, hash);
layoutProgressUpdate(true);
return 1;
}
int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash) {
SHA256_CTX ctx;
sha256_Init(&ctx);
sha256_Update(&ctx, (const uint8_t *)&(identity->index), sizeof(uint32_t));
if (identity->has_proto && identity->proto[0]) {
sha256_Update(&ctx, (const uint8_t *)(identity->proto),
strlen(identity->proto));
sha256_Update(&ctx, (const uint8_t *)"://", 3);
}
if (identity->has_user && identity->user[0]) {
sha256_Update(&ctx, (const uint8_t *)(identity->user),
strlen(identity->user));
sha256_Update(&ctx, (const uint8_t *)"@", 1);
}
if (identity->has_host && identity->host[0]) {
sha256_Update(&ctx, (const uint8_t *)(identity->host),
strlen(identity->host));
}
if (identity->has_port && identity->port[0]) {
sha256_Update(&ctx, (const uint8_t *)":", 1);
sha256_Update(&ctx, (const uint8_t *)(identity->port),
strlen(identity->port));
}
if (identity->has_path && identity->path[0]) {
sha256_Update(&ctx, (const uint8_t *)(identity->path),
strlen(identity->path));
}
sha256_Final(&ctx, hash);
return 1;
}

82
legacy/firmware/crypto.h Normal file
View File

@ -0,0 +1,82 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __CRYPTO_H__
#define __CRYPTO_H__
#include <bip32.h>
#include <ecdsa.h>
#include <pb.h>
#include <sha2.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include "coins.h"
#include "hasher.h"
#include "messages-bitcoin.pb.h"
#include "messages-crypto.pb.h"
#define ser_length_size(len) ((len) < 253 ? 1 : (len) < 0x10000 ? 3 : 5)
uint32_t ser_length(uint32_t len, uint8_t *out);
uint32_t ser_length_hash(Hasher *hasher, uint32_t len);
int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len,
uint8_t *signature);
int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len,
uint8_t *signature);
int cryptoMessageSign(const CoinInfo *coin, HDNode *node,
InputScriptType script_type, const uint8_t *message,
size_t message_len, uint8_t *signature);
int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message,
size_t message_len, const char *address,
const uint8_t *signature);
/* ECIES disabled
int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t
msg_size, bool display_only, uint8_t *nonce, size_t *nonce_len, uint8_t
*payload, size_t *payload_len, uint8_t *hmac, size_t *hmac_len, const uint8_t
*privkey, const uint8_t *address_raw);
int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t
payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey,
uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t
*address_raw);
*/
const HDNode *cryptoMultisigPubkey(const CoinInfo *coin,
const MultisigRedeemScriptType *multisig,
uint32_t index);
uint32_t cryptoMultisigPubkeyCount(const MultisigRedeemScriptType *multisig);
int cryptoMultisigPubkeyIndex(const CoinInfo *coin,
const MultisigRedeemScriptType *multisig,
const uint8_t *pubkey);
int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig,
uint8_t *hash);
int cryptoIdentityFingerprint(const IdentityType *identity, uint8_t *hash);
#endif

65
legacy/firmware/debug.c Normal file
View File

@ -0,0 +1,65 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "debug.h"
#include "oled.h"
#include "trezor.h"
#include "util.h"
#if DEBUG_LOG
void oledDebug(const char *line) {
static const char *lines[8] = {0, 0, 0, 0, 0, 0, 0, 0};
static char id = 3;
for (int i = 0; i < 7; i++) {
lines[i] = lines[i + 1];
}
lines[7] = line;
oledClear();
for (int i = 0; i < 8; i++) {
if (lines[i]) {
oledDrawChar(0, i * 8, '0' + (id + i) % 10, FONT_STANDARD);
oledDrawString(8, i * 8, lines[i], FONT_STANDARD);
}
}
oledRefresh();
id = (id + 1) % 10;
}
void debugLog(int level, const char *bucket, const char *text) {
(void)level;
(void)bucket;
#if EMULATOR
puts(text);
#else
oledDebug(text);
#endif
}
char *debugInt(const uint32_t i) {
static uint8_t n = 0;
static char id[8][9];
uint32hex(i, id[n]);
debugLog(0, "", id[n]);
char *ret = (char *)id[n];
n = (n + 1) % 8;
return ret;
}
#endif

42
legacy/firmware/debug.h Normal file
View File

@ -0,0 +1,42 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __DEBUG_H__
#define __DEBUG_H__
#include <stdint.h>
#include "trezor.h"
#if DEBUG_LOG
void debugLog(int level, const char *bucket, const char *text);
char *debugInt(const uint32_t i);
#else
#define debugLog(L, B, T) \
do { \
} while (0)
#define debugInt(I) \
do { \
} while (0)
#endif
#endif

1
legacy/firmware/defs Symbolic link
View File

@ -0,0 +1 @@
../vendor/trezor-common/defs

771
legacy/firmware/ethereum.c Normal file
View File

@ -0,0 +1,771 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2016 Alex Beregszaszi <alex@rtfs.hu>
* Copyright (C) 2016 Pavol Rusnak <stick@satoshilabs.com>
* Copyright (C) 2016 Jochen Hoenicke <hoenicke@gmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ethereum.h"
#include "address.h"
#include "crypto.h"
#include "ecdsa.h"
#include "ethereum_networks.h"
#include "ethereum_tokens.h"
#include "fsm.h"
#include "gettext.h"
#include "layout2.h"
#include "memzero.h"
#include "messages.h"
#include "messages.pb.h"
#include "protect.h"
#include "secp256k1.h"
#include "sha3.h"
#include "transaction.h"
#include "util.h"
/* maximum supported chain id. v must fit in an uint32_t. */
#define MAX_CHAIN_ID 2147483629
static bool ethereum_signing = false;
static uint32_t data_total, data_left;
static EthereumTxRequest msg_tx_request;
static CONFIDENTIAL uint8_t privkey[32];
static uint32_t chain_id;
static uint32_t tx_type;
struct SHA3_CTX keccak_ctx;
static inline void hash_data(const uint8_t *buf, size_t size) {
sha3_Update(&keccak_ctx, buf, size);
}
/*
* Push an RLP encoded length to the hash buffer.
*/
static void hash_rlp_length(uint32_t length, uint8_t firstbyte) {
uint8_t buf[4];
if (length == 1 && firstbyte <= 0x7f) {
/* empty length header */
} else if (length <= 55) {
buf[0] = 0x80 + length;
hash_data(buf, 1);
} else if (length <= 0xff) {
buf[0] = 0xb7 + 1;
buf[1] = length;
hash_data(buf, 2);
} else if (length <= 0xffff) {
buf[0] = 0xb7 + 2;
buf[1] = length >> 8;
buf[2] = length & 0xff;
hash_data(buf, 3);
} else {
buf[0] = 0xb7 + 3;
buf[1] = length >> 16;
buf[2] = length >> 8;
buf[3] = length & 0xff;
hash_data(buf, 4);
}
}
/*
* Push an RLP encoded list length to the hash buffer.
*/
static void hash_rlp_list_length(uint32_t length) {
uint8_t buf[4];
if (length <= 55) {
buf[0] = 0xc0 + length;
hash_data(buf, 1);
} else if (length <= 0xff) {
buf[0] = 0xf7 + 1;
buf[1] = length;
hash_data(buf, 2);
} else if (length <= 0xffff) {
buf[0] = 0xf7 + 2;
buf[1] = length >> 8;
buf[2] = length & 0xff;
hash_data(buf, 3);
} else {
buf[0] = 0xf7 + 3;
buf[1] = length >> 16;
buf[2] = length >> 8;
buf[3] = length & 0xff;
hash_data(buf, 4);
}
}
/*
* Push an RLP encoded length field and data to the hash buffer.
*/
static void hash_rlp_field(const uint8_t *buf, size_t size) {
hash_rlp_length(size, buf[0]);
hash_data(buf, size);
}
/*
* Push an RLP encoded number to the hash buffer.
* Ethereum yellow paper says to convert to big endian and strip leading zeros.
*/
static void hash_rlp_number(uint32_t number) {
if (!number) {
return;
}
uint8_t data[4];
data[0] = (number >> 24) & 0xff;
data[1] = (number >> 16) & 0xff;
data[2] = (number >> 8) & 0xff;
data[3] = (number)&0xff;
int offset = 0;
while (!data[offset]) {
offset++;
}
hash_rlp_field(data + offset, 4 - offset);
}
/*
* Calculate the number of bytes needed for an RLP length header.
* NOTE: supports up to 16MB of data (how unlikely...)
* FIXME: improve
*/
static int rlp_calculate_length(int length, uint8_t firstbyte) {
if (length == 1 && firstbyte <= 0x7f) {
return 1;
} else if (length <= 55) {
return 1 + length;
} else if (length <= 0xff) {
return 2 + length;
} else if (length <= 0xffff) {
return 3 + length;
} else {
return 4 + length;
}
}
static int rlp_calculate_number_length(uint32_t number) {
if (number <= 0x7f) {
return 1;
} else if (number <= 0xff) {
return 2;
} else if (number <= 0xffff) {
return 3;
} else if (number <= 0xffffff) {
return 4;
} else {
return 5;
}
}
static void send_request_chunk(void) {
int progress = 1000 - (data_total > 1000000 ? data_left / (data_total / 800)
: data_left * 800 / data_total);
layoutProgress(_("Signing"), progress);
msg_tx_request.has_data_length = true;
msg_tx_request.data_length = data_left <= 1024 ? data_left : 1024;
msg_write(MessageType_MessageType_EthereumTxRequest, &msg_tx_request);
}
static int ethereum_is_canonic(uint8_t v, uint8_t signature[64]) {
(void)signature;
return (v & 2) == 0;
}
static void send_signature(void) {
uint8_t hash[32], sig[64];
uint8_t v;
layoutProgress(_("Signing"), 1000);
/* eip-155 replay protection */
if (chain_id) {
/* hash v=chain_id, r=0, s=0 */
hash_rlp_number(chain_id);
hash_rlp_length(0, 0);
hash_rlp_length(0, 0);
}
keccak_Final(&keccak_ctx, hash);
if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, &v,
ethereum_is_canonic) != 0) {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed"));
ethereum_signing_abort();
return;
}
memzero(privkey, sizeof(privkey));
/* Send back the result */
msg_tx_request.has_data_length = false;
msg_tx_request.has_signature_v = true;
if (chain_id > MAX_CHAIN_ID) {
msg_tx_request.signature_v = v;
} else if (chain_id) {
msg_tx_request.signature_v = v + 2 * chain_id + 35;
} else {
msg_tx_request.signature_v = v + 27;
}
msg_tx_request.has_signature_r = true;
msg_tx_request.signature_r.size = 32;
memcpy(msg_tx_request.signature_r.bytes, sig, 32);
msg_tx_request.has_signature_s = true;
msg_tx_request.signature_s.size = 32;
memcpy(msg_tx_request.signature_s.bytes, sig + 32, 32);
msg_write(MessageType_MessageType_EthereumTxRequest, &msg_tx_request);
ethereum_signing_abort();
}
/* Format a 256 bit number (amount in wei) into a human readable format
* using standard ethereum units.
* The buffer must be at least 25 bytes.
*/
static void ethereumFormatAmount(const bignum256 *amnt, const TokenType *token,
char *buf, int buflen) {
bignum256 bn1e9;
bn_read_uint32(1000000000, &bn1e9);
const char *suffix = NULL;
int decimals = 18;
if (token == UnknownToken) {
strlcpy(buf, "Unknown token value", buflen);
return;
} else if (token != NULL) {
suffix = token->ticker;
decimals = token->decimals;
} else if (bn_is_less(amnt, &bn1e9)) {
suffix = " Wei";
decimals = 0;
} else {
if (tx_type == 1 || tx_type == 6) {
suffix = " WAN";
} else {
ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id);
}
}
bn_format(amnt, NULL, suffix, decimals, 0, false, buf, buflen);
}
static void layoutEthereumConfirmTx(const uint8_t *to, uint32_t to_len,
const uint8_t *value, uint32_t value_len,
const TokenType *token) {
bignum256 val;
uint8_t pad_val[32];
memzero(pad_val, sizeof(pad_val));
memcpy(pad_val + (32 - value_len), value, value_len);
bn_read_be(pad_val, &val);
char amount[32];
if (token == NULL) {
if (bn_is_zero(&val)) {
strcpy(amount, _("message"));
} else {
ethereumFormatAmount(&val, NULL, amount, sizeof(amount));
}
} else {
ethereumFormatAmount(&val, token, amount, sizeof(amount));
}
char _to1[] = "to 0x__________";
char _to2[] = "_______________";
char _to3[] = "_______________?";
if (to_len) {
char to_str[41];
bool rskip60 = false;
// constants from trezor-common/defs/ethereum/networks.json
switch (chain_id) {
case 30:
rskip60 = true;
break;
case 31:
rskip60 = true;
break;
}
ethereum_address_checksum(to, to_str, rskip60, chain_id);
memcpy(_to1 + 5, to_str, 10);
memcpy(_to2, to_str + 10, 15);
memcpy(_to3, to_str + 25, 15);
} else {
strlcpy(_to1, _("to new contract?"), sizeof(_to1));
strlcpy(_to2, "", sizeof(_to2));
strlcpy(_to3, "", sizeof(_to3));
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Send"), amount, _to1, _to2, _to3, NULL);
}
static void layoutEthereumData(const uint8_t *data, uint32_t len,
uint32_t total_len) {
char hexdata[3][17];
char summary[20];
uint32_t printed = 0;
for (int i = 0; i < 3; i++) {
uint32_t linelen = len - printed;
if (linelen > 8) {
linelen = 8;
}
data2hex(data, linelen, hexdata[i]);
data += linelen;
printed += linelen;
}
strcpy(summary, "... bytes");
char *p = summary + 11;
uint32_t number = total_len;
while (number > 0) {
*p-- = '0' + number % 10;
number = number / 10;
}
char *summarystart = summary;
if (total_len == printed) summarystart = summary + 4;
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Transaction data:"), hexdata[0], hexdata[1], hexdata[2],
summarystart, NULL);
}
static void layoutEthereumFee(const uint8_t *value, uint32_t value_len,
const uint8_t *gas_price, uint32_t gas_price_len,
const uint8_t *gas_limit, uint32_t gas_limit_len,
bool is_token) {
bignum256 val, gas;
uint8_t pad_val[32];
char tx_value[32];
char gas_value[32];
memzero(tx_value, sizeof(tx_value));
memzero(gas_value, sizeof(gas_value));
memzero(pad_val, sizeof(pad_val));
memcpy(pad_val + (32 - gas_price_len), gas_price, gas_price_len);
bn_read_be(pad_val, &val);
memzero(pad_val, sizeof(pad_val));
memcpy(pad_val + (32 - gas_limit_len), gas_limit, gas_limit_len);
bn_read_be(pad_val, &gas);
bn_multiply(&val, &gas, &secp256k1.prime);
ethereumFormatAmount(&gas, NULL, gas_value, sizeof(gas_value));
memzero(pad_val, sizeof(pad_val));
memcpy(pad_val + (32 - value_len), value, value_len);
bn_read_be(pad_val, &val);
if (bn_is_zero(&val)) {
strcpy(tx_value, is_token ? _("token") : _("message"));
} else {
ethereumFormatAmount(&val, NULL, tx_value, sizeof(tx_value));
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Really send"), tx_value, _("paying up to"), gas_value,
_("for gas?"), NULL);
}
/*
* RLP fields:
* - nonce (0 .. 32)
* - gas_price (0 .. 32)
* - gas_limit (0 .. 32)
* - to (0, 20)
* - value (0 .. 32)
* - data (0 ..)
*/
static bool ethereum_signing_check(const EthereumSignTx *msg) {
if (!msg->has_gas_price || !msg->has_gas_limit) {
return false;
}
size_t tolen = msg->has_to ? strlen(msg->to) : 0;
if (tolen != 42 && tolen != 40 && tolen != 0) {
/* Address has wrong length */
return false;
}
// sending transaction to address 0 (contract creation) without a data field
if (tolen == 0 && (!msg->has_data_length || msg->data_length == 0)) {
return false;
}
if (msg->gas_price.size + msg->gas_limit.size > 30) {
// sanity check that fee doesn't overflow
return false;
}
return true;
}
void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node) {
ethereum_signing = true;
sha3_256_Init(&keccak_ctx);
memzero(&msg_tx_request, sizeof(EthereumTxRequest));
/* set fields to 0, to avoid conditions later */
if (!msg->has_value) msg->value.size = 0;
if (!msg->has_data_initial_chunk) msg->data_initial_chunk.size = 0;
bool toset;
uint8_t pubkeyhash[20];
if (msg->has_to && ethereum_parse(msg->to, pubkeyhash)) {
toset = true;
} else {
msg->to[0] = 0;
toset = false;
memzero(pubkeyhash, sizeof(pubkeyhash));
}
if (!msg->has_nonce) msg->nonce.size = 0;
/* eip-155 chain id */
if (msg->has_chain_id) {
if (msg->chain_id < 1) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Chain Id out of bounds"));
ethereum_signing_abort();
return;
}
chain_id = msg->chain_id;
} else {
chain_id = 0;
}
/* Wanchain txtype */
if (msg->has_tx_type) {
if (msg->tx_type == 1 || msg->tx_type == 6) {
tx_type = msg->tx_type;
} else {
fsm_sendFailure(FailureType_Failure_DataError, _("Txtype out of bounds"));
ethereum_signing_abort();
return;
}
} else {
tx_type = 0;
}
if (msg->has_data_length && msg->data_length > 0) {
if (!msg->has_data_initial_chunk || msg->data_initial_chunk.size == 0) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Data length provided, but no initial chunk"));
ethereum_signing_abort();
return;
}
/* Our encoding only supports transactions up to 2^24 bytes. To
* prevent exceeding the limit we use a stricter limit on data length.
*/
if (msg->data_length > 16000000) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Data length exceeds limit"));
ethereum_signing_abort();
return;
}
data_total = msg->data_length;
} else {
data_total = 0;
}
if (msg->data_initial_chunk.size > data_total) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid size of initial chunk"));
ethereum_signing_abort();
return;
}
// safety checks
if (!ethereum_signing_check(msg)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Safety check failed"));
ethereum_signing_abort();
return;
}
const TokenType *token = NULL;
// detect ERC-20 token
if (toset && msg->value.size == 0 && data_total == 68 &&
msg->data_initial_chunk.size == 68 &&
memcmp(msg->data_initial_chunk.bytes,
"\xa9\x05\x9c\xbb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
16) == 0) {
token = tokenByChainAddress(chain_id, pubkeyhash);
}
if (token != NULL) {
layoutEthereumConfirmTx(msg->data_initial_chunk.bytes + 16, 20,
msg->data_initial_chunk.bytes + 36, 32, token);
} else {
layoutEthereumConfirmTx(pubkeyhash, 20, msg->value.bytes, msg->value.size,
NULL);
}
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
ethereum_signing_abort();
return;
}
if (token == NULL && data_total > 0) {
layoutEthereumData(msg->data_initial_chunk.bytes,
msg->data_initial_chunk.size, data_total);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
ethereum_signing_abort();
return;
}
}
layoutEthereumFee(msg->value.bytes, msg->value.size, msg->gas_price.bytes,
msg->gas_price.size, msg->gas_limit.bytes,
msg->gas_limit.size, token != NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
ethereum_signing_abort();
return;
}
/* Stage 1: Calculate total RLP length */
uint32_t rlp_length = 0;
layoutProgress(_("Signing"), 0);
rlp_length += rlp_calculate_length(msg->nonce.size, msg->nonce.bytes[0]);
rlp_length +=
rlp_calculate_length(msg->gas_price.size, msg->gas_price.bytes[0]);
rlp_length +=
rlp_calculate_length(msg->gas_limit.size, msg->gas_limit.bytes[0]);
rlp_length += rlp_calculate_length(toset ? 20 : 0, pubkeyhash[0]);
rlp_length += rlp_calculate_length(msg->value.size, msg->value.bytes[0]);
rlp_length +=
rlp_calculate_length(data_total, msg->data_initial_chunk.bytes[0]);
if (tx_type) {
rlp_length += rlp_calculate_number_length(tx_type);
}
if (chain_id) {
rlp_length += rlp_calculate_number_length(chain_id);
rlp_length += rlp_calculate_length(0, 0);
rlp_length += rlp_calculate_length(0, 0);
}
/* Stage 2: Store header fields */
hash_rlp_list_length(rlp_length);
layoutProgress(_("Signing"), 100);
if (tx_type) {
hash_rlp_number(tx_type);
}
hash_rlp_field(msg->nonce.bytes, msg->nonce.size);
hash_rlp_field(msg->gas_price.bytes, msg->gas_price.size);
hash_rlp_field(msg->gas_limit.bytes, msg->gas_limit.size);
hash_rlp_field(pubkeyhash, toset ? 20 : 0);
hash_rlp_field(msg->value.bytes, msg->value.size);
hash_rlp_length(data_total, msg->data_initial_chunk.bytes[0]);
hash_data(msg->data_initial_chunk.bytes, msg->data_initial_chunk.size);
data_left = data_total - msg->data_initial_chunk.size;
memcpy(privkey, node->private_key, 32);
if (data_left > 0) {
send_request_chunk();
} else {
send_signature();
}
}
void ethereum_signing_txack(const EthereumTxAck *tx) {
if (!ethereum_signing) {
fsm_sendFailure(FailureType_Failure_UnexpectedMessage,
_("Not in Ethereum signing mode"));
layoutHome();
return;
}
if (tx->data_chunk.size > data_left) {
fsm_sendFailure(FailureType_Failure_DataError, _("Too much data"));
ethereum_signing_abort();
return;
}
if (data_left > 0 && (!tx->has_data_chunk || tx->data_chunk.size == 0)) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Empty data chunk received"));
ethereum_signing_abort();
return;
}
hash_data(tx->data_chunk.bytes, tx->data_chunk.size);
data_left -= tx->data_chunk.size;
if (data_left > 0) {
send_request_chunk();
} else {
send_signature();
}
}
void ethereum_signing_abort(void) {
if (ethereum_signing) {
memzero(privkey, sizeof(privkey));
layoutHome();
ethereum_signing = false;
}
}
static void ethereum_message_hash(const uint8_t *message, size_t message_len,
uint8_t hash[32]) {
struct SHA3_CTX ctx;
sha3_256_Init(&ctx);
sha3_Update(&ctx, (const uint8_t *)"\x19" "Ethereum Signed Message:\n", 26);
uint8_t c;
if (message_len > 1000000000) {
c = '0' + message_len / 1000000000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 100000000) {
c = '0' + message_len / 100000000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 10000000) {
c = '0' + message_len / 10000000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 1000000) {
c = '0' + message_len / 1000000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 100000) {
c = '0' + message_len / 100000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 10000) {
c = '0' + message_len / 10000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 1000) {
c = '0' + message_len / 1000 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 100) {
c = '0' + message_len / 100 % 10;
sha3_Update(&ctx, &c, 1);
}
if (message_len > 10) {
c = '0' + message_len / 10 % 10;
sha3_Update(&ctx, &c, 1);
}
c = '0' + message_len % 10;
sha3_Update(&ctx, &c, 1);
sha3_Update(&ctx, message, message_len);
keccak_Final(&ctx, hash);
}
void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node,
EthereumMessageSignature *resp) {
uint8_t pubkeyhash[20];
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) {
return;
}
resp->has_address = true;
resp->address[0] = '0';
resp->address[1] = 'x';
ethereum_address_checksum(pubkeyhash, resp->address + 2, false, 0);
// ethereum_address_checksum adds trailing zero
uint8_t hash[32];
ethereum_message_hash(msg->message.bytes, msg->message.size, hash);
uint8_t v;
if (ecdsa_sign_digest(&secp256k1, node->private_key, hash,
resp->signature.bytes, &v, ethereum_is_canonic) != 0) {
fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed"));
return;
}
resp->has_signature = true;
resp->signature.bytes[64] = 27 + v;
resp->signature.size = 65;
msg_write(MessageType_MessageType_EthereumMessageSignature, resp);
}
int ethereum_message_verify(const EthereumVerifyMessage *msg) {
if (msg->signature.size != 65) {
fsm_sendFailure(FailureType_Failure_DataError, _("Malformed signature"));
return 1;
}
uint8_t pubkeyhash[20];
if (!ethereum_parse(msg->address, pubkeyhash)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Malformed address"));
return 1;
}
uint8_t pubkey[65];
uint8_t hash[32];
ethereum_message_hash(msg->message.bytes, msg->message.size, hash);
/* v should be 27, 28 but some implementations use 0,1. We are
* compatible with both.
*/
uint8_t v = msg->signature.bytes[64];
if (v >= 27) {
v -= 27;
}
if (v >= 2 || ecdsa_recover_pub_from_sig(
&secp256k1, pubkey, msg->signature.bytes, hash, v) != 0) {
return 2;
}
struct SHA3_CTX ctx;
sha3_256_Init(&ctx);
sha3_Update(&ctx, pubkey + 1, 64);
keccak_Final(&ctx, hash);
/* result are the least significant 160 bits */
if (memcmp(pubkeyhash, hash + 12, 20) != 0) {
return 2;
}
return 0;
}
bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]) {
memzero(pubkeyhash, 20);
size_t len = strlen(address);
if (len == 40) {
// do nothing
} else if (len == 42) {
// check for "0x" prefix and strip it when required
if (address[0] != '0') return false;
if (address[1] != 'x' && address[1] != 'X') return false;
address += 2;
len -= 2;
} else {
return false;
}
for (size_t i = 0; i < len; i++) {
if (address[i] >= '0' && address[i] <= '9') {
pubkeyhash[i / 2] |= (address[i] - '0') << ((1 - (i % 2)) * 4);
} else if (address[i] >= 'a' && address[i] <= 'f') {
pubkeyhash[i / 2] |= ((address[i] - 'a') + 10) << ((1 - (i % 2)) * 4);
} else if (address[i] >= 'A' && address[i] <= 'F') {
pubkeyhash[i / 2] |= ((address[i] - 'A') + 10) << ((1 - (i % 2)) * 4);
} else {
return false;
}
}
return true;
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2016 Alex Beregszaszi <alex@rtfs.hu>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ETHEREUM_H__
#define __ETHEREUM_H__
#include <stdbool.h>
#include <stdint.h>
#include "bip32.h"
#include "messages-ethereum.pb.h"
void ethereum_signing_init(EthereumSignTx *msg, const HDNode *node);
void ethereum_signing_abort(void);
void ethereum_signing_txack(const EthereumTxAck *msg);
void ethereum_message_sign(const EthereumSignMessage *msg, const HDNode *node,
EthereumMessageSignature *resp);
int ethereum_message_verify(const EthereumVerifyMessage *msg);
bool ethereum_parse(const char *address, uint8_t pubkeyhash[20]);
#endif

View File

@ -0,0 +1,34 @@
<%
BKSL = "\\"
networks = list(supported_on("trezor1", eth))
max_chain_id_length = 0
max_suffix_length = 0
for n in networks:
max_chain_id_length = max(len(str(n.chain_id)), max_chain_id_length)
max_suffix_length = max(len(n.shortcut), max_suffix_length)
def align_chain_id(n):
return "{:>{w}}".format(n.chain_id, w=max_chain_id_length)
def align_suffix(n):
cstr = c_str(" " + n.shortcut) + ";"
# we add two quotes, a space and a semicolon. hence +4 chars
return "{:<{w}}".format(cstr, w=max_suffix_length + 4)
%>\
// This file is automatically generated from ethereum_networks.h.mako
// DO NOT EDIT
#ifndef __ETHEREUM_NETWORKS_H__
#define __ETHEREUM_NETWORKS_H__
#define ASSIGN_ETHEREUM_SUFFIX(suffix, chain_id) ${BKSL}
switch (chain_id) { ${BKSL}
% for n in networks:
case ${align_chain_id(n)}: suffix = ${align_suffix(n)} break; /* ${n.name} */ ${BKSL}
% endfor
default: suffix = " UNKN"; break; /* unknown chain */ ${BKSL}
}
#endif

View File

@ -0,0 +1,24 @@
// This file is automatically generated from ethereum_tokens.c.mako
// DO NOT EDIT
#include <string.h>
#include "ethereum_tokens.h"
const TokenType tokens[TOKENS_COUNT] = {
% for t in supported_on("trezor1", erc20):
{${"{:>2}".format(t.chain_id)}, ${c_str(t.address_bytes)}, " ${ascii(t.symbol)}", ${t.decimals}}, // ${t.chain} / ${t.name}
% endfor
};
const TokenType *UnknownToken = (const TokenType *)1;
const TokenType *tokenByChainAddress(uint32_t chain_id, const uint8_t *address)
{
if (!address) return 0;
for (int i = 0; i < TOKENS_COUNT; i++) {
if (chain_id == tokens[i].chain_id && memcmp(address, tokens[i].address, 20) == 0) {
return &(tokens[i]);
}
}
return UnknownToken;
}

View File

@ -0,0 +1,25 @@
// This file is automatically generated from ethereum_tokens.h.mako
// DO NOT EDIT
#ifndef __ETHEREUM_TOKENS_H__
#define __ETHEREUM_TOKENS_H__
#include <stdint.h>
<% erc20_list = list(supported_on("trezor1", erc20)) %>\
#define TOKENS_COUNT ${len(erc20_list)}
typedef struct {
uint32_t chain_id;
const char * const address;
const char * const ticker;
int decimals;
} TokenType;
extern const TokenType tokens[TOKENS_COUNT];
extern const TokenType *UnknownToken;
const TokenType *tokenByChainAddress(uint32_t chain_id, const uint8_t *address);
#endif

260
legacy/firmware/fsm.c Normal file
View File

@ -0,0 +1,260 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libopencm3/stm32/flash.h>
#include "address.h"
#include "aes/aes.h"
#include "base58.h"
#include "bip32.h"
#include "bip39.h"
#include "coins.h"
#include "config.h"
#include "crypto.h"
#include "curves.h"
#include "debug.h"
#include "ecdsa.h"
#include "ethereum.h"
#include "fsm.h"
#include "gettext.h"
#include "hmac.h"
#include "layout2.h"
#include "lisk.h"
#include "memory.h"
#include "memzero.h"
#include "messages.h"
#include "messages.pb.h"
#include "nem.h"
#include "nem2.h"
#include "oled.h"
#include "pinmatrix.h"
#include "protect.h"
#include "recovery.h"
#include "reset.h"
#include "rfc6979.h"
#include "rng.h"
#include "secp256k1.h"
#include "signing.h"
#include "stellar.h"
#include "supervise.h"
#include "transaction.h"
#include "trezor.h"
#include "usb.h"
#include "util.h"
// message methods
static uint8_t msg_resp[MSG_OUT_SIZE] __attribute__((aligned));
#define RESP_INIT(TYPE) \
TYPE *resp = (TYPE *)(void *)msg_resp; \
_Static_assert(sizeof(msg_resp) >= sizeof(TYPE), #TYPE " is too large"); \
memzero(resp, sizeof(TYPE));
#define CHECK_INITIALIZED \
if (!config_isInitialized()) { \
fsm_sendFailure(FailureType_Failure_NotInitialized, NULL); \
return; \
}
#define CHECK_NOT_INITIALIZED \
if (config_isInitialized()) { \
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, \
_("Device is already initialized. Use Wipe first.")); \
return; \
}
#define CHECK_PIN \
if (!protectPin(true)) { \
layoutHome(); \
return; \
}
#define CHECK_PIN_UNCACHED \
if (!protectPin(false)) { \
layoutHome(); \
return; \
}
#define CHECK_PARAM(cond, errormsg) \
if (!(cond)) { \
fsm_sendFailure(FailureType_Failure_DataError, (errormsg)); \
layoutHome(); \
return; \
}
void fsm_sendSuccess(const char *text) {
RESP_INIT(Success);
if (text) {
resp->has_message = true;
strlcpy(resp->message, text, sizeof(resp->message));
}
msg_write(MessageType_MessageType_Success, resp);
}
#if DEBUG_LINK
void fsm_sendFailureDebug(FailureType code, const char *text,
const char *source)
#else
void fsm_sendFailure(FailureType code, const char *text)
#endif
{
if (protectAbortedByCancel) {
protectAbortedByCancel = false;
}
if (protectAbortedByInitialize) {
fsm_msgInitialize((Initialize *)0);
protectAbortedByInitialize = false;
return;
}
RESP_INIT(Failure);
resp->has_code = true;
resp->code = code;
if (!text) {
switch (code) {
case FailureType_Failure_UnexpectedMessage:
text = _("Unexpected message");
break;
case FailureType_Failure_ButtonExpected:
text = _("Button expected");
break;
case FailureType_Failure_DataError:
text = _("Data error");
break;
case FailureType_Failure_ActionCancelled:
text = _("Action cancelled by user");
break;
case FailureType_Failure_PinExpected:
text = _("PIN expected");
break;
case FailureType_Failure_PinCancelled:
text = _("PIN cancelled");
break;
case FailureType_Failure_PinInvalid:
text = _("PIN invalid");
break;
case FailureType_Failure_InvalidSignature:
text = _("Invalid signature");
break;
case FailureType_Failure_ProcessError:
text = _("Process error");
break;
case FailureType_Failure_NotEnoughFunds:
text = _("Not enough funds");
break;
case FailureType_Failure_NotInitialized:
text = _("Device not initialized");
break;
case FailureType_Failure_PinMismatch:
text = _("PIN mismatch");
break;
case FailureType_Failure_FirmwareError:
text = _("Firmware error");
break;
}
}
#if DEBUG_LINK
resp->has_message = true;
strlcpy(resp->message, source, sizeof(resp->message));
if (text) {
strlcat(resp->message, text, sizeof(resp->message));
}
#else
if (text) {
resp->has_message = true;
strlcpy(resp->message, text, sizeof(resp->message));
}
#endif
msg_write(MessageType_MessageType_Failure, resp);
}
static const CoinInfo *fsm_getCoin(bool has_name, const char *name) {
const CoinInfo *coin;
if (has_name) {
coin = coinByName(name);
} else {
coin = coinByName("Bitcoin");
}
if (!coin) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid coin name"));
layoutHome();
return 0;
}
return coin;
}
static HDNode *fsm_getDerivedNode(const char *curve, const uint32_t *address_n,
size_t address_n_count,
uint32_t *fingerprint) {
static CONFIDENTIAL HDNode node;
if (fingerprint) {
*fingerprint = 0;
}
if (!config_getRootNode(&node, curve, true)) {
fsm_sendFailure(FailureType_Failure_NotInitialized,
_("Device not initialized or passphrase request cancelled "
"or unsupported curve"));
layoutHome();
return 0;
}
if (!address_n || address_n_count == 0) {
return &node;
}
if (hdnode_private_ckd_cached(&node, address_n, address_n_count,
fingerprint) == 0) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive private key"));
layoutHome();
return 0;
}
return &node;
}
static bool fsm_layoutAddress(const char *address, const char *desc,
bool ignorecase, size_t prefixlen,
const uint32_t *address_n, size_t address_n_count,
bool address_is_account) {
bool qrcode = false;
for (;;) {
const char *display_addr = address;
if (prefixlen && !qrcode) {
display_addr += prefixlen;
}
layoutAddress(display_addr, desc, qrcode, ignorecase, address_n,
address_n_count, address_is_account);
if (protectButton(ButtonRequestType_ButtonRequest_Address, false)) {
return true;
}
if (protectAbortedByCancel || protectAbortedByInitialize) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return false;
}
qrcode = !qrcode;
}
}
#include "fsm_msg_coin.h"
#include "fsm_msg_common.h"
#include "fsm_msg_crypto.h"
#include "fsm_msg_debug.h"
#include "fsm_msg_ethereum.h"
#include "fsm_msg_lisk.h"
#include "fsm_msg_nem.h"
#include "fsm_msg_stellar.h"

136
legacy/firmware/fsm.h Normal file
View File

@ -0,0 +1,136 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __FSM_H__
#define __FSM_H__
#include "messages-bitcoin.pb.h"
#include "messages-crypto.pb.h"
#include "messages-debug.pb.h"
#include "messages-ethereum.pb.h"
#include "messages-lisk.pb.h"
#include "messages-management.pb.h"
#include "messages-nem.pb.h"
#include "messages-stellar.pb.h"
// message functions
void fsm_sendSuccess(const char *text);
#if DEBUG_LINK
void fsm_sendFailureDebug(FailureType code, const char *text,
const char *source);
#define fsm_sendFailure(code, text) \
fsm_sendFailureDebug((code), (text), __FILE__ ":" VERSTR(__LINE__) ":")
#else
void fsm_sendFailure(FailureType code, const char *text);
#endif
// void fsm_msgPinMatrixAck(const PinMatrixAck *msg); // tiny
// void fsm_msgButtonAck(const ButtonAck *msg); // tiny
// void fsm_msgPassphraseAck(const PassphraseAck *msg); // tiny
// common
void fsm_msgInitialize(const Initialize *msg);
void fsm_msgGetFeatures(const GetFeatures *msg);
void fsm_msgPing(const Ping *msg);
void fsm_msgChangePin(const ChangePin *msg);
void fsm_msgWipeDevice(const WipeDevice *msg);
void fsm_msgGetEntropy(const GetEntropy *msg);
void fsm_msgLoadDevice(const LoadDevice *msg);
void fsm_msgResetDevice(const ResetDevice *msg);
void fsm_msgEntropyAck(const EntropyAck *msg);
void fsm_msgBackupDevice(const BackupDevice *msg);
void fsm_msgCancel(const Cancel *msg);
void fsm_msgClearSession(const ClearSession *msg);
void fsm_msgApplySettings(const ApplySettings *msg);
void fsm_msgApplyFlags(const ApplyFlags *msg);
void fsm_msgRecoveryDevice(const RecoveryDevice *msg);
void fsm_msgWordAck(const WordAck *msg);
void fsm_msgSetU2FCounter(const SetU2FCounter *msg);
// coin
void fsm_msgGetPublicKey(const GetPublicKey *msg);
void fsm_msgSignTx(const SignTx *msg);
void fsm_msgTxAck(
TxAck *msg); // not const because we mutate input/output scripts
void fsm_msgGetAddress(const GetAddress *msg);
void fsm_msgSignMessage(const SignMessage *msg);
void fsm_msgVerifyMessage(const VerifyMessage *msg);
// crypto
void fsm_msgCipherKeyValue(const CipherKeyValue *msg);
void fsm_msgSignIdentity(const SignIdentity *msg);
void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg);
void fsm_msgCosiCommit(const CosiCommit *msg);
void fsm_msgCosiSign(const CosiSign *msg);
// debug
#if DEBUG_LINK
// void fsm_msgDebugLinkDecision(const DebugLinkDecision *msg); // tiny
void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg);
void fsm_msgDebugLinkStop(const DebugLinkStop *msg);
void fsm_msgDebugLinkMemoryWrite(const DebugLinkMemoryWrite *msg);
void fsm_msgDebugLinkMemoryRead(const DebugLinkMemoryRead *msg);
void fsm_msgDebugLinkFlashErase(const DebugLinkFlashErase *msg);
#endif
// ethereum
void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg);
void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg);
void fsm_msgEthereumSignTx(
EthereumSignTx
*msg); // not const because we mutate transaction during validation
void fsm_msgEthereumTxAck(const EthereumTxAck *msg);
void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg);
void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg);
// lisk
void fsm_msgLiskGetAddress(const LiskGetAddress *msg);
void fsm_msgLiskGetPublicKey(const LiskGetPublicKey *msg);
void fsm_msgLiskSignMessage(const LiskSignMessage *msg);
void fsm_msgLiskVerifyMessage(const LiskVerifyMessage *msg);
void fsm_msgLiskSignTx(LiskSignTx *msg); // not const because we mutate
// transaction during validation
// nem
void fsm_msgNEMGetAddress(
NEMGetAddress *msg); // not const because we mutate msg->network
void fsm_msgNEMSignTx(
NEMSignTx *msg); // not const because we mutate msg->network
void fsm_msgNEMDecryptMessage(
NEMDecryptMessage *msg); // not const because we mutate msg->payload
// stellar
void fsm_msgStellarGetAddress(const StellarGetAddress *msg);
void fsm_msgStellarSignTx(const StellarSignTx *msg);
void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg);
void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg);
void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg);
void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg);
void fsm_msgStellarCreatePassiveOfferOp(const StellarCreatePassiveOfferOp *msg);
void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg);
void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg);
void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg);
void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg);
void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg);
void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg);
#endif

View File

@ -0,0 +1,329 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgGetPublicKey(const GetPublicKey *msg) {
RESP_INIT(PublicKey);
CHECK_INITIALIZED
CHECK_PIN
InputScriptType script_type =
msg->has_script_type ? msg->script_type : InputScriptType_SPENDADDRESS;
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
if (!coin) return;
const char *curve = coin->curve_name;
if (msg->has_ecdsa_curve_name) {
curve = msg->ecdsa_curve_name;
}
uint32_t fingerprint;
HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n,
msg->address_n_count, &fingerprint);
if (!node) return;
hdnode_fill_public_key(node);
if (msg->has_show_display && msg->show_display) {
layoutPublicKey(node->public_key);
if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
resp->has_node = true;
resp->node.depth = node->depth;
resp->node.fingerprint = fingerprint;
resp->node.child_num = node->child_num;
resp->node.chain_code.size = 32;
memcpy(resp->node.chain_code.bytes, node->chain_code, 32);
resp->node.has_private_key = false;
resp->node.has_public_key = true;
resp->node.public_key.size = 33;
memcpy(resp->node.public_key.bytes, node->public_key, 33);
if (node->public_key[0] == 1) {
/* ed25519 public key */
resp->node.public_key.bytes[0] = 0;
}
resp->has_xpub = true;
if (coin->xpub_magic && (script_type == InputScriptType_SPENDADDRESS ||
script_type == InputScriptType_SPENDMULTISIG)) {
hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub,
sizeof(resp->xpub));
} else if (coin->has_segwit && coin->xpub_magic_segwit_p2sh &&
script_type == InputScriptType_SPENDP2SHWITNESS) {
hdnode_serialize_public(node, fingerprint, coin->xpub_magic_segwit_p2sh,
resp->xpub, sizeof(resp->xpub));
} else if (coin->has_segwit && coin->xpub_magic_segwit_native &&
script_type == InputScriptType_SPENDWITNESS) {
hdnode_serialize_public(node, fingerprint, coin->xpub_magic_segwit_native,
resp->xpub, sizeof(resp->xpub));
} else {
fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid combination of coin and script_type"));
layoutHome();
return;
}
msg_write(MessageType_MessageType_PublicKey, resp);
layoutHome();
}
void fsm_msgSignTx(const SignTx *msg) {
CHECK_INITIALIZED
CHECK_PARAM(msg->inputs_count > 0,
_("Transaction must have at least one input"));
CHECK_PARAM(msg->outputs_count > 0,
_("Transaction must have at least one output"));
CHECK_PARAM(msg->inputs_count + msg->outputs_count >= msg->inputs_count,
_("Value overflow"));
CHECK_PIN
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
if (!coin) return;
const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL);
if (!node) return;
signing_init(msg, coin, node);
}
void fsm_msgTxAck(TxAck *msg) {
CHECK_PARAM(msg->has_tx, _("No transaction provided"));
signing_txack(&(msg->tx));
}
static bool path_mismatched(const CoinInfo *coin, const GetAddress *msg) {
bool mismatch = false;
// m : no path
if (msg->address_n_count == 0) {
return false;
}
// m/44' : BIP44 Legacy
// m / purpose' / coin_type' / account' / change / address_index
if (msg->address_n[0] == (0x80000000 + 44)) {
mismatch |= (msg->script_type != InputScriptType_SPENDADDRESS);
mismatch |= (msg->address_n_count != 5);
mismatch |= (msg->address_n[1] != coin->coin_type);
mismatch |= (msg->address_n[2] & 0x80000000) == 0;
mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000;
mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000;
return mismatch;
}
// m/45' - BIP45 Copay Abandoned Multisig P2SH
// m / purpose' / cosigner_index / change / address_index
if (msg->address_n[0] == (0x80000000 + 45)) {
mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG);
mismatch |= (msg->address_n_count != 4);
mismatch |= (msg->address_n[1] & 0x80000000) == 0x80000000;
mismatch |= (msg->address_n[2] & 0x80000000) == 0x80000000;
mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000;
return mismatch;
}
// m/48' - BIP48 Copay Multisig P2SH
// m / purpose' / coin_type' / account' / change / address_index
if (msg->address_n[0] == (0x80000000 + 48)) {
mismatch |= (msg->script_type != InputScriptType_SPENDMULTISIG);
mismatch |= (msg->address_n_count != 5);
mismatch |= (msg->address_n[1] != coin->coin_type);
mismatch |= (msg->address_n[2] & 0x80000000) == 0;
mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000;
mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000;
return mismatch;
}
// m/49' : BIP49 SegWit
// m / purpose' / coin_type' / account' / change / address_index
if (msg->address_n[0] == (0x80000000 + 49)) {
mismatch |= (msg->script_type != InputScriptType_SPENDP2SHWITNESS);
mismatch |= !coin->has_segwit;
mismatch |= !coin->has_address_type_p2sh;
mismatch |= (msg->address_n_count != 5);
mismatch |= (msg->address_n[1] != coin->coin_type);
mismatch |= (msg->address_n[2] & 0x80000000) == 0;
mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000;
mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000;
return mismatch;
}
// m/84' : BIP84 Native SegWit
// m / purpose' / coin_type' / account' / change / address_index
if (msg->address_n[0] == (0x80000000 + 84)) {
mismatch |= (msg->script_type != InputScriptType_SPENDWITNESS);
mismatch |= !coin->has_segwit;
mismatch |= !coin->bech32_prefix;
mismatch |= (msg->address_n_count != 5);
mismatch |= (msg->address_n[1] != coin->coin_type);
mismatch |= (msg->address_n[2] & 0x80000000) == 0;
mismatch |= (msg->address_n[3] & 0x80000000) == 0x80000000;
mismatch |= (msg->address_n[4] & 0x80000000) == 0x80000000;
return mismatch;
}
return false;
}
void fsm_msgGetAddress(const GetAddress *msg) {
RESP_INIT(Address);
CHECK_INITIALIZED
CHECK_PIN
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
if (!coin) return;
HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
hdnode_fill_public_key(node);
char address[MAX_ADDR_SIZE];
if (msg->has_multisig) { // use progress bar only for multisig
layoutProgress(_("Computing address"), 0);
}
if (!compute_address(coin, msg->script_type, node, msg->has_multisig,
&msg->multisig, address)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Can't encode address"));
layoutHome();
return;
}
if (msg->has_show_display && msg->show_display) {
char desc[20];
if (msg->has_multisig) {
strlcpy(desc, "Multisig __ of __:", sizeof(desc));
const uint32_t m = msg->multisig.m;
const uint32_t n = msg->multisig.pubkeys_count;
desc[9] = (m < 10) ? ' ' : ('0' + (m / 10));
desc[10] = '0' + (m % 10);
desc[15] = (n < 10) ? ' ' : ('0' + (n / 10));
desc[16] = '0' + (n % 10);
} else {
strlcpy(desc, _("Address:"), sizeof(desc));
}
bool mismatch = path_mismatched(coin, msg);
if (mismatch) {
layoutDialogSwipe(&bmp_icon_warning, _("Abort"), _("Continue"), NULL,
_("Wrong address path"), _("for selected coin."), NULL,
_("Continue at your"), _("own risk!"), NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
bool is_cashaddr = coin->cashaddr_prefix != NULL;
bool is_bech32 = msg->script_type == InputScriptType_SPENDWITNESS;
if (!fsm_layoutAddress(address, desc, is_cashaddr || is_bech32,
is_cashaddr ? strlen(coin->cashaddr_prefix) + 1 : 0,
msg->address_n, msg->address_n_count, false)) {
return;
}
}
strlcpy(resp->address, address, sizeof(resp->address));
msg_write(MessageType_MessageType_Address, resp);
layoutHome();
}
void fsm_msgSignMessage(const SignMessage *msg) {
// CHECK_PARAM(is_ascii_only(msg->message.bytes, msg->message.size), _("Cannot
// sign non-ASCII strings"));
RESP_INIT(MessageSignature);
CHECK_INITIALIZED
layoutSignMessage(msg->message.bytes, msg->message.size);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
if (!coin) return;
HDNode *node = fsm_getDerivedNode(coin->curve_name, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
layoutProgressSwipe(_("Signing"), 0);
if (cryptoMessageSign(coin, node, msg->script_type, msg->message.bytes,
msg->message.size, resp->signature.bytes) == 0) {
resp->has_address = true;
hdnode_fill_public_key(node);
if (!compute_address(coin, msg->script_type, node, false, NULL,
resp->address)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Error computing address"));
layoutHome();
return;
}
resp->has_signature = true;
resp->signature.size = 65;
msg_write(MessageType_MessageType_MessageSignature, resp);
} else {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Error signing message"));
}
layoutHome();
}
void fsm_msgVerifyMessage(const VerifyMessage *msg) {
CHECK_PARAM(msg->has_address, _("No address provided"));
CHECK_PARAM(msg->has_message, _("No message provided"));
const CoinInfo *coin = fsm_getCoin(msg->has_coin_name, msg->coin_name);
if (!coin) return;
layoutProgressSwipe(_("Verifying"), 0);
if (msg->signature.size == 65 &&
cryptoMessageVerify(coin, msg->message.bytes, msg->message.size,
msg->address, msg->signature.bytes) == 0) {
layoutVerifyAddress(coin, msg->address);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
layoutVerifyMessage(msg->message.bytes, msg->message.size);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
fsm_sendSuccess(_("Message verified"));
} else {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature"));
}
layoutHome();
}

View File

@ -0,0 +1,412 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgInitialize(const Initialize *msg) {
recovery_abort();
signing_abort();
if (msg && msg->has_state && msg->state.size == 64) {
uint8_t i_state[64];
if (!session_getState(msg->state.bytes, i_state, NULL)) {
session_clear(false); // do not clear PIN
} else {
if (0 != memcmp(msg->state.bytes, i_state, 64)) {
session_clear(false); // do not clear PIN
}
}
} else {
session_clear(false); // do not clear PIN
}
layoutHome();
fsm_msgGetFeatures(0);
}
void fsm_msgGetFeatures(const GetFeatures *msg) {
(void)msg;
RESP_INIT(Features);
resp->has_vendor = true;
strlcpy(resp->vendor, "trezor.io", sizeof(resp->vendor));
resp->has_major_version = true;
resp->major_version = VERSION_MAJOR;
resp->has_minor_version = true;
resp->minor_version = VERSION_MINOR;
resp->has_patch_version = true;
resp->patch_version = VERSION_PATCH;
resp->has_device_id = true;
strlcpy(resp->device_id, config_uuid_str, sizeof(resp->device_id));
resp->has_pin_protection = true;
resp->pin_protection = config_hasPin();
resp->has_passphrase_protection = true;
config_getPassphraseProtection(&(resp->passphrase_protection));
#ifdef SCM_REVISION
int len = sizeof(SCM_REVISION) - 1;
resp->has_revision = true;
memcpy(resp->revision.bytes, SCM_REVISION, len);
resp->revision.size = len;
#endif
resp->has_bootloader_hash = true;
resp->bootloader_hash.size =
memory_bootloader_hash(resp->bootloader_hash.bytes);
resp->has_language =
config_getLanguage(resp->language, sizeof(resp->language));
resp->has_label = config_getLabel(resp->label, sizeof(resp->label));
resp->has_initialized = true;
resp->initialized = config_isInitialized();
resp->has_imported = config_getImported(&(resp->imported));
resp->has_pin_cached = true;
resp->pin_cached = session_isUnlocked() && config_hasPin();
resp->has_passphrase_cached = true;
resp->passphrase_cached = session_isPassphraseCached();
resp->has_needs_backup = true;
config_getNeedsBackup(&(resp->needs_backup));
resp->has_unfinished_backup = true;
config_getUnfinishedBackup(&(resp->unfinished_backup));
resp->has_no_backup = true;
config_getNoBackup(&(resp->no_backup));
resp->has_flags = config_getFlags(&(resp->flags));
resp->has_model = true;
strlcpy(resp->model, "1", sizeof(resp->model));
msg_write(MessageType_MessageType_Features, resp);
}
void fsm_msgPing(const Ping *msg) {
RESP_INIT(Success);
if (msg->has_button_protection && msg->button_protection) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("answer to ping?"), NULL,
NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
if (msg->has_pin_protection && msg->pin_protection) {
CHECK_PIN
}
if (msg->has_passphrase_protection && msg->passphrase_protection) {
if (!protectPassphrase()) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
return;
}
}
if (msg->has_message) {
resp->has_message = true;
memcpy(&(resp->message), &(msg->message), sizeof(resp->message));
}
msg_write(MessageType_MessageType_Success, resp);
layoutHome();
}
void fsm_msgChangePin(const ChangePin *msg) {
bool removal = msg->has_remove && msg->remove;
if (removal) {
if (config_hasPin()) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("remove current PIN?"),
NULL, NULL, NULL, NULL);
} else {
fsm_sendSuccess(_("PIN removed"));
return;
}
} else {
if (config_hasPin()) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("change current PIN?"),
NULL, NULL, NULL, NULL);
} else {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("set new PIN?"), NULL,
NULL, NULL, NULL);
}
}
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
if (protectChangePin(removal)) {
if (removal) {
fsm_sendSuccess(_("PIN removed"));
} else {
fsm_sendSuccess(_("PIN changed"));
}
}
layoutHome();
}
void fsm_msgWipeDevice(const WipeDevice *msg) {
(void)msg;
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("wipe the device?"), NULL,
_("All data will be lost."), NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_WipeDevice, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
config_wipe();
// the following does not work on Mac anyway :-/ Linux/Windows are fine, so it
// is not needed usbReconnect(); // force re-enumeration because of the serial
// number change
fsm_sendSuccess(_("Device wiped"));
layoutHome();
}
void fsm_msgGetEntropy(const GetEntropy *msg) {
#if !DEBUG_RNG
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("send entropy?"), NULL, NULL,
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
#endif
RESP_INIT(Entropy);
uint32_t len = msg->size;
if (len > 1024) {
len = 1024;
}
resp->entropy.size = len;
random_buffer(resp->entropy.bytes, len);
msg_write(MessageType_MessageType_Entropy, resp);
layoutHome();
}
void fsm_msgLoadDevice(const LoadDevice *msg) {
CHECK_PIN
CHECK_NOT_INITIALIZED
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"), NULL,
_("Loading private seed"), _("is not recommended."),
_("Continue only if you"), _("know what you are"),
_("doing!"), NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
if (msg->has_mnemonic && !(msg->has_skip_checksum && msg->skip_checksum)) {
if (!mnemonic_check(msg->mnemonic)) {
fsm_sendFailure(FailureType_Failure_DataError,
_("Mnemonic with wrong checksum provided"));
layoutHome();
return;
}
}
config_loadDevice(msg);
fsm_sendSuccess(_("Device loaded"));
layoutHome();
}
void fsm_msgResetDevice(const ResetDevice *msg) {
CHECK_PIN
CHECK_NOT_INITIALIZED
CHECK_PARAM(!msg->has_strength || msg->strength == 128 ||
msg->strength == 192 || msg->strength == 256,
_("Invalid seed strength"));
reset_init(msg->has_display_random && msg->display_random,
msg->has_strength ? msg->strength : 128,
msg->has_passphrase_protection && msg->passphrase_protection,
msg->has_pin_protection && msg->pin_protection,
msg->has_language ? msg->language : 0,
msg->has_label ? msg->label : 0,
msg->has_u2f_counter ? msg->u2f_counter : 0,
msg->has_skip_backup ? msg->skip_backup : false,
msg->has_no_backup ? msg->no_backup : false);
}
void fsm_msgEntropyAck(const EntropyAck *msg) {
if (msg->has_entropy) {
reset_entropy(msg->entropy.bytes, msg->entropy.size);
} else {
reset_entropy(0, 0);
}
}
void fsm_msgBackupDevice(const BackupDevice *msg) {
CHECK_INITIALIZED
CHECK_PIN_UNCACHED
(void)
msg;
char mnemonic[MAX_MNEMONIC_LEN + 1];
if (config_getMnemonic(mnemonic, sizeof(mnemonic))) {
reset_backup(true, mnemonic);
}
memzero(mnemonic, sizeof(mnemonic));
}
void fsm_msgCancel(const Cancel *msg) {
(void)msg;
recovery_abort();
signing_abort();
ethereum_signing_abort();
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
}
void fsm_msgClearSession(const ClearSession *msg) {
(void)msg;
session_clear(true); // clear PIN as well
layoutScreensaver();
fsm_sendSuccess(_("Session cleared"));
}
void fsm_msgApplySettings(const ApplySettings *msg) {
CHECK_PARAM(msg->has_label || msg->has_language || msg->has_use_passphrase ||
msg->has_homescreen || msg->has_auto_lock_delay_ms,
_("No setting provided"));
CHECK_PIN
if (msg->has_label) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("change name to"),
msg->label, "?", NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
if (msg->has_language) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("change language to"),
msg->language, "?", NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
if (msg->has_use_passphrase) {
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"),
msg->use_passphrase ? _("enable passphrase") : _("disable passphrase"),
_("protection?"), NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
if (msg->has_homescreen) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("change the home"),
_("screen?"), NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
if (msg->has_auto_lock_delay_ms) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you really want to"), _("change auto-lock"),
_("delay?"), NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
if (msg->has_label) {
config_setLabel(msg->label);
}
if (msg->has_language) {
config_setLanguage(msg->language);
}
if (msg->has_use_passphrase) {
config_setPassphraseProtection(msg->use_passphrase);
}
if (msg->has_homescreen) {
config_setHomescreen(msg->homescreen.bytes, msg->homescreen.size);
}
if (msg->has_auto_lock_delay_ms) {
config_setAutoLockDelayMs(msg->auto_lock_delay_ms);
}
fsm_sendSuccess(_("Settings applied"));
layoutHome();
}
void fsm_msgApplyFlags(const ApplyFlags *msg) {
CHECK_PIN
if (msg->has_flags) {
config_applyFlags(msg->flags);
}
fsm_sendSuccess(_("Flags applied"));
}
void fsm_msgRecoveryDevice(const RecoveryDevice *msg) {
CHECK_PIN_UNCACHED
const bool dry_run = msg->has_dry_run ? msg->dry_run : false;
if (!dry_run) {
CHECK_NOT_INITIALIZED
}
CHECK_PARAM(!msg->has_word_count || msg->word_count == 12 ||
msg->word_count == 18 || msg->word_count == 24,
_("Invalid word count"));
recovery_init(msg->has_word_count ? msg->word_count : 12,
msg->has_passphrase_protection && msg->passphrase_protection,
msg->has_pin_protection && msg->pin_protection,
msg->has_language ? msg->language : 0,
msg->has_label ? msg->label : 0,
msg->has_enforce_wordlist && msg->enforce_wordlist,
msg->has_type ? msg->type : 0,
msg->has_u2f_counter ? msg->u2f_counter : 0, dry_run);
}
void fsm_msgWordAck(const WordAck *msg) { recovery_word(msg->word); }
void fsm_msgSetU2FCounter(const SetU2FCounter *msg) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Do you want to set"), _("the U2F counter?"), NULL, NULL,
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
config_setU2FCounter(msg->u2f_counter);
fsm_sendSuccess(_("U2F counter set"));
layoutHome();
}

View File

@ -0,0 +1,299 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgCipherKeyValue(const CipherKeyValue *msg) {
CHECK_INITIALIZED
CHECK_PARAM(msg->has_key, _("No key provided"));
CHECK_PARAM(msg->has_value, _("No value provided"));
CHECK_PARAM(msg->value.size % 16 == 0,
_("Value length must be a multiple of 16"));
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
bool encrypt = msg->has_encrypt && msg->encrypt;
bool ask_on_encrypt = msg->has_ask_on_encrypt && msg->ask_on_encrypt;
bool ask_on_decrypt = msg->has_ask_on_decrypt && msg->ask_on_decrypt;
if ((encrypt && ask_on_encrypt) || (!encrypt && ask_on_decrypt)) {
layoutCipherKeyValue(encrypt, msg->key);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
uint8_t data[256 + 4];
strlcpy((char *)data, msg->key, sizeof(data));
strlcat((char *)data, ask_on_encrypt ? "E1" : "E0", sizeof(data));
strlcat((char *)data, ask_on_decrypt ? "D1" : "D0", sizeof(data));
hmac_sha512(node->private_key, 32, data, strlen((char *)data), data);
if (msg->iv.size == 16) {
// override iv if provided
memcpy(data + 32, msg->iv.bytes, 16);
}
RESP_INIT(CipheredKeyValue);
if (encrypt) {
aes_encrypt_ctx ctx;
aes_encrypt_key256(data, &ctx);
aes_cbc_encrypt(msg->value.bytes, resp->value.bytes, msg->value.size,
data + 32, &ctx);
} else {
aes_decrypt_ctx ctx;
aes_decrypt_key256(data, &ctx);
aes_cbc_decrypt(msg->value.bytes, resp->value.bytes, msg->value.size,
data + 32, &ctx);
}
resp->has_value = true;
resp->value.size = msg->value.size;
msg_write(MessageType_MessageType_CipheredKeyValue, resp);
layoutHome();
}
void fsm_msgSignIdentity(const SignIdentity *msg) {
RESP_INIT(SignedIdentity);
CHECK_INITIALIZED
layoutSignIdentity(&(msg->identity),
msg->has_challenge_visual ? msg->challenge_visual : 0);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
uint8_t hash[32];
if (!msg->has_identity ||
cryptoIdentityFingerprint(&(msg->identity), hash) == 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity"));
layoutHome();
return;
}
uint32_t address_n[5];
address_n[0] = 0x80000000 | 13;
address_n[1] = 0x80000000 | hash[0] | (hash[1] << 8) | (hash[2] << 16) |
((uint32_t)hash[3] << 24);
address_n[2] = 0x80000000 | hash[4] | (hash[5] << 8) | (hash[6] << 16) |
((uint32_t)hash[7] << 24);
address_n[3] = 0x80000000 | hash[8] | (hash[9] << 8) | (hash[10] << 16) |
((uint32_t)hash[11] << 24);
address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) |
((uint32_t)hash[15] << 24);
const char *curve = SECP256K1_NAME;
if (msg->has_ecdsa_curve_name) {
curve = msg->ecdsa_curve_name;
}
HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL);
if (!node) return;
bool sign_ssh =
msg->identity.has_proto && (strcmp(msg->identity.proto, "ssh") == 0);
bool sign_gpg =
msg->identity.has_proto && (strcmp(msg->identity.proto, "gpg") == 0);
int result = 0;
layoutProgressSwipe(_("Signing"), 0);
if (sign_ssh) { // SSH does not sign visual challenge
result = sshMessageSign(node, msg->challenge_hidden.bytes,
msg->challenge_hidden.size, resp->signature.bytes);
} else if (sign_gpg) { // GPG should sign a message digest
result = gpgMessageSign(node, msg->challenge_hidden.bytes,
msg->challenge_hidden.size, resp->signature.bytes);
} else {
uint8_t digest[64];
sha256_Raw(msg->challenge_hidden.bytes, msg->challenge_hidden.size, digest);
sha256_Raw((const uint8_t *)msg->challenge_visual,
strlen(msg->challenge_visual), digest + 32);
result = cryptoMessageSign(&(coins[0]), node, InputScriptType_SPENDADDRESS,
digest, 64, resp->signature.bytes);
}
if (result == 0) {
hdnode_fill_public_key(node);
if (strcmp(curve, SECP256K1_NAME) != 0) {
resp->has_address = false;
} else {
resp->has_address = true;
hdnode_get_address(
node, 0x00, resp->address,
sizeof(resp->address)); // hardcoded Bitcoin address type
}
resp->has_public_key = true;
resp->public_key.size = 33;
memcpy(resp->public_key.bytes, node->public_key, 33);
if (node->public_key[0] == 1) {
/* ed25519 public key */
resp->public_key.bytes[0] = 0;
}
resp->has_signature = true;
resp->signature.size = 65;
msg_write(MessageType_MessageType_SignedIdentity, resp);
} else {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Error signing identity"));
}
layoutHome();
}
void fsm_msgGetECDHSessionKey(const GetECDHSessionKey *msg) {
RESP_INIT(ECDHSessionKey);
CHECK_INITIALIZED
layoutDecryptIdentity(&msg->identity);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
uint8_t hash[32];
if (!msg->has_identity ||
cryptoIdentityFingerprint(&(msg->identity), hash) == 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid identity"));
layoutHome();
return;
}
uint32_t address_n[5];
address_n[0] = 0x80000000 | 17;
address_n[1] = 0x80000000 | hash[0] | (hash[1] << 8) | (hash[2] << 16) |
((uint32_t)hash[3] << 24);
address_n[2] = 0x80000000 | hash[4] | (hash[5] << 8) | (hash[6] << 16) |
((uint32_t)hash[7] << 24);
address_n[3] = 0x80000000 | hash[8] | (hash[9] << 8) | (hash[10] << 16) |
((uint32_t)hash[11] << 24);
address_n[4] = 0x80000000 | hash[12] | (hash[13] << 8) | (hash[14] << 16) |
((uint32_t)hash[15] << 24);
const char *curve = SECP256K1_NAME;
if (msg->has_ecdsa_curve_name) {
curve = msg->ecdsa_curve_name;
}
const HDNode *node = fsm_getDerivedNode(curve, address_n, 5, NULL);
if (!node) return;
int result_size = 0;
if (hdnode_get_shared_key(node, msg->peer_public_key.bytes,
resp->session_key.bytes, &result_size) == 0) {
resp->has_session_key = true;
resp->session_key.size = result_size;
msg_write(MessageType_MessageType_ECDHSessionKey, resp);
} else {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Error getting ECDH session key"));
}
layoutHome();
}
void fsm_msgCosiCommit(const CosiCommit *msg) {
RESP_INIT(CosiCommitment);
CHECK_INITIALIZED
CHECK_PARAM(msg->has_data, _("No data provided"));
layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size, false);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
uint8_t nonce[32];
sha256_Raw(msg->data.bytes, msg->data.size, nonce);
rfc6979_state rng;
init_rfc6979(node->private_key, nonce, &rng);
generate_rfc6979(nonce, &rng);
resp->has_commitment = true;
resp->has_pubkey = true;
resp->commitment.size = 32;
resp->pubkey.size = 32;
ed25519_publickey(nonce, resp->commitment.bytes);
ed25519_publickey(node->private_key, resp->pubkey.bytes);
msg_write(MessageType_MessageType_CosiCommitment, resp);
layoutHome();
}
void fsm_msgCosiSign(const CosiSign *msg) {
RESP_INIT(CosiSignature);
CHECK_INITIALIZED
CHECK_PARAM(msg->has_data, _("No data provided"));
CHECK_PARAM(msg->has_global_commitment && msg->global_commitment.size == 32,
_("Invalid global commitment"));
CHECK_PARAM(msg->has_global_pubkey && msg->global_pubkey.size == 32,
_("Invalid global pubkey"));
layoutCosiCommitSign(msg->address_n, msg->address_n_count, msg->data.bytes,
msg->data.size, true);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
uint8_t nonce[32];
sha256_Raw(msg->data.bytes, msg->data.size, nonce);
rfc6979_state rng;
init_rfc6979(node->private_key, nonce, &rng);
generate_rfc6979(nonce, &rng);
resp->has_signature = true;
resp->signature.size = 32;
ed25519_cosi_sign(msg->data.bytes, msg->data.size, node->private_key, nonce,
msg->global_commitment.bytes, msg->global_pubkey.bytes,
resp->signature.bytes);
msg_write(MessageType_MessageType_CosiSignature, resp);
layoutHome();
}

View File

@ -0,0 +1,103 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#if DEBUG_LINK
void fsm_msgDebugLinkGetState(const DebugLinkGetState *msg) {
(void)msg;
// Do not use RESP_INIT because it clears msg_resp, but another message
// might be being handled
DebugLinkState resp;
memzero(&resp, sizeof(resp));
resp.has_layout = true;
resp.layout.size = OLED_BUFSIZE;
memcpy(resp.layout.bytes, oledGetBuffer(), OLED_BUFSIZE);
resp.has_pin = config_getPin(resp.pin, sizeof(resp.pin));
resp.has_matrix = true;
strlcpy(resp.matrix, pinmatrix_get(), sizeof(resp.matrix));
resp.has_reset_entropy = true;
resp.reset_entropy.size = reset_get_int_entropy(resp.reset_entropy.bytes);
resp.has_reset_word = true;
strlcpy(resp.reset_word, reset_get_word(), sizeof(resp.reset_word));
resp.has_recovery_fake_word = true;
strlcpy(resp.recovery_fake_word, recovery_get_fake_word(),
sizeof(resp.recovery_fake_word));
resp.has_recovery_word_pos = true;
resp.recovery_word_pos = recovery_get_word_pos();
resp.has_mnemonic_secret = config_getMnemonicBytes(
resp.mnemonic_secret.bytes, sizeof(resp.mnemonic_secret.bytes),
&resp.mnemonic_secret.size);
resp.mnemonic_type = 0; // BIP-39
resp.has_node = config_dumpNode(&(resp.node));
resp.has_passphrase_protection =
config_getPassphraseProtection(&(resp.passphrase_protection));
msg_debug_write(MessageType_MessageType_DebugLinkState, &resp);
}
void fsm_msgDebugLinkStop(const DebugLinkStop *msg) { (void)msg; }
void fsm_msgDebugLinkMemoryRead(const DebugLinkMemoryRead *msg) {
RESP_INIT(DebugLinkMemory);
uint32_t length = 1024;
if (msg->has_length && msg->length < length) length = msg->length;
resp->has_memory = true;
memcpy(resp->memory.bytes, FLASH_PTR(msg->address), length);
resp->memory.size = length;
msg_debug_write(MessageType_MessageType_DebugLinkMemory, resp);
}
void fsm_msgDebugLinkMemoryWrite(const DebugLinkMemoryWrite *msg) {
uint32_t length = msg->memory.size;
if (msg->flash) {
svc_flash_unlock();
svc_flash_program(FLASH_CR_PROGRAM_X32);
for (uint32_t i = 0; i < length; i += 4) {
uint32_t word;
memcpy(&word, msg->memory.bytes + i, 4);
flash_write32(msg->address + i, word);
}
uint32_t dummy = svc_flash_lock();
(void)dummy;
} else {
#if !EMULATOR
memcpy((void *)msg->address, msg->memory.bytes, length);
#endif
}
}
void fsm_msgDebugLinkFlashErase(const DebugLinkFlashErase *msg) {
svc_flash_unlock();
svc_flash_erase_sector(msg->sector);
uint32_t dummy = svc_flash_lock();
(void)dummy;
}
#endif

View File

@ -0,0 +1,185 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgEthereumGetPublicKey(const EthereumGetPublicKey *msg) {
RESP_INIT(EthereumPublicKey);
CHECK_INITIALIZED
CHECK_PIN
// we use Bitcoin-like format for ETH
const CoinInfo *coin = fsm_getCoin(true, "Bitcoin");
if (!coin) return;
const char *curve = coin->curve_name;
uint32_t fingerprint;
HDNode *node = node = fsm_getDerivedNode(curve, msg->address_n,
msg->address_n_count, &fingerprint);
if (!node) return;
hdnode_fill_public_key(node);
if (msg->has_show_display && msg->show_display) {
layoutPublicKey(node->public_key);
if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
resp->has_node = true;
resp->node.depth = node->depth;
resp->node.fingerprint = fingerprint;
resp->node.child_num = node->child_num;
resp->node.chain_code.size = 32;
memcpy(resp->node.chain_code.bytes, node->chain_code, 32);
resp->node.has_private_key = false;
resp->node.has_public_key = true;
resp->node.public_key.size = 33;
memcpy(resp->node.public_key.bytes, node->public_key, 33);
resp->has_xpub = true;
hdnode_serialize_public(node, fingerprint, coin->xpub_magic, resp->xpub,
sizeof(resp->xpub));
msg_write(MessageType_MessageType_EthereumPublicKey, resp);
layoutHome();
}
void fsm_msgEthereumSignTx(EthereumSignTx *msg) {
CHECK_INITIALIZED
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
ethereum_signing_init(msg, node);
}
void fsm_msgEthereumTxAck(const EthereumTxAck *msg) {
ethereum_signing_txack(msg);
}
void fsm_msgEthereumGetAddress(const EthereumGetAddress *msg) {
RESP_INIT(EthereumAddress);
CHECK_INITIALIZED
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
uint8_t pubkeyhash[20];
if (!hdnode_get_ethereum_pubkeyhash(node, pubkeyhash)) return;
uint32_t slip44 =
(msg->address_n_count > 1) ? (msg->address_n[1] & 0x7fffffff) : 0;
bool rskip60 = false;
uint32_t chain_id = 0;
// constants from trezor-common/defs/ethereum/networks.json
switch (slip44) {
case 137:
rskip60 = true;
chain_id = 30;
break;
case 37310:
rskip60 = true;
chain_id = 31;
break;
}
resp->has_address = true;
resp->address[0] = '0';
resp->address[1] = 'x';
ethereum_address_checksum(pubkeyhash, resp->address + 2, rskip60, chain_id);
// ethereum_address_checksum adds trailing zero
if (msg->has_show_display && msg->show_display) {
char desc[16];
strlcpy(desc, "Address:", sizeof(desc));
if (!fsm_layoutAddress(resp->address, desc, false, 0, msg->address_n,
msg->address_n_count, true)) {
return;
}
}
msg_write(MessageType_MessageType_EthereumAddress, resp);
layoutHome();
}
void fsm_msgEthereumSignMessage(const EthereumSignMessage *msg) {
RESP_INIT(EthereumMessageSignature);
CHECK_INITIALIZED
layoutSignMessage(msg->message.bytes, msg->message.size);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
ethereum_message_sign(msg, node, resp);
layoutHome();
}
void fsm_msgEthereumVerifyMessage(const EthereumVerifyMessage *msg) {
CHECK_PARAM(msg->has_address, _("No address provided"));
CHECK_PARAM(msg->has_message, _("No message provided"));
if (ethereum_message_verify(msg) != 0) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature"));
return;
}
uint8_t pubkeyhash[20];
if (!ethereum_parse(msg->address, pubkeyhash)) {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid address"));
return;
}
layoutVerifyAddress(NULL, msg->address);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
layoutVerifyMessage(msg->message.bytes, msg->message.size);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
fsm_sendSuccess(_("Message verified"));
layoutHome();
}

View File

@ -0,0 +1,144 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 alepop <alepooop@gmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgLiskGetAddress(const LiskGetAddress *msg) {
CHECK_INITIALIZED
CHECK_PIN
RESP_INIT(LiskAddress);
HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
resp->has_address = true;
hdnode_fill_public_key(node);
lisk_get_address_from_public_key(&node->public_key[1], resp->address);
if (msg->has_show_display && msg->show_display) {
if (!fsm_layoutAddress(resp->address, _("Address:"), true, 0,
msg->address_n, msg->address_n_count, false)) {
return;
}
}
msg_write(MessageType_MessageType_LiskAddress, resp);
layoutHome();
}
void fsm_msgLiskGetPublicKey(const LiskGetPublicKey *msg) {
CHECK_INITIALIZED
CHECK_PIN
RESP_INIT(LiskPublicKey);
HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
hdnode_fill_public_key(node);
resp->has_public_key = true;
resp->public_key.size = 32;
if (msg->has_show_display && msg->show_display) {
layoutLiskPublicKey(&node->public_key[1]);
if (!protectButton(ButtonRequestType_ButtonRequest_PublicKey, true)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
memcpy(&resp->public_key.bytes, &node->public_key[1],
sizeof(resp->public_key.bytes));
msg_write(MessageType_MessageType_LiskPublicKey, resp);
layoutHome();
}
void fsm_msgLiskSignMessage(const LiskSignMessage *msg) {
CHECK_INITIALIZED
CHECK_PIN
RESP_INIT(LiskMessageSignature);
HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
hdnode_fill_public_key(node);
lisk_sign_message(node, msg, resp);
msg_write(MessageType_MessageType_LiskMessageSignature, resp);
layoutHome();
}
void fsm_msgLiskVerifyMessage(const LiskVerifyMessage *msg) {
if (lisk_verify_message(msg)) {
char address[MAX_LISK_ADDRESS_SIZE];
lisk_get_address_from_public_key((const uint8_t *)&msg->public_key,
address);
layoutLiskVerifyAddress(address);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
layoutVerifyMessage(msg->message.bytes, msg->message.size);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
fsm_sendSuccess(_("Message verified"));
} else {
fsm_sendFailure(FailureType_Failure_DataError, _("Invalid signature"));
}
layoutHome();
}
void fsm_msgLiskSignTx(LiskSignTx *msg) {
CHECK_INITIALIZED
CHECK_PIN
RESP_INIT(LiskSignedTx);
HDNode *node = fsm_getDerivedNode(ED25519_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
hdnode_fill_public_key(node);
lisk_sign_tx(node, msg, resp);
msg_write(MessageType_MessageType_LiskSignedTx, resp);
layoutHome();
}

View File

@ -0,0 +1,352 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgNEMGetAddress(NEMGetAddress *msg) {
if (!msg->has_network) {
msg->network = NEM_NETWORK_MAINNET;
}
const char *network;
CHECK_PARAM((network = nem_network_name(msg->network)),
_("Invalid NEM network"));
CHECK_INITIALIZED
CHECK_PIN
RESP_INIT(NEMAddress);
HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
if (!hdnode_get_nem_address(node, msg->network, resp->address)) return;
if (msg->has_show_display && msg->show_display) {
char desc[16];
strlcpy(desc, network, sizeof(desc));
strlcat(desc, ":", sizeof(desc));
if (!fsm_layoutAddress(resp->address, desc, true, 0, msg->address_n,
msg->address_n_count, false)) {
return;
}
}
msg_write(MessageType_MessageType_NEMAddress, resp);
layoutHome();
}
void fsm_msgNEMSignTx(NEMSignTx *msg) {
const char *reason;
#define NEM_CHECK_PARAM(s) CHECK_PARAM((reason = (s)) == NULL, reason)
#define NEM_CHECK_PARAM_WHEN(b, s) \
CHECK_PARAM(!(b) || (reason = (s)) == NULL, reason)
CHECK_PARAM(msg->has_transaction, _("No common provided"));
// Ensure exactly one transaction is provided
unsigned int provided = msg->has_transfer + msg->has_provision_namespace +
msg->has_mosaic_creation + msg->has_supply_change +
msg->has_aggregate_modification +
msg->has_importance_transfer;
CHECK_PARAM(provided != 0, _("No transaction provided"));
CHECK_PARAM(provided == 1, _("More than one transaction provided"));
NEM_CHECK_PARAM(nem_validate_common(&msg->transaction, false));
NEM_CHECK_PARAM_WHEN(
msg->has_transfer,
nem_validate_transfer(&msg->transfer, msg->transaction.network));
NEM_CHECK_PARAM_WHEN(
msg->has_provision_namespace,
nem_validate_provision_namespace(&msg->provision_namespace,
msg->transaction.network));
NEM_CHECK_PARAM_WHEN(msg->has_mosaic_creation,
nem_validate_mosaic_creation(&msg->mosaic_creation,
msg->transaction.network));
NEM_CHECK_PARAM_WHEN(msg->has_supply_change,
nem_validate_supply_change(&msg->supply_change));
NEM_CHECK_PARAM_WHEN(msg->has_aggregate_modification,
nem_validate_aggregate_modification(
&msg->aggregate_modification, !msg->has_multisig));
NEM_CHECK_PARAM_WHEN(
msg->has_importance_transfer,
nem_validate_importance_transfer(&msg->importance_transfer));
bool cosigning = msg->has_cosigning && msg->cosigning;
if (msg->has_multisig) {
NEM_CHECK_PARAM(nem_validate_common(&msg->multisig, true));
CHECK_PARAM(msg->transaction.network == msg->multisig.network,
_("Inner transaction network is different"));
} else {
CHECK_PARAM(!cosigning, _("No multisig transaction to cosign"));
}
CHECK_INITIALIZED
CHECK_PIN
const char *network = nem_network_name(msg->transaction.network);
if (msg->has_multisig) {
char address[NEM_ADDRESS_SIZE + 1];
nem_get_address(msg->multisig.signer.bytes, msg->multisig.network, address);
if (!nem_askMultisig(address, network, cosigning, msg->transaction.fee)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
}
RESP_INIT(NEMSignedTx);
HDNode *node =
fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->transaction.address_n,
msg->transaction.address_n_count, NULL);
if (!node) return;
hdnode_fill_public_key(node);
const NEMTransactionCommon *common =
msg->has_multisig ? &msg->multisig : &msg->transaction;
char address[NEM_ADDRESS_SIZE + 1];
hdnode_get_nem_address(node, common->network, address);
if (msg->has_transfer) {
msg->transfer.mosaics_count = nem_canonicalizeMosaics(
msg->transfer.mosaics, msg->transfer.mosaics_count);
}
if (msg->has_transfer && !nem_askTransfer(common, &msg->transfer, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_provision_namespace &&
!nem_askProvisionNamespace(common, &msg->provision_namespace, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_mosaic_creation &&
!nem_askMosaicCreation(common, &msg->mosaic_creation, network, address)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_supply_change &&
!nem_askSupplyChange(common, &msg->supply_change, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_aggregate_modification &&
!nem_askAggregateModification(common, &msg->aggregate_modification,
network, !msg->has_multisig)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
if (msg->has_importance_transfer &&
!nem_askImportanceTransfer(common, &msg->importance_transfer, network)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled,
_("Signing cancelled by user"));
layoutHome();
return;
}
nem_transaction_ctx context;
nem_transaction_start(&context, &node->public_key[1], resp->data.bytes,
sizeof(resp->data.bytes));
if (msg->has_multisig) {
uint8_t buffer[sizeof(resp->data.bytes)];
nem_transaction_ctx inner;
nem_transaction_start(&inner, msg->multisig.signer.bytes, buffer,
sizeof(buffer));
if (msg->has_transfer &&
!nem_fsmTransfer(&inner, NULL, &msg->multisig, &msg->transfer)) {
layoutHome();
return;
}
if (msg->has_provision_namespace &&
!nem_fsmProvisionNamespace(&inner, &msg->multisig,
&msg->provision_namespace)) {
layoutHome();
return;
}
if (msg->has_mosaic_creation &&
!nem_fsmMosaicCreation(&inner, &msg->multisig, &msg->mosaic_creation)) {
layoutHome();
return;
}
if (msg->has_supply_change &&
!nem_fsmSupplyChange(&inner, &msg->multisig, &msg->supply_change)) {
layoutHome();
return;
}
if (msg->has_aggregate_modification &&
!nem_fsmAggregateModification(&inner, &msg->multisig,
&msg->aggregate_modification)) {
layoutHome();
return;
}
if (msg->has_importance_transfer &&
!nem_fsmImportanceTransfer(&inner, &msg->multisig,
&msg->importance_transfer)) {
layoutHome();
return;
}
if (!nem_fsmMultisig(&context, &msg->transaction, &inner, cosigning)) {
layoutHome();
return;
}
} else {
if (msg->has_transfer &&
!nem_fsmTransfer(&context, node, &msg->transaction, &msg->transfer)) {
layoutHome();
return;
}
if (msg->has_provision_namespace &&
!nem_fsmProvisionNamespace(&context, &msg->transaction,
&msg->provision_namespace)) {
layoutHome();
return;
}
if (msg->has_mosaic_creation &&
!nem_fsmMosaicCreation(&context, &msg->transaction,
&msg->mosaic_creation)) {
layoutHome();
return;
}
if (msg->has_supply_change &&
!nem_fsmSupplyChange(&context, &msg->transaction,
&msg->supply_change)) {
layoutHome();
return;
}
if (msg->has_aggregate_modification &&
!nem_fsmAggregateModification(&context, &msg->transaction,
&msg->aggregate_modification)) {
layoutHome();
return;
}
if (msg->has_importance_transfer &&
!nem_fsmImportanceTransfer(&context, &msg->transaction,
&msg->importance_transfer)) {
layoutHome();
return;
}
}
resp->has_data = true;
resp->data.size =
nem_transaction_end(&context, node->private_key, resp->signature.bytes);
resp->has_signature = true;
resp->signature.size = sizeof(ed25519_signature);
msg_write(MessageType_MessageType_NEMSignedTx, resp);
layoutHome();
}
void fsm_msgNEMDecryptMessage(NEMDecryptMessage *msg) {
RESP_INIT(NEMDecryptedMessage);
CHECK_INITIALIZED
CHECK_PARAM(nem_network_name(msg->network), _("Invalid NEM network"));
CHECK_PARAM(msg->has_payload, _("No payload provided"));
CHECK_PARAM(msg->payload.size >= NEM_ENCRYPTED_PAYLOAD_SIZE(0),
_("Invalid encrypted payload"));
CHECK_PARAM(msg->has_public_key, _("No public key provided"));
CHECK_PARAM(msg->public_key.size == 32, _("Invalid public key"));
char address[NEM_ADDRESS_SIZE + 1];
nem_get_address(msg->public_key.bytes, msg->network, address);
layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Decrypt message"), _("Confirm address?"), address);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
CHECK_PIN
const HDNode *node = fsm_getDerivedNode(ED25519_KECCAK_NAME, msg->address_n,
msg->address_n_count, NULL);
if (!node) return;
const uint8_t *salt = msg->payload.bytes;
uint8_t *iv = &msg->payload.bytes[NEM_SALT_SIZE];
const uint8_t *payload = &msg->payload.bytes[NEM_SALT_SIZE + AES_BLOCK_SIZE];
size_t size = msg->payload.size - NEM_SALT_SIZE - AES_BLOCK_SIZE;
// hdnode_nem_decrypt mutates the IV, so this will modify msg
bool ret = hdnode_nem_decrypt(node, msg->public_key.bytes, iv, salt, payload,
size, resp->payload.bytes);
if (!ret) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to decrypt payload"));
layoutHome();
return;
}
resp->has_payload = true;
resp->payload.size = NEM_DECRYPTED_SIZE(resp->payload.bytes, size);
layoutNEMTransferPayload(resp->payload.bytes, resp->payload.size, true);
if (!protectButton(ButtonRequestType_ButtonRequest_Other, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
msg_write(MessageType_MessageType_NEMDecryptedMessage, resp);
layoutHome();
}

View File

@ -0,0 +1,274 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 ZuluCrypto <zulucrypto@protonmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
void fsm_msgStellarGetAddress(const StellarGetAddress *msg) {
RESP_INIT(StellarAddress);
CHECK_INITIALIZED
CHECK_PIN
const HDNode *node = stellar_deriveNode(msg->address_n, msg->address_n_count);
if (!node) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive private key"));
return;
}
if (msg->has_show_display && msg->show_display) {
const char **str_addr_rows = stellar_lineBreakAddress(node->public_key + 1);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Share public account ID?"), str_addr_rows[0],
str_addr_rows[1], str_addr_rows[2], NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
}
resp->has_address = true;
stellar_publicAddressAsStr(node->public_key + 1, resp->address,
sizeof(resp->address));
msg_write(MessageType_MessageType_StellarAddress, resp);
layoutHome();
}
void fsm_msgStellarSignTx(const StellarSignTx *msg) {
CHECK_INITIALIZED
CHECK_PIN
if (!stellar_signingInit(msg)) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to derive private key"));
layoutHome();
return;
}
// Confirm transaction basics
stellar_layoutTransactionSummary(msg);
// Respond with a request for the first operation
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
void fsm_msgStellarCreateAccountOp(const StellarCreateAccountOp *msg) {
if (!stellar_confirmCreateAccountOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarPaymentOp(const StellarPaymentOp *msg) {
// This will display additional dialogs to the user
if (!stellar_confirmPaymentOp(msg)) return;
// Last operation was confirmed, send a StellarSignedTx
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarPathPaymentOp(const StellarPathPaymentOp *msg) {
if (!stellar_confirmPathPaymentOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarManageOfferOp(const StellarManageOfferOp *msg) {
if (!stellar_confirmManageOfferOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarCreatePassiveOfferOp(
const StellarCreatePassiveOfferOp *msg) {
if (!stellar_confirmCreatePassiveOfferOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarSetOptionsOp(const StellarSetOptionsOp *msg) {
if (!stellar_confirmSetOptionsOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarChangeTrustOp(const StellarChangeTrustOp *msg) {
if (!stellar_confirmChangeTrustOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarAllowTrustOp(const StellarAllowTrustOp *msg) {
if (!stellar_confirmAllowTrustOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarAccountMergeOp(const StellarAccountMergeOp *msg) {
if (!stellar_confirmAccountMergeOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarManageDataOp(const StellarManageDataOp *msg) {
if (!stellar_confirmManageDataOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}
void fsm_msgStellarBumpSequenceOp(const StellarBumpSequenceOp *msg) {
if (!stellar_confirmBumpSequenceOp(msg)) return;
if (stellar_allOperationsConfirmed()) {
RESP_INIT(StellarSignedTx);
stellar_fillSignedTx(resp);
msg_write(MessageType_MessageType_StellarSignedTx, resp);
layoutHome();
}
// Request the next operation to sign
else {
RESP_INIT(StellarTxOpRequest);
msg_write(MessageType_MessageType_StellarTxOpRequest, resp);
}
}

25
legacy/firmware/gettext.h Normal file
View File

@ -0,0 +1,25 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GETTEXT_H__
#define __GETTEXT_H__
#define _(X) (X)
#endif

33
legacy/firmware/header.S Normal file
View File

@ -0,0 +1,33 @@
.syntax unified
#include "version.h"
.section .header, "a"
.type g_header, %object
.size g_header, .-g_header
g_header:
.byte 'T','R','Z','F' // magic
.word reset_handler // reset handler, replace later with : .word g_header_end - g_header // hdrlen
.word 0 // expiry
.word _codelen // codelen
.byte VERSION_MAJOR // vmajor
.byte VERSION_MINOR // vminor
.byte VERSION_PATCH // vpatch
.byte 0 // vbuild
.byte FIX_VERSION_MAJOR // fix_vmajor
.byte FIX_VERSION_MINOR // fix_vminor
.byte FIX_VERSION_PATCH // fix_vpatch
.byte 0 // fix_vbuild
. = . + 8 // reserved
. = . + 512 // hash1 ... hash16
. = . + 64 // sig1
. = . + 64 // sig2
. = . + 64 // sig3
.byte 0 // sigindex1
.byte 0 // sigindex2
.byte 0 // sigindex3
. = . + 220 // reserved
. = . + 65 // reserved
g_header_end:

945
legacy/firmware/layout2.c Normal file
View File

@ -0,0 +1,945 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ctype.h>
#include <stdint.h>
#include <string.h>
#include "bignum.h"
#include "bitmaps.h"
#include "config.h"
#include "gettext.h"
#include "layout2.h"
#include "memzero.h"
#include "nem2.h"
#include "oled.h"
#include "qrcodegen.h"
#include "secp256k1.h"
#include "string.h"
#include "timer.h"
#include "util.h"
#define BITCOIN_DIVISIBILITY (8)
static const char *slip44_extras(uint32_t coin_type) {
if ((coin_type & 0x80000000) == 0) {
return 0;
}
switch (coin_type & 0x7fffffff) {
case 40:
return "EXP"; // Expanse
case 43:
return "NEM"; // NEM
case 60:
return "ETH"; // Ethereum Mainnet
case 61:
return "ETC"; // Ethereum Classic Mainnet
case 108:
return "UBQ"; // UBIQ
case 137:
return "RSK"; // Rootstock Mainnet
case 37310:
return "tRSK"; // Rootstock Testnet
}
return 0;
}
#define BIP32_MAX_LAST_ELEMENT 1000000
static const char *address_n_str(const uint32_t *address_n,
size_t address_n_count,
bool address_is_account) {
if (address_n_count > 8) {
return _("Unknown long path");
}
if (address_n_count == 0) {
return _("Path: m");
}
// known BIP44/49 path
static char path[100];
if (address_n_count == 5 &&
(address_n[0] == (0x80000000 + 44) || address_n[0] == (0x80000000 + 49) ||
address_n[0] == (0x80000000 + 84)) &&
(address_n[1] & 0x80000000) && (address_n[2] & 0x80000000) &&
(address_n[3] <= 1) && (address_n[4] <= BIP32_MAX_LAST_ELEMENT)) {
bool native_segwit = (address_n[0] == (0x80000000 + 84));
bool p2sh_segwit = (address_n[0] == (0x80000000 + 49));
bool legacy = false;
const CoinInfo *coin = coinBySlip44(address_n[1]);
const char *abbr = 0;
if (native_segwit) {
if (coin && coin->has_segwit && coin->bech32_prefix) {
abbr = coin->coin_shortcut + 1;
}
} else if (p2sh_segwit) {
if (coin && coin->has_segwit && coin->has_address_type_p2sh) {
abbr = coin->coin_shortcut + 1;
}
} else {
if (coin) {
if (coin->has_segwit && coin->has_address_type_p2sh) {
legacy = true;
}
abbr = coin->coin_shortcut + 1;
} else {
abbr = slip44_extras(address_n[1]);
}
}
const uint32_t accnum = address_is_account
? ((address_n[4] & 0x7fffffff) + 1)
: (address_n[2] & 0x7fffffff) + 1;
if (abbr && accnum < 100) {
memzero(path, sizeof(path));
strlcpy(path, abbr, sizeof(path));
// TODO: how to name accounts?
// currently we have "legacy account", "account" and "segwit account"
// for BIP44/P2PKH, BIP49/P2SH-P2WPKH and BIP84/P2WPKH respectivelly
if (legacy) {
strlcat(path, " legacy", sizeof(path));
}
if (native_segwit) {
strlcat(path, " segwit", sizeof(path));
}
if (address_is_account) {
strlcat(path, " address #", sizeof(path));
} else {
strlcat(path, " account #", sizeof(path));
}
char acc[3];
memzero(acc, sizeof(acc));
if (accnum < 10) {
acc[0] = '0' + accnum;
} else {
acc[0] = '0' + (accnum / 10);
acc[1] = '0' + (accnum % 10);
}
strlcat(path, acc, sizeof(path));
return path;
}
}
// "Path: m" / i '
static char address_str[7 + 8 * (1 + 10 + 1) + 1];
char *c = address_str + sizeof(address_str) - 1;
*c = 0;
c--;
for (int n = (int)address_n_count - 1; n >= 0; n--) {
uint32_t i = address_n[n];
if (i & 0x80000000) {
*c = '\'';
c--;
}
i = i & 0x7fffffff;
do {
*c = '0' + (i % 10);
c--;
i /= 10;
} while (i > 0);
*c = '/';
c--;
}
*c = 'm';
c--;
*c = ' ';
c--;
*c = ':';
c--;
*c = 'h';
c--;
*c = 't';
c--;
*c = 'a';
c--;
*c = 'P';
return c;
}
// split longer string into 4 rows, rowlen chars each
const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen) {
static char str[4][32 + 1];
if (rowlen > 32) {
rowlen = 32;
}
memzero(str, sizeof(str));
strlcpy(str[0], (char *)msg, rowlen + 1);
if (len > rowlen) {
strlcpy(str[1], (char *)msg + rowlen, rowlen + 1);
}
if (len > rowlen * 2) {
strlcpy(str[2], (char *)msg + rowlen * 2, rowlen + 1);
}
if (len > rowlen * 3) {
strlcpy(str[3], (char *)msg + rowlen * 3, rowlen + 1);
}
if (len > rowlen * 4) {
str[3][rowlen - 1] = '.';
str[3][rowlen - 2] = '.';
str[3][rowlen - 3] = '.';
}
static const char *ret[4] = {str[0], str[1], str[2], str[3]};
return ret;
}
const char **split_message_hex(const uint8_t *msg, uint32_t len) {
char hex[32 * 2 + 1];
memzero(hex, sizeof(hex));
uint32_t size = len;
if (len > 32) {
size = 32;
}
data2hex(msg, size, hex);
if (len > 32) {
hex[63] = '.';
hex[62] = '.';
}
return split_message((const uint8_t *)hex, size * 2, 16);
}
void *layoutLast = layoutHome;
void layoutDialogSwipe(const BITMAP *icon, const char *btnNo,
const char *btnYes, const char *desc, const char *line1,
const char *line2, const char *line3, const char *line4,
const char *line5, const char *line6) {
layoutLast = layoutDialogSwipe;
layoutSwipe();
layoutDialog(icon, btnNo, btnYes, desc, line1, line2, line3, line4, line5,
line6);
}
void layoutProgressSwipe(const char *desc, int permil) {
if (layoutLast == layoutProgressSwipe) {
oledClear();
} else {
layoutLast = layoutProgressSwipe;
layoutSwipe();
}
layoutProgress(desc, permil);
}
void layoutScreensaver(void) {
layoutLast = layoutScreensaver;
oledClear();
oledRefresh();
}
void layoutHome(void) {
if (layoutLast == layoutHome || layoutLast == layoutScreensaver) {
oledClear();
} else {
layoutSwipe();
}
layoutLast = layoutHome;
char label[MAX_LABEL_LEN + 1] = _("Go to trezor.io/start");
if (config_isInitialized()) {
config_getLabel(label, sizeof(label));
}
uint8_t homescreen[HOMESCREEN_SIZE];
if (config_getHomescreen(homescreen, sizeof(homescreen))) {
BITMAP b;
b.width = 128;
b.height = 64;
b.data = homescreen;
oledDrawBitmap(0, 0, &b);
} else {
if (label[0] != '\0') {
oledDrawBitmap(44, 4, &bmp_logo48);
oledDrawStringCenter(OLED_WIDTH / 2, OLED_HEIGHT - 8, label,
FONT_STANDARD);
} else {
oledDrawBitmap(40, 0, &bmp_logo64);
}
}
bool no_backup = false;
bool unfinished_backup = false;
bool needs_backup = false;
config_getNoBackup(&no_backup);
config_getUnfinishedBackup(&unfinished_backup);
config_getNeedsBackup(&needs_backup);
if (no_backup) {
oledBox(0, 0, 127, 8, false);
oledDrawStringCenter(OLED_WIDTH / 2, 0, "SEEDLESS", FONT_STANDARD);
} else if (unfinished_backup) {
oledBox(0, 0, 127, 8, false);
oledDrawStringCenter(OLED_WIDTH / 2, 0, "BACKUP FAILED!", FONT_STANDARD);
} else if (needs_backup) {
oledBox(0, 0, 127, 8, false);
oledDrawStringCenter(OLED_WIDTH / 2, 0, "NEEDS BACKUP!", FONT_STANDARD);
}
oledRefresh();
// Reset lock screen timeout
system_millis_lock_start = timer_ms();
}
static void render_address_dialog(const CoinInfo *coin, const char *address,
const char *line1, const char *line2,
const char *extra_line) {
if (coin && coin->cashaddr_prefix) {
/* If this is a cashaddr address, remove the prefix from the
* string presented to the user
*/
int prefix_len = strlen(coin->cashaddr_prefix);
if (strncmp(address, coin->cashaddr_prefix, prefix_len) == 0 &&
address[prefix_len] == ':') {
address += prefix_len + 1;
}
}
int addrlen = strlen(address);
int numlines = addrlen <= 42 ? 2 : 3;
int linelen = (addrlen - 1) / numlines + 1;
if (linelen > 21) {
linelen = 21;
}
const char **str = split_message((const uint8_t *)address, addrlen, linelen);
layoutLast = layoutDialogSwipe;
layoutSwipe();
oledClear();
oledDrawBitmap(0, 0, &bmp_icon_question);
oledDrawString(20, 0 * 9, line1, FONT_STANDARD);
oledDrawString(20, 1 * 9, line2, FONT_STANDARD);
int left = linelen > 18 ? 0 : 20;
oledDrawString(left, 2 * 9, str[0], FONT_FIXED);
oledDrawString(left, 3 * 9, str[1], FONT_FIXED);
oledDrawString(left, 4 * 9, str[2], FONT_FIXED);
oledDrawString(left, 5 * 9, str[3], FONT_FIXED);
if (!str[3][0]) {
if (extra_line) {
oledDrawString(0, 5 * 9, extra_line, FONT_STANDARD);
} else {
oledHLine(OLED_HEIGHT - 13);
}
}
layoutButtonNo(_("Cancel"));
layoutButtonYes(_("Confirm"));
oledRefresh();
}
void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out) {
char str_out[32 + 3];
bn_format_uint64(out->amount, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY,
0, false, str_out, sizeof(str_out) - 3);
strlcat(str_out, " to", sizeof(str_out));
const char *address = out->address;
const char *extra_line =
(out->address_n_count > 0)
? address_n_str(out->address_n, out->address_n_count, false)
: 0;
render_address_dialog(coin, address, _("Confirm sending"), str_out,
extra_line);
}
void layoutConfirmOmni(const uint8_t *data, uint32_t size) {
const char *desc;
char str_out[32];
uint32_t tx_type, currency;
REVERSE32(*(const uint32_t *)(data + 4), tx_type);
if (tx_type == 0x00000000 && size == 20) { // OMNI simple send
desc = _("Simple send of ");
REVERSE32(*(const uint32_t *)(data + 8), currency);
const char *suffix = " UNKN";
switch (currency) {
case 1:
suffix = " OMNI";
break;
case 2:
suffix = " tOMNI";
break;
case 3:
suffix = " MAID";
break;
case 31:
suffix = " USDT";
break;
}
uint64_t amount_be, amount;
memcpy(&amount_be, data + 12, sizeof(uint64_t));
REVERSE64(amount_be, amount);
bn_format_uint64(amount, NULL, suffix, BITCOIN_DIVISIBILITY, 0, false,
str_out, sizeof(str_out));
} else {
desc = _("Unknown transaction");
str_out[0] = 0;
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm OMNI Transaction:"), NULL, desc, NULL, str_out,
NULL);
}
static bool is_valid_ascii(const uint8_t *data, uint32_t size) {
for (uint32_t i = 0; i < size; i++) {
if (data[i] < ' ' || data[i] > '~') {
return false;
}
}
return true;
}
void layoutConfirmOpReturn(const uint8_t *data, uint32_t size) {
const char **str;
if (!is_valid_ascii(data, size)) {
str = split_message_hex(data, size);
} else {
str = split_message(data, size, 20);
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm OP_RETURN:"), str[0], str[1], str[2], str[3],
NULL);
}
void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out,
uint64_t amount_fee) {
char str_out[32], str_fee[32];
bn_format_uint64(amount_out, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY,
0, false, str_out, sizeof(str_out));
bn_format_uint64(amount_fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY,
0, false, str_fee, sizeof(str_fee));
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Really send"), str_out, _("from your wallet?"),
_("Fee included:"), str_fee, NULL);
}
void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee) {
char str_fee[32];
bn_format_uint64(fee, NULL, coin->coin_shortcut, BITCOIN_DIVISIBILITY, 0,
false, str_fee, sizeof(str_fee));
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Fee"), str_fee, _("is unexpectedly high."), NULL,
_("Send anyway?"), NULL);
}
void layoutSignMessage(const uint8_t *msg, uint32_t len) {
const char **str;
if (!is_valid_ascii(msg, len)) {
str = split_message_hex(msg, len);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Sign binary message?"), str[0], str[1], str[2], str[3],
NULL, NULL);
} else {
str = split_message(msg, len, 20);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Sign message?"), str[0], str[1], str[2], str[3], NULL,
NULL);
}
}
void layoutVerifyMessage(const uint8_t *msg, uint32_t len) {
const char **str;
if (!is_valid_ascii(msg, len)) {
str = split_message_hex(msg, len);
layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"),
_("Verified binary message"), str[0], str[1], str[2],
str[3], NULL, NULL);
} else {
str = split_message(msg, len, 20);
layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"),
_("Verified message"), str[0], str[1], str[2], str[3],
NULL, NULL);
}
}
void layoutVerifyAddress(const CoinInfo *coin, const char *address) {
render_address_dialog(coin, address, _("Confirm address?"),
_("Message signed by:"), 0);
}
void layoutCipherKeyValue(bool encrypt, const char *key) {
const char **str = split_message((const uint8_t *)key, strlen(key), 16);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"),
encrypt ? _("Encrypt value of this key?")
: _("Decrypt value of this key?"),
str[0], str[1], str[2], str[3], NULL, NULL);
}
void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing) {
const char **str = split_message(msg, len, 16);
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Confirm"),
signing ? _("Encrypt+Sign message?") : _("Encrypt message?"), str[0],
str[1], str[2], str[3], NULL, NULL);
}
void layoutDecryptMessage(const uint8_t *msg, uint32_t len,
const char *address) {
const char **str = split_message(msg, len, 16);
layoutDialogSwipe(
&bmp_icon_info, NULL, _("OK"),
address ? _("Decrypted signed message") : _("Decrypted message"), str[0],
str[1], str[2], str[3], NULL, NULL);
}
void layoutResetWord(const char *word, int pass, int word_pos, bool last) {
layoutLast = layoutResetWord;
layoutSwipe();
const char *btnYes;
if (last) {
if (pass == 1) {
btnYes = _("Finish");
} else {
btnYes = _("Again");
}
} else {
btnYes = _("Next");
}
const char *action;
if (pass == 1) {
action = _("Please check the seed");
} else {
action = _("Write down the seed");
}
char index_str[] = "##th word is:";
if (word_pos < 10) {
index_str[0] = ' ';
} else {
index_str[0] = '0' + word_pos / 10;
}
index_str[1] = '0' + word_pos % 10;
if (word_pos == 1 || word_pos == 21) {
index_str[2] = 's';
index_str[3] = 't';
} else if (word_pos == 2 || word_pos == 22) {
index_str[2] = 'n';
index_str[3] = 'd';
} else if (word_pos == 3 || word_pos == 23) {
index_str[2] = 'r';
index_str[3] = 'd';
}
int left = 0;
oledClear();
oledDrawBitmap(0, 0, &bmp_icon_info);
left = bmp_icon_info.width + 4;
oledDrawString(left, 0 * 9, action, FONT_STANDARD);
oledDrawString(left, 2 * 9, word_pos < 10 ? index_str + 1 : index_str,
FONT_STANDARD);
oledDrawString(left, 3 * 9, word, FONT_STANDARD | FONT_DOUBLE);
oledHLine(OLED_HEIGHT - 13);
layoutButtonYes(btnYes);
oledRefresh();
}
#define QR_MAX_VERSION 9
void layoutAddress(const char *address, const char *desc, bool qrcode,
bool ignorecase, const uint32_t *address_n,
size_t address_n_count, bool address_is_account) {
if (layoutLast != layoutAddress) {
layoutSwipe();
} else {
oledClear();
}
layoutLast = layoutAddress;
uint32_t addrlen = strlen(address);
if (qrcode) {
char address_upcase[addrlen + 1];
if (ignorecase) {
for (uint32_t i = 0; i < addrlen + 1; i++) {
address_upcase[i] = address[i] >= 'a' && address[i] <= 'z'
? address[i] + 'A' - 'a'
: address[i];
}
}
uint8_t codedata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)];
uint8_t tempdata[qrcodegen_BUFFER_LEN_FOR_VERSION(QR_MAX_VERSION)];
int side = 0;
if (qrcodegen_encodeText(ignorecase ? address_upcase : address, tempdata,
codedata, qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN,
QR_MAX_VERSION, qrcodegen_Mask_AUTO, true)) {
side = qrcodegen_getSize(codedata);
}
oledInvert(0, 0, 63, 63);
if (side > 0 && side <= 29) {
int offset = 32 - side;
for (int i = 0; i < side; i++) {
for (int j = 0; j < side; j++) {
if (qrcodegen_getModule(codedata, i, j)) {
oledBox(offset + i * 2, offset + j * 2, offset + 1 + i * 2,
offset + 1 + j * 2, false);
}
}
}
} else if (side > 0 && side <= 60) {
int offset = 32 - (side / 2);
for (int i = 0; i < side; i++) {
for (int j = 0; j < side; j++) {
if (qrcodegen_getModule(codedata, i, j)) {
oledClearPixel(offset + i, offset + j);
}
}
}
}
} else {
if (desc) {
oledDrawString(0, 0 * 9, desc, FONT_STANDARD);
}
if (addrlen > 10) { // don't split short addresses
uint32_t rowlen =
(addrlen - 1) / (addrlen <= 42 ? 2 : addrlen <= 63 ? 3 : 4) + 1;
const char **str =
split_message((const uint8_t *)address, addrlen, rowlen);
for (int i = 0; i < 4; i++) {
oledDrawString(0, (i + 1) * 9 + 4, str[i], FONT_FIXED);
}
} else {
oledDrawString(0, (0 + 1) * 9 + 4, address, FONT_FIXED);
}
oledDrawString(
0, 42, address_n_str(address_n, address_n_count, address_is_account),
FONT_STANDARD);
}
if (!qrcode) {
layoutButtonNo(_("QR Code"));
}
layoutButtonYes(_("Continue"));
oledRefresh();
}
void layoutPublicKey(const uint8_t *pubkey) {
char desc[16];
strlcpy(desc, "Public Key: 00", sizeof(desc));
if (pubkey[0] == 1) {
/* ed25519 public key */
// pass - leave 00
} else {
data2hex(pubkey, 1, desc + 12);
}
const char **str = split_message_hex(pubkey + 1, 32 * 2);
layoutDialogSwipe(&bmp_icon_question, NULL, _("Continue"), NULL, desc, str[0],
str[1], str[2], str[3], NULL);
}
void layoutSignIdentity(const IdentityType *identity, const char *challenge) {
char row_proto[8 + 11 + 1];
char row_hostport[64 + 6 + 1];
char row_user[64 + 8 + 1];
bool is_gpg = (strcmp(identity->proto, "gpg") == 0);
if (identity->has_proto && identity->proto[0]) {
if (strcmp(identity->proto, "https") == 0) {
strlcpy(row_proto, _("Web sign in to:"), sizeof(row_proto));
} else if (is_gpg) {
strlcpy(row_proto, _("GPG sign for:"), sizeof(row_proto));
} else {
strlcpy(row_proto, identity->proto, sizeof(row_proto));
char *p = row_proto;
while (*p) {
*p = toupper((int)*p);
p++;
}
strlcat(row_proto, _(" login to:"), sizeof(row_proto));
}
} else {
strlcpy(row_proto, _("Login to:"), sizeof(row_proto));
}
if (identity->has_host && identity->host[0]) {
strlcpy(row_hostport, identity->host, sizeof(row_hostport));
if (identity->has_port && identity->port[0]) {
strlcat(row_hostport, ":", sizeof(row_hostport));
strlcat(row_hostport, identity->port, sizeof(row_hostport));
}
} else {
row_hostport[0] = 0;
}
if (identity->has_user && identity->user[0]) {
strlcpy(row_user, _("user: "), sizeof(row_user));
strlcat(row_user, identity->user, sizeof(row_user));
} else {
row_user[0] = 0;
}
if (is_gpg) {
// Split "First Last <first@last.com>" into 2 lines:
// "First Last"
// "first@last.com"
char *email_start = strchr(row_hostport, '<');
if (email_start) {
strlcpy(row_user, email_start + 1, sizeof(row_user));
*email_start = 0;
char *email_end = strchr(row_user, '>');
if (email_end) {
*email_end = 0;
}
}
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Do you want to sign in?"),
row_proto[0] ? row_proto : NULL,
row_hostport[0] ? row_hostport : NULL,
row_user[0] ? row_user : NULL, challenge, NULL, NULL);
}
void layoutDecryptIdentity(const IdentityType *identity) {
char row_proto[8 + 11 + 1];
char row_hostport[64 + 6 + 1];
char row_user[64 + 8 + 1];
if (identity->has_proto && identity->proto[0]) {
strlcpy(row_proto, identity->proto, sizeof(row_proto));
char *p = row_proto;
while (*p) {
*p = toupper((int)*p);
p++;
}
strlcat(row_proto, _(" decrypt for:"), sizeof(row_proto));
} else {
strlcpy(row_proto, _("Decrypt for:"), sizeof(row_proto));
}
if (identity->has_host && identity->host[0]) {
strlcpy(row_hostport, identity->host, sizeof(row_hostport));
if (identity->has_port && identity->port[0]) {
strlcat(row_hostport, ":", sizeof(row_hostport));
strlcat(row_hostport, identity->port, sizeof(row_hostport));
}
} else {
row_hostport[0] = 0;
}
if (identity->has_user && identity->user[0]) {
strlcpy(row_user, _("user: "), sizeof(row_user));
strlcat(row_user, identity->user, sizeof(row_user));
} else {
row_user[0] = 0;
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"),
_("Do you want to decrypt?"),
row_proto[0] ? row_proto : NULL,
row_hostport[0] ? row_hostport : NULL,
row_user[0] ? row_user : NULL, NULL, NULL, NULL);
}
void layoutU2FDialog(const char *verb, const char *appname) {
layoutDialog(&bmp_webauthn, NULL, verb, NULL, verb, _("U2F security key?"),
NULL, appname, NULL, NULL);
}
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes,
const char *desc, const char *line1, const char *address) {
static char first_third[NEM_ADDRESS_SIZE / 3 + 1];
strlcpy(first_third, address, sizeof(first_third));
static char second_third[NEM_ADDRESS_SIZE / 3 + 1];
strlcpy(second_third, &address[NEM_ADDRESS_SIZE / 3], sizeof(second_third));
const char *third_third = &address[NEM_ADDRESS_SIZE * 2 / 3];
layoutDialogSwipe(icon, btnNo, btnYes, desc, line1, first_third, second_third,
third_third, NULL, NULL);
}
void layoutNEMTransferXEM(const char *desc, uint64_t quantity,
const bignum256 *multiplier, uint64_t fee) {
char str_out[32], str_fee[32];
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, quantity, multiplier,
str_out, sizeof(str_out));
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee, NULL, str_fee,
sizeof(str_fee));
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc,
_("Confirm transfer of"), str_out, _("and network fee of"),
str_fee, NULL, NULL);
}
void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc,
uint64_t fee1, const char *fee2_desc, uint64_t fee2) {
char str_fee1[32], str_fee2[32];
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee1, NULL, str_fee1,
sizeof(str_fee1));
if (fee2_desc) {
nem_mosaicFormatAmount(NEM_MOSAIC_DEFINITION_XEM, fee2, NULL, str_fee2,
sizeof(str_fee2));
}
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), confirm ? _("Confirm") : _("Next"), desc,
fee1_desc, str_fee1, fee2_desc, fee2_desc ? str_fee2 : NULL, NULL, NULL);
}
void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier,
uint8_t network) {
char str_out[32], str_levy[32];
nem_mosaicFormatAmount(definition, quantity, multiplier, str_out,
sizeof(str_out));
if (definition->has_levy) {
nem_mosaicFormatLevy(definition, quantity, multiplier, network, str_levy,
sizeof(str_levy));
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"),
definition->has_name ? definition->name : _("Mosaic"),
_("Confirm transfer of"), str_out,
definition->has_levy ? _("and levy of") : NULL,
definition->has_levy ? str_levy : NULL, NULL, NULL);
}
void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic,
uint64_t quantity,
const bignum256 *multiplier) {
char mosaic_name[32];
nem_mosaicFormatName(namespace, mosaic, mosaic_name, sizeof(mosaic_name));
char str_out[32];
nem_mosaicFormatAmount(NULL, quantity, multiplier, str_out, sizeof(str_out));
char *decimal = strchr(str_out, '.');
if (decimal != NULL) {
*decimal = '\0';
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"),
_("Unknown Mosaic"), _("Confirm transfer of"), str_out,
_("raw units of"), mosaic_name, NULL, NULL);
}
void layoutNEMTransferPayload(const uint8_t *payload, size_t length,
bool encrypted) {
if (length >= 1 && payload[0] == 0xFE) {
char encoded[(length - 1) * 2 + 1];
data2hex(&payload[1], length - 1, encoded);
const char **str =
split_message((uint8_t *)encoded, sizeof(encoded) - 1, 16);
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"),
encrypted ? _("Encrypted hex data") : _("Unencrypted hex data"), str[0],
str[1], str[2], str[3], NULL, NULL);
} else {
const char **str = split_message(payload, length, 16);
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"),
encrypted ? _("Encrypted message") : _("Unencrypted message"), str[0],
str[1], str[2], str[3], NULL, NULL);
}
}
void layoutNEMMosaicDescription(const char *description) {
const char **str =
split_message((uint8_t *)description, strlen(description), 16);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"),
_("Mosaic Description"), str[0], str[1], str[2], str[3],
NULL, NULL);
}
void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network) {
const NEMMosaicDefinition *mosaic;
if (nem_mosaicMatches(definition, definition->levy_namespace,
definition->levy_mosaic, network)) {
mosaic = definition;
} else {
mosaic = nem_mosaicByName(definition->levy_namespace,
definition->levy_mosaic, network);
}
char mosaic_name[32];
if (mosaic == NULL) {
nem_mosaicFormatName(definition->levy_namespace, definition->levy_mosaic,
mosaic_name, sizeof(mosaic_name));
}
char str_out[32];
switch (definition->levy) {
case NEMMosaicLevy_MosaicLevy_Percentile:
bn_format_uint64(definition->fee, NULL, NULL, 0, 0, false, str_out,
sizeof(str_out));
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"), _("Percentile Levy"),
_("Raw levy value is"), str_out, _("in"),
mosaic ? (mosaic == definition ? _("the same mosaic") : mosaic->name)
: mosaic_name,
NULL, NULL);
break;
case NEMMosaicLevy_MosaicLevy_Absolute:
default:
nem_mosaicFormatAmount(mosaic, definition->fee, NULL, str_out,
sizeof(str_out));
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"), _("Absolute Levy"),
_("Levy is"), str_out,
mosaic ? (mosaic == definition ? _("in the same mosaic") : NULL)
: _("in raw units of"),
mosaic ? NULL : mosaic_name, NULL, NULL);
break;
}
}
static inline bool is_slip18(const uint32_t *address_n,
size_t address_n_count) {
return address_n_count == 2 && address_n[0] == (0x80000000 + 10018) &&
(address_n[1] & 0x80000000) && (address_n[1] & 0x7FFFFFFF) <= 9;
}
void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count,
const uint8_t *data, uint32_t len, bool final_sign) {
char *desc = final_sign ? _("CoSi sign message?") : _("CoSi commit message?");
char desc_buf[32];
if (is_slip18(address_n, address_n_count)) {
if (final_sign) {
strlcpy(desc_buf, _("CoSi sign index #?"), sizeof(desc_buf));
desc_buf[16] = '0' + (address_n[1] & 0x7FFFFFFF);
} else {
strlcpy(desc_buf, _("CoSi commit index #?"), sizeof(desc_buf));
desc_buf[18] = '0' + (address_n[1] & 0x7FFFFFFF);
}
desc = desc_buf;
}
char str[4][17];
if (len == 32) {
data2hex(data, 8, str[0]);
data2hex(data + 8, 8, str[1]);
data2hex(data + 16, 8, str[2]);
data2hex(data + 24, 8, str[3]);
} else {
strlcpy(str[0], "Data", sizeof(str[0]));
strlcpy(str[1], "of", sizeof(str[1]));
strlcpy(str[2], "unsupported", sizeof(str[2]));
strlcpy(str[3], "length", sizeof(str[3]));
}
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), desc, str[0],
str[1], str[2], str[3], NULL, NULL);
}

94
legacy/firmware/layout2.h Normal file
View File

@ -0,0 +1,94 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LAYOUT2_H__
#define __LAYOUT2_H__
#include "bignum.h"
#include "bitmaps.h"
#include "coins.h"
#include "layout.h"
#include "trezor.h"
#include "messages-bitcoin.pb.h"
#include "messages-crypto.pb.h"
#include "messages-nem.pb.h"
extern void *layoutLast;
#if DEBUG_LINK
#define layoutSwipe oledClear
#else
#define layoutSwipe oledSwipeLeft
#endif
void layoutDialogSwipe(const BITMAP *icon, const char *btnNo,
const char *btnYes, const char *desc, const char *line1,
const char *line2, const char *line3, const char *line4,
const char *line5, const char *line6);
void layoutProgressSwipe(const char *desc, int permil);
void layoutScreensaver(void);
void layoutHome(void);
void layoutConfirmOutput(const CoinInfo *coin, const TxOutputType *out);
void layoutConfirmOmni(const uint8_t *data, uint32_t size);
void layoutConfirmOpReturn(const uint8_t *data, uint32_t size);
void layoutConfirmTx(const CoinInfo *coin, uint64_t amount_out,
uint64_t amount_fee);
void layoutFeeOverThreshold(const CoinInfo *coin, uint64_t fee);
void layoutSignMessage(const uint8_t *msg, uint32_t len);
void layoutVerifyAddress(const CoinInfo *coin, const char *address);
void layoutVerifyMessage(const uint8_t *msg, uint32_t len);
void layoutCipherKeyValue(bool encrypt, const char *key);
void layoutEncryptMessage(const uint8_t *msg, uint32_t len, bool signing);
void layoutDecryptMessage(const uint8_t *msg, uint32_t len,
const char *address);
void layoutResetWord(const char *word, int pass, int word_pos, bool last);
void layoutAddress(const char *address, const char *desc, bool qrcode,
bool ignorecase, const uint32_t *address_n,
size_t address_n_count, bool address_is_account);
void layoutPublicKey(const uint8_t *pubkey);
void layoutSignIdentity(const IdentityType *identity, const char *challenge);
void layoutDecryptIdentity(const IdentityType *identity);
void layoutU2FDialog(const char *verb, const char *appname);
void layoutNEMDialog(const BITMAP *icon, const char *btnNo, const char *btnYes,
const char *desc, const char *line1, const char *address);
void layoutNEMTransferXEM(const char *desc, uint64_t quantity,
const bignum256 *multiplier, uint64_t fee);
void layoutNEMNetworkFee(const char *desc, bool confirm, const char *fee1_desc,
uint64_t fee1, const char *fee2_desc, uint64_t fee2);
void layoutNEMTransferMosaic(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier,
uint8_t network);
void layoutNEMTransferUnknownMosaic(const char *namespace, const char *mosaic,
uint64_t quantity,
const bignum256 *multiplier);
void layoutNEMTransferPayload(const uint8_t *payload, size_t length,
bool encrypted);
void layoutNEMMosaicDescription(const char *description);
void layoutNEMLevy(const NEMMosaicDefinition *definition, uint8_t network);
void layoutCosiCommitSign(const uint32_t *address_n, size_t address_n_count,
const uint8_t *data, uint32_t len, bool final_sign);
const char **split_message(const uint8_t *msg, uint32_t len, uint32_t rowlen);
const char **split_message_hex(const uint8_t *msg, uint32_t len);
#endif

354
legacy/firmware/lisk.c Normal file
View File

@ -0,0 +1,354 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 alepop <alepooop@gmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lisk.h"
#include "bitmaps.h"
#include "crypto.h"
#include "curves.h"
#include "fsm.h"
#include "gettext.h"
#include "layout2.h"
#include "messages.pb.h"
#include "protect.h"
#include "util.h"
void lisk_get_address_from_public_key(const uint8_t *public_key,
char *address) {
uint64_t digest[4];
sha256_Raw(public_key, 32, (uint8_t *)digest);
bn_format_uint64(digest[0], NULL, "L", 0, 0, false, address,
MAX_LISK_ADDRESS_SIZE);
}
void lisk_message_hash(const uint8_t *message, size_t message_len,
uint8_t hash[32]) {
SHA256_CTX ctx;
sha256_Init(&ctx);
sha256_Update(&ctx, (const uint8_t *)"\x15" "Lisk Signed Message:\n", 22);
uint8_t varint[5];
uint32_t l = ser_length(message_len, varint);
sha256_Update(&ctx, varint, l);
sha256_Update(&ctx, message, message_len);
sha256_Final(&ctx, hash);
sha256_Raw(hash, 32, hash);
}
void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg,
LiskMessageSignature *resp) {
layoutSignMessage(msg->message.bytes, msg->message.size);
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, NULL);
layoutHome();
return;
}
layoutProgressSwipe(_("Signing"), 0);
uint8_t signature[64];
uint8_t hash[32];
lisk_message_hash(msg->message.bytes, msg->message.size, hash);
ed25519_sign(hash, 32, node->private_key, &node->public_key[1], signature);
memcpy(resp->signature.bytes, signature, sizeof(signature));
memcpy(resp->public_key.bytes, &node->public_key[1], 32);
resp->has_signature = true;
resp->signature.size = 64;
resp->has_public_key = true;
resp->public_key.size = 32;
}
bool lisk_verify_message(const LiskVerifyMessage *msg) {
uint8_t hash[32];
lisk_message_hash(msg->message.bytes, msg->message.size, hash);
return 0 == ed25519_sign_open(hash, 32, msg->public_key.bytes,
msg->signature.bytes);
}
static void lisk_update_raw_tx(const HDNode *node, LiskSignTx *msg) {
if (!msg->transaction.has_sender_public_key) {
memcpy(msg->transaction.sender_public_key.bytes, &node->public_key[1], 32);
}
// For CastVotes transactions, recipientId should be equal to transaction
// creator address.
if (msg->transaction.type == LiskTransactionType_CastVotes &&
!msg->transaction.has_recipient_id) {
msg->transaction.has_recipient_id = true;
lisk_get_address_from_public_key(&node->public_key[1],
msg->transaction.recipient_id);
}
}
static void lisk_hashupdate_uint32(SHA256_CTX *ctx, uint32_t value) {
uint8_t data[4];
write_le(data, value);
sha256_Update(ctx, data, sizeof(data));
}
static void lisk_hashupdate_uint64_le(SHA256_CTX *ctx, uint64_t value) {
sha256_Update(ctx, (uint8_t *)&value, sizeof(uint64_t));
}
static void lisk_hashupdate_uint64_be(SHA256_CTX *ctx, uint64_t value) {
uint8_t data[8];
data[0] = value >> 56;
data[1] = value >> 48;
data[2] = value >> 40;
data[3] = value >> 32;
data[4] = value >> 24;
data[5] = value >> 16;
data[6] = value >> 8;
data[7] = value;
sha256_Update(ctx, data, sizeof(data));
}
static void lisk_hashupdate_asset(SHA256_CTX *ctx, LiskTransactionType type,
LiskTransactionAsset *asset) {
switch (type) {
case LiskTransactionType_Transfer:
if (asset->has_data) {
sha256_Update(ctx, (const uint8_t *)asset->data, strlen(asset->data));
}
break;
case LiskTransactionType_RegisterDelegate:
if (asset->has_delegate && asset->delegate.has_username) {
sha256_Update(ctx, (const uint8_t *)asset->delegate.username,
strlen(asset->delegate.username));
}
break;
case LiskTransactionType_CastVotes: {
for (int i = 0; i < asset->votes_count; i++) {
sha256_Update(ctx, (uint8_t *)asset->votes[i], strlen(asset->votes[i]));
}
break;
}
case LiskTransactionType_RegisterSecondPassphrase:
if (asset->has_signature && asset->signature.has_public_key) {
sha256_Update(ctx, asset->signature.public_key.bytes,
asset->signature.public_key.size);
}
break;
case LiskTransactionType_RegisterMultisignatureAccount:
if (asset->has_multisignature) {
sha256_Update(ctx, (uint8_t *)&(asset->multisignature.min), 1);
sha256_Update(ctx, (uint8_t *)&(asset->multisignature.life_time), 1);
for (int i = 0; i < asset->multisignature.keys_group_count; i++) {
sha256_Update(ctx, (uint8_t *)asset->multisignature.keys_group[i],
strlen(asset->multisignature.keys_group[i]));
};
}
break;
default:
fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid transaction type"));
break;
}
}
#define MAX_LISK_VALUE_SIZE 20
static void lisk_format_value(uint64_t value, char *formated_value) {
bn_format_uint64(value, NULL, " LSK", 8, 0, false, formated_value,
MAX_LISK_VALUE_SIZE);
}
void lisk_sign_tx(const HDNode *node, LiskSignTx *msg, LiskSignedTx *resp) {
lisk_update_raw_tx(node, msg);
if (msg->has_transaction) {
SHA256_CTX ctx;
sha256_Init(&ctx);
switch (msg->transaction.type) {
case LiskTransactionType_Transfer:
layoutRequireConfirmTx(msg->transaction.recipient_id,
msg->transaction.amount);
break;
case LiskTransactionType_RegisterDelegate:
layoutRequireConfirmDelegateRegistration(&msg->transaction.asset);
break;
case LiskTransactionType_CastVotes:
layoutRequireConfirmCastVotes(&msg->transaction.asset);
break;
case LiskTransactionType_RegisterSecondPassphrase:
layoutLiskPublicKey(msg->transaction.asset.signature.public_key.bytes);
break;
case LiskTransactionType_RegisterMultisignatureAccount:
layoutRequireConfirmMultisig(&msg->transaction.asset);
break;
default:
fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid transaction type"));
layoutHome();
break;
}
if (!protectButton((msg->transaction.type ==
LiskTransactionType_RegisterSecondPassphrase
? ButtonRequestType_ButtonRequest_PublicKey
: ButtonRequestType_ButtonRequest_SignTx),
false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled");
layoutHome();
return;
}
layoutRequireConfirmFee(msg->transaction.fee, msg->transaction.amount);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled");
layoutHome();
return;
}
layoutProgressSwipe(_("Signing transaction"), 0);
sha256_Update(&ctx, (const uint8_t *)&msg->transaction.type, 1);
lisk_hashupdate_uint32(&ctx, msg->transaction.timestamp);
sha256_Update(&ctx, msg->transaction.sender_public_key.bytes, 32);
if (msg->transaction.has_requester_public_key) {
sha256_Update(&ctx, msg->transaction.requester_public_key.bytes,
msg->transaction.requester_public_key.size);
}
uint64_t recipient_id = 0;
if (msg->transaction.has_recipient_id &&
msg->transaction.recipient_id[0] != 0) {
// parse integer from lisk address ("123L" -> 123)
for (size_t i = 0; i < strlen(msg->transaction.recipient_id) - 1; i++) {
if (msg->transaction.recipient_id[i] < '0' ||
msg->transaction.recipient_id[i] > '9') {
fsm_sendFailure(FailureType_Failure_DataError,
_("Invalid recipient_id"));
layoutHome();
return;
}
recipient_id *= 10;
recipient_id += (msg->transaction.recipient_id[i] - '0');
}
}
lisk_hashupdate_uint64_be(&ctx, recipient_id);
lisk_hashupdate_uint64_le(&ctx, msg->transaction.amount);
lisk_hashupdate_asset(&ctx, msg->transaction.type, &msg->transaction.asset);
// if signature exist calculate second signature
if (msg->transaction.has_signature) {
sha256_Update(&ctx, msg->transaction.signature.bytes,
msg->transaction.signature.size);
}
uint8_t hash[32];
sha256_Final(&ctx, hash);
ed25519_sign(hash, 32, node->private_key, &node->public_key[1],
resp->signature.bytes);
resp->has_signature = true;
resp->signature.size = 64;
}
}
// Layouts
void layoutLiskPublicKey(const uint8_t *pubkey) {
const char **str = split_message_hex(pubkey, 32);
layoutDialogSwipe(&bmp_icon_question, NULL, _("Continue"), NULL,
_("Public Key:"), str[0], str[1], str[2], str[3], NULL);
}
void layoutLiskVerifyAddress(const char *address) {
const char **str =
split_message((const uint8_t *)address, strlen(address), 10);
layoutDialogSwipe(&bmp_icon_info, _("Cancel"), _("Confirm"),
_("Confirm address?"), _("Message signed by:"), str[0],
str[1], NULL, NULL, NULL);
}
void layoutRequireConfirmTx(char *recipient_id, uint64_t amount) {
char formated_amount[MAX_LISK_VALUE_SIZE];
const char **str =
split_message((const uint8_t *)recipient_id, strlen(recipient_id), 16);
lisk_format_value(amount, formated_amount);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm sending"), formated_amount, _("to:"), str[0],
str[1], NULL);
}
void layoutRequireConfirmFee(uint64_t fee, uint64_t amount) {
char formated_amount[MAX_LISK_VALUE_SIZE];
char formated_fee[MAX_LISK_VALUE_SIZE];
lisk_format_value(amount, formated_amount);
lisk_format_value(fee, formated_fee);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm transaction"), formated_amount, _("fee:"),
formated_fee, NULL, NULL);
}
void layoutRequireConfirmDelegateRegistration(LiskTransactionAsset *asset) {
if (asset->has_delegate && asset->delegate.has_username) {
const char **str = split_message((const uint8_t *)asset->delegate.username,
strlen(asset->delegate.username), 20);
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm transaction"), _("Do you really want to"),
_("register a delegate?"), str[0], str[1], NULL);
}
}
void layoutRequireConfirmCastVotes(LiskTransactionAsset *asset) {
uint8_t plus = 0;
uint8_t minus = 0;
char add_votes_txt[13];
char remove_votes_txt[16];
for (int i = 0; i < asset->votes_count; i++) {
if (asset->votes[i][0] == '+') {
plus += 1;
} else {
minus += 1;
}
}
bn_format_uint64(plus, "Add ", NULL, 0, 0, false, add_votes_txt,
sizeof(add_votes_txt));
bn_format_uint64(minus, "Remove ", NULL, 0, 0, false, remove_votes_txt,
sizeof(remove_votes_txt));
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm transaction"), add_votes_txt, remove_votes_txt,
NULL, NULL, NULL);
}
void layoutRequireConfirmMultisig(LiskTransactionAsset *asset) {
char keys_group_str[25];
char life_time_str[14];
char min_str[8];
bn_format_uint64(asset->multisignature.keys_group_count,
"Keys group length: ", NULL, 0, 0, false, keys_group_str,
sizeof(keys_group_str));
bn_format_uint64(asset->multisignature.life_time, "Life time: ", NULL, 0, 0,
false, life_time_str, sizeof(life_time_str));
bn_format_uint64(asset->multisignature.min, "Min: ", NULL, 0, 0, false,
min_str, sizeof(min_str));
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Confirm"), NULL,
_("Confirm transaction"), keys_group_str, life_time_str,
min_str, NULL, NULL);
}

46
legacy/firmware/lisk.h Normal file
View File

@ -0,0 +1,46 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2018 alepop <alepooop@gmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LISK_H__
#define __LISK_H__
#include <stdbool.h>
#include "bip32.h"
#include "messages-lisk.pb.h"
#define MAX_LISK_ADDRESS_SIZE 23
void lisk_sign_message(const HDNode *node, const LiskSignMessage *msg,
LiskMessageSignature *resp);
bool lisk_verify_message(const LiskVerifyMessage *msg);
void lisk_sign_tx(const HDNode *node, LiskSignTx *msg, LiskSignedTx *resp);
// Helpers
void lisk_get_address_from_public_key(const uint8_t *public_key, char *address);
// Layout
void layoutLiskPublicKey(const uint8_t *pubkey);
void layoutLiskVerifyAddress(const char *address);
void layoutRequireConfirmTx(char *recipient_id, uint64_t amount);
void layoutRequireConfirmDelegateRegistration(LiskTransactionAsset *asset);
void layoutRequireConfirmCastVotes(LiskTransactionAsset *asset);
void layoutRequireConfirmMultisig(LiskTransactionAsset *asset);
void layoutRequireConfirmFee(uint64_t fee, uint64_t amount);
#endif

369
legacy/firmware/messages.c Normal file
View File

@ -0,0 +1,369 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "debug.h"
#include "fsm.h"
#include "gettext.h"
#include "memzero.h"
#include "messages.h"
#include "trezor.h"
#include "util.h"
#include "messages.pb.h"
#include "pb_decode.h"
#include "pb_encode.h"
struct MessagesMap_t {
char type; // n = normal, d = debug
char dir; // i = in, o = out
uint16_t msg_id;
const pb_field_t *fields;
void (*process_func)(const void *ptr);
};
static const struct MessagesMap_t MessagesMap[] = {
#include "messages_map.h"
// end
{0, 0, 0, 0, 0}};
#include "messages_map_limits.h"
const pb_field_t *MessageFields(char type, char dir, uint16_t msg_id) {
const struct MessagesMap_t *m = MessagesMap;
while (m->type) {
if (type == m->type && dir == m->dir && msg_id == m->msg_id) {
return m->fields;
}
m++;
}
return 0;
}
void MessageProcessFunc(char type, char dir, uint16_t msg_id, void *ptr) {
const struct MessagesMap_t *m = MessagesMap;
while (m->type) {
if (type == m->type && dir == m->dir && msg_id == m->msg_id) {
m->process_func(ptr);
return;
}
m++;
}
}
static uint32_t msg_out_start = 0;
static uint32_t msg_out_end = 0;
static uint32_t msg_out_cur = 0;
static uint8_t msg_out[MSG_OUT_SIZE];
#if DEBUG_LINK
static uint32_t msg_debug_out_start = 0;
static uint32_t msg_debug_out_end = 0;
static uint32_t msg_debug_out_cur = 0;
static uint8_t msg_debug_out[MSG_DEBUG_OUT_SIZE];
#endif
static inline void msg_out_append(uint8_t c) {
if (msg_out_cur == 0) {
msg_out[msg_out_end * 64] = '?';
msg_out_cur = 1;
}
msg_out[msg_out_end * 64 + msg_out_cur] = c;
msg_out_cur++;
if (msg_out_cur == 64) {
msg_out_cur = 0;
msg_out_end = (msg_out_end + 1) % (MSG_OUT_SIZE / 64);
}
}
#if DEBUG_LINK
static inline void msg_debug_out_append(uint8_t c) {
if (msg_debug_out_cur == 0) {
msg_debug_out[msg_debug_out_end * 64] = '?';
msg_debug_out_cur = 1;
}
msg_debug_out[msg_debug_out_end * 64 + msg_debug_out_cur] = c;
msg_debug_out_cur++;
if (msg_debug_out_cur == 64) {
msg_debug_out_cur = 0;
msg_debug_out_end = (msg_debug_out_end + 1) % (MSG_DEBUG_OUT_SIZE / 64);
}
}
#endif
static inline void msg_out_pad(void) {
if (msg_out_cur == 0) return;
while (msg_out_cur < 64) {
msg_out[msg_out_end * 64 + msg_out_cur] = 0;
msg_out_cur++;
}
msg_out_cur = 0;
msg_out_end = (msg_out_end + 1) % (MSG_OUT_SIZE / 64);
}
#if DEBUG_LINK
static inline void msg_debug_out_pad(void) {
if (msg_debug_out_cur == 0) return;
while (msg_debug_out_cur < 64) {
msg_debug_out[msg_debug_out_end * 64 + msg_debug_out_cur] = 0;
msg_debug_out_cur++;
}
msg_debug_out_cur = 0;
msg_debug_out_end = (msg_debug_out_end + 1) % (MSG_DEBUG_OUT_SIZE / 64);
}
#endif
static bool pb_callback_out(pb_ostream_t *stream, const uint8_t *buf,
size_t count) {
(void)stream;
for (size_t i = 0; i < count; i++) {
msg_out_append(buf[i]);
}
return true;
}
#if DEBUG_LINK
static bool pb_debug_callback_out(pb_ostream_t *stream, const uint8_t *buf,
size_t count) {
(void)stream;
for (size_t i = 0; i < count; i++) {
msg_debug_out_append(buf[i]);
}
return true;
}
#endif
bool msg_write_common(char type, uint16_t msg_id, const void *msg_ptr) {
const pb_field_t *fields = MessageFields(type, 'o', msg_id);
if (!fields) { // unknown message
return false;
}
size_t len;
if (!pb_get_encoded_size(&len, fields, msg_ptr)) {
return false;
}
void (*append)(uint8_t);
bool (*pb_callback)(pb_ostream_t *, const uint8_t *, size_t);
if (type == 'n') {
append = msg_out_append;
pb_callback = pb_callback_out;
} else
#if DEBUG_LINK
if (type == 'd') {
append = msg_debug_out_append;
pb_callback = pb_debug_callback_out;
} else
#endif
{
return false;
}
append('#');
append('#');
append((msg_id >> 8) & 0xFF);
append(msg_id & 0xFF);
append((len >> 24) & 0xFF);
append((len >> 16) & 0xFF);
append((len >> 8) & 0xFF);
append(len & 0xFF);
pb_ostream_t stream = {pb_callback, 0, SIZE_MAX, 0, 0};
bool status = pb_encode(&stream, fields, msg_ptr);
if (type == 'n') {
msg_out_pad();
}
#if DEBUG_LINK
else if (type == 'd') {
msg_debug_out_pad();
}
#endif
return status;
}
enum {
READSTATE_IDLE,
READSTATE_READING,
};
void msg_process(char type, uint16_t msg_id, const pb_field_t *fields,
uint8_t *msg_raw, uint32_t msg_size) {
static uint8_t msg_data[MSG_IN_SIZE];
memzero(msg_data, sizeof(msg_data));
pb_istream_t stream = pb_istream_from_buffer(msg_raw, msg_size);
bool status = pb_decode(&stream, fields, msg_data);
if (status) {
MessageProcessFunc(type, 'i', msg_id, msg_data);
} else {
fsm_sendFailure(FailureType_Failure_DataError, stream.errmsg);
}
}
void msg_read_common(char type, const uint8_t *buf, uint32_t len) {
static char read_state = READSTATE_IDLE;
static uint8_t msg_in[MSG_IN_SIZE];
static uint16_t msg_id = 0xFFFF;
static uint32_t msg_size = 0;
static uint32_t msg_pos = 0;
static const pb_field_t *fields = 0;
if (len != 64) return;
if (read_state == READSTATE_IDLE) {
if (buf[0] != '?' || buf[1] != '#' ||
buf[2] != '#') { // invalid start - discard
return;
}
msg_id = (buf[3] << 8) + buf[4];
msg_size =
((uint32_t)buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8];
fields = MessageFields(type, 'i', msg_id);
if (!fields) { // unknown message
fsm_sendFailure(FailureType_Failure_UnexpectedMessage,
_("Unknown message"));
return;
}
if (msg_size > MSG_IN_SIZE) { // message is too big :(
fsm_sendFailure(FailureType_Failure_DataError, _("Message too big"));
return;
}
read_state = READSTATE_READING;
memcpy(msg_in, buf + 9, len - 9);
msg_pos = len - 9;
} else if (read_state == READSTATE_READING) {
if (buf[0] != '?') { // invalid contents
read_state = READSTATE_IDLE;
return;
}
/* raw data starts at buf + 1 with len - 1 bytes */
buf++;
len = MIN(len - 1, MSG_IN_SIZE - msg_pos);
memcpy(msg_in + msg_pos, buf, len);
msg_pos += len;
}
if (msg_pos >= msg_size) {
msg_process(type, msg_id, fields, msg_in, msg_size);
msg_pos = 0;
read_state = READSTATE_IDLE;
}
}
const uint8_t *msg_out_data(void) {
if (msg_out_start == msg_out_end) return 0;
uint8_t *data = msg_out + (msg_out_start * 64);
msg_out_start = (msg_out_start + 1) % (MSG_OUT_SIZE / 64);
debugLog(0, "", "msg_out_data");
return data;
}
#if DEBUG_LINK
const uint8_t *msg_debug_out_data(void) {
if (msg_debug_out_start == msg_debug_out_end) return 0;
uint8_t *data = msg_debug_out + (msg_debug_out_start * 64);
msg_debug_out_start = (msg_debug_out_start + 1) % (MSG_DEBUG_OUT_SIZE / 64);
debugLog(0, "", "msg_debug_out_data");
return data;
}
#endif
CONFIDENTIAL uint8_t msg_tiny[128];
_Static_assert(sizeof(msg_tiny) >= sizeof(Cancel), "msg_tiny too tiny");
_Static_assert(sizeof(msg_tiny) >= sizeof(Initialize), "msg_tiny too tiny");
_Static_assert(sizeof(msg_tiny) >= sizeof(PassphraseAck), "msg_tiny too tiny");
_Static_assert(sizeof(msg_tiny) >= sizeof(ButtonAck), "msg_tiny too tiny");
_Static_assert(sizeof(msg_tiny) >= sizeof(PinMatrixAck), "msg_tiny too tiny");
#if DEBUG_LINK
_Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkDecision),
"msg_tiny too tiny");
_Static_assert(sizeof(msg_tiny) >= sizeof(DebugLinkGetState),
"msg_tiny too tiny");
#endif
uint16_t msg_tiny_id = 0xFFFF;
void msg_read_tiny(const uint8_t *buf, int len) {
if (len != 64) return;
if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
return;
}
uint16_t msg_id = (buf[3] << 8) + buf[4];
uint32_t msg_size =
((uint32_t)buf[5] << 24) + (buf[6] << 16) + (buf[7] << 8) + buf[8];
if (msg_size > 64 || len - msg_size < 9) {
return;
}
const pb_field_t *fields = 0;
pb_istream_t stream = pb_istream_from_buffer(buf + 9, msg_size);
switch (msg_id) {
case MessageType_MessageType_PinMatrixAck:
fields = PinMatrixAck_fields;
break;
case MessageType_MessageType_ButtonAck:
fields = ButtonAck_fields;
break;
case MessageType_MessageType_PassphraseAck:
fields = PassphraseAck_fields;
break;
case MessageType_MessageType_Cancel:
fields = Cancel_fields;
break;
case MessageType_MessageType_Initialize:
fields = Initialize_fields;
break;
#if DEBUG_LINK
case MessageType_MessageType_DebugLinkDecision:
fields = DebugLinkDecision_fields;
break;
case MessageType_MessageType_DebugLinkGetState:
fields = DebugLinkGetState_fields;
break;
#endif
}
if (fields) {
bool status = pb_decode(&stream, fields, msg_tiny);
if (status) {
msg_tiny_id = msg_id;
} else {
fsm_sendFailure(FailureType_Failure_DataError, stream.errmsg);
msg_tiny_id = 0xFFFF;
}
} else {
fsm_sendFailure(FailureType_Failure_UnexpectedMessage,
_("Unknown message"));
msg_tiny_id = 0xFFFF;
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __MESSAGES_H__
#define __MESSAGES_H__
#include <stdbool.h>
#include <stdint.h>
#include "trezor.h"
#define MSG_IN_SIZE (15 * 1024)
#define MSG_OUT_SIZE (3 * 1024)
#define msg_read(buf, len) msg_read_common('n', (buf), (len))
#define msg_write(id, ptr) msg_write_common('n', (id), (ptr))
const uint8_t *msg_out_data(void);
#if DEBUG_LINK
#define MSG_DEBUG_OUT_SIZE (2 * 1024)
#define msg_debug_read(buf, len) msg_read_common('d', (buf), (len))
#define msg_debug_write(id, ptr) msg_write_common('d', (id), (ptr))
const uint8_t *msg_debug_out_data(void);
#endif
void msg_read_common(char type, const uint8_t *buf, uint32_t len);
bool msg_write_common(char type, uint16_t msg_id, const void *msg_ptr);
void msg_read_tiny(const uint8_t *buf, int len);
extern uint8_t msg_tiny[128];
extern uint16_t msg_tiny_id;
#endif

790
legacy/firmware/nem2.c Normal file
View File

@ -0,0 +1,790 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "nem2.h"
#include "aes/aes.h"
#include "fsm.h"
#include "gettext.h"
#include "layout2.h"
#include "memzero.h"
#include "protect.h"
#include "rng.h"
#include "secp256k1.h"
const char *nem_validate_common(NEMTransactionCommon *common, bool inner) {
if (!common->has_network) {
common->has_network = true;
common->network = NEM_NETWORK_MAINNET;
}
if (common->network > 0xFF || nem_network_name(common->network) == NULL) {
return inner ? _("Invalid NEM network in inner transaction")
: _("Invalid NEM network");
}
if (!common->has_timestamp) {
return inner ? _("No timestamp provided in inner transaction")
: _("No timestamp provided");
}
if (!common->has_fee) {
return inner ? _("No fee provided in inner transaction")
: _("No fee provided");
}
if (!common->has_deadline) {
return inner ? _("No deadline provided in inner transaction")
: _("No deadline provided");
}
if (inner != common->has_signer) {
return inner ? _("No signer provided in inner transaction")
: _("Signer not allowed in outer transaction");
}
if (common->has_signer && common->signer.size != sizeof(ed25519_public_key)) {
return _("Invalid signer public key in inner transaction");
}
return NULL;
}
const char *nem_validate_transfer(const NEMTransfer *transfer,
uint8_t network) {
if (!transfer->has_recipient) return _("No recipient provided");
if (!transfer->has_amount) return _("No amount provided");
if (transfer->has_public_key &&
transfer->public_key.size != sizeof(ed25519_public_key)) {
return _("Invalid recipient public key");
}
if (!nem_validate_address(transfer->recipient, network))
return _("Invalid recipient address");
for (size_t i = 0; i < transfer->mosaics_count; i++) {
const NEMMosaic *mosaic = &transfer->mosaics[i];
if (!mosaic->has_namespace) return _("No mosaic namespace provided");
if (!mosaic->has_mosaic) return _("No mosaic name provided");
if (!mosaic->has_quantity) return _("No mosaic quantity provided");
}
return NULL;
}
const char *nem_validate_provision_namespace(
const NEMProvisionNamespace *provision_namespace, uint8_t network) {
if (!provision_namespace->has_namespace) return _("No namespace provided");
if (!provision_namespace->has_sink) return _("No rental sink provided");
if (!provision_namespace->has_fee) return _("No rental sink fee provided");
if (!nem_validate_address(provision_namespace->sink, network))
return _("Invalid rental sink address");
return NULL;
}
const char *nem_validate_mosaic_creation(
const NEMMosaicCreation *mosaic_creation, uint8_t network) {
if (!mosaic_creation->has_definition)
return _("No mosaic definition provided");
if (!mosaic_creation->has_sink) return _("No creation sink provided");
if (!mosaic_creation->has_fee) return _("No creation sink fee provided");
if (!nem_validate_address(mosaic_creation->sink, network))
return _("Invalid creation sink address");
if (mosaic_creation->definition.has_name)
return _("Name not allowed in mosaic creation transactions");
if (mosaic_creation->definition.has_ticker)
return _("Ticker not allowed in mosaic creation transactions");
if (mosaic_creation->definition.networks_count)
return _("Networks not allowed in mosaic creation transactions");
if (!mosaic_creation->definition.has_namespace)
return _("No mosaic namespace provided");
if (!mosaic_creation->definition.has_mosaic)
return _("No mosaic name provided");
if (mosaic_creation->definition.has_levy) {
if (!mosaic_creation->definition.has_fee)
return _("No levy address provided");
if (!mosaic_creation->definition.has_levy_address)
return _("No levy address provided");
if (!mosaic_creation->definition.has_levy_namespace)
return _("No levy namespace provided");
if (!mosaic_creation->definition.has_levy_mosaic)
return _("No levy mosaic name provided");
if (!mosaic_creation->definition.has_divisibility)
return _("No divisibility provided");
if (!mosaic_creation->definition.has_supply) return _("No supply provided");
if (!mosaic_creation->definition.has_mutable_supply)
return _("No supply mutability provided");
if (!mosaic_creation->definition.has_transferable)
return _("No mosaic transferability provided");
if (!mosaic_creation->definition.has_description)
return _("No description provided");
if (mosaic_creation->definition.divisibility > NEM_MAX_DIVISIBILITY)
return _("Invalid divisibility provided");
if (mosaic_creation->definition.supply > NEM_MAX_SUPPLY)
return _("Invalid supply provided");
if (!nem_validate_address(mosaic_creation->definition.levy_address,
network))
return _("Invalid levy address");
}
return NULL;
}
const char *nem_validate_supply_change(
const NEMMosaicSupplyChange *supply_change) {
if (!supply_change->has_namespace) return _("No namespace provided");
if (!supply_change->has_mosaic) return _("No mosaic provided");
if (!supply_change->has_type) return _("No type provided");
if (!supply_change->has_delta) return _("No delta provided");
return NULL;
}
const char *nem_validate_aggregate_modification(
const NEMAggregateModification *aggregate_modification, bool creation) {
if (creation && aggregate_modification->modifications_count == 0) {
return _("No modifications provided");
}
for (size_t i = 0; i < aggregate_modification->modifications_count; i++) {
const NEMCosignatoryModification *modification =
&aggregate_modification->modifications[i];
if (!modification->has_type) return _("No modification type provided");
if (!modification->has_public_key)
return _("No cosignatory public key provided");
if (modification->public_key.size != 32)
return _("Invalid cosignatory public key provided");
if (creation && modification->type ==
NEMModificationType_CosignatoryModification_Delete) {
return _("Cannot remove cosignatory when converting account");
}
}
return NULL;
}
const char *nem_validate_importance_transfer(
const NEMImportanceTransfer *importance_transfer) {
if (!importance_transfer->has_mode) return _("No mode provided");
if (!importance_transfer->has_public_key)
return _("No remote account provided");
if (importance_transfer->public_key.size != 32)
return _("Invalid remote account provided");
return NULL;
}
bool nem_askTransfer(const NEMTransactionCommon *common,
const NEMTransfer *transfer, const char *desc) {
if (transfer->mosaics_count) {
const NEMMosaic *xem = NULL;
bool unknownMosaic = false;
const NEMMosaicDefinition *definitions[transfer->mosaics_count];
for (size_t i = 0; i < transfer->mosaics_count; i++) {
const NEMMosaic *mosaic = &transfer->mosaics[i];
definitions[i] =
nem_mosaicByName(mosaic->namespace, mosaic->mosaic, common->network);
if (definitions[i] == NEM_MOSAIC_DEFINITION_XEM) {
xem = mosaic;
} else if (definitions[i] == NULL) {
unknownMosaic = true;
}
}
bignum256 multiplier;
bn_read_uint64(transfer->amount, &multiplier);
if (unknownMosaic) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("I take the risk"),
_("Unknown Mosaics"), _("Divisibility and levy"),
_("cannot be shown for"), _("unknown mosaics!"), NULL,
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput,
false)) {
return false;
}
}
layoutNEMTransferXEM(desc, xem ? xem->quantity : 0, &multiplier,
common->fee);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
for (size_t i = 0; i < transfer->mosaics_count; i++) {
const NEMMosaic *mosaic = &transfer->mosaics[i];
if (mosaic == xem) {
continue;
}
if (definitions[i]) {
layoutNEMTransferMosaic(definitions[i], mosaic->quantity, &multiplier,
common->network);
} else {
layoutNEMTransferUnknownMosaic(mosaic->namespace, mosaic->mosaic,
mosaic->quantity, &multiplier);
}
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput,
false)) {
return false;
}
}
} else {
layoutNEMTransferXEM(desc, transfer->amount, NULL, common->fee);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
}
if (transfer->has_payload) {
layoutNEMTransferPayload(transfer->payload.bytes, transfer->payload.size,
transfer->has_public_key);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
}
layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Confirm"), desc,
_("Confirm transfer to"), transfer->recipient);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
return true;
}
bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node,
const NEMTransactionCommon *common,
const NEMTransfer *transfer) {
static uint8_t
encrypted[NEM_ENCRYPTED_PAYLOAD_SIZE(sizeof(transfer->payload.bytes))];
const uint8_t *payload = transfer->payload.bytes;
size_t size = transfer->payload.size;
if (transfer->has_public_key) {
if (node == NULL) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Private key unavailable for encrypted message"));
return false;
}
random_buffer(encrypted, NEM_SALT_SIZE + AES_BLOCK_SIZE);
const uint8_t *salt = encrypted;
const uint8_t *iv = &encrypted[NEM_SALT_SIZE];
uint8_t *buffer = &encrypted[NEM_SALT_SIZE + AES_BLOCK_SIZE];
bool ret = hdnode_nem_encrypt(node, transfer->public_key.bytes, iv, salt,
payload, size, buffer);
if (!ret) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to encrypt payload"));
return false;
}
payload = encrypted;
size = NEM_ENCRYPTED_PAYLOAD_SIZE(size);
}
bool ret = nem_transaction_create_transfer(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, transfer->recipient, transfer->amount, payload, size,
transfer->has_public_key, transfer->mosaics_count);
if (!ret) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to create transfer transaction"));
return false;
}
for (size_t i = 0; i < transfer->mosaics_count; i++) {
const NEMMosaic *mosaic = &transfer->mosaics[i];
ret = nem_transaction_write_mosaic(context, mosaic->namespace,
mosaic->mosaic, mosaic->quantity);
if (!ret) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to attach mosaics"));
return false;
}
}
return true;
}
bool nem_askProvisionNamespace(const NEMTransactionCommon *common,
const NEMProvisionNamespace *provision_namespace,
const char *desc) {
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"), desc, _("Create namespace"),
provision_namespace->namespace,
provision_namespace->has_parent ? _("under namespace") : NULL,
provision_namespace->has_parent ? provision_namespace->parent : NULL,
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
layoutNEMNetworkFee(desc, true, _("Confirm rental fee of"),
provision_namespace->fee, _("and network fee of"),
common->fee);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
return true;
}
bool nem_fsmProvisionNamespace(
nem_transaction_ctx *context, const NEMTransactionCommon *common,
const NEMProvisionNamespace *provision_namespace) {
return nem_transaction_create_provision_namespace(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, provision_namespace->namespace,
provision_namespace->has_parent ? provision_namespace->parent : NULL,
provision_namespace->sink, provision_namespace->fee);
}
bool nem_askMosaicCreation(const NEMTransactionCommon *common,
const NEMMosaicCreation *mosaic_creation,
const char *desc, const char *address) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc,
_("Create mosaic"), mosaic_creation->definition.mosaic,
_("under namespace"), mosaic_creation->definition.namespace,
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
layoutNEMMosaicDescription(mosaic_creation->definition.description);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
char str_out[32];
bn_format_uint64(mosaic_creation->definition.supply, NULL, NULL,
mosaic_creation->definition.divisibility,
mosaic_creation->definition.divisibility, true, str_out,
sizeof(str_out));
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"), _("Properties"),
mosaic_creation->definition.mutable_supply ? _("Mutable supply:")
: _("Immutable supply:"),
str_out, _("Mosaic will be"),
mosaic_creation->definition.transferable ? _("transferable")
: _("non-transferable"),
NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
if (mosaic_creation->definition.has_levy) {
layoutNEMLevy(&mosaic_creation->definition, common->network);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
if (strcmp(address, mosaic_creation->definition.levy_address) == 0) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"),
_("Levy Recipient"), _("Levy will be paid to"),
_("yourself"), NULL, NULL, NULL, NULL);
} else {
layoutNEMDialog(&bmp_icon_question, _("Cancel"), _("Next"),
_("Levy Recipient"), _("Levy will be paid to"),
mosaic_creation->definition.levy_address);
}
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
}
layoutNEMNetworkFee(desc, true, _("Confirm creation fee"),
mosaic_creation->fee, _("and network fee of"),
common->fee);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
return true;
}
bool nem_fsmMosaicCreation(nem_transaction_ctx *context,
const NEMTransactionCommon *common,
const NEMMosaicCreation *mosaic_creation) {
return nem_transaction_create_mosaic_creation(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, mosaic_creation->definition.namespace,
mosaic_creation->definition.mosaic,
mosaic_creation->definition.description,
mosaic_creation->definition.divisibility,
mosaic_creation->definition.supply,
mosaic_creation->definition.mutable_supply,
mosaic_creation->definition.transferable,
mosaic_creation->definition.levy, mosaic_creation->definition.fee,
mosaic_creation->definition.levy_address,
mosaic_creation->definition.levy_namespace,
mosaic_creation->definition.levy_mosaic, mosaic_creation->sink,
mosaic_creation->fee);
}
bool nem_askSupplyChange(const NEMTransactionCommon *common,
const NEMMosaicSupplyChange *supply_change,
const char *desc) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc,
_("Modify supply for"), supply_change->mosaic,
_("under namespace"), supply_change->namespace, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
char str_out[32];
bn_format_uint64(supply_change->delta, NULL, NULL, 0, 0, false, str_out,
sizeof(str_out));
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"), desc,
supply_change->type == NEMSupplyChangeType_SupplyChange_Increase
? _("Increase supply by")
: _("Decrease supply by"),
str_out, _("whole units"), NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL,
0);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
return true;
}
bool nem_fsmSupplyChange(nem_transaction_ctx *context,
const NEMTransactionCommon *common,
const NEMMosaicSupplyChange *supply_change) {
return nem_transaction_create_mosaic_supply_change(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, supply_change->namespace, supply_change->mosaic,
supply_change->type, supply_change->delta);
}
bool nem_askAggregateModification(
const NEMTransactionCommon *common,
const NEMAggregateModification *aggregate_modification, const char *desc,
bool creation) {
if (creation) {
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc,
_("Convert account to"), _("multisig account?"), NULL,
NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
}
char address[NEM_ADDRESS_SIZE + 1];
for (size_t i = 0; i < aggregate_modification->modifications_count; i++) {
const NEMCosignatoryModification *modification =
&aggregate_modification->modifications[i];
nem_get_address(modification->public_key.bytes, common->network, address);
layoutNEMDialog(
&bmp_icon_question, _("Cancel"), _("Next"), desc,
modification->type == NEMModificationType_CosignatoryModification_Add
? _("Add cosignatory")
: _("Remove cosignatory"),
address);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
}
int32_t relative_change = aggregate_modification->relative_change;
if (relative_change) {
char str_out[32];
bn_format_uint64(relative_change < 0 ? -relative_change : relative_change,
NULL, NULL, 0, 0, false, str_out, sizeof(str_out));
layoutDialogSwipe(&bmp_icon_question, _("Cancel"), _("Next"), desc,
creation ? _("Set minimum")
: (relative_change < 0 ? _("Decrease minimum")
: _("Increase minimum")),
creation ? _("cosignatories to") : _("cosignatories by"),
str_out, NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
}
layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL,
0);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
return true;
}
bool nem_fsmAggregateModification(
nem_transaction_ctx *context, const NEMTransactionCommon *common,
const NEMAggregateModification *aggregate_modification) {
bool ret = nem_transaction_create_aggregate_modification(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, aggregate_modification->modifications_count,
aggregate_modification->relative_change != 0);
if (!ret) return false;
for (size_t i = 0; i < aggregate_modification->modifications_count; i++) {
const NEMCosignatoryModification *modification =
&aggregate_modification->modifications[i];
ret = nem_transaction_write_cosignatory_modification(
context, modification->type, modification->public_key.bytes);
if (!ret) return false;
}
if (aggregate_modification->relative_change) {
ret = nem_transaction_write_minimum_cosignatories(
context, aggregate_modification->relative_change);
if (!ret) return false;
}
return true;
}
bool nem_askImportanceTransfer(const NEMTransactionCommon *common,
const NEMImportanceTransfer *importance_transfer,
const char *desc) {
layoutDialogSwipe(
&bmp_icon_question, _("Cancel"), _("Next"), desc,
importance_transfer->mode ==
NEMImportanceTransferMode_ImportanceTransfer_Activate
? _("Activate remote")
: _("Deactivate remote"),
_("harvesting?"), NULL, NULL, NULL, NULL);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
layoutNEMNetworkFee(desc, true, _("Confirm network fee"), common->fee, NULL,
0);
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
return false;
}
return true;
}
bool nem_fsmImportanceTransfer(
nem_transaction_ctx *context, const NEMTransactionCommon *common,
const NEMImportanceTransfer *importance_transfer) {
return nem_transaction_create_importance_transfer(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, importance_transfer->mode,
importance_transfer->public_key.bytes);
}
bool nem_askMultisig(const char *address, const char *desc, bool cosigning,
uint64_t fee) {
layoutNEMDialog(
&bmp_icon_question, _("Cancel"), _("Next"), desc,
cosigning ? _("Cosign transaction for") : _("Initiate transaction for"),
address);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
layoutNEMNetworkFee(desc, false, _("Confirm multisig fee"), fee, NULL, 0);
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
return false;
}
return true;
}
bool nem_fsmMultisig(nem_transaction_ctx *context,
const NEMTransactionCommon *common,
const nem_transaction_ctx *inner, bool cosigning) {
bool ret;
if (cosigning) {
ret = nem_transaction_create_multisig_signature(
context, common->network, common->timestamp, NULL, common->fee,
common->deadline, inner);
} else {
ret = nem_transaction_create_multisig(context, common->network,
common->timestamp, NULL, common->fee,
common->deadline, inner);
}
if (!ret) {
fsm_sendFailure(FailureType_Failure_ProcessError,
_("Failed to create multisig transaction"));
return false;
}
return true;
}
const NEMMosaicDefinition *nem_mosaicByName(const char *namespace,
const char *mosaic,
uint8_t network) {
for (size_t i = 0; i < NEM_MOSAIC_DEFINITIONS_COUNT; i++) {
const NEMMosaicDefinition *definition = &NEM_MOSAIC_DEFINITIONS[i];
if (nem_mosaicMatches(definition, namespace, mosaic, network)) {
return definition;
}
}
return NULL;
}
static inline size_t format_amount(const NEMMosaicDefinition *definition,
const bignum256 *amnt,
const bignum256 *multiplier, int divisor,
char *str_out, size_t size) {
bignum256 val;
memcpy(&val, amnt, sizeof(bignum256));
if (multiplier) {
bn_multiply(multiplier, &val, &secp256k1.prime);
divisor += NEM_MOSAIC_DEFINITION_XEM->divisibility;
}
return bn_format(
&val, NULL,
definition && definition->has_ticker ? definition->ticker : NULL,
definition && definition->has_divisibility ? definition->divisibility : 0,
-divisor, false, str_out, size);
}
size_t nem_canonicalizeMosaics(NEMMosaic *mosaics, size_t mosaics_count) {
if (mosaics_count <= 1) {
return mosaics_count;
}
size_t actual_count = 0;
bool skip[mosaics_count];
memzero(skip, sizeof(skip));
// Merge duplicates
for (size_t i = 0; i < mosaics_count; i++) {
if (skip[i]) continue;
NEMMosaic *mosaic = &mosaics[actual_count];
if (actual_count++ != i) {
memcpy(mosaic, &mosaics[i], sizeof(NEMMosaic));
}
for (size_t j = i + 1; j < mosaics_count; j++) {
if (skip[j]) continue;
const NEMMosaic *new_mosaic = &mosaics[j];
if (nem_mosaicCompare(mosaic, new_mosaic) == 0) {
skip[j] = true;
mosaic->quantity += new_mosaic->quantity;
}
}
}
NEMMosaic temp;
// Sort mosaics
for (size_t i = 0; i < actual_count - 1; i++) {
NEMMosaic *a = &mosaics[i];
for (size_t j = i + 1; j < actual_count; j++) {
NEMMosaic *b = &mosaics[j];
if (nem_mosaicCompare(a, b) > 0) {
memcpy(&temp, a, sizeof(NEMMosaic));
memcpy(a, b, sizeof(NEMMosaic));
memcpy(b, &temp, sizeof(NEMMosaic));
}
}
}
return actual_count;
}
void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier,
char *str_out, size_t size) {
bignum256 amnt;
bn_read_uint64(quantity, &amnt);
format_amount(definition, &amnt, multiplier, 0, str_out, size);
}
bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier,
uint8_t network, char *str_out, size_t size) {
if (!definition->has_levy || !definition->has_fee) {
return false;
}
bignum256 amnt, fee;
bn_read_uint64(quantity, &amnt);
bn_read_uint64(definition->fee, &fee);
const NEMMosaicDefinition *mosaic = nem_mosaicByName(
definition->levy_namespace, definition->levy_mosaic, network);
switch (definition->levy) {
case NEMMosaicLevy_MosaicLevy_Absolute:
return format_amount(mosaic, &fee, NULL, 0, str_out, size);
case NEMMosaicLevy_MosaicLevy_Percentile:
bn_multiply(&fee, &amnt, &secp256k1.prime);
return format_amount(mosaic, &amnt, multiplier,
NEM_LEVY_PERCENTILE_DIVISOR, str_out, size);
default:
return false;
}
}

153
legacy/firmware/nem2.h Normal file
View File

@ -0,0 +1,153 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2017 Saleem Rashid <trezor@saleemrashid.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __NEM2_H__
#define __NEM2_H__
#include "nem.h"
#include "nem_mosaics.h"
#include "messages-nem.pb.h"
#include <stdbool.h>
const char *nem_validate_common(NEMTransactionCommon *common, bool inner);
const char *nem_validate_transfer(const NEMTransfer *transfer, uint8_t network);
const char *nem_validate_provision_namespace(
const NEMProvisionNamespace *provision_namespace, uint8_t network);
const char *nem_validate_mosaic_creation(
const NEMMosaicCreation *mosaic_creation, uint8_t network);
const char *nem_validate_supply_change(
const NEMMosaicSupplyChange *supply_change);
const char *nem_validate_aggregate_modification(
const NEMAggregateModification *aggregate_modification, bool creation);
const char *nem_validate_importance_transfer(
const NEMImportanceTransfer *importance_transfer);
bool nem_askTransfer(const NEMTransactionCommon *common,
const NEMTransfer *transfer, const char *desc);
bool nem_fsmTransfer(nem_transaction_ctx *context, const HDNode *node,
const NEMTransactionCommon *common,
const NEMTransfer *transfer);
bool nem_askProvisionNamespace(const NEMTransactionCommon *common,
const NEMProvisionNamespace *provision_namespace,
const char *desc);
bool nem_fsmProvisionNamespace(
nem_transaction_ctx *context, const NEMTransactionCommon *common,
const NEMProvisionNamespace *provision_namespace);
bool nem_askMosaicCreation(const NEMTransactionCommon *common,
const NEMMosaicCreation *mosaic_creation,
const char *desc, const char *address);
bool nem_fsmMosaicCreation(nem_transaction_ctx *context,
const NEMTransactionCommon *common,
const NEMMosaicCreation *mosaic_creation);
bool nem_askSupplyChange(const NEMTransactionCommon *common,
const NEMMosaicSupplyChange *supply_change,
const char *desc);
bool nem_fsmSupplyChange(nem_transaction_ctx *context,
const NEMTransactionCommon *common,
const NEMMosaicSupplyChange *supply_change);
bool nem_askAggregateModification(
const NEMTransactionCommon *common,
const NEMAggregateModification *aggregate_modification, const char *desc,
bool creation);
bool nem_fsmAggregateModification(
nem_transaction_ctx *context, const NEMTransactionCommon *common,
const NEMAggregateModification *aggregate_modification);
bool nem_askImportanceTransfer(const NEMTransactionCommon *common,
const NEMImportanceTransfer *importance_transfer,
const char *desc);
bool nem_fsmImportanceTransfer(
nem_transaction_ctx *context, const NEMTransactionCommon *common,
const NEMImportanceTransfer *importance_transfer);
bool nem_askMultisig(const char *address, const char *desc, bool cosigning,
uint64_t fee);
bool nem_fsmMultisig(nem_transaction_ctx *context,
const NEMTransactionCommon *common,
const nem_transaction_ctx *inner, bool cosigning);
const NEMMosaicDefinition *nem_mosaicByName(const char *namespace,
const char *mosaic,
uint8_t network);
size_t nem_canonicalizeMosaics(NEMMosaic *mosaics, size_t mosaics_count);
void nem_mosaicFormatAmount(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier,
char *str_out, size_t size);
bool nem_mosaicFormatLevy(const NEMMosaicDefinition *definition,
uint64_t quantity, const bignum256 *multiplier,
uint8_t network, char *str_out, size_t size);
static inline void nem_mosaicFormatName(const char *namespace,
const char *mosaic, char *str_out,
size_t size) {
strlcpy(str_out, namespace, size);
strlcat(str_out, ".", size);
strlcat(str_out, mosaic, size);
}
static inline bool nem_mosaicMatches(const NEMMosaicDefinition *definition,
const char *namespace, const char *mosaic,
uint8_t network) {
if (strcmp(namespace, definition->namespace) == 0 &&
strcmp(mosaic, definition->mosaic) == 0) {
if (definition->networks_count == 0) {
return true;
}
for (size_t i = 0; i < definition->networks_count; i++) {
if (definition->networks[i] == network) {
return true;
}
}
}
return false;
}
static inline int nem_mosaicCompare(const NEMMosaic *a, const NEMMosaic *b) {
size_t namespace_length = strlen(a->namespace);
// Ensure that strlen(a->namespace) <= strlen(b->namespace)
if (namespace_length > strlen(b->namespace)) {
return -nem_mosaicCompare(b, a);
}
int r = strncmp(a->namespace, b->namespace, namespace_length);
if (r == 0 && b->namespace[namespace_length] != '\0') {
// The next character would be the separator
r = (':' - b->namespace[namespace_length]);
}
if (r == 0) {
// Finally compare the mosaic
r = strcmp(a->mosaic, b->mosaic);
}
return r;
}
#endif

View File

@ -0,0 +1,36 @@
<%
ATTRIBUTES = (
("name", c_str),
("ticker", lambda s: c_str(" " + s) if s else "NULL"),
("namespace", c_str),
("mosaic", c_str),
("divisibility", int),
("levy", lambda s: "NEMMosaicLevy_" + s),
("fee", int),
("levy_namespace", c_str),
("levy_mosaic", c_str),
)
%>\
// This file is automatically generated from nem_mosaics.c.mako
// DO NOT EDIT
#include "nem_mosaics.h"
const NEMMosaicDefinition NEM_MOSAIC_DEFINITIONS[NEM_MOSAIC_DEFINITIONS_COUNT] = {
% for m in supported_on("trezor1", nem):
{
% for attr, func in ATTRIBUTES:
% if attr in m:
.has_${attr} = true,
.${attr} = ${func(m[attr])},
% endif
% endfor
% if "networks" in m:
.networks_count = ${len(m["networks"])},
.networks = { ${", ".join(map(str, m["networks"]))} },
% endif
},
% endfor
};
const NEMMosaicDefinition *NEM_MOSAIC_DEFINITION_XEM = NEM_MOSAIC_DEFINITIONS;

View File

@ -0,0 +1,15 @@
// This file is automatically generated from nem_mosaics.h.mako
// DO NOT EDIT
#ifndef __NEM_MOSAICS_H__
#define __NEM_MOSAICS_H__
#include "messages-nem.pb.h"
<% nem_list = list(supported_on("trezor1", nem)) %>\
#define NEM_MOSAIC_DEFINITIONS_COUNT (${len(nem_list)})
extern const NEMMosaicDefinition NEM_MOSAIC_DEFINITIONS[NEM_MOSAIC_DEFINITIONS_COUNT];
extern const NEMMosaicDefinition *NEM_MOSAIC_DEFINITION_XEM;
#endif

67
legacy/firmware/otp.c Normal file
View File

@ -0,0 +1,67 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2019 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "otp.h"
#include <libopencm3/stm32/flash.h>
#define FLASH_OTP_BASE 0x1FFF7800U
#define FLASH_OTP_LOCK_BASE 0x1FFF7A00U
bool flash_otp_is_locked(uint8_t block) {
return 0x00 == *(volatile uint8_t *)(FLASH_OTP_LOCK_BASE + block);
}
bool flash_otp_lock(uint8_t block) {
if (block >= FLASH_OTP_NUM_BLOCKS) {
return false;
}
flash_unlock();
flash_program_byte(FLASH_OTP_LOCK_BASE + block, 0x00);
flash_lock();
return true;
}
bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data,
uint8_t datalen) {
if (block >= FLASH_OTP_NUM_BLOCKS ||
offset + datalen > FLASH_OTP_BLOCK_SIZE) {
return false;
}
for (uint8_t i = 0; i < datalen; i++) {
data[i] = *(volatile uint8_t *)(FLASH_OTP_BASE +
block * FLASH_OTP_BLOCK_SIZE + offset + i);
}
return true;
}
bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data,
uint8_t datalen) {
if (block >= FLASH_OTP_NUM_BLOCKS ||
offset + datalen > FLASH_OTP_BLOCK_SIZE) {
return false;
}
flash_unlock();
for (uint8_t i = 0; i < datalen; i++) {
uint32_t address =
FLASH_OTP_BASE + block * FLASH_OTP_BLOCK_SIZE + offset + i;
flash_program_byte(address, data[i]);
}
flash_lock();
return true;
}

38
legacy/firmware/otp.h Normal file
View File

@ -0,0 +1,38 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2019 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __OTP_H__
#define __OTP_H__
#include <stdbool.h>
#include <stdint.h>
#define FLASH_OTP_NUM_BLOCKS 16
#define FLASH_OTP_BLOCK_SIZE 32
#define FLASH_OTP_BLOCK_RANDOMNESS 3
bool flash_otp_is_locked(uint8_t block);
bool flash_otp_lock(uint8_t block);
bool flash_otp_read(uint8_t block, uint8_t offset, uint8_t *data,
uint8_t datalen);
bool flash_otp_write(uint8_t block, uint8_t offset, const uint8_t *data,
uint8_t datalen);
#endif

View File

@ -0,0 +1,78 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include "layout2.h"
#include "oled.h"
#include "pinmatrix.h"
#include "rng.h"
static char pinmatrix_perm[10] = "XXXXXXXXX";
void pinmatrix_draw(const char *text) {
const BITMAP *bmp_digits[10] = {
&bmp_digit0, &bmp_digit1, &bmp_digit2, &bmp_digit3, &bmp_digit4,
&bmp_digit5, &bmp_digit6, &bmp_digit7, &bmp_digit8, &bmp_digit9,
};
layoutSwipe();
const int w = bmp_digit0.width, h = bmp_digit0.height, pad = 2;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
// use (2 - j) instead of j to achieve 789456123 layout
int k = pinmatrix_perm[i + (2 - j) * 3] - '0';
if (text) {
oledDrawStringCenter(OLED_WIDTH / 2, 0, text, FONT_STANDARD);
}
oledDrawBitmap((OLED_WIDTH - 3 * w - 2 * pad) / 2 + i * (w + pad),
OLED_HEIGHT - 3 * h - 2 * pad + j * (h + pad),
bmp_digits[k]);
}
}
oledRefresh();
}
void pinmatrix_start(const char *text) {
for (int i = 0; i < 9; i++) {
pinmatrix_perm[i] = '1' + i;
}
pinmatrix_perm[9] = 0;
random_permute(pinmatrix_perm, 9);
pinmatrix_draw(text);
}
void pinmatrix_done(char *pin) {
int k, i = 0;
while (pin && pin[i]) {
k = pin[i] - '1';
if (k >= 0 && k <= 8) {
pin[i] = pinmatrix_perm[k];
} else {
pin[i] = 'X';
}
i++;
}
memset(pinmatrix_perm, 'X', sizeof(pinmatrix_perm) - 1);
}
#if DEBUG_LINK
const char *pinmatrix_get(void) { return pinmatrix_perm; }
#endif

View File

@ -0,0 +1,27 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __PINMATRIX_H__
#define __PINMATRIX_H__
void pinmatrix_start(const char *text);
void pinmatrix_done(char *pin);
const char *pinmatrix_get(void);
#endif

320
legacy/firmware/protect.c Normal file
View File

@ -0,0 +1,320 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "protect.h"
#include "buttons.h"
#include "config.h"
#include "debug.h"
#include "fsm.h"
#include "gettext.h"
#include "layout2.h"
#include "memory.h"
#include "memzero.h"
#include "messages.h"
#include "messages.pb.h"
#include "oled.h"
#include "pinmatrix.h"
#include "usb.h"
#include "util.h"
#define MAX_WRONG_PINS 15
bool protectAbortedByCancel = false;
bool protectAbortedByInitialize = false;
bool protectButton(ButtonRequestType type, bool confirm_only) {
ButtonRequest resp;
bool result = false;
bool acked = false;
#if DEBUG_LINK
bool debug_decided = false;
#endif
memzero(&resp, sizeof(ButtonRequest));
resp.has_code = true;
resp.code = type;
usbTiny(1);
buttonUpdate(); // Clear button state
msg_write(MessageType_MessageType_ButtonRequest, &resp);
for (;;) {
usbPoll();
// check for ButtonAck
if (msg_tiny_id == MessageType_MessageType_ButtonAck) {
msg_tiny_id = 0xFFFF;
acked = true;
}
// button acked - check buttons
if (acked) {
usbSleep(5);
buttonUpdate();
if (button.YesUp) {
result = true;
break;
}
if (!confirm_only && button.NoUp) {
result = false;
break;
}
}
// check for Cancel / Initialize
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
msg_tiny_id = 0xFFFF;
result = false;
break;
}
#if DEBUG_LINK
// check DebugLink
if (msg_tiny_id == MessageType_MessageType_DebugLinkDecision) {
msg_tiny_id = 0xFFFF;
DebugLinkDecision *dld = (DebugLinkDecision *)msg_tiny;
result = dld->yes_no;
debug_decided = true;
}
if (acked && debug_decided) {
break;
}
if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) {
msg_tiny_id = 0xFFFF;
fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny);
}
#endif
}
usbTiny(0);
return result;
}
const char *requestPin(PinMatrixRequestType type, const char *text) {
PinMatrixRequest resp;
memzero(&resp, sizeof(PinMatrixRequest));
resp.has_type = true;
resp.type = type;
usbTiny(1);
msg_write(MessageType_MessageType_PinMatrixRequest, &resp);
pinmatrix_start(text);
for (;;) {
usbPoll();
if (msg_tiny_id == MessageType_MessageType_PinMatrixAck) {
msg_tiny_id = 0xFFFF;
PinMatrixAck *pma = (PinMatrixAck *)msg_tiny;
pinmatrix_done(pma->pin); // convert via pinmatrix
usbTiny(0);
return pma->pin;
}
// check for Cancel / Initialize
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
pinmatrix_done(0);
msg_tiny_id = 0xFFFF;
usbTiny(0);
return 0;
}
#if DEBUG_LINK
if (msg_tiny_id == MessageType_MessageType_DebugLinkGetState) {
msg_tiny_id = 0xFFFF;
fsm_msgDebugLinkGetState((DebugLinkGetState *)msg_tiny);
}
#endif
}
}
secbool protectPinUiCallback(uint32_t wait, uint32_t progress,
const char *message) {
// Convert wait to secstr string.
char secstrbuf[] = _("________0 seconds");
char *secstr = secstrbuf + 9;
uint32_t secs = wait;
do {
secstr--;
*secstr = (secs % 10) + '0';
secs /= 10;
} while (secs > 0 && secstr >= secstrbuf);
if (wait == 1) {
// Change "seconds" to "second".
secstrbuf[16] = 0;
}
oledClear();
oledDrawStringCenter(OLED_WIDTH / 2, 0 * 9, message, FONT_STANDARD);
oledDrawStringCenter(OLED_WIDTH / 2, 2 * 9, _("Please wait"), FONT_STANDARD);
oledDrawStringCenter(OLED_WIDTH / 2, 3 * 9, secstr, FONT_STANDARD);
oledDrawStringCenter(OLED_WIDTH / 2, 4 * 9, _("to continue ..."),
FONT_STANDARD);
// progressbar
oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1);
oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0);
progress = progress * (OLED_WIDTH - 4) / 1000;
if (progress > OLED_WIDTH - 4) {
progress = OLED_WIDTH - 4;
}
oledBox(2, OLED_HEIGHT - 6, 1 + progress, OLED_HEIGHT - 3, 1);
oledRefresh();
// Check for Cancel / Initialize.
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
msg_tiny_id = 0xFFFF;
usbTiny(0);
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return sectrue;
}
return secfalse;
}
bool protectPin(bool use_cached) {
if (use_cached && session_isUnlocked()) {
return true;
}
const char *pin = "";
if (config_hasPin()) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current,
_("Please enter current PIN:"));
if (!pin) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
}
bool ret = config_unlock(pin);
if (!ret) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
}
return ret;
}
bool protectChangePin(bool removal) {
static CONFIDENTIAL char old_pin[MAX_PIN_LEN + 1] = "";
static CONFIDENTIAL char new_pin[MAX_PIN_LEN + 1] = "";
const char *pin = NULL;
if (config_hasPin()) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current,
_("Please enter current PIN:"));
if (pin == NULL) {
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
// If removing, defer the check to config_changePin().
if (!removal) {
usbTiny(1);
bool ret = config_unlock(pin);
usbTiny(0);
if (ret == false) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
return false;
}
}
strlcpy(old_pin, pin, sizeof(old_pin));
}
if (!removal) {
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst,
_("Please enter new PIN:"));
if (pin == NULL) {
memzero(old_pin, sizeof(old_pin));
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
strlcpy(new_pin, pin, sizeof(new_pin));
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond,
_("Please re-enter new PIN:"));
if (pin == NULL) {
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
fsm_sendFailure(FailureType_Failure_PinCancelled, NULL);
return false;
}
if (strncmp(new_pin, pin, sizeof(new_pin)) != 0) {
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
fsm_sendFailure(FailureType_Failure_PinMismatch, NULL);
return false;
}
}
bool ret = config_changePin(old_pin, new_pin);
memzero(old_pin, sizeof(old_pin));
memzero(new_pin, sizeof(new_pin));
if (ret == false) {
fsm_sendFailure(FailureType_Failure_PinInvalid, NULL);
}
return ret;
}
bool protectPassphrase(void) {
bool passphrase_protection = false;
config_getPassphraseProtection(&passphrase_protection);
if (!passphrase_protection || session_isPassphraseCached()) {
return true;
}
PassphraseRequest resp;
memzero(&resp, sizeof(PassphraseRequest));
usbTiny(1);
msg_write(MessageType_MessageType_PassphraseRequest, &resp);
layoutDialogSwipe(&bmp_icon_info, NULL, NULL, NULL, _("Please enter your"),
_("passphrase using"), _("the computer's"), _("keyboard."),
NULL, NULL);
bool result;
for (;;) {
usbPoll();
// TODO: correctly process PassphraseAck with state field set (mismatch =>
// Failure)
if (msg_tiny_id == MessageType_MessageType_PassphraseAck) {
msg_tiny_id = 0xFFFF;
PassphraseAck *ppa = (PassphraseAck *)msg_tiny;
session_cachePassphrase(ppa->has_passphrase ? ppa->passphrase : "");
result = true;
break;
}
// check for Cancel / Initialize
protectAbortedByCancel = (msg_tiny_id == MessageType_MessageType_Cancel);
protectAbortedByInitialize =
(msg_tiny_id == MessageType_MessageType_Initialize);
if (protectAbortedByCancel || protectAbortedByInitialize) {
msg_tiny_id = 0xFFFF;
result = false;
break;
}
}
usbTiny(0);
layoutHome();
return result;
}

37
legacy/firmware/protect.h Normal file
View File

@ -0,0 +1,37 @@
/*
* This file is part of the TREZOR project, https://trezor.io/
*
* Copyright (C) 2014 Pavol Rusnak <stick@satoshilabs.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __PROTECT_H__
#define __PROTECT_H__
#include <stdbool.h>
#include "messages-common.pb.h"
#include "secbool.h"
bool protectButton(ButtonRequestType type, bool confirm_only);
secbool protectPinUiCallback(uint32_t wait, uint32_t progress,
const char* message);
bool protectPin(bool use_cached);
bool protectChangePin(bool removal);
bool protectPassphrase(void);
extern bool protectAbortedByCancel;
extern bool protectAbortedByInitialize;
#endif

8
legacy/firmware/protob/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
*.pb
*_pb2.py
*.pb.c
*.pb.h
*.pyc
messages_map.h
messages_map_limits.h
__pycache__/

View File

@ -0,0 +1,32 @@
ifneq ($(V),1)
Q := @
endif
all: messages_map.h messages_map_limits.h messages-bitcoin.pb.c messages-common.pb.c messages-crypto.pb.c messages-debug.pb.c messages-ethereum.pb.c messages-management.pb.c messages-nem.pb.c messages.pb.c messages-stellar.pb.c messages-lisk.pb.c messages_nem_pb2.py
PYTHON ?= python
%.pb.c: %.pb %.options
@printf " NANOPB $@\n"
$(Q)$(PYTHON) ../../vendor/nanopb/generator/nanopb_generator.py $< \
-L '#include "%s"' \
-T \
-s "mangle_names:M_FLATTEN"
%.pb: %.proto
@printf " PROTOC $@\n"
$(Q)protoc -I/usr/include -I. $< -o $@
messages_%_pb2.py: messages-%.proto
@printf " PROTOC $@\n"
$(Q)protoc -I/usr/include -I. $< --python_out=.
%_pb2.py: %.proto
@printf " PROTOC $@\n"
$(Q)protoc -I/usr/include -I. $< --python_out=.
messages_map.h messages_map_limits.h: messages_map.py messages_pb2.py
$(Q)$(PYTHON) $< Cardano Tezos Ripple Monero DebugMonero Ontology Tron Eos Binance
clean:
rm -f *.pb *.o *.d *.pb.c *.pb.h *_pb2.py messages_map.h messages_map_limits.h

Some files were not shown because too many files have changed in this diff Show More