mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-21 23:18:13 +00:00
MONOREPO MERGE trezor-mcu
This commit is contained in:
commit
6aa05f8a6f
19
.gitmodules
vendored
19
.gitmodules
vendored
@ -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
2
legacy/.clang-format
Normal file
@ -0,0 +1,2 @@
|
||||
---
|
||||
BasedOnStyle: Google
|
1
legacy/.dockerignore
Normal file
1
legacy/.dockerignore
Normal file
@ -0,0 +1 @@
|
||||
_attic/
|
14
legacy/.gitignore
vendored
Normal file
14
legacy/.gitignore
vendored
Normal 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
63
legacy/.travis.yml
Normal 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
5
legacy/CONTRIBUTING.md
Normal 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
165
legacy/COPYING
Normal 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
77
legacy/Dockerfile
Normal 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
43
legacy/Makefile
Normal 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
226
legacy/Makefile.include
Normal 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
14
legacy/Pipfile
Normal 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
214
legacy/Pipfile.lock
generated
Normal 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
74
legacy/README.md
Normal 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
9
legacy/bootloader/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
*.o
|
||||
*.a
|
||||
*.d
|
||||
*.bin
|
||||
*.elf
|
||||
*.hex
|
||||
*.list
|
||||
*.srec
|
||||
*.log
|
50
legacy/bootloader/ChangeLog
Normal file
50
legacy/bootloader/ChangeLog
Normal 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
|
21
legacy/bootloader/Makefile
Normal file
21
legacy/bootloader/Makefile
Normal 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
|
153
legacy/bootloader/bootloader.c
Normal file
153
legacy/bootloader/bootloader.c
Normal 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;
|
||||
}
|
41
legacy/bootloader/bootloader.h
Normal file
41
legacy/bootloader/bootloader.h
Normal 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
|
12
legacy/bootloader/combine/prepare.py
Executable file
12
legacy/bootloader/combine/prepare.py
Executable 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))
|
2
legacy/bootloader/combine/write.sh
Executable file
2
legacy/bootloader/combine/write.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
st-flash write combined.bin 0x8000000
|
14
legacy/bootloader/firmware_align.py
Executable file
14
legacy/bootloader/firmware_align.py
Executable 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()
|
259
legacy/bootloader/firmware_sign.py
Executable file
259
legacy/bootloader/firmware_sign.py
Executable 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)
|
30
legacy/bootloader/firmware_sign_split.py
Executable file
30
legacy/bootloader/firmware_sign_split.py
Executable 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
|
203
legacy/bootloader/signatures.c
Normal file
203
legacy/bootloader/signatures.c
Normal 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(©, hdr, sizeof(image_header));
|
||||
memzero(copy.sig1, sizeof(copy.sig1));
|
||||
memzero(copy.sig2, sizeof(copy.sig2));
|
||||
memzero(copy.sig3, sizeof(copy.sig3));
|
||||
copy.sigindex1 = 0;
|
||||
copy.sigindex2 = 0;
|
||||
copy.sigindex3 = 0;
|
||||
sha256_Raw((const uint8_t *)©, sizeof(image_header), hash);
|
||||
}
|
||||
|
||||
bool firmware_present_new(void) {
|
||||
const image_header *hdr =
|
||||
(const image_header *)FLASH_PTR(FLASH_FWHEADER_START);
|
||||
if (hdr->magic != FIRMWARE_MAGIC_NEW) return false;
|
||||
// we need to ignore hdrlen for now
|
||||
// because we keep reset_handler ptr there
|
||||
// for compatibility with older bootloaders
|
||||
// after this is no longer necessary, let's uncomment the line below:
|
||||
// if (hdr->hdrlen != FLASH_FWHEADER_LEN) return false;
|
||||
if (hdr->codelen > FLASH_APP_LEN) return false;
|
||||
if (hdr->codelen < 4096) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
69
legacy/bootloader/signatures.h
Normal file
69
legacy/bootloader/signatures.h
Normal 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
467
legacy/bootloader/usb.c
Normal 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
25
legacy/bootloader/usb.h
Normal 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
|
77
legacy/bootloader/usb_desc.h
Normal file
77
legacy/bootloader/usb_desc.h
Normal 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",
|
||||
};
|
49
legacy/bootloader/usb_erase.h
Normal file
49
legacy/bootloader/usb_erase.h
Normal 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();
|
||||
}
|
92
legacy/bootloader/usb_send.h
Normal file
92
legacy/bootloader/usb_send.h
Normal 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
19
legacy/build.sh
Executable 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
70
legacy/buttons.c
Normal 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
50
legacy/buttons.h
Normal 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
83
legacy/common.c
Normal 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
43
legacy/common.h
Normal 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
18
legacy/demo/Makefile
Normal 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
309
legacy/demo/demo.c
Normal 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
17
legacy/emulator/Makefile
Normal 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
40
legacy/emulator/buttons.c
Normal 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;
|
||||
}
|
38
legacy/emulator/emulator.h
Normal file
38
legacy/emulator/emulator.h
Normal 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
136
legacy/emulator/memory.c
Normal 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
150
legacy/emulator/oled.c
Normal 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, ¤t_mode) != 0) {
|
||||
fprintf(stderr, "Failed to get current display mode: %s\n",
|
||||
SDL_GetError());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dstrect.x = (current_mode.w - OLED_WIDTH * scale) / 2;
|
||||
dstrect.y = (current_mode.h - OLED_HEIGHT * scale) / 2;
|
||||
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
} else {
|
||||
dstrect.x = 0;
|
||||
dstrect.y = 0;
|
||||
}
|
||||
|
||||
dstrect.w = OLED_WIDTH * scale;
|
||||
dstrect.h = OLED_HEIGHT * scale;
|
||||
|
||||
texture =
|
||||
SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||
SDL_TEXTUREACCESS_STREAMING, OLED_WIDTH, OLED_HEIGHT);
|
||||
|
||||
oledClear();
|
||||
oledRefresh();
|
||||
}
|
||||
|
||||
void oledRefresh(void) {
|
||||
/* Draw triangle in upper right corner */
|
||||
oledInvertDebugLink();
|
||||
|
||||
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
32
legacy/emulator/rng.c
Normal 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
108
legacy/emulator/setup.c
Normal 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
44
legacy/emulator/strl.c
Normal 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
30
legacy/emulator/strl.h
Normal 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
32
legacy/emulator/timer.c
Normal 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
127
legacy/emulator/udp.c
Normal 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
6
legacy/firmware/.gitignore
vendored
Normal 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
178
legacy/firmware/ChangeLog
Normal 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
139
legacy/firmware/Makefile
Normal 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
192
legacy/firmware/bl_check.c
Normal 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
|
||||
}
|
25
legacy/firmware/bl_check.h
Normal file
25
legacy/firmware/bl_check.h
Normal 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
19
legacy/firmware/bl_data.py
Executable 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)
|
51
legacy/firmware/coin_info.c.mako
Normal file
51
legacy/firmware/coin_info.c.mako
Normal 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
|
||||
};
|
14
legacy/firmware/coin_info.h.mako
Normal file
14
legacy/firmware/coin_info.h.mako
Normal 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
80
legacy/firmware/coins.c
Normal 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
64
legacy/firmware/coins.h
Normal 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
937
legacy/firmware/config.c
Normal 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
158
legacy/firmware/config.h
Normal 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
498
legacy/firmware/crypto.c
Normal 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
82
legacy/firmware/crypto.h
Normal 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
65
legacy/firmware/debug.c
Normal 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
42
legacy/firmware/debug.h
Normal 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
1
legacy/firmware/defs
Symbolic link
@ -0,0 +1 @@
|
||||
../vendor/trezor-common/defs
|
771
legacy/firmware/ethereum.c
Normal file
771
legacy/firmware/ethereum.c
Normal 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;
|
||||
}
|
37
legacy/firmware/ethereum.h
Normal file
37
legacy/firmware/ethereum.h
Normal 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
|
34
legacy/firmware/ethereum_networks.h.mako
Normal file
34
legacy/firmware/ethereum_networks.h.mako
Normal 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
|
24
legacy/firmware/ethereum_tokens.c.mako
Normal file
24
legacy/firmware/ethereum_tokens.c.mako
Normal 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;
|
||||
}
|
25
legacy/firmware/ethereum_tokens.h.mako
Normal file
25
legacy/firmware/ethereum_tokens.h.mako
Normal 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
260
legacy/firmware/fsm.c
Normal 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
136
legacy/firmware/fsm.h
Normal 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
|
329
legacy/firmware/fsm_msg_coin.h
Normal file
329
legacy/firmware/fsm_msg_coin.h
Normal 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();
|
||||
}
|
412
legacy/firmware/fsm_msg_common.h
Normal file
412
legacy/firmware/fsm_msg_common.h
Normal 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();
|
||||
}
|
299
legacy/firmware/fsm_msg_crypto.h
Normal file
299
legacy/firmware/fsm_msg_crypto.h
Normal 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();
|
||||
}
|
103
legacy/firmware/fsm_msg_debug.h
Normal file
103
legacy/firmware/fsm_msg_debug.h
Normal 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
|
185
legacy/firmware/fsm_msg_ethereum.h
Normal file
185
legacy/firmware/fsm_msg_ethereum.h
Normal 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();
|
||||
}
|
144
legacy/firmware/fsm_msg_lisk.h
Normal file
144
legacy/firmware/fsm_msg_lisk.h
Normal 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();
|
||||
}
|
352
legacy/firmware/fsm_msg_nem.h
Normal file
352
legacy/firmware/fsm_msg_nem.h
Normal 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();
|
||||
}
|
274
legacy/firmware/fsm_msg_stellar.h
Normal file
274
legacy/firmware/fsm_msg_stellar.h
Normal 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
25
legacy/firmware/gettext.h
Normal 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
33
legacy/firmware/header.S
Normal 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
945
legacy/firmware/layout2.c
Normal 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
94
legacy/firmware/layout2.h
Normal 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
354
legacy/firmware/lisk.c
Normal 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
46
legacy/firmware/lisk.h
Normal 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
369
legacy/firmware/messages.c
Normal 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;
|
||||
}
|
||||
}
|
52
legacy/firmware/messages.h
Normal file
52
legacy/firmware/messages.h
Normal 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
790
legacy/firmware/nem2.c
Normal 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
153
legacy/firmware/nem2.h
Normal 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
|
36
legacy/firmware/nem_mosaics.c.mako
Normal file
36
legacy/firmware/nem_mosaics.c.mako
Normal 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;
|
15
legacy/firmware/nem_mosaics.h.mako
Normal file
15
legacy/firmware/nem_mosaics.h.mako
Normal 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
67
legacy/firmware/otp.c
Normal 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
38
legacy/firmware/otp.h
Normal 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
|
78
legacy/firmware/pinmatrix.c
Normal file
78
legacy/firmware/pinmatrix.c
Normal 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
|
27
legacy/firmware/pinmatrix.h
Normal file
27
legacy/firmware/pinmatrix.h
Normal 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
320
legacy/firmware/protect.c
Normal 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
37
legacy/firmware/protect.h
Normal 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
8
legacy/firmware/protob/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
*.pb
|
||||
*_pb2.py
|
||||
*.pb.c
|
||||
*.pb.h
|
||||
*.pyc
|
||||
messages_map.h
|
||||
messages_map_limits.h
|
||||
__pycache__/
|
32
legacy/firmware/protob/Makefile
Normal file
32
legacy/firmware/protob/Makefile
Normal 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
Loading…
Reference in New Issue
Block a user