import v1.0.0
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
*.a
|
||||||
|
*.d
|
||||||
|
*.o
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.hex
|
||||||
|
*.list
|
||||||
|
*.srec
|
||||||
|
usb.pb*
|
||||||
|
bootloader
|
6
.gitmodules
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[submodule "trezor-crypto"]
|
||||||
|
path = trezor-crypto
|
||||||
|
url = https://github.com/trezor/trezor-crypto.git
|
||||||
|
[submodule "trezor-common"]
|
||||||
|
path = trezor-common
|
||||||
|
url = https://github.com/trezor/trezor-common.git
|
165
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.
|
15
Makefile
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
OBJS += buttons.o
|
||||||
|
OBJS += layout.o
|
||||||
|
OBJS += oled.o
|
||||||
|
OBJS += rng.o
|
||||||
|
OBJS += serialno.o
|
||||||
|
OBJS += setup.o
|
||||||
|
OBJS += util.o
|
||||||
|
OBJS += memory.o
|
||||||
|
OBJS += gen/bitmaps.o
|
||||||
|
OBJS += gen/fonts.o
|
||||||
|
|
||||||
|
libtrezor.a: $(OBJS)
|
||||||
|
ar rcs libtrezor.a $(OBJS)
|
||||||
|
|
||||||
|
include Makefile.include
|
123
Makefile.include
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
PREFIX ?= arm-none-eabi-
|
||||||
|
CC = $(PREFIX)gcc
|
||||||
|
LD = $(PREFIX)gcc
|
||||||
|
OBJCOPY = $(PREFIX)objcopy
|
||||||
|
OBJDUMP = $(PREFIX)objdump
|
||||||
|
FLASH = st-flash
|
||||||
|
OPENOCD = openocd
|
||||||
|
TOP_DIR = /home/stick/work/trezor/trezor-mcu
|
||||||
|
TOOLCHAIN_DIR = $(TOP_DIR)/../libopencm3
|
||||||
|
|
||||||
|
OPTFLAGS = -Os -g
|
||||||
|
|
||||||
|
CFLAGS += $(OPTFLAGS) \
|
||||||
|
-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 \
|
||||||
|
-mcpu=cortex-m3 \
|
||||||
|
-mthumb \
|
||||||
|
-msoft-float \
|
||||||
|
-DSTM32F2 \
|
||||||
|
-I$(TOOLCHAIN_DIR)/include \
|
||||||
|
-I$(TOP_DIR) \
|
||||||
|
-I$(TOP_DIR)/gen \
|
||||||
|
-I$(TOP_DIR)/trezor-crypto \
|
||||||
|
|
||||||
|
ifdef APPVER
|
||||||
|
CFLAGS += -DAPPVER=$(APPVER)
|
||||||
|
LDSCRIPT = $(TOP_DIR)/memory_app_$(APPVER).ld
|
||||||
|
else
|
||||||
|
LDSCRIPT = $(TOP_DIR)/memory.ld
|
||||||
|
endif
|
||||||
|
|
||||||
|
LDFLAGS += --static \
|
||||||
|
-Wl,--start-group \
|
||||||
|
-lc \
|
||||||
|
-lgcc \
|
||||||
|
-lnosys \
|
||||||
|
-Wl,--end-group \
|
||||||
|
-L$(TOP_DIR) \
|
||||||
|
-L$(TOOLCHAIN_DIR)/lib \
|
||||||
|
-L$(TOOLCHAIN_DIR)/lib/stm32/f2 \
|
||||||
|
-T$(LDSCRIPT) \
|
||||||
|
-nostartfiles \
|
||||||
|
-Wl,--gc-sections \
|
||||||
|
-mthumb \
|
||||||
|
-march=armv7 \
|
||||||
|
-mfix-cortex-m3-ldrd \
|
||||||
|
-msoft-float
|
||||||
|
|
||||||
|
all: $(NAME).bin
|
||||||
|
|
||||||
|
flash: $(NAME).bin
|
||||||
|
$(FLASH) write $(NAME).bin 0x8000000
|
||||||
|
|
||||||
|
flash2: $(NAME).hex
|
||||||
|
$(OPENOCD) -f board/stm32f4discovery.cfg \
|
||||||
|
-c "init" \
|
||||||
|
-c "reset init" \
|
||||||
|
-c "stm32f2x mass_erase 0" \
|
||||||
|
-c "flash write_image $(NAME).hex" \
|
||||||
|
-c "reset" \
|
||||||
|
-c "shutdown"
|
||||||
|
|
||||||
|
upload:
|
||||||
|
../../python-trezor/cmd.py firmware_update -f $(NAME).bin
|
||||||
|
|
||||||
|
sign: $(NAME).bin
|
||||||
|
../bootloader/firmware_sign.py -f $(NAME).bin
|
||||||
|
|
||||||
|
release: $(NAME).bin
|
||||||
|
../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
|
||||||
|
$(OBJCOPY) -Obinary $(NAME).elf $(NAME).bin
|
||||||
|
|
||||||
|
$(NAME).hex: $(NAME).elf
|
||||||
|
$(OBJCOPY) -Oihex $(NAME).elf $(NAME).hex
|
||||||
|
|
||||||
|
$(NAME).srec: $(NAME).elf
|
||||||
|
$(OBJCOPY) -Osrec $(NAME).elf $(NAME).srec
|
||||||
|
|
||||||
|
$(NAME).list: $(NAME).elf
|
||||||
|
$(OBJDUMP) -S $(NAME).elf > $(NAME).list
|
||||||
|
|
||||||
|
$(NAME).elf: $(OBJS) $(LDSCRIPT) $(TOOLCHAIN_DIR)/lib/libopencm3_stm32f2.a $(TOP_DIR)/libtrezor.a
|
||||||
|
$(LD) -o $(NAME).elf $(OBJS) -ltrezor -lopencm3_stm32f2 $(LDFLAGS)
|
||||||
|
|
||||||
|
%.o: %.c Makefile
|
||||||
|
$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
%.small.o: %.c Makefile
|
||||||
|
$(CC) $(CFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(OBJS)
|
||||||
|
rm -f *.a
|
||||||
|
rm -f *.d
|
||||||
|
rm -f *.elf
|
||||||
|
rm -f *.bin
|
||||||
|
rm -f *.hex
|
||||||
|
rm -f *.srec
|
||||||
|
rm -f *.list
|
3
README
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Embedded software for TREZOR
|
||||||
|
|
||||||
|
http://bitcointrezor.com/
|
68
buttons.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
void buttonUpdate()
|
||||||
|
{
|
||||||
|
uint16_t state;
|
||||||
|
static uint16_t last_state = BTN_PIN_YES | BTN_PIN_NO;
|
||||||
|
|
||||||
|
state = gpio_port_read(BTN_PORT);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
40
buttons.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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/f2/gpio.h>
|
||||||
|
|
||||||
|
struct buttonState {
|
||||||
|
volatile bool YesUp;
|
||||||
|
volatile int YesDown;
|
||||||
|
volatile bool NoUp;
|
||||||
|
volatile int NoDown;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct buttonState button;
|
||||||
|
|
||||||
|
void buttonUpdate(void);
|
||||||
|
|
||||||
|
#define BTN_PORT GPIOC
|
||||||
|
#define BTN_PIN_YES GPIO2
|
||||||
|
#define BTN_PIN_NO GPIO5
|
||||||
|
|
||||||
|
#endif
|
17
demo/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
APPVER = 1.0.0
|
||||||
|
|
||||||
|
NAME = demo
|
||||||
|
|
||||||
|
OBJS += demo.o
|
||||||
|
|
||||||
|
OBJS += ../trezor-crypto/bignum.o
|
||||||
|
OBJS += ../trezor-crypto/bip32.o
|
||||||
|
OBJS += ../trezor-crypto/ecdsa.o
|
||||||
|
OBJS += ../trezor-crypto/hmac.o
|
||||||
|
OBJS += ../trezor-crypto/ripemd160.o
|
||||||
|
OBJS += ../trezor-crypto/secp256k1.o
|
||||||
|
OBJS += ../trezor-crypto/sha2.o
|
||||||
|
OBJS += ../trezor-crypto/bip39.o
|
||||||
|
OBJS += ../trezor-crypto/pbkdf2.o
|
||||||
|
|
||||||
|
include ../Makefile.include
|
285
demo/demo.c
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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/usb/usbd.h>
|
||||||
|
#include <libopencm3/usb/hid.h>
|
||||||
|
#include "bitmaps.h"
|
||||||
|
#include "buttons.h"
|
||||||
|
#include "layout.h"
|
||||||
|
#include "oled.h"
|
||||||
|
#include "setup.h"
|
||||||
|
//#include "util.h"
|
||||||
|
#include "hmac.h"
|
||||||
|
#include "pbkdf2.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 = 0x534c,
|
||||||
|
.idProduct = 0x0001,
|
||||||
|
.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 int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
|
||||||
|
void (**complete)(usbd_device *, struct usb_setup_data *))
|
||||||
|
{
|
||||||
|
(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)
|
||||||
|
{
|
||||||
|
setup();
|
||||||
|
oledInit();
|
||||||
|
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, frame % 4);
|
||||||
|
pbkdf2(pass, passlen, salt, saltlen, 100, seed, 64, NULL);
|
||||||
|
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;
|
||||||
|
}
|
48
firmware/Makefile
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
APPVER = 1.0.0
|
||||||
|
|
||||||
|
NAME = trezor
|
||||||
|
|
||||||
|
OBJS += ssp.o
|
||||||
|
OBJS += usb.o
|
||||||
|
OBJS += messages.o
|
||||||
|
OBJS += storage.o
|
||||||
|
OBJS += trezor.o
|
||||||
|
OBJS += pinmatrix.o
|
||||||
|
OBJS += fsm.o
|
||||||
|
OBJS += coins.o
|
||||||
|
OBJS += transaction.o
|
||||||
|
OBJS += protect.o
|
||||||
|
OBJS += layout2.o
|
||||||
|
OBJS += recovery.o
|
||||||
|
OBJS += reset.o
|
||||||
|
OBJS += signing.o
|
||||||
|
|
||||||
|
OBJS += debug.o
|
||||||
|
|
||||||
|
OBJS += ../trezor-crypto/bignum.o
|
||||||
|
OBJS += ../trezor-crypto/ecdsa.o
|
||||||
|
OBJS += ../trezor-crypto/secp256k1.o
|
||||||
|
OBJS += ../trezor-crypto/sha2.o
|
||||||
|
OBJS += ../trezor-crypto/hmac.o
|
||||||
|
OBJS += ../trezor-crypto/bip32.o
|
||||||
|
OBJS += ../trezor-crypto/ripemd160.o
|
||||||
|
OBJS += ../trezor-crypto/bip39.o
|
||||||
|
OBJS += ../trezor-crypto/pbkdf2.o
|
||||||
|
OBJS += ../trezor-crypto/aescrypt.o
|
||||||
|
OBJS += ../trezor-crypto/aeskey.o
|
||||||
|
OBJS += ../trezor-crypto/aestab.o
|
||||||
|
|
||||||
|
OBJS += protob/pb_decode.o
|
||||||
|
OBJS += protob/pb_encode.o
|
||||||
|
OBJS += protob/messages.pb.o
|
||||||
|
OBJS += protob/storage.pb.o
|
||||||
|
OBJS += protob/types.pb.o
|
||||||
|
|
||||||
|
include ../Makefile.include
|
||||||
|
|
||||||
|
# CFLAGS += -fstack-protector -fstack-protector-all
|
||||||
|
CFLAGS += -Iprotob -DPB_FIELD_16BIT=1
|
||||||
|
CFLAGS += -DDEBUG_LINK=0
|
||||||
|
CFLAGS += -DDEBUG_LOG=0
|
||||||
|
CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"'
|
||||||
|
CFLAGS += -DSCM_REVISION_LEN=20
|
61
firmware/coins.c
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "coins.h"
|
||||||
|
|
||||||
|
const CoinType coins[COINS_COUNT] = {
|
||||||
|
{true, "Bitcoin", true, "BTC", true, 0, true, 10000},
|
||||||
|
{true, "Testnet", true, "TEST", true, 111, true, 10000000},
|
||||||
|
{true, "Namecoin", true, "NMC", true, 52, true, 10000000},
|
||||||
|
{true, "Litecoin", true, "LTC", true, 48, true, 10000000},
|
||||||
|
};
|
||||||
|
|
||||||
|
const CoinType *coinByShortcut(const char *shortcut)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < COINS_COUNT; i++) {
|
||||||
|
if (strcmp(shortcut, coins[i].coin_shortcut) == 0) {
|
||||||
|
return &(coins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CoinType *coinByName(const char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < COINS_COUNT; i++) {
|
||||||
|
if (strcmp(name, coins[i].coin_name) == 0) {
|
||||||
|
return &(coins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CoinType *coinByAddressType(uint8_t address_type)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < COINS_COUNT; i++) {
|
||||||
|
if (address_type == coins[i].address_type) {
|
||||||
|
return &(coins[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
33
firmware/coins.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "types.pb.h"
|
||||||
|
|
||||||
|
#define COINS_COUNT 4
|
||||||
|
|
||||||
|
extern const CoinType coins[COINS_COUNT];
|
||||||
|
|
||||||
|
const CoinType *coinByShortcut(const char *shortcut);
|
||||||
|
const CoinType *coinByName(const char *name);
|
||||||
|
const CoinType *coinByAddressType(uint8_t address_type);
|
||||||
|
|
||||||
|
#endif
|
53
firmware/debug.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "trezor.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "oled.h"
|
||||||
|
|
||||||
|
#if DEBUG_LOG
|
||||||
|
|
||||||
|
void oledDebug(const char *line)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static const char *lines[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
static char id = 3;
|
||||||
|
for (i = 0; i < 7; i++) {
|
||||||
|
lines[i] = lines[i + 1];
|
||||||
|
}
|
||||||
|
lines[7] = line;
|
||||||
|
oledClear();
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (lines[i]) {
|
||||||
|
oledDrawChar(0, i * 8, '0' + (id + i) % 10);
|
||||||
|
oledDrawString(8, i * 8, lines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
oledRefresh();
|
||||||
|
id = (id + 1) % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void debugLog(int level, const char *bucket, const char *text)
|
||||||
|
{
|
||||||
|
(void)level;
|
||||||
|
(void)bucket;
|
||||||
|
oledDebug(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
35
firmware/debug.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "trezor.h"
|
||||||
|
|
||||||
|
#if DEBUG_LOG
|
||||||
|
|
||||||
|
void debugLog(int level, const char *bucket, const char *text);
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define debugLog(L, B, T) do{}while(0)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
672
firmware/fsm.c
Normal file
@ -0,0 +1,672 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "trezor.h"
|
||||||
|
#include "fsm.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "bip32.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "coins.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
#include "rng.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "oled.h"
|
||||||
|
#include "protect.h"
|
||||||
|
#include "pinmatrix.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "ecdsa.h"
|
||||||
|
#include "reset.h"
|
||||||
|
#include "recovery.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "usb.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "signing.h"
|
||||||
|
|
||||||
|
// message methods
|
||||||
|
|
||||||
|
static uint8_t msg_resp[MSG_OUT_SIZE];
|
||||||
|
|
||||||
|
#define RESP_INIT(TYPE) TYPE *resp = (TYPE *)msg_resp; memset(resp, 0, sizeof(TYPE));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_sendFailure(FailureType code, const char *text)
|
||||||
|
{
|
||||||
|
RESP_INIT(Failure);
|
||||||
|
resp->has_code = true;
|
||||||
|
resp->code = code;
|
||||||
|
if (text) {
|
||||||
|
resp->has_message = true;
|
||||||
|
strlcpy(resp->message, text, sizeof(resp->message));
|
||||||
|
}
|
||||||
|
msg_write(MessageType_MessageType_Failure, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
HDNode *fsm_getRootNode(void)
|
||||||
|
{
|
||||||
|
static HDNode node;
|
||||||
|
if (!storage_getRootNode(&node)) {
|
||||||
|
layoutHome();
|
||||||
|
fsm_sendFailure(FailureType_Failure_NotInitialized, "Device not initialized or passphrase request cancelled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return &node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_deriveKey(HDNode *node, uint32_t *address_n, size_t address_n_count)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (address_n_count > 3) {
|
||||||
|
layoutProgressSwipe("Preparing keys", 0, 0);
|
||||||
|
}
|
||||||
|
for (i = 0; i < address_n_count; i++) {
|
||||||
|
hdnode_private_ckd(node, address_n[i]);
|
||||||
|
if (address_n_count > 3) {
|
||||||
|
layoutProgress("Preparing keys", 1000 * i / address_n_count, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgInitialize(Initialize *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
recovery_abort();
|
||||||
|
signing_abort();
|
||||||
|
RESP_INIT(Features);
|
||||||
|
resp->has_vendor = true; strlcpy(resp->vendor, "bitcointrezor.com", 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, storage_uuid_str, sizeof(resp->device_id));
|
||||||
|
resp->has_pin_protection = true; resp->pin_protection = storage.has_pin;
|
||||||
|
resp->has_passphrase_protection = true; resp->passphrase_protection = storage.passphrase_protection;
|
||||||
|
#ifdef SCM_REVISION
|
||||||
|
resp->has_revision = true; memcpy(resp->revision.bytes, SCM_REVISION, sizeof(resp->revision)); resp->revision.size = SCM_REVISION_LEN;
|
||||||
|
#endif
|
||||||
|
resp->has_bootloader_hash = true; resp->bootloader_hash.size = memory_bootloader_hash(resp->bootloader_hash.bytes);
|
||||||
|
if (storage.has_language) {
|
||||||
|
resp->has_language = true;
|
||||||
|
strlcpy(resp->language, storage.language, sizeof(resp->language));
|
||||||
|
}
|
||||||
|
if (storage.has_label) {
|
||||||
|
resp->has_label = true;
|
||||||
|
strlcpy(resp->label, storage.label, sizeof(resp->label));
|
||||||
|
}
|
||||||
|
resp->coins_count = COINS_COUNT;
|
||||||
|
memcpy(resp->coins, coins, COINS_COUNT * sizeof(CoinType));
|
||||||
|
resp->has_initialized = true; resp->initialized = storage_isInitialized();
|
||||||
|
msg_write(MessageType_MessageType_Features, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgPing(Ping *msg)
|
||||||
|
{
|
||||||
|
RESP_INIT(Success);
|
||||||
|
|
||||||
|
if (msg->has_button_protection && msg->button_protection) {
|
||||||
|
layoutDialogSwipe(DIALOG_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, "Ping cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->has_pin_protection && msg->pin_protection) {
|
||||||
|
if (!protectPin(true)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->has_passphrase_protection && msg->passphrase_protection) {
|
||||||
|
if (!protectPassphrase()) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Ping cancelled");
|
||||||
|
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(ChangePin *msg)
|
||||||
|
{
|
||||||
|
bool removal = msg->has_remove && msg->remove;
|
||||||
|
if (removal) {
|
||||||
|
if (storage_hasPin()) {
|
||||||
|
layoutDialogSwipe(DIALOG_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 (storage_hasPin()) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change current PIN?", NULL, NULL, NULL, NULL);
|
||||||
|
} else {
|
||||||
|
layoutDialogSwipe(DIALOG_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, removal ? "PIN removal cancelled" : "PIN change cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!protectPin(false)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (removal) {
|
||||||
|
storage_setPin(0);
|
||||||
|
fsm_sendSuccess("PIN removed");
|
||||||
|
} else {
|
||||||
|
if (protectChangePin()) {
|
||||||
|
fsm_sendSuccess("PIN changed");
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "PIN change failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgWipeDevice(WipeDevice *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
layoutDialogSwipe(DIALOG_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, "Wipe cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
storage_reset();
|
||||||
|
storage_reset_uuid();
|
||||||
|
storage_commit();
|
||||||
|
// 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_msgFirmwareErase(FirmwareErase *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in bootloader mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgFirmwareUpload(FirmwareUpload *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in bootloader mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgGetEntropy(GetEntropy *msg)
|
||||||
|
{
|
||||||
|
layoutDialogSwipe(DIALOG_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, "Entropy cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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_msgGetPublicKey(GetPublicKey *msg)
|
||||||
|
{
|
||||||
|
RESP_INIT(PublicKey);
|
||||||
|
|
||||||
|
HDNode *node = fsm_getRootNode();
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
fsm_deriveKey(node, msg->address_n, msg->address_n_count);
|
||||||
|
|
||||||
|
resp->node.depth = node->depth;
|
||||||
|
resp->node.fingerprint = node->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);
|
||||||
|
|
||||||
|
msg_write(MessageType_MessageType_PublicKey, resp);
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgLoadDevice(LoadDevice *msg)
|
||||||
|
{
|
||||||
|
if (storage_isInitialized()) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Device is already initialized. Use Wipe first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage_loadDevice(msg);
|
||||||
|
storage_commit();
|
||||||
|
fsm_sendSuccess("Device loaded");
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgResetDevice(ResetDevice *msg)
|
||||||
|
{
|
||||||
|
if (storage_isInitialized()) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Device is already initialized. Use Wipe first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgSignTx(SignTx *msg)
|
||||||
|
{
|
||||||
|
if (msg->inputs_count < 1) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Transaction must have at least one input");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->outputs_count < 1) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Transaction must have at least one output");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!protectPin(true)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HDNode *node = fsm_getRootNode();
|
||||||
|
if (!node) return;
|
||||||
|
const CoinType *coin = coinByName(msg->coin_name);
|
||||||
|
if (!coin) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Invalid coin name");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
signing_init(msg->inputs_count, msg->outputs_count, coin, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgSimpleSignTx(SimpleSignTx *msg)
|
||||||
|
{
|
||||||
|
RESP_INIT(TxRequest);
|
||||||
|
|
||||||
|
if (msg->inputs_count < 1) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Transaction must have at least one input");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->outputs_count < 1) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Transaction must have at least one output");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!protectPin(true)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HDNode *node = fsm_getRootNode();
|
||||||
|
if (!node) return;
|
||||||
|
const CoinType *coin = coinByName(msg->coin_name);
|
||||||
|
if (!coin) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Invalid coin name");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t version = 1;
|
||||||
|
uint32_t lock_time = 0;
|
||||||
|
int tx_size = transactionSimpleSign(coin, node, msg->inputs, msg->inputs_count, msg->outputs, msg->outputs_count, version, lock_time, resp->serialized.serialized_tx.bytes);
|
||||||
|
if (tx_size < 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Signing cancelled by user");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tx_size == 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Error signing transaction");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
// determine change address
|
||||||
|
uint64_t change_spend = 0;
|
||||||
|
for (i = 0; i < msg->outputs_count; i++) {
|
||||||
|
if (msg->outputs[i].address_n_count > 0) { // address_n set -> change address
|
||||||
|
if (change_spend == 0) { // not set
|
||||||
|
change_spend = msg->outputs[i].amount;
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Only one change output allowed");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check origin transactions
|
||||||
|
uint8_t prev_hashes[ pb_arraysize(SimpleSignTx, transactions) ][32];
|
||||||
|
for (i = 0; i < msg->transactions_count; i++) {
|
||||||
|
if (!transactionHash(&(msg->transactions[i]), prev_hashes[i])) {
|
||||||
|
memset(prev_hashes[i], 0, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate spendings
|
||||||
|
uint64_t to_spend = 0;
|
||||||
|
bool found;
|
||||||
|
for (i = 0; i < msg->inputs_count; i++) {
|
||||||
|
found = false;
|
||||||
|
for (j = 0; j < msg->transactions_count; j++) {
|
||||||
|
if (memcmp(msg->inputs[i].prev_hash.bytes, prev_hashes[j], 32) == 0) { // found prev TX
|
||||||
|
if (msg->inputs[i].prev_index < msg->transactions[j].bin_outputs_count) {
|
||||||
|
to_spend += msg->transactions[j].bin_outputs[msg->inputs[i].prev_index].amount;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Invalid prevhash");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t spending = 0;
|
||||||
|
for (i = 0; i < msg->outputs_count; i++) {
|
||||||
|
spending += msg->outputs[i].amount;
|
||||||
|
}
|
||||||
|
if (spending > to_spend) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t fee = to_spend - spending;
|
||||||
|
if (fee > (((uint64_t)tx_size + 999) / 1000) * coin->maxfee_kb) {
|
||||||
|
layoutFeeOverThreshold(coin, fee, ((uint64_t)tx_size + 999) / 1000);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Fee over threshold. Signing cancelled.");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// last confirmation
|
||||||
|
layoutConfirmTx(coin, to_spend - change_spend - fee, fee);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user");
|
||||||
|
} else {
|
||||||
|
resp->has_request_type = true;
|
||||||
|
resp->request_type = RequestType_TXFINISHED;
|
||||||
|
resp->has_serialized = true;
|
||||||
|
resp->serialized.has_serialized_tx = true;
|
||||||
|
resp->serialized.serialized_tx.size = (uint32_t)tx_size;
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgCancel(Cancel *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
recovery_abort();
|
||||||
|
signing_abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgTxAck(TxAck *msg)
|
||||||
|
{
|
||||||
|
if (msg->has_tx) {
|
||||||
|
signing_txack(&(msg->tx));
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "No transaction provided");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgApplySettings(ApplySettings *msg)
|
||||||
|
{
|
||||||
|
if (msg->has_label && msg->has_language) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "and language to", msg->language, "?");
|
||||||
|
} else
|
||||||
|
if (msg->has_label) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change label to", msg->label, "?", NULL, NULL);
|
||||||
|
} else
|
||||||
|
if (msg->has_language) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL, "Do you really want to", "change language to", msg->language, "?", NULL, NULL);
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "No setting provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Apply settings cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!protectPin(true)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (msg->has_label) {
|
||||||
|
storage_setLabel(msg->label);
|
||||||
|
}
|
||||||
|
if (msg->has_language) {
|
||||||
|
storage_setLanguage(msg->language);
|
||||||
|
}
|
||||||
|
storage_commit();
|
||||||
|
fsm_sendSuccess("Settings applied");
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgGetAddress(GetAddress *msg)
|
||||||
|
{
|
||||||
|
RESP_INIT(Address);
|
||||||
|
|
||||||
|
HDNode *node = fsm_getRootNode();
|
||||||
|
if (!node) return;
|
||||||
|
const CoinType *coin = coinByName(msg->coin_name);
|
||||||
|
if (!coin) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Invalid coin name");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsm_deriveKey(node, msg->address_n, msg->address_n_count);
|
||||||
|
|
||||||
|
ecdsa_get_address(node->public_key, coin->address_type, resp->address);
|
||||||
|
|
||||||
|
msg_write(MessageType_MessageType_Address, resp);
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgEntropyAck(EntropyAck *msg)
|
||||||
|
{
|
||||||
|
if (msg->has_entropy) {
|
||||||
|
reset_entropy(msg->entropy.bytes, msg->entropy.size);
|
||||||
|
} else {
|
||||||
|
reset_entropy(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgSignMessage(SignMessage *msg)
|
||||||
|
{
|
||||||
|
RESP_INIT(MessageSignature);
|
||||||
|
|
||||||
|
layoutSignMessage(msg->message.bytes, msg->message.size);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_ProtectCall, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Sign message cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!protectPin(true)) {
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HDNode *node = fsm_getRootNode();
|
||||||
|
if (!node) return;
|
||||||
|
const CoinType *coin = coinByName(msg->coin_name);
|
||||||
|
if (!coin) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Invalid coin name");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsm_deriveKey(node, msg->address_n, msg->address_n_count);
|
||||||
|
|
||||||
|
ecdsa_get_address(node->public_key, coin->address_type, resp->address);
|
||||||
|
if (transactionMessageSign(msg->message.bytes, msg->message.size, node->private_key, resp->address, resp->signature.bytes)) {
|
||||||
|
resp->has_address = true;
|
||||||
|
resp->has_signature = true;
|
||||||
|
resp->signature.size = 65;
|
||||||
|
msg_write(MessageType_MessageType_MessageSignature, resp);
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Error signing message");
|
||||||
|
}
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgVerifyMessage(VerifyMessage *msg)
|
||||||
|
{
|
||||||
|
const char *address = msg->has_address ? msg->address : 0;
|
||||||
|
if (msg->signature.size == 65 && transactionMessageVerify(msg->message.bytes, msg->message.size, msg->signature.bytes, address)) {
|
||||||
|
// TODO: show verified message & wait for button
|
||||||
|
// layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "OK", NULL, "Verified message", NULL, NULL, NULL, NULL, NULL);
|
||||||
|
// protectButton(ButtonRequestType_ButtonRequest_Other, true);
|
||||||
|
fsm_sendSuccess("Message verified");
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_InvalidSignature, "Invalid signature");
|
||||||
|
}
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgEstimateTxSize(EstimateTxSize *msg)
|
||||||
|
{
|
||||||
|
RESP_INIT(TxSize);
|
||||||
|
resp->has_tx_size = true;
|
||||||
|
resp->tx_size = transactionEstimateSize(msg->inputs_count, msg->outputs_count);
|
||||||
|
msg_write(MessageType_MessageType_TxSize, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgRecoveryDevice(RecoveryDevice *msg)
|
||||||
|
{
|
||||||
|
if (storage_isInitialized()) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Device is already initialized. Use Wipe first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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 : false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgWordAck(WordAck *msg)
|
||||||
|
{
|
||||||
|
recovery_word(msg->word);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG_LINK
|
||||||
|
|
||||||
|
void fsm_msgDebugLinkGetState(DebugLinkGetState *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
RESP_INIT(DebugLinkState);
|
||||||
|
|
||||||
|
// resp->has_layout = true;
|
||||||
|
// resp->layout.size = OLED_BUFSIZE;
|
||||||
|
// memcpy(resp->layout.bytes, oledGetBuffer(), OLED_BUFSIZE);
|
||||||
|
|
||||||
|
if (storage.has_pin) {
|
||||||
|
resp->has_pin = true;
|
||||||
|
strlcpy(resp->pin, storage.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();
|
||||||
|
|
||||||
|
if (storage.has_mnemonic) {
|
||||||
|
resp->has_mnemonic = true;
|
||||||
|
strlcpy(resp->mnemonic, storage.mnemonic, sizeof(resp->mnemonic));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (storage.has_node) {
|
||||||
|
resp->has_node = true;
|
||||||
|
memcpy(&(resp->node), &(storage.node), sizeof(HDNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
resp->has_passphrase_protection = true;
|
||||||
|
resp->passphrase_protection = storage.has_passphrase_protection && storage.passphrase_protection;
|
||||||
|
|
||||||
|
msg_debug_write(MessageType_MessageType_DebugLinkState, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fsm_msgDebugLinkStop(DebugLinkStop *msg)
|
||||||
|
{
|
||||||
|
(void)msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
63
firmware/fsm.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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.pb.h"
|
||||||
|
|
||||||
|
// message functions
|
||||||
|
|
||||||
|
void fsm_sendSuccess(const char *text);
|
||||||
|
void fsm_sendFailure(FailureType code, const char *text);
|
||||||
|
|
||||||
|
void fsm_msgInitialize(Initialize *msg);
|
||||||
|
void fsm_msgPing(Ping *msg);
|
||||||
|
void fsm_msgChangePin(ChangePin *msg);
|
||||||
|
void fsm_msgWipeDevice(WipeDevice *msg);
|
||||||
|
void fsm_msgFirmwareErase(FirmwareErase *msg);
|
||||||
|
void fsm_msgFirmwareUpload(FirmwareUpload *msg);
|
||||||
|
void fsm_msgGetEntropy(GetEntropy *msg);
|
||||||
|
void fsm_msgGetPublicKey(GetPublicKey *msg);
|
||||||
|
void fsm_msgLoadDevice(LoadDevice *msg);
|
||||||
|
void fsm_msgResetDevice(ResetDevice *msg);
|
||||||
|
void fsm_msgSignTx(SignTx *msg);
|
||||||
|
void fsm_msgSimpleSignTx(SimpleSignTx *msg);
|
||||||
|
//void fsm_msgPinMatrixAck(PinMatrixAck *msg);
|
||||||
|
void fsm_msgCancel(Cancel *msg);
|
||||||
|
void fsm_msgTxAck(TxAck *msg);
|
||||||
|
void fsm_msgApplySettings(ApplySettings *msg);
|
||||||
|
//void fsm_msgButtonAck(ButtonAck *msg);
|
||||||
|
void fsm_msgGetAddress(GetAddress *msg);
|
||||||
|
void fsm_msgEntropyAck(EntropyAck *msg);
|
||||||
|
void fsm_msgSignMessage(SignMessage *msg);
|
||||||
|
void fsm_msgVerifyMessage(VerifyMessage *msg);
|
||||||
|
//void fsm_msgPassphraseAck(PassphraseAck *msg);
|
||||||
|
void fsm_msgEstimateTxSize(EstimateTxSize *msg);
|
||||||
|
void fsm_msgRecoveryDevice(RecoveryDevice *msg);
|
||||||
|
void fsm_msgWordAck(WordAck *msg);
|
||||||
|
|
||||||
|
// debug message functions
|
||||||
|
#if DEBUG_LINK
|
||||||
|
//void fsm_msgDebugLinkDecision(DebugLinkDecision *msg);
|
||||||
|
void fsm_msgDebugLinkGetState(DebugLinkGetState *msg);
|
||||||
|
void fsm_msgDebugLinkStop(DebugLinkStop *msg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
191
firmware/layout2.c
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "oled.h"
|
||||||
|
#include "bitmaps.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
void *layoutLast = layoutHome;
|
||||||
|
|
||||||
|
void layoutDialogSwipe(LayoutDialogIcon 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;
|
||||||
|
oledSwipeLeft();
|
||||||
|
layoutDialog(icon, btnNo, btnYes, desc, line1, line2, line3, line4, line5, line6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutProgressSwipe(const char *desc, int permil, int gearstep)
|
||||||
|
{
|
||||||
|
if (layoutLast == layoutProgressSwipe) {
|
||||||
|
oledClear();
|
||||||
|
} else {
|
||||||
|
layoutLast = layoutProgressSwipe;
|
||||||
|
oledSwipeLeft();
|
||||||
|
}
|
||||||
|
layoutProgress(desc, permil, gearstep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutHome(void)
|
||||||
|
{
|
||||||
|
if (layoutLast == layoutHome) {
|
||||||
|
oledClear();
|
||||||
|
} else {
|
||||||
|
layoutLast = layoutHome;
|
||||||
|
oledSwipeLeft();
|
||||||
|
}
|
||||||
|
const char *label = storage_getLabel();
|
||||||
|
if (label && strlen(label) > 0) {
|
||||||
|
oledDrawBitmap(44, 4, &bmp_logo48);
|
||||||
|
oledDrawStringCenter(OLED_HEIGHT - 8, label);
|
||||||
|
} else {
|
||||||
|
oledDrawBitmap(40, 0, &bmp_logo64);
|
||||||
|
}
|
||||||
|
oledRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *str_amount(uint64_t amnt, const char *abbr, char *buf, int len)
|
||||||
|
{
|
||||||
|
memset(buf, 0, len);
|
||||||
|
uint64_t a = amnt, b = 1;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
buf[16 - i] = '0' + (a / b) % 10;
|
||||||
|
b *= 10;
|
||||||
|
}
|
||||||
|
buf[8] = '.';
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
buf[7 - i] = '0' + (a / b) % 10;
|
||||||
|
b *= 10;
|
||||||
|
}
|
||||||
|
i = 17;
|
||||||
|
while (i > 10 && buf[i - 1] == '0') { // drop trailing zeroes
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
if (abbr) {
|
||||||
|
buf[i] = ' ';
|
||||||
|
strlcpy(buf + i + 1, abbr, len - i - 1);
|
||||||
|
} else {
|
||||||
|
buf[i] = 0;
|
||||||
|
}
|
||||||
|
const char *r = buf;
|
||||||
|
while (*r == '0' && *(r + 1) != '.') r++; // drop leading zeroes
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char buf_out[32], buf_fee[32];
|
||||||
|
|
||||||
|
void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out)
|
||||||
|
{
|
||||||
|
static char first_half[17 + 1];
|
||||||
|
strlcpy(first_half, out->address, sizeof(first_half));
|
||||||
|
const char *str_out = str_amount(out->amount, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_out, sizeof(buf_out));
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION,
|
||||||
|
"Cancel",
|
||||||
|
"Confirm",
|
||||||
|
NULL,
|
||||||
|
"Confirm sending",
|
||||||
|
str_out,
|
||||||
|
"to",
|
||||||
|
first_half,
|
||||||
|
out->address + 17,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_fee)
|
||||||
|
{
|
||||||
|
const char *str_out = str_amount(amount_out, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_out, sizeof(buf_out));
|
||||||
|
const char *str_fee = str_amount(amount_fee, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_fee, sizeof(buf_fee));
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION,
|
||||||
|
"Cancel",
|
||||||
|
"Confirm",
|
||||||
|
NULL,
|
||||||
|
"Really send",
|
||||||
|
str_out,
|
||||||
|
"from your wallet?",
|
||||||
|
"Fee will be",
|
||||||
|
str_fee,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutFeeOverThreshold(const CoinType *coin, uint64_t fee, uint32_t kb)
|
||||||
|
{
|
||||||
|
(void)kb;
|
||||||
|
const char *str_out = str_amount(fee, coin->has_coin_shortcut ? coin->coin_shortcut : NULL, buf_out, sizeof(buf_out));
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION,
|
||||||
|
"Cancel",
|
||||||
|
"Confirm",
|
||||||
|
NULL,
|
||||||
|
"Fee",
|
||||||
|
str_out,
|
||||||
|
"is unexpectedly high.",
|
||||||
|
NULL,
|
||||||
|
"Send anyway?",
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutSignMessage(const uint8_t *msg, uint32_t len)
|
||||||
|
{
|
||||||
|
bool ascii = true;
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (msg[i] < 0x20 || msg[i] >= 0x80) {
|
||||||
|
ascii = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char str[4][17];
|
||||||
|
memset(str, 0, sizeof(str));
|
||||||
|
if (ascii) {
|
||||||
|
strlcpy(str[0], (char *)msg, 17);
|
||||||
|
if (len > 16) {
|
||||||
|
strlcpy(str[1], (char *)msg + 16, 17);
|
||||||
|
}
|
||||||
|
if (len > 32) {
|
||||||
|
strlcpy(str[2], (char *)msg + 32, 17);
|
||||||
|
}
|
||||||
|
if (len > 48) {
|
||||||
|
strlcpy(str[3], (char *)msg + 48, 17);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data2hex(msg, len > 8 ? 8 : len, str[0]);
|
||||||
|
if (len > 8) {
|
||||||
|
data2hex(msg + 8, len > 16 ? 8 : len - 8, str[1]);
|
||||||
|
}
|
||||||
|
if (len > 16) {
|
||||||
|
data2hex(msg + 16, len > 24 ? 8 : len - 16, str[2]);
|
||||||
|
}
|
||||||
|
if (len > 24) {
|
||||||
|
data2hex(msg + 24, len > 32 ? 8 : len - 24, str[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_QUESTION, "Cancel", "Confirm", NULL,
|
||||||
|
ascii ? "Sign text message?" : "Sign binary message?",
|
||||||
|
str[0], str[1], str[2], str[3], NULL);
|
||||||
|
}
|
35
firmware/layout2.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "layout.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
|
||||||
|
void layoutDialogSwipe(LayoutDialogIcon 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, int gearstep);
|
||||||
|
|
||||||
|
void layoutHome(void);
|
||||||
|
void layoutConfirmOutput(const CoinType *coin, const TxOutputType *out);
|
||||||
|
void layoutConfirmTx(const CoinType *coin, uint64_t amount_out, uint64_t amount_fee);
|
||||||
|
void layoutFeeOverThreshold(const CoinType *coin, uint64_t fee, uint32_t kb);
|
||||||
|
void layoutSignMessage(const uint8_t *msg, uint32_t len);
|
||||||
|
|
||||||
|
#endif
|
414
firmware/messages.c
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "trezor.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "fsm.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "pb_decode.h"
|
||||||
|
#include "pb_encode.h"
|
||||||
|
#include "messages.pb.h"
|
||||||
|
|
||||||
|
// SimpleSignTx_size is the largest message we operate with
|
||||||
|
#if MSG_IN_SIZE < SimpleSignTx_size
|
||||||
|
#error "MSG_IN_SIZE is too small!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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)(void *ptr);
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct MessagesMap_t MessagesMap[] = {
|
||||||
|
// in messages
|
||||||
|
{'n', 'i', MessageType_MessageType_Initialize, Initialize_fields, (void (*)(void *))fsm_msgInitialize},
|
||||||
|
{'n', 'i', MessageType_MessageType_Ping, Ping_fields, (void (*)(void *))fsm_msgPing},
|
||||||
|
{'n', 'i', MessageType_MessageType_ChangePin, ChangePin_fields, (void (*)(void *))fsm_msgChangePin},
|
||||||
|
{'n', 'i', MessageType_MessageType_WipeDevice, WipeDevice_fields, (void (*)(void *))fsm_msgWipeDevice},
|
||||||
|
{'n', 'i', MessageType_MessageType_FirmwareErase, FirmwareErase_fields, (void (*)(void *))fsm_msgFirmwareErase},
|
||||||
|
{'n', 'i', MessageType_MessageType_FirmwareUpload, FirmwareUpload_fields, (void (*)(void *))fsm_msgFirmwareUpload},
|
||||||
|
{'n', 'i', MessageType_MessageType_GetEntropy, GetEntropy_fields, (void (*)(void *))fsm_msgGetEntropy},
|
||||||
|
{'n', 'i', MessageType_MessageType_GetPublicKey, GetPublicKey_fields, (void (*)(void *))fsm_msgGetPublicKey},
|
||||||
|
{'n', 'i', MessageType_MessageType_LoadDevice, LoadDevice_fields, (void (*)(void *))fsm_msgLoadDevice},
|
||||||
|
{'n', 'i', MessageType_MessageType_ResetDevice, ResetDevice_fields, (void (*)(void *))fsm_msgResetDevice},
|
||||||
|
{'n', 'i', MessageType_MessageType_SignTx, SignTx_fields, (void (*)(void *))fsm_msgSignTx},
|
||||||
|
{'n', 'i', MessageType_MessageType_SimpleSignTx, SimpleSignTx_fields, (void (*)(void *))fsm_msgSimpleSignTx},
|
||||||
|
// {'n', 'i', MessageType_MessageType_PinMatrixAck, PinMatrixAck_fields, (void (*)(void *))fsm_msgPinMatrixAck},
|
||||||
|
{'n', 'i', MessageType_MessageType_Cancel, Cancel_fields, (void (*)(void *))fsm_msgCancel},
|
||||||
|
{'n', 'i', MessageType_MessageType_TxAck, TxAck_fields, (void (*)(void *))fsm_msgTxAck},
|
||||||
|
{'n', 'i', MessageType_MessageType_ApplySettings, ApplySettings_fields, (void (*)(void *))fsm_msgApplySettings},
|
||||||
|
// {'n', 'i', MessageType_MessageType_ButtonAck, ButtonAck_fields, (void (*)(void *))fsm_msgButtonAck},
|
||||||
|
{'n', 'i', MessageType_MessageType_GetAddress, GetAddress_fields, (void (*)(void *))fsm_msgGetAddress},
|
||||||
|
{'n', 'i', MessageType_MessageType_EntropyAck, EntropyAck_fields, (void (*)(void *))fsm_msgEntropyAck},
|
||||||
|
{'n', 'i', MessageType_MessageType_SignMessage, SignMessage_fields, (void (*)(void *))fsm_msgSignMessage},
|
||||||
|
{'n', 'i', MessageType_MessageType_VerifyMessage, VerifyMessage_fields, (void (*)(void *))fsm_msgVerifyMessage},
|
||||||
|
// {'n', 'i', MessageType_MessageType_PassphraseAck, PassphraseAck_fields, (void (*)(void *))fsm_msgPassphraseAck},
|
||||||
|
{'n', 'i', MessageType_MessageType_EstimateTxSize, EstimateTxSize_fields, (void (*)(void *))fsm_msgEstimateTxSize},
|
||||||
|
{'n', 'i', MessageType_MessageType_RecoveryDevice, RecoveryDevice_fields, (void (*)(void *))fsm_msgRecoveryDevice},
|
||||||
|
{'n', 'i', MessageType_MessageType_WordAck, WordAck_fields, (void (*)(void *))fsm_msgWordAck},
|
||||||
|
// out messages
|
||||||
|
{'n', 'o', MessageType_MessageType_Success, Success_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_Failure, Failure_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_Entropy, Entropy_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_PublicKey, PublicKey_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_Features, Features_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_PinMatrixRequest, PinMatrixRequest_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_TxRequest, TxRequest_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_ButtonRequest, ButtonRequest_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_Address, Address_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_EntropyRequest, EntropyRequest_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_MessageSignature, MessageSignature_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_PassphraseRequest, PassphraseRequest_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_TxSize, TxSize_fields, 0},
|
||||||
|
{'n', 'o', MessageType_MessageType_WordRequest, WordRequest_fields, 0},
|
||||||
|
#if DEBUG_LINK
|
||||||
|
// debug in messages
|
||||||
|
// {'d', 'i', MessageType_MessageType_DebugLinkDecision, DebugLinkDecision_fields, (void (*)(void *))fsm_msgDebugLinkDecision},
|
||||||
|
{'d', 'i', MessageType_MessageType_DebugLinkGetState, DebugLinkGetState_fields, (void (*)(void *))fsm_msgDebugLinkGetState},
|
||||||
|
{'d', 'i', MessageType_MessageType_DebugLinkStop, DebugLinkStop_fields, (void (*)(void *))fsm_msgDebugLinkStop},
|
||||||
|
// debug out messages
|
||||||
|
{'d', 'o', MessageType_MessageType_DebugLinkState, DebugLinkState_fields, 0},
|
||||||
|
{'d', 'o', MessageType_MessageType_DebugLinkLog, DebugLinkLog_fields, 0},
|
||||||
|
#endif
|
||||||
|
// end
|
||||||
|
{0, 0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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;
|
||||||
|
size_t i;
|
||||||
|
for (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;
|
||||||
|
size_t i;
|
||||||
|
for (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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb_ostream_t sizestream = {0, 0, SIZE_MAX, 0, 0};
|
||||||
|
bool status = pb_encode(&sizestream, fields, msg_ptr);
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t len = sizestream.bytes_written;
|
||||||
|
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};
|
||||||
|
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];
|
||||||
|
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_SyntaxError, stream.errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void msg_read_common(char type, uint8_t *buf, int 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 (read_state == READSTATE_IDLE) {
|
||||||
|
if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') { // invalid start - discard
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg_id = (buf[3] << 8) + buf[4];
|
||||||
|
msg_size = (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_SyntaxError, "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;
|
||||||
|
}
|
||||||
|
memcpy(msg_in + msg_pos, buf + 1, len - 1);
|
||||||
|
msg_pos += len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg_pos >= msg_size) {
|
||||||
|
msg_process(type, msg_id, fields, msg_in, msg_size);
|
||||||
|
msg_pos = 0;
|
||||||
|
read_state = READSTATE_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
uint8_t msg_tiny[64];
|
||||||
|
uint16_t msg_tiny_id = 0xFFFF;
|
||||||
|
|
||||||
|
void msg_read_tiny(uint8_t *buf, int len)
|
||||||
|
{
|
||||||
|
if (len < 9) return;
|
||||||
|
if (buf[0] != '?' || buf[1] != '#' || buf[2] != '#') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t msg_id = (buf[3] << 8) + buf[4];
|
||||||
|
uint32_t msg_size = (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, len - 9);
|
||||||
|
|
||||||
|
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_SyntaxError, stream.errmsg);
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Unknown message");
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
}
|
||||||
|
}
|
53
firmware/messages.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "trezor.h"
|
||||||
|
|
||||||
|
#define MSG_IN_SIZE (24*1024)
|
||||||
|
|
||||||
|
#define MSG_OUT_SIZE (9*1024)
|
||||||
|
|
||||||
|
#define msg_read(buf, len) msg_read_common('n', (buf), (len))
|
||||||
|
#define msg_write(id, ptr) msg_write_common('n', (id), (ptr))
|
||||||
|
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))
|
||||||
|
uint8_t *msg_debug_out_data(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void msg_read_common(char type, uint8_t *buf, int len);
|
||||||
|
bool msg_write_common(char type, uint16_t msg_id, const void *msg_ptr);
|
||||||
|
|
||||||
|
void msg_read_tiny(uint8_t *buf, int len);
|
||||||
|
void msg_debug_read_tiny(uint8_t *buf, int len);
|
||||||
|
extern uint8_t msg_tiny[64];
|
||||||
|
extern uint16_t msg_tiny_id;
|
||||||
|
|
||||||
|
#endif
|
87
firmware/pinmatrix.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "pinmatrix.h"
|
||||||
|
#include "oled.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,
|
||||||
|
};
|
||||||
|
oledSwipeLeft();
|
||||||
|
const int w = bmp_digit0.width, h = bmp_digit0.height, pad = 2;
|
||||||
|
int i, j, k;
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
for (j = 0; j < 3; j++) {
|
||||||
|
// use (2 - j) instead of j to achieve 789456123 layout
|
||||||
|
k = pinmatrix_perm[i + (2 - j) * 3] - '0';
|
||||||
|
if (text) {
|
||||||
|
oledDrawStringCenter(0, text);
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
char t;
|
||||||
|
|
||||||
|
for (i = 0; i < 9; i++) {
|
||||||
|
pinmatrix_perm[i] = '1' + i;
|
||||||
|
}
|
||||||
|
pinmatrix_perm[9] = 0;
|
||||||
|
for (i = 0; i < 10000; i++) {
|
||||||
|
j = random32() % 9;
|
||||||
|
k = random32() % 9;
|
||||||
|
t = pinmatrix_perm[j];
|
||||||
|
pinmatrix_perm[j] = pinmatrix_perm[k];
|
||||||
|
pinmatrix_perm[k] = t;
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *pinmatrix_get(void)
|
||||||
|
{
|
||||||
|
return pinmatrix_perm;
|
||||||
|
}
|
27
firmware/pinmatrix.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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
|
222
firmware/protect.c
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "storage.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "usb.h"
|
||||||
|
#include "oled.h"
|
||||||
|
#include "buttons.h"
|
||||||
|
#include "pinmatrix.h"
|
||||||
|
#include "fsm.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
bool protectButton(ButtonRequestType type, bool confirm_only)
|
||||||
|
{
|
||||||
|
ButtonRequest resp;
|
||||||
|
bool result;
|
||||||
|
bool acked = false;
|
||||||
|
|
||||||
|
memset(&resp, 0, sizeof(ButtonRequest));
|
||||||
|
resp.has_code = true;
|
||||||
|
resp.code = type;
|
||||||
|
usbTiny(1);
|
||||||
|
msg_write(MessageType_MessageType_ButtonRequest, &resp);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
usbPoll();
|
||||||
|
|
||||||
|
// wait for ButtonAck
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_ButtonAck) {
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
acked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// button acked - check buttons
|
||||||
|
if (acked) {
|
||||||
|
delay(50000);
|
||||||
|
buttonUpdate();
|
||||||
|
if (button.YesUp) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!confirm_only && button.NoUp) {
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_Cancel || msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||||
|
fsm_msgInitialize((Initialize *)msg_tiny);
|
||||||
|
}
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check debug link
|
||||||
|
#if DEBUG_LINK
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_DebugLinkDecision) {
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
DebugLinkDecision *dld = (DebugLinkDecision *)msg_tiny;
|
||||||
|
result = dld->yes_no;
|
||||||
|
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;
|
||||||
|
memset(&resp, 0, 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;
|
||||||
|
}
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_Cancel || msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||||
|
pinmatrix_done(0);
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||||
|
fsm_msgInitialize((Initialize *)msg_tiny);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool protectPin(bool use_cached)
|
||||||
|
{
|
||||||
|
if (!storage.has_pin || strlen(storage.pin) == 0 || (use_cached && session_isPinCached())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const char *pin;
|
||||||
|
uint32_t wait = storage_getPinFails();
|
||||||
|
if (wait) {
|
||||||
|
if (wait > 4) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Wrong PIN entered", NULL, "Please wait ...", NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
wait = (wait < 32) ? (1u << wait) : 0xFFFFFFFF;
|
||||||
|
while (--wait > 0) {
|
||||||
|
delay(1000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_Current, "Please enter current PIN:");
|
||||||
|
if (!pin) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_PinCancelled, "PIN Cancelled");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (storage_isPinCorrect(pin)) {
|
||||||
|
session_cachePin(pin);
|
||||||
|
storage_resetPinFails();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
storage_increasePinFails();
|
||||||
|
fsm_sendFailure(FailureType_Failure_PinInvalid, "Invalid PIN");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool protectChangePin(void)
|
||||||
|
{
|
||||||
|
const char *pin;
|
||||||
|
char pin1[17], pin2[17];
|
||||||
|
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewFirst, "Please enter new PIN:");
|
||||||
|
if (!pin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
strlcpy(pin1, pin, sizeof(pin1));
|
||||||
|
pin = requestPin(PinMatrixRequestType_PinMatrixRequestType_NewSecond, "Please re-enter new PIN:");
|
||||||
|
if (!pin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
strlcpy(pin2, pin, sizeof(pin2));
|
||||||
|
if (strcmp(pin1, pin2) == 0) {
|
||||||
|
storage_setPin(pin1);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool protectPassphrase(void)
|
||||||
|
{
|
||||||
|
if (!storage.has_passphrase_protection || !storage.passphrase_protection || session_isPassphraseCached()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassphraseRequest resp;
|
||||||
|
memset(&resp, 0, sizeof(PassphraseRequest));
|
||||||
|
usbTiny(1);
|
||||||
|
msg_write(MessageType_MessageType_PassphraseRequest, &resp);
|
||||||
|
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Please enter your", "passphrase using", "the computer's", "keyboard.", NULL, NULL);
|
||||||
|
|
||||||
|
bool result;
|
||||||
|
for (;;) {
|
||||||
|
usbPoll();
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_PassphraseAck) {
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
PassphraseAck *ppa = (PassphraseAck *)msg_tiny;
|
||||||
|
session_cachePassphrase(ppa->passphrase);
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_Cancel || msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||||
|
if (msg_tiny_id == MessageType_MessageType_Initialize) {
|
||||||
|
fsm_msgInitialize((Initialize *)msg_tiny);
|
||||||
|
}
|
||||||
|
msg_tiny_id = 0xFFFF;
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usbTiny(0);
|
||||||
|
layoutHome();
|
||||||
|
return result;
|
||||||
|
}
|
31
firmware/protect.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "types.pb.h"
|
||||||
|
|
||||||
|
bool protectButton(ButtonRequestType type, bool confirm_only);
|
||||||
|
bool protectPin(bool use_cached);
|
||||||
|
bool protectChangePin(void);
|
||||||
|
bool protectPassphrase(void);
|
||||||
|
|
||||||
|
#endif
|
1
firmware/protob/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.pb
|
10
firmware/protob/Makefile
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
all: messages.pb.c storage.pb.c types.pb.c
|
||||||
|
|
||||||
|
%.pb.c: %.pb %.options
|
||||||
|
nanopb $< -L '#include "%s"' -T
|
||||||
|
|
||||||
|
%.pb: %.proto
|
||||||
|
protoc -I/usr/include -I. $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.pb *.o *.pb.c *.pb.h
|
79
firmware/protob/messages.options
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
Features.vendor max_size:33
|
||||||
|
Features.device_id max_size:25
|
||||||
|
Features.language max_size:17
|
||||||
|
Features.label max_size:33
|
||||||
|
Features.coins max_count:4
|
||||||
|
Features.revision max_size:20
|
||||||
|
Features.bootloader_hash max_size:32
|
||||||
|
|
||||||
|
ApplySettings.language max_size:17
|
||||||
|
ApplySettings.label max_size:33
|
||||||
|
|
||||||
|
Ping.message max_size:256
|
||||||
|
|
||||||
|
Success.message max_size:256
|
||||||
|
|
||||||
|
Failure.message max_size:256
|
||||||
|
|
||||||
|
ButtonRequest.data max_size:256
|
||||||
|
|
||||||
|
PinMatrixAck.pin max_size:10
|
||||||
|
|
||||||
|
PassphraseAck.passphrase max_size:51
|
||||||
|
|
||||||
|
Entropy.entropy max_size:1024
|
||||||
|
|
||||||
|
GetPublicKey.address_n max_count:8
|
||||||
|
|
||||||
|
GetAddress.address_n max_count:8
|
||||||
|
GetAddress.coin_name max_size:17
|
||||||
|
|
||||||
|
Address.address max_size:35
|
||||||
|
|
||||||
|
LoadDevice.mnemonic max_size:241
|
||||||
|
LoadDevice.pin max_size:10
|
||||||
|
LoadDevice.language max_size:17
|
||||||
|
LoadDevice.label max_size:33
|
||||||
|
|
||||||
|
ResetDevice.language max_size:17
|
||||||
|
ResetDevice.label max_size:33
|
||||||
|
|
||||||
|
EntropyAck.entropy max_size:128
|
||||||
|
|
||||||
|
RecoveryDevice.language max_size:17
|
||||||
|
RecoveryDevice.label max_size:33
|
||||||
|
|
||||||
|
WordAck.word max_size:12
|
||||||
|
|
||||||
|
SignMessage.address_n max_count:8
|
||||||
|
SignMessage.message max_size:256
|
||||||
|
SignMessage.coin_name max_size:17
|
||||||
|
|
||||||
|
VerifyMessage.address max_size:35
|
||||||
|
VerifyMessage.signature max_size:65
|
||||||
|
VerifyMessage.message max_size:256
|
||||||
|
|
||||||
|
MessageSignature.address max_size:35
|
||||||
|
MessageSignature.signature max_size:65
|
||||||
|
|
||||||
|
EstimateTxSize.coin_name max_size:17
|
||||||
|
|
||||||
|
SignTx.coin_name max_size:17
|
||||||
|
|
||||||
|
SimpleSignTx.inputs max_count:4
|
||||||
|
SimpleSignTx.outputs max_count:4
|
||||||
|
SimpleSignTx.transactions max_count:4
|
||||||
|
SimpleSignTx.coin_name max_size:17
|
||||||
|
|
||||||
|
FirmwareUpload.payload max_size:0 # not used in firmware
|
||||||
|
|
||||||
|
DebugLinkState.layout max_size:1024
|
||||||
|
DebugLinkState.pin max_size:10
|
||||||
|
DebugLinkState.matrix max_size:10
|
||||||
|
DebugLinkState.mnemonic max_size:241
|
||||||
|
DebugLinkState.reset_word max_size:12
|
||||||
|
DebugLinkState.reset_entropy max_size:128
|
||||||
|
DebugLinkState.recovery_fake_word max_size:12
|
||||||
|
|
||||||
|
DebugLinkLog.bucket max_size:33
|
||||||
|
DebugLinkLog.text max_size:256
|
305
firmware/protob/messages.pb.c
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
/* Automatically generated nanopb constant definitions */
|
||||||
|
/* Generated by nanopb-0.2.7 */
|
||||||
|
|
||||||
|
#include "messages.pb.h"
|
||||||
|
|
||||||
|
const char GetAddress_coin_name_default[17] = "Bitcoin";
|
||||||
|
const char LoadDevice_language_default[17] = "english";
|
||||||
|
const uint32_t ResetDevice_strength_default = 128u;
|
||||||
|
const char ResetDevice_language_default[17] = "english";
|
||||||
|
const char RecoveryDevice_language_default[17] = "english";
|
||||||
|
const char SignMessage_coin_name_default[17] = "Bitcoin";
|
||||||
|
const char EstimateTxSize_coin_name_default[17] = "Bitcoin";
|
||||||
|
const char SignTx_coin_name_default[17] = "Bitcoin";
|
||||||
|
const char SimpleSignTx_coin_name_default[17] = "Bitcoin";
|
||||||
|
|
||||||
|
|
||||||
|
const pb_field_t Initialize_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Features_fields[15] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, Features, vendor, vendor, 0),
|
||||||
|
PB_FIELD2( 2, UINT32 , OPTIONAL, STATIC , OTHER, Features, major_version, vendor, 0),
|
||||||
|
PB_FIELD2( 3, UINT32 , OPTIONAL, STATIC , OTHER, Features, minor_version, major_version, 0),
|
||||||
|
PB_FIELD2( 4, UINT32 , OPTIONAL, STATIC , OTHER, Features, patch_version, minor_version, 0),
|
||||||
|
PB_FIELD2( 5, BOOL , OPTIONAL, STATIC , OTHER, Features, bootloader_mode, patch_version, 0),
|
||||||
|
PB_FIELD2( 6, STRING , OPTIONAL, STATIC , OTHER, Features, device_id, bootloader_mode, 0),
|
||||||
|
PB_FIELD2( 7, BOOL , OPTIONAL, STATIC , OTHER, Features, pin_protection, device_id, 0),
|
||||||
|
PB_FIELD2( 8, BOOL , OPTIONAL, STATIC , OTHER, Features, passphrase_protection, pin_protection, 0),
|
||||||
|
PB_FIELD2( 9, STRING , OPTIONAL, STATIC , OTHER, Features, language, passphrase_protection, 0),
|
||||||
|
PB_FIELD2( 10, STRING , OPTIONAL, STATIC , OTHER, Features, label, language, 0),
|
||||||
|
PB_FIELD2( 11, MESSAGE , REPEATED, STATIC , OTHER, Features, coins, label, &CoinType_fields),
|
||||||
|
PB_FIELD2( 12, BOOL , OPTIONAL, STATIC , OTHER, Features, initialized, coins, 0),
|
||||||
|
PB_FIELD2( 13, BYTES , OPTIONAL, STATIC , OTHER, Features, revision, initialized, 0),
|
||||||
|
PB_FIELD2( 14, BYTES , OPTIONAL, STATIC , OTHER, Features, bootloader_hash, revision, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t ApplySettings_fields[3] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, ApplySettings, language, language, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, ApplySettings, label, language, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t ChangePin_fields[2] = {
|
||||||
|
PB_FIELD2( 1, BOOL , OPTIONAL, STATIC , FIRST, ChangePin, remove, remove, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Ping_fields[5] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, Ping, message, message, 0),
|
||||||
|
PB_FIELD2( 2, BOOL , OPTIONAL, STATIC , OTHER, Ping, button_protection, message, 0),
|
||||||
|
PB_FIELD2( 3, BOOL , OPTIONAL, STATIC , OTHER, Ping, pin_protection, button_protection, 0),
|
||||||
|
PB_FIELD2( 4, BOOL , OPTIONAL, STATIC , OTHER, Ping, passphrase_protection, pin_protection, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Success_fields[2] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, Success, message, message, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Failure_fields[3] = {
|
||||||
|
PB_FIELD2( 1, ENUM , OPTIONAL, STATIC , FIRST, Failure, code, code, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, Failure, message, code, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t ButtonRequest_fields[3] = {
|
||||||
|
PB_FIELD2( 1, ENUM , OPTIONAL, STATIC , FIRST, ButtonRequest, code, code, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, ButtonRequest, data, code, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t ButtonAck_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t PinMatrixRequest_fields[2] = {
|
||||||
|
PB_FIELD2( 1, ENUM , OPTIONAL, STATIC , FIRST, PinMatrixRequest, type, type, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t PinMatrixAck_fields[2] = {
|
||||||
|
PB_FIELD2( 1, STRING , REQUIRED, STATIC , FIRST, PinMatrixAck, pin, pin, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Cancel_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t PassphraseRequest_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t PassphraseAck_fields[2] = {
|
||||||
|
PB_FIELD2( 1, STRING , REQUIRED, STATIC , FIRST, PassphraseAck, passphrase, passphrase, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t GetEntropy_fields[2] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, GetEntropy, size, size, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Entropy_fields[2] = {
|
||||||
|
PB_FIELD2( 1, BYTES , REQUIRED, STATIC , FIRST, Entropy, entropy, entropy, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t GetPublicKey_fields[2] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REPEATED, STATIC , FIRST, GetPublicKey, address_n, address_n, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t PublicKey_fields[2] = {
|
||||||
|
PB_FIELD2( 1, MESSAGE , REQUIRED, STATIC , FIRST, PublicKey, node, node, &HDNodeType_fields),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t GetAddress_fields[3] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REPEATED, STATIC , FIRST, GetAddress, address_n, address_n, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, GetAddress, coin_name, address_n, &GetAddress_coin_name_default),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t Address_fields[2] = {
|
||||||
|
PB_FIELD2( 1, STRING , REQUIRED, STATIC , FIRST, Address, address, address, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t WipeDevice_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t LoadDevice_fields[8] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, LoadDevice, mnemonic, mnemonic, 0),
|
||||||
|
PB_FIELD2( 2, MESSAGE , OPTIONAL, STATIC , OTHER, LoadDevice, node, mnemonic, &HDNodeType_fields),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, LoadDevice, pin, node, 0),
|
||||||
|
PB_FIELD2( 4, BOOL , OPTIONAL, STATIC , OTHER, LoadDevice, passphrase_protection, pin, 0),
|
||||||
|
PB_FIELD2( 5, STRING , OPTIONAL, STATIC , OTHER, LoadDevice, language, passphrase_protection, &LoadDevice_language_default),
|
||||||
|
PB_FIELD2( 6, STRING , OPTIONAL, STATIC , OTHER, LoadDevice, label, language, 0),
|
||||||
|
PB_FIELD2( 7, BOOL , OPTIONAL, STATIC , OTHER, LoadDevice, skip_checksum, label, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t ResetDevice_fields[7] = {
|
||||||
|
PB_FIELD2( 1, BOOL , OPTIONAL, STATIC , FIRST, ResetDevice, display_random, display_random, 0),
|
||||||
|
PB_FIELD2( 2, UINT32 , OPTIONAL, STATIC , OTHER, ResetDevice, strength, display_random, &ResetDevice_strength_default),
|
||||||
|
PB_FIELD2( 3, BOOL , OPTIONAL, STATIC , OTHER, ResetDevice, passphrase_protection, strength, 0),
|
||||||
|
PB_FIELD2( 4, BOOL , OPTIONAL, STATIC , OTHER, ResetDevice, pin_protection, passphrase_protection, 0),
|
||||||
|
PB_FIELD2( 5, STRING , OPTIONAL, STATIC , OTHER, ResetDevice, language, pin_protection, &ResetDevice_language_default),
|
||||||
|
PB_FIELD2( 6, STRING , OPTIONAL, STATIC , OTHER, ResetDevice, label, language, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t EntropyRequest_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t EntropyAck_fields[2] = {
|
||||||
|
PB_FIELD2( 1, BYTES , OPTIONAL, STATIC , FIRST, EntropyAck, entropy, entropy, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t RecoveryDevice_fields[7] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, RecoveryDevice, word_count, word_count, 0),
|
||||||
|
PB_FIELD2( 2, BOOL , OPTIONAL, STATIC , OTHER, RecoveryDevice, passphrase_protection, word_count, 0),
|
||||||
|
PB_FIELD2( 3, BOOL , OPTIONAL, STATIC , OTHER, RecoveryDevice, pin_protection, passphrase_protection, 0),
|
||||||
|
PB_FIELD2( 4, STRING , OPTIONAL, STATIC , OTHER, RecoveryDevice, language, pin_protection, &RecoveryDevice_language_default),
|
||||||
|
PB_FIELD2( 5, STRING , OPTIONAL, STATIC , OTHER, RecoveryDevice, label, language, 0),
|
||||||
|
PB_FIELD2( 6, BOOL , OPTIONAL, STATIC , OTHER, RecoveryDevice, enforce_wordlist, label, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t WordRequest_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t WordAck_fields[2] = {
|
||||||
|
PB_FIELD2( 1, STRING , REQUIRED, STATIC , FIRST, WordAck, word, word, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t SignMessage_fields[4] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REPEATED, STATIC , FIRST, SignMessage, address_n, address_n, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , REQUIRED, STATIC , OTHER, SignMessage, message, address_n, 0),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, SignMessage, coin_name, message, &SignMessage_coin_name_default),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t VerifyMessage_fields[4] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, VerifyMessage, address, address, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, VerifyMessage, signature, address, 0),
|
||||||
|
PB_FIELD2( 3, BYTES , OPTIONAL, STATIC , OTHER, VerifyMessage, message, signature, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t MessageSignature_fields[3] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, MessageSignature, address, address, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, MessageSignature, signature, address, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t EstimateTxSize_fields[4] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, EstimateTxSize, outputs_count, outputs_count, 0),
|
||||||
|
PB_FIELD2( 2, UINT32 , REQUIRED, STATIC , OTHER, EstimateTxSize, inputs_count, outputs_count, 0),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, EstimateTxSize, coin_name, inputs_count, &EstimateTxSize_coin_name_default),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxSize_fields[2] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, TxSize, tx_size, tx_size, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t SignTx_fields[4] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, SignTx, outputs_count, outputs_count, 0),
|
||||||
|
PB_FIELD2( 2, UINT32 , REQUIRED, STATIC , OTHER, SignTx, inputs_count, outputs_count, 0),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, SignTx, coin_name, inputs_count, &SignTx_coin_name_default),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t SimpleSignTx_fields[5] = {
|
||||||
|
PB_FIELD2( 1, MESSAGE , REPEATED, STATIC , FIRST, SimpleSignTx, inputs, inputs, &TxInputType_fields),
|
||||||
|
PB_FIELD2( 2, MESSAGE , REPEATED, STATIC , OTHER, SimpleSignTx, outputs, inputs, &TxOutputType_fields),
|
||||||
|
PB_FIELD2( 3, MESSAGE , REPEATED, STATIC , OTHER, SimpleSignTx, transactions, outputs, &TransactionType_fields),
|
||||||
|
PB_FIELD2( 4, STRING , OPTIONAL, STATIC , OTHER, SimpleSignTx, coin_name, transactions, &SimpleSignTx_coin_name_default),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxRequest_fields[4] = {
|
||||||
|
PB_FIELD2( 1, ENUM , OPTIONAL, STATIC , FIRST, TxRequest, request_type, request_type, 0),
|
||||||
|
PB_FIELD2( 2, MESSAGE , OPTIONAL, STATIC , OTHER, TxRequest, details, request_type, &TxRequestDetailsType_fields),
|
||||||
|
PB_FIELD2( 3, MESSAGE , OPTIONAL, STATIC , OTHER, TxRequest, serialized, details, &TxRequestSerializedType_fields),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxAck_fields[2] = {
|
||||||
|
PB_FIELD2( 1, MESSAGE , OPTIONAL, STATIC , FIRST, TxAck, tx, tx, &TransactionType_fields),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t FirmwareErase_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t FirmwareUpload_fields[2] = {
|
||||||
|
PB_FIELD2( 1, BYTES , REQUIRED, STATIC , FIRST, FirmwareUpload, payload, payload, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t DebugLinkDecision_fields[2] = {
|
||||||
|
PB_FIELD2( 1, BOOL , REQUIRED, STATIC , FIRST, DebugLinkDecision, yes_no, yes_no, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t DebugLinkGetState_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t DebugLinkState_fields[11] = {
|
||||||
|
PB_FIELD2( 1, BYTES , OPTIONAL, STATIC , FIRST, DebugLinkState, layout, layout, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, DebugLinkState, pin, layout, 0),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, DebugLinkState, matrix, pin, 0),
|
||||||
|
PB_FIELD2( 4, STRING , OPTIONAL, STATIC , OTHER, DebugLinkState, mnemonic, matrix, 0),
|
||||||
|
PB_FIELD2( 5, MESSAGE , OPTIONAL, STATIC , OTHER, DebugLinkState, node, mnemonic, &HDNodeType_fields),
|
||||||
|
PB_FIELD2( 6, BOOL , OPTIONAL, STATIC , OTHER, DebugLinkState, passphrase_protection, node, 0),
|
||||||
|
PB_FIELD2( 7, STRING , OPTIONAL, STATIC , OTHER, DebugLinkState, reset_word, passphrase_protection, 0),
|
||||||
|
PB_FIELD2( 8, BYTES , OPTIONAL, STATIC , OTHER, DebugLinkState, reset_entropy, reset_word, 0),
|
||||||
|
PB_FIELD2( 9, STRING , OPTIONAL, STATIC , OTHER, DebugLinkState, recovery_fake_word, reset_entropy, 0),
|
||||||
|
PB_FIELD2( 10, UINT32 , OPTIONAL, STATIC , OTHER, DebugLinkState, recovery_word_pos, recovery_fake_word, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t DebugLinkStop_fields[1] = {
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t DebugLinkLog_fields[4] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, DebugLinkLog, level, level, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, DebugLinkLog, bucket, level, 0),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, DebugLinkLog, text, bucket, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Check that field information fits in pb_field_t */
|
||||||
|
#if !defined(PB_FIELD_32BIT)
|
||||||
|
/* If you get an error here, it means that you need to define PB_FIELD_32BIT
|
||||||
|
* compile-time option. You can do that in pb.h or on compiler command line.
|
||||||
|
*
|
||||||
|
* The reason you need to do this is that some of your messages contain tag
|
||||||
|
* numbers or field sizes that are larger than what can fit in 8 or 16 bit
|
||||||
|
* field descriptors.
|
||||||
|
*/
|
||||||
|
STATIC_ASSERT((pb_membersize(Features, coins[0]) < 65536 && pb_membersize(PublicKey, node) < 65536 && pb_membersize(LoadDevice, node) < 65536 && pb_membersize(SimpleSignTx, inputs[0]) < 65536 && pb_membersize(SimpleSignTx, outputs[0]) < 65536 && pb_membersize(SimpleSignTx, transactions[0]) < 65536 && pb_membersize(TxRequest, details) < 65536 && pb_membersize(TxRequest, serialized) < 65536 && pb_membersize(TxAck, tx) < 65536 && pb_membersize(DebugLinkState, node) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_Initialize_Features_ApplySettings_ChangePin_Ping_Success_Failure_ButtonRequest_ButtonAck_PinMatrixRequest_PinMatrixAck_Cancel_PassphraseRequest_PassphraseAck_GetEntropy_Entropy_GetPublicKey_PublicKey_GetAddress_Address_WipeDevice_LoadDevice_ResetDevice_EntropyRequest_EntropyAck_RecoveryDevice_WordRequest_WordAck_SignMessage_VerifyMessage_MessageSignature_EstimateTxSize_TxSize_SignTx_SimpleSignTx_TxRequest_TxAck_FirmwareErase_FirmwareUpload_DebugLinkDecision_DebugLinkGetState_DebugLinkState_DebugLinkStop_DebugLinkLog)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
|
||||||
|
#error Field descriptor for Entropy.entropy is too large. Define PB_FIELD_16BIT to fix this.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
639
firmware/protob/messages.pb.h
Normal file
@ -0,0 +1,639 @@
|
|||||||
|
/* Automatically generated nanopb header */
|
||||||
|
/* Generated by nanopb-0.2.7 */
|
||||||
|
|
||||||
|
#ifndef _PB_MESSAGES_PB_H_
|
||||||
|
#define _PB_MESSAGES_PB_H_
|
||||||
|
#include "pb.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Enum definitions */
|
||||||
|
typedef enum _MessageType {
|
||||||
|
MessageType_MessageType_Initialize = 0,
|
||||||
|
MessageType_MessageType_Ping = 1,
|
||||||
|
MessageType_MessageType_Success = 2,
|
||||||
|
MessageType_MessageType_Failure = 3,
|
||||||
|
MessageType_MessageType_ChangePin = 4,
|
||||||
|
MessageType_MessageType_WipeDevice = 5,
|
||||||
|
MessageType_MessageType_FirmwareErase = 6,
|
||||||
|
MessageType_MessageType_FirmwareUpload = 7,
|
||||||
|
MessageType_MessageType_GetEntropy = 9,
|
||||||
|
MessageType_MessageType_Entropy = 10,
|
||||||
|
MessageType_MessageType_GetPublicKey = 11,
|
||||||
|
MessageType_MessageType_PublicKey = 12,
|
||||||
|
MessageType_MessageType_LoadDevice = 13,
|
||||||
|
MessageType_MessageType_ResetDevice = 14,
|
||||||
|
MessageType_MessageType_SignTx = 15,
|
||||||
|
MessageType_MessageType_SimpleSignTx = 16,
|
||||||
|
MessageType_MessageType_Features = 17,
|
||||||
|
MessageType_MessageType_PinMatrixRequest = 18,
|
||||||
|
MessageType_MessageType_PinMatrixAck = 19,
|
||||||
|
MessageType_MessageType_Cancel = 20,
|
||||||
|
MessageType_MessageType_TxRequest = 21,
|
||||||
|
MessageType_MessageType_TxAck = 22,
|
||||||
|
MessageType_MessageType_ApplySettings = 25,
|
||||||
|
MessageType_MessageType_ButtonRequest = 26,
|
||||||
|
MessageType_MessageType_ButtonAck = 27,
|
||||||
|
MessageType_MessageType_GetAddress = 29,
|
||||||
|
MessageType_MessageType_Address = 30,
|
||||||
|
MessageType_MessageType_EntropyRequest = 35,
|
||||||
|
MessageType_MessageType_EntropyAck = 36,
|
||||||
|
MessageType_MessageType_SignMessage = 38,
|
||||||
|
MessageType_MessageType_VerifyMessage = 39,
|
||||||
|
MessageType_MessageType_MessageSignature = 40,
|
||||||
|
MessageType_MessageType_PassphraseRequest = 41,
|
||||||
|
MessageType_MessageType_PassphraseAck = 42,
|
||||||
|
MessageType_MessageType_EstimateTxSize = 43,
|
||||||
|
MessageType_MessageType_TxSize = 44,
|
||||||
|
MessageType_MessageType_RecoveryDevice = 45,
|
||||||
|
MessageType_MessageType_WordRequest = 46,
|
||||||
|
MessageType_MessageType_WordAck = 47,
|
||||||
|
MessageType_MessageType_DebugLinkDecision = 100,
|
||||||
|
MessageType_MessageType_DebugLinkGetState = 101,
|
||||||
|
MessageType_MessageType_DebugLinkState = 102,
|
||||||
|
MessageType_MessageType_DebugLinkStop = 103,
|
||||||
|
MessageType_MessageType_DebugLinkLog = 104
|
||||||
|
} MessageType;
|
||||||
|
|
||||||
|
/* Struct definitions */
|
||||||
|
typedef struct _ButtonAck {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} ButtonAck;
|
||||||
|
|
||||||
|
typedef struct _Cancel {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} Cancel;
|
||||||
|
|
||||||
|
typedef struct _DebugLinkGetState {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} DebugLinkGetState;
|
||||||
|
|
||||||
|
typedef struct _DebugLinkStop {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} DebugLinkStop;
|
||||||
|
|
||||||
|
typedef struct _EntropyRequest {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} EntropyRequest;
|
||||||
|
|
||||||
|
typedef struct _FirmwareErase {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} FirmwareErase;
|
||||||
|
|
||||||
|
typedef struct _Initialize {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} Initialize;
|
||||||
|
|
||||||
|
typedef struct _PassphraseRequest {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} PassphraseRequest;
|
||||||
|
|
||||||
|
typedef struct _WipeDevice {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} WipeDevice;
|
||||||
|
|
||||||
|
typedef struct _WordRequest {
|
||||||
|
uint8_t dummy_field;
|
||||||
|
} WordRequest;
|
||||||
|
|
||||||
|
typedef struct _Address {
|
||||||
|
char address[35];
|
||||||
|
} Address;
|
||||||
|
|
||||||
|
typedef struct _ApplySettings {
|
||||||
|
bool has_language;
|
||||||
|
char language[17];
|
||||||
|
bool has_label;
|
||||||
|
char label[33];
|
||||||
|
} ApplySettings;
|
||||||
|
|
||||||
|
typedef struct _ButtonRequest {
|
||||||
|
bool has_code;
|
||||||
|
ButtonRequestType code;
|
||||||
|
bool has_data;
|
||||||
|
char data[256];
|
||||||
|
} ButtonRequest;
|
||||||
|
|
||||||
|
typedef struct _ChangePin {
|
||||||
|
bool has_remove;
|
||||||
|
bool remove;
|
||||||
|
} ChangePin;
|
||||||
|
|
||||||
|
typedef struct _DebugLinkDecision {
|
||||||
|
bool yes_no;
|
||||||
|
} DebugLinkDecision;
|
||||||
|
|
||||||
|
typedef struct _DebugLinkLog {
|
||||||
|
bool has_level;
|
||||||
|
uint32_t level;
|
||||||
|
bool has_bucket;
|
||||||
|
char bucket[33];
|
||||||
|
bool has_text;
|
||||||
|
char text[256];
|
||||||
|
} DebugLinkLog;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[1024];
|
||||||
|
} DebugLinkState_layout_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[128];
|
||||||
|
} DebugLinkState_reset_entropy_t;
|
||||||
|
|
||||||
|
typedef struct _DebugLinkState {
|
||||||
|
bool has_layout;
|
||||||
|
DebugLinkState_layout_t layout;
|
||||||
|
bool has_pin;
|
||||||
|
char pin[10];
|
||||||
|
bool has_matrix;
|
||||||
|
char matrix[10];
|
||||||
|
bool has_mnemonic;
|
||||||
|
char mnemonic[241];
|
||||||
|
bool has_node;
|
||||||
|
HDNodeType node;
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
bool has_reset_word;
|
||||||
|
char reset_word[12];
|
||||||
|
bool has_reset_entropy;
|
||||||
|
DebugLinkState_reset_entropy_t reset_entropy;
|
||||||
|
bool has_recovery_fake_word;
|
||||||
|
char recovery_fake_word[12];
|
||||||
|
bool has_recovery_word_pos;
|
||||||
|
uint32_t recovery_word_pos;
|
||||||
|
} DebugLinkState;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[1024];
|
||||||
|
} Entropy_entropy_t;
|
||||||
|
|
||||||
|
typedef struct _Entropy {
|
||||||
|
Entropy_entropy_t entropy;
|
||||||
|
} Entropy;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[128];
|
||||||
|
} EntropyAck_entropy_t;
|
||||||
|
|
||||||
|
typedef struct _EntropyAck {
|
||||||
|
bool has_entropy;
|
||||||
|
EntropyAck_entropy_t entropy;
|
||||||
|
} EntropyAck;
|
||||||
|
|
||||||
|
typedef struct _EstimateTxSize {
|
||||||
|
uint32_t outputs_count;
|
||||||
|
uint32_t inputs_count;
|
||||||
|
bool has_coin_name;
|
||||||
|
char coin_name[17];
|
||||||
|
} EstimateTxSize;
|
||||||
|
|
||||||
|
typedef struct _Failure {
|
||||||
|
bool has_code;
|
||||||
|
FailureType code;
|
||||||
|
bool has_message;
|
||||||
|
char message[256];
|
||||||
|
} Failure;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[20];
|
||||||
|
} Features_revision_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[32];
|
||||||
|
} Features_bootloader_hash_t;
|
||||||
|
|
||||||
|
typedef struct _Features {
|
||||||
|
bool has_vendor;
|
||||||
|
char vendor[33];
|
||||||
|
bool has_major_version;
|
||||||
|
uint32_t major_version;
|
||||||
|
bool has_minor_version;
|
||||||
|
uint32_t minor_version;
|
||||||
|
bool has_patch_version;
|
||||||
|
uint32_t patch_version;
|
||||||
|
bool has_bootloader_mode;
|
||||||
|
bool bootloader_mode;
|
||||||
|
bool has_device_id;
|
||||||
|
char device_id[25];
|
||||||
|
bool has_pin_protection;
|
||||||
|
bool pin_protection;
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
bool has_language;
|
||||||
|
char language[17];
|
||||||
|
bool has_label;
|
||||||
|
char label[33];
|
||||||
|
size_t coins_count;
|
||||||
|
CoinType coins[4];
|
||||||
|
bool has_initialized;
|
||||||
|
bool initialized;
|
||||||
|
bool has_revision;
|
||||||
|
Features_revision_t revision;
|
||||||
|
bool has_bootloader_hash;
|
||||||
|
Features_bootloader_hash_t bootloader_hash;
|
||||||
|
} Features;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[0];
|
||||||
|
} FirmwareUpload_payload_t;
|
||||||
|
|
||||||
|
typedef struct _FirmwareUpload {
|
||||||
|
FirmwareUpload_payload_t payload;
|
||||||
|
} FirmwareUpload;
|
||||||
|
|
||||||
|
typedef struct _GetAddress {
|
||||||
|
size_t address_n_count;
|
||||||
|
uint32_t address_n[8];
|
||||||
|
bool has_coin_name;
|
||||||
|
char coin_name[17];
|
||||||
|
} GetAddress;
|
||||||
|
|
||||||
|
typedef struct _GetEntropy {
|
||||||
|
uint32_t size;
|
||||||
|
} GetEntropy;
|
||||||
|
|
||||||
|
typedef struct _GetPublicKey {
|
||||||
|
size_t address_n_count;
|
||||||
|
uint32_t address_n[8];
|
||||||
|
} GetPublicKey;
|
||||||
|
|
||||||
|
typedef struct _LoadDevice {
|
||||||
|
bool has_mnemonic;
|
||||||
|
char mnemonic[241];
|
||||||
|
bool has_node;
|
||||||
|
HDNodeType node;
|
||||||
|
bool has_pin;
|
||||||
|
char pin[10];
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
bool has_language;
|
||||||
|
char language[17];
|
||||||
|
bool has_label;
|
||||||
|
char label[33];
|
||||||
|
bool has_skip_checksum;
|
||||||
|
bool skip_checksum;
|
||||||
|
} LoadDevice;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[65];
|
||||||
|
} MessageSignature_signature_t;
|
||||||
|
|
||||||
|
typedef struct _MessageSignature {
|
||||||
|
bool has_address;
|
||||||
|
char address[35];
|
||||||
|
bool has_signature;
|
||||||
|
MessageSignature_signature_t signature;
|
||||||
|
} MessageSignature;
|
||||||
|
|
||||||
|
typedef struct _PassphraseAck {
|
||||||
|
char passphrase[51];
|
||||||
|
} PassphraseAck;
|
||||||
|
|
||||||
|
typedef struct _PinMatrixAck {
|
||||||
|
char pin[10];
|
||||||
|
} PinMatrixAck;
|
||||||
|
|
||||||
|
typedef struct _PinMatrixRequest {
|
||||||
|
bool has_type;
|
||||||
|
PinMatrixRequestType type;
|
||||||
|
} PinMatrixRequest;
|
||||||
|
|
||||||
|
typedef struct _Ping {
|
||||||
|
bool has_message;
|
||||||
|
char message[256];
|
||||||
|
bool has_button_protection;
|
||||||
|
bool button_protection;
|
||||||
|
bool has_pin_protection;
|
||||||
|
bool pin_protection;
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
} Ping;
|
||||||
|
|
||||||
|
typedef struct _PublicKey {
|
||||||
|
HDNodeType node;
|
||||||
|
} PublicKey;
|
||||||
|
|
||||||
|
typedef struct _RecoveryDevice {
|
||||||
|
bool has_word_count;
|
||||||
|
uint32_t word_count;
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
bool has_pin_protection;
|
||||||
|
bool pin_protection;
|
||||||
|
bool has_language;
|
||||||
|
char language[17];
|
||||||
|
bool has_label;
|
||||||
|
char label[33];
|
||||||
|
bool has_enforce_wordlist;
|
||||||
|
bool enforce_wordlist;
|
||||||
|
} RecoveryDevice;
|
||||||
|
|
||||||
|
typedef struct _ResetDevice {
|
||||||
|
bool has_display_random;
|
||||||
|
bool display_random;
|
||||||
|
bool has_strength;
|
||||||
|
uint32_t strength;
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
bool has_pin_protection;
|
||||||
|
bool pin_protection;
|
||||||
|
bool has_language;
|
||||||
|
char language[17];
|
||||||
|
bool has_label;
|
||||||
|
char label[33];
|
||||||
|
} ResetDevice;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[256];
|
||||||
|
} SignMessage_message_t;
|
||||||
|
|
||||||
|
typedef struct _SignMessage {
|
||||||
|
size_t address_n_count;
|
||||||
|
uint32_t address_n[8];
|
||||||
|
SignMessage_message_t message;
|
||||||
|
bool has_coin_name;
|
||||||
|
char coin_name[17];
|
||||||
|
} SignMessage;
|
||||||
|
|
||||||
|
typedef struct _SignTx {
|
||||||
|
uint32_t outputs_count;
|
||||||
|
uint32_t inputs_count;
|
||||||
|
bool has_coin_name;
|
||||||
|
char coin_name[17];
|
||||||
|
} SignTx;
|
||||||
|
|
||||||
|
typedef struct _SimpleSignTx {
|
||||||
|
size_t inputs_count;
|
||||||
|
TxInputType inputs[4];
|
||||||
|
size_t outputs_count;
|
||||||
|
TxOutputType outputs[4];
|
||||||
|
size_t transactions_count;
|
||||||
|
TransactionType transactions[4];
|
||||||
|
bool has_coin_name;
|
||||||
|
char coin_name[17];
|
||||||
|
} SimpleSignTx;
|
||||||
|
|
||||||
|
typedef struct _Success {
|
||||||
|
bool has_message;
|
||||||
|
char message[256];
|
||||||
|
} Success;
|
||||||
|
|
||||||
|
typedef struct _TxAck {
|
||||||
|
bool has_tx;
|
||||||
|
TransactionType tx;
|
||||||
|
} TxAck;
|
||||||
|
|
||||||
|
typedef struct _TxRequest {
|
||||||
|
bool has_request_type;
|
||||||
|
RequestType request_type;
|
||||||
|
bool has_details;
|
||||||
|
TxRequestDetailsType details;
|
||||||
|
bool has_serialized;
|
||||||
|
TxRequestSerializedType serialized;
|
||||||
|
} TxRequest;
|
||||||
|
|
||||||
|
typedef struct _TxSize {
|
||||||
|
bool has_tx_size;
|
||||||
|
uint32_t tx_size;
|
||||||
|
} TxSize;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[65];
|
||||||
|
} VerifyMessage_signature_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[256];
|
||||||
|
} VerifyMessage_message_t;
|
||||||
|
|
||||||
|
typedef struct _VerifyMessage {
|
||||||
|
bool has_address;
|
||||||
|
char address[35];
|
||||||
|
bool has_signature;
|
||||||
|
VerifyMessage_signature_t signature;
|
||||||
|
bool has_message;
|
||||||
|
VerifyMessage_message_t message;
|
||||||
|
} VerifyMessage;
|
||||||
|
|
||||||
|
typedef struct _WordAck {
|
||||||
|
char word[12];
|
||||||
|
} WordAck;
|
||||||
|
|
||||||
|
/* Default values for struct fields */
|
||||||
|
extern const char GetAddress_coin_name_default[17];
|
||||||
|
extern const char LoadDevice_language_default[17];
|
||||||
|
extern const uint32_t ResetDevice_strength_default;
|
||||||
|
extern const char ResetDevice_language_default[17];
|
||||||
|
extern const char RecoveryDevice_language_default[17];
|
||||||
|
extern const char SignMessage_coin_name_default[17];
|
||||||
|
extern const char EstimateTxSize_coin_name_default[17];
|
||||||
|
extern const char SignTx_coin_name_default[17];
|
||||||
|
extern const char SimpleSignTx_coin_name_default[17];
|
||||||
|
|
||||||
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
|
#define Address_address_tag 1
|
||||||
|
#define ApplySettings_language_tag 1
|
||||||
|
#define ApplySettings_label_tag 2
|
||||||
|
#define ButtonRequest_code_tag 1
|
||||||
|
#define ButtonRequest_data_tag 2
|
||||||
|
#define ChangePin_remove_tag 1
|
||||||
|
#define DebugLinkDecision_yes_no_tag 1
|
||||||
|
#define DebugLinkLog_level_tag 1
|
||||||
|
#define DebugLinkLog_bucket_tag 2
|
||||||
|
#define DebugLinkLog_text_tag 3
|
||||||
|
#define DebugLinkState_layout_tag 1
|
||||||
|
#define DebugLinkState_pin_tag 2
|
||||||
|
#define DebugLinkState_matrix_tag 3
|
||||||
|
#define DebugLinkState_mnemonic_tag 4
|
||||||
|
#define DebugLinkState_node_tag 5
|
||||||
|
#define DebugLinkState_passphrase_protection_tag 6
|
||||||
|
#define DebugLinkState_reset_word_tag 7
|
||||||
|
#define DebugLinkState_reset_entropy_tag 8
|
||||||
|
#define DebugLinkState_recovery_fake_word_tag 9
|
||||||
|
#define DebugLinkState_recovery_word_pos_tag 10
|
||||||
|
#define Entropy_entropy_tag 1
|
||||||
|
#define EntropyAck_entropy_tag 1
|
||||||
|
#define EstimateTxSize_outputs_count_tag 1
|
||||||
|
#define EstimateTxSize_inputs_count_tag 2
|
||||||
|
#define EstimateTxSize_coin_name_tag 3
|
||||||
|
#define Failure_code_tag 1
|
||||||
|
#define Failure_message_tag 2
|
||||||
|
#define Features_vendor_tag 1
|
||||||
|
#define Features_major_version_tag 2
|
||||||
|
#define Features_minor_version_tag 3
|
||||||
|
#define Features_patch_version_tag 4
|
||||||
|
#define Features_bootloader_mode_tag 5
|
||||||
|
#define Features_device_id_tag 6
|
||||||
|
#define Features_pin_protection_tag 7
|
||||||
|
#define Features_passphrase_protection_tag 8
|
||||||
|
#define Features_language_tag 9
|
||||||
|
#define Features_label_tag 10
|
||||||
|
#define Features_coins_tag 11
|
||||||
|
#define Features_initialized_tag 12
|
||||||
|
#define Features_revision_tag 13
|
||||||
|
#define Features_bootloader_hash_tag 14
|
||||||
|
#define FirmwareUpload_payload_tag 1
|
||||||
|
#define GetAddress_address_n_tag 1
|
||||||
|
#define GetAddress_coin_name_tag 2
|
||||||
|
#define GetEntropy_size_tag 1
|
||||||
|
#define GetPublicKey_address_n_tag 1
|
||||||
|
#define LoadDevice_mnemonic_tag 1
|
||||||
|
#define LoadDevice_node_tag 2
|
||||||
|
#define LoadDevice_pin_tag 3
|
||||||
|
#define LoadDevice_passphrase_protection_tag 4
|
||||||
|
#define LoadDevice_language_tag 5
|
||||||
|
#define LoadDevice_label_tag 6
|
||||||
|
#define LoadDevice_skip_checksum_tag 7
|
||||||
|
#define MessageSignature_address_tag 1
|
||||||
|
#define MessageSignature_signature_tag 2
|
||||||
|
#define PassphraseAck_passphrase_tag 1
|
||||||
|
#define PinMatrixAck_pin_tag 1
|
||||||
|
#define PinMatrixRequest_type_tag 1
|
||||||
|
#define Ping_message_tag 1
|
||||||
|
#define Ping_button_protection_tag 2
|
||||||
|
#define Ping_pin_protection_tag 3
|
||||||
|
#define Ping_passphrase_protection_tag 4
|
||||||
|
#define PublicKey_node_tag 1
|
||||||
|
#define RecoveryDevice_word_count_tag 1
|
||||||
|
#define RecoveryDevice_passphrase_protection_tag 2
|
||||||
|
#define RecoveryDevice_pin_protection_tag 3
|
||||||
|
#define RecoveryDevice_language_tag 4
|
||||||
|
#define RecoveryDevice_label_tag 5
|
||||||
|
#define RecoveryDevice_enforce_wordlist_tag 6
|
||||||
|
#define ResetDevice_display_random_tag 1
|
||||||
|
#define ResetDevice_strength_tag 2
|
||||||
|
#define ResetDevice_passphrase_protection_tag 3
|
||||||
|
#define ResetDevice_pin_protection_tag 4
|
||||||
|
#define ResetDevice_language_tag 5
|
||||||
|
#define ResetDevice_label_tag 6
|
||||||
|
#define SignMessage_address_n_tag 1
|
||||||
|
#define SignMessage_message_tag 2
|
||||||
|
#define SignMessage_coin_name_tag 3
|
||||||
|
#define SignTx_outputs_count_tag 1
|
||||||
|
#define SignTx_inputs_count_tag 2
|
||||||
|
#define SignTx_coin_name_tag 3
|
||||||
|
#define SimpleSignTx_inputs_tag 1
|
||||||
|
#define SimpleSignTx_outputs_tag 2
|
||||||
|
#define SimpleSignTx_transactions_tag 3
|
||||||
|
#define SimpleSignTx_coin_name_tag 4
|
||||||
|
#define Success_message_tag 1
|
||||||
|
#define TxAck_tx_tag 1
|
||||||
|
#define TxRequest_request_type_tag 1
|
||||||
|
#define TxRequest_details_tag 2
|
||||||
|
#define TxRequest_serialized_tag 3
|
||||||
|
#define TxSize_tx_size_tag 1
|
||||||
|
#define VerifyMessage_address_tag 1
|
||||||
|
#define VerifyMessage_signature_tag 2
|
||||||
|
#define VerifyMessage_message_tag 3
|
||||||
|
#define WordAck_word_tag 1
|
||||||
|
|
||||||
|
/* Struct field encoding specification for nanopb */
|
||||||
|
extern const pb_field_t Initialize_fields[1];
|
||||||
|
extern const pb_field_t Features_fields[15];
|
||||||
|
extern const pb_field_t ApplySettings_fields[3];
|
||||||
|
extern const pb_field_t ChangePin_fields[2];
|
||||||
|
extern const pb_field_t Ping_fields[5];
|
||||||
|
extern const pb_field_t Success_fields[2];
|
||||||
|
extern const pb_field_t Failure_fields[3];
|
||||||
|
extern const pb_field_t ButtonRequest_fields[3];
|
||||||
|
extern const pb_field_t ButtonAck_fields[1];
|
||||||
|
extern const pb_field_t PinMatrixRequest_fields[2];
|
||||||
|
extern const pb_field_t PinMatrixAck_fields[2];
|
||||||
|
extern const pb_field_t Cancel_fields[1];
|
||||||
|
extern const pb_field_t PassphraseRequest_fields[1];
|
||||||
|
extern const pb_field_t PassphraseAck_fields[2];
|
||||||
|
extern const pb_field_t GetEntropy_fields[2];
|
||||||
|
extern const pb_field_t Entropy_fields[2];
|
||||||
|
extern const pb_field_t GetPublicKey_fields[2];
|
||||||
|
extern const pb_field_t PublicKey_fields[2];
|
||||||
|
extern const pb_field_t GetAddress_fields[3];
|
||||||
|
extern const pb_field_t Address_fields[2];
|
||||||
|
extern const pb_field_t WipeDevice_fields[1];
|
||||||
|
extern const pb_field_t LoadDevice_fields[8];
|
||||||
|
extern const pb_field_t ResetDevice_fields[7];
|
||||||
|
extern const pb_field_t EntropyRequest_fields[1];
|
||||||
|
extern const pb_field_t EntropyAck_fields[2];
|
||||||
|
extern const pb_field_t RecoveryDevice_fields[7];
|
||||||
|
extern const pb_field_t WordRequest_fields[1];
|
||||||
|
extern const pb_field_t WordAck_fields[2];
|
||||||
|
extern const pb_field_t SignMessage_fields[4];
|
||||||
|
extern const pb_field_t VerifyMessage_fields[4];
|
||||||
|
extern const pb_field_t MessageSignature_fields[3];
|
||||||
|
extern const pb_field_t EstimateTxSize_fields[4];
|
||||||
|
extern const pb_field_t TxSize_fields[2];
|
||||||
|
extern const pb_field_t SignTx_fields[4];
|
||||||
|
extern const pb_field_t SimpleSignTx_fields[5];
|
||||||
|
extern const pb_field_t TxRequest_fields[4];
|
||||||
|
extern const pb_field_t TxAck_fields[2];
|
||||||
|
extern const pb_field_t FirmwareErase_fields[1];
|
||||||
|
extern const pb_field_t FirmwareUpload_fields[2];
|
||||||
|
extern const pb_field_t DebugLinkDecision_fields[2];
|
||||||
|
extern const pb_field_t DebugLinkGetState_fields[1];
|
||||||
|
extern const pb_field_t DebugLinkState_fields[11];
|
||||||
|
extern const pb_field_t DebugLinkStop_fields[1];
|
||||||
|
extern const pb_field_t DebugLinkLog_fields[4];
|
||||||
|
|
||||||
|
/* Maximum encoded size of messages (where known) */
|
||||||
|
#define Initialize_size 0
|
||||||
|
#define Features_size (222 + 4*CoinType_size)
|
||||||
|
#define ApplySettings_size 54
|
||||||
|
#define ChangePin_size 2
|
||||||
|
#define Ping_size 265
|
||||||
|
#define Success_size 259
|
||||||
|
#define Failure_size 265
|
||||||
|
#define ButtonRequest_size 265
|
||||||
|
#define ButtonAck_size 0
|
||||||
|
#define PinMatrixRequest_size 6
|
||||||
|
#define PinMatrixAck_size 12
|
||||||
|
#define Cancel_size 0
|
||||||
|
#define PassphraseRequest_size 0
|
||||||
|
#define PassphraseAck_size 53
|
||||||
|
#define GetEntropy_size 6
|
||||||
|
#define Entropy_size 1027
|
||||||
|
#define GetPublicKey_size 48
|
||||||
|
#define PublicKey_size (6 + HDNodeType_size)
|
||||||
|
#define GetAddress_size 67
|
||||||
|
#define Address_size 37
|
||||||
|
#define WipeDevice_size 0
|
||||||
|
#define LoadDevice_size (320 + HDNodeType_size)
|
||||||
|
#define ResetDevice_size 66
|
||||||
|
#define EntropyRequest_size 0
|
||||||
|
#define EntropyAck_size 131
|
||||||
|
#define RecoveryDevice_size 66
|
||||||
|
#define WordRequest_size 0
|
||||||
|
#define WordAck_size 14
|
||||||
|
#define SignMessage_size 326
|
||||||
|
#define VerifyMessage_size 363
|
||||||
|
#define MessageSignature_size 104
|
||||||
|
#define EstimateTxSize_size 31
|
||||||
|
#define TxSize_size 6
|
||||||
|
#define SignTx_size 31
|
||||||
|
#define SimpleSignTx_size (91 + 4*TxInputType_size + 4*TxOutputType_size + 4*TransactionType_size)
|
||||||
|
#define TxRequest_size (18 + TxRequestDetailsType_size + TxRequestSerializedType_size)
|
||||||
|
#define TxAck_size (6 + TransactionType_size)
|
||||||
|
#define FirmwareErase_size 0
|
||||||
|
#define FirmwareUpload_size 2
|
||||||
|
#define DebugLinkDecision_size 2
|
||||||
|
#define DebugLinkGetState_size 0
|
||||||
|
#define DebugLinkState_size (1468 + HDNodeType_size)
|
||||||
|
#define DebugLinkStop_size 0
|
||||||
|
#define DebugLinkLog_size 300
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1
firmware/protob/messages.proto
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../trezor-common/protob/messages.proto
|
519
firmware/protob/pb.h
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
/* Common parts of the nanopb library. Most of these are quite low-level
|
||||||
|
* stuff. For the high-level interface, see pb_encode.h and pb_decode.h.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PB_H_
|
||||||
|
#define _PB_H_
|
||||||
|
|
||||||
|
/*****************************************************************
|
||||||
|
* Nanopb compilation time options. You can change these here by *
|
||||||
|
* uncommenting the lines, or on the compiler command line. *
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
/* Enable support for dynamically allocated fields */
|
||||||
|
/* #define PB_ENABLE_MALLOC 1 */
|
||||||
|
|
||||||
|
/* Define this if your CPU architecture is big endian, i.e. it
|
||||||
|
* stores the most-significant byte first. */
|
||||||
|
/* #define __BIG_ENDIAN__ 1 */
|
||||||
|
|
||||||
|
/* Increase the number of required fields that are tracked.
|
||||||
|
* A compiler warning will tell if you need this. */
|
||||||
|
/* #define PB_MAX_REQUIRED_FIELDS 256 */
|
||||||
|
|
||||||
|
/* Add support for tag numbers > 255 and fields larger than 255 bytes. */
|
||||||
|
/* #define PB_FIELD_16BIT 1 */
|
||||||
|
|
||||||
|
/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */
|
||||||
|
/* #define PB_FIELD_32BIT 1 */
|
||||||
|
|
||||||
|
/* Disable support for error messages in order to save some code space. */
|
||||||
|
/* #define PB_NO_ERRMSG 1 */
|
||||||
|
|
||||||
|
/* Disable support for custom streams (support only memory buffers). */
|
||||||
|
/* #define PB_BUFFER_ONLY 1 */
|
||||||
|
|
||||||
|
/* Switch back to the old-style callback function signature.
|
||||||
|
* This was the default until nanopb-0.2.1. */
|
||||||
|
/* #define PB_OLD_CALLBACK_STYLE */
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************
|
||||||
|
* You usually don't need to change anything below this line. *
|
||||||
|
* Feel free to look around and use the defined macros, though. *
|
||||||
|
******************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
/* Version of the nanopb library. Just in case you want to check it in
|
||||||
|
* your own program. */
|
||||||
|
#define NANOPB_VERSION nanopb-0.2.7
|
||||||
|
|
||||||
|
/* Include all the system headers needed by nanopb. You will need the
|
||||||
|
* definitions of the following:
|
||||||
|
* - strlen, memcpy, memset functions
|
||||||
|
* - [u]int8_t, [u]int16_t, [u]int32_t, [u]int64_t
|
||||||
|
* - size_t
|
||||||
|
* - bool
|
||||||
|
*
|
||||||
|
* If you don't have the standard header files, you can instead provide
|
||||||
|
* a custom header that defines or includes all this. In that case,
|
||||||
|
* define PB_SYSTEM_HEADER to the path of this file.
|
||||||
|
*/
|
||||||
|
#ifdef PB_SYSTEM_HEADER
|
||||||
|
#include PB_SYSTEM_HEADER
|
||||||
|
#else
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef PB_ENABLE_MALLOC
|
||||||
|
#include <stdlib.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Macro for defining packed structures (compiler dependent).
|
||||||
|
* This just reduces memory requirements, but is not required.
|
||||||
|
*/
|
||||||
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
|
/* For GCC and clang */
|
||||||
|
# define PB_PACKED_STRUCT_START
|
||||||
|
# define PB_PACKED_STRUCT_END
|
||||||
|
# define pb_packed __attribute__((packed))
|
||||||
|
#elif defined(__ICCARM__)
|
||||||
|
/* For IAR ARM compiler */
|
||||||
|
# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)")
|
||||||
|
# define PB_PACKED_STRUCT_END _Pragma("pack(pop)")
|
||||||
|
# define pb_packed
|
||||||
|
#elif defined(_MSC_VER) && (_MSC_VER >= 1500)
|
||||||
|
/* For Microsoft Visual C++ */
|
||||||
|
# define PB_PACKED_STRUCT_START __pragma(pack(push, 1))
|
||||||
|
# define PB_PACKED_STRUCT_END __pragma(pack(pop))
|
||||||
|
# define pb_packed
|
||||||
|
#else
|
||||||
|
/* Unknown compiler */
|
||||||
|
# define PB_PACKED_STRUCT_START
|
||||||
|
# define PB_PACKED_STRUCT_END
|
||||||
|
# define pb_packed
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Handly macro for suppressing unreferenced-parameter compiler warnings. */
|
||||||
|
#ifndef UNUSED
|
||||||
|
#define UNUSED(x) (void)(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Compile-time assertion, used for checking compatible compilation options.
|
||||||
|
* If this does not work properly on your compiler, use #define STATIC_ASSERT
|
||||||
|
* to disable it.
|
||||||
|
*
|
||||||
|
* But before doing that, check carefully the error message / place where it
|
||||||
|
* comes from to see if the error has a real cause. Unfortunately the error
|
||||||
|
* message is not always very clear to read, but you can see the reason better
|
||||||
|
* in the place where the STATIC_ASSERT macro was called.
|
||||||
|
*/
|
||||||
|
#ifndef STATIC_ASSERT
|
||||||
|
#define STATIC_ASSERT(COND,MSG) typedef char STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1];
|
||||||
|
#define STATIC_ASSERT_MSG(MSG, LINE, COUNTER) STATIC_ASSERT_MSG_(MSG, LINE, COUNTER)
|
||||||
|
#define STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) static_assertion_##MSG##LINE##COUNTER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Number of required fields to keep track of. */
|
||||||
|
#ifndef PB_MAX_REQUIRED_FIELDS
|
||||||
|
#define PB_MAX_REQUIRED_FIELDS 64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if PB_MAX_REQUIRED_FIELDS < 64
|
||||||
|
#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* List of possible field types. These are used in the autogenerated code.
|
||||||
|
* Least-significant 4 bits tell the scalar type
|
||||||
|
* Most-significant 4 bits specify repeated/required/packed etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef uint8_t pb_type_t;
|
||||||
|
|
||||||
|
/**** Field data types ****/
|
||||||
|
|
||||||
|
/* Numeric types */
|
||||||
|
#define PB_LTYPE_VARINT 0x00 /* int32, int64, enum, bool */
|
||||||
|
#define PB_LTYPE_UVARINT 0x01 /* uint32, uint64 */
|
||||||
|
#define PB_LTYPE_SVARINT 0x02 /* sint32, sint64 */
|
||||||
|
#define PB_LTYPE_FIXED32 0x03 /* fixed32, sfixed32, float */
|
||||||
|
#define PB_LTYPE_FIXED64 0x04 /* fixed64, sfixed64, double */
|
||||||
|
|
||||||
|
/* Marker for last packable field type. */
|
||||||
|
#define PB_LTYPE_LAST_PACKABLE 0x04
|
||||||
|
|
||||||
|
/* Byte array with pre-allocated buffer.
|
||||||
|
* data_size is the length of the allocated PB_BYTES_ARRAY structure. */
|
||||||
|
#define PB_LTYPE_BYTES 0x05
|
||||||
|
|
||||||
|
/* String with pre-allocated buffer.
|
||||||
|
* data_size is the maximum length. */
|
||||||
|
#define PB_LTYPE_STRING 0x06
|
||||||
|
|
||||||
|
/* Submessage
|
||||||
|
* submsg_fields is pointer to field descriptions */
|
||||||
|
#define PB_LTYPE_SUBMESSAGE 0x07
|
||||||
|
|
||||||
|
/* Extension pseudo-field
|
||||||
|
* The field contains a pointer to pb_extension_t */
|
||||||
|
#define PB_LTYPE_EXTENSION 0x08
|
||||||
|
|
||||||
|
/* Number of declared LTYPES */
|
||||||
|
#define PB_LTYPES_COUNT 9
|
||||||
|
#define PB_LTYPE_MASK 0x0F
|
||||||
|
|
||||||
|
/**** Field repetition rules ****/
|
||||||
|
|
||||||
|
#define PB_HTYPE_REQUIRED 0x00
|
||||||
|
#define PB_HTYPE_OPTIONAL 0x10
|
||||||
|
#define PB_HTYPE_REPEATED 0x20
|
||||||
|
#define PB_HTYPE_MASK 0x30
|
||||||
|
|
||||||
|
/**** Field allocation types ****/
|
||||||
|
|
||||||
|
#define PB_ATYPE_STATIC 0x00
|
||||||
|
#define PB_ATYPE_POINTER 0x80
|
||||||
|
#define PB_ATYPE_CALLBACK 0x40
|
||||||
|
#define PB_ATYPE_MASK 0xC0
|
||||||
|
|
||||||
|
#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK)
|
||||||
|
#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK)
|
||||||
|
#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK)
|
||||||
|
|
||||||
|
/* Data type used for storing sizes of struct fields
|
||||||
|
* and array counts.
|
||||||
|
*/
|
||||||
|
#if defined(PB_FIELD_32BIT)
|
||||||
|
typedef uint32_t pb_size_t;
|
||||||
|
typedef int32_t pb_ssize_t;
|
||||||
|
#elif defined(PB_FIELD_16BIT)
|
||||||
|
typedef uint16_t pb_size_t;
|
||||||
|
typedef int16_t pb_ssize_t;
|
||||||
|
#else
|
||||||
|
typedef uint8_t pb_size_t;
|
||||||
|
typedef int8_t pb_ssize_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This structure is used in auto-generated constants
|
||||||
|
* to specify struct fields.
|
||||||
|
* You can change field sizes if you need structures
|
||||||
|
* larger than 256 bytes or field tags larger than 256.
|
||||||
|
* The compiler should complain if your .proto has such
|
||||||
|
* structures. Fix that by defining PB_FIELD_16BIT or
|
||||||
|
* PB_FIELD_32BIT.
|
||||||
|
*/
|
||||||
|
PB_PACKED_STRUCT_START
|
||||||
|
typedef struct _pb_field_t pb_field_t;
|
||||||
|
struct _pb_field_t {
|
||||||
|
pb_size_t tag;
|
||||||
|
pb_type_t type;
|
||||||
|
pb_size_t data_offset; /* Offset of field data, relative to previous field. */
|
||||||
|
pb_ssize_t size_offset; /* Offset of array size or has-boolean, relative to data */
|
||||||
|
pb_size_t data_size; /* Data size in bytes for a single item */
|
||||||
|
pb_size_t array_size; /* Maximum number of entries in array */
|
||||||
|
|
||||||
|
/* Field definitions for submessage
|
||||||
|
* OR default value for all other non-array, non-callback types
|
||||||
|
* If null, then field will zeroed. */
|
||||||
|
const void *ptr;
|
||||||
|
} pb_packed;
|
||||||
|
PB_PACKED_STRUCT_END
|
||||||
|
|
||||||
|
/* Make sure that the standard integer types are of the expected sizes.
|
||||||
|
* All kinds of things may break otherwise.. atleast all fixed* types.
|
||||||
|
*
|
||||||
|
* If you get errors here, it probably means that your stdint.h is not
|
||||||
|
* correct for your platform.
|
||||||
|
*/
|
||||||
|
STATIC_ASSERT(sizeof(int8_t) == 1, INT8_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint8_t) == 1, UINT8_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(int16_t) == 2, INT16_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint16_t) == 2, UINT16_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(int32_t) == 4, INT32_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint32_t) == 4, UINT32_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(int64_t) == 8, INT64_T_WRONG_SIZE)
|
||||||
|
STATIC_ASSERT(sizeof(uint64_t) == 8, UINT64_T_WRONG_SIZE)
|
||||||
|
|
||||||
|
/* This structure is used for 'bytes' arrays.
|
||||||
|
* It has the number of bytes in the beginning, and after that an array.
|
||||||
|
* Note that actual structs used will have a different length of bytes array.
|
||||||
|
*/
|
||||||
|
#define PB_BYTES_ARRAY_T(n) struct { size_t size; uint8_t bytes[n]; }
|
||||||
|
#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes))
|
||||||
|
|
||||||
|
struct _pb_bytes_array_t {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[1];
|
||||||
|
};
|
||||||
|
typedef struct _pb_bytes_array_t pb_bytes_array_t;
|
||||||
|
|
||||||
|
/* This structure is used for giving the callback function.
|
||||||
|
* It is stored in the message structure and filled in by the method that
|
||||||
|
* calls pb_decode.
|
||||||
|
*
|
||||||
|
* The decoding callback will be given a limited-length stream
|
||||||
|
* If the wire type was string, the length is the length of the string.
|
||||||
|
* If the wire type was a varint/fixed32/fixed64, the length is the length
|
||||||
|
* of the actual value.
|
||||||
|
* The function may be called multiple times (especially for repeated types,
|
||||||
|
* but also otherwise if the message happens to contain the field multiple
|
||||||
|
* times.)
|
||||||
|
*
|
||||||
|
* The encoding callback will receive the actual output stream.
|
||||||
|
* It should write all the data in one call, including the field tag and
|
||||||
|
* wire type. It can write multiple fields.
|
||||||
|
*
|
||||||
|
* The callback can be null if you want to skip a field.
|
||||||
|
*/
|
||||||
|
typedef struct _pb_istream_t pb_istream_t;
|
||||||
|
typedef struct _pb_ostream_t pb_ostream_t;
|
||||||
|
typedef struct _pb_callback_t pb_callback_t;
|
||||||
|
struct _pb_callback_t {
|
||||||
|
#ifdef PB_OLD_CALLBACK_STYLE
|
||||||
|
/* Deprecated since nanopb-0.2.1 */
|
||||||
|
union {
|
||||||
|
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void *arg);
|
||||||
|
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, const void *arg);
|
||||||
|
} funcs;
|
||||||
|
#else
|
||||||
|
/* New function signature, which allows modifying arg contents in callback. */
|
||||||
|
union {
|
||||||
|
bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg);
|
||||||
|
bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg);
|
||||||
|
} funcs;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Free arg for use by callback */
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Wire types. Library user needs these only in encoder callbacks. */
|
||||||
|
typedef enum {
|
||||||
|
PB_WT_VARINT = 0,
|
||||||
|
PB_WT_64BIT = 1,
|
||||||
|
PB_WT_STRING = 2,
|
||||||
|
PB_WT_32BIT = 5
|
||||||
|
} pb_wire_type_t;
|
||||||
|
|
||||||
|
/* Structure for defining the handling of unknown/extension fields.
|
||||||
|
* Usually the pb_extension_type_t structure is automatically generated,
|
||||||
|
* while the pb_extension_t structure is created by the user. However,
|
||||||
|
* if you want to catch all unknown fields, you can also create a custom
|
||||||
|
* pb_extension_type_t with your own callback.
|
||||||
|
*/
|
||||||
|
typedef struct _pb_extension_type_t pb_extension_type_t;
|
||||||
|
typedef struct _pb_extension_t pb_extension_t;
|
||||||
|
struct _pb_extension_type_t {
|
||||||
|
/* Called for each unknown field in the message.
|
||||||
|
* If you handle the field, read off all of its data and return true.
|
||||||
|
* If you do not handle the field, do not read anything and return true.
|
||||||
|
* If you run into an error, return false.
|
||||||
|
* Set to NULL for default handler.
|
||||||
|
*/
|
||||||
|
bool (*decode)(pb_istream_t *stream, pb_extension_t *extension,
|
||||||
|
uint32_t tag, pb_wire_type_t wire_type);
|
||||||
|
|
||||||
|
/* Called once after all regular fields have been encoded.
|
||||||
|
* If you have something to write, do so and return true.
|
||||||
|
* If you do not have anything to write, just return true.
|
||||||
|
* If you run into an error, return false.
|
||||||
|
* Set to NULL for default handler.
|
||||||
|
*/
|
||||||
|
bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||||
|
|
||||||
|
/* Free field for use by the callback. */
|
||||||
|
const void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _pb_extension_t {
|
||||||
|
/* Type describing the extension field. Usually you'll initialize
|
||||||
|
* this to a pointer to the automatically generated structure. */
|
||||||
|
const pb_extension_type_t *type;
|
||||||
|
|
||||||
|
/* Destination for the decoded data. This must match the datatype
|
||||||
|
* of the extension field. */
|
||||||
|
void *dest;
|
||||||
|
|
||||||
|
/* Pointer to the next extension handler, or NULL.
|
||||||
|
* If this extension does not match a field, the next handler is
|
||||||
|
* automatically called. */
|
||||||
|
pb_extension_t *next;
|
||||||
|
|
||||||
|
/* The decoder sets this to true if the extension was found.
|
||||||
|
* Ignored for encoding. */
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Memory allocation functions to use. You can define pb_realloc and
|
||||||
|
* pb_free to custom functions if you want. */
|
||||||
|
#ifdef PB_ENABLE_MALLOC
|
||||||
|
# ifndef pb_realloc
|
||||||
|
# define pb_realloc(ptr, size) realloc(ptr, size)
|
||||||
|
# endif
|
||||||
|
# ifndef pb_free
|
||||||
|
# define pb_free(ptr) free(ptr)
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* These macros are used to declare pb_field_t's in the constant array. */
|
||||||
|
/* Size of a structure member, in bytes. */
|
||||||
|
#define pb_membersize(st, m) (sizeof ((st*)0)->m)
|
||||||
|
/* Number of entries in an array. */
|
||||||
|
#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0]))
|
||||||
|
/* Delta from start of one member to the start of another member. */
|
||||||
|
#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2))
|
||||||
|
/* Marks the end of the field list */
|
||||||
|
#define PB_LAST_FIELD {0,(pb_type_t) 0,0,0,0,0,0}
|
||||||
|
|
||||||
|
/* Macros for filling in the data_offset field */
|
||||||
|
/* data_offset for first field in a message */
|
||||||
|
#define PB_DATAOFFSET_FIRST(st, m1, m2) (offsetof(st, m1))
|
||||||
|
/* data_offset for subsequent fields */
|
||||||
|
#define PB_DATAOFFSET_OTHER(st, m1, m2) (offsetof(st, m1) - offsetof(st, m2) - pb_membersize(st, m2))
|
||||||
|
/* Choose first/other based on m1 == m2 (deprecated, remains for backwards compatibility) */
|
||||||
|
#define PB_DATAOFFSET_CHOOSE(st, m1, m2) (int)(offsetof(st, m1) == offsetof(st, m2) \
|
||||||
|
? PB_DATAOFFSET_FIRST(st, m1, m2) \
|
||||||
|
: PB_DATAOFFSET_OTHER(st, m1, m2))
|
||||||
|
|
||||||
|
/* Required fields are the simplest. They just have delta (padding) from
|
||||||
|
* previous field end, and the size of the field. Pointer is used for
|
||||||
|
* submessages and default values.
|
||||||
|
*/
|
||||||
|
#define PB_REQUIRED_STATIC(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_STATIC | PB_HTYPE_REQUIRED | ltype, \
|
||||||
|
fd, 0, pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
/* Optional fields add the delta to the has_ variable. */
|
||||||
|
#define PB_OPTIONAL_STATIC(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
|
||||||
|
fd, \
|
||||||
|
pb_delta(st, has_ ## m, m), \
|
||||||
|
pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
/* Repeated fields have a _count field and also the maximum number of entries. */
|
||||||
|
#define PB_REPEATED_STATIC(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_STATIC | PB_HTYPE_REPEATED | ltype, \
|
||||||
|
fd, \
|
||||||
|
pb_delta(st, m ## _count, m), \
|
||||||
|
pb_membersize(st, m[0]), \
|
||||||
|
pb_arraysize(st, m), ptr}
|
||||||
|
|
||||||
|
/* Allocated fields carry the size of the actual data, not the pointer */
|
||||||
|
#define PB_REQUIRED_POINTER(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_POINTER | PB_HTYPE_REQUIRED | ltype, \
|
||||||
|
fd, 0, pb_membersize(st, m[0]), 0, ptr}
|
||||||
|
|
||||||
|
/* Optional fields don't need a has_ variable, as information would be redundant */
|
||||||
|
#define PB_OPTIONAL_POINTER(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_POINTER | PB_HTYPE_OPTIONAL | ltype, \
|
||||||
|
fd, 0, pb_membersize(st, m[0]), 0, ptr}
|
||||||
|
|
||||||
|
/* Repeated fields have a _count field and a pointer to array of pointers */
|
||||||
|
#define PB_REPEATED_POINTER(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_POINTER | PB_HTYPE_REPEATED | ltype, \
|
||||||
|
fd, pb_delta(st, m ## _count, m), \
|
||||||
|
pb_membersize(st, m[0]), 0, ptr}
|
||||||
|
|
||||||
|
/* Callbacks are much like required fields except with special datatype. */
|
||||||
|
#define PB_REQUIRED_CALLBACK(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_REQUIRED | ltype, \
|
||||||
|
fd, 0, pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
#define PB_OPTIONAL_CALLBACK(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
|
||||||
|
fd, 0, pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
#define PB_REPEATED_CALLBACK(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_REPEATED | ltype, \
|
||||||
|
fd, 0, pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
/* Optional extensions don't have the has_ field, as that would be redundant. */
|
||||||
|
#define PB_OPTEXT_STATIC(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_STATIC | PB_HTYPE_OPTIONAL | ltype, \
|
||||||
|
0, \
|
||||||
|
0, \
|
||||||
|
pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
#define PB_OPTEXT_CALLBACK(tag, st, m, fd, ltype, ptr) \
|
||||||
|
{tag, PB_ATYPE_CALLBACK | PB_HTYPE_OPTIONAL | ltype, \
|
||||||
|
0, 0, pb_membersize(st, m), 0, ptr}
|
||||||
|
|
||||||
|
/* The mapping from protobuf types to LTYPEs is done using these macros. */
|
||||||
|
#define PB_LTYPE_MAP_BOOL PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES
|
||||||
|
#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64
|
||||||
|
#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32
|
||||||
|
#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64
|
||||||
|
#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32
|
||||||
|
#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT
|
||||||
|
#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE
|
||||||
|
#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32
|
||||||
|
#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64
|
||||||
|
#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT
|
||||||
|
#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT
|
||||||
|
#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING
|
||||||
|
#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT
|
||||||
|
#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT
|
||||||
|
#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION
|
||||||
|
|
||||||
|
/* This is the actual macro used in field descriptions.
|
||||||
|
* It takes these arguments:
|
||||||
|
* - Field tag number
|
||||||
|
* - Field type: BOOL, BYTES, DOUBLE, ENUM, FIXED32, FIXED64,
|
||||||
|
* FLOAT, INT32, INT64, MESSAGE, SFIXED32, SFIXED64
|
||||||
|
* SINT32, SINT64, STRING, UINT32, UINT64 or EXTENSION
|
||||||
|
* - Field rules: REQUIRED, OPTIONAL or REPEATED
|
||||||
|
* - Allocation: STATIC or CALLBACK
|
||||||
|
* - Message name
|
||||||
|
* - Field name
|
||||||
|
* - Previous field name (or field name again for first field)
|
||||||
|
* - Pointer to default value or submsg fields.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PB_FIELD(tag, type, rules, allocation, message, field, prevfield, ptr) \
|
||||||
|
PB_ ## rules ## _ ## allocation(tag, message, field, \
|
||||||
|
PB_DATAOFFSET_CHOOSE(message, field, prevfield), \
|
||||||
|
PB_LTYPE_MAP_ ## type, ptr)
|
||||||
|
|
||||||
|
/* This is a new version of the macro used by nanopb generator from
|
||||||
|
* version 0.2.3 onwards. It avoids the use of a ternary expression in
|
||||||
|
* the initialization, which confused some compilers.
|
||||||
|
*
|
||||||
|
* - Placement: FIRST or OTHER, depending on if this is the first field in structure.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define PB_FIELD2(tag, type, rules, allocation, placement, message, field, prevfield, ptr) \
|
||||||
|
PB_ ## rules ## _ ## allocation(tag, message, field, \
|
||||||
|
PB_DATAOFFSET_ ## placement(message, field, prevfield), \
|
||||||
|
PB_LTYPE_MAP_ ## type, ptr)
|
||||||
|
|
||||||
|
|
||||||
|
/* These macros are used for giving out error messages.
|
||||||
|
* They are mostly a debugging aid; the main error information
|
||||||
|
* is the true/false return value from functions.
|
||||||
|
* Some code space can be saved by disabling the error
|
||||||
|
* messages if not used.
|
||||||
|
*/
|
||||||
|
#ifdef PB_NO_ERRMSG
|
||||||
|
#define PB_RETURN_ERROR(stream,msg) \
|
||||||
|
do {\
|
||||||
|
UNUSED(stream); \
|
||||||
|
return false; \
|
||||||
|
} while(0)
|
||||||
|
#define PB_GET_ERROR(stream) "(errmsg disabled)"
|
||||||
|
#else
|
||||||
|
#define PB_RETURN_ERROR(stream,msg) \
|
||||||
|
do {\
|
||||||
|
if ((stream)->errmsg == NULL) \
|
||||||
|
(stream)->errmsg = (msg); \
|
||||||
|
return false; \
|
||||||
|
} while(0)
|
||||||
|
#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1178
firmware/protob/pb_decode.c
Normal file
149
firmware/protob/pb_decode.h
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c.
|
||||||
|
* The main function is pb_decode. You also need an input stream, and the
|
||||||
|
* field descriptions created by nanopb_generator.py.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PB_DECODE_H_
|
||||||
|
#define _PB_DECODE_H_
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Structure for defining custom input streams. You will need to provide
|
||||||
|
* a callback function to read the bytes from your storage, which can be
|
||||||
|
* for example a file or a network socket.
|
||||||
|
*
|
||||||
|
* The callback must conform to these rules:
|
||||||
|
*
|
||||||
|
* 1) Return false on IO errors. This will cause decoding to abort.
|
||||||
|
* 2) You can use state to store your own data (e.g. buffer pointer),
|
||||||
|
* and rely on pb_read to verify that no-body reads past bytes_left.
|
||||||
|
* 3) Your callback may be used with substreams, in which case bytes_left
|
||||||
|
* is different than from the main stream. Don't use bytes_left to compute
|
||||||
|
* any pointers.
|
||||||
|
*/
|
||||||
|
struct _pb_istream_t
|
||||||
|
{
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
/* Callback pointer is not used in buffer-only configuration.
|
||||||
|
* Having an int pointer here allows binary compatibility but
|
||||||
|
* gives an error if someone tries to assign callback function.
|
||||||
|
*/
|
||||||
|
int *callback;
|
||||||
|
#else
|
||||||
|
bool (*callback)(pb_istream_t *stream, uint8_t *buf, size_t count);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void *state; /* Free field for use by callback implementation */
|
||||||
|
size_t bytes_left;
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
const char *errmsg;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
* Main decoding functions *
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
/* Decode a single protocol buffers message from input stream into a C structure.
|
||||||
|
* Returns true on success, false on any failure.
|
||||||
|
* The actual struct pointed to by dest must match the description in fields.
|
||||||
|
* Callback fields of the destination structure must be initialized by caller.
|
||||||
|
* All other fields will be initialized by this function.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* MyMessage msg = {};
|
||||||
|
* uint8_t buffer[64];
|
||||||
|
* pb_istream_t stream;
|
||||||
|
*
|
||||||
|
* // ... read some data into buffer ...
|
||||||
|
*
|
||||||
|
* stream = pb_istream_from_buffer(buffer, count);
|
||||||
|
* pb_decode(&stream, MyMessage_fields, &msg);
|
||||||
|
*/
|
||||||
|
bool pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
|
||||||
|
|
||||||
|
/* Same as pb_decode, except does not initialize the destination structure
|
||||||
|
* to default values. This is slightly faster if you need no default values
|
||||||
|
* and just do memset(struct, 0, sizeof(struct)) yourself.
|
||||||
|
*
|
||||||
|
* This can also be used for 'merging' two messages, i.e. update only the
|
||||||
|
* fields that exist in the new message.
|
||||||
|
*
|
||||||
|
* Note: If this function returns with an error, it will not release any
|
||||||
|
* dynamically allocated fields. You will need to call pb_release() yourself.
|
||||||
|
*/
|
||||||
|
bool pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
|
||||||
|
|
||||||
|
/* Same as pb_decode, except expects the stream to start with the message size
|
||||||
|
* encoded as varint. Corresponds to parseDelimitedFrom() in Google's
|
||||||
|
* protobuf API.
|
||||||
|
*/
|
||||||
|
bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct);
|
||||||
|
|
||||||
|
#ifdef PB_ENABLE_MALLOC
|
||||||
|
/* Release any allocated pointer fields. If you use dynamic allocation, you should
|
||||||
|
* call this for any successfully decoded message when you are done with it. If
|
||||||
|
* pb_decode() returns with an error, the message is already released.
|
||||||
|
*/
|
||||||
|
void pb_release(const pb_field_t fields[], void *dest_struct);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Functions for manipulating streams *
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/* Create an input stream for reading from a memory buffer.
|
||||||
|
*
|
||||||
|
* Alternatively, you can use a custom stream that reads directly from e.g.
|
||||||
|
* a file or a network socket.
|
||||||
|
*/
|
||||||
|
pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize);
|
||||||
|
|
||||||
|
/* Function to read from a pb_istream_t. You can use this if you need to
|
||||||
|
* read some custom header data, or to read data in field callbacks.
|
||||||
|
*/
|
||||||
|
bool pb_read(pb_istream_t *stream, uint8_t *buf, size_t count);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Helper functions for writing field callbacks *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* Decode the tag for the next field in the stream. Gives the wire type and
|
||||||
|
* field tag. At end of the message, returns false and sets eof to true. */
|
||||||
|
bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof);
|
||||||
|
|
||||||
|
/* Skip the field payload data, given the wire type. */
|
||||||
|
bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type);
|
||||||
|
|
||||||
|
/* Decode an integer in the varint format. This works for bool, enum, int32,
|
||||||
|
* int64, uint32 and uint64 field types. */
|
||||||
|
bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest);
|
||||||
|
|
||||||
|
/* Decode an integer in the zig-zagged svarint format. This works for sint32
|
||||||
|
* and sint64. */
|
||||||
|
bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest);
|
||||||
|
|
||||||
|
/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to
|
||||||
|
* a 4-byte wide C variable. */
|
||||||
|
bool pb_decode_fixed32(pb_istream_t *stream, void *dest);
|
||||||
|
|
||||||
|
/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to
|
||||||
|
* a 8-byte wide C variable. */
|
||||||
|
bool pb_decode_fixed64(pb_istream_t *stream, void *dest);
|
||||||
|
|
||||||
|
/* Make a limited-length substream for reading a PB_WT_STRING field. */
|
||||||
|
bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream);
|
||||||
|
void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
667
firmware/protob/pb_encode.c
Normal file
@ -0,0 +1,667 @@
|
|||||||
|
/* pb_encode.c -- encode a protobuf using minimal resources
|
||||||
|
*
|
||||||
|
* 2011 Petteri Aimonen <jpa@kapsi.fi>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
#include "pb_encode.h"
|
||||||
|
|
||||||
|
/* Use the GCC warn_unused_result attribute to check that all return values
|
||||||
|
* are propagated correctly. On other compilers and gcc before 3.4.0 just
|
||||||
|
* ignore the annotation.
|
||||||
|
*/
|
||||||
|
#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
|
||||||
|
#define checkreturn
|
||||||
|
#else
|
||||||
|
#define checkreturn __attribute__((warn_unused_result))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Declarations internal to this file *
|
||||||
|
**************************************/
|
||||||
|
typedef bool (*pb_encoder_t)(pb_ostream_t *stream, const pb_field_t *field, const void *src) checkreturn;
|
||||||
|
|
||||||
|
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
||||||
|
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field, const void *pData, size_t count, pb_encoder_t func);
|
||||||
|
static bool checkreturn encode_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
|
||||||
|
static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension);
|
||||||
|
static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_t *field, const void *pData);
|
||||||
|
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src);
|
||||||
|
|
||||||
|
/* --- Function pointers to field encoders ---
|
||||||
|
* Order in the array must match pb_action_t LTYPE numbering.
|
||||||
|
*/
|
||||||
|
static const pb_encoder_t PB_ENCODERS[PB_LTYPES_COUNT] = {
|
||||||
|
&pb_enc_varint,
|
||||||
|
&pb_enc_uvarint,
|
||||||
|
&pb_enc_svarint,
|
||||||
|
&pb_enc_fixed32,
|
||||||
|
&pb_enc_fixed64,
|
||||||
|
|
||||||
|
&pb_enc_bytes,
|
||||||
|
&pb_enc_string,
|
||||||
|
&pb_enc_submessage,
|
||||||
|
NULL /* extensions */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*******************************
|
||||||
|
* pb_ostream_t implementation *
|
||||||
|
*******************************/
|
||||||
|
|
||||||
|
static bool checkreturn buf_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
||||||
|
{
|
||||||
|
uint8_t *dest = (uint8_t*)stream->state;
|
||||||
|
stream->state = dest + count;
|
||||||
|
|
||||||
|
while (count--)
|
||||||
|
*dest++ = *buf++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize)
|
||||||
|
{
|
||||||
|
pb_ostream_t stream;
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
stream.callback = (void*)1; /* Just a marker value */
|
||||||
|
#else
|
||||||
|
stream.callback = &buf_write;
|
||||||
|
#endif
|
||||||
|
stream.state = buf;
|
||||||
|
stream.max_size = bufsize;
|
||||||
|
stream.bytes_written = 0;
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
stream.errmsg = NULL;
|
||||||
|
#endif
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count)
|
||||||
|
{
|
||||||
|
if (stream->callback != NULL)
|
||||||
|
{
|
||||||
|
if (stream->bytes_written + count > stream->max_size)
|
||||||
|
PB_RETURN_ERROR(stream, "stream full");
|
||||||
|
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
if (!buf_write(stream, buf, count))
|
||||||
|
PB_RETURN_ERROR(stream, "io error");
|
||||||
|
#else
|
||||||
|
if (!stream->callback(stream, buf, count))
|
||||||
|
PB_RETURN_ERROR(stream, "io error");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->bytes_written += count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************
|
||||||
|
* Encode a single field *
|
||||||
|
*************************/
|
||||||
|
|
||||||
|
/* Encode a static array. Handles the size calculations and possible packing. */
|
||||||
|
static bool checkreturn encode_array(pb_ostream_t *stream, const pb_field_t *field,
|
||||||
|
const void *pData, size_t count, pb_encoder_t func)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
const void *p;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size)
|
||||||
|
PB_RETURN_ERROR(stream, "array max size exceeded");
|
||||||
|
|
||||||
|
/* We always pack arrays if the datatype allows it. */
|
||||||
|
if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE)
|
||||||
|
{
|
||||||
|
if (!pb_encode_tag(stream, PB_WT_STRING, field->tag))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Determine the total size of packed array. */
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32)
|
||||||
|
{
|
||||||
|
size = 4 * count;
|
||||||
|
}
|
||||||
|
else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64)
|
||||||
|
{
|
||||||
|
size = 8 * count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pb_ostream_t sizestream = PB_OSTREAM_SIZING;
|
||||||
|
p = pData;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!func(&sizestream, field, p))
|
||||||
|
return false;
|
||||||
|
p = (const char*)p + field->data_size;
|
||||||
|
}
|
||||||
|
size = sizestream.bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pb_encode_varint(stream, (uint64_t)size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stream->callback == NULL)
|
||||||
|
return pb_write(stream, NULL, size); /* Just sizing.. */
|
||||||
|
|
||||||
|
/* Write the data */
|
||||||
|
p = pData;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!func(stream, field, p))
|
||||||
|
return false;
|
||||||
|
p = (const char*)p + field->data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = pData;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Normally the data is stored directly in the array entries, but
|
||||||
|
* for pointer-type string and bytes fields, the array entries are
|
||||||
|
* actually pointers themselves also. So we have to dereference once
|
||||||
|
* more to get to the actual data. */
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER &&
|
||||||
|
(PB_LTYPE(field->type) == PB_LTYPE_STRING ||
|
||||||
|
PB_LTYPE(field->type) == PB_LTYPE_BYTES))
|
||||||
|
{
|
||||||
|
if (!func(stream, field, *(const void* const*)p))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!func(stream, field, p))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
p = (const char*)p + field->data_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a field with static or pointer allocation, i.e. one whose data
|
||||||
|
* is available to the encoder directly. */
|
||||||
|
static bool checkreturn encode_basic_field(pb_ostream_t *stream,
|
||||||
|
const pb_field_t *field, const void *pData)
|
||||||
|
{
|
||||||
|
pb_encoder_t func;
|
||||||
|
const void *pSize;
|
||||||
|
bool implicit_has = true;
|
||||||
|
|
||||||
|
func = PB_ENCODERS[PB_LTYPE(field->type)];
|
||||||
|
|
||||||
|
if (field->size_offset)
|
||||||
|
pSize = (const char*)pData + field->size_offset;
|
||||||
|
else
|
||||||
|
pSize = &implicit_has;
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
|
||||||
|
{
|
||||||
|
/* pData is a pointer to the field, which contains pointer to
|
||||||
|
* the data. If the 2nd pointer is NULL, it is interpreted as if
|
||||||
|
* the has_field was false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pData = *(const void* const*)pData;
|
||||||
|
implicit_has = (pData != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (PB_HTYPE(field->type))
|
||||||
|
{
|
||||||
|
case PB_HTYPE_REQUIRED:
|
||||||
|
if (!pData)
|
||||||
|
PB_RETURN_ERROR(stream, "missing required field");
|
||||||
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
|
return false;
|
||||||
|
if (!func(stream, field, pData))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_HTYPE_OPTIONAL:
|
||||||
|
if (*(const bool*)pSize)
|
||||||
|
{
|
||||||
|
if (!pb_encode_tag_for_field(stream, field))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!func(stream, field, pData))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_HTYPE_REPEATED:
|
||||||
|
if (!encode_array(stream, field, pData, *(const size_t*)pSize, func))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a field with callback semantics. This means that a user function is
|
||||||
|
* called to provide and encode the actual data. */
|
||||||
|
static bool checkreturn encode_callback_field(pb_ostream_t *stream,
|
||||||
|
const pb_field_t *field, const void *pData)
|
||||||
|
{
|
||||||
|
const pb_callback_t *callback = (const pb_callback_t*)pData;
|
||||||
|
|
||||||
|
#ifdef PB_OLD_CALLBACK_STYLE
|
||||||
|
const void *arg = callback->arg;
|
||||||
|
#else
|
||||||
|
void * const *arg = &(callback->arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (callback->funcs.encode != NULL)
|
||||||
|
{
|
||||||
|
if (!callback->funcs.encode(stream, field, arg))
|
||||||
|
PB_RETURN_ERROR(stream, "callback error");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Encode a single field of any callback or static type. */
|
||||||
|
static bool checkreturn encode_field(pb_ostream_t *stream,
|
||||||
|
const pb_field_t *field, const void *pData)
|
||||||
|
{
|
||||||
|
switch (PB_ATYPE(field->type))
|
||||||
|
{
|
||||||
|
case PB_ATYPE_STATIC:
|
||||||
|
case PB_ATYPE_POINTER:
|
||||||
|
return encode_basic_field(stream, field, pData);
|
||||||
|
|
||||||
|
case PB_ATYPE_CALLBACK:
|
||||||
|
return encode_callback_field(stream, field, pData);
|
||||||
|
|
||||||
|
default:
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default handler for extension fields. Expects to have a pb_field_t
|
||||||
|
* pointer in the extension->type->arg field. */
|
||||||
|
static bool checkreturn default_extension_encoder(pb_ostream_t *stream,
|
||||||
|
const pb_extension_t *extension)
|
||||||
|
{
|
||||||
|
const pb_field_t *field = (const pb_field_t*)extension->type->arg;
|
||||||
|
return encode_field(stream, field, extension->dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Walk through all the registered extensions and give them a chance
|
||||||
|
* to encode themselves. */
|
||||||
|
static bool checkreturn encode_extension_field(pb_ostream_t *stream,
|
||||||
|
const pb_field_t *field, const void *pData)
|
||||||
|
{
|
||||||
|
const pb_extension_t *extension = *(const pb_extension_t* const *)pData;
|
||||||
|
UNUSED(field);
|
||||||
|
|
||||||
|
while (extension)
|
||||||
|
{
|
||||||
|
bool status;
|
||||||
|
if (extension->type->encode)
|
||||||
|
status = extension->type->encode(stream, extension);
|
||||||
|
else
|
||||||
|
status = default_extension_encoder(stream, extension);
|
||||||
|
|
||||||
|
if (!status)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
extension = extension->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* Encode all fields *
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
bool checkreturn pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
|
||||||
|
{
|
||||||
|
const pb_field_t *field = fields;
|
||||||
|
const void *pData = src_struct;
|
||||||
|
size_t prev_size = 0;
|
||||||
|
|
||||||
|
while (field->tag != 0)
|
||||||
|
{
|
||||||
|
pData = (const char*)pData + prev_size + field->data_offset;
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
|
||||||
|
prev_size = sizeof(const void*);
|
||||||
|
else
|
||||||
|
prev_size = field->data_size;
|
||||||
|
|
||||||
|
/* Special case for static arrays */
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
|
||||||
|
PB_HTYPE(field->type) == PB_HTYPE_REPEATED)
|
||||||
|
{
|
||||||
|
prev_size *= field->array_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PB_LTYPE(field->type) == PB_LTYPE_EXTENSION)
|
||||||
|
{
|
||||||
|
/* Special case for the extension field placeholder */
|
||||||
|
if (!encode_extension_field(stream, field, pData))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Regular field */
|
||||||
|
if (!encode_field(stream, field, pData))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
field++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
|
||||||
|
{
|
||||||
|
return pb_encode_submessage(stream, fields, src_struct);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct)
|
||||||
|
{
|
||||||
|
pb_ostream_t stream = PB_OSTREAM_SIZING;
|
||||||
|
|
||||||
|
if (!pb_encode(&stream, fields, src_struct))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*size = stream.bytes_written;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Helper functions *
|
||||||
|
********************/
|
||||||
|
bool checkreturn pb_encode_varint(pb_ostream_t *stream, uint64_t value)
|
||||||
|
{
|
||||||
|
uint8_t buffer[10];
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
if (value == 0)
|
||||||
|
return pb_write(stream, (uint8_t*)&value, 1);
|
||||||
|
|
||||||
|
while (value)
|
||||||
|
{
|
||||||
|
buffer[i] = (uint8_t)((value & 0x7F) | 0x80);
|
||||||
|
value >>= 7;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
buffer[i-1] &= 0x7F; /* Unset top bit on last byte */
|
||||||
|
|
||||||
|
return pb_write(stream, buffer, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_svarint(pb_ostream_t *stream, int64_t value)
|
||||||
|
{
|
||||||
|
uint64_t zigzagged;
|
||||||
|
if (value < 0)
|
||||||
|
zigzagged = ~((uint64_t)value << 1);
|
||||||
|
else
|
||||||
|
zigzagged = (uint64_t)value << 1;
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, zigzagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value)
|
||||||
|
{
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
const uint8_t *bytes = value;
|
||||||
|
uint8_t lebytes[4];
|
||||||
|
lebytes[0] = bytes[3];
|
||||||
|
lebytes[1] = bytes[2];
|
||||||
|
lebytes[2] = bytes[1];
|
||||||
|
lebytes[3] = bytes[0];
|
||||||
|
return pb_write(stream, lebytes, 4);
|
||||||
|
#else
|
||||||
|
return pb_write(stream, (const uint8_t*)value, 4);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value)
|
||||||
|
{
|
||||||
|
#ifdef __BIG_ENDIAN__
|
||||||
|
const uint8_t *bytes = value;
|
||||||
|
uint8_t lebytes[8];
|
||||||
|
lebytes[0] = bytes[7];
|
||||||
|
lebytes[1] = bytes[6];
|
||||||
|
lebytes[2] = bytes[5];
|
||||||
|
lebytes[3] = bytes[4];
|
||||||
|
lebytes[4] = bytes[3];
|
||||||
|
lebytes[5] = bytes[2];
|
||||||
|
lebytes[6] = bytes[1];
|
||||||
|
lebytes[7] = bytes[0];
|
||||||
|
return pb_write(stream, lebytes, 8);
|
||||||
|
#else
|
||||||
|
return pb_write(stream, (const uint8_t*)value, 8);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number)
|
||||||
|
{
|
||||||
|
uint64_t tag = ((uint64_t)field_number << 3) | wiretype;
|
||||||
|
return pb_encode_varint(stream, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field)
|
||||||
|
{
|
||||||
|
pb_wire_type_t wiretype;
|
||||||
|
switch (PB_LTYPE(field->type))
|
||||||
|
{
|
||||||
|
case PB_LTYPE_VARINT:
|
||||||
|
case PB_LTYPE_UVARINT:
|
||||||
|
case PB_LTYPE_SVARINT:
|
||||||
|
wiretype = PB_WT_VARINT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_LTYPE_FIXED32:
|
||||||
|
wiretype = PB_WT_32BIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_LTYPE_FIXED64:
|
||||||
|
wiretype = PB_WT_64BIT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PB_LTYPE_BYTES:
|
||||||
|
case PB_LTYPE_STRING:
|
||||||
|
case PB_LTYPE_SUBMESSAGE:
|
||||||
|
wiretype = PB_WT_STRING;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field type");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_tag(stream, wiretype, field->tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size)
|
||||||
|
{
|
||||||
|
if (!pb_encode_varint(stream, (uint64_t)size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return pb_write(stream, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct)
|
||||||
|
{
|
||||||
|
/* First calculate the message size using a non-writing substream. */
|
||||||
|
pb_ostream_t substream = PB_OSTREAM_SIZING;
|
||||||
|
size_t size;
|
||||||
|
bool status;
|
||||||
|
|
||||||
|
if (!pb_encode(&substream, fields, src_struct))
|
||||||
|
{
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
stream->errmsg = substream.errmsg;
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = substream.bytes_written;
|
||||||
|
|
||||||
|
if (!pb_encode_varint(stream, (uint64_t)size))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (stream->callback == NULL)
|
||||||
|
return pb_write(stream, NULL, size); /* Just sizing */
|
||||||
|
|
||||||
|
if (stream->bytes_written + size > stream->max_size)
|
||||||
|
PB_RETURN_ERROR(stream, "stream full");
|
||||||
|
|
||||||
|
/* Use a substream to verify that a callback doesn't write more than
|
||||||
|
* what it did the first time. */
|
||||||
|
substream.callback = stream->callback;
|
||||||
|
substream.state = stream->state;
|
||||||
|
substream.max_size = size;
|
||||||
|
substream.bytes_written = 0;
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
substream.errmsg = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
status = pb_encode(&substream, fields, src_struct);
|
||||||
|
|
||||||
|
stream->bytes_written += substream.bytes_written;
|
||||||
|
stream->state = substream.state;
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
stream->errmsg = substream.errmsg;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (substream.bytes_written != size)
|
||||||
|
PB_RETURN_ERROR(stream, "submsg size changed");
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Field encoders */
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
int64_t value = 0;
|
||||||
|
|
||||||
|
/* Cases 1 and 2 are for compilers that have smaller types for bool
|
||||||
|
* or enums. */
|
||||||
|
switch (field->data_size)
|
||||||
|
{
|
||||||
|
case 1: value = *(const int8_t*)src; break;
|
||||||
|
case 2: value = *(const int16_t*)src; break;
|
||||||
|
case 4: value = *(const int32_t*)src; break;
|
||||||
|
case 8: value = *(const int64_t*)src; break;
|
||||||
|
default: PB_RETURN_ERROR(stream, "invalid data_size");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, (uint64_t)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_uvarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
switch (field->data_size)
|
||||||
|
{
|
||||||
|
case 4: value = *(const uint32_t*)src; break;
|
||||||
|
case 8: value = *(const uint64_t*)src; break;
|
||||||
|
default: PB_RETURN_ERROR(stream, "invalid data_size");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_varint(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_svarint(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
int64_t value = 0;
|
||||||
|
|
||||||
|
switch (field->data_size)
|
||||||
|
{
|
||||||
|
case 4: value = *(const int32_t*)src; break;
|
||||||
|
case 8: value = *(const int64_t*)src; break;
|
||||||
|
default: PB_RETURN_ERROR(stream, "invalid data_size");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_svarint(stream, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_fixed64(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
UNUSED(field);
|
||||||
|
return pb_encode_fixed64(stream, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_fixed32(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
UNUSED(field);
|
||||||
|
return pb_encode_fixed32(stream, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)src;
|
||||||
|
|
||||||
|
if (src == NULL)
|
||||||
|
{
|
||||||
|
/* Threat null pointer as an empty bytes field */
|
||||||
|
return pb_encode_string(stream, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_STATIC &&
|
||||||
|
PB_BYTES_ARRAY_T_ALLOCSIZE(bytes->size) > field->data_size)
|
||||||
|
{
|
||||||
|
PB_RETURN_ERROR(stream, "bytes size exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_string(stream, bytes->bytes, bytes->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
/* strnlen() is not always available, so just use a loop */
|
||||||
|
size_t size = 0;
|
||||||
|
size_t max_size = field->data_size;
|
||||||
|
const char *p = (const char*)src;
|
||||||
|
|
||||||
|
if (PB_ATYPE(field->type) == PB_ATYPE_POINTER)
|
||||||
|
max_size = (size_t)-1;
|
||||||
|
|
||||||
|
if (src == NULL)
|
||||||
|
{
|
||||||
|
size = 0; /* Threat null pointer as an empty string */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (size < max_size && *p != '\0')
|
||||||
|
{
|
||||||
|
size++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb_encode_string(stream, (const uint8_t*)src, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_t *field, const void *src)
|
||||||
|
{
|
||||||
|
if (field->ptr == NULL)
|
||||||
|
PB_RETURN_ERROR(stream, "invalid field descriptor");
|
||||||
|
|
||||||
|
return pb_encode_submessage(stream, (const pb_field_t*)field->ptr, src);
|
||||||
|
}
|
||||||
|
|
154
firmware/protob/pb_encode.h
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c.
|
||||||
|
* The main function is pb_encode. You also need an output stream, and the
|
||||||
|
* field descriptions created by nanopb_generator.py.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PB_ENCODE_H_
|
||||||
|
#define _PB_ENCODE_H_
|
||||||
|
|
||||||
|
#include "pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Structure for defining custom output streams. You will need to provide
|
||||||
|
* a callback function to write the bytes to your storage, which can be
|
||||||
|
* for example a file or a network socket.
|
||||||
|
*
|
||||||
|
* The callback must conform to these rules:
|
||||||
|
*
|
||||||
|
* 1) Return false on IO errors. This will cause encoding to abort.
|
||||||
|
* 2) You can use state to store your own data (e.g. buffer pointer).
|
||||||
|
* 3) pb_write will update bytes_written after your callback runs.
|
||||||
|
* 4) Substreams will modify max_size and bytes_written. Don't use them
|
||||||
|
* to calculate any pointers.
|
||||||
|
*/
|
||||||
|
struct _pb_ostream_t
|
||||||
|
{
|
||||||
|
#ifdef PB_BUFFER_ONLY
|
||||||
|
/* Callback pointer is not used in buffer-only configuration.
|
||||||
|
* Having an int pointer here allows binary compatibility but
|
||||||
|
* gives an error if someone tries to assign callback function.
|
||||||
|
* Also, NULL pointer marks a 'sizing stream' that does not
|
||||||
|
* write anything.
|
||||||
|
*/
|
||||||
|
int *callback;
|
||||||
|
#else
|
||||||
|
bool (*callback)(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
||||||
|
#endif
|
||||||
|
void *state; /* Free field for use by callback implementation. */
|
||||||
|
size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */
|
||||||
|
size_t bytes_written; /* Number of bytes written so far. */
|
||||||
|
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
const char *errmsg;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
* Main encoding functions *
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
/* Encode a single protocol buffers message from C structure into a stream.
|
||||||
|
* Returns true on success, false on any failure.
|
||||||
|
* The actual struct pointed to by src_struct must match the description in fields.
|
||||||
|
* All required fields in the struct are assumed to have been filled in.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* MyMessage msg = {};
|
||||||
|
* uint8_t buffer[64];
|
||||||
|
* pb_ostream_t stream;
|
||||||
|
*
|
||||||
|
* msg.field1 = 42;
|
||||||
|
* stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
|
||||||
|
* pb_encode(&stream, MyMessage_fields, &msg);
|
||||||
|
*/
|
||||||
|
bool pb_encode(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
|
/* Same as pb_encode, but prepends the length of the message as a varint.
|
||||||
|
* Corresponds to writeDelimitedTo() in Google's protobuf API.
|
||||||
|
*/
|
||||||
|
bool pb_encode_delimited(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
|
/* Encode the message to get the size of the encoded data, but do not store
|
||||||
|
* the data. */
|
||||||
|
bool pb_get_encoded_size(size_t *size, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
* Functions for manipulating streams *
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/* Create an output stream for writing into a memory buffer.
|
||||||
|
* The number of bytes written can be found in stream.bytes_written after
|
||||||
|
* encoding the message.
|
||||||
|
*
|
||||||
|
* Alternatively, you can use a custom stream that writes directly to e.g.
|
||||||
|
* a file or a network socket.
|
||||||
|
*/
|
||||||
|
pb_ostream_t pb_ostream_from_buffer(uint8_t *buf, size_t bufsize);
|
||||||
|
|
||||||
|
/* Pseudo-stream for measuring the size of a message without actually storing
|
||||||
|
* the encoded data.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* MyMessage msg = {};
|
||||||
|
* pb_ostream_t stream = PB_OSTREAM_SIZING;
|
||||||
|
* pb_encode(&stream, MyMessage_fields, &msg);
|
||||||
|
* printf("Message size is %d\n", stream.bytes_written);
|
||||||
|
*/
|
||||||
|
#ifndef PB_NO_ERRMSG
|
||||||
|
#define PB_OSTREAM_SIZING {0,0,0,0,0}
|
||||||
|
#else
|
||||||
|
#define PB_OSTREAM_SIZING {0,0,0,0}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Function to write into a pb_ostream_t stream. You can use this if you need
|
||||||
|
* to append or prepend some custom headers to the message.
|
||||||
|
*/
|
||||||
|
bool pb_write(pb_ostream_t *stream, const uint8_t *buf, size_t count);
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************
|
||||||
|
* Helper functions for writing field callbacks *
|
||||||
|
************************************************/
|
||||||
|
|
||||||
|
/* Encode field header based on type and field number defined in the field
|
||||||
|
* structure. Call this from the callback before writing out field contents. */
|
||||||
|
bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_t *field);
|
||||||
|
|
||||||
|
/* Encode field header by manually specifing wire type. You need to use this
|
||||||
|
* if you want to write out packed arrays from a callback field. */
|
||||||
|
bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number);
|
||||||
|
|
||||||
|
/* Encode an integer in the varint format.
|
||||||
|
* This works for bool, enum, int32, int64, uint32 and uint64 field types. */
|
||||||
|
bool pb_encode_varint(pb_ostream_t *stream, uint64_t value);
|
||||||
|
|
||||||
|
/* Encode an integer in the zig-zagged svarint format.
|
||||||
|
* This works for sint32 and sint64. */
|
||||||
|
bool pb_encode_svarint(pb_ostream_t *stream, int64_t value);
|
||||||
|
|
||||||
|
/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */
|
||||||
|
bool pb_encode_string(pb_ostream_t *stream, const uint8_t *buffer, size_t size);
|
||||||
|
|
||||||
|
/* Encode a fixed32, sfixed32 or float value.
|
||||||
|
* You need to pass a pointer to a 4-byte wide C variable. */
|
||||||
|
bool pb_encode_fixed32(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
/* Encode a fixed64, sfixed64 or double value.
|
||||||
|
* You need to pass a pointer to a 8-byte wide C variable. */
|
||||||
|
bool pb_encode_fixed64(pb_ostream_t *stream, const void *value);
|
||||||
|
|
||||||
|
/* Encode a submessage field.
|
||||||
|
* You need to pass the pb_field_t array and pointer to struct, just like
|
||||||
|
* with pb_encode(). This internally encodes the submessage twice, first to
|
||||||
|
* calculate message size and then to actually write it out.
|
||||||
|
*/
|
||||||
|
bool pb_encode_submessage(pb_ostream_t *stream, const pb_field_t fields[], const void *src_struct);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
4
firmware/protob/storage.options
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Storage.mnemonic max_size:241
|
||||||
|
Storage.pin max_size:10
|
||||||
|
Storage.language max_size:17
|
||||||
|
Storage.label max_size:33
|
44
firmware/protob/storage.pb.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* Automatically generated nanopb constant definitions */
|
||||||
|
/* Generated by nanopb-0.2.7 */
|
||||||
|
|
||||||
|
#include "storage.pb.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const pb_field_t Storage_fields[9] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, Storage, version, version, 0),
|
||||||
|
PB_FIELD2( 2, MESSAGE , OPTIONAL, STATIC , OTHER, Storage, node, version, &HDNodeType_fields),
|
||||||
|
PB_FIELD2( 3, STRING , OPTIONAL, STATIC , OTHER, Storage, mnemonic, node, 0),
|
||||||
|
PB_FIELD2( 4, BOOL , OPTIONAL, STATIC , OTHER, Storage, passphrase_protection, mnemonic, 0),
|
||||||
|
PB_FIELD2( 5, UINT32 , OPTIONAL, STATIC , OTHER, Storage, pin_failed_attempts, passphrase_protection, 0),
|
||||||
|
PB_FIELD2( 6, STRING , OPTIONAL, STATIC , OTHER, Storage, pin, pin_failed_attempts, 0),
|
||||||
|
PB_FIELD2( 7, STRING , OPTIONAL, STATIC , OTHER, Storage, language, pin, 0),
|
||||||
|
PB_FIELD2( 8, STRING , OPTIONAL, STATIC , OTHER, Storage, label, language, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Check that field information fits in pb_field_t */
|
||||||
|
#if !defined(PB_FIELD_32BIT)
|
||||||
|
/* If you get an error here, it means that you need to define PB_FIELD_32BIT
|
||||||
|
* compile-time option. You can do that in pb.h or on compiler command line.
|
||||||
|
*
|
||||||
|
* The reason you need to do this is that some of your messages contain tag
|
||||||
|
* numbers or field sizes that are larger than what can fit in 8 or 16 bit
|
||||||
|
* field descriptors.
|
||||||
|
*/
|
||||||
|
STATIC_ASSERT((pb_membersize(Storage, node) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_Storage)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
|
||||||
|
/* If you get an error here, it means that you need to define PB_FIELD_16BIT
|
||||||
|
* compile-time option. You can do that in pb.h or on compiler command line.
|
||||||
|
*
|
||||||
|
* The reason you need to do this is that some of your messages contain tag
|
||||||
|
* numbers or field sizes that are larger than what can fit in the default
|
||||||
|
* 8 bit descriptors.
|
||||||
|
*/
|
||||||
|
STATIC_ASSERT((pb_membersize(Storage, node) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_Storage)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
55
firmware/protob/storage.pb.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* Automatically generated nanopb header */
|
||||||
|
/* Generated by nanopb-0.2.7 */
|
||||||
|
|
||||||
|
#ifndef _PB_STORAGE_PB_H_
|
||||||
|
#define _PB_STORAGE_PB_H_
|
||||||
|
#include "pb.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Enum definitions */
|
||||||
|
/* Struct definitions */
|
||||||
|
typedef struct _Storage {
|
||||||
|
uint32_t version;
|
||||||
|
bool has_node;
|
||||||
|
HDNodeType node;
|
||||||
|
bool has_mnemonic;
|
||||||
|
char mnemonic[241];
|
||||||
|
bool has_passphrase_protection;
|
||||||
|
bool passphrase_protection;
|
||||||
|
bool has_pin_failed_attempts;
|
||||||
|
uint32_t pin_failed_attempts;
|
||||||
|
bool has_pin;
|
||||||
|
char pin[10];
|
||||||
|
bool has_language;
|
||||||
|
char language[17];
|
||||||
|
bool has_label;
|
||||||
|
char label[33];
|
||||||
|
} Storage;
|
||||||
|
|
||||||
|
/* Default values for struct fields */
|
||||||
|
|
||||||
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
|
#define Storage_version_tag 1
|
||||||
|
#define Storage_node_tag 2
|
||||||
|
#define Storage_mnemonic_tag 3
|
||||||
|
#define Storage_passphrase_protection_tag 4
|
||||||
|
#define Storage_pin_failed_attempts_tag 5
|
||||||
|
#define Storage_pin_tag 6
|
||||||
|
#define Storage_language_tag 7
|
||||||
|
#define Storage_label_tag 8
|
||||||
|
|
||||||
|
/* Struct field encoding specification for nanopb */
|
||||||
|
extern const pb_field_t Storage_fields[9];
|
||||||
|
|
||||||
|
/* Maximum encoded size of messages (where known) */
|
||||||
|
#define Storage_size (330 + HDNodeType_size)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1
firmware/protob/storage.proto
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../trezor-common/protob/storage.proto
|
26
firmware/protob/types.options
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
HDNodeType.chain_code max_size:32
|
||||||
|
HDNodeType.private_key max_size:32
|
||||||
|
HDNodeType.public_key max_size:33
|
||||||
|
|
||||||
|
CoinType.coin_name max_size:17
|
||||||
|
CoinType.coin_shortcut max_size:9
|
||||||
|
|
||||||
|
TxInputType.address_n max_count:8
|
||||||
|
TxInputType.prev_hash max_size:32
|
||||||
|
TxInputType.script_sig max_size:256
|
||||||
|
|
||||||
|
TxOutputType.address max_size:35
|
||||||
|
TxOutputType.address_n max_count:8
|
||||||
|
TxOutputType.script_args max_count:3
|
||||||
|
TxOutputType.script_args max_size:16
|
||||||
|
|
||||||
|
TxOutputBinType.script_pubkey max_size:256
|
||||||
|
|
||||||
|
TransactionType.inputs max_count:8
|
||||||
|
TransactionType.bin_outputs max_count:4
|
||||||
|
TransactionType.outputs max_count:4
|
||||||
|
|
||||||
|
TxRequestDetailsType.tx_hash max_size:32
|
||||||
|
|
||||||
|
TxRequestSerializedType.signature max_size:80
|
||||||
|
TxRequestSerializedType.serialized_tx max_size:1024
|
144
firmware/protob/types.pb.c
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/* Automatically generated nanopb constant definitions */
|
||||||
|
/* Generated by nanopb-0.2.7 */
|
||||||
|
|
||||||
|
#include "types.pb.h"
|
||||||
|
|
||||||
|
const uint32_t TxInputType_sequence_default = 4294967295u;
|
||||||
|
|
||||||
|
|
||||||
|
const pb_field_t HDNodeType_fields[7] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REQUIRED, STATIC , FIRST, HDNodeType, depth, depth, 0),
|
||||||
|
PB_FIELD2( 2, UINT32 , REQUIRED, STATIC , OTHER, HDNodeType, fingerprint, depth, 0),
|
||||||
|
PB_FIELD2( 3, UINT32 , REQUIRED, STATIC , OTHER, HDNodeType, child_num, fingerprint, 0),
|
||||||
|
PB_FIELD2( 4, BYTES , REQUIRED, STATIC , OTHER, HDNodeType, chain_code, child_num, 0),
|
||||||
|
PB_FIELD2( 5, BYTES , OPTIONAL, STATIC , OTHER, HDNodeType, private_key, chain_code, 0),
|
||||||
|
PB_FIELD2( 6, BYTES , OPTIONAL, STATIC , OTHER, HDNodeType, public_key, private_key, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t CoinType_fields[5] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, CoinType, coin_name, coin_name, 0),
|
||||||
|
PB_FIELD2( 2, STRING , OPTIONAL, STATIC , OTHER, CoinType, coin_shortcut, coin_name, 0),
|
||||||
|
PB_FIELD2( 3, UINT32 , OPTIONAL, STATIC , OTHER, CoinType, address_type, coin_shortcut, 0),
|
||||||
|
PB_FIELD2( 4, UINT64 , OPTIONAL, STATIC , OTHER, CoinType, maxfee_kb, address_type, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxInputType_fields[6] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , REPEATED, STATIC , FIRST, TxInputType, address_n, address_n, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , REQUIRED, STATIC , OTHER, TxInputType, prev_hash, address_n, 0),
|
||||||
|
PB_FIELD2( 3, UINT32 , REQUIRED, STATIC , OTHER, TxInputType, prev_index, prev_hash, 0),
|
||||||
|
PB_FIELD2( 4, BYTES , OPTIONAL, STATIC , OTHER, TxInputType, script_sig, prev_index, 0),
|
||||||
|
PB_FIELD2( 5, UINT32 , OPTIONAL, STATIC , OTHER, TxInputType, sequence, script_sig, &TxInputType_sequence_default),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxOutputType_fields[6] = {
|
||||||
|
PB_FIELD2( 1, STRING , OPTIONAL, STATIC , FIRST, TxOutputType, address, address, 0),
|
||||||
|
PB_FIELD2( 2, UINT32 , REPEATED, STATIC , OTHER, TxOutputType, address_n, address, 0),
|
||||||
|
PB_FIELD2( 3, UINT64 , REQUIRED, STATIC , OTHER, TxOutputType, amount, address_n, 0),
|
||||||
|
PB_FIELD2( 4, ENUM , REQUIRED, STATIC , OTHER, TxOutputType, script_type, amount, 0),
|
||||||
|
PB_FIELD2( 5, BYTES , REPEATED, STATIC , OTHER, TxOutputType, script_args, script_type, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxOutputBinType_fields[3] = {
|
||||||
|
PB_FIELD2( 1, UINT64 , REQUIRED, STATIC , FIRST, TxOutputBinType, amount, amount, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , REQUIRED, STATIC , OTHER, TxOutputBinType, script_pubkey, amount, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TransactionType_fields[8] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, TransactionType, version, version, 0),
|
||||||
|
PB_FIELD2( 2, MESSAGE , REPEATED, STATIC , OTHER, TransactionType, inputs, version, &TxInputType_fields),
|
||||||
|
PB_FIELD2( 3, MESSAGE , REPEATED, STATIC , OTHER, TransactionType, bin_outputs, inputs, &TxOutputBinType_fields),
|
||||||
|
PB_FIELD2( 4, UINT32 , OPTIONAL, STATIC , OTHER, TransactionType, lock_time, bin_outputs, 0),
|
||||||
|
PB_FIELD2( 5, MESSAGE , REPEATED, STATIC , OTHER, TransactionType, outputs, lock_time, &TxOutputType_fields),
|
||||||
|
PB_FIELD2( 6, UINT32 , OPTIONAL, STATIC , OTHER, TransactionType, inputs_cnt, outputs, 0),
|
||||||
|
PB_FIELD2( 7, UINT32 , OPTIONAL, STATIC , OTHER, TransactionType, outputs_cnt, inputs_cnt, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxRequestDetailsType_fields[3] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, TxRequestDetailsType, request_index, request_index, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, TxRequestDetailsType, tx_hash, request_index, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
const pb_field_t TxRequestSerializedType_fields[4] = {
|
||||||
|
PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, TxRequestSerializedType, signature_index, signature_index, 0),
|
||||||
|
PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, TxRequestSerializedType, signature, signature_index, 0),
|
||||||
|
PB_FIELD2( 3, BYTES , OPTIONAL, STATIC , OTHER, TxRequestSerializedType, serialized_tx, signature, 0),
|
||||||
|
PB_LAST_FIELD
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool wire_in;
|
||||||
|
} wire_in_struct;
|
||||||
|
|
||||||
|
static const pb_field_t wire_in_field =
|
||||||
|
PB_FIELD2(50002, BOOL , OPTEXT, STATIC , FIRST, wire_in_struct, wire_in, wire_in, 0);
|
||||||
|
|
||||||
|
const pb_extension_type_t wire_in = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&wire_in_field
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool wire_out;
|
||||||
|
} wire_out_struct;
|
||||||
|
|
||||||
|
static const pb_field_t wire_out_field =
|
||||||
|
PB_FIELD2(50003, BOOL , OPTEXT, STATIC , FIRST, wire_out_struct, wire_out, wire_out, 0);
|
||||||
|
|
||||||
|
const pb_extension_type_t wire_out = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&wire_out_field
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool wire_debug_in;
|
||||||
|
} wire_debug_in_struct;
|
||||||
|
|
||||||
|
static const pb_field_t wire_debug_in_field =
|
||||||
|
PB_FIELD2(50004, BOOL , OPTEXT, STATIC , FIRST, wire_debug_in_struct, wire_debug_in, wire_debug_in, 0);
|
||||||
|
|
||||||
|
const pb_extension_type_t wire_debug_in = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&wire_debug_in_field
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool wire_debug_out;
|
||||||
|
} wire_debug_out_struct;
|
||||||
|
|
||||||
|
static const pb_field_t wire_debug_out_field =
|
||||||
|
PB_FIELD2(50005, BOOL , OPTEXT, STATIC , FIRST, wire_debug_out_struct, wire_debug_out, wire_debug_out, 0);
|
||||||
|
|
||||||
|
const pb_extension_type_t wire_debug_out = {
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
&wire_debug_out_field
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Check that field information fits in pb_field_t */
|
||||||
|
#if !defined(PB_FIELD_32BIT)
|
||||||
|
/* If you get an error here, it means that you need to define PB_FIELD_32BIT
|
||||||
|
* compile-time option. You can do that in pb.h or on compiler command line.
|
||||||
|
*
|
||||||
|
* The reason you need to do this is that some of your messages contain tag
|
||||||
|
* numbers or field sizes that are larger than what can fit in 8 or 16 bit
|
||||||
|
* field descriptors.
|
||||||
|
*/
|
||||||
|
STATIC_ASSERT((pb_membersize(TransactionType, inputs[0]) < 65536 && pb_membersize(TransactionType, bin_outputs[0]) < 65536 && pb_membersize(TransactionType, outputs[0]) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_HDNodeType_CoinType_TxInputType_TxOutputType_TxOutputBinType_TransactionType_TxRequestDetailsType_TxRequestSerializedType)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT)
|
||||||
|
#error Field descriptor for TxRequestSerializedType.serialized_tx is too large. Define PB_FIELD_16BIT to fix this.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
262
firmware/protob/types.pb.h
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
/* Automatically generated nanopb header */
|
||||||
|
/* Generated by nanopb-0.2.7 */
|
||||||
|
|
||||||
|
#ifndef _PB_TYPES_PB_H_
|
||||||
|
#define _PB_TYPES_PB_H_
|
||||||
|
#include "pb.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Enum definitions */
|
||||||
|
typedef enum _FailureType {
|
||||||
|
FailureType_Failure_UnexpectedMessage = 1,
|
||||||
|
FailureType_Failure_ButtonExpected = 2,
|
||||||
|
FailureType_Failure_SyntaxError = 3,
|
||||||
|
FailureType_Failure_ActionCancelled = 4,
|
||||||
|
FailureType_Failure_PinExpected = 5,
|
||||||
|
FailureType_Failure_PinCancelled = 6,
|
||||||
|
FailureType_Failure_PinInvalid = 7,
|
||||||
|
FailureType_Failure_InvalidSignature = 8,
|
||||||
|
FailureType_Failure_Other = 9,
|
||||||
|
FailureType_Failure_NotEnoughFunds = 10,
|
||||||
|
FailureType_Failure_NotInitialized = 11,
|
||||||
|
FailureType_Failure_FirmwareError = 99
|
||||||
|
} FailureType;
|
||||||
|
|
||||||
|
typedef enum _ScriptType {
|
||||||
|
ScriptType_PAYTOADDRESS = 0,
|
||||||
|
ScriptType_PAYTOSCRIPTHASH = 1
|
||||||
|
} ScriptType;
|
||||||
|
|
||||||
|
typedef enum _RequestType {
|
||||||
|
RequestType_TXINPUT = 0,
|
||||||
|
RequestType_TXOUTPUT = 1,
|
||||||
|
RequestType_TXMETA = 2,
|
||||||
|
RequestType_TXFINISHED = 3
|
||||||
|
} RequestType;
|
||||||
|
|
||||||
|
typedef enum _ButtonRequestType {
|
||||||
|
ButtonRequestType_ButtonRequest_Other = 1,
|
||||||
|
ButtonRequestType_ButtonRequest_FeeOverThreshold = 2,
|
||||||
|
ButtonRequestType_ButtonRequest_ConfirmOutput = 3,
|
||||||
|
ButtonRequestType_ButtonRequest_ResetDevice = 4,
|
||||||
|
ButtonRequestType_ButtonRequest_ConfirmWord = 5,
|
||||||
|
ButtonRequestType_ButtonRequest_WipeDevice = 6,
|
||||||
|
ButtonRequestType_ButtonRequest_ProtectCall = 7,
|
||||||
|
ButtonRequestType_ButtonRequest_SignTx = 8
|
||||||
|
} ButtonRequestType;
|
||||||
|
|
||||||
|
typedef enum _PinMatrixRequestType {
|
||||||
|
PinMatrixRequestType_PinMatrixRequestType_Current = 1,
|
||||||
|
PinMatrixRequestType_PinMatrixRequestType_NewFirst = 2,
|
||||||
|
PinMatrixRequestType_PinMatrixRequestType_NewSecond = 3
|
||||||
|
} PinMatrixRequestType;
|
||||||
|
|
||||||
|
/* Struct definitions */
|
||||||
|
typedef struct _CoinType {
|
||||||
|
bool has_coin_name;
|
||||||
|
char coin_name[17];
|
||||||
|
bool has_coin_shortcut;
|
||||||
|
char coin_shortcut[9];
|
||||||
|
bool has_address_type;
|
||||||
|
uint32_t address_type;
|
||||||
|
bool has_maxfee_kb;
|
||||||
|
uint64_t maxfee_kb;
|
||||||
|
} CoinType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[32];
|
||||||
|
} HDNodeType_chain_code_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[32];
|
||||||
|
} HDNodeType_private_key_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[33];
|
||||||
|
} HDNodeType_public_key_t;
|
||||||
|
|
||||||
|
typedef struct _HDNodeType {
|
||||||
|
uint32_t depth;
|
||||||
|
uint32_t fingerprint;
|
||||||
|
uint32_t child_num;
|
||||||
|
HDNodeType_chain_code_t chain_code;
|
||||||
|
bool has_private_key;
|
||||||
|
HDNodeType_private_key_t private_key;
|
||||||
|
bool has_public_key;
|
||||||
|
HDNodeType_public_key_t public_key;
|
||||||
|
} HDNodeType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[32];
|
||||||
|
} TxInputType_prev_hash_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[256];
|
||||||
|
} TxInputType_script_sig_t;
|
||||||
|
|
||||||
|
typedef struct _TxInputType {
|
||||||
|
size_t address_n_count;
|
||||||
|
uint32_t address_n[8];
|
||||||
|
TxInputType_prev_hash_t prev_hash;
|
||||||
|
uint32_t prev_index;
|
||||||
|
bool has_script_sig;
|
||||||
|
TxInputType_script_sig_t script_sig;
|
||||||
|
bool has_sequence;
|
||||||
|
uint32_t sequence;
|
||||||
|
} TxInputType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[256];
|
||||||
|
} TxOutputBinType_script_pubkey_t;
|
||||||
|
|
||||||
|
typedef struct _TxOutputBinType {
|
||||||
|
uint64_t amount;
|
||||||
|
TxOutputBinType_script_pubkey_t script_pubkey;
|
||||||
|
} TxOutputBinType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[16];
|
||||||
|
} TxOutputType_script_args_t;
|
||||||
|
|
||||||
|
typedef struct _TxOutputType {
|
||||||
|
bool has_address;
|
||||||
|
char address[35];
|
||||||
|
size_t address_n_count;
|
||||||
|
uint32_t address_n[8];
|
||||||
|
uint64_t amount;
|
||||||
|
ScriptType script_type;
|
||||||
|
size_t script_args_count;
|
||||||
|
TxOutputType_script_args_t script_args[3];
|
||||||
|
} TxOutputType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[32];
|
||||||
|
} TxRequestDetailsType_tx_hash_t;
|
||||||
|
|
||||||
|
typedef struct _TxRequestDetailsType {
|
||||||
|
bool has_request_index;
|
||||||
|
uint32_t request_index;
|
||||||
|
bool has_tx_hash;
|
||||||
|
TxRequestDetailsType_tx_hash_t tx_hash;
|
||||||
|
} TxRequestDetailsType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[80];
|
||||||
|
} TxRequestSerializedType_signature_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
size_t size;
|
||||||
|
uint8_t bytes[1024];
|
||||||
|
} TxRequestSerializedType_serialized_tx_t;
|
||||||
|
|
||||||
|
typedef struct _TxRequestSerializedType {
|
||||||
|
bool has_signature_index;
|
||||||
|
uint32_t signature_index;
|
||||||
|
bool has_signature;
|
||||||
|
TxRequestSerializedType_signature_t signature;
|
||||||
|
bool has_serialized_tx;
|
||||||
|
TxRequestSerializedType_serialized_tx_t serialized_tx;
|
||||||
|
} TxRequestSerializedType;
|
||||||
|
|
||||||
|
typedef struct _TransactionType {
|
||||||
|
bool has_version;
|
||||||
|
uint32_t version;
|
||||||
|
size_t inputs_count;
|
||||||
|
TxInputType inputs[8];
|
||||||
|
size_t bin_outputs_count;
|
||||||
|
TxOutputBinType bin_outputs[4];
|
||||||
|
bool has_lock_time;
|
||||||
|
uint32_t lock_time;
|
||||||
|
size_t outputs_count;
|
||||||
|
TxOutputType outputs[4];
|
||||||
|
bool has_inputs_cnt;
|
||||||
|
uint32_t inputs_cnt;
|
||||||
|
bool has_outputs_cnt;
|
||||||
|
uint32_t outputs_cnt;
|
||||||
|
} TransactionType;
|
||||||
|
|
||||||
|
/* Extensions */
|
||||||
|
extern const pb_extension_type_t wire_in;
|
||||||
|
extern const pb_extension_type_t wire_out;
|
||||||
|
extern const pb_extension_type_t wire_debug_in;
|
||||||
|
extern const pb_extension_type_t wire_debug_out;
|
||||||
|
|
||||||
|
/* Default values for struct fields */
|
||||||
|
extern const uint32_t TxInputType_sequence_default;
|
||||||
|
|
||||||
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
|
#define CoinType_coin_name_tag 1
|
||||||
|
#define CoinType_coin_shortcut_tag 2
|
||||||
|
#define CoinType_address_type_tag 3
|
||||||
|
#define CoinType_maxfee_kb_tag 4
|
||||||
|
#define HDNodeType_depth_tag 1
|
||||||
|
#define HDNodeType_fingerprint_tag 2
|
||||||
|
#define HDNodeType_child_num_tag 3
|
||||||
|
#define HDNodeType_chain_code_tag 4
|
||||||
|
#define HDNodeType_private_key_tag 5
|
||||||
|
#define HDNodeType_public_key_tag 6
|
||||||
|
#define TxInputType_address_n_tag 1
|
||||||
|
#define TxInputType_prev_hash_tag 2
|
||||||
|
#define TxInputType_prev_index_tag 3
|
||||||
|
#define TxInputType_script_sig_tag 4
|
||||||
|
#define TxInputType_sequence_tag 5
|
||||||
|
#define TxOutputBinType_amount_tag 1
|
||||||
|
#define TxOutputBinType_script_pubkey_tag 2
|
||||||
|
#define TxOutputType_address_tag 1
|
||||||
|
#define TxOutputType_address_n_tag 2
|
||||||
|
#define TxOutputType_amount_tag 3
|
||||||
|
#define TxOutputType_script_type_tag 4
|
||||||
|
#define TxOutputType_script_args_tag 5
|
||||||
|
#define TxRequestDetailsType_request_index_tag 1
|
||||||
|
#define TxRequestDetailsType_tx_hash_tag 2
|
||||||
|
#define TxRequestSerializedType_signature_index_tag 1
|
||||||
|
#define TxRequestSerializedType_signature_tag 2
|
||||||
|
#define TxRequestSerializedType_serialized_tx_tag 3
|
||||||
|
#define TransactionType_version_tag 1
|
||||||
|
#define TransactionType_inputs_tag 2
|
||||||
|
#define TransactionType_bin_outputs_tag 3
|
||||||
|
#define TransactionType_outputs_tag 5
|
||||||
|
#define TransactionType_lock_time_tag 4
|
||||||
|
#define TransactionType_inputs_cnt_tag 6
|
||||||
|
#define TransactionType_outputs_cnt_tag 7
|
||||||
|
#define wire_in_tag 50002
|
||||||
|
#define wire_out_tag 50003
|
||||||
|
#define wire_debug_in_tag 50004
|
||||||
|
#define wire_debug_out_tag 50005
|
||||||
|
|
||||||
|
/* Struct field encoding specification for nanopb */
|
||||||
|
extern const pb_field_t HDNodeType_fields[7];
|
||||||
|
extern const pb_field_t CoinType_fields[5];
|
||||||
|
extern const pb_field_t TxInputType_fields[6];
|
||||||
|
extern const pb_field_t TxOutputType_fields[6];
|
||||||
|
extern const pb_field_t TxOutputBinType_fields[3];
|
||||||
|
extern const pb_field_t TransactionType_fields[8];
|
||||||
|
extern const pb_field_t TxRequestDetailsType_fields[3];
|
||||||
|
extern const pb_field_t TxRequestSerializedType_fields[4];
|
||||||
|
|
||||||
|
/* Maximum encoded size of messages (where known) */
|
||||||
|
#define HDNodeType_size 121
|
||||||
|
#define CoinType_size 47
|
||||||
|
#define TxInputType_size 353
|
||||||
|
#define TxOutputType_size 156
|
||||||
|
#define TxOutputBinType_size 270
|
||||||
|
#define TransactionType_size 4600
|
||||||
|
#define TxRequestDetailsType_size 40
|
||||||
|
#define TxRequestSerializedType_size 1115
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
1
firmware/protob/types.proto
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../trezor-common/protob/types.proto
|
179
firmware/recovery.c
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "recovery.h"
|
||||||
|
#include "fsm.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "protect.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "rng.h"
|
||||||
|
#include "bip39.h"
|
||||||
|
|
||||||
|
static uint32_t word_count;
|
||||||
|
static bool awaiting_word = false;
|
||||||
|
static bool enforce_wordlist;
|
||||||
|
static char fake_word[12];
|
||||||
|
static uint32_t word_pos;
|
||||||
|
static uint32_t word_index;
|
||||||
|
static char word_order[36];
|
||||||
|
static char words[24][12];
|
||||||
|
|
||||||
|
void next_word(void) {
|
||||||
|
word_pos = word_order[word_index];
|
||||||
|
if (word_pos == 0) {
|
||||||
|
const char **wl = mnemonic_wordlist();
|
||||||
|
strlcpy(fake_word, wl[random32() & 0x7FF], sizeof(fake_word));
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Please enter the word", NULL, fake_word, NULL, "on your computer", NULL);
|
||||||
|
} else {
|
||||||
|
fake_word[0] = 0;
|
||||||
|
char descbuf[] = "__. word";
|
||||||
|
char *desc = descbuf;
|
||||||
|
if (word_pos < 10) {
|
||||||
|
desc++;
|
||||||
|
} else {
|
||||||
|
descbuf[0] = '0' + word_pos / 10;
|
||||||
|
}
|
||||||
|
descbuf[1] = '0' + word_pos % 10;
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, NULL, NULL, "Please enter the", NULL, desc, NULL, "of your mnemonic", NULL);
|
||||||
|
}
|
||||||
|
WordRequest resp;
|
||||||
|
memset(&resp, 0, sizeof(WordRequest));
|
||||||
|
msg_write(MessageType_MessageType_WordRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist)
|
||||||
|
{
|
||||||
|
if (_word_count != 12 && _word_count != 18 && _word_count != 24) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid word count (has to be 12, 18 or 24 bits)");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
word_count = _word_count;
|
||||||
|
enforce_wordlist = _enforce_wordlist;
|
||||||
|
|
||||||
|
if (pin_protection && !protectChangePin()) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "PIN change failed");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.has_passphrase_protection = true;
|
||||||
|
storage.passphrase_protection = passphrase_protection;
|
||||||
|
storage_setLanguage(language);
|
||||||
|
storage_setLabel(label);
|
||||||
|
|
||||||
|
uint32_t i, j, k;
|
||||||
|
char t;
|
||||||
|
for (i = 0; i < word_count; i++) {
|
||||||
|
word_order[i] = i + 1;
|
||||||
|
}
|
||||||
|
for (i = word_count; i < word_count + word_count / 2; i++) {
|
||||||
|
word_order[i] = 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < 10000; i++) {
|
||||||
|
j = random32() % (word_count + word_count / 2);
|
||||||
|
k = random32() % (word_count + word_count / 2);
|
||||||
|
t = word_order[j];
|
||||||
|
word_order[j] = word_order[k];
|
||||||
|
word_order[k] = t;
|
||||||
|
}
|
||||||
|
awaiting_word = true;
|
||||||
|
word_index = 0;
|
||||||
|
next_word();
|
||||||
|
}
|
||||||
|
|
||||||
|
void recovery_word(const char *word)
|
||||||
|
{
|
||||||
|
if (!awaiting_word) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Recovery mode");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (word_pos == 0) { // fake word
|
||||||
|
if (strcmp(word, fake_word) != 0) {
|
||||||
|
storage_reset();
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "Wrong word retyped");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else { // real word
|
||||||
|
if (enforce_wordlist) { // check if word is valid
|
||||||
|
const char **wl = mnemonic_wordlist();
|
||||||
|
bool found = false;
|
||||||
|
while (*wl) {
|
||||||
|
if (strcmp(word, *wl) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wl++;
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
storage_reset();
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "Word not found in a wordlist");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strlcpy(words[word_pos - 1], word, sizeof(words[word_pos - 1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (word_index + 1 == word_count + word_count / 2) { // last one
|
||||||
|
uint32_t i;
|
||||||
|
strlcpy(storage.mnemonic, words[0], sizeof(storage.mnemonic));
|
||||||
|
for (i = 1; i < word_count; i++) {
|
||||||
|
strlcat(storage.mnemonic, " ", sizeof(storage.mnemonic));
|
||||||
|
strlcat(storage.mnemonic, words[i], sizeof(storage.mnemonic));
|
||||||
|
}
|
||||||
|
if (!enforce_wordlist || mnemonic_check(storage.mnemonic)) {
|
||||||
|
storage.has_mnemonic = true;
|
||||||
|
storage_commit();
|
||||||
|
fsm_sendSuccess("Device recovered");
|
||||||
|
} else {
|
||||||
|
storage_reset();
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid mnemonic, are words in correct order?");
|
||||||
|
}
|
||||||
|
awaiting_word = false;
|
||||||
|
layoutHome();
|
||||||
|
} else {
|
||||||
|
word_index++;
|
||||||
|
next_word();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void recovery_abort(void)
|
||||||
|
{
|
||||||
|
if (awaiting_word) {
|
||||||
|
layoutHome();
|
||||||
|
awaiting_word = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *recovery_get_fake_word(void)
|
||||||
|
{
|
||||||
|
return fake_word;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t recovery_get_word_pos(void)
|
||||||
|
{
|
||||||
|
return word_pos;
|
||||||
|
}
|
32
firmware/recovery.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __RECOVERY_H__
|
||||||
|
#define __RECOVERY_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
void recovery_init(uint32_t _word_count, bool passphrase_protection, bool pin_protection, const char *language, const char *label, bool _enforce_wordlist);
|
||||||
|
void recovery_word(const char *word);
|
||||||
|
void recovery_abort(void);
|
||||||
|
const char *recovery_get_fake_word(void);
|
||||||
|
uint32_t recovery_get_word_pos(void);
|
||||||
|
|
||||||
|
#endif
|
151
firmware/reset.c
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "reset.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "rng.h"
|
||||||
|
#include "sha2.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "fsm.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
#include "protect.h"
|
||||||
|
#include "bip39.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static uint32_t strength;
|
||||||
|
static uint8_t int_entropy[32];
|
||||||
|
static bool awaiting_entropy = false;
|
||||||
|
|
||||||
|
void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label)
|
||||||
|
{
|
||||||
|
if (_strength != 128 && _strength != 192 && _strength != 256) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_SyntaxError, "Invalid strength (has to be 128, 192 or 256 bits)");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strength = _strength;
|
||||||
|
|
||||||
|
random_buffer(int_entropy, 32);
|
||||||
|
|
||||||
|
char ent_str[4][17];
|
||||||
|
data2hex(int_entropy , 8, ent_str[0]);
|
||||||
|
data2hex(int_entropy + 8, 8, ent_str[1]);
|
||||||
|
data2hex(int_entropy + 16, 8, ent_str[2]);
|
||||||
|
data2hex(int_entropy + 24, 8, ent_str[3]);
|
||||||
|
|
||||||
|
if (display_random) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, "Cancel", "Continue", NULL, "Internal entropy:", ent_str[0], ent_str[1], ent_str[2], ent_str[3], NULL);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_ResetDevice, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Reset cancelled");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_protection && !protectChangePin()) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "PIN change failed");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.has_passphrase_protection = true;
|
||||||
|
storage.passphrase_protection = passphrase_protection;
|
||||||
|
storage_setLanguage(language);
|
||||||
|
storage_setLabel(label);
|
||||||
|
|
||||||
|
EntropyRequest resp;
|
||||||
|
memset(&resp, 0, sizeof(EntropyRequest));
|
||||||
|
msg_write(MessageType_MessageType_EntropyRequest, &resp);
|
||||||
|
awaiting_entropy = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char current_word[10];
|
||||||
|
|
||||||
|
void reset_entropy(const uint8_t *ext_entropy, uint32_t len)
|
||||||
|
{
|
||||||
|
if (!awaiting_entropy) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Reset mode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SHA256_CTX ctx;
|
||||||
|
sha256_Init(&ctx);
|
||||||
|
sha256_Update(&ctx, int_entropy, 32);
|
||||||
|
sha256_Update(&ctx, ext_entropy, len);
|
||||||
|
sha256_Final(int_entropy, &ctx);
|
||||||
|
strlcpy(storage.mnemonic, mnemonic_from_data(int_entropy, strength / 8), sizeof(storage.mnemonic));
|
||||||
|
memset(int_entropy, 0, 32);
|
||||||
|
awaiting_entropy = false;
|
||||||
|
|
||||||
|
int pass, idx, i = 0, j;
|
||||||
|
|
||||||
|
for (pass = 0; pass < 2; pass++) {
|
||||||
|
i = 0;
|
||||||
|
for (idx = 0; idx < (int)strength/32*3; idx++) {
|
||||||
|
// copy current_word
|
||||||
|
j = 0;
|
||||||
|
while (storage.mnemonic[i] != ' ' && storage.mnemonic[i] != 0 && j + 1 < (int)sizeof(current_word)) {
|
||||||
|
current_word[j] = storage.mnemonic[i];
|
||||||
|
i++; j++;
|
||||||
|
}
|
||||||
|
current_word[j] = 0; if (storage.mnemonic[i] != 0) i++;
|
||||||
|
char descbuf[] = "__. word is:";
|
||||||
|
char *desc = descbuf;
|
||||||
|
if (idx + 1 < 10) {
|
||||||
|
desc++;
|
||||||
|
} else {
|
||||||
|
descbuf[0] = '0' + (idx + 1) / 10;
|
||||||
|
}
|
||||||
|
descbuf[1] = '0' + (idx + 1) % 10;
|
||||||
|
if (idx + 1 == (int)strength/32*3) { // last word
|
||||||
|
if (pass == 1) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Finish", NULL, "Please check the seed", NULL, desc, NULL, current_word, NULL);
|
||||||
|
} else {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Again", NULL, "Write down the seed", NULL, desc, NULL, current_word, NULL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pass == 1) {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Please check the seed", NULL, desc, NULL, current_word, NULL);
|
||||||
|
} else {
|
||||||
|
layoutDialogSwipe(DIALOG_ICON_INFO, NULL, "Next", NULL, "Write down the seed", NULL, desc, NULL, current_word, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmWord, true)) {
|
||||||
|
storage_reset();
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.has_mnemonic = true;
|
||||||
|
storage_commit();
|
||||||
|
fsm_sendSuccess("Device reset");
|
||||||
|
layoutHome();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t reset_get_int_entropy(uint8_t *entropy) {
|
||||||
|
memcpy(entropy, int_entropy, 32);
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *reset_get_word(void) {
|
||||||
|
return current_word;
|
||||||
|
}
|
31
firmware/reset.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __RESET_H__
|
||||||
|
#define __RESET_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
void reset_init(bool display_random, uint32_t _strength, bool passphrase_protection, bool pin_protection, const char *language, const char *label);
|
||||||
|
void reset_entropy(const uint8_t *ext_entropy, uint32_t len);
|
||||||
|
uint32_t reset_get_int_entropy(uint8_t *entropy);
|
||||||
|
const char *reset_get_word(void);
|
||||||
|
|
||||||
|
#endif
|
426
firmware/signing.c
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "signing.h"
|
||||||
|
#include "fsm.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "transaction.h"
|
||||||
|
#include "ecdsa.h"
|
||||||
|
#include "protect.h"
|
||||||
|
|
||||||
|
static uint32_t inputs_count;
|
||||||
|
static uint32_t outputs_count;
|
||||||
|
static const CoinType *coin;
|
||||||
|
static const HDNode *root;
|
||||||
|
static HDNode node;
|
||||||
|
static bool signing = false;
|
||||||
|
enum {
|
||||||
|
STAGE_REQUEST_1_INPUT,
|
||||||
|
STAGE_REQUEST_2_PREV_META,
|
||||||
|
STAGE_REQUEST_2_PREV_INPUT,
|
||||||
|
STAGE_REQUEST_2_PREV_OUTPUT,
|
||||||
|
STAGE_REQUEST_3_INPUT,
|
||||||
|
STAGE_REQUEST_3_OUTPUT,
|
||||||
|
STAGE_REQUEST_4_OUTPUT
|
||||||
|
} signing_stage;
|
||||||
|
static uint32_t idx1i, idx2i, idx2o, idx3i, idx3o, idx4o;
|
||||||
|
static TxRequest resp;
|
||||||
|
static TxInputType input;
|
||||||
|
static TxOutputBinType bin_output;
|
||||||
|
static TxStruct to, tp, ti, tc;
|
||||||
|
static uint8_t hash[32], hash_check[32], privkey[32], pubkey[33], sig[64];
|
||||||
|
static uint64_t to_spend, spending, change_spend;
|
||||||
|
const uint32_t version = 1;
|
||||||
|
const uint32_t lock_time = 0;
|
||||||
|
static uint32_t progress, progress_total;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Workflow of streamed signing
|
||||||
|
|
||||||
|
I - input
|
||||||
|
O - output
|
||||||
|
|
||||||
|
foreach I:
|
||||||
|
Request I STAGE_REQUEST_1_INPUT
|
||||||
|
|
||||||
|
Calculate amount of I:
|
||||||
|
Request prevhash I, META STAGE_REQUEST_2_PREV_META
|
||||||
|
foreach prevhash I: STAGE_REQUEST_2_PREV_INPUT
|
||||||
|
Request prevhash I
|
||||||
|
foreach prevhash O: STAGE_REQUEST_2_PREV_OUTPUT
|
||||||
|
Request prevhash O
|
||||||
|
Store amount of I
|
||||||
|
Calculate hash of streamed tx, compare to prevhash I
|
||||||
|
|
||||||
|
foreach I: STAGE_REQUEST_3_INPUT
|
||||||
|
Request I
|
||||||
|
If I == I-to-be-signed:
|
||||||
|
Fill scriptsig
|
||||||
|
Add I to StreamTransactionSign
|
||||||
|
foreach O:
|
||||||
|
Request O STAGE_REQUEST_3_OUTPUT
|
||||||
|
If I=0:
|
||||||
|
Display output
|
||||||
|
Ask for confirmation
|
||||||
|
Add O to StreamTransactionSign
|
||||||
|
|
||||||
|
If I=0:
|
||||||
|
Check tx fee
|
||||||
|
Calculate txhash
|
||||||
|
else:
|
||||||
|
Compare current hash with txhash
|
||||||
|
If different:
|
||||||
|
Failure
|
||||||
|
|
||||||
|
Sign StreamTransactionSign
|
||||||
|
Return signed chunk
|
||||||
|
*/
|
||||||
|
|
||||||
|
void send_req_1_input(void)
|
||||||
|
{
|
||||||
|
idx2i = idx2o = idx3i = idx3o = 0;
|
||||||
|
signing_stage = STAGE_REQUEST_1_INPUT;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXINPUT;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_request_index = true;
|
||||||
|
resp.details.request_index = idx1i;
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_2_prev_meta(void)
|
||||||
|
{
|
||||||
|
signing_stage = STAGE_REQUEST_2_PREV_META;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXMETA;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_tx_hash = true;
|
||||||
|
resp.details.tx_hash.size = input.prev_hash.size;
|
||||||
|
memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, input.prev_hash.size);
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_2_prev_input(void)
|
||||||
|
{
|
||||||
|
signing_stage = STAGE_REQUEST_2_PREV_INPUT;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXINPUT;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_request_index = true;
|
||||||
|
resp.details.request_index = idx2i;
|
||||||
|
resp.details.has_tx_hash = true;
|
||||||
|
resp.details.tx_hash.size = input.prev_hash.size;
|
||||||
|
memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, resp.details.tx_hash.size);
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_2_prev_output(void)
|
||||||
|
{
|
||||||
|
signing_stage = STAGE_REQUEST_2_PREV_OUTPUT;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXOUTPUT;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_request_index = true;
|
||||||
|
resp.details.request_index = idx2o;
|
||||||
|
resp.details.has_tx_hash = true;
|
||||||
|
resp.details.tx_hash.size = input.prev_hash.size;
|
||||||
|
memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, resp.details.tx_hash.size);
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_3_input(void)
|
||||||
|
{
|
||||||
|
signing_stage = STAGE_REQUEST_3_INPUT;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXINPUT;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_request_index = true;
|
||||||
|
resp.details.request_index = idx3i;
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_3_output(void)
|
||||||
|
{
|
||||||
|
signing_stage = STAGE_REQUEST_3_OUTPUT;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXOUTPUT;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_request_index = true;
|
||||||
|
resp.details.request_index = idx3o;
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_4_output(void)
|
||||||
|
{
|
||||||
|
signing_stage = STAGE_REQUEST_4_OUTPUT;
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXOUTPUT;
|
||||||
|
resp.has_details = true;
|
||||||
|
resp.details.has_request_index = true;
|
||||||
|
resp.details.request_index = idx4o;
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_req_finished(void)
|
||||||
|
{
|
||||||
|
resp.has_request_type = true;
|
||||||
|
resp.request_type = RequestType_TXFINISHED;
|
||||||
|
msg_write(MessageType_MessageType_TxRequest, &resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, HDNode *_root)
|
||||||
|
{
|
||||||
|
inputs_count = _inputs_count;
|
||||||
|
outputs_count = _outputs_count;
|
||||||
|
coin = _coin;
|
||||||
|
root = _root;
|
||||||
|
|
||||||
|
idx1i = idx2i = idx2o = idx3i = idx3o = idx4o = 0;
|
||||||
|
to_spend = 0;
|
||||||
|
spending = 0;
|
||||||
|
change_spend = 0;
|
||||||
|
memset(&input, 0, sizeof(TxInputType));
|
||||||
|
memset(&resp, 0, sizeof(TxRequest));
|
||||||
|
|
||||||
|
signing = true;
|
||||||
|
progress = 1;
|
||||||
|
progress_total = inputs_count * (1 + inputs_count + outputs_count) + outputs_count;
|
||||||
|
|
||||||
|
tx_init(&to, inputs_count, outputs_count, version, lock_time, false);
|
||||||
|
|
||||||
|
send_req_1_input();
|
||||||
|
}
|
||||||
|
|
||||||
|
void signing_txack(TransactionType *tx)
|
||||||
|
{
|
||||||
|
if (!signing) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_UnexpectedMessage, "Not in Signing mode");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int co;
|
||||||
|
|
||||||
|
memset(&resp, 0, sizeof(TxRequest));
|
||||||
|
|
||||||
|
switch (signing_stage) {
|
||||||
|
case STAGE_REQUEST_1_INPUT:
|
||||||
|
layoutProgress("Signing", 1000 * progress / progress_total, progress); progress++;
|
||||||
|
memcpy(&input, tx->inputs, sizeof(TxInputType));
|
||||||
|
send_req_2_prev_meta();
|
||||||
|
return;
|
||||||
|
case STAGE_REQUEST_2_PREV_META:
|
||||||
|
tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, false);
|
||||||
|
send_req_2_prev_input();
|
||||||
|
return;
|
||||||
|
case STAGE_REQUEST_2_PREV_INPUT:
|
||||||
|
if (!tx_hash_input(&tp, tx->inputs)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize input");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (idx2i < tp.inputs_len - 1) {
|
||||||
|
idx2i++;
|
||||||
|
send_req_2_prev_input();
|
||||||
|
} else {
|
||||||
|
send_req_2_prev_output();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case STAGE_REQUEST_2_PREV_OUTPUT:
|
||||||
|
if (!tx_hash_output(&tp, tx->bin_outputs)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (idx2o == input.prev_index) {
|
||||||
|
to_spend += tx->bin_outputs[0].amount;
|
||||||
|
}
|
||||||
|
if (idx2o < tp.outputs_len - 1) {
|
||||||
|
idx2o++;
|
||||||
|
send_req_2_prev_output();
|
||||||
|
} else {
|
||||||
|
tx_hash_final(&tp, hash, true);
|
||||||
|
if (memcmp(hash, input.prev_hash.bytes, 32) != 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Encountered invalid prevhash");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tx_init(&ti, inputs_count, outputs_count, version, lock_time, true);
|
||||||
|
tx_init(&tc, inputs_count, outputs_count, version, lock_time, true);
|
||||||
|
memset(privkey, 0, 32);
|
||||||
|
memset(pubkey, 0, 33);
|
||||||
|
send_req_3_input();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case STAGE_REQUEST_3_INPUT:
|
||||||
|
layoutProgress("Signing", 1000 * progress / progress_total, progress); progress++;
|
||||||
|
if (!tx_hash_input(&tc, tx->inputs)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize input");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (idx3i == idx1i) {
|
||||||
|
memcpy(&node, root, sizeof(HDNode));
|
||||||
|
uint32_t k;
|
||||||
|
for (k = 0; k < tx->inputs[0].address_n_count; k++) {
|
||||||
|
hdnode_private_ckd(&node, tx->inputs[0].address_n[k]);
|
||||||
|
}
|
||||||
|
ecdsa_get_pubkeyhash(node.public_key, hash);
|
||||||
|
tx->inputs[0].script_sig.size = compile_script_sig(coin->address_type, hash, tx->inputs[0].script_sig.bytes);
|
||||||
|
if (tx->inputs[0].script_sig.size == 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(privkey, node.private_key, 32);
|
||||||
|
memcpy(pubkey, node.public_key, 33);
|
||||||
|
} else {
|
||||||
|
tx->inputs[0].script_sig.size = 0;
|
||||||
|
}
|
||||||
|
if (!tx_hash_input(&ti, tx->inputs)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize input");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (idx3i < inputs_count - 1) {
|
||||||
|
idx3i++;
|
||||||
|
send_req_3_input();
|
||||||
|
} else {
|
||||||
|
send_req_3_output();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case STAGE_REQUEST_3_OUTPUT:
|
||||||
|
layoutProgress("Signing", 1000 * progress / progress_total, progress); progress++;
|
||||||
|
co = compile_output(coin, root, tx->outputs, &bin_output, idx1i == 0);
|
||||||
|
if (co < 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Signing cancelled by user");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
} else if (co == 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to compile output");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tx_hash_output(&tc, &bin_output)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tx_hash_output(&ti, &bin_output)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (idx1i == 0) {
|
||||||
|
if (tx->outputs[0].address_n_count > 0) { // address_n set -> change address
|
||||||
|
if (change_spend == 0) { // not set
|
||||||
|
change_spend = tx->outputs[0].amount;
|
||||||
|
} else {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Only one change output allowed");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spending += tx->outputs[0].amount;
|
||||||
|
}
|
||||||
|
if (idx3o < outputs_count - 1) {
|
||||||
|
idx3o++;
|
||||||
|
send_req_3_output();
|
||||||
|
} else {
|
||||||
|
if (idx1i == 0) {
|
||||||
|
tx_hash_final(&tc, hash_check, false);
|
||||||
|
} else {
|
||||||
|
tx_hash_final(&tc, hash, false);
|
||||||
|
if (memcmp(hash, hash_check, 32) != 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tx_hash_final(&ti, hash, false);
|
||||||
|
resp.has_serialized = true;
|
||||||
|
resp.serialized.has_signature_index = true;
|
||||||
|
resp.serialized.signature_index = idx1i;
|
||||||
|
resp.serialized.has_signature = true;
|
||||||
|
resp.serialized.has_serialized_tx = true;
|
||||||
|
ecdsa_sign_digest(privkey, hash, sig);
|
||||||
|
resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes);
|
||||||
|
input.script_sig.size = serialize_script_sig(resp.serialized.signature.bytes, resp.serialized.signature.size, pubkey, 33, input.script_sig.bytes);
|
||||||
|
resp.serialized.serialized_tx.size = tx_serialize_input(&to, input.prev_hash.bytes, input.prev_index, input.script_sig.bytes, input.script_sig.size, input.sequence, resp.serialized.serialized_tx.bytes);
|
||||||
|
if (idx1i < inputs_count - 1) {
|
||||||
|
idx1i++;
|
||||||
|
send_req_1_input();
|
||||||
|
} else {
|
||||||
|
if (spending > to_spend) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint64_t fee = to_spend - spending;
|
||||||
|
if (fee > (((uint64_t)tc.size + 999) / 1000) * coin->maxfee_kb) {
|
||||||
|
layoutFeeOverThreshold(coin, fee, ((uint64_t)tc.size + 999) / 1000);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Fee over threshold. Signing cancelled.");
|
||||||
|
layoutHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// last confirmation
|
||||||
|
layoutConfirmTx(coin, to_spend - change_spend - fee, fee);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
send_req_4_output();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case STAGE_REQUEST_4_OUTPUT:
|
||||||
|
layoutProgress("Signing", 1000 * progress / progress_total, progress); progress++;
|
||||||
|
if (compile_output(coin, root, tx->outputs, &bin_output, false) <= 0) {
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Failed to compile output");
|
||||||
|
signing_abort();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resp.has_serialized = true;
|
||||||
|
resp.serialized.has_serialized_tx = true;
|
||||||
|
resp.serialized.serialized_tx.size = tx_serialize_output(&to, bin_output.amount, bin_output.script_pubkey.bytes, bin_output.script_pubkey.size, resp.serialized.serialized_tx.bytes);
|
||||||
|
if (idx4o < outputs_count - 1) {
|
||||||
|
idx4o++;
|
||||||
|
send_req_4_output();
|
||||||
|
} else {
|
||||||
|
send_req_finished();
|
||||||
|
signing_abort();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fsm_sendFailure(FailureType_Failure_Other, "Signing error");
|
||||||
|
signing_abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void signing_abort(void)
|
||||||
|
{
|
||||||
|
if (signing) {
|
||||||
|
layoutHome();
|
||||||
|
signing = false;
|
||||||
|
}
|
||||||
|
}
|
32
firmware/signing.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __SIGNING_H__
|
||||||
|
#define __SIGNING_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "bip32.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
|
||||||
|
void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, HDNode *_root);
|
||||||
|
void signing_abort(void);
|
||||||
|
void signing_txack(TransactionType *tx);
|
||||||
|
|
||||||
|
#endif
|
40
firmware/ssp.c
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "ssp.h"
|
||||||
|
#include "rng.h"
|
||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
void *__stack_chk_guard = 0;
|
||||||
|
|
||||||
|
void __stack_chk_guard_setup(void)
|
||||||
|
{
|
||||||
|
unsigned char * p;
|
||||||
|
p = (unsigned char *) &__stack_chk_guard;
|
||||||
|
p[0] = 0;
|
||||||
|
p[1] = 0;
|
||||||
|
p[2] = '\n';
|
||||||
|
p[3] = 0xFF; // random32() & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__((noreturn)) __stack_chk_fail(void)
|
||||||
|
{
|
||||||
|
layoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Stack smashing", "detected.", NULL, "Please unplug", "the device.", NULL);
|
||||||
|
for (;;) {} // loop forever
|
||||||
|
}
|
26
firmware/ssp.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __SSP_H_
|
||||||
|
#define __SSP_H_
|
||||||
|
|
||||||
|
void __stack_chk_guard_setup(void);
|
||||||
|
void __attribute__((noreturn)) __stack_chk_fail(void);
|
||||||
|
|
||||||
|
#endif
|
327
firmware/storage.c
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
|
|
||||||
|
#include <libopencm3/stm32/f2/flash.h>
|
||||||
|
|
||||||
|
#include "messages.pb.h"
|
||||||
|
#include "storage.pb.h"
|
||||||
|
|
||||||
|
#include "trezor.h"
|
||||||
|
#include "aes.h"
|
||||||
|
#include "bip32.h"
|
||||||
|
#include "bip39.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "rng.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "protect.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
|
||||||
|
Storage storage;
|
||||||
|
|
||||||
|
uint8_t storage_uuid[12];
|
||||||
|
char storage_uuid_str[25];
|
||||||
|
|
||||||
|
static bool sessionRootNodeCached;
|
||||||
|
static HDNode sessionRootNode;
|
||||||
|
|
||||||
|
static bool sessionPinCached;
|
||||||
|
static char sessionPin[17];
|
||||||
|
|
||||||
|
static bool sessionPassphraseCached;
|
||||||
|
static char sessionPassphrase[51];
|
||||||
|
|
||||||
|
/*
|
||||||
|
storage layout:
|
||||||
|
|
||||||
|
offset | type/length | description
|
||||||
|
--------+-------------+-------------------------------
|
||||||
|
0x0000 | 4 bytes | magic = 'stor'
|
||||||
|
0x0004 | 12 bytes | uuid
|
||||||
|
0x0010 | ? | Storage structure
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define STORAGE_VERSION 1
|
||||||
|
|
||||||
|
void storage_from_flash(uint32_t version)
|
||||||
|
{
|
||||||
|
switch (version) {
|
||||||
|
case 1:
|
||||||
|
memcpy(&storage, (void *)(FLASH_STORAGE_START + 4 + sizeof(storage_uuid)), sizeof(Storage));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
storage.version = STORAGE_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_init(void)
|
||||||
|
{
|
||||||
|
storage_reset();
|
||||||
|
// if magic is ok
|
||||||
|
if (memcmp((void *)FLASH_STORAGE_START, "stor", 4) == 0) {
|
||||||
|
// load uuid
|
||||||
|
memcpy(storage_uuid, (void *)(FLASH_STORAGE_START + 4), sizeof(storage_uuid));
|
||||||
|
data2hex(storage_uuid, sizeof(storage_uuid), storage_uuid_str);
|
||||||
|
// load storage struct
|
||||||
|
uint32_t version = ((Storage *)(FLASH_STORAGE_START + 4 + sizeof(storage_uuid)))->version;
|
||||||
|
if (version && version <= STORAGE_VERSION) {
|
||||||
|
storage_from_flash(version);
|
||||||
|
}
|
||||||
|
if (version != STORAGE_VERSION) {
|
||||||
|
storage_commit();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
storage_reset_uuid();
|
||||||
|
storage_commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_reset_uuid(void)
|
||||||
|
{
|
||||||
|
// set random uuid
|
||||||
|
random_buffer(storage_uuid, sizeof(storage_uuid));
|
||||||
|
data2hex(storage_uuid, sizeof(storage_uuid), storage_uuid_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_reset(void)
|
||||||
|
{
|
||||||
|
// reset storage struct
|
||||||
|
memset(&storage, 0, sizeof(storage));
|
||||||
|
storage.version = STORAGE_VERSION;
|
||||||
|
sessionRootNodeCached = false; memset(&sessionRootNode, 0, sizeof(sessionRootNode));
|
||||||
|
sessionPassphraseCached = false; memset(&sessionPassphrase, 0, sizeof(sessionPassphrase));
|
||||||
|
sessionPinCached = false; memset(&sessionPin, 0, sizeof(sessionPin));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t meta_backup[FLASH_META_LEN];
|
||||||
|
|
||||||
|
void storage_commit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32_t *w;
|
||||||
|
// backup meta
|
||||||
|
memcpy(meta_backup, (void *)FLASH_META_START, FLASH_META_LEN);
|
||||||
|
flash_unlock();
|
||||||
|
// erase storage
|
||||||
|
for (i = FLASH_META_SECTOR_FIRST; i <= FLASH_META_SECTOR_LAST; i++) {
|
||||||
|
flash_erase_sector(i, FLASH_CR_PROGRAM_X32);
|
||||||
|
}
|
||||||
|
// modify storage
|
||||||
|
memcpy(meta_backup + FLASH_META_DESC_LEN, "stor", 4);
|
||||||
|
memcpy(meta_backup + FLASH_META_DESC_LEN + 4, storage_uuid, sizeof(storage_uuid));
|
||||||
|
memcpy(meta_backup + FLASH_META_DESC_LEN + 4 + sizeof(storage_uuid), &storage, sizeof(Storage));
|
||||||
|
// copy it back
|
||||||
|
for (i = 0; i < FLASH_META_LEN / 4; i++) {
|
||||||
|
w = (uint32_t *)(meta_backup + i * 4);
|
||||||
|
flash_program_word(FLASH_META_START + i * 4, *w);
|
||||||
|
}
|
||||||
|
flash_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_loadDevice(LoadDevice *msg)
|
||||||
|
{
|
||||||
|
storage_reset();
|
||||||
|
|
||||||
|
if (msg->has_pin > 0) {
|
||||||
|
storage_setPin(msg->pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->has_passphrase_protection) {
|
||||||
|
storage.has_passphrase_protection = true;
|
||||||
|
storage.passphrase_protection = msg->passphrase_protection;
|
||||||
|
} else {
|
||||||
|
storage.has_passphrase_protection = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->has_node) {
|
||||||
|
storage.has_node = true;
|
||||||
|
storage.has_mnemonic = false;
|
||||||
|
memcpy(&storage.node, &(msg->node), sizeof(HDNodeType));
|
||||||
|
sessionRootNodeCached = false;
|
||||||
|
memset(&sessionRootNode, 0, sizeof(sessionRootNode));
|
||||||
|
} else if (msg->has_mnemonic) {
|
||||||
|
storage.has_mnemonic = true;
|
||||||
|
storage.has_node = false;
|
||||||
|
strlcpy(storage.mnemonic, msg->mnemonic, sizeof(storage.mnemonic));
|
||||||
|
sessionRootNodeCached = false;
|
||||||
|
memset(&sessionRootNode, 0, sizeof(sessionRootNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->has_language) {
|
||||||
|
storage_setLanguage(msg->language);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg->has_label) {
|
||||||
|
storage_setLabel(msg->label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_setLabel(const char *label)
|
||||||
|
{
|
||||||
|
if (!label) return;
|
||||||
|
storage.has_label = true;
|
||||||
|
strlcpy(storage.label, label, sizeof(storage.label));
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_setLanguage(const char *lang)
|
||||||
|
{
|
||||||
|
if (!lang) return;
|
||||||
|
// sanity check
|
||||||
|
if (strcmp(lang, "english") == 0) {
|
||||||
|
storage.has_language = true;
|
||||||
|
strlcpy(storage.language, lang, sizeof(storage.language));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_root_node_callback(uint32_t iter, uint32_t total)
|
||||||
|
{
|
||||||
|
static uint8_t i;
|
||||||
|
layoutProgress("Waking up", 1000 * iter / total, i++);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool storage_getRootNode(HDNode *node)
|
||||||
|
{
|
||||||
|
// root node is properly cached
|
||||||
|
if (sessionRootNodeCached) {
|
||||||
|
memcpy(node, &sessionRootNode, sizeof(HDNode));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if storage has node, decrypt and use it
|
||||||
|
if (storage.has_node) {
|
||||||
|
if (!protectPassphrase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hdnode_from_xprv(storage.node.depth, storage.node.fingerprint, storage.node.child_num, storage.node.chain_code.bytes, storage.node.private_key.bytes, &sessionRootNode);
|
||||||
|
if (storage.has_passphrase_protection > 0) {
|
||||||
|
// decrypt hd node
|
||||||
|
aes_ctx ctx;
|
||||||
|
aes_enc_key((const uint8_t *)sessionPassphrase, strlen(sessionPassphrase), &ctx);
|
||||||
|
aes_enc_blk(sessionRootNode.chain_code, sessionRootNode.chain_code, &ctx);
|
||||||
|
aes_enc_blk(sessionRootNode.chain_code + 16, sessionRootNode.chain_code + 16, &ctx);
|
||||||
|
aes_enc_blk(sessionRootNode.private_key, sessionRootNode.private_key, &ctx);
|
||||||
|
aes_enc_blk(sessionRootNode.private_key + 16, sessionRootNode.private_key + 16, &ctx);
|
||||||
|
}
|
||||||
|
memcpy(node, &sessionRootNode, sizeof(HDNode));
|
||||||
|
sessionRootNodeCached = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if storage has mnemonic, convert it to node and use it
|
||||||
|
if (storage.has_mnemonic) {
|
||||||
|
if (!protectPassphrase()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint8_t seed[64];
|
||||||
|
layoutProgressSwipe("Waking up", 0, 0);
|
||||||
|
mnemonic_to_seed(storage.mnemonic, sessionPassphrase, seed, get_root_node_callback); // BIP-0039
|
||||||
|
hdnode_from_seed(seed, sizeof(seed), &sessionRootNode);
|
||||||
|
memcpy(node, &sessionRootNode, sizeof(HDNode));
|
||||||
|
sessionRootNodeCached = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *storage_getLabel(void)
|
||||||
|
{
|
||||||
|
return storage.has_label ? storage.label : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *storage_getLanguage(void)
|
||||||
|
{
|
||||||
|
return storage.has_language ? storage.language : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool storage_isPinCorrect(const char *pin)
|
||||||
|
{
|
||||||
|
return strcmp(storage.pin, pin) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool storage_hasPin(void)
|
||||||
|
{
|
||||||
|
return storage.has_pin && strlen(storage.pin) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_setPin(const char *pin)
|
||||||
|
{
|
||||||
|
if (pin && strlen(pin) > 0) {
|
||||||
|
storage.has_pin = true;
|
||||||
|
strlcpy(storage.pin, pin, sizeof(storage.pin));
|
||||||
|
} else {
|
||||||
|
storage.has_pin = false;
|
||||||
|
storage.pin[0] = 0;
|
||||||
|
}
|
||||||
|
storage_commit();
|
||||||
|
sessionPinCached = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_cachePassphrase(const char *passphrase)
|
||||||
|
{
|
||||||
|
strlcpy(sessionPassphrase, passphrase, sizeof(sessionPassphrase));
|
||||||
|
sessionPassphraseCached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool session_isPassphraseCached(void)
|
||||||
|
{
|
||||||
|
return sessionPassphraseCached;
|
||||||
|
}
|
||||||
|
|
||||||
|
void session_cachePin(const char *pin)
|
||||||
|
{
|
||||||
|
strlcpy(sessionPin, pin, sizeof(sessionPin));
|
||||||
|
sessionPinCached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool session_isPinCached(void)
|
||||||
|
{
|
||||||
|
return sessionPinCached && strcmp(sessionPin, storage.pin) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_resetPinFails(void)
|
||||||
|
{
|
||||||
|
storage.has_pin_failed_attempts = true;
|
||||||
|
storage.pin_failed_attempts = 0;
|
||||||
|
storage_commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void storage_increasePinFails(void)
|
||||||
|
{
|
||||||
|
if (!storage.has_pin_failed_attempts) {
|
||||||
|
storage.has_pin_failed_attempts = true;
|
||||||
|
storage.pin_failed_attempts = 1;
|
||||||
|
} else {
|
||||||
|
storage.pin_failed_attempts++;
|
||||||
|
}
|
||||||
|
storage_commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t storage_getPinFails(void)
|
||||||
|
{
|
||||||
|
return storage.has_pin_failed_attempts ? storage.pin_failed_attempts : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool storage_isInitialized(void)
|
||||||
|
{
|
||||||
|
return storage.has_node || storage.has_mnemonic;
|
||||||
|
}
|
61
firmware/storage.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __STORAGE_H__
|
||||||
|
#define __STORAGE_H__
|
||||||
|
|
||||||
|
#include "types.pb.h"
|
||||||
|
#include "storage.pb.h"
|
||||||
|
#include "messages.pb.h"
|
||||||
|
#include "bip32.h"
|
||||||
|
|
||||||
|
void storage_init(void);
|
||||||
|
void storage_reset_uuid(void);
|
||||||
|
void storage_reset(void);
|
||||||
|
void storage_commit(void);
|
||||||
|
|
||||||
|
void storage_loadDevice(LoadDevice *msg);
|
||||||
|
|
||||||
|
bool storage_getRootNode(HDNode *node);
|
||||||
|
|
||||||
|
const char *storage_getLabel(void);
|
||||||
|
void storage_setLabel(const char *label);
|
||||||
|
|
||||||
|
const char *storage_getLanguage(void);
|
||||||
|
void storage_setLanguage(const char *lang);
|
||||||
|
|
||||||
|
void session_cachePassphrase(const char *passphrase);
|
||||||
|
bool session_isPassphraseCached(void);
|
||||||
|
|
||||||
|
bool storage_isPinCorrect(const char *pin);
|
||||||
|
bool storage_hasPin(void);
|
||||||
|
void storage_setPin(const char *pin);
|
||||||
|
void session_cachePin(const char *pin);
|
||||||
|
bool session_isPinCached(void);
|
||||||
|
void storage_resetPinFails(void);
|
||||||
|
void storage_increasePinFails(void);
|
||||||
|
uint32_t storage_getPinFails(void);
|
||||||
|
|
||||||
|
bool storage_isInitialized(void);
|
||||||
|
|
||||||
|
extern Storage storage;
|
||||||
|
|
||||||
|
extern char storage_uuid_str[25];
|
||||||
|
|
||||||
|
#endif
|
459
firmware/transaction.c
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "transaction.h"
|
||||||
|
#include "ecdsa.h"
|
||||||
|
#include "coins.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "protect.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "messages.pb.h"
|
||||||
|
|
||||||
|
// aux methods
|
||||||
|
|
||||||
|
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 op_push(uint32_t i, uint8_t *out) {
|
||||||
|
if (i < 0x4C) {
|
||||||
|
out[0] = i & 0xFF;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (i < 0xFF) {
|
||||||
|
out[0] = 0x4C;
|
||||||
|
out[1] = i & 0xFF;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (i < 0xFFFF) {
|
||||||
|
out[0] = 0x4D;
|
||||||
|
out[1] = i & 0xFF;
|
||||||
|
out[2] = (i >> 8) & 0xFF;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
out[0] = 0x4E;
|
||||||
|
out[1] = i & 0xFF;
|
||||||
|
out[2] = (i >> 8) & 0xFF;
|
||||||
|
out[3] = (i >> 16) & 0xFF;
|
||||||
|
out[4] = (i >> 24) & 0xFF;
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm)
|
||||||
|
{
|
||||||
|
// address_n provided-> change address -> calculate from address_n
|
||||||
|
if (in->address_n_count > 0) {
|
||||||
|
HDNode node;
|
||||||
|
uint32_t k;
|
||||||
|
memcpy(&node, root, sizeof(HDNode));
|
||||||
|
for (k = 0; k < in->address_n_count; k++) {
|
||||||
|
hdnode_private_ckd(&node, in->address_n[k]);
|
||||||
|
}
|
||||||
|
ecdsa_get_address(node.public_key, coin->address_type, in->address);
|
||||||
|
} else
|
||||||
|
if (in->has_address) { // address provided -> regular output
|
||||||
|
if (needs_confirm) {
|
||||||
|
layoutConfirmOutput(coin, in);
|
||||||
|
if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // does not have address_n neither address
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(out, 0, sizeof(TxOutputBinType));
|
||||||
|
out->amount = in->amount;
|
||||||
|
|
||||||
|
if (in->script_type == ScriptType_PAYTOADDRESS) {
|
||||||
|
out->script_pubkey.bytes[0] = 0x76; // OP_DUP
|
||||||
|
out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160
|
||||||
|
out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes
|
||||||
|
uint8_t decoded[21];
|
||||||
|
if (!ecdsa_address_decode(in->address, decoded)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(out->script_pubkey.bytes + 3, decoded + 1, 20);
|
||||||
|
out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY
|
||||||
|
out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG
|
||||||
|
out->script_pubkey.size = 25;
|
||||||
|
return 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in->script_type == ScriptType_PAYTOSCRIPTHASH) {
|
||||||
|
out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160
|
||||||
|
out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes
|
||||||
|
uint8_t decoded[21];
|
||||||
|
if (!ecdsa_address_decode(in->address, decoded)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(out->script_pubkey.bytes + 2, decoded + 1, 20);
|
||||||
|
out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL
|
||||||
|
out->script_pubkey.size = 23;
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t compile_script_sig(uint8_t address_type, const uint8_t *pubkeyhash, uint8_t *out)
|
||||||
|
{
|
||||||
|
if (coinByAddressType(address_type)) { // valid coin type
|
||||||
|
out[0] = 0x76; // OP_DUP
|
||||||
|
out[1] = 0xA9; // OP_HASH_160
|
||||||
|
out[2] = 0x14; // pushing 20 bytes
|
||||||
|
memcpy(out + 3, pubkeyhash, 20);
|
||||||
|
out[23] = 0x88; // OP_EQUALVERIFY
|
||||||
|
out[24] = 0xAC; // OP_CHECKSIG
|
||||||
|
return 25;
|
||||||
|
} else {
|
||||||
|
return 0; // unsupported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int serialize_script_sig(uint8_t *signature, uint32_t signature_len, uint8_t *pubkey, uint32_t pubkey_len, uint8_t *out)
|
||||||
|
{
|
||||||
|
uint32_t r = 0;
|
||||||
|
r += op_push(signature_len + 1, out + r);
|
||||||
|
memcpy(out + r, signature, signature_len); r += signature_len;
|
||||||
|
out[r] = 0x01; r++;
|
||||||
|
r += op_push(pubkey_len, out + r);
|
||||||
|
memcpy(out + r, pubkey, pubkey_len); r += pubkey_len;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tx methods
|
||||||
|
|
||||||
|
uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out)
|
||||||
|
{
|
||||||
|
memcpy(out, &(tx->version), 4);
|
||||||
|
return 4 + ser_length(tx->inputs_len, out + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tx_serialize_input(TxStruct *tx, uint8_t *prev_hash, uint32_t prev_index, uint8_t *script_sig, uint32_t script_sig_len, uint32_t sequence, uint8_t *out)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (tx->have_inputs >= tx->inputs_len) {
|
||||||
|
// already got all inputs
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint32_t r = 0;
|
||||||
|
if (tx->have_inputs == 0) {
|
||||||
|
r += tx_serialize_header(tx, out + r);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
*(out + r + i) = prev_hash[31 - i];
|
||||||
|
}
|
||||||
|
r += 32;
|
||||||
|
memcpy(out + r, &prev_index, 4); r += 4;
|
||||||
|
r += ser_length(script_sig_len, out + r);
|
||||||
|
memcpy(out + r, script_sig, script_sig_len); r+= script_sig_len;
|
||||||
|
memcpy(out + r, &sequence, 4); r += 4;
|
||||||
|
|
||||||
|
tx->have_inputs++;
|
||||||
|
tx->size += r;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out)
|
||||||
|
{
|
||||||
|
return ser_length(tx->outputs_len, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out)
|
||||||
|
{
|
||||||
|
memcpy(out, &(tx->lock_time), 4);
|
||||||
|
if (tx->add_hash_type) {
|
||||||
|
uint32_t ht = 1;
|
||||||
|
memcpy(out + 4, &ht, 4);
|
||||||
|
return 8;
|
||||||
|
} else {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubkey, uint32_t script_pubkey_len, uint8_t *out)
|
||||||
|
{
|
||||||
|
if (tx->have_inputs < tx->inputs_len) {
|
||||||
|
// not all inputs provided
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (tx->have_outputs >= tx->outputs_len) {
|
||||||
|
// already got all outputs
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
uint32_t r = 0;
|
||||||
|
if (tx->have_outputs == 0) {
|
||||||
|
r += tx_serialize_middle(tx, out + r);
|
||||||
|
}
|
||||||
|
memcpy(out + r, &amount, 8); r += 8;
|
||||||
|
r += ser_length(script_pubkey_len, out + r);
|
||||||
|
memcpy(out + r, script_pubkey, script_pubkey_len); r+= script_pubkey_len;
|
||||||
|
tx->have_outputs++;
|
||||||
|
if (tx->have_outputs == tx->outputs_len) {
|
||||||
|
r += tx_serialize_footer(tx, out + r);
|
||||||
|
}
|
||||||
|
tx->size += r;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, bool add_hash_type)
|
||||||
|
{
|
||||||
|
tx->inputs_len = inputs_len;
|
||||||
|
tx->outputs_len = outputs_len;
|
||||||
|
tx->version = version;
|
||||||
|
tx->lock_time = lock_time;
|
||||||
|
tx->add_hash_type = add_hash_type;
|
||||||
|
tx->have_inputs = 0;
|
||||||
|
tx->have_outputs = 0;
|
||||||
|
tx->size = 0;
|
||||||
|
sha256_Init(&(tx->ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tx_hash_input(TxStruct *t, TxInputType *input)
|
||||||
|
{
|
||||||
|
uint8_t buf[512];
|
||||||
|
uint32_t r = tx_serialize_input(t, input->prev_hash.bytes, input->prev_index, input->script_sig.bytes, input->script_sig.size, input->sequence, buf);
|
||||||
|
if (!r) return false;
|
||||||
|
sha256_Update(&(t->ctx), buf, r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tx_hash_output(TxStruct *t, TxOutputBinType *output)
|
||||||
|
{
|
||||||
|
uint8_t buf[512];
|
||||||
|
uint32_t r = tx_serialize_output(t, output->amount, output->script_pubkey.bytes, output->script_pubkey.size, buf);
|
||||||
|
if (!r) return false;
|
||||||
|
sha256_Update(&(t->ctx), buf, r);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse)
|
||||||
|
{
|
||||||
|
sha256_Final(hash, &(t->ctx));
|
||||||
|
sha256_Raw(hash, 32, hash);
|
||||||
|
if (!reverse) return;
|
||||||
|
uint8_t i, k;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
k = hash[31 - i];
|
||||||
|
hash[31 - i] = hash[i];
|
||||||
|
hash[i] = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transactionHash(TransactionType *tx, uint8_t *hash)
|
||||||
|
{
|
||||||
|
TxStruct t;
|
||||||
|
uint32_t i;
|
||||||
|
tx_init(&t, tx->inputs_count, tx->bin_outputs_count, tx->version, tx->lock_time, false);
|
||||||
|
for (i = 0; i < tx->inputs_count; i++) {
|
||||||
|
if (!tx_hash_input(&t, &(tx->inputs[i]))) return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < tx->bin_outputs_count; i++) {
|
||||||
|
if (!tx_hash_output(&t, &(tx->bin_outputs[i]))) return false;
|
||||||
|
}
|
||||||
|
tx_hash_final(&t, hash, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int transactionSimpleSign(const CoinType *coin, HDNode *root, TxInputType *inputs, uint32_t inputs_count, TxOutputType *outputs, uint32_t outputs_count, uint32_t version, uint32_t lock_time, uint8_t *out)
|
||||||
|
{
|
||||||
|
uint32_t idx, i, k, r = 0;
|
||||||
|
TxStruct ti, to;
|
||||||
|
uint8_t buf[512];
|
||||||
|
TxInputType input;
|
||||||
|
TxOutputBinType output;
|
||||||
|
HDNode node;
|
||||||
|
uint8_t privkey[32], pubkey[33], hash[32], sig[64];
|
||||||
|
|
||||||
|
layoutProgressSwipe("Signing", 0, 0);
|
||||||
|
tx_init(&to, inputs_count, outputs_count, version, lock_time, false);
|
||||||
|
for (idx = 0; idx < inputs_count; idx++) {
|
||||||
|
// compute inner transaction
|
||||||
|
memcpy(&input, &(inputs[idx]), sizeof(TxInputType));
|
||||||
|
tx_init(&ti, inputs_count, outputs_count, version, lock_time, true);
|
||||||
|
memset(privkey, 0, 32);
|
||||||
|
memset(pubkey, 0, 33);
|
||||||
|
for (i = 0; i < inputs_count; i++) {
|
||||||
|
if (i == idx) {
|
||||||
|
memcpy(&node, root, sizeof(HDNode));
|
||||||
|
for (k = 0; k < inputs[i].address_n_count; k++) {
|
||||||
|
hdnode_private_ckd(&node, inputs[i].address_n[k]);
|
||||||
|
}
|
||||||
|
ecdsa_get_pubkeyhash(node.public_key, hash);
|
||||||
|
inputs[i].script_sig.size = compile_script_sig(coin->address_type, hash, inputs[i].script_sig.bytes);
|
||||||
|
if (inputs[i].script_sig.size == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
memcpy(privkey, node.private_key, 32);
|
||||||
|
memcpy(pubkey, node.public_key, 33);
|
||||||
|
} else {
|
||||||
|
inputs[i].script_sig.size = 0;
|
||||||
|
}
|
||||||
|
if (!tx_hash_input(&ti, &(inputs[i]))) return 0;
|
||||||
|
}
|
||||||
|
for (i = 0; i < outputs_count; i++) {
|
||||||
|
int co = compile_output(coin, root, &(outputs[i]), &output, idx == 0);
|
||||||
|
if (co <= 0) {
|
||||||
|
return co;
|
||||||
|
}
|
||||||
|
if (!tx_hash_output(&ti, &output)) return 0;
|
||||||
|
}
|
||||||
|
tx_hash_final(&ti, hash, false);
|
||||||
|
ecdsa_sign_digest(privkey, hash, sig);
|
||||||
|
int der_len = ecdsa_sig_to_der(sig, buf);
|
||||||
|
input.script_sig.size = serialize_script_sig(buf, der_len, pubkey, 33, input.script_sig.bytes);
|
||||||
|
r += tx_serialize_input(&to, input.prev_hash.bytes, input.prev_index, input.script_sig.bytes, input.script_sig.size, input.sequence, out + r);
|
||||||
|
layoutProgress("Signing", 1000 * idx / inputs_count, idx);
|
||||||
|
}
|
||||||
|
for (i = 0; i < outputs_count; i++) {
|
||||||
|
if (compile_output(coin, root, &(outputs[i]), &output, false) <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
r += tx_serialize_output(&to, output.amount, output.script_pubkey.bytes, output.script_pubkey.size, out + r);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs)
|
||||||
|
{
|
||||||
|
return 10 + inputs * 149 + outputs * 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t transactionEstimateSizeKb(uint32_t inputs, uint32_t outputs)
|
||||||
|
{
|
||||||
|
return (transactionEstimateSize(inputs, outputs) + 999) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transactionMessageSign(uint8_t *message, uint32_t message_len, uint8_t *privkey, const char *address, uint8_t *signature)
|
||||||
|
{
|
||||||
|
if (message_len >= 256) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHA256_CTX ctx;
|
||||||
|
uint8_t i, hash[32];
|
||||||
|
|
||||||
|
sha256_Init(&ctx);
|
||||||
|
sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25);
|
||||||
|
i = message_len;
|
||||||
|
sha256_Update(&ctx, &i, 1);
|
||||||
|
sha256_Update(&ctx, message, message_len);
|
||||||
|
sha256_Final(hash, &ctx);
|
||||||
|
sha256_Raw(hash, 32, hash);
|
||||||
|
|
||||||
|
ecdsa_sign_digest(privkey, hash, signature + 1);
|
||||||
|
for (i = 27 + 4; i < 27 + 4 + 4; i++) {
|
||||||
|
signature[0] = i;
|
||||||
|
if (transactionMessageVerify(message, message_len, signature, address)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool transactionMessageVerify(uint8_t *message, uint32_t message_len, uint8_t *signature, const char *address)
|
||||||
|
{
|
||||||
|
if (message_len >= 256) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compressed;
|
||||||
|
uint8_t nV = signature[0];
|
||||||
|
bignum256 r, s, e;
|
||||||
|
curve_point cp, cp2;
|
||||||
|
SHA256_CTX ctx;
|
||||||
|
uint8_t i, pubkey[65], decoded[21], hash[32];
|
||||||
|
char addr[35];
|
||||||
|
|
||||||
|
if (nV < 27 || nV >= 35) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
compressed = (nV >= 31);
|
||||||
|
if (compressed) {
|
||||||
|
nV -= 4;
|
||||||
|
}
|
||||||
|
uint8_t recid = nV - 27;
|
||||||
|
// read r and s
|
||||||
|
bn_read_be(signature + 1, &r);
|
||||||
|
bn_read_be(signature + 33, &s);
|
||||||
|
// x = r + (recid / 2) * order
|
||||||
|
bn_zero(&cp.x);
|
||||||
|
for (i = 0; i < recid / 2; i++) {
|
||||||
|
bn_addmod(&cp.x, &order256k1, &prime256k1);
|
||||||
|
}
|
||||||
|
bn_addmod(&cp.x, &r, &prime256k1);
|
||||||
|
// compute y from x
|
||||||
|
uncompress_coords(recid % 2, &cp.x, &cp.y);
|
||||||
|
// calculate hash
|
||||||
|
sha256_Init(&ctx);
|
||||||
|
sha256_Update(&ctx, (const uint8_t *)"\x18" "Bitcoin Signed Message:" "\n", 25);
|
||||||
|
i = message_len;
|
||||||
|
sha256_Update(&ctx, &i, 1);
|
||||||
|
sha256_Update(&ctx, message, message_len);
|
||||||
|
sha256_Final(hash, &ctx);
|
||||||
|
sha256_Raw(hash, 32, hash);
|
||||||
|
// e = -hash
|
||||||
|
bn_read_be(hash, &e);
|
||||||
|
bn_substract_noprime(&order256k1, &e, &e);
|
||||||
|
// r = r^-1
|
||||||
|
bn_inverse(&r, &order256k1);
|
||||||
|
point_multiply(&s, &cp, &cp);
|
||||||
|
scalar_multiply(&e, &cp2);
|
||||||
|
point_add(&cp2, &cp);
|
||||||
|
point_multiply(&r, &cp, &cp);
|
||||||
|
pubkey[0] = 0x04;
|
||||||
|
bn_write_be(&cp.x, pubkey + 1);
|
||||||
|
bn_write_be(&cp.y, pubkey + 33);
|
||||||
|
// check if the address is correct when provided
|
||||||
|
if (address) {
|
||||||
|
ecdsa_address_decode(address, decoded);
|
||||||
|
if (compressed) {
|
||||||
|
pubkey[0] = 0x02 | (cp.y.val[0] & 0x01);
|
||||||
|
}
|
||||||
|
ecdsa_get_address(pubkey, decoded[0], addr);
|
||||||
|
if (strcmp(addr, address) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check if signature verifies the digest
|
||||||
|
if (ecdsa_verify_digest(pubkey, signature + 1, hash) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
67
firmware/transaction.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __TRANSACTION_H__
|
||||||
|
#define __TRANSACTION_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "sha2.h"
|
||||||
|
#include "bip32.h"
|
||||||
|
#include "types.pb.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t inputs_len;
|
||||||
|
uint32_t outputs_len;
|
||||||
|
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t lock_time;
|
||||||
|
bool add_hash_type;
|
||||||
|
|
||||||
|
uint32_t have_inputs;
|
||||||
|
uint32_t have_outputs;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
SHA256_CTX ctx;
|
||||||
|
} TxStruct;
|
||||||
|
|
||||||
|
uint32_t compile_script_sig(uint8_t address_type, const uint8_t *pubkeyhash, uint8_t *out);
|
||||||
|
int serialize_script_sig(uint8_t *signature, uint32_t signature_len, uint8_t *pubkey, uint32_t pubkey_len, uint8_t *out);
|
||||||
|
int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm);
|
||||||
|
uint32_t tx_serialize_input(TxStruct *tx, uint8_t *prev_hash, uint32_t prev_index, uint8_t *script_sig, uint32_t script_sig_len, uint32_t sequence, uint8_t *out);
|
||||||
|
uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubkey, uint32_t script_pubkey_len, uint8_t *out);
|
||||||
|
|
||||||
|
void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, bool add_hash_type);
|
||||||
|
bool tx_hash_input(TxStruct *t, TxInputType *input);
|
||||||
|
bool tx_hash_output(TxStruct *t, TxOutputBinType *output);
|
||||||
|
void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse);
|
||||||
|
|
||||||
|
bool transactionHash(TransactionType *tx, uint8_t *hash);
|
||||||
|
|
||||||
|
int transactionSimpleSign(const CoinType *coin, HDNode *root, TxInputType *inputs, uint32_t inputs_count, TxOutputType *outputs, uint32_t outputs_count, uint32_t version, uint32_t lock_time, uint8_t *out);
|
||||||
|
|
||||||
|
uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs);
|
||||||
|
|
||||||
|
uint32_t transactionEstimateSizeKb(uint32_t inputs, uint32_t outputs);
|
||||||
|
|
||||||
|
bool transactionMessageSign(uint8_t *message, uint32_t message_len, uint8_t *privkey, const char *address, uint8_t *signature);
|
||||||
|
|
||||||
|
bool transactionMessageVerify(uint8_t *message, uint32_t message_len, uint8_t *signature, const char *address);
|
||||||
|
|
||||||
|
#endif
|
53
firmware/trezor.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "trezor.h"
|
||||||
|
#include "oled.h"
|
||||||
|
#include "bitmaps.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include "usb.h"
|
||||||
|
#include "setup.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "layout2.h"
|
||||||
|
#include "ssp.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
setup();
|
||||||
|
oledInit();
|
||||||
|
// __stack_chk_guard_setup();
|
||||||
|
#if DEBUG_LINK
|
||||||
|
oledSetDebug(1);
|
||||||
|
storage_reset(); // wipe storage if debug link
|
||||||
|
storage_reset_uuid();
|
||||||
|
storage_commit();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
oledDrawBitmap(40, 0, &bmp_logo64);
|
||||||
|
oledRefresh();
|
||||||
|
|
||||||
|
storage_init();
|
||||||
|
layoutHome();
|
||||||
|
usbInit();
|
||||||
|
for (;;) {
|
||||||
|
usbPoll();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
38
firmware/trezor.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __TREZOR_H__
|
||||||
|
#define __TREZOR_H__
|
||||||
|
|
||||||
|
#define VERSION_MAJOR 1
|
||||||
|
#define VERSION_MINOR 0
|
||||||
|
#define VERSION_PATCH 0
|
||||||
|
|
||||||
|
#define STR(X) #X
|
||||||
|
#define VERSTR(X) STR(X)
|
||||||
|
|
||||||
|
#ifndef DEBUG_LINK
|
||||||
|
#define DEBUG_LINK 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_LOG
|
||||||
|
#define DEBUG_LOG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
334
firmware/usb.c
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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/usbd.h>
|
||||||
|
#include <libopencm3/usb/hid.h>
|
||||||
|
|
||||||
|
#include "trezor.h"
|
||||||
|
#include "usb.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "messages.h"
|
||||||
|
#include "storage.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define ENDPOINT_ADDRESS_IN (0x81)
|
||||||
|
#define ENDPOINT_ADDRESS_OUT (0x01)
|
||||||
|
#define ENDPOINT_ADDRESS_DEBUG_IN (0x82)
|
||||||
|
#define ENDPOINT_ADDRESS_DEBUG_OUT (0x02)
|
||||||
|
|
||||||
|
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 = 0x534c,
|
||||||
|
.idProduct = 0x0001,
|
||||||
|
.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 = 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 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),
|
||||||
|
}};
|
||||||
|
|
||||||
|
#if DEBUG_LINK
|
||||||
|
static const struct usb_endpoint_descriptor hid_endpoints_debug[2] = {{
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = ENDPOINT_ADDRESS_DEBUG_IN,
|
||||||
|
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
|
||||||
|
.wMaxPacketSize = 64,
|
||||||
|
.bInterval = 1,
|
||||||
|
}, {
|
||||||
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_ENDPOINT,
|
||||||
|
.bEndpointAddress = ENDPOINT_ADDRESS_DEBUG_OUT,
|
||||||
|
.bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT,
|
||||||
|
.wMaxPacketSize = 64,
|
||||||
|
.bInterval = 1,
|
||||||
|
}};
|
||||||
|
|
||||||
|
static const struct usb_interface_descriptor hid_iface_debug[] = {{
|
||||||
|
.bLength = USB_DT_INTERFACE_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_INTERFACE,
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
|
.bInterfaceSubClass = 0,
|
||||||
|
.bInterfaceProtocol = 0,
|
||||||
|
.iInterface = 0,
|
||||||
|
.endpoint = hid_endpoints_debug,
|
||||||
|
.extra = &hid_function,
|
||||||
|
.extralen = sizeof(hid_function),
|
||||||
|
}};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct usb_interface ifaces[] = {{
|
||||||
|
.num_altsetting = 1,
|
||||||
|
.altsetting = hid_iface,
|
||||||
|
#if DEBUG_LINK
|
||||||
|
}, {
|
||||||
|
.num_altsetting = 1,
|
||||||
|
.altsetting = hid_iface_debug,
|
||||||
|
#endif
|
||||||
|
}};
|
||||||
|
|
||||||
|
static const struct usb_config_descriptor config = {
|
||||||
|
.bLength = USB_DT_CONFIGURATION_SIZE,
|
||||||
|
.bDescriptorType = USB_DT_CONFIGURATION,
|
||||||
|
.wTotalLength = 0,
|
||||||
|
#if DEBUG_LINK
|
||||||
|
.bNumInterfaces = 2,
|
||||||
|
#else
|
||||||
|
.bNumInterfaces = 1,
|
||||||
|
#endif
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = 0,
|
||||||
|
.bmAttributes = 0x80,
|
||||||
|
.bMaxPower = 0x32,
|
||||||
|
.interface = ifaces,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *usb_strings[] = {
|
||||||
|
"SatoshiLabs",
|
||||||
|
"TREZOR",
|
||||||
|
(const char *)storage_uuid_str,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hid_control_request(usbd_device *dev, struct usb_setup_data *req, uint8_t **buf, uint16_t *len,
|
||||||
|
void (**complete)(usbd_device *, struct usb_setup_data *))
|
||||||
|
{
|
||||||
|
(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 volatile char tiny = 0;
|
||||||
|
|
||||||
|
static void hid_rx_callback(usbd_device *dev, uint8_t ep)
|
||||||
|
{
|
||||||
|
(void)ep;
|
||||||
|
static uint8_t buf[64] __attribute__ ((aligned(4)));
|
||||||
|
if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_OUT, buf, 64) != 64) return;
|
||||||
|
debugLog(0, "", "hid_rx_callback");
|
||||||
|
if (!tiny) {
|
||||||
|
msg_read(buf, 64);
|
||||||
|
} else {
|
||||||
|
msg_read_tiny(buf, 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG_LINK
|
||||||
|
static void hid_debug_rx_callback(usbd_device *dev, uint8_t ep)
|
||||||
|
{
|
||||||
|
(void)ep;
|
||||||
|
static uint8_t buf[64] __attribute__ ((aligned(4)));
|
||||||
|
if ( usbd_ep_read_packet(dev, ENDPOINT_ADDRESS_DEBUG_OUT, buf, 64) != 64) return;
|
||||||
|
debugLog(0, "", "hid_debug_rx_callback");
|
||||||
|
if (!tiny) {
|
||||||
|
msg_debug_read(buf, 64);
|
||||||
|
} else {
|
||||||
|
msg_read_tiny(buf, 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void hid_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, hid_rx_callback);
|
||||||
|
#if DEBUG_LINK
|
||||||
|
usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_IN, USB_ENDPOINT_ATTR_INTERRUPT, 64, 0);
|
||||||
|
usbd_ep_setup(dev, ENDPOINT_ADDRESS_DEBUG_OUT, USB_ENDPOINT_ATTR_INTERRUPT, 64, hid_debug_rx_callback);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usbPoll(void)
|
||||||
|
{
|
||||||
|
static uint8_t *data;
|
||||||
|
// poll read buffer
|
||||||
|
usbd_poll(usbd_dev);
|
||||||
|
// write pending data
|
||||||
|
data = msg_out_data();
|
||||||
|
if (data) {
|
||||||
|
while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_IN, data, 64) != 64 ) {}
|
||||||
|
}
|
||||||
|
#if DEBUG_LINK
|
||||||
|
// write pending debug data
|
||||||
|
data = msg_debug_out_data();
|
||||||
|
if (data) {
|
||||||
|
while ( usbd_ep_write_packet(usbd_dev, ENDPOINT_ADDRESS_DEBUG_IN, data, 64) != 64 ) {}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void usbReconnect(void)
|
||||||
|
{
|
||||||
|
usbd_disconnect(usbd_dev, 1);
|
||||||
|
delay(1000);
|
||||||
|
usbd_disconnect(usbd_dev, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usbTiny(char set)
|
||||||
|
{
|
||||||
|
tiny = set;
|
||||||
|
}
|
28
firmware/usb.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 usbInit(void);
|
||||||
|
void usbPoll(void);
|
||||||
|
void usbReconnect(void);
|
||||||
|
void usbTiny(char set);
|
||||||
|
|
||||||
|
#endif
|
49
gen/bitmaps.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "bitmaps.h"
|
||||||
|
|
||||||
|
const uint8_t bmp_digit0_data[] = { 0xff, 0xff, 0xf8, 0x1f, 0xf0, 0x0f, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xf0, 0x0f, 0xf8, 0x1f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit1_data[] = { 0xff, 0xff, 0xfc, 0x3f, 0xf8, 0x3f, 0xf0, 0x3f, 0xf0, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xfc, 0x3f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit2_data[] = { 0xff, 0xff, 0xe0, 0x1f, 0xe0, 0x0f, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xf8, 0x0f, 0xf0, 0x1f, 0xe1, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xe0, 0x07, 0xe0, 0x07, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit3_data[] = { 0xff, 0xff, 0xe0, 0x1f, 0xe0, 0x0f, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xf8, 0x0f, 0xf8, 0x0f, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xe0, 0x0f, 0xe0, 0x1f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit4_data[] = { 0xff, 0xff, 0xff, 0x0f, 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf1, 0x0f, 0xe3, 0x0f, 0xc7, 0x0f, 0xcf, 0x0f, 0xc0, 0x0f, 0xc0, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit5_data[] = { 0xff, 0xff, 0xe0, 0x1f, 0xe0, 0x1f, 0xe7, 0xff, 0xe7, 0xff, 0xe0, 0x1f, 0xe0, 0x0f, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xff, 0x87, 0xe0, 0x0f, 0xe0, 0x1f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit6_data[] = { 0xff, 0xff, 0xf8, 0x1f, 0xf0, 0x1f, 0xe1, 0xff, 0xe1, 0xff, 0xe0, 0x1f, 0xe0, 0x0f, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xf0, 0x0f, 0xf8, 0x1f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit7_data[] = { 0xff, 0xff, 0xe0, 0x07, 0xe0, 0x07, 0xff, 0x87, 0xff, 0x87, 0xff, 0x0f, 0xfe, 0x1f, 0xfc, 0x1f, 0xfc, 0x3f, 0xf8, 0x7f, 0xf8, 0x7f, 0xf8, 0x7f, 0xf8, 0x7f, 0xf8, 0x7f, 0xf8, 0x7f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit8_data[] = { 0xff, 0xff, 0xf8, 0x1f, 0xf0, 0x0f, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xf0, 0x0f, 0xf0, 0x0f, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xf0, 0x0f, 0xf8, 0x1f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_digit9_data[] = { 0xff, 0xff, 0xf8, 0x1f, 0xf0, 0x0f, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xe1, 0x87, 0xf0, 0x07, 0xf8, 0x07, 0xff, 0x87, 0xff, 0x87, 0xf8, 0x0f, 0xf8, 0x1f, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_gears0_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0c, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x1e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x3e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x1f, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x1f, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x1f, 0xf0, 0x1f, 0xc0, 0x00, 0x00, 0x01, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x3e, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfc, 0xc0, 0x18, 0x00, 0x00, 0x7f, 0xfd, 0xe0, 0x3c, 0x00, 0x00, 0x7f, 0xfd, 0xe0, 0x7c, 0x00, 0x00, 0xff, 0xfd, 0xff, 0xf8, 0x00, 0x00, 0xf8, 0x7e, 0xff, 0xf8, 0x00, 0x00, 0xf0, 0x1e, 0xff, 0xf0, 0x00, 0x00, 0x60, 0x0d, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x3f, 0xc0, 0x00, 0x00, 0x3f, 0xe0, 0x3f, 0xc0, 0x00, 0x00, 0x3f, 0xe0, 0x3f, 0xc0, 0x00, 0x00, 0x07, 0xe0, 0x3e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_gears1_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x07, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xcf, 0x80, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x3e, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x01, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x01, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x01, 0xf8, 0x3f, 0xe1, 0xc0, 0x00, 0x00, 0xff, 0xfc, 0xc1, 0xc0, 0x00, 0x00, 0xff, 0xfb, 0x03, 0xc0, 0x00, 0x01, 0xff, 0xf7, 0xc3, 0xc0, 0x00, 0x03, 0xff, 0xf7, 0xff, 0xc0, 0x00, 0x03, 0xc7, 0xf7, 0xff, 0xe0, 0x00, 0x01, 0x81, 0xf3, 0xff, 0xf0, 0x00, 0x00, 0x00, 0xf1, 0xff, 0xff, 0x80, 0x00, 0x00, 0xe1, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xf0, 0x7f, 0xc0, 0x00, 0x00, 0x03, 0xe0, 0x3f, 0x80, 0x00, 0x00, 0x03, 0xe0, 0x3e, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x3e, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3e, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x1f, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_gears2_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc1, 0x80, 0x00, 0x00, 0x07, 0x3f, 0xf7, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x3e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x03, 0xf8, 0x3f, 0x0e, 0x00, 0x00, 0x07, 0xff, 0xff, 0x8f, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xcf, 0x00, 0x00, 0x06, 0x1f, 0xe1, 0x9f, 0x83, 0x00, 0x00, 0x0f, 0xce, 0x7f, 0xef, 0x80, 0x00, 0x07, 0x9f, 0xff, 0xff, 0x80, 0x00, 0x07, 0x9f, 0xff, 0xff, 0x00, 0x00, 0x03, 0x8f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x07, 0xf0, 0x7e, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x0c, 0x3f, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_gears3_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x01, 0x81, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xe1, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0xf8, 0x3f, 0xe0, 0x00, 0x00, 0x01, 0xf0, 0x1f, 0xc0, 0x00, 0x00, 0x01, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x1e, 0x00, 0x00, 0x00, 0x0f, 0xf8, 0x3e, 0x70, 0x00, 0x00, 0x00, 0xff, 0xff, 0x78, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x78, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x7c, 0x0e, 0x00, 0x00, 0x1f, 0xff, 0x7f, 0x9f, 0x00, 0x00, 0x1f, 0x87, 0x7f, 0xff, 0x00, 0x00, 0x1e, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x1e, 0x01, 0xff, 0xfc, 0x00, 0x00, 0x0c, 0x1f, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x3f, 0xf0, 0x7c, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x3c, 0x00, 0x00, 0x00, 0x3f, 0xe0, 0x3e, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x3f, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x3f, 0xc0, 0x00, 0x00, 0x03, 0xe0, 0x3f, 0xc0, 0x00, 0x00, 0x03, 0xf0, 0x7f, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x07, 0x8f, 0xe0, 0x00, 0x00, 0x00, 0x03, 0x03, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_icon_error_data[] = { 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7b, 0xde, 0xf1, 0x8f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfc, 0x3f, 0xf8, 0x1f, 0xf1, 0x8f, 0x7b, 0xde, 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, };
|
||||||
|
const uint8_t bmp_icon_info_data[] = { 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3e, 0x7c, 0x7e, 0x7e, 0xff, 0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0x7e, 0x7e, 0x3e, 0x7c, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, };
|
||||||
|
const uint8_t bmp_icon_ok_data[] = { 0x07, 0xe0, 0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 0xff, 0xef, 0xff, 0xdf, 0xff, 0xbf, 0xf9, 0x3f, 0xf8, 0x7f, 0xfc, 0xff, 0x7e, 0xfe, 0x3f, 0xfc, 0x1f, 0xf8, 0x0f, 0xf0, 0x07, 0xe0, };
|
||||||
|
const uint8_t bmp_icon_question_data[] = { 0x07, 0xe0, 0x0f, 0xf0, 0x1e, 0x78, 0x3c, 0x3c, 0x79, 0x9e, 0xf3, 0xcf, 0xff, 0xcf, 0xff, 0x9f, 0xff, 0x3f, 0xfe, 0x7f, 0xfe, 0x7f, 0x7f, 0xfe, 0x3e, 0x7c, 0x1e, 0x78, 0x0f, 0xf0, 0x07, 0xe0, };
|
||||||
|
const uint8_t bmp_icon_warning_data[] = { 0x01, 0x80, 0x01, 0x80, 0x03, 0xc0, 0x03, 0xc0, 0x07, 0xe0, 0x07, 0xe0, 0x0e, 0x70, 0x0e, 0x70, 0x1e, 0x78, 0x1e, 0x78, 0x3e, 0x7c, 0x3f, 0xfc, 0x7e, 0x7e, 0x7e, 0x7e, 0xff, 0xff, 0xff, 0xff, };
|
||||||
|
const uint8_t bmp_logo48_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x0f, 0xff, 0xf0, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x3f, 0x81, 0xfc, 0x00, 0x00, 0x3e, 0x00, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x3e, 0x00, 0x00, 0x78, 0x00, 0x1e, 0x00, 0x00, 0xf8, 0x00, 0x1f, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x00, 0xf0, 0x00, 0x0f, 0x00, 0x03, 0xff, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xc0, 0x00, 0x03, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0x80, 0x00, 0x01, 0xf0, 0x0f, 0xe0, 0x00, 0x07, 0xf0, 0x0f, 0xfc, 0x00, 0x3f, 0xf0, 0x0f, 0xff, 0x81, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1f, 0xff, 0xf8, 0x00, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_logo48_empty_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x01, 0x81, 0x80, 0x00, 0x00, 0x06, 0x00, 0x60, 0x00, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x10, 0x7e, 0x08, 0x00, 0x00, 0x21, 0x81, 0x84, 0x00, 0x00, 0x22, 0x00, 0x44, 0x00, 0x00, 0x44, 0x00, 0x22, 0x00, 0x00, 0x48, 0x00, 0x12, 0x00, 0x00, 0x88, 0x00, 0x11, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x00, 0x90, 0x00, 0x09, 0x00, 0x03, 0x9f, 0xff, 0xf9, 0xc0, 0x04, 0x00, 0x00, 0x00, 0x20, 0x08, 0x00, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, 0x10, 0x08, 0x3f, 0xff, 0xfc, 0x10, 0x08, 0x40, 0x00, 0x02, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x80, 0x00, 0x01, 0x10, 0x08, 0x60, 0x00, 0x06, 0x10, 0x08, 0x1c, 0x00, 0x38, 0x10, 0x08, 0x03, 0x81, 0xc0, 0x10, 0x07, 0x00, 0x7e, 0x00, 0xe0, 0x00, 0xe0, 0x00, 0x07, 0x00, 0x00, 0x1c, 0x00, 0x38, 0x00, 0x00, 0x03, 0x81, 0xc0, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_logo64_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0x80, 0x00, 0x00, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x3f, 0xf0, 0x1f, 0xf8, 0x00, 0x00, 0x3f, 0xc0, 0x07, 0xf8, 0x00, 0x00, 0x7f, 0x80, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x7e, 0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xff, 0x80, 0x03, 0xff, 0xf0, 0x1f, 0xc0, 0x00, 0x00, 0x07, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0x80, 0x00, 0x00, 0x03, 0xf0, 0x1f, 0xe0, 0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xfc, 0x00, 0x00, 0x7f, 0xf0, 0x1f, 0xff, 0x00, 0x01, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x1f, 0xff, 0xf0, 0x1f, 0xff, 0xfc, 0x7f, 0xff, 0xf0, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
const uint8_t bmp_logo64_empty_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x70, 0x1c, 0x00, 0x00, 0x00, 0x01, 0x80, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x08, 0x00, 0x00, 0x20, 0x00, 0x00, 0x10, 0x0f, 0xe0, 0x10, 0x00, 0x00, 0x20, 0x30, 0x18, 0x08, 0x00, 0x00, 0x20, 0x40, 0x04, 0x08, 0x00, 0x00, 0x40, 0x80, 0x02, 0x04, 0x00, 0x00, 0x41, 0x00, 0x01, 0x04, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x82, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x84, 0x00, 0x00, 0x42, 0x00, 0x00, 0x87, 0xff, 0xff, 0xc2, 0x00, 0x03, 0x80, 0x00, 0x00, 0x03, 0x80, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x70, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x7f, 0xfc, 0x00, 0x10, 0x10, 0x3f, 0x80, 0x03, 0xf8, 0x10, 0x10, 0x40, 0x00, 0x00, 0x04, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x80, 0x00, 0x00, 0x02, 0x10, 0x10, 0x60, 0x00, 0x00, 0x0c, 0x10, 0x10, 0x1c, 0x00, 0x00, 0x70, 0x10, 0x10, 0x03, 0x00, 0x01, 0x80, 0x10, 0x10, 0x00, 0xf0, 0x1e, 0x00, 0x10, 0x10, 0x00, 0x0c, 0x60, 0x00, 0x10, 0x0e, 0x00, 0x03, 0x80, 0x00, 0xe0, 0x01, 0xc0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x30, 0x00, 0x00, 0x18, 0x00, 0x00, 0x0e, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x01, 0xc0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };
|
||||||
|
|
||||||
|
const BITMAP bmp_digit0 = {16, 16, bmp_digit0_data};
|
||||||
|
const BITMAP bmp_digit1 = {16, 16, bmp_digit1_data};
|
||||||
|
const BITMAP bmp_digit2 = {16, 16, bmp_digit2_data};
|
||||||
|
const BITMAP bmp_digit3 = {16, 16, bmp_digit3_data};
|
||||||
|
const BITMAP bmp_digit4 = {16, 16, bmp_digit4_data};
|
||||||
|
const BITMAP bmp_digit5 = {16, 16, bmp_digit5_data};
|
||||||
|
const BITMAP bmp_digit6 = {16, 16, bmp_digit6_data};
|
||||||
|
const BITMAP bmp_digit7 = {16, 16, bmp_digit7_data};
|
||||||
|
const BITMAP bmp_digit8 = {16, 16, bmp_digit8_data};
|
||||||
|
const BITMAP bmp_digit9 = {16, 16, bmp_digit9_data};
|
||||||
|
const BITMAP bmp_gears0 = {48, 48, bmp_gears0_data};
|
||||||
|
const BITMAP bmp_gears1 = {48, 48, bmp_gears1_data};
|
||||||
|
const BITMAP bmp_gears2 = {48, 48, bmp_gears2_data};
|
||||||
|
const BITMAP bmp_gears3 = {48, 48, bmp_gears3_data};
|
||||||
|
const BITMAP bmp_icon_error = {16, 16, bmp_icon_error_data};
|
||||||
|
const BITMAP bmp_icon_info = {16, 16, bmp_icon_info_data};
|
||||||
|
const BITMAP bmp_icon_ok = {16, 16, bmp_icon_ok_data};
|
||||||
|
const BITMAP bmp_icon_question = {16, 16, bmp_icon_question_data};
|
||||||
|
const BITMAP bmp_icon_warning = {16, 16, bmp_icon_warning_data};
|
||||||
|
const BITMAP bmp_logo48 = {40, 48, bmp_logo48_data};
|
||||||
|
const BITMAP bmp_logo48_empty = {40, 48, bmp_logo48_empty_data};
|
||||||
|
const BITMAP bmp_logo64 = {48, 64, bmp_logo64_data};
|
||||||
|
const BITMAP bmp_logo64_empty = {48, 64, bmp_logo64_empty_data};
|
35
gen/bitmaps.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef __BITMAPS_H__
|
||||||
|
#define __BITMAPS_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t width, height;
|
||||||
|
const uint8_t *data;
|
||||||
|
} BITMAP;
|
||||||
|
|
||||||
|
extern const BITMAP bmp_digit0;
|
||||||
|
extern const BITMAP bmp_digit1;
|
||||||
|
extern const BITMAP bmp_digit2;
|
||||||
|
extern const BITMAP bmp_digit3;
|
||||||
|
extern const BITMAP bmp_digit4;
|
||||||
|
extern const BITMAP bmp_digit5;
|
||||||
|
extern const BITMAP bmp_digit6;
|
||||||
|
extern const BITMAP bmp_digit7;
|
||||||
|
extern const BITMAP bmp_digit8;
|
||||||
|
extern const BITMAP bmp_digit9;
|
||||||
|
extern const BITMAP bmp_gears0;
|
||||||
|
extern const BITMAP bmp_gears1;
|
||||||
|
extern const BITMAP bmp_gears2;
|
||||||
|
extern const BITMAP bmp_gears3;
|
||||||
|
extern const BITMAP bmp_icon_error;
|
||||||
|
extern const BITMAP bmp_icon_info;
|
||||||
|
extern const BITMAP bmp_icon_ok;
|
||||||
|
extern const BITMAP bmp_icon_question;
|
||||||
|
extern const BITMAP bmp_icon_warning;
|
||||||
|
extern const BITMAP bmp_logo48;
|
||||||
|
extern const BITMAP bmp_logo48_empty;
|
||||||
|
extern const BITMAP bmp_logo64;
|
||||||
|
extern const BITMAP bmp_logo64_empty;
|
||||||
|
|
||||||
|
#endif
|
2
gen/bitmaps/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
*.c
|
||||||
|
*.h
|
BIN
gen/bitmaps/digit0.png
Normal file
After Width: | Height: | Size: 121 B |
BIN
gen/bitmaps/digit1.png
Normal file
After Width: | Height: | Size: 112 B |
BIN
gen/bitmaps/digit2.png
Normal file
After Width: | Height: | Size: 129 B |
BIN
gen/bitmaps/digit3.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
gen/bitmaps/digit4.png
Normal file
After Width: | Height: | Size: 137 B |
BIN
gen/bitmaps/digit5.png
Normal file
After Width: | Height: | Size: 128 B |
BIN
gen/bitmaps/digit6.png
Normal file
After Width: | Height: | Size: 133 B |
BIN
gen/bitmaps/digit7.png
Normal file
After Width: | Height: | Size: 127 B |
BIN
gen/bitmaps/digit8.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
gen/bitmaps/digit9.png
Normal file
After Width: | Height: | Size: 132 B |
BIN
gen/bitmaps/gears0.png
Normal file
After Width: | Height: | Size: 301 B |
BIN
gen/bitmaps/gears1.png
Normal file
After Width: | Height: | Size: 326 B |
BIN
gen/bitmaps/gears2.png
Normal file
After Width: | Height: | Size: 293 B |
BIN
gen/bitmaps/gears3.png
Normal file
After Width: | Height: | Size: 330 B |
59
gen/bitmaps/generate.py
Executable file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
hdrs = []
|
||||||
|
data = []
|
||||||
|
imgs = []
|
||||||
|
|
||||||
|
def encode_pixels(img):
|
||||||
|
r = ''
|
||||||
|
img = [ (x[0] + x[1] + x[2] > 384 and '1' or '0') for x in img]
|
||||||
|
for i in range(len(img) / 8):
|
||||||
|
c = ''.join(img[i * 8 : i * 8 + 8])
|
||||||
|
r += '0x%02x, ' % int(c, 2)
|
||||||
|
return r
|
||||||
|
|
||||||
|
cnt = 0
|
||||||
|
for fn in sorted(glob.glob('*.png')):
|
||||||
|
print 'Processing:', fn
|
||||||
|
im = Image.open(fn)
|
||||||
|
name = os.path.splitext(fn)[0]
|
||||||
|
w, h = im.size
|
||||||
|
if w % 8 != 0:
|
||||||
|
raise Exception('Width must be divisable by 8! (%s is %dx%d)' % (fn, w, h))
|
||||||
|
img = list(im.getdata())
|
||||||
|
hdrs.append('extern const BITMAP bmp_%s;\n' % name)
|
||||||
|
imgs.append('const BITMAP bmp_%s = {%d, %d, bmp_%s_data};\n' % (name, w, h, name))
|
||||||
|
data.append('const uint8_t bmp_%s_data[] = { %s};\n' % (name, encode_pixels(img)))
|
||||||
|
cnt += 1
|
||||||
|
|
||||||
|
with open('../bitmaps.c', 'wt') as f:
|
||||||
|
f.write('#include "bitmaps.h"\n\n')
|
||||||
|
for i in range(cnt):
|
||||||
|
f.write(data[i])
|
||||||
|
f.write('\n')
|
||||||
|
for i in range(cnt):
|
||||||
|
f.write(imgs[i])
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
with open('../bitmaps.h', 'wt') as f:
|
||||||
|
f.write('''#ifndef __BITMAPS_H__
|
||||||
|
#define __BITMAPS_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t width, height;
|
||||||
|
const uint8_t *data;
|
||||||
|
} BITMAP;
|
||||||
|
|
||||||
|
''')
|
||||||
|
|
||||||
|
for i in range(cnt):
|
||||||
|
f.write(hdrs[i])
|
||||||
|
|
||||||
|
f.write('\n#endif\n')
|
||||||
|
f.close()
|
BIN
gen/bitmaps/icon_error.png
Normal file
After Width: | Height: | Size: 152 B |
BIN
gen/bitmaps/icon_info.png
Normal file
After Width: | Height: | Size: 138 B |
BIN
gen/bitmaps/icon_ok.png
Normal file
After Width: | Height: | Size: 167 B |
BIN
gen/bitmaps/icon_question.png
Normal file
After Width: | Height: | Size: 153 B |
BIN
gen/bitmaps/icon_warning.png
Normal file
After Width: | Height: | Size: 138 B |
BIN
gen/bitmaps/logo48.png
Normal file
After Width: | Height: | Size: 283 B |
BIN
gen/bitmaps/logo48_empty.png
Normal file
After Width: | Height: | Size: 310 B |
BIN
gen/bitmaps/logo64.png
Normal file
After Width: | Height: | Size: 317 B |
BIN
gen/bitmaps/logo64_empty.png
Normal file
After Width: | Height: | Size: 444 B |
118
gen/fonts.c
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#include "fonts.h"
|
||||||
|
|
||||||
|
const uint8_t *font_data[FONT_END - FONT_START + 1] = {
|
||||||
|
(uint8_t *)"\x01\x00",
|
||||||
|
(uint8_t *)"\x02\xfa\xfa",
|
||||||
|
(uint8_t *)"\x03\xc0\x00\xc0",
|
||||||
|
(uint8_t *)"\x05\x6c\xfe\x6c\xfe\x6c",
|
||||||
|
(uint8_t *)"\x05\x32\xff\x5a\xff\x4c",
|
||||||
|
(uint8_t *)"\x06\xc0\xc6\x1c\x70\xc6\x06",
|
||||||
|
(uint8_t *)"\x06\x5c\xfe\xb2\xfe\x4c\x1e",
|
||||||
|
(uint8_t *)"\x01\xc0",
|
||||||
|
(uint8_t *)"\x03\x3c\x7e\x81",
|
||||||
|
(uint8_t *)"\x03\x81\x7e\x3c",
|
||||||
|
(uint8_t *)"\x05\x6c\x38\xfe\x38\x6c",
|
||||||
|
(uint8_t *)"\x05\x10\x10\x7c\x10\x10",
|
||||||
|
(uint8_t *)"\x02\x03\x06",
|
||||||
|
(uint8_t *)"\x04\x10\x10\x10\x10",
|
||||||
|
(uint8_t *)"\x02\x06\x06",
|
||||||
|
(uint8_t *)"\x03\x0e\x38\xe0",
|
||||||
|
(uint8_t *)"\x05\x7c\xfe\x82\xfe\x7c",
|
||||||
|
(uint8_t *)"\x03\x40\xfe\xfe",
|
||||||
|
(uint8_t *)"\x05\x8e\x9e\x92\xf2\x62",
|
||||||
|
(uint8_t *)"\x05\x82\x92\x92\xfe\x6c",
|
||||||
|
(uint8_t *)"\x05\x18\x28\x48\xfe\xfe",
|
||||||
|
(uint8_t *)"\x05\xe2\xa2\xa2\xbe\x1c",
|
||||||
|
(uint8_t *)"\x05\x7c\xfe\xa2\xbe\x1c",
|
||||||
|
(uint8_t *)"\x05\x80\x8e\xbe\xf0\xc0",
|
||||||
|
(uint8_t *)"\x05\x6c\xfe\x92\xfe\x6c",
|
||||||
|
(uint8_t *)"\x05\x70\xfa\x8a\xfe\x7c",
|
||||||
|
(uint8_t *)"\x02\x36\x36",
|
||||||
|
(uint8_t *)"\x02\x33\x36",
|
||||||
|
(uint8_t *)"\x04\x10\x38\x6c\xc6",
|
||||||
|
(uint8_t *)"\x04\x28\x28\x28\x28",
|
||||||
|
(uint8_t *)"\x04\xc6\x6c\x38\x10",
|
||||||
|
(uint8_t *)"\x05\x80\x9a\xba\xe0\x40",
|
||||||
|
(uint8_t *)"\x06\x7c\xfe\xaa\xba\xfa\x78",
|
||||||
|
(uint8_t *)"\x05\x7e\xfe\x88\xfe\x7e",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\xa2\xfe\x5c",
|
||||||
|
(uint8_t *)"\x05\x7c\xfe\x82\x82\x82",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x82\xfe\x7c",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\xa2\xa2\x82",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\xa0\xa0\x80",
|
||||||
|
(uint8_t *)"\x05\x7c\xfe\x82\x9e\x1e",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x20\xfe\xfe",
|
||||||
|
(uint8_t *)"\x02\xfe\xfe",
|
||||||
|
(uint8_t *)"\x04\x02\x02\xfe\xfc",
|
||||||
|
(uint8_t *)"\x06\xfe\xfe\x38\x6c\xc6\x82",
|
||||||
|
(uint8_t *)"\x04\xfe\xfe\x02\x02",
|
||||||
|
(uint8_t *)"\x07\xfe\x7e\x30\x18\x30\x7e\xfe",
|
||||||
|
(uint8_t *)"\x06\xfe\x7e\x30\x18\xfc\xfe",
|
||||||
|
(uint8_t *)"\x06\x7c\xfe\x82\x82\xfe\x7c",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x88\xf8\x70",
|
||||||
|
(uint8_t *)"\x06\x7c\xfe\x82\x86\xff\x7d",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x88\xfe\x72",
|
||||||
|
(uint8_t *)"\x04\x62\xf2\x9e\x8c",
|
||||||
|
(uint8_t *)"\x06\x80\x80\xfe\xfe\x80\x80",
|
||||||
|
(uint8_t *)"\x05\xfc\xfe\x02\xfe\xfc",
|
||||||
|
(uint8_t *)"\x06\xe0\xf8\x1e\x1e\xf8\xe0",
|
||||||
|
(uint8_t *)"\x07\xf0\xfe\x1e\x3c\x1e\xfe\xf0",
|
||||||
|
(uint8_t *)"\x06\xc6\xee\x38\x38\xee\xc6",
|
||||||
|
(uint8_t *)"\x06\xc0\xe0\x3e\x3e\xe0\xc0",
|
||||||
|
(uint8_t *)"\x05\x8e\x9e\xba\xf2\xe2",
|
||||||
|
(uint8_t *)"\x03\xff\xff\x81",
|
||||||
|
(uint8_t *)"\x03\xe0\x38\x0e",
|
||||||
|
(uint8_t *)"\x03\x81\xff\xff",
|
||||||
|
(uint8_t *)"\x03\x60\xc0\x60",
|
||||||
|
(uint8_t *)"\x06\x02\x02\x02\x02\x02\x02",
|
||||||
|
(uint8_t *)"\x02\x80\x40",
|
||||||
|
(uint8_t *)"\x05\x04\x2e\x2a\x3e\x1e",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x22\x3e\x1c",
|
||||||
|
(uint8_t *)"\x04\x1c\x3e\x22\x22",
|
||||||
|
(uint8_t *)"\x05\x1c\x3e\x22\xfe\xfe",
|
||||||
|
(uint8_t *)"\x05\x1c\x3e\x2a\x3a\x1a",
|
||||||
|
(uint8_t *)"\x03\x7e\xfe\xa0",
|
||||||
|
(uint8_t *)"\x05\x18\x3d\x25\x3f\x3e",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x20\x3e\x1e",
|
||||||
|
(uint8_t *)"\x02\xbe\xbe",
|
||||||
|
(uint8_t *)"\x03\x01\xbf\xbe",
|
||||||
|
(uint8_t *)"\x05\xfe\xfe\x1c\x36\x22",
|
||||||
|
(uint8_t *)"\x02\xfe\xfe",
|
||||||
|
(uint8_t *)"\x08\x3e\x3e\x20\x3e\x3e\x20\x3e\x1e",
|
||||||
|
(uint8_t *)"\x05\x3e\x3e\x20\x3e\x1e",
|
||||||
|
(uint8_t *)"\x05\x1c\x3e\x22\x3e\x1c",
|
||||||
|
(uint8_t *)"\x05\x3f\x3f\x24\x3c\x18",
|
||||||
|
(uint8_t *)"\x05\x18\x3c\x24\x3f\x3f",
|
||||||
|
(uint8_t *)"\x04\x3e\x3e\x10\x30",
|
||||||
|
(uint8_t *)"\x04\x1a\x3a\x2e\x2c",
|
||||||
|
(uint8_t *)"\x03\xfc\xfe\x22",
|
||||||
|
(uint8_t *)"\x05\x3c\x3e\x02\x3e\x3e",
|
||||||
|
(uint8_t *)"\x05\x30\x3c\x0e\x3c\x30",
|
||||||
|
(uint8_t *)"\x07\x38\x3e\x06\x1c\x06\x3e\x38",
|
||||||
|
(uint8_t *)"\x05\x36\x3e\x08\x3e\x36",
|
||||||
|
(uint8_t *)"\x05\x38\x3d\x05\x3f\x3e",
|
||||||
|
(uint8_t *)"\x05\x26\x2e\x3a\x32\x22",
|
||||||
|
(uint8_t *)"\x07\x44\xee\x7c\x38\x7c\xee\x44",
|
||||||
|
(uint8_t *)"\x02\xff\xff",
|
||||||
|
(uint8_t *)"\x07\x18\x1c\x0e\x18\x30\x40\x80",
|
||||||
|
(uint8_t *)"\x06\x10\x38\x7c\x10\x10\x10",
|
||||||
|
(uint8_t *)"\x06\x10\x10\x10\x7c\x38\x10",
|
||||||
|
(uint8_t *)"\x05\x7e\xff\x52\xff\x2c",
|
||||||
|
};
|
||||||
|
|
||||||
|
int fontCharWidth(char c) {
|
||||||
|
if ((c < FONT_START) || (c > FONT_END)) {
|
||||||
|
c = '?';
|
||||||
|
}
|
||||||
|
return font_data[(int)(c - FONT_START)][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
int fontStringWidth(const char *s) {
|
||||||
|
if (!s) return 0;
|
||||||
|
int l = 0;
|
||||||
|
char *c;
|
||||||
|
for (c = (char *)s; *c; c++) {
|
||||||
|
l += fontCharWidth(*c) + 1;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
15
gen/fonts.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef __FONTS_H__
|
||||||
|
#define __FONTS_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define FONT_START 32
|
||||||
|
#define FONT_END 128
|
||||||
|
#define FONT_HEIGHT 8
|
||||||
|
|
||||||
|
int fontCharWidth(char c);
|
||||||
|
int fontStringWidth(const char *s);
|
||||||
|
|
||||||
|
extern const uint8_t *font_data[FONT_END - FONT_START + 1];
|
||||||
|
|
||||||
|
#endif
|
BIN
gen/fonts/font.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
31
gen/fonts/generate.py
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
class Img(object):
|
||||||
|
|
||||||
|
def __init__(self, fn):
|
||||||
|
im = Image.open(fn)
|
||||||
|
self.w, self.h = im.size
|
||||||
|
self.data = list(im.getdata())
|
||||||
|
|
||||||
|
def pixel(self, r, c):
|
||||||
|
p = self.data[ r + c * self.w ]
|
||||||
|
if p == (255, 255, 255):
|
||||||
|
return '0'
|
||||||
|
if p == (0, 0, 0):
|
||||||
|
return '1'
|
||||||
|
if p == (255, 0, 255):
|
||||||
|
return None
|
||||||
|
raise Exception('Unknown color', p)
|
||||||
|
|
||||||
|
img = Img('font.png')
|
||||||
|
cur = ''
|
||||||
|
|
||||||
|
for i in range(img.w):
|
||||||
|
if img.pixel(i, 0) == None:
|
||||||
|
cur = '\\x%02x' % (len(cur) / 4) + cur
|
||||||
|
print '\t(uint8_t *)"%s",' % cur
|
||||||
|
cur = ''
|
||||||
|
continue
|
||||||
|
val = img.pixel(i, 0) + img.pixel(i, 1) + img.pixel(i, 2) + img.pixel(i, 3) + img.pixel(i, 4) + img.pixel(i, 5) + img.pixel(i, 6) + img.pixel(i, 7)
|
||||||
|
cur += '\\x%02x' % int(val, 2)
|
99
gen/handlers/handlers.py
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
handlers = [
|
||||||
|
'hard_fault_handler',
|
||||||
|
'mem_manage_handler',
|
||||||
|
'bus_fault_handler',
|
||||||
|
'usage_fault_handler',
|
||||||
|
'nvic_wwdg_isr',
|
||||||
|
'pvd_isr',
|
||||||
|
'tamp_stamp_isr',
|
||||||
|
'rtc_wkup_isr',
|
||||||
|
'flash_isr',
|
||||||
|
'rcc_isr',
|
||||||
|
'exti0_isr',
|
||||||
|
'exti1_isr',
|
||||||
|
'exti2_isr',
|
||||||
|
'exti3_isr',
|
||||||
|
'exti4_isr',
|
||||||
|
'dma1_stream0_isr',
|
||||||
|
'dma1_stream1_isr',
|
||||||
|
'dma1_stream2_isr',
|
||||||
|
'dma1_stream3_isr',
|
||||||
|
'dma1_stream4_isr',
|
||||||
|
'dma1_stream5_isr',
|
||||||
|
'dma1_stream6_isr',
|
||||||
|
'adc_isr',
|
||||||
|
'can1_tx_isr',
|
||||||
|
'can1_rx0_isr',
|
||||||
|
'can1_rx1_isr',
|
||||||
|
'can1_sce_isr',
|
||||||
|
'exti9_5_isr',
|
||||||
|
'tim1_brk_tim9_isr',
|
||||||
|
'tim1_up_tim10_isr',
|
||||||
|
'tim1_trg_com_tim11_isr',
|
||||||
|
'tim1_cc_isr',
|
||||||
|
'tim2_isr',
|
||||||
|
'tim3_isr',
|
||||||
|
'tim4_isr',
|
||||||
|
'i2c1_ev_isr',
|
||||||
|
'i2c1_er_isr',
|
||||||
|
'i2c2_ev_isr',
|
||||||
|
'i2c2_er_isr',
|
||||||
|
'spi1_isr',
|
||||||
|
'spi2_isr',
|
||||||
|
'usart1_isr',
|
||||||
|
'usart2_isr',
|
||||||
|
'usart3_isr',
|
||||||
|
'exti15_10_isr',
|
||||||
|
'rtc_alarm_isr',
|
||||||
|
'usb_fs_wkup_isr',
|
||||||
|
'tim8_brk_tim12_isr',
|
||||||
|
'tim8_up_tim13_isr',
|
||||||
|
'tim8_trg_com_tim14_isr',
|
||||||
|
'tim8_cc_isr',
|
||||||
|
'dma1_stream7_isr',
|
||||||
|
'fsmc_isr',
|
||||||
|
'sdio_isr',
|
||||||
|
'tim5_isr',
|
||||||
|
'spi3_isr',
|
||||||
|
'uart4_isr',
|
||||||
|
'uart5_isr',
|
||||||
|
'tim6_dac_isr',
|
||||||
|
'tim7_isr',
|
||||||
|
'dma2_stream0_isr',
|
||||||
|
'dma2_stream1_isr',
|
||||||
|
'dma2_stream2_isr',
|
||||||
|
'dma2_stream3_isr',
|
||||||
|
'dma2_stream4_isr',
|
||||||
|
'eth_isr',
|
||||||
|
'eth_wkup_isr',
|
||||||
|
'can2_tx_isr',
|
||||||
|
'can2_rx0_isr',
|
||||||
|
'can2_rx1_isr',
|
||||||
|
'can2_sce_isr',
|
||||||
|
'otg_fs_isr',
|
||||||
|
'dma2_stream5_isr',
|
||||||
|
'dma2_stream6_isr',
|
||||||
|
'dma2_stream7_isr',
|
||||||
|
'usart6_isr',
|
||||||
|
'i2c3_ev_isr',
|
||||||
|
'i2c3_er_isr',
|
||||||
|
'otg_hs_ep1_out_isr',
|
||||||
|
'otg_hs_ep1_in_isr',
|
||||||
|
'otg_hs_wkup_isr',
|
||||||
|
'otg_hs_isr',
|
||||||
|
'dcmi_isr',
|
||||||
|
'cryp_isr',
|
||||||
|
'hash_rng_isr',
|
||||||
|
]
|
||||||
|
|
||||||
|
with open('handlers.c', 'wt') as f:
|
||||||
|
f.write('#include "layout.h"\n')
|
||||||
|
f.write('#include "oled.h"\n\n')
|
||||||
|
for i in handlers:
|
||||||
|
f.write('void __attribute__((noreturn)) %s(void)\n' % i)
|
||||||
|
f.write('{\n')
|
||||||
|
f.write('\tlayoutDialog(DIALOG_ICON_ERROR, NULL, NULL, NULL, "Encountered", NULL, "%s", NULL, "Please restart", "the device.");\n' % i.upper())
|
||||||
|
f.write('\tfor (;;) {} // loop forever\n')
|
||||||
|
f.write('}\n\n')
|
105
layout.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 "layout.h"
|
||||||
|
#include "oled.h"
|
||||||
|
|
||||||
|
void layoutDialog(LayoutDialogIcon 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)
|
||||||
|
{
|
||||||
|
int left = 0;
|
||||||
|
oledClear();
|
||||||
|
switch (icon) {
|
||||||
|
case DIALOG_NOICON:
|
||||||
|
break;
|
||||||
|
case DIALOG_ICON_ERROR:
|
||||||
|
oledDrawBitmap(0, 0, &bmp_icon_error);
|
||||||
|
left = 20;
|
||||||
|
break;
|
||||||
|
case DIALOG_ICON_INFO:
|
||||||
|
oledDrawBitmap(0, 0, &bmp_icon_info);
|
||||||
|
left = 20;
|
||||||
|
break;
|
||||||
|
case DIALOG_ICON_QUESTION:
|
||||||
|
oledDrawBitmap(0, 0, &bmp_icon_question);
|
||||||
|
left = 20;
|
||||||
|
break;
|
||||||
|
case DIALOG_ICON_WARNING:
|
||||||
|
oledDrawBitmap(0, 0, &bmp_icon_warning);
|
||||||
|
left = 20;
|
||||||
|
break;
|
||||||
|
case DIALOG_ICON_OK:
|
||||||
|
oledDrawBitmap(0, 0, &bmp_icon_ok);
|
||||||
|
left = 20;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line1) oledDrawString(left, 0 * 9, line1);
|
||||||
|
if (line2) oledDrawString(left, 1 * 9, line2);
|
||||||
|
if (line3) oledDrawString(left, 2 * 9, line3);
|
||||||
|
if (line4) oledDrawString(left, 3 * 9, line4);
|
||||||
|
if (desc) {
|
||||||
|
oledDrawStringCenter(OLED_HEIGHT - 2 * 9 - 1, desc);
|
||||||
|
if (btnYes || btnNo) {
|
||||||
|
oledHLine(OLED_HEIGHT - 21);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (line5) oledDrawString(left, 4 * 9, line5);
|
||||||
|
if (line6) oledDrawString(left, 5 * 9, line6);
|
||||||
|
if (btnYes || btnNo) {
|
||||||
|
oledHLine(OLED_HEIGHT - 13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (btnNo) {
|
||||||
|
oledDrawString(1, OLED_HEIGHT - 8, "{");
|
||||||
|
oledDrawString(fontCharWidth('{') + 3, OLED_HEIGHT - 8, btnNo);
|
||||||
|
oledInvert(0, OLED_HEIGHT - 9, fontCharWidth('{') + fontStringWidth(btnNo) + 2, OLED_HEIGHT - 1);
|
||||||
|
}
|
||||||
|
if (btnYes) {
|
||||||
|
oledDrawString(OLED_WIDTH - fontCharWidth('}') - 1, OLED_HEIGHT - 8, "}");
|
||||||
|
oledDrawString(OLED_WIDTH - fontStringWidth(btnYes) - fontCharWidth('}') - 3, OLED_HEIGHT - 8, btnYes);
|
||||||
|
oledInvert(OLED_WIDTH - fontStringWidth(btnYes) - fontCharWidth('}') - 4, OLED_HEIGHT - 9, OLED_WIDTH - 1, OLED_HEIGHT - 1);
|
||||||
|
}
|
||||||
|
oledRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void layoutProgress(const char *desc, int permil, int gearstep)
|
||||||
|
{
|
||||||
|
const BITMAP *bmp_gears[4] = { &bmp_gears0, &bmp_gears1, &bmp_gears2, &bmp_gears3 };
|
||||||
|
oledClear();
|
||||||
|
oledDrawBitmap(40, 0, bmp_gears[gearstep % 4]);
|
||||||
|
// progressbar
|
||||||
|
oledFrame(0, OLED_HEIGHT - 8, OLED_WIDTH - 1, OLED_HEIGHT - 1);
|
||||||
|
oledBox(1, OLED_HEIGHT - 7, OLED_WIDTH - 2, OLED_HEIGHT - 2, 0);
|
||||||
|
permil = permil * (OLED_WIDTH - 4) / 1000;
|
||||||
|
if (permil < 0) {
|
||||||
|
permil = 0;
|
||||||
|
}
|
||||||
|
if (permil > OLED_WIDTH - 4) {
|
||||||
|
permil = OLED_WIDTH - 4;
|
||||||
|
}
|
||||||
|
oledBox(2, OLED_HEIGHT - 6, 1 + permil, OLED_HEIGHT - 3, 1);
|
||||||
|
|
||||||
|
// text
|
||||||
|
oledBox(0, OLED_HEIGHT - 16, OLED_WIDTH - 1, OLED_HEIGHT - 16 + 7, 0);
|
||||||
|
if (desc) {
|
||||||
|
oledDrawStringCenter(OLED_HEIGHT - 16, desc);
|
||||||
|
}
|
||||||
|
oledRefresh();
|
||||||
|
}
|
37
layout.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __LAYOUT_H__
|
||||||
|
#define __LAYOUT_H__
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DIALOG_NOICON = 0,
|
||||||
|
DIALOG_ICON_ERROR,
|
||||||
|
DIALOG_ICON_INFO,
|
||||||
|
DIALOG_ICON_QUESTION,
|
||||||
|
DIALOG_ICON_WARNING,
|
||||||
|
DIALOG_ICON_OK,
|
||||||
|
} LayoutDialogIcon;
|
||||||
|
|
||||||
|
void layoutDialog(LayoutDialogIcon 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 layoutProgress(const char *desc, int permil, int gearstep);
|
||||||
|
|
||||||
|
#endif
|
45
memory.c
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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/f2/flash.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "memory.h"
|
||||||
|
#include "sha2.h"
|
||||||
|
|
||||||
|
#define OPTION_BYTES_1 ((uint64_t *)0x1FFFC000)
|
||||||
|
#define OPTION_BYTES_2 ((uint64_t *)0x1FFFC008)
|
||||||
|
|
||||||
|
void memory_protect(void)
|
||||||
|
{
|
||||||
|
// set RDP level 2 WRP for sectors 0 and 1
|
||||||
|
if ((((*OPTION_BYTES_1) & 0xFFFF) == 0xCCFF) && (((*OPTION_BYTES_2) & 0xFFFF) == 0xFFFC)) {
|
||||||
|
return; // already set up correctly - bail out
|
||||||
|
}
|
||||||
|
flash_unlock_option_bytes();
|
||||||
|
// WRP + RDP
|
||||||
|
flash_program_option_bytes( 0xFFFC0000 + 0xCCFF);
|
||||||
|
flash_lock_option_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
int memory_bootloader_hash(uint8_t *hash)
|
||||||
|
{
|
||||||
|
sha256_Raw((const uint8_t *)FLASH_BOOT_START, FLASH_BOOT_LEN, hash);
|
||||||
|
sha256_Raw(hash, 32, hash);
|
||||||
|
return 32;
|
||||||
|
}
|
104
memory.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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 __MEMORY_H__
|
||||||
|
#define __MEMORY_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
flash memory layout:
|
||||||
|
|
||||||
|
name | range | size | function
|
||||||
|
-----------+-------------------------+---------+------------------
|
||||||
|
Sector 0 | 0x08000000 - 0x08003FFF | 16 KiB | bootloader code
|
||||||
|
Sector 1 | 0x08004000 - 0x08007FFF | 16 KiB | bootloader code
|
||||||
|
-----------+-------------------------+---------+------------------
|
||||||
|
Sector 2 | 0x08008000 - 0x0800BFFF | 16 KiB | metadata area
|
||||||
|
Sector 3 | 0x0800C000 - 0x0800FFFF | 16 KiB | metadata area
|
||||||
|
-----------+-------------------------+---------+------------------
|
||||||
|
Sector 4 | 0x08010000 - 0x0801FFFF | 64 KiB | application code
|
||||||
|
Sector 5 | 0x08020000 - 0x0803FFFF | 128 KiB | application code
|
||||||
|
Sector 6 | 0x08040000 - 0x0805FFFF | 128 KiB | application code
|
||||||
|
Sector 7 | 0x08060000 - 0x0807FFFF | 128 KiB | application code
|
||||||
|
===========+=========================+============================
|
||||||
|
Sector 8 | 0x08080000 - 0x0809FFFF | 128 KiB | N/A
|
||||||
|
Sector 9 | 0x080A0000 - 0x080BFFFF | 128 KiB | N/A
|
||||||
|
Sector 10 | 0x080C0000 - 0x080DFFFF | 128 KiB | N/A
|
||||||
|
Sector 11 | 0x080E0000 - 0x080FFFFF | 128 KiB | N/A
|
||||||
|
|
||||||
|
metadata area:
|
||||||
|
|
||||||
|
offset | type/length | description
|
||||||
|
--------+-------------+-------------------------------
|
||||||
|
0x0000 | 4 bytes | magic = 'TRZR'
|
||||||
|
0x0004 | uint32 | length of the code (codelen)
|
||||||
|
0x0008 | uint8 | signature index #1
|
||||||
|
0x0009 | uint8 | signature index #2
|
||||||
|
0x000A | uint8 | signature index #3
|
||||||
|
0x000B | uint8 | flags
|
||||||
|
0x000C | 52 bytes | reserved
|
||||||
|
0x0040 | 64 bytes | signature #1
|
||||||
|
0x0080 | 64 bytes | signature #2
|
||||||
|
0x00C0 | 64 bytes | signature #3
|
||||||
|
0x0100 | 32K-256 B | persistent storage
|
||||||
|
|
||||||
|
flags & 0x01 -> restore storage after flashing (if signatures are ok)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FLASH_ORIGIN (0x08000000)
|
||||||
|
|
||||||
|
#define FLASH_TOTAL_SIZE (512 * 1024)
|
||||||
|
|
||||||
|
#define FLASH_BOOT_START (FLASH_ORIGIN)
|
||||||
|
#define FLASH_BOOT_LEN (0x8000)
|
||||||
|
|
||||||
|
#define FLASH_META_START (FLASH_BOOT_START + FLASH_BOOT_LEN)
|
||||||
|
#define FLASH_META_LEN (0x8000)
|
||||||
|
|
||||||
|
#define FLASH_APP_START (FLASH_META_START + FLASH_META_LEN)
|
||||||
|
|
||||||
|
#define FLASH_META_MAGIC (FLASH_META_START)
|
||||||
|
#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_META_FLAGS (FLASH_META_START + 0x000B)
|
||||||
|
#define FLASH_META_SIG1 (FLASH_META_START + 0x0040)
|
||||||
|
#define FLASH_META_SIG2 (FLASH_META_START + 0x0080)
|
||||||
|
#define FLASH_META_SIG3 (FLASH_META_START + 0x00C0)
|
||||||
|
|
||||||
|
#define FLASH_META_DESC_LEN (0x100)
|
||||||
|
|
||||||
|
#define FLASH_STORAGE_START (FLASH_META_START + FLASH_META_DESC_LEN)
|
||||||
|
#define FLASH_STORAGE_LEN (FLASH_APP_START - FLASH_STORAGE_START)
|
||||||
|
|
||||||
|
#define FLASH_BOOT_SECTOR_FIRST 0
|
||||||
|
#define FLASH_BOOT_SECTOR_LAST 1
|
||||||
|
|
||||||
|
#define FLASH_META_SECTOR_FIRST 2
|
||||||
|
#define FLASH_META_SECTOR_LAST 3
|
||||||
|
|
||||||
|
#define FLASH_CODE_SECTOR_FIRST 4
|
||||||
|
#define FLASH_CODE_SECTOR_LAST 7
|
||||||
|
|
||||||
|
void memory_protect(void);
|
||||||
|
int memory_bootloader_hash(uint8_t *hash);
|
||||||
|
|
||||||
|
#endif
|
9
memory.ld
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* STM32F205RE - 512K Flash, 128K RAM */
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
rom (rx) : ORIGIN = 0x08000000, LENGTH = 512K
|
||||||
|
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||||
|
}
|
||||||
|
|
||||||
|
INCLUDE libopencm3_stm32f2.ld
|
9
memory_app_0.0.0.ld
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* STM32F205RE - 512K Flash, 128K RAM */
|
||||||
|
/* program starts at 0x08010000 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
rom (rx) : ORIGIN = 0x08004000, LENGTH = 512K - 16K
|
||||||
|
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||||
|
}
|
||||||
|
|
||||||
|
INCLUDE libopencm3_stm32f2.ld
|
9
memory_app_1.0.0.ld
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/* STM32F205RE - 512K Flash, 128K RAM */
|
||||||
|
/* program starts at 0x08010000 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
rom (rx) : ORIGIN = 0x08010000, LENGTH = 512K - 64K
|
||||||
|
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
|
||||||
|
}
|
||||||
|
|
||||||
|
INCLUDE libopencm3_stm32f2.ld
|
331
oled.c
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the TREZOR project.
|
||||||
|
*
|
||||||
|
* 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/f2/gpio.h>
|
||||||
|
#include <libopencm3/stm32/f2/spi.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "oled.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define OLED_SETCONTRAST 0x81
|
||||||
|
#define OLED_DISPLAYALLON_RESUME 0xA4
|
||||||
|
#define OLED_DISPLAYALLON 0xA5
|
||||||
|
#define OLED_NORMALDISPLAY 0xA6
|
||||||
|
#define OLED_INVERTDISPLAY 0xA7
|
||||||
|
#define OLED_DISPLAYOFF 0xAE
|
||||||
|
#define OLED_DISPLAYON 0xAF
|
||||||
|
#define OLED_SETDISPLAYOFFSET 0xD3
|
||||||
|
#define OLED_SETCOMPINS 0xDA
|
||||||
|
#define OLED_SETVCOMDETECT 0xDB
|
||||||
|
#define OLED_SETDISPLAYCLOCKDIV 0xD5
|
||||||
|
#define OLED_SETPRECHARGE 0xD9
|
||||||
|
#define OLED_SETMULTIPLEX 0xA8
|
||||||
|
#define OLED_SETLOWCOLUMN 0x00
|
||||||
|
#define OLED_SETHIGHCOLUMN 0x10
|
||||||
|
#define OLED_SETSTARTLINE 0x40
|
||||||
|
#define OLED_MEMORYMODE 0x20
|
||||||
|
#define OLED_COMSCANINC 0xC0
|
||||||
|
#define OLED_COMSCANDEC 0xC8
|
||||||
|
#define OLED_SEGREMAP 0xA0
|
||||||
|
#define OLED_CHARGEPUMP 0x8D
|
||||||
|
|
||||||
|
#define SPI_BASE SPI1
|
||||||
|
#define OLED_DC_PORT GPIOB
|
||||||
|
#define OLED_DC_PIN GPIO0 // PB0 | Data/Command
|
||||||
|
#define OLED_CS_PORT GPIOA
|
||||||
|
#define OLED_CS_PIN GPIO4 // PA4 | SPI Select
|
||||||
|
#define OLED_RST_PORT GPIOB
|
||||||
|
#define OLED_RST_PIN GPIO1 // PB1 | Reset display
|
||||||
|
|
||||||
|
#define OLED_BUFSET(X,Y) _oledbuffer[OLED_BUFSIZE - 1 - (X) - ((Y)/8)*OLED_WIDTH] |= (1 << (7 - (Y)%8))
|
||||||
|
#define OLED_BUFCLR(X,Y) _oledbuffer[OLED_BUFSIZE - 1 - (X) - ((Y)/8)*OLED_WIDTH] &= ~(1 << (7 - (Y)%8))
|
||||||
|
#define OLED_BUFTGL(X,Y) _oledbuffer[OLED_BUFSIZE - 1 - (X) - ((Y)/8)*OLED_WIDTH] ^= (1 << (7 - (Y)%8))
|
||||||
|
|
||||||
|
static uint8_t _oledbuffer[OLED_BUFSIZE];
|
||||||
|
static char is_debug_mode = 0;
|
||||||
|
|
||||||
|
inline void SPISend(uint32_t base, uint8_t *data, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
delay(400);
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
spi_send(base, data[i]);
|
||||||
|
}
|
||||||
|
delay(800);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledInit()
|
||||||
|
{
|
||||||
|
static uint8_t s[25] = {
|
||||||
|
OLED_DISPLAYOFF,
|
||||||
|
OLED_SETDISPLAYCLOCKDIV,
|
||||||
|
0x80,
|
||||||
|
OLED_SETMULTIPLEX,
|
||||||
|
0x3F, // 128x64
|
||||||
|
OLED_SETDISPLAYOFFSET,
|
||||||
|
0x00,
|
||||||
|
OLED_SETSTARTLINE | 0x00,
|
||||||
|
OLED_CHARGEPUMP,
|
||||||
|
0x14,
|
||||||
|
OLED_MEMORYMODE,
|
||||||
|
0x00,
|
||||||
|
OLED_SEGREMAP | 0x01,
|
||||||
|
OLED_COMSCANDEC,
|
||||||
|
OLED_SETCOMPINS,
|
||||||
|
0x12, // 128x64
|
||||||
|
OLED_SETCONTRAST,
|
||||||
|
0xCF,
|
||||||
|
OLED_SETPRECHARGE,
|
||||||
|
0xF1,
|
||||||
|
OLED_SETVCOMDETECT,
|
||||||
|
0x40,
|
||||||
|
OLED_DISPLAYALLON_RESUME,
|
||||||
|
OLED_NORMALDISPLAY,
|
||||||
|
OLED_DISPLAYON
|
||||||
|
};
|
||||||
|
|
||||||
|
gpio_clear(OLED_DC_PORT, OLED_DC_PIN); // set to CMD
|
||||||
|
gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect
|
||||||
|
|
||||||
|
// Reset the LCD
|
||||||
|
gpio_set(OLED_RST_PORT, OLED_RST_PIN);
|
||||||
|
delay(40);
|
||||||
|
gpio_clear(OLED_RST_PORT, OLED_RST_PIN);
|
||||||
|
delay(400);
|
||||||
|
gpio_set(OLED_RST_PORT, OLED_RST_PIN);
|
||||||
|
|
||||||
|
// init
|
||||||
|
gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select
|
||||||
|
SPISend(SPI_BASE, s, 25);
|
||||||
|
gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect
|
||||||
|
|
||||||
|
oledClear();
|
||||||
|
oledRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledClear()
|
||||||
|
{
|
||||||
|
memset(_oledbuffer, 0, sizeof(_oledbuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledRefresh()
|
||||||
|
{
|
||||||
|
static uint8_t s[3] = {OLED_SETLOWCOLUMN | 0x00, OLED_SETHIGHCOLUMN | 0x00, OLED_SETSTARTLINE | 0x00};
|
||||||
|
|
||||||
|
// draw triangle in upper right corner
|
||||||
|
if (is_debug_mode) {
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 5, 0); OLED_BUFTGL(OLED_WIDTH - 4, 0); OLED_BUFTGL(OLED_WIDTH - 3, 0); OLED_BUFTGL(OLED_WIDTH - 2, 0); OLED_BUFTGL(OLED_WIDTH - 1, 0);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 4, 1); OLED_BUFTGL(OLED_WIDTH - 3, 1); OLED_BUFTGL(OLED_WIDTH - 2, 1); OLED_BUFTGL(OLED_WIDTH - 1, 1);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 3, 2); OLED_BUFTGL(OLED_WIDTH - 2, 2); OLED_BUFTGL(OLED_WIDTH - 1, 2);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 2, 3); OLED_BUFTGL(OLED_WIDTH - 1, 3);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 1, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select
|
||||||
|
SPISend(SPI_BASE, s, 3);
|
||||||
|
gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect
|
||||||
|
|
||||||
|
gpio_set(OLED_DC_PORT, OLED_DC_PIN); // set to DATA
|
||||||
|
gpio_clear(OLED_CS_PORT, OLED_CS_PIN); // SPI select
|
||||||
|
SPISend(SPI_BASE, _oledbuffer, sizeof(_oledbuffer));
|
||||||
|
gpio_set(OLED_CS_PORT, OLED_CS_PIN); // SPI deselect
|
||||||
|
gpio_clear(OLED_DC_PORT, OLED_DC_PIN); // set to CMD
|
||||||
|
|
||||||
|
// return it back
|
||||||
|
if (is_debug_mode) {
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 5, 0); OLED_BUFTGL(OLED_WIDTH - 4, 0); OLED_BUFTGL(OLED_WIDTH - 3, 0); OLED_BUFTGL(OLED_WIDTH - 2, 0); OLED_BUFTGL(OLED_WIDTH - 1, 0);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 4, 1); OLED_BUFTGL(OLED_WIDTH - 3, 1); OLED_BUFTGL(OLED_WIDTH - 2, 1); OLED_BUFTGL(OLED_WIDTH - 1, 1);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 3, 2); OLED_BUFTGL(OLED_WIDTH - 2, 2); OLED_BUFTGL(OLED_WIDTH - 1, 2);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 2, 3); OLED_BUFTGL(OLED_WIDTH - 1, 3);
|
||||||
|
OLED_BUFTGL(OLED_WIDTH - 1, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *oledGetBuffer()
|
||||||
|
{
|
||||||
|
return _oledbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledSetDebug(char set)
|
||||||
|
{
|
||||||
|
is_debug_mode = set;
|
||||||
|
oledRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledSetBuffer(uint8_t *buf)
|
||||||
|
{
|
||||||
|
memcpy(_oledbuffer, buf, sizeof(_oledbuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledDrawPixel(int x, int y)
|
||||||
|
{
|
||||||
|
if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
|
||||||
|
OLED_BUFSET(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledClearPixel(int x, int y)
|
||||||
|
{
|
||||||
|
if ((x < 0) || (y < 0) || (x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
|
||||||
|
OLED_BUFCLR(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledDrawChar(int x, int y, char c)
|
||||||
|
{
|
||||||
|
uint8_t width, *column;
|
||||||
|
|
||||||
|
if ((x >= OLED_WIDTH) || (y >= OLED_HEIGHT)) return;
|
||||||
|
|
||||||
|
if ((c < FONT_START) || (c > FONT_END)) {
|
||||||
|
c = '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
width = font_data[(int)(c - FONT_START)][0];
|
||||||
|
column = (uint8_t *)(font_data[(int)(c - FONT_START)] + 1);
|
||||||
|
|
||||||
|
int xoffset, yoffset;
|
||||||
|
for (xoffset = 0; xoffset < width; xoffset++) {
|
||||||
|
for (yoffset = 0; yoffset < FONT_HEIGHT; yoffset++) {
|
||||||
|
if (column[xoffset] & (1 << (FONT_HEIGHT - 1 - yoffset))) {
|
||||||
|
oledDrawPixel(x + xoffset, y + yoffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledDrawString(int x, int y, const char* text)
|
||||||
|
{
|
||||||
|
if (!text) return;
|
||||||
|
const char *c;
|
||||||
|
int l = 0;
|
||||||
|
for (c = text; *c; c++) {
|
||||||
|
oledDrawChar(x + l, y, *c);
|
||||||
|
l += fontCharWidth(*c) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledDrawStringCenter(int y, const char* text)
|
||||||
|
{
|
||||||
|
int x = ( OLED_WIDTH - fontStringWidth(text) ) / 2;
|
||||||
|
oledDrawString(x, y, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledDrawStringRight(int x, int y, const char* text)
|
||||||
|
{
|
||||||
|
x -= fontStringWidth(text);
|
||||||
|
oledDrawString(x, y, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define min(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
|
||||||
|
void oledDrawBitmap(int x, int y, const BITMAP *bmp)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
for (i = 0; i < min(bmp->width, OLED_WIDTH - x); i++) {
|
||||||
|
for (j = 0; j < min(bmp->height, OLED_HEIGHT - y); j++) {
|
||||||
|
if (bmp->data[(i / 8) + j * bmp->width / 8] & (1 << (7 - i % 8))) {
|
||||||
|
OLED_BUFSET(x + i, y + j);
|
||||||
|
} else {
|
||||||
|
OLED_BUFCLR(x + i, y + j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledInvert(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
if ((x1 >= OLED_WIDTH) || (y1 >= OLED_HEIGHT) || (x2 >= OLED_WIDTH) || (y2 >= OLED_HEIGHT)) return;
|
||||||
|
int x, y;
|
||||||
|
for (x = x1; x <= x2; x++) {
|
||||||
|
for (y = y1; y <= y2; y++) {
|
||||||
|
OLED_BUFTGL(x,y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledBox(int x1, int y1, int x2, int y2, char val)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
for (x = x1; x <= x2; x++) {
|
||||||
|
for (y = y1; y <= y2; y++) {
|
||||||
|
val ? oledDrawPixel(x, y) : oledClearPixel(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledHLine(int y) {
|
||||||
|
int x;
|
||||||
|
for (x = 0; x < OLED_WIDTH; x++) {
|
||||||
|
oledDrawPixel(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledFrame(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
for (x = x1; x <= x2; x++) {
|
||||||
|
oledDrawPixel(x, y1);
|
||||||
|
oledDrawPixel(x, y2);
|
||||||
|
}
|
||||||
|
for (y = y1 + 1; y < y2; y++) {
|
||||||
|
oledDrawPixel(x1, y);
|
||||||
|
oledDrawPixel(x2, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledSwipeLeft(void)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
for (i = 0; i < OLED_WIDTH / 4; i++) {
|
||||||
|
for (j = 0; j < OLED_HEIGHT / 8; j++) {
|
||||||
|
for (k = OLED_WIDTH / 4 - 1; k > 0; k--) {
|
||||||
|
_oledbuffer[k * 4 + 3 + j * OLED_WIDTH] = _oledbuffer[k * 4 - 1 + j * OLED_WIDTH];
|
||||||
|
_oledbuffer[k * 4 + 2 + j * OLED_WIDTH] = _oledbuffer[k * 4 - 2 + j * OLED_WIDTH];
|
||||||
|
_oledbuffer[k * 4 + 1 + j * OLED_WIDTH] = _oledbuffer[k * 4 - 3 + j * OLED_WIDTH];
|
||||||
|
_oledbuffer[k * 4 + 0 + j * OLED_WIDTH] = _oledbuffer[k * 4 - 4 + j * OLED_WIDTH];
|
||||||
|
}
|
||||||
|
_oledbuffer[j * OLED_WIDTH] = 0;
|
||||||
|
_oledbuffer[j * OLED_WIDTH + 1] = 0;
|
||||||
|
_oledbuffer[j * OLED_WIDTH + 2] = 0;
|
||||||
|
_oledbuffer[j * OLED_WIDTH + 3] = 0;
|
||||||
|
}
|
||||||
|
oledRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void oledSwipeRight(void)
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
for (i = 0; i < OLED_WIDTH / 4; i++) {
|
||||||
|
for (j = 0; j < OLED_HEIGHT / 8; j++) {
|
||||||
|
for (k = 0; k < OLED_WIDTH / 4 - 1; k++) {
|
||||||
|
_oledbuffer[k * 4 + 0 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 4 + j * OLED_WIDTH];
|
||||||
|
_oledbuffer[k * 4 + 1 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 5 + j * OLED_WIDTH];
|
||||||
|
_oledbuffer[k * 4 + 2 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 6 + j * OLED_WIDTH];
|
||||||
|
_oledbuffer[k * 4 + 3 + j * OLED_WIDTH] = _oledbuffer[k * 4 + 7 + j * OLED_WIDTH];
|
||||||
|
}
|
||||||
|
_oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 1] = 0;
|
||||||
|
_oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 2] = 0;
|
||||||
|
_oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 3] = 0;
|
||||||
|
_oledbuffer[j * OLED_WIDTH + OLED_WIDTH - 4] = 0;
|
||||||
|
}
|
||||||
|
oledRefresh();
|
||||||
|
}
|
||||||
|
}
|