Compare commits
8 commits
df4567d015
...
ccceda910e
Author | SHA1 | Date | |
---|---|---|---|
|
ccceda910e | ||
|
51e1221fca | ||
|
bb310e566b | ||
|
67c4533030 | ||
|
e66b6ef4cb | ||
|
2a9f413ea7 | ||
|
a789756101 | ||
|
c315274d61 |
5 changed files with 809 additions and 1 deletions
5
LICENSE
5
LICENSE
|
@ -1,6 +1,9 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017 BitShares Munich IVS
|
Copyright (c) 2018 AGORISE, LTD.
|
||||||
|
An International Business Company, Cyprus Reg# ΗΕ375959
|
||||||
|
|
||||||
|
Contains works from BitShares Munich IVS
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
80
Testing/HelloPerso_Test/Makefile
Normal file
80
Testing/HelloPerso_Test/Makefile
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#*******************************************************************************
|
||||||
|
# Ledger Blue
|
||||||
|
# (c) 2016 Ledger
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#*******************************************************************************
|
||||||
|
|
||||||
|
ifeq ($(BOLOS_SDK),)
|
||||||
|
$(error BOLOS_SDK is not set)
|
||||||
|
endif
|
||||||
|
include $(BOLOS_SDK)/Makefile.defines
|
||||||
|
|
||||||
|
# Main app configuration
|
||||||
|
|
||||||
|
APPNAME = "Hello Perso"
|
||||||
|
APPVERSION = 1.0.0
|
||||||
|
APP_LOAD_PARAMS = --appFlags 0x40 $(COMMON_LOAD_PARAMS)
|
||||||
|
|
||||||
|
# Build configuration
|
||||||
|
|
||||||
|
APP_SOURCE_PATH += src
|
||||||
|
SDK_SOURCE_PATH += lib_stusb lib_stusb_impl
|
||||||
|
|
||||||
|
DEFINES += APPVERSION=\"$(APPVERSION)\"
|
||||||
|
|
||||||
|
DEFINES += OS_IO_SEPROXYHAL IO_SEPROXYHAL_BUFFER_SIZE_B=128
|
||||||
|
DEFINES += HAVE_BAGL HAVE_SPRINTF
|
||||||
|
DEFINES += PRINTF\(...\)=
|
||||||
|
|
||||||
|
DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=7 IO_HID_EP_LENGTH=64 HAVE_USB_APDU
|
||||||
|
|
||||||
|
# Compiler, assembler, and linker
|
||||||
|
|
||||||
|
ifneq ($(BOLOS_ENV),)
|
||||||
|
$(info BOLOS_ENV=$(BOLOS_ENV))
|
||||||
|
CLANGPATH := $(BOLOS_ENV)/clang-arm-fropi/bin/
|
||||||
|
GCCPATH := $(BOLOS_ENV)/gcc-arm-none-eabi-5_3-2016q1/bin/
|
||||||
|
else
|
||||||
|
$(info BOLOS_ENV is not set: falling back to CLANGPATH and GCCPATH)
|
||||||
|
endif
|
||||||
|
ifeq ($(CLANGPATH),)
|
||||||
|
$(info CLANGPATH is not set: clang will be used from PATH)
|
||||||
|
endif
|
||||||
|
ifeq ($(GCCPATH),)
|
||||||
|
$(info GCCPATH is not set: arm-none-eabi-* will be used from PATH)
|
||||||
|
endif
|
||||||
|
|
||||||
|
CC := $(CLANGPATH)clang
|
||||||
|
CFLAGS += -O3 -Os
|
||||||
|
|
||||||
|
AS := $(GCCPATH)arm-none-eabi-gcc
|
||||||
|
AFLAGS +=
|
||||||
|
|
||||||
|
LD := $(GCCPATH)arm-none-eabi-gcc
|
||||||
|
LDFLAGS += -O3 -Os
|
||||||
|
LDLIBS += -lm -lgcc -lc
|
||||||
|
|
||||||
|
# Main rules
|
||||||
|
|
||||||
|
all: default
|
||||||
|
|
||||||
|
load: all
|
||||||
|
python -m ledgerblue.loadApp $(APP_LOAD_PARAMS)
|
||||||
|
|
||||||
|
delete:
|
||||||
|
python -m ledgerblue.deleteApp $(COMMON_DELETE_PARAMS)
|
||||||
|
|
||||||
|
# Import generic rules from the SDK
|
||||||
|
|
||||||
|
include $(BOLOS_SDK)/Makefile.rules
|
61
Testing/HelloPerso_Test/helloperso-notes.md
Normal file
61
Testing/HelloPerso_Test/helloperso-notes.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# Notes on Hello Perso sample app.
|
||||||
|
|
||||||
|
Hello Perso is a sample app from the LedgerHQ repository
|
||||||
|
[blue-sample-apps](https://github.com/LedgerHQ/blue-sample-apps). It's
|
||||||
|
stated purpose is to be a "a simple application showing the UI and master
|
||||||
|
seed derivation."
|
||||||
|
|
||||||
|
### Compiling:
|
||||||
|
|
||||||
|
Compiles fine inside Docker image, as in the process described in the
|
||||||
|
[Getting Started](http://ledger.readthedocs.io/en/latest/nanos/setup.html)
|
||||||
|
tutorial (which takes you through the Hello World tutorial).
|
||||||
|
|
||||||
|
In extracting the hex-encoded compiled app from the docker container to my
|
||||||
|
local machine, I changed the app name as follows:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
sudo docker cp [container_id]:/home/blue-sample-apps/blue-app-helloperso/bin/app.hex perso-app.hex
|
||||||
|
```
|
||||||
|
|
||||||
|
I then loaded the app onto the Nano S using the following, and then
|
||||||
|
discovered a problem:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
python -m ledgerblue.loadApp --targetId 0x31100002 --apdu --fileName perso-app.hex --appName HelloPerso --appFlags 0x00 --icon ""
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem: Hello Perso app requires the `APPLICATION_FLAG_GLOBAL_PIN`
|
||||||
|
permission flag. I had just copied and pasted my loadApp command line for
|
||||||
|
HelloWorld and changed the app name and image name. But the `--appFlags`
|
||||||
|
argument needed to be changed to `--appFlags 0x40`.
|
||||||
|
|
||||||
|
Symptoms: With the wrong permissions set, even though the app was
|
||||||
|
successfuly loaded onto the device, it would freeze the Nano every
|
||||||
|
time I started the app, requiring a power-cycle to get back to the
|
||||||
|
dashboard.
|
||||||
|
|
||||||
|
The correct load command was:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
python -m ledgerblue.loadApp --targetId 0x31100002 --apdu --fileName perso-app.hex --appName HelloPerso --appFlags 0x40 --icon ""
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to remove apps:
|
||||||
|
|
||||||
|
Problem Two: Repeating the loadApp() command with the correct flags failed
|
||||||
|
because the app already existed on the device. It apparently won't
|
||||||
|
overwrite. Thus I had to find a delete command, which fortunately is
|
||||||
|
referenced in the Makefile. To manually delete the app, this is the
|
||||||
|
command:
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
python -m ledgerblue.deleteApp --targetId 0x31100002 --appName HelloPerso
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission flags defined:
|
||||||
|
|
||||||
|
The `0x40` permission flag that was needed is defined by macro constant
|
||||||
|
`APPLICATION_FLAG_GLOBAL_PIN` in
|
||||||
|
[nanos-secure-sdk/include/os.h](https://github.com/LedgerHQ/nanos-secure-sdk/blob/master/include/os.h),
|
||||||
|
along with many other application flags.
|
574
Testing/HelloPerso_Test/src/main.c
Normal file
574
Testing/HelloPerso_Test/src/main.c
Normal file
|
@ -0,0 +1,574 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Ledger Blue
|
||||||
|
* (c) 2016 Ledger
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
#include "os.h"
|
||||||
|
#include "cx.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "os_io_seproxyhal.h"
|
||||||
|
|
||||||
|
unsigned char G_io_seproxyhal_spi_buffer[IO_SEPROXYHAL_BUFFER_SIZE_B];
|
||||||
|
|
||||||
|
static const bagl_element_t *io_seproxyhal_touch_exit(const bagl_element_t *e);
|
||||||
|
static const bagl_element_t *io_seproxyhal_touch_next(const bagl_element_t *e);
|
||||||
|
static const bagl_element_t *io_seproxyhal_touch_auth(const bagl_element_t *e);
|
||||||
|
static bool derive(void);
|
||||||
|
static void ui_idle(void);
|
||||||
|
|
||||||
|
static char address[100];
|
||||||
|
static unsigned int path[5];
|
||||||
|
ux_state_t ux;
|
||||||
|
|
||||||
|
static const char NOT_AVAILABLE[] = "Not available";
|
||||||
|
|
||||||
|
// ********************************************************************************
|
||||||
|
// Ledger Blue specific UI
|
||||||
|
// ********************************************************************************
|
||||||
|
|
||||||
|
#ifdef TARGET_BLUE
|
||||||
|
|
||||||
|
static const bagl_element_t const bagl_ui_sample_blue[] = {
|
||||||
|
// {
|
||||||
|
// {type, userid, x, y, width, height, stroke, radius, fill, fgcolor,
|
||||||
|
// bgcolor, font_id, icon_id},
|
||||||
|
// text,
|
||||||
|
// touch_area_brim,
|
||||||
|
// overfgcolor,
|
||||||
|
// overbgcolor,
|
||||||
|
// tap,
|
||||||
|
// out,
|
||||||
|
// over,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
{BAGL_RECTANGLE, 0x00, 0, 60, 320, 420, 0, 0, BAGL_FILL, 0xf9f9f9,
|
||||||
|
0xf9f9f9, 0, 0},
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_RECTANGLE, 0x00, 0, 0, 320, 60, 0, 0, BAGL_FILL, 0x1d2028,
|
||||||
|
0x1d2028, 0, 0},
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_LABEL, 0x00, 20, 0, 320, 60, 0, 0, BAGL_FILL, 0xFFFFFF, 0x1d2028,
|
||||||
|
BAGL_FONT_OPEN_SANS_LIGHT_14px | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
|
||||||
|
"Hello Perso",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_LABEL, 0x00, 20, 100, 320, 60, 0, 0, 0, 0, 0xF9F9F9F9,
|
||||||
|
BAGL_FONT_OPEN_SANS_LIGHT_13px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
||||||
|
address,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 165, 225, 120, 40, 0, 6,
|
||||||
|
BAGL_FILL, 0x41ccb4, 0xF9F9F9, BAGL_FONT_OPEN_SANS_LIGHT_14px |
|
||||||
|
BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
|
||||||
|
"EXIT",
|
||||||
|
0,
|
||||||
|
0x37ae99,
|
||||||
|
0xF9F9F9,
|
||||||
|
io_seproxyhal_touch_exit,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 165, 280, 120, 40, 0, 6,
|
||||||
|
BAGL_FILL, 0x41ccb4, 0xF9F9F9, BAGL_FONT_OPEN_SANS_LIGHT_14px |
|
||||||
|
BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
|
||||||
|
"NEXT",
|
||||||
|
0,
|
||||||
|
0x37ae99,
|
||||||
|
0xF9F9F9,
|
||||||
|
io_seproxyhal_touch_next,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_BUTTON | BAGL_FLAG_TOUCHABLE, 0x00, 165, 335, 120, 40, 0, 6,
|
||||||
|
BAGL_FILL, 0x41ccb4, 0xF9F9F9, BAGL_FONT_OPEN_SANS_LIGHT_14px |
|
||||||
|
BAGL_FONT_ALIGNMENT_CENTER | BAGL_FONT_ALIGNMENT_MIDDLE, 0},
|
||||||
|
"AUTH",
|
||||||
|
0,
|
||||||
|
0x37ae99,
|
||||||
|
0xF9F9F9,
|
||||||
|
io_seproxyhal_touch_auth,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
bagl_ui_sample_blue_button(unsigned int button_mask,
|
||||||
|
unsigned int button_mask_counter) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ********************************************************************************
|
||||||
|
// Ledger Nano S specific UI
|
||||||
|
// ********************************************************************************
|
||||||
|
|
||||||
|
#ifdef TARGET_NANOS
|
||||||
|
|
||||||
|
static const bagl_element_t bagl_ui_sample_nanos[] = {
|
||||||
|
// {
|
||||||
|
// {type, userid, x, y, width, height, stroke, radius, fill, fgcolor,
|
||||||
|
// bgcolor, font_id, icon_id},
|
||||||
|
// text,
|
||||||
|
// touch_area_brim,
|
||||||
|
// overfgcolor,
|
||||||
|
// overbgcolor,
|
||||||
|
// tap,
|
||||||
|
// out,
|
||||||
|
// over,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000,
|
||||||
|
0xFFFFFF, 0, 0},
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_LABELINE, 0x02, 0, 12, 128, 11, 0, 0, 0, 0xFFFFFF, 0x000000,
|
||||||
|
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
|
||||||
|
"Address",
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_LABELINE, 0x02, 23, 26, 82, 11, 0x80 | 10, 0, 0, 0xFFFFFF,
|
||||||
|
0x000000, BAGL_FONT_OPEN_SANS_EXTRABOLD_11px |
|
||||||
|
BAGL_FONT_ALIGNMENT_CENTER, 26},
|
||||||
|
address,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
||||||
|
BAGL_GLYPH_ICON_CROSS},
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{BAGL_ICON, 0x00, 117, 13, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
|
||||||
|
BAGL_GLYPH_ICON_CHECK},
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const bagl_element_t*
|
||||||
|
bagl_ui_sample_nanos_prepro(const bagl_element_t *element) {
|
||||||
|
switch (element->component.userid) {
|
||||||
|
case 2:
|
||||||
|
io_seproxyhal_setup_ticker(
|
||||||
|
MAX(3000, 1000 + bagl_label_roundtrip_duration_ms(element, 7)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
bagl_ui_sample_nanos_button(unsigned int button_mask,
|
||||||
|
unsigned int button_mask_counter) {
|
||||||
|
switch (button_mask) {
|
||||||
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
|
||||||
|
io_seproxyhal_touch_auth(NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
|
||||||
|
io_seproxyhal_touch_next(NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: // EXIT
|
||||||
|
io_seproxyhal_touch_exit(NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const bagl_element_t *io_seproxyhal_touch_exit(const bagl_element_t *e) {
|
||||||
|
// Go back to the dashboard
|
||||||
|
os_sched_exit(0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bagl_element_t *io_seproxyhal_touch_next(const bagl_element_t *e) {
|
||||||
|
path[4]++;
|
||||||
|
if (!derive()) {
|
||||||
|
path[4]--;
|
||||||
|
}
|
||||||
|
ui_idle();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bagl_element_t *io_seproxyhal_touch_auth(const bagl_element_t *e) {
|
||||||
|
if (!os_global_pin_is_validated()) {
|
||||||
|
bolos_ux_params_t params;
|
||||||
|
os_memset(¶ms, 0, sizeof(params));
|
||||||
|
params.ux_id = BOLOS_UX_VALIDATE_PIN;
|
||||||
|
os_ux_blocking(¶ms);
|
||||||
|
derive();
|
||||||
|
ui_idle();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short io_exchange_al(unsigned char channel, unsigned short tx_len) {
|
||||||
|
switch (channel & ~(IO_FLAGS)) {
|
||||||
|
case CHANNEL_KEYBOARD:
|
||||||
|
break;
|
||||||
|
|
||||||
|
// multiplexed io exchange over a SPI channel and TLV encapsulated protocol
|
||||||
|
case CHANNEL_SPI:
|
||||||
|
if (tx_len) {
|
||||||
|
io_seproxyhal_spi_send(G_io_apdu_buffer, tx_len);
|
||||||
|
|
||||||
|
if (channel & IO_RESET_AFTER_REPLIED) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
return 0; // nothing received from the master so far (it's a tx
|
||||||
|
// transaction)
|
||||||
|
} else {
|
||||||
|
return io_seproxyhal_spi_recv(G_io_apdu_buffer,
|
||||||
|
sizeof(G_io_apdu_buffer), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
THROW(INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ui_idle(void) {
|
||||||
|
#ifdef TARGET_BLUE
|
||||||
|
UX_DISPLAY(bagl_ui_sample_blue, NULL);
|
||||||
|
#else
|
||||||
|
UX_DISPLAY(bagl_ui_sample_nanos, bagl_ui_sample_nanos_prepro);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sample_main(void) {
|
||||||
|
volatile unsigned int rx = 0;
|
||||||
|
volatile unsigned int tx = 0;
|
||||||
|
volatile unsigned int flags = 0;
|
||||||
|
|
||||||
|
// DESIGN NOTE: the bootloader ignores the way APDU are fetched. The only
|
||||||
|
// goal is to retrieve APDU.
|
||||||
|
// When APDU are to be fetched from multiple IOs, like NFC+USB+BLE, make
|
||||||
|
// sure the io_event is called with a
|
||||||
|
// switch event, before the apdu is replied to the bootloader. This avoid
|
||||||
|
// APDU injection faults.
|
||||||
|
for (;;) {
|
||||||
|
volatile unsigned short sw = 0;
|
||||||
|
|
||||||
|
BEGIN_TRY {
|
||||||
|
TRY {
|
||||||
|
rx = tx;
|
||||||
|
tx = 0; // ensure no race in catch_other if io_exchange throws
|
||||||
|
// an error
|
||||||
|
rx = io_exchange(CHANNEL_APDU | flags, rx);
|
||||||
|
flags = 0;
|
||||||
|
|
||||||
|
// no apdu received, well, reset the session, and reset the
|
||||||
|
// bootloader configuration
|
||||||
|
if (rx == 0) {
|
||||||
|
THROW(0x6982);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_io_apdu_buffer[0] != 0x80) {
|
||||||
|
THROW(0x6E00);
|
||||||
|
}
|
||||||
|
|
||||||
|
// unauthenticated instruction
|
||||||
|
switch (G_io_apdu_buffer[1]) {
|
||||||
|
case 0x00: // reset
|
||||||
|
flags |= IO_RESET_AFTER_REPLIED;
|
||||||
|
THROW(0x9000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x01: // case 1
|
||||||
|
THROW(0x9000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x02: // echo
|
||||||
|
tx = rx;
|
||||||
|
THROW(0x9000);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xFF: // return to dashboard
|
||||||
|
goto return_to_dashboard;
|
||||||
|
|
||||||
|
default:
|
||||||
|
THROW(0x6D00);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CATCH_OTHER(e) {
|
||||||
|
switch (e & 0xF000) {
|
||||||
|
case 0x6000:
|
||||||
|
case 0x9000:
|
||||||
|
sw = e;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sw = 0x6800 | (e & 0x7FF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Unexpected exception => report
|
||||||
|
G_io_apdu_buffer[tx] = sw >> 8;
|
||||||
|
G_io_apdu_buffer[tx + 1] = sw;
|
||||||
|
tx += 2;
|
||||||
|
}
|
||||||
|
FINALLY {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return_to_dashboard:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void io_seproxyhal_display(const bagl_element_t *element) {
|
||||||
|
io_seproxyhal_display_default((bagl_element_t *)element);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char io_event(unsigned char channel) {
|
||||||
|
// nothing done with the event, throw an error on the transport layer if
|
||||||
|
// needed
|
||||||
|
|
||||||
|
// can't have more than one tag in the reply, not supported yet.
|
||||||
|
switch (G_io_seproxyhal_spi_buffer[0]) {
|
||||||
|
case SEPROXYHAL_TAG_FINGER_EVENT:
|
||||||
|
UX_FINGER_EVENT(G_io_seproxyhal_spi_buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEPROXYHAL_TAG_BUTTON_PUSH_EVENT: // for Nano S
|
||||||
|
UX_BUTTON_PUSH_EVENT(G_io_seproxyhal_spi_buffer);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEPROXYHAL_TAG_DISPLAY_PROCESSED_EVENT:
|
||||||
|
if (UX_DISPLAYED()) {
|
||||||
|
// TODO perform actions after all screen elements have been
|
||||||
|
// displayed
|
||||||
|
} else {
|
||||||
|
UX_DISPLAYED_EVENT();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEPROXYHAL_TAG_TICKER_EVENT:
|
||||||
|
UX_REDISPLAY();
|
||||||
|
break;
|
||||||
|
|
||||||
|
// unknown events are acknowledged
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// close the event if not done previously (by a display or whatever)
|
||||||
|
if (!io_seproxyhal_spi_is_status_sent()) {
|
||||||
|
io_seproxyhal_general_status();
|
||||||
|
}
|
||||||
|
|
||||||
|
// command has been processed, DO NOT reset the current APDU transport
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char BASE58ALPHABET[] = {
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
||||||
|
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
|
||||||
|
'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'm',
|
||||||
|
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
|
||||||
|
|
||||||
|
static unsigned int encode_base58(const void *in, unsigned int length,
|
||||||
|
char *out, unsigned int maxoutlen) {
|
||||||
|
char tmp[164];
|
||||||
|
char buffer[164];
|
||||||
|
unsigned char j;
|
||||||
|
unsigned char startAt;
|
||||||
|
unsigned char zeroCount = 0;
|
||||||
|
if (length > sizeof(tmp)) {
|
||||||
|
THROW(INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
os_memmove(tmp, in, length);
|
||||||
|
while ((zeroCount < length) && (tmp[zeroCount] == 0)) {
|
||||||
|
++zeroCount;
|
||||||
|
}
|
||||||
|
j = 2 * length;
|
||||||
|
startAt = zeroCount;
|
||||||
|
while (startAt < length) {
|
||||||
|
unsigned short remainder = 0;
|
||||||
|
unsigned char divLoop;
|
||||||
|
for (divLoop = startAt; divLoop < length; divLoop++) {
|
||||||
|
unsigned short digit256 = (unsigned short)(tmp[divLoop] & 0xff);
|
||||||
|
unsigned short tmpDiv = remainder * 256 + digit256;
|
||||||
|
tmp[divLoop] = (unsigned char)(tmpDiv / 58);
|
||||||
|
remainder = (tmpDiv % 58);
|
||||||
|
}
|
||||||
|
if (tmp[startAt] == 0) {
|
||||||
|
++startAt;
|
||||||
|
}
|
||||||
|
buffer[--j] = BASE58ALPHABET[remainder];
|
||||||
|
}
|
||||||
|
while ((j < (2 * length)) && (buffer[j] == BASE58ALPHABET[0])) {
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
while (zeroCount-- > 0) {
|
||||||
|
buffer[--j] = BASE58ALPHABET[0];
|
||||||
|
}
|
||||||
|
length = 2 * length - j;
|
||||||
|
if (maxoutlen < length) {
|
||||||
|
THROW(EXCEPTION_OVERFLOW);
|
||||||
|
}
|
||||||
|
os_memmove(out, (buffer + j), length);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool derive() {
|
||||||
|
cx_ecfp_private_key_t privateKey;
|
||||||
|
cx_ecfp_public_key_t publicKey;
|
||||||
|
union {
|
||||||
|
cx_sha256_t shasha;
|
||||||
|
cx_ripemd160_t riprip;
|
||||||
|
} u;
|
||||||
|
unsigned char privateKeyData[32];
|
||||||
|
unsigned char tmp[25];
|
||||||
|
unsigned int length;
|
||||||
|
|
||||||
|
if (!os_global_pin_is_validated()) {
|
||||||
|
os_memmove(address, NOT_AVAILABLE, sizeof(NOT_AVAILABLE));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
os_perso_derive_node_bip32(CX_CURVE_256K1, path, 5, privateKeyData, NULL);
|
||||||
|
|
||||||
|
cx_ecdsa_init_private_key(CX_CURVE_256K1, privateKeyData, 32, &privateKey);
|
||||||
|
cx_ecfp_generate_pair(CX_CURVE_256K1, &publicKey, &privateKey, 1);
|
||||||
|
publicKey.W[0] = ((publicKey.W[64] & 1) ? 0x03 : 0x02);
|
||||||
|
cx_sha256_init(&u.shasha);
|
||||||
|
cx_hash(&u.shasha.header, CX_LAST, publicKey.W, 33, privateKeyData);
|
||||||
|
cx_ripemd160_init(&u.riprip);
|
||||||
|
cx_hash(&u.riprip.header, CX_LAST, privateKeyData, 32, tmp + 1);
|
||||||
|
tmp[0] = 0;
|
||||||
|
cx_sha256_init(&u.shasha);
|
||||||
|
cx_hash(&u.shasha.header, CX_LAST, tmp, 21, privateKeyData);
|
||||||
|
cx_sha256_init(&u.shasha);
|
||||||
|
cx_hash(&u.shasha.header, CX_LAST, privateKeyData, 32, privateKeyData);
|
||||||
|
os_memmove(tmp + 21, privateKeyData, 4);
|
||||||
|
length = encode_base58(tmp, sizeof(tmp), address, sizeof(address));
|
||||||
|
address[length] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((section(".boot"))) int main(void) {
|
||||||
|
// exit critical section
|
||||||
|
__asm volatile("cpsie i");
|
||||||
|
|
||||||
|
// ensure exception will work as planned
|
||||||
|
os_boot();
|
||||||
|
|
||||||
|
UX_INIT();
|
||||||
|
|
||||||
|
BEGIN_TRY {
|
||||||
|
TRY {
|
||||||
|
io_seproxyhal_init();
|
||||||
|
|
||||||
|
// Invalidate the current authentication to demonstrate
|
||||||
|
// reauthentication
|
||||||
|
// Reauthenticate with "Auth" (Blue) or left button (Nano S)
|
||||||
|
os_global_pin_invalidate();
|
||||||
|
|
||||||
|
#ifdef LISTEN_BLE
|
||||||
|
if (os_seph_features() &
|
||||||
|
SEPROXYHAL_TAG_SESSION_START_EVENT_FEATURE_BLE) {
|
||||||
|
BLE_power(0, NULL);
|
||||||
|
// restart IOs
|
||||||
|
BLE_power(1, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
USB_power(0);
|
||||||
|
USB_power(1);
|
||||||
|
|
||||||
|
path[0] = 44 | 0x80000000;
|
||||||
|
path[1] = 0 | 0x80000000;
|
||||||
|
path[2] = 0 | 0x80000000;
|
||||||
|
path[3] = 0;
|
||||||
|
path[4] = 0;
|
||||||
|
|
||||||
|
derive();
|
||||||
|
ui_idle();
|
||||||
|
|
||||||
|
sample_main();
|
||||||
|
}
|
||||||
|
CATCH_OTHER(e) {
|
||||||
|
}
|
||||||
|
FINALLY {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TRY;
|
||||||
|
}
|
90
doc/nanos-getting-started.md
Normal file
90
doc/nanos-getting-started.md
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
# Some Background for getting started developing for Nano S
|
||||||
|
|
||||||
|
### Relevent Repositories
|
||||||
|
|
||||||
|
**The development SDK:** contains header and library files for compiling
|
||||||
|
Nano S applications. Located at:
|
||||||
|
[LedgerHQ/nanos-secure-sdk](https://github.com/LedgerHQ/nanos-secure-sdk).
|
||||||
|
|
||||||
|
**Python Tools for Ledger Blue and Nano S:** These contain tools for loading
|
||||||
|
apps onto the Nano S, and basic communication between Nano S and a python
|
||||||
|
interface on the computer. Located at:
|
||||||
|
[LedgerHQ/blue-loader-python](https://github.com/LedgerHQ/blue-loader-python).
|
||||||
|
|
||||||
|
* The various python scripts are documented reasonably well at:
|
||||||
|
[Script Reference](https://ledger.readthedocs.io/projects/blue-loader-python/en/0.1.15/script_reference.html)
|
||||||
|
|
||||||
|
**Sample Apps:** These provide examples of basic operations on the Nano S
|
||||||
|
device. Located at:
|
||||||
|
[LedgerHQ/blue-sample-apps](https://github.com/LedgerHQ/blue-sample-apps).
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
Comprehensive developer documentation does not seem to be available yet (but
|
||||||
|
is expected). Some very good general overview information for developers on
|
||||||
|
the way the BOLOS operating system and apps work and interact is located
|
||||||
|
here, however:
|
||||||
|
[Ledger Documentation Hub](http://ledger.readthedocs.io/en/latest/index.html).
|
||||||
|
|
||||||
|
A tutorial for building, installing, and running the Hello World sample app
|
||||||
|
is located here: (part of the docs mentioned above)
|
||||||
|
[Getting started](http://ledger.readthedocs.io/en/latest/nanos/setup.html).
|
||||||
|
It may be slightly out of date.
|
||||||
|
|
||||||
|
## Notes and tidbits
|
||||||
|
|
||||||
|
### Hardware memory and executable formats
|
||||||
|
|
||||||
|
Some basic info about the Nano S execution environment memory specs and
|
||||||
|
layout can be divined by looking at the linker scripts `script.ld` and
|
||||||
|
`script.ux.ld` in the SDK at
|
||||||
|
[LedgerHQ/nanos-secure-sdk](https://github.com/LedgerHQ/nanos-secure-sdk).
|
||||||
|
|
||||||
|
Note, what follows here comes from my *very fuzzy* understanding of the
|
||||||
|
linking and loading process and of the BOLOS architecture, and thus is
|
||||||
|
conjectural and could be very wrong, but... from these scripts it can be
|
||||||
|
devined, e.g., that UX code (I think this is BOLOS-provided library code
|
||||||
|
with different security priveledges that the app developer doesn't write,
|
||||||
|
but this is a pure guess) is loaded into a 2 kB window with a 768 byte
|
||||||
|
(wow!) stack size; and that application code is loaded into a 4 kB window
|
||||||
|
with another 768 byte stack. Obviously, this is an *extrememly* tight
|
||||||
|
execution envirement.
|
||||||
|
|
||||||
|
Some wiggle-room is provided by the fact that there is also a read-only (to
|
||||||
|
the application) non-volatile flash ram window of 400 kB. According to docs
|
||||||
|
at
|
||||||
|
[(Ledger docs)](http://ledger.readthedocs.io/en/latest/userspace/memory.html),
|
||||||
|
the executable code and const-declared variables are stored here, so that
|
||||||
|
the limited available volatile ram is not used up for this. And also, a
|
||||||
|
mechanism is provided to carve off *some* of the ro nvram as rw so that apps
|
||||||
|
can keep some persistant storage.
|
||||||
|
|
||||||
|
I actally have no idea how to read these linker scripts, so my conclusions
|
||||||
|
here are "as best I understand them." There is some documentation on linker
|
||||||
|
scripts here:
|
||||||
|
[LD Scripts](https://sourceware.org/binutils/docs/ld/Scripts.html).
|
||||||
|
|
||||||
|
Another interesting oddity: the link script places "RAM-initialized
|
||||||
|
variables" into a memory region titled DISCARD and apparently mapped to a
|
||||||
|
non-existent region of address-space on the Nano. This would have the
|
||||||
|
effect of just vaporizing ram-initialized variables altogether, I would
|
||||||
|
(naively??) think. It's weird, but I do think this is exactly what it does,
|
||||||
|
because the BOLOS documentation includes this warning:
|
||||||
|
|
||||||
|
> Initializers of global non-const variables (including NVRAM variables) are
|
||||||
|
> ignored. As such, this data must be initialized by application code.
|
||||||
|
|
||||||
|
So global data cannot have initializers. Got it. Couldn't tell you why, but
|
||||||
|
got it. :thumbsup:.
|
||||||
|
|
||||||
|
Another point: BOLOS code, being embedded, makes frequent use of the
|
||||||
|
**volatile** keyword. For some reminders of when/what/why `volatile` is
|
||||||
|
used, here are two links that came up in my google search:
|
||||||
|
* [How to Use C's volatile Keyword](https://barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword)
|
||||||
|
* [Nine ways to break your systems code using volatile](https://blog.regehr.org/archives/28)
|
||||||
|
|
||||||
|
Ah, one reason for such frequent use of `volatile` is the TRY/CATCH macros
|
||||||
|
evidently can confuse the optimizer in some way. For this reason, it is
|
||||||
|
recommended to declare variables that may be modified inside
|
||||||
|
try/catch/finally contexts as `volatile`. Referenced in the docs here:
|
||||||
|
[Error Handling](http://ledger.readthedocs.io/en/latest/userspace/troubleshooting.html#error-handling).
|
Loading…
Reference in a new issue