commit 0d0a1ab5f2987a926c7a717b93a2a3e59bf3344b Author: Pavol Rusnak Date: Tue Apr 29 14:26:51 2014 +0200 import v1.0.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..335be47ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.a +*.d +*.o +*.bin +*.elf +*.hex +*.list +*.srec +usb.pb* +bootloader diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..e02923314 --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/COPYING b/COPYING new file mode 100644 index 000000000..65c5ca88a --- /dev/null +++ b/COPYING @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..4d13b6063 --- /dev/null +++ b/Makefile @@ -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 diff --git a/Makefile.include b/Makefile.include new file mode 100644 index 000000000..0efd84047 --- /dev/null +++ b/Makefile.include @@ -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 diff --git a/README b/README new file mode 100644 index 000000000..98772f8ca --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +Embedded software for TREZOR + +http://bitcointrezor.com/ diff --git a/buttons.c b/buttons.c new file mode 100644 index 000000000..5bac9dcd5 --- /dev/null +++ b/buttons.c @@ -0,0 +1,68 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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; +} diff --git a/buttons.h b/buttons.h new file mode 100644 index 000000000..78a0fe24f --- /dev/null +++ b/buttons.h @@ -0,0 +1,40 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __BUTTONS_H__ +#define __BUTTONS_H__ + +#include + +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 diff --git a/demo/Makefile b/demo/Makefile new file mode 100644 index 000000000..5f16147c7 --- /dev/null +++ b/demo/Makefile @@ -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 diff --git a/demo/demo.c b/demo/demo.c new file mode 100644 index 000000000..77dc3b5f1 --- /dev/null +++ b/demo/demo.c @@ -0,0 +1,285 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include +#include +#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; +} diff --git a/firmware/Makefile b/firmware/Makefile new file mode 100644 index 000000000..ccd2d9ccc --- /dev/null +++ b/firmware/Makefile @@ -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 diff --git a/firmware/coins.c b/firmware/coins.c new file mode 100644 index 000000000..9c028f675 --- /dev/null +++ b/firmware/coins.c @@ -0,0 +1,61 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#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; +} diff --git a/firmware/coins.h b/firmware/coins.h new file mode 100644 index 000000000..6ce02c2d2 --- /dev/null +++ b/firmware/coins.h @@ -0,0 +1,33 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/debug.c b/firmware/debug.c new file mode 100644 index 000000000..0149b4d50 --- /dev/null +++ b/firmware/debug.c @@ -0,0 +1,53 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/debug.h b/firmware/debug.h new file mode 100644 index 000000000..4b71db3a5 --- /dev/null +++ b/firmware/debug.h @@ -0,0 +1,35 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/fsm.c b/firmware/fsm.c new file mode 100644 index 000000000..d14e86d19 --- /dev/null +++ b/firmware/fsm.c @@ -0,0 +1,672 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/fsm.h b/firmware/fsm.h new file mode 100644 index 000000000..8344f67b3 --- /dev/null +++ b/firmware/fsm.h @@ -0,0 +1,63 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/layout2.c b/firmware/layout2.c new file mode 100644 index 000000000..49adcce4c --- /dev/null +++ b/firmware/layout2.c @@ -0,0 +1,191 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include + +#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); +} diff --git a/firmware/layout2.h b/firmware/layout2.h new file mode 100644 index 000000000..f406ae415 --- /dev/null +++ b/firmware/layout2.h @@ -0,0 +1,35 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/messages.c b/firmware/messages.c new file mode 100644 index 000000000..70ff6da83 --- /dev/null +++ b/firmware/messages.c @@ -0,0 +1,414 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include + +#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; + } +} diff --git a/firmware/messages.h b/firmware/messages.h new file mode 100644 index 000000000..6f3b83e2f --- /dev/null +++ b/firmware/messages.h @@ -0,0 +1,53 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __MESSAGES_H__ +#define __MESSAGES_H__ + +#include +#include +#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 diff --git a/firmware/pinmatrix.c b/firmware/pinmatrix.c new file mode 100644 index 000000000..821297244 --- /dev/null +++ b/firmware/pinmatrix.c @@ -0,0 +1,87 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include + +#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; +} diff --git a/firmware/pinmatrix.h b/firmware/pinmatrix.h new file mode 100644 index 000000000..9c6c31293 --- /dev/null +++ b/firmware/pinmatrix.h @@ -0,0 +1,27 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __PINMATRIX_H__ +#define __PINMATRIX_H__ + +void pinmatrix_start(const char *text); +void pinmatrix_done(char *pin); +const char *pinmatrix_get(void); + +#endif diff --git a/firmware/protect.c b/firmware/protect.c new file mode 100644 index 000000000..57684a2ed --- /dev/null +++ b/firmware/protect.c @@ -0,0 +1,222 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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; +} diff --git a/firmware/protect.h b/firmware/protect.h new file mode 100644 index 000000000..07fdb2532 --- /dev/null +++ b/firmware/protect.h @@ -0,0 +1,31 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __PROTECT_H__ +#define __PROTECT_H__ + +#include +#include "types.pb.h" + +bool protectButton(ButtonRequestType type, bool confirm_only); +bool protectPin(bool use_cached); +bool protectChangePin(void); +bool protectPassphrase(void); + +#endif diff --git a/firmware/protob/.gitignore b/firmware/protob/.gitignore new file mode 100644 index 000000000..0a5bea8f2 --- /dev/null +++ b/firmware/protob/.gitignore @@ -0,0 +1 @@ +*.pb diff --git a/firmware/protob/Makefile b/firmware/protob/Makefile new file mode 100644 index 000000000..f9dd9c718 --- /dev/null +++ b/firmware/protob/Makefile @@ -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 diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options new file mode 100644 index 000000000..56c540b5e --- /dev/null +++ b/firmware/protob/messages.options @@ -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 diff --git a/firmware/protob/messages.pb.c b/firmware/protob/messages.pb.c new file mode 100644 index 000000000..22578bca6 --- /dev/null +++ b/firmware/protob/messages.pb.c @@ -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 + + diff --git a/firmware/protob/messages.pb.h b/firmware/protob/messages.pb.h new file mode 100644 index 000000000..225e8ab09 --- /dev/null +++ b/firmware/protob/messages.pb.h @@ -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 diff --git a/firmware/protob/messages.proto b/firmware/protob/messages.proto new file mode 120000 index 000000000..f788ef73a --- /dev/null +++ b/firmware/protob/messages.proto @@ -0,0 +1 @@ +../../trezor-common/protob/messages.proto \ No newline at end of file diff --git a/firmware/protob/pb.h b/firmware/protob/pb.h new file mode 100644 index 000000000..b5cb3ea4b --- /dev/null +++ b/firmware/protob/pb.h @@ -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 +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#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 diff --git a/firmware/protob/pb_decode.c b/firmware/protob/pb_decode.c new file mode 100644 index 000000000..9a48c60fc --- /dev/null +++ b/firmware/protob/pb_decode.c @@ -0,0 +1,1178 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* 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 + +#include "pb.h" +#include "pb_decode.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +/* Iterator for pb_field_t list */ +typedef struct { + const pb_field_t *start; /* Start of the pb_field_t array */ + const pb_field_t *pos; /* Current position of the iterator */ + unsigned field_index; /* Zero-based index of the field. */ + unsigned required_field_index; /* Zero-based index that counts only the required fields */ + void *dest_struct; /* Pointer to the destination structure to decode to */ + void *pData; /* Pointer where to store current field value */ + void *pSize; /* Pointer where to store the size of current array field */ +} pb_field_iterator_t; + +typedef bool (*pb_decoder_t)(pb_istream_t *stream, const pb_field_t *field, void *dest) checkreturn; + +static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count); +static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size); +static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct); +static bool pb_field_next(pb_field_iterator_t *iter); +static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_field_iterator_t *iter); +static bool checkreturn find_extension_field(pb_field_iterator_t *iter); +static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +/* --- Function pointers to field decoders --- + * Order in the array must match pb_action_t LTYPE numbering. + */ +static const pb_decoder_t PB_DECODERS[PB_LTYPES_COUNT] = { + &pb_dec_varint, + &pb_dec_uvarint, + &pb_dec_svarint, + &pb_dec_fixed32, + &pb_dec_fixed64, + + &pb_dec_bytes, + &pb_dec_string, + &pb_dec_submessage, + NULL /* extensions */ +}; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, uint8_t *buf, size_t count) +{ + uint8_t *source = (uint8_t*)stream->state; + stream->state = source + count; + + if (buf != NULL) + { + while (count--) + *buf++ = *source++; + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, uint8_t *buf, size_t count) +{ +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + uint8_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + stream->bytes_left -= count; + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, uint8_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(uint8_t*)stream->state; + stream->state = (uint8_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(uint8_t *buf, size_t bufsize) +{ + pb_istream_t stream; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + stream.state = buf; + stream.bytes_left = bufsize; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + uint8_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + return false; + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (bitpos >= 32) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_readbyte(stream, &byte)) + return false; + + result |= (uint32_t)(byte & 0x7F) << bitpos; + bitpos = (uint8_t)(bitpos + 7); + } while (byte & 0x80); + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + uint8_t byte; + uint8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (bitpos >= 64) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_readbyte(stream, &byte)) + return false; + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + uint8_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + return pb_read(stream, NULL, length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32(stream, &temp)) + { + if (stream->bytes_left == 0) + *eof = true; + + return false; + } + + if (temp == 0) + { + *eof = true; /* Special feature: allow 0-terminated messages. */ + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, uint8_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) return false; + if (!pb_read(stream, buf, 1)) return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = size; + stream->bytes_left -= size; + return true; +} + +void pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif +} + +static void pb_field_init(pb_field_iterator_t *iter, const pb_field_t *fields, void *dest_struct) +{ + iter->start = iter->pos = fields; + iter->field_index = 0; + iter->required_field_index = 0; + iter->pData = (char*)dest_struct + iter->pos->data_offset; + iter->pSize = (char*)iter->pData + iter->pos->size_offset; + iter->dest_struct = dest_struct; +} + +static bool pb_field_next(pb_field_iterator_t *iter) +{ + bool notwrapped = true; + size_t prev_size = iter->pos->data_size; + + if (PB_ATYPE(iter->pos->type) == PB_ATYPE_STATIC && + PB_HTYPE(iter->pos->type) == PB_HTYPE_REPEATED) + { + prev_size *= iter->pos->array_size; + } + else if (PB_ATYPE(iter->pos->type) == PB_ATYPE_POINTER) + { + prev_size = sizeof(void*); + } + + if (iter->pos->tag == 0) + return false; /* Only happens with empty message types */ + + if (PB_HTYPE(iter->pos->type) == PB_HTYPE_REQUIRED) + iter->required_field_index++; + + iter->pos++; + iter->field_index++; + if (iter->pos->tag == 0) + { + iter->pos = iter->start; + iter->field_index = 0; + iter->required_field_index = 0; + iter->pData = iter->dest_struct; + prev_size = 0; + notwrapped = false; + } + + iter->pData = (char*)iter->pData + prev_size + iter->pos->data_offset; + iter->pSize = (char*)iter->pData + iter->pos->size_offset; + return notwrapped; +} + +static bool checkreturn pb_field_find(pb_field_iterator_t *iter, uint32_t tag) +{ + unsigned start = iter->field_index; + + do { + if (iter->pos->tag == tag && + PB_LTYPE(iter->pos->type) != PB_LTYPE_EXTENSION) + { + return true; + } + (void)pb_field_next(iter); + } while (iter->field_index != start); + + return false; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) +{ + pb_type_t type; + pb_decoder_t func; + + type = iter->pos->type; + func = PB_DECODERS[PB_LTYPE(type)]; + + switch (PB_HTYPE(type)) + { + case PB_HTYPE_REQUIRED: + return func(stream, iter->pos, iter->pData); + + case PB_HTYPE_OPTIONAL: + *(bool*)iter->pSize = true; + return func(stream, iter->pos, iter->pData); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + size_t *size = (size_t*)iter->pSize; + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < iter->pos->array_size) + { + void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); + if (!func(&substream, iter->pos, pItem)) + { + status = false; + break; + } + (*size)++; + } + pb_close_string_substream(stream, &substream); + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + + return status; + } + else + { + /* Repeated field */ + size_t *size = (size_t*)iter->pSize; + void *pItem = (uint8_t*)iter->pData + iter->pos->data_size * (*size); + if (*size >= iter->pos->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + (*size)++; + return func(stream, iter->pos, pItem); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + size_t size = array_size * data_size; + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iterator_t *iter) +{ + if (PB_LTYPE(iter->pos->type) == PB_LTYPE_STRING || + PB_LTYPE(iter->pos->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE(iter->pos->type) == PB_LTYPE_SUBMESSAGE) + { + pb_message_set_to_defaults((const pb_field_t *) iter->pos->ptr, pItem); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) +{ +#ifndef PB_ENABLE_MALLOC + UNUSED(wire_type); + UNUSED(iter); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + pb_type_t type; + pb_decoder_t func; + + type = iter->pos->type; + func = PB_DECODERS[PB_LTYPE(type)]; + + switch (PB_HTYPE(type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + if (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES) + { + return func(stream, iter->pos, iter->pData); + } + else + { + if (!allocate_field(stream, iter->pData, iter->pos->data_size, 1)) + return false; + + initialize_pointer_field(*(void**)iter->pData, iter); + return func(stream, iter->pos, *(void**)iter->pData); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + size_t *size = (size_t*)iter->pSize; + size_t allocated_size = *size; + void *pItem; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + allocated_size += (substream.bytes_left - 1) / iter->pos->data_size + 1; + + if (!allocate_field(&substream, iter->pData, iter->pos->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size); + initialize_pointer_field(pItem, iter); + if (!func(&substream, iter->pos, pItem)) + { + status = false; + break; + } + (*size)++; + } + pb_close_string_substream(stream, &substream); + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + size_t *size = (size_t*)iter->pSize; + void *pItem; + + (*size)++; + if (!allocate_field(stream, iter->pData, iter->pos->data_size, *size)) + return false; + + pItem = *(uint8_t**)iter->pData + iter->pos->data_size * (*size - 1); + initialize_pointer_field(pItem, iter); + return func(stream, iter->pos, pItem); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) +{ + pb_callback_t *pCallback = (pb_callback_t*)iter->pData; + +#ifdef PB_OLD_CALLBACK_STYLE + void *arg = pCallback->arg; +#else + void **arg = &(pCallback->arg); +#endif + + if (pCallback->funcs.decode == NULL) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + if (!pCallback->funcs.decode(&substream, iter->pos, arg)) + PB_RETURN_ERROR(stream, "callback failed"); + } while (substream.bytes_left); + + pb_close_string_substream(stream, &substream); + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + uint8_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return pCallback->funcs.decode(&substream, iter->pos, arg); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iterator_t *iter) +{ + switch (PB_ATYPE(iter->pos->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, iter); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, iter); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, iter); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects a pb_field_t structure + * in extension->type->arg. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + const pb_field_t *field = (const pb_field_t*)extension->type->arg; + pb_field_iterator_t iter; + + if (field->tag != tag) + return true; + + iter.start = field; + iter.pos = field; + iter.field_index = 0; + iter.required_field_index = 0; + iter.dest_struct = extension->dest; + iter.pData = extension->dest; + iter.pSize = &extension->found; + + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_field_iterator_t *iter) +{ + pb_extension_t *extension = *(pb_extension_t* const *)iter->pData; + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Step through the iterator until an extension field is found or until all + * entries have been checked. There can be only one extension field per + * message. Returns false if no extension field is found. */ +static bool checkreturn find_extension_field(pb_field_iterator_t *iter) +{ + unsigned start = iter->field_index; + + do { + if (PB_LTYPE(iter->pos->type) == PB_LTYPE_EXTENSION) + return true; + (void)pb_field_next(iter); + } while (iter->field_index != start); + + return false; +} + +/* Initialize message fields to default values, recursively */ +static void pb_message_set_to_defaults(const pb_field_t fields[], void *dest_struct) +{ + pb_field_iterator_t iter; + pb_field_init(&iter, fields, dest_struct); + + do + { + pb_type_t type; + type = iter.pos->type; + + /* Avoid crash on empty message types (zero fields) */ + if (iter.pos->tag == 0) + continue; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)iter.pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Set array count to 0, no need to initialize contents. */ + *(size_t*)iter.pSize = 0; + continue; + } + + if (PB_LTYPE(iter.pos->type) == PB_LTYPE_SUBMESSAGE) + { + /* Initialize submessage to defaults */ + pb_message_set_to_defaults((const pb_field_t *) iter.pos->ptr, iter.pData); + } + else if (iter.pos->ptr != NULL) + { + /* Initialize to default value */ + memcpy(iter.pData, iter.pos->ptr, iter.pos->data_size); + } + else + { + /* Initialize to zeros */ + memset(iter.pData, 0, iter.pos->data_size); + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)iter.pData = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + *(size_t*)iter.pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + } while (pb_field_next(&iter)); +} + +/********************* + * Decode all fields * + *********************/ + +bool checkreturn pb_decode_noinit(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + uint8_t fields_seen[(PB_MAX_REQUIRED_FIELDS + 7) / 8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t extension_range_start = 0; + pb_field_iterator_t iter; + + pb_field_init(&iter, fields, dest_struct); + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (!pb_field_find(&iter, tag)) + { + /* No match found, check if it matches an extension. */ + if (tag >= extension_range_start) + { + if (!find_extension_field(&iter)) + extension_range_start = (uint32_t)-1; + else + extension_range_start = iter.pos->tag; + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, &iter)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + if (PB_HTYPE(iter.pos->type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + fields_seen[iter.required_field_index >> 3] |= (uint8_t)(1 << (iter.required_field_index & 7)); + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all required fields were present. */ + { + /* First figure out the number of required fields by + * seeking to the end of the field array. Usually we + * are already close to end after decoding. + */ + unsigned req_field_count; + pb_type_t last_type; + unsigned i; + do { + req_field_count = iter.required_field_index; + last_type = iter.pos->type; + } while (pb_field_next(&iter)); + + /* Fixup if last field was also required. */ + if (PB_HTYPE(last_type) == PB_HTYPE_REQUIRED && iter.pos->tag != 0) + req_field_count++; + + /* Check the whole bytes */ + for (i = 0; i < (req_field_count >> 3); i++) + { + if (fields_seen[i] != 0xFF) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits */ + if (fields_seen[req_field_count >> 3] != (0xFF >> (8 - (req_field_count & 7)))) + PB_RETURN_ERROR(stream, "missing required field"); + } + + return true; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + bool status; + pb_message_set_to_defaults(fields, dest_struct); + status = pb_decode_noinit(stream, fields, dest_struct); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool pb_decode_delimited(pb_istream_t *stream, const pb_field_t fields[], void *dest_struct) +{ + pb_istream_t substream; + bool status; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode(&substream, fields, dest_struct); + pb_close_string_substream(stream, &substream); + return status; +} + +#ifdef PB_ENABLE_MALLOC +void pb_release(const pb_field_t fields[], void *dest_struct) +{ + pb_field_iterator_t iter; + pb_field_init(&iter, fields, dest_struct); + + do + { + pb_type_t type; + type = iter.pos->type; + + /* Avoid crash on empty message types (zero fields) */ + if (iter.pos->tag == 0) + continue; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)iter.pData; + size_t count = *(size_t*)iter.pSize; + while (count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + else if (PB_LTYPE(type) == PB_LTYPE_SUBMESSAGE) + { + /* Release fields in submessages */ + void *pItem = *(void**)iter.pData; + size_t count = (pItem ? 1 : 0); + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(size_t*)iter.pSize; + } + + while (count--) + { + pb_release((const pb_field_t*)iter.pos->ptr, pItem); + pItem = (uint8_t*)pItem + iter.pos->data_size; + } + } + + /* Release main item */ + pb_free(*(void**)iter.pData); + *(void**)iter.pData = NULL; + } + } while (pb_field_next(&iter)); +} +#endif + +/* Field decoders */ + +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest) +{ + uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (int64_t)(~(value >> 1)); + else + *dest = (int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + #ifdef __BIG_ENDIAN__ + uint8_t *bytes = (uint8_t*)dest; + uint8_t lebytes[4]; + + if (!pb_read(stream, lebytes, 4)) + return false; + + bytes[0] = lebytes[3]; + bytes[1] = lebytes[2]; + bytes[2] = lebytes[1]; + bytes[3] = lebytes[0]; + return true; + #else + return pb_read(stream, (uint8_t*)dest, 4); + #endif +} + +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + #ifdef __BIG_ENDIAN__ + uint8_t *bytes = (uint8_t*)dest; + uint8_t lebytes[8]; + + if (!pb_read(stream, lebytes, 8)) + return false; + + bytes[0] = lebytes[7]; + bytes[1] = lebytes[6]; + bytes[2] = lebytes[5]; + bytes[3] = lebytes[4]; + bytes[4] = lebytes[3]; + bytes[5] = lebytes[2]; + bytes[6] = lebytes[1]; + bytes[7] = lebytes[0]; + return true; + #else + return pb_read(stream, (uint8_t*)dest, 8); + #endif +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + switch (field->data_size) + { + case 1: *(int8_t*)dest = (int8_t)value; break; + case 2: *(int16_t*)dest = (int16_t)value; break; + case 4: *(int32_t*)dest = (int32_t)value; break; + case 8: *(int64_t*)dest = (int64_t)value; break; + default: PB_RETURN_ERROR(stream, "invalid data_size"); + } + + return true; +} + +static bool checkreturn pb_dec_uvarint(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + switch (field->data_size) + { + case 4: *(uint32_t*)dest = (uint32_t)value; break; + case 8: *(uint64_t*)dest = value; break; + default: PB_RETURN_ERROR(stream, "invalid data_size"); + } + + return true; +} + +static bool checkreturn pb_dec_svarint(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + int64_t value; + if (!pb_decode_svarint(stream, &value)) + return false; + + switch (field->data_size) + { + case 4: *(int32_t*)dest = (int32_t)value; break; + case 8: *(int64_t*)dest = value; break; + default: PB_RETURN_ERROR(stream, "invalid data_size"); + } + + return true; +} + +static bool checkreturn pb_dec_fixed32(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + UNUSED(field); + return pb_decode_fixed32(stream, dest); +} + +static bool checkreturn pb_dec_fixed64(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + UNUSED(field); + return pb_decode_fixed64(stream, dest); +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint32_t size; + pb_bytes_array_t *bdest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (!allocate_field(stream, dest, PB_BYTES_ARRAY_T_ALLOCSIZE(size), 1)) + return false; + bdest = *(pb_bytes_array_t**)dest; +#endif + } + else + { + if (PB_BYTES_ARRAY_T_ALLOCSIZE(size) > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + bdest = (pb_bytes_array_t*)dest; + } + + bdest->size = size; + return pb_read(stream, bdest->bytes, size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + uint32_t size; + size_t alloc_size; + bool status; + if (!pb_decode_varint32(stream, &size)) + return false; + + /* Space for null terminator */ + alloc_size = size + 1; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (!allocate_field(stream, dest, alloc_size, 1)) + return false; + dest = *(void**)dest; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + status = pb_read(stream, (uint8_t*)dest, size); + *((uint8_t*)dest + size) = 0; + return status; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_t *field, void *dest) +{ + bool status; + pb_istream_t substream; + const pb_field_t* submsg_fields = (const pb_field_t*)field->ptr; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->ptr == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* New array entries need to be initialized, while required and optional + * submessages have already been initialized in the top-level pb_decode. */ + if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + status = pb_decode(&substream, submsg_fields, dest); + else + status = pb_decode_noinit(&substream, submsg_fields, dest); + + pb_close_string_substream(stream, &substream); + return status; +} diff --git a/firmware/protob/pb_decode.h b/firmware/protob/pb_decode.h new file mode 100644 index 000000000..8dc67408a --- /dev/null +++ b/firmware/protob/pb_decode.h @@ -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 diff --git a/firmware/protob/pb_encode.c b/firmware/protob/pb_encode.c new file mode 100644 index 000000000..dc5a27349 --- /dev/null +++ b/firmware/protob/pb_encode.c @@ -0,0 +1,667 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#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); +} + diff --git a/firmware/protob/pb_encode.h b/firmware/protob/pb_encode.h new file mode 100644 index 000000000..f82bac8f8 --- /dev/null +++ b/firmware/protob/pb_encode.h @@ -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 diff --git a/firmware/protob/storage.options b/firmware/protob/storage.options new file mode 100644 index 000000000..eb495de97 --- /dev/null +++ b/firmware/protob/storage.options @@ -0,0 +1,4 @@ +Storage.mnemonic max_size:241 +Storage.pin max_size:10 +Storage.language max_size:17 +Storage.label max_size:33 diff --git a/firmware/protob/storage.pb.c b/firmware/protob/storage.pb.c new file mode 100644 index 000000000..ac40b48b3 --- /dev/null +++ b/firmware/protob/storage.pb.c @@ -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 + + diff --git a/firmware/protob/storage.pb.h b/firmware/protob/storage.pb.h new file mode 100644 index 000000000..3e7824c97 --- /dev/null +++ b/firmware/protob/storage.pb.h @@ -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 diff --git a/firmware/protob/storage.proto b/firmware/protob/storage.proto new file mode 120000 index 000000000..b7b890d7b --- /dev/null +++ b/firmware/protob/storage.proto @@ -0,0 +1 @@ +../../trezor-common/protob/storage.proto \ No newline at end of file diff --git a/firmware/protob/types.options b/firmware/protob/types.options new file mode 100644 index 000000000..9c29909b9 --- /dev/null +++ b/firmware/protob/types.options @@ -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 diff --git a/firmware/protob/types.pb.c b/firmware/protob/types.pb.c new file mode 100644 index 000000000..587f391da --- /dev/null +++ b/firmware/protob/types.pb.c @@ -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 + + diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h new file mode 100644 index 000000000..9c2b7225c --- /dev/null +++ b/firmware/protob/types.pb.h @@ -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 diff --git a/firmware/protob/types.proto b/firmware/protob/types.proto new file mode 120000 index 000000000..6f8a7a998 --- /dev/null +++ b/firmware/protob/types.proto @@ -0,0 +1 @@ +../../trezor-common/protob/types.proto \ No newline at end of file diff --git a/firmware/recovery.c b/firmware/recovery.c new file mode 100644 index 000000000..18b383ede --- /dev/null +++ b/firmware/recovery.c @@ -0,0 +1,179 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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; +} diff --git a/firmware/recovery.h b/firmware/recovery.h new file mode 100644 index 000000000..4d581138b --- /dev/null +++ b/firmware/recovery.h @@ -0,0 +1,32 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __RECOVERY_H__ +#define __RECOVERY_H__ + +#include +#include + +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 diff --git a/firmware/reset.c b/firmware/reset.c new file mode 100644 index 000000000..4bc5ba0ad --- /dev/null +++ b/firmware/reset.c @@ -0,0 +1,151 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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; +} diff --git a/firmware/reset.h b/firmware/reset.h new file mode 100644 index 000000000..157d94ce1 --- /dev/null +++ b/firmware/reset.h @@ -0,0 +1,31 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __RESET_H__ +#define __RESET_H__ + +#include +#include + +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 diff --git a/firmware/signing.c b/firmware/signing.c new file mode 100644 index 000000000..5b5cf9d1c --- /dev/null +++ b/firmware/signing.c @@ -0,0 +1,426 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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; + } +} diff --git a/firmware/signing.h b/firmware/signing.h new file mode 100644 index 000000000..84d609b87 --- /dev/null +++ b/firmware/signing.h @@ -0,0 +1,32 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __SIGNING_H__ +#define __SIGNING_H__ + +#include +#include +#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 diff --git a/firmware/ssp.c b/firmware/ssp.c new file mode 100644 index 000000000..089a77af1 --- /dev/null +++ b/firmware/ssp.c @@ -0,0 +1,40 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 +} diff --git a/firmware/ssp.h b/firmware/ssp.h new file mode 100644 index 000000000..c1cb26020 --- /dev/null +++ b/firmware/ssp.h @@ -0,0 +1,26 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __SSP_H_ +#define __SSP_H_ + +void __stack_chk_guard_setup(void); +void __attribute__((noreturn)) __stack_chk_fail(void); + +#endif diff --git a/firmware/storage.c b/firmware/storage.c new file mode 100644 index 000000000..3351d1482 --- /dev/null +++ b/firmware/storage.c @@ -0,0 +1,327 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include + +#include + +#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; +} diff --git a/firmware/storage.h b/firmware/storage.h new file mode 100644 index 000000000..0efe52f13 --- /dev/null +++ b/firmware/storage.h @@ -0,0 +1,61 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/transaction.c b/firmware/transaction.c new file mode 100644 index 000000000..38d148077 --- /dev/null +++ b/firmware/transaction.c @@ -0,0 +1,459 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#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; +} diff --git a/firmware/transaction.h b/firmware/transaction.h new file mode 100644 index 000000000..c63e7ce0c --- /dev/null +++ b/firmware/transaction.h @@ -0,0 +1,67 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __TRANSACTION_H__ +#define __TRANSACTION_H__ + +#include +#include +#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 diff --git a/firmware/trezor.c b/firmware/trezor.c new file mode 100644 index 000000000..9298285ae --- /dev/null +++ b/firmware/trezor.c @@ -0,0 +1,53 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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; +} diff --git a/firmware/trezor.h b/firmware/trezor.h new file mode 100644 index 000000000..c6ecbb1d3 --- /dev/null +++ b/firmware/trezor.h @@ -0,0 +1,38 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/firmware/usb.c b/firmware/usb.c new file mode 100644 index 000000000..43c7bd901 --- /dev/null +++ b/firmware/usb.c @@ -0,0 +1,334 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include + +#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; +} diff --git a/firmware/usb.h b/firmware/usb.h new file mode 100644 index 000000000..eec42e34e --- /dev/null +++ b/firmware/usb.h @@ -0,0 +1,28 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __USB_H__ +#define __USB_H__ + +void usbInit(void); +void usbPoll(void); +void usbReconnect(void); +void usbTiny(char set); + +#endif diff --git a/gen/bitmaps.c b/gen/bitmaps.c new file mode 100644 index 000000000..64808987f --- /dev/null +++ b/gen/bitmaps.c @@ -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}; diff --git a/gen/bitmaps.h b/gen/bitmaps.h new file mode 100644 index 000000000..9eff4cd59 --- /dev/null +++ b/gen/bitmaps.h @@ -0,0 +1,35 @@ +#ifndef __BITMAPS_H__ +#define __BITMAPS_H__ + +#include + +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 diff --git a/gen/bitmaps/.gitignore b/gen/bitmaps/.gitignore new file mode 100644 index 000000000..68359a786 --- /dev/null +++ b/gen/bitmaps/.gitignore @@ -0,0 +1,2 @@ +*.c +*.h diff --git a/gen/bitmaps/digit0.png b/gen/bitmaps/digit0.png new file mode 100644 index 000000000..9dfc0a41c Binary files /dev/null and b/gen/bitmaps/digit0.png differ diff --git a/gen/bitmaps/digit1.png b/gen/bitmaps/digit1.png new file mode 100644 index 000000000..8645ae6f1 Binary files /dev/null and b/gen/bitmaps/digit1.png differ diff --git a/gen/bitmaps/digit2.png b/gen/bitmaps/digit2.png new file mode 100644 index 000000000..81f206e79 Binary files /dev/null and b/gen/bitmaps/digit2.png differ diff --git a/gen/bitmaps/digit3.png b/gen/bitmaps/digit3.png new file mode 100644 index 000000000..eef549440 Binary files /dev/null and b/gen/bitmaps/digit3.png differ diff --git a/gen/bitmaps/digit4.png b/gen/bitmaps/digit4.png new file mode 100644 index 000000000..6096ec996 Binary files /dev/null and b/gen/bitmaps/digit4.png differ diff --git a/gen/bitmaps/digit5.png b/gen/bitmaps/digit5.png new file mode 100644 index 000000000..aafcd3c54 Binary files /dev/null and b/gen/bitmaps/digit5.png differ diff --git a/gen/bitmaps/digit6.png b/gen/bitmaps/digit6.png new file mode 100644 index 000000000..0b0c59eac Binary files /dev/null and b/gen/bitmaps/digit6.png differ diff --git a/gen/bitmaps/digit7.png b/gen/bitmaps/digit7.png new file mode 100644 index 000000000..e4eea9e0a Binary files /dev/null and b/gen/bitmaps/digit7.png differ diff --git a/gen/bitmaps/digit8.png b/gen/bitmaps/digit8.png new file mode 100644 index 000000000..de021652a Binary files /dev/null and b/gen/bitmaps/digit8.png differ diff --git a/gen/bitmaps/digit9.png b/gen/bitmaps/digit9.png new file mode 100644 index 000000000..2a7458aae Binary files /dev/null and b/gen/bitmaps/digit9.png differ diff --git a/gen/bitmaps/gears0.png b/gen/bitmaps/gears0.png new file mode 100644 index 000000000..10f593597 Binary files /dev/null and b/gen/bitmaps/gears0.png differ diff --git a/gen/bitmaps/gears1.png b/gen/bitmaps/gears1.png new file mode 100644 index 000000000..9fa6a8102 Binary files /dev/null and b/gen/bitmaps/gears1.png differ diff --git a/gen/bitmaps/gears2.png b/gen/bitmaps/gears2.png new file mode 100644 index 000000000..56c199862 Binary files /dev/null and b/gen/bitmaps/gears2.png differ diff --git a/gen/bitmaps/gears3.png b/gen/bitmaps/gears3.png new file mode 100644 index 000000000..9aa384062 Binary files /dev/null and b/gen/bitmaps/gears3.png differ diff --git a/gen/bitmaps/generate.py b/gen/bitmaps/generate.py new file mode 100755 index 000000000..54b3ac2cb --- /dev/null +++ b/gen/bitmaps/generate.py @@ -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 + +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() diff --git a/gen/bitmaps/icon_error.png b/gen/bitmaps/icon_error.png new file mode 100644 index 000000000..f23a1e794 Binary files /dev/null and b/gen/bitmaps/icon_error.png differ diff --git a/gen/bitmaps/icon_info.png b/gen/bitmaps/icon_info.png new file mode 100644 index 000000000..2044c7622 Binary files /dev/null and b/gen/bitmaps/icon_info.png differ diff --git a/gen/bitmaps/icon_ok.png b/gen/bitmaps/icon_ok.png new file mode 100644 index 000000000..36fcee2a2 Binary files /dev/null and b/gen/bitmaps/icon_ok.png differ diff --git a/gen/bitmaps/icon_question.png b/gen/bitmaps/icon_question.png new file mode 100644 index 000000000..b3ef44162 Binary files /dev/null and b/gen/bitmaps/icon_question.png differ diff --git a/gen/bitmaps/icon_warning.png b/gen/bitmaps/icon_warning.png new file mode 100644 index 000000000..1e7ea18d5 Binary files /dev/null and b/gen/bitmaps/icon_warning.png differ diff --git a/gen/bitmaps/logo48.png b/gen/bitmaps/logo48.png new file mode 100644 index 000000000..86ec68676 Binary files /dev/null and b/gen/bitmaps/logo48.png differ diff --git a/gen/bitmaps/logo48_empty.png b/gen/bitmaps/logo48_empty.png new file mode 100644 index 000000000..cdadefb34 Binary files /dev/null and b/gen/bitmaps/logo48_empty.png differ diff --git a/gen/bitmaps/logo64.png b/gen/bitmaps/logo64.png new file mode 100644 index 000000000..d07a745fb Binary files /dev/null and b/gen/bitmaps/logo64.png differ diff --git a/gen/bitmaps/logo64_empty.png b/gen/bitmaps/logo64_empty.png new file mode 100644 index 000000000..ec8ba1da4 Binary files /dev/null and b/gen/bitmaps/logo64_empty.png differ diff --git a/gen/fonts.c b/gen/fonts.c new file mode 100644 index 000000000..b811732b6 --- /dev/null +++ b/gen/fonts.c @@ -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; +} diff --git a/gen/fonts.h b/gen/fonts.h new file mode 100644 index 000000000..0cd6b6220 --- /dev/null +++ b/gen/fonts.h @@ -0,0 +1,15 @@ +#ifndef __FONTS_H__ +#define __FONTS_H__ + +#include + +#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 diff --git a/gen/fonts/font.png b/gen/fonts/font.png new file mode 100644 index 000000000..efc88495d Binary files /dev/null and b/gen/fonts/font.png differ diff --git a/gen/fonts/generate.py b/gen/fonts/generate.py new file mode 100755 index 000000000..b7a1dd7cf --- /dev/null +++ b/gen/fonts/generate.py @@ -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) diff --git a/gen/handlers/handlers.py b/gen/handlers/handlers.py new file mode 100755 index 000000000..d74a64afe --- /dev/null +++ b/gen/handlers/handlers.py @@ -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') diff --git a/layout.c b/layout.c new file mode 100644 index 000000000..6513c7cf5 --- /dev/null +++ b/layout.c @@ -0,0 +1,105 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include + +#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(); +} diff --git a/layout.h b/layout.h new file mode 100644 index 000000000..71e819189 --- /dev/null +++ b/layout.h @@ -0,0 +1,37 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __LAYOUT_H__ +#define __LAYOUT_H__ + +#include + +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 diff --git a/memory.c b/memory.c new file mode 100644 index 000000000..c82f9cf36 --- /dev/null +++ b/memory.c @@ -0,0 +1,45 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include +#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; +} diff --git a/memory.h b/memory.h new file mode 100644 index 000000000..3d9c8914a --- /dev/null +++ b/memory.h @@ -0,0 +1,104 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#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 diff --git a/memory.ld b/memory.ld new file mode 100644 index 000000000..539837839 --- /dev/null +++ b/memory.ld @@ -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 diff --git a/memory_app_0.0.0.ld b/memory_app_0.0.0.ld new file mode 100644 index 000000000..ce3f3f7cc --- /dev/null +++ b/memory_app_0.0.0.ld @@ -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 diff --git a/memory_app_1.0.0.ld b/memory_app_1.0.0.ld new file mode 100644 index 000000000..a8188fb60 --- /dev/null +++ b/memory_app_1.0.0.ld @@ -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 diff --git a/oled.c b/oled.c new file mode 100644 index 000000000..d207a1419 --- /dev/null +++ b/oled.c @@ -0,0 +1,331 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include + +#include + +#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(); + } +} diff --git a/oled.h b/oled.h new file mode 100644 index 000000000..f23728cfd --- /dev/null +++ b/oled.h @@ -0,0 +1,57 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __OLED_H__ +#define __OLED_H__ + +#include + +#include "bitmaps.h" +#include "fonts.h" + +#define OLED_WIDTH 128 +#define OLED_HEIGHT 64 +#define OLED_BUFSIZE (OLED_WIDTH * OLED_HEIGHT / 8) + +#ifdef APPVER +#define oledInit() do{}while(0) +#else +void oledInit(void); +#endif +void oledClear(void); +void oledRefresh(void); + +void oledSetDebug(char set); +void oledSetBuffer(uint8_t *buf); +const uint8_t *oledGetBuffer(void); +void oledDrawPixel(int x, int y); +void oledClearPixel(int x, int y); +void oledDrawChar(int x, int y, char c); +void oledDrawString(int x, int y, const char* text); +void oledDrawStringCenter(int y, const char* text); +void oledDrawStringRight(int x, int y, const char* text); +void oledDrawBitmap(int x, int y, const BITMAP *bmp); +void oledInvert(int x1, int y1, int x2, int y2); +void oledBox(int x1, int y1, int x2, int y2, char val); +void oledHLine(int y); +void oledFrame(int x1, int y1, int x2, int y2); +void oledSwipeLeft(void); +void oledSwipeRight(void); + +#endif diff --git a/rng.c b/rng.c new file mode 100644 index 000000000..7bf275ac3 --- /dev/null +++ b/rng.c @@ -0,0 +1,45 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include + +#include "rng.h" + +uint32_t random32(void) +{ + static uint32_t last = 0, new = 0; + while (new == last) { + if (((RNG_SR & (RNG_SR_SEIS | RNG_SR_CEIS)) == 0) && ((RNG_SR & RNG_SR_DRDY) > 0)) { + new = RNG_DR; + } + } + last = new; + return new; +} + +void random_buffer(uint8_t *buf, uint32_t len) +{ + uint32_t i, r = 0; + for (i = 0; i < len; i++) { + if (i % 4 == 0) { + r = random32(); + } + buf[i] = (r >> ((i % 4) * 8)) & 0xFF; + } +} diff --git a/rng.h b/rng.h new file mode 100644 index 000000000..c8e02e398 --- /dev/null +++ b/rng.h @@ -0,0 +1,28 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __RNG_H__ +#define __RNG_H__ + +#include + +uint32_t random32(void); +void random_buffer(uint8_t *buf, uint32_t len); + +#endif diff --git a/serialno.c b/serialno.c new file mode 100644 index 000000000..024d6ed49 --- /dev/null +++ b/serialno.c @@ -0,0 +1,46 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include + +#include "serialno.h" +#include "util.h" +#include "sha2.h" + +#if defined(STM32F4) || defined(STM32F2) +#define UNIQUE_SERIAL_ADDR 0x1FFF7A10 +#elif defined(STM32F3) +#define UNIQUE_SERIAL_ADDR 0x1FFFF7AC +#elif defined(STM32L1) +#define UNIQUE_SERIAL_ADDR 0x1FF80050 +#else // STM32F1 +#define UNIQUE_SERIAL_ADDR 0x1FFFF7E8 +#endif + +void fill_serialno_fixed(char *s) +{ + uint8_t uuid[32]; + memcpy(uuid, (uint8_t *)UNIQUE_SERIAL_ADDR, 12); + memcpy(uuid + 12, (uint8_t *)UNIQUE_SERIAL_ADDR, 12); + memcpy(uuid + 24, (uint8_t *)UNIQUE_SERIAL_ADDR, 8); + sha256_Raw(uuid, 32, uuid); + sha256_Raw(uuid, 32, uuid); + data2hex(uuid, 12, s); +} diff --git a/serialno.h b/serialno.h new file mode 100644 index 000000000..82fe483a2 --- /dev/null +++ b/serialno.h @@ -0,0 +1,26 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __SERIALNO_H__ +#define __SERIALNO_H__ + +// buffer has to be (at least) 25 chars long +void fill_serialno_fixed(char *s); + +#endif diff --git a/setup.c b/setup.c new file mode 100644 index 000000000..310d86bf0 --- /dev/null +++ b/setup.c @@ -0,0 +1,66 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include +#include +#include + +void setup(void) +{ + // setup clock + clock_scale_t clock = hse_8mhz_3v3[CLOCK_3V3_120MHZ]; + rcc_clock_setup_hse_3v3(&clock); + + // enable GPIO clock - A (oled), B(oled), C (buttons) + rcc_peripheral_enable_clock(&RCC_AHB1ENR, RCC_AHB1ENR_IOPAEN | RCC_AHB1ENR_IOPBEN | RCC_AHB1ENR_IOPCEN); + + // enable SPI clock + rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_SPI1EN); + + // enable OTG FS clock + rcc_peripheral_enable_clock(&RCC_AHB2ENR, RCC_AHB2ENR_OTGFSEN); + + // enable RNG + rcc_peripheral_enable_clock(&RCC_AHB2ENR, RCC_AHB2ENR_RNGEN); + RNG_CR |= RNG_CR_IE | RNG_CR_RNGEN; + + // set GPIO for buttons + gpio_mode_setup(GPIOC, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO2 | GPIO5); + + // set GPIO for OLED display + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO4); + gpio_mode_setup(GPIOB, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO0 | GPIO1); + + // enable SPI 1 for OLED display + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO5 | GPIO7); + gpio_set_af(GPIOA, GPIO_AF5, GPIO5 | GPIO7); + +// spi_disable_crc(SPI1); + spi_init_master(SPI1, SPI_CR1_BAUDRATE_FPCLK_DIV_64, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT, SPI_CR1_MSBFIRST); + spi_enable_ss_output(SPI1); +// spi_enable_software_slave_management(SPI1); +// spi_set_nss_high(SPI1); +// spi_clear_mode_fault(SPI1); + spi_enable(SPI1); + + // enable OTG_FS + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9 | GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO9 | GPIO11 | GPIO12); +} diff --git a/setup.h b/setup.h new file mode 100644 index 000000000..a54c81b95 --- /dev/null +++ b/setup.h @@ -0,0 +1,29 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __SETUP_H__ +#define __SETUP_H__ + +#ifdef APPVER +#define setup() do{}while(0) +#else +void setup(void); +#endif + +#endif diff --git a/trezor-common b/trezor-common new file mode 160000 index 000000000..e4f9dcd7c --- /dev/null +++ b/trezor-common @@ -0,0 +1 @@ +Subproject commit e4f9dcd7c966c136bd33ccc58321db8d8b83d09b diff --git a/trezor-crypto b/trezor-crypto new file mode 160000 index 000000000..94d4a3733 --- /dev/null +++ b/trezor-crypto @@ -0,0 +1 @@ +Subproject commit 94d4a3733ed10c6db9225a23edcdfee6c7fcb2b2 diff --git a/util.c b/util.c new file mode 100644 index 000000000..7f40f7de4 --- /dev/null +++ b/util.c @@ -0,0 +1,81 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#include +#include "util.h" + +inline void delay(uint32_t wait) +{ + while (--wait > 0) __asm__("nop"); +} + +static const char *hexdigits = "0123456789ABCDEF"; + +void uint32hex(uint32_t num, char *str) +{ + uint32_t i; + for (i = 0; i < 8; i++) { + str[i] = hexdigits[(num >> (28 - i * 4)) & 0xF]; + } +} + +// converts data to hexa +void data2hex(const void *data, uint32_t len, char *str) +{ + uint32_t i; + const uint8_t *cdata = (uint8_t *)data; + for (i = 0; i < len; i++) { + str[i * 2 ] = hexdigits[(cdata[i] >> 4) & 0xF]; + str[i * 2 + 1] = hexdigits[cdata[i] & 0xF]; + } + str[len * 2] = 0; +} + +uint32_t readprotobufint(uint8_t **ptr) +{ + uint32_t result = (**ptr & 0x7F); + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128; + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128 * 128; + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128 * 128 * 128; + if (**ptr & 0x80) { + (*ptr)++; + result += (**ptr & 0x7F) * 128 * 128 * 128 * 128; + } + } + } + } + (*ptr)++; + return result; +} + +void __attribute__((noreturn)) system_halt(void) +{ + for (;;) {} // loop forever +} + +void __attribute__((noreturn)) system_reset(void) +{ + scb_reset_system(); +} diff --git a/util.h b/util.h new file mode 100644 index 000000000..d7bb5b424 --- /dev/null +++ b/util.h @@ -0,0 +1,41 @@ +/* + * This file is part of the TREZOR project. + * + * Copyright (C) 2014 Pavol Rusnak + * + * 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 . + */ + +#ifndef __UTIL_H_ +#define __UTIL_H_ + +#include + +inline void delay(uint32_t wait); + +// converts uint32 to hexa (8 digits) +void uint32hex(uint32_t num, char *str); + +// converts data to hexa +void data2hex(const void *data, uint32_t len, char *str); + +// read protobuf integer and advance pointer +uint32_t readprotobufint(uint8_t **ptr); + +// halt execution (or do an endless loop) +void __attribute__((noreturn)) system_halt(void); +// reset system +void __attribute__((noreturn)) system_reset(void); + +#endif