Merge pull request #39 from SoloKeysSec/bootloader

Bootloader
This commit is contained in:
Conor Patrick 2018-12-08 14:44:27 -05:00 committed by GitHub
commit 51cf2d5ec9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 2705 additions and 617 deletions

View File

@ -23,13 +23,13 @@ else
export LDFLAGS = -Wl,--gc-sections
endif
LDFLAGS += $(LIBCBOR)
CFLAGS = -O2 -fdata-sections -ffunction-sections
CFLAGS = -O2 -fdata-sections -ffunction-sections
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
CFLAGS += $(INCLUDES)
# for crypto/tiny-AES-c
CFLAGS += -DAES256=1
CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\"
name = main
@ -84,12 +84,6 @@ venv:
virtualenv venv
./venv/bin/pip install wheel
venv/bin/mkdocs: venv
./venv/bin/pip install mkdocs mkdocs-material
.PHONY: docsrv
docsrv: venv/bin/mkdocs
./venv/bin/mkdocs serve
.PHONY: fido2-test
fido2-test:

View File

@ -1,28 +1,171 @@
To build, develop and debug the firmware for the STM32L442 (WIP!) via cross-compilation on Linux, no vendor-specific software is necessary.
To build, develop and debug the firmware for the STM32L432. This will work
for Solo Hacker, the Nucleo development board, or you own homemade Solo.
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; the L432 chip differs from the L442 used for Solo only in that it lacks a cryptographic accelerator. The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
You will need the following packages (naming given for Arch Linux):
# Prerequisites
- arm-none-eabi-gcc
- arm-none-eabi-newlib
- arm-none-eabi-binutils
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system.
and one of
You can also install the ARM toolchain using a package manage like `apt-get` or `pacman`,
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
To program your build, you'll need one of the following programs.
- [openocd](http://openocd.org)
- [stlink](https://github.com/texane/stlink)
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
If you remove the `.exe` extensions in the [Makefile](https://github.com/SoloKeysSec/solo/blob/master/targets/stm32l442/Makefile), and possibly add a `-g` flag, compilation runs through.
# Compilation
To flash and step through the code:
Enter the `stm32l4xx` target directory.
* connect the Nucleo to your PC
* attach one of the debuggers: `st-util` (for stlink), or `openocd -f interface/stlink-v2-1.cfg -f target/stm32l4x.cfg` (for openocd)
* launch `gdb` via `arm-none-eabi-gdb -q solo.elf`
* connect gdb to the debugger via `target extended-remote :4242` (for stlink), or `target remote :3333` (for openocd)
* flash the firmware via `load`
* optionally set a breakpoint via `break main`
* `continue`, and start stepping 🙌
```
cd targets/stm32l442
```
Note that the code for `targets/stm32l442` currently consists of only a blinky hello world...
Build the cbor library.
```bash
make cbor
```
Now build the Solo bootloader.
```
make clean
make boot
# Or to make a Solo Hacker build:
# make boot-hacker
```
Now build the Solo application.
```
make clean
make
# Or to make a Solo Hacker build:
# make all-hacker
```
Note that for hacker builds, the bootloader must be built using the `boot-hacker` recipe.
Merge the two files together. This script also patches a spot in memory to
allow the bootloader to boot the application. This memory spot is later used for
signed firmware updates.
```
python merge_hex.py solo.hex bootloader.hex all.hex
```
You can now program Solo with `all.hex`.
# Solo Hacker
A Solo Hacker build is more friendly for development. If you just want to test your
solo build or do any sort of development, you should start with this.
It is the same build as a production Solo, but it has extra commands available.
* Allows updates at any time without pressing button.
* Opens a USB emulated serial port for printing.
* Doesn't lock the flash or debugger at all.
You if build with Solo Hacker, you can always completely overwrite it and start over.
If it's not a hacker build, you cannot reprogram Solo as easily.
* `all-hacker`: can be reprogrammed again over USB or via wire with a programmer.
* `all`: can be reprogrammed using only signed updates or via wire with a programmer.
* `all-locked`: Can only be reprogrammed via signed updates unless they are disabled.
# Programming
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
## ST USB DFU
If your Solo has never been programmed, it will boot the ST USB DFU. You can program
it via USB. After you program it, it will still be in ST USB DFU mode. You additionally
need to supply a command to tell the DFU program to "detach" and boot the application.
If you power cycle Solo, it will return to DFU mode so you can reprogram it. If you don't
want it to do this, you must set the option bytes bit `nBOOT0=1`. Now it will always boot the application.
Example using STM32CubeProg.
```
# Program all.hex
STM32_Programmer_CLI -c port=usb1 -halt -d all.hex
# If you want it to always boot application, set nBOOT0=1
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
```
If Solo has been programmed with a hacker build, you can return it to ST DFU mode using just USB.
```
# Use our programmer script
# Makes it boot to ST DFU once
python tools/programmer.py --st-dfu
# OR
# Make it boot to ST DFU every boot (initial state basically)
python tools/programmer.py --enter-bootloader
python tools/programmer.py --st-dfu
```
## Solo / Solo Hacker updates
To program a Solo Hacker device, run the following. Note you should only specify the application
firmware, not the combined bootloader+application! I.e. not `all.hex` from above.
```bash
python tools/programmer.py target/stm32l442/solo.hex
```
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
If the application gets bricked, you can hold down the button for 2 seconds while
plugging it in the token make it stay in the bootloader. Holding the button an additional 5 seconds
will return it to the ST DFU.
If this is not a device with a hacker build, you can only program signed updates.
```
python tools/programmer.py /path/to/firmware.json
```
If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
firmware by running the following command.
```
python tools/sign_firmware.py /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
```
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
to the token.
# Permanently locking the device
If you plan to be using your Solo for real, you should lock it permanently. This prevents
someone from connecting a debugger to your token and stealing credentials.
To do this, build the non-hacker bootloader and locked version of the firmware.
```
make clean
make boot
make clean
make all-locked
python merge_hex.py solo.hex bootloader.hex all.hex
```
Now when you program `all.hex`, the device will lock itself when it first boots. You can only update it
with signed updates.
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```
# No more signed updates.
python tools/programmer.py --disable
```

View File

@ -6,6 +6,6 @@ To host or develop locally:
pip install mkdocs mkdocs-material
```
`make docsrv` and visit [localhost:8000](http://localhost:8000).
`mkdocs serve` and visit [localhost:8000](http://localhost:8000).
The file `runtime.txt` is necessary to tell Netlify to use Python3.

View File

@ -18,7 +18,8 @@
#include "aes.h"
#include "ctap.h"
#include "device.h"
#include "app.h"
#include "log.h"
#include APP_CONFIG
#ifdef USING_PC
typedef enum
@ -104,7 +105,7 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
if(klen > 64)
{
printf("Error, key size must be <= 64\n");
printf2(TAG_ERR,"Error, key size must be <= 64\n");
exit(1);
}
@ -134,7 +135,7 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
if(klen > 64)
{
printf("Error, key size must be <= 64\n");
printf2(TAG_ERR,"Error, key size must be <= 64\n");
exit(1);
}
memmove(buf, key, klen);
@ -168,7 +169,7 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
{
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
{
printf("error, uECC failed\n");
printf2(TAG_ERR,"error, uECC failed\n");
exit(1);
}
}
@ -205,19 +206,19 @@ void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_I
if (_key_len != 32) goto fail;
break;
default:
printf("error, invalid ECDSA alg specifier\n");
printf2(TAG_ERR,"error, invalid ECDSA alg specifier\n");
exit(1);
}
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
{
printf("error, uECC failed\n");
printf2(TAG_ERR,"error, uECC failed\n");
exit(1);
}
return;
fail:
printf("error, invalid key length\n");
printf2(TAG_ERR,"error, invalid key length\n");
exit(1);
}
@ -257,7 +258,7 @@ void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
{
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
{
printf("Error, uECC_make_key failed\n");
printf2(TAG_ERR,"Error, uECC_make_key failed\n");
exit(1);
}
}
@ -266,7 +267,7 @@ void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey
{
if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
{
printf("Error, uECC_shared_secret failed\n");
printf2(TAG_ERR,"Error, uECC_shared_secret failed\n");
exit(1);
}

View File

@ -34,7 +34,7 @@
#include "util.h"
#include "log.h"
#include "device.h"
#include "app.h"
#include APP_CONFIG
#include "wallet.h"
#include "device.h"
@ -1463,7 +1463,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
else
{
printf2(TAG_ERR, "unwanted GET_NEXT_ASSERTION. lastcmd == 0x%02x\n", getAssertionState.lastcmd);
dump_hex1(TAG_GREEN, &getAssertionState, sizeof(getAssertionState));
dump_hex1(TAG_GREEN, (uint8_t*)&getAssertionState, sizeof(getAssertionState));
status = CTAP2_ERR_NOT_ALLOWED;
}
break;

View File

@ -30,7 +30,8 @@
#include "time.h"
#include "util.h"
#include "log.h"
#include "app.h"
#include "extensions.h"
#include APP_CONFIG
typedef enum
{
@ -682,6 +683,56 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
printf1(TAG_HID,"CTAPHID_CANCEL\n");
is_busy = 0;
break;
#if defined(IS_BOOTLOADER)
case CTAPHID_BOOT:
printf1(TAG_HID,"CTAPHID_BOOT\n");
ctap_response_init(&ctap_resp);
u2f_set_writeback_buffer(&ctap_resp);
is_busy = bootloader_bridge(len, ctap_buffer);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_BOOT;
wb.bcnt = (ctap_resp.length + 1);
ctaphid_write(&wb, &is_busy, 1);
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
#endif
#if defined(SOLO_HACKER)
case CTAPHID_ENTERBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_solo_bootloader();
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_ENTERBOOT;
wb.bcnt = 0;
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
case CTAPHID_ENTERSTBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_st_bootloader();
break;
#endif
#if !defined(IS_BOOTLOADER)
case CTAPHID_GETRNG:
printf1(TAG_HID,"CTAPHID_GETRNG\n");
ctap_response_init(&ctap_resp);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_GETRNG;
wb.bcnt = ctap_buffer[0];
if (!wb.bcnt)
wb.bcnt = 57;
memset(ctap_buffer,0,wb.bcnt);
ctap_generate_rng(ctap_buffer, wb.bcnt);
ctaphid_write(&wb, &ctap_buffer, wb.bcnt);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
#endif
default:
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);

View File

@ -38,6 +38,12 @@
#define CTAPHID_ERROR (TYPE_INIT | 0x3f)
#define CTAPHID_KEEPALIVE (TYPE_INIT | 0x3b)
// Custom commands between 0x40-0x7f
#define CTAPHID_BOOT (TYPE_INIT | 0x50)
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
#define ERR_INVALID_CMD 0x01
#define ERR_INVALID_PAR 0x02
#define ERR_INVALID_SEQ 0x04

View File

@ -45,6 +45,7 @@ void main_loop_delay();
void heartbeat();
void authenticator_read_state(AuthenticatorState * );
void authenticator_read_backup_state(AuthenticatorState * );
@ -62,6 +63,9 @@ void device_manage();
// A timer should be set up to call `ctaphid_update_status`
void device_set_status(int status);
// Returns if button is currently pressed
int device_is_button_pressed();
// Test for user presence
// Return 1 for user is present, 0 user not present, -1 if cancel is requested.
extern int ctap_user_presence_test();
@ -90,4 +94,11 @@ void ctap_store_rk(int index,CTAP_residentKey * rk);
void ctap_load_rk(int index,CTAP_residentKey * rk);
void ctap_overwrite_rk(int index,CTAP_residentKey * rk);
// For Solo hacker
void boot_solo_bootloader();
void boot_st_bootloader();
#endif

View File

@ -23,9 +23,68 @@
#include "extensions.h"
#include "u2f.h"
#include "wallet.h"
#include "device.h"
#include "log.h"
#define htonl(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8) \
| ((x & 0xff0000) >> 8) | ((x & 0xff000000) >> 24) )
int is_extension_request(uint8_t * kh, int len)
{
wallet_request * req = (wallet_request *) kh;
if (len < WALLET_MIN_LENGTH)
return 0;
return memcmp(req->tag, WALLET_TAG, sizeof(WALLET_TAG)-1) == 0;
}
int extension_needs_atomic_count(uint8_t klen, uint8_t * keyh)
{
return ((wallet_request *) keyh)->operation == WalletRegister
|| ((wallet_request *) keyh)->operation == WalletSign;
}
int16_t bridge_u2f_to_extensions(uint8_t * _chal, uint8_t * _appid, uint8_t klen, uint8_t * keyh)
{
int8_t ret = 0;
uint32_t count;
uint8_t up = 1;
uint8_t sig[72];
if (extension_needs_atomic_count(klen, keyh))
{
count = htonl(ctap_atomic_count(0));
}
else
{
count = htonl(10);
}
u2f_response_writeback(&up,1);
u2f_response_writeback((uint8_t *)&count,4);
u2f_response_writeback((uint8_t *)&ret,1);
#ifdef IS_BOOTLOADER
ret = bootloader_bridge(klen, keyh);
#elif defined(WALLET_EXTENSION)
ret = bridge_u2f_to_wallet(_chal, _appid, klen, keyh);
#endif
if (ret != 0)
{
u2f_reset_response();
u2f_response_writeback(&up,1);
u2f_response_writeback((uint8_t *)&count,4);
memset(sig,0,sizeof(sig));
sig[0] = ret;
u2f_response_writeback(sig,72);
}
return U2F_SW_NO_ERROR;
}
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{
@ -37,7 +96,7 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
if (req->p1 == U2F_AUTHENTICATE_CHECK)
{
if (is_wallet_device((uint8_t *) &auth->kh, auth->khl)) // Pin requests
if (is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
{
rcode = U2F_SW_CONDITIONS_NOT_SATISFIED;
}
@ -45,23 +104,25 @@ int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
{
rcode = U2F_SW_WRONG_DATA;
}
printf1(TAG_WALLET,"Ignoring U2F request\n");
printf1(TAG_EXT,"Ignoring U2F request\n");
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
goto end;
}
else
{
if ( ! is_wallet_device((uint8_t *) &auth->kh, auth->khl)) // Pin requests
if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
{
rcode = U2F_SW_WRONG_PAYLOAD;
printf1(TAG_WALLET,"Ignoring U2F request\n");
printf1(TAG_EXT, "Ignoring U2F request\n");
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
goto end;
}
rcode = bridge_u2f_to_wallet(auth->chal, auth->app, auth->khl, (uint8_t*)&auth->kh);
rcode = bridge_u2f_to_extensions(auth->chal, auth->app, auth->khl, (uint8_t*)&auth->kh);
}
}
else if (req->ins == U2F_VERSION)
{
printf1(TAG_U2F, "U2F_VERSION\n");
printf1(TAG_EXT, "U2F_VERSION\n");
if (len)
{
rcode = U2F_SW_WRONG_LENGTH;

View File

@ -25,4 +25,6 @@
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len);
int bootloader_bridge(int klen, uint8_t * keyh);
#endif /* EXTENSIONS_H_ */

View File

@ -20,7 +20,7 @@
SOFTWARE.
*/
#include "wallet.h"
#include "app.h"
#include APP_CONFIG
#include "ctap.h"
#include "ctap_errors.h"
#include "crypto.h"
@ -52,26 +52,6 @@ typedef enum
#endif
typedef enum
{
WalletSign = 0x10,
WalletRegister = 0x11,
WalletPin = 0x12,
WalletReset= 0x13,
WalletVersion= 0x14,
WalletRng = 0x15,
} WalletOperation;
int is_wallet_device(uint8_t * kh, int len)
{
wallet_request * req = (wallet_request *) kh;
if (len < WALLET_MIN_LENGTH)
return 0;
return memcmp(req->tag, WALLET_TAG, sizeof(WALLET_TAG)-1) == 0;
}
// return 1 if hash is valid, 0 otherwise
int check_pinhash(uint8_t * pinAuth, uint8_t * msg, uint8_t len)
{
@ -200,8 +180,7 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
int reqlen = klen;
int i;
int8_t ret = 0;
uint32_t count;
uint8_t up = 1;
uint8_t sig[200];
uint8_t * args[5] = {NULL,NULL,NULL,NULL,NULL};
@ -222,21 +201,6 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
printf1(TAG_WALLET, "u2f2wallet [%d]: ",reqlen); dump_hex1(TAG_WALLET, msg_buf,reqlen);
if (req->operation == WalletRegister || req->operation == WalletSign)
{
count = ctap_atomic_count(0);
}
else
{
count = 10;
}
u2f_response_writeback(&up,1);
u2f_response_writeback((uint8_t *)&count,4);
u2f_response_writeback((uint8_t *)&ret,1);
#ifndef IS_BOOTLOADER
int offset = 0;
for (i = 0; i < MIN(5,req->numArgs); i++)
{
@ -348,8 +312,6 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
memmove(chksum, args[0] + lens[0] - 4, 4);
lens[0] -= 4;
/*printf("chksum: "); dump_hex1(TAG_WALLET, chksum, 4);*/
// perform integrity check
/*printf1(TAG_WALLET,"shasum on [%d]: ",lens[0]); dump_hex1(TAG_WALLET, args[0], lens[0]);*/
crypto_sha256_init();
@ -466,24 +428,8 @@ int16_t bridge_u2f_to_wallet(uint8_t * _chal, uint8_t * _appid, uint8_t klen, ui
ret = CTAP1_ERR_INVALID_COMMAND;
break;
}
#else
ret = bootloader_bridge(klen, keyh);
#endif
cleanup:
if (ret != 0)
{
u2f_reset_response();
u2f_response_writeback(&up,1);
u2f_response_writeback((uint8_t *)&count,4);
memset(sig,0,sizeof(sig));
sig[0] = ret;
u2f_response_writeback(sig,72);
}
else
{
/*u2f_response_writeback(sig,sizeof(sig));*/
}
return U2F_SW_NO_ERROR;
return ret;
}

View File

@ -92,10 +92,21 @@ typedef struct
}__attribute__((packed)) wallet_request;
int16_t bridge_u2f_to_wallet(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
typedef enum
{
WalletSign = 0x10,
WalletRegister = 0x11,
WalletPin = 0x12,
WalletReset= 0x13,
WalletVersion= 0x14,
WalletRng = 0x15,
} WalletOperation;
int16_t bridge_u2f_to_extensions(uint8_t * chal, uint8_t * appid, uint8_t klen, uint8_t * keyh);
// return 1 if request is a wallet request
int is_wallet_device(uint8_t * req, int len);
int is_extension_request(uint8_t * req, int len);
void wallet_init();

View File

@ -25,12 +25,17 @@
#include "log.h"
#include "util.h"
#if DEBUG_LEVEL > 0
static uint32_t LOGMASK = TAG_FILENO;
void set_logging_mask(uint32_t mask)
{
LOGMASK = mask;
}
struct logtag
{
uint32_t tagn;
@ -55,6 +60,8 @@ struct logtag tagtable[] = {
{TAG_TIME,"TIME"},
{TAG_WALLET,"WALLET"},
{TAG_STOR,"STOR"},
{TAG_BOOT,"BOOT"},
{TAG_BOOT,"EXT"},
};
@ -107,3 +114,4 @@ void LOG_HEX(uint32_t tag, uint8_t * data, int length)
set_logging_tag(tag);
dump_hex(data,length);
}
#endif

View File

@ -22,7 +22,7 @@
#ifndef _LOG_H
#define _LOG_H
#include "app.h"
#include APP_CONFIG
#include <stdint.h>
#ifndef DEBUG_LEVEL
@ -33,7 +33,7 @@
void LOG(uint32_t tag, const char * filename, int num, const char * fmt, ...);
void LOG_HEX(uint32_t tag, uint8_t * data, int length);
void set_logging_mask(uint32_t mask);
void set_logging_tag(uint32_t tag);
typedef enum
@ -55,12 +55,15 @@ typedef enum
TAG_WALLET = (1 << 14),
TAG_STOR = (1 << 15),
TAG_DUMP2 = (1 << 16),
TAG_BOOT = (1 << 17),
TAG_EXT = (1 << 17),
TAG_FILENO = (1<<31)
} LOG_TAG;
#if DEBUG_LEVEL == 1
#if DEBUG_LEVEL > 0
void set_logging_mask(uint32_t mask);
#define printf1(tag,fmt, ...) LOG(tag & ~(TAG_FILENO), NULL, 0, fmt, ##__VA_ARGS__)
#define printf2(tag,fmt, ...) LOG(tag | TAG_FILENO,__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define printf3(tag,fmt, ...) LOG(tag | TAG_FILENO,__FILE__, __LINE__, fmt, ##__VA_ARGS__)
@ -69,6 +72,7 @@ typedef enum
#else
#define set_logging_mask(mask)
#define printf1(fmt, ...)
#define printf2(fmt, ...)
#define printf3(fmt, ...)

View File

@ -30,7 +30,7 @@
#include "util.h"
#include "log.h"
#include "ctap.h"
#include "app.h"
#include APP_CONFIG
#if !defined(TEST)
@ -61,11 +61,15 @@ int main(int argc, char * argv[])
device_init();
printf1(TAG_GEN,"init device\n");
printf1(TAG_GEN,"init ctaphid\n");
ctaphid_init();
usbhid_init();
printf1(TAG_GEN,"init usb\n");
ctaphid_init();
printf1(TAG_GEN,"init ctaphid\n");
printf1(TAG_GEN,"init ctap\n");
ctap_init();
printf1(TAG_GEN,"init ctap\n");
memset(hidmsg,0,sizeof(hidmsg));
@ -74,9 +78,8 @@ int main(int argc, char * argv[])
while(1)
{
if (millis() - t1 > 100)
if (millis() - t1 > HEARTBEAT_PERIOD)
{
/*printf("heartbeat %ld\n", beat++);*/
heartbeat();
t1 = millis();
}

View File

@ -31,18 +31,18 @@
void ctap_init()
{
printf("STUB: ctap_init\n");
printf1(TAG_GEN,"STUB: ctap_init\n");
}
#endif
#if defined(STUB_CTAPHID)
void ctaphid_init()
{
printf("STUB: ctaphid_init\n");
printf1(TAG_GEN,"STUB: ctaphid_init\n");
}
void ctaphid_handle_packet(uint8_t * hidmsg)
{
printf("STUB: ctaphid_handle_packet\n");
printf1(TAG_GEN,"STUB: ctaphid_handle_packet\n");
}
void ctaphid_check_timeouts()
@ -57,7 +57,7 @@ void ctaphid_check_timeouts()
void ctap_reset_state()
{
printf("STUB: ctap_reset_state\n");
printf1(TAG_GEN,"STUB: ctap_reset_state\n");
}
void ctap_response_init(CTAP_RESPONSE * resp)
@ -66,12 +66,12 @@ void ctap_response_init(CTAP_RESPONSE * resp)
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
{
printf("STUB: u2f_request\n");
printf1(TAG_GEN,"STUB: u2f_request\n");
}
uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
{
printf("STUB: ctap_request\n");
printf1(TAG_GEN,"STUB: ctap_request\n");
return 0;
}
#endif

View File

@ -19,7 +19,7 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include "app.h"
#include APP_CONFIG
#ifdef TEST_POWER
/*

View File

@ -26,7 +26,7 @@
#include "log.h"
#include "device.h"
#include "wallet.h"
#include "app.h"
#include APP_CONFIG
// void u2f_response_writeback(uint8_t * buf, uint8_t len);
static int16_t u2f_register(struct u2f_register_request * req);
@ -34,6 +34,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response();
static CTAP_RESPONSE * _u2f_resp = NULL;
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
@ -43,7 +44,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
uint8_t byte;
_u2f_resp = resp;
u2f_set_writeback_buffer(resp);
if (req->cla != 0)
{
@ -137,6 +138,10 @@ void u2f_reset_response()
ctap_response_init(_u2f_resp);
}
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp)
{
_u2f_resp = resp;
}
static void dump_signature_der(uint8_t * sig)
{
@ -284,8 +289,6 @@ static int16_t u2f_register(struct u2f_register_request * req)
crypto_ecc256_load_attestation_key();
/*printf("check key handle size: %d vs %d\n", U2F_KEY_HANDLE_SIZE, sizeof(struct u2f_key_handle));*/
printf1(TAG_U2F, "sha256: "); dump_hex1(TAG_U2F,hash,32);
crypto_ecc256_sign(hash, 32, sig);

View File

@ -116,6 +116,7 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response();
void u2f_set_writeback_buffer(CTAP_RESPONSE * resp);
int16_t u2f_version();

View File

@ -30,5 +30,3 @@ void dump_hex(uint8_t * buf, int size)
}
printf("\n");
}

View File

@ -20,5 +20,20 @@
void printing_init();
// 0xRRGGBB
#define LED_INIT_VALUE 0x000800
#define LED_MAX_SCALER 30
#define LED_MIN_SCALER 1
// # of ms between each change in LED
#define HEARTBEAT_PERIOD 100
// Each LED channel will be multiplied by a integer between LED_MAX_SCALER
// and LED_MIN_SCALER to cause the slow pulse. E.g.
// #define LED_INIT_VALUE 0x301000
// #define LED_MAX_SCALER 30
// #define LED_MIN_SCALER 1
// #define HEARTBEAT_PERIOD 8
// Will pulse from 0x301000 to 0x903000 to 0x301000 ...
// Which will take ~8 * (30)*2 ms
#endif /* SRC_APP_H_ */

View File

@ -1,84 +1,58 @@
CC=arm-none-eabi-gcc
CP=arm-none-eabi-objcopy
SZ=arm-none-eabi-size
AR=arm-none-eabi-ar
ifndef DEBUG
DEBUG=0
endif
# ST related
SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c)
all:
$(MAKE) -f application.mk -j8 solo.hex DEBUG=$(DEBUG) EXTRA_DEFINES='-DFLASH_ROP=1'
# FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
all-hacker:
$(MAKE) -f application.mk -j8 solo.hex DEBUG=$(DEBUG) EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c
all-locked:
$(MAKE) -f application.mk -j8 solo.hex EXTRA_DEFINES='-DFLASH_ROP=2'
debugboot-app:
$(MAKE) -f application.mk -j8 solo.hex DEBUG=1 \
LDSCRIPT=linker/stm32l4xx_extra.ld EXTRA_DEFINES='-DAPPLICATION_START_PAGE=16 -DSOLO_HACKER'
OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o)
debugboot-boot:
$(MAKE) -f bootloader.mk -j8 bootloader.hex DEBUG=1 \
LDSCRIPT=linker/bootloader_stm32l4xx_extra.ld EXTRA_DEFINES='-DAPPLICATION_START_PAGE=16 -DSOLO_HACKER'
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c
boot:
$(MAKE) -f bootloader.mk -j8 bootloader.hex DEBUG=$(DEBUG)
SEARCH=-L../../tinycbor/lib
LDSCRIPT=stm32l432xx.ld
CFLAGS= $(INC)
TARGET=solo
HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
# Nucleo board
#CHIP=STM32L432xx
# Solo
CHIP=STM32L442xx
DEFINES = -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER
# DEFINES += -DTEST_SOLO_STM32 -DTEST -DTEST_FIFO=1
CFLAGS=$(INC) -c $(DEFINES) -Wall -fdata-sections -ffunction-sections $(HW)
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -ltinycbor
.PRECIOUS: %.o
all: $(TARGET).elf
$(SZ) $^
%.o: %.c
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
%.o: %.s
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
%.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@
%.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex
boot-hacker:
$(MAKE) -f bootloader.mk -j8 bootloader.hex EXTRA_DEFINES='-DSOLO_HACKER'
clean:
rm -f *.o src/*.o src/*.elf *.elf *.hex $(OBJ)
$(MAKE) -f application.mk clean
$(MAKE) -f bootloader.mk clean
flash: $(TARGET).hex
flash: solo.hex bootloader.hex
python merge_hex.py solo.hex bootloader.hex all.hex
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d $(TARGET).hex -rst
STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
flash_dfu: solo.hex bootloader.hex
python merge_hex.py solo.hex bootloader.hex all.hex
# STM32_Programmer_CLI -c port=usb1 -halt -e all --readunprotect
STM32_Programmer_CLI -c port=usb1 -halt -rdu -d all.hex
flashboot: solo.hex bootloader.hex
python merge_hex.py solo.hex bootloader.hex all.hex
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
# tell ST DFU to enter application
detach:
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
bootloader.hex:
echo "You need to build the bootloader first."
solo.hex:
echo "You need to build the application first."
cbor:
cd ../../tinycbor/ && make clean
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
LDFLAGS="$(LDFLAGS_LIB)" \
CFLAGS="$(CFLAGS)"
$(MAKE) -f application.mk -j8 cbor

View File

@ -0,0 +1,4 @@
# STM32L4xx Solo
Check out our [official documentation](https://solo.solokeys.io/building/)
for instructions on building and programming!

View File

@ -0,0 +1,84 @@
CC=arm-none-eabi-gcc
CP=arm-none-eabi-objcopy
SZ=arm-none-eabi-size
AR=arm-none-eabi-ar
# ST related
SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c)
# FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c ../../crypto/tiny-AES-c/aes.c
OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o)
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c
SEARCH=-L../../tinycbor/lib
ifndef LDSCRIPT
LDSCRIPT=linker/stm32l4xx.ld
endif
CFLAGS= $(INC)
TARGET=solo
HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
# Nucleo board
#CHIP=STM32L432xx
# Solo
CHIP=STM32L442xx
ifndef DEBUG
DEBUG=0
endif
DEFINES = -DDEBUG_LEVEL=$(DEBUG) -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER -DAPP_CONFIG=\"app.h\" $(EXTRA_DEFINES)
# DEFINES += -DTEST_SOLO_STM32 -DTEST -DTEST_FIFO=1
CFLAGS=$(INC) -c $(DEFINES) -Wall -fdata-sections -ffunction-sections $(HW) -g
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -ltinycbor
.PRECIOUS: %.o
all: $(TARGET).elf
$(SZ) $^
%.o: %.c
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
%.o: %.s
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
%.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@
%.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex
clean:
rm -f *.o src/*.o src/*.elf bootloader/*.o $(OBJ)
cbor:
cd ../../tinycbor/ && make clean
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
LDFLAGS="$(LDFLAGS_LIB)" \
CFLAGS="$(CFLAGS)"

View File

@ -0,0 +1,76 @@
CC=arm-none-eabi-gcc
CP=arm-none-eabi-objcopy
SZ=arm-none-eabi-size
AR=arm-none-eabi-ar
# ST related
SRC = bootloader/main.c bootloader/bootloader.c
SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
SRC += src/fifo.c src/crypto.c src/attestation.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(wildcard lib/*.c) $(wildcard lib/usbd/*.c)
# FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
# Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c
OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o)
INC = -Ibootloader/ -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c
ifndef LDSCRIPT
LDSCRIPT=linker/bootloader_stm32l4xx.ld
endif
CFLAGS= $(INC)
TARGET=bootloader
HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
# Nucleo board
#CHIP=STM32L432xx
# Solo
CHIP=STM32L442xx
ifndef DEBUG
DEBUG=0
endif
DEFINES = -DDEBUG_LEVEL=$(DEBUG) -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER -DAPP_CONFIG=\"bootloader.h\" $(EXTRA_DEFINES)
# DEFINES += -DTEST_SOLO_STM32 -DTEST -DTEST_FIFO=1
CFLAGS=$(INC) -c $(DEFINES) -Wall -fdata-sections -ffunction-sections $(HW) -g
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref
.PRECIOUS: %.o
all: $(TARGET).elf
$(SZ) $^
%.o: %.c
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
%.o: %.s
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
%.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@
%.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex
clean:
rm -f *.o src/*.o bootloader/*.o src/*.elf $(OBJ)

View File

@ -0,0 +1,231 @@
#include <stdint.h>
#include <stdlib.h>
#include APP_CONFIG
#include "uECC.h"
#include "u2f.h"
#include "device.h"
#include "flash.h"
#include "crypto.h"
#include "led.h"
#include "memory_layout.h"
#include "ctap_errors.h"
#include "log.h"
extern uint8_t REBOOT_FLAG;
typedef enum
{
BootWrite = 0x40,
BootDone = 0x41,
BootCheck = 0x42,
BootErase = 0x43,
BootVersion = 0x44,
BootReboot = 0x45,
BootBootloader = 0x46,
BootDisable = 0x47,
} BootOperation;
typedef struct {
uint8_t op;
uint8_t addr[3];
uint8_t tag[4];
uint8_t lenh;
uint8_t lenl;
uint8_t payload[255 - 10];
} __attribute__((packed)) BootloaderReq;
static void erase_application()
{
int page;
for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++)
{
flash_erase_page(page);
}
}
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
#define LAST_PAGE (APPLICATION_END_PAGE-1)
static void disable_bootloader()
{
uint8_t page[PAGE_SIZE];
memmove(page, (uint8_t*)LAST_ADDR, PAGE_SIZE);
memset(page+PAGE_SIZE -4, 0, 4);
flash_erase_page(LAST_PAGE);
flash_write(LAST_ADDR, page, PAGE_SIZE);
}
static void authorize_application()
{
// uint32_t zero = 0;
// uint32_t * ptr;
// ptr = (uint32_t *)AUTH_WORD_ADDR;
// flash_write((uint32_t)ptr, (uint8_t *)&zero, 4);
uint8_t page[PAGE_SIZE];
if (is_authorized_to_boot())
return;
memmove(page, (uint8_t*)LAST_ADDR, PAGE_SIZE);
memset(page+PAGE_SIZE -8, 0, 4);
flash_erase_page(LAST_PAGE);
flash_write(LAST_ADDR, page, PAGE_SIZE);
}
int is_authorized_to_boot()
{
uint32_t * auth = (uint32_t *)AUTH_WORD_ADDR;
return *auth == 0;
}
int is_bootloader_disabled()
{
uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4);
return *auth == 0;
}
int bootloader_bridge(int klen, uint8_t * keyh)
{
static int has_erased = 0;
int i;
BootloaderReq * req = (BootloaderReq * )keyh;
uint8_t hash[32];
uint8_t version = 1;
uint16_t len = (req->lenh << 8) | (req->lenl);
if (len > klen-10)
{
printf1(TAG_BOOT,"Invalid length %d / %d\r\n", len, klen-9);
return CTAP1_ERR_INVALID_LENGTH;
}
uint8_t * pubkey = (uint8_t*)"\x85\xaa\xce\xda\xd4\xb4\xd8\x0d\xf7\x0e\xe8\x91\x6d\x69\x8e\x00\x7a\x27\x40\x76\x93\x7a\x1d\x63\xb1\xcf\xe8\x22\xdd\x9f\xbc\x43\x3e\x34\x0a\x05\x9d\x8a\x9d\x72\xdc\xc2\x4b\x56\x9c\x64\x3d\xc1\x0d\x14\x64\x69\x52\x31\xd7\x54\xa3\xb6\x69\xa7\x6f\x6b\x81\x8d";
const struct uECC_Curve_t * curve = NULL;
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
uint32_t * ptr = (uint32_t *)addr;
switch(req->op){
case BootWrite:
printf1(TAG_BOOT, "BootWrite: %08lx\r\n",(uint32_t)ptr);
if ((uint32_t)ptr < APPLICATION_START_ADDR || (uint32_t)ptr >= APPLICATION_END_ADDR
|| ((uint32_t)ptr+len) > APPLICATION_END_ADDR)
{
printf1(TAG_BOOT,"Bound exceeded [%08lx, %08lx]\r\n",APPLICATION_START_ADDR,APPLICATION_END_ADDR);
return CTAP2_ERR_NOT_ALLOWED;
}
if (!has_erased || is_authorized_to_boot())
{
erase_application();
has_erased = 1;
}
if (is_authorized_to_boot())
{
printf2(TAG_ERR, "Error, boot check bypassed\n");
exit(1);
}
flash_write((uint32_t)ptr,req->payload, len);
break;
case BootDone:
printf1(TAG_BOOT, "BootDone: ");
#ifndef SOLO_HACKER
if (len != 64)
{
printf1(TAG_BOOT,"Invalid length for signature\r\n");
return CTAP1_ERR_INVALID_LENGTH;
}
dump_hex1(TAG_BOOT, req->payload, 32);
ptr = (uint32_t *)APPLICATION_START_ADDR;
crypto_sha256_init();
crypto_sha256_update((uint8_t*)ptr, APPLICATION_END_ADDR-APPLICATION_START_ADDR);
crypto_sha256_final(hash);
curve = uECC_secp256r1();
if (! uECC_verify(pubkey,
hash,
32,
req->payload,
curve))
{
return CTAP2_ERR_OPERATION_DENIED;
}
#endif
authorize_application();
REBOOT_FLAG = 1;
break;
case BootCheck:
return 0;
break;
case BootErase:
printf1(TAG_BOOT, "BootErase.\r\n");
erase_application();
return 0;
break;
case BootVersion:
has_erased = 0;
printf1(TAG_BOOT, "BootVersion.\r\n");
u2f_response_writeback(&version,1);
return 0;
break;
case BootReboot:
printf1(TAG_BOOT, "BootReboot.\r\n");
REBOOT_FLAG = 1;
break;
case BootDisable:
printf1(TAG_BOOT, "BootDisable %08lx.\r\n", *(uint32_t *)(AUTH_WORD_ADDR+4));
if (req->payload[0] == 0xcd && req->payload[1] == 0xde
&& req->payload[2] == 0xba && req->payload[3] == 0xaa)
{
disable_bootloader();
version = 0;
u2f_response_writeback(&version,1);
}
else
{
version = CTAP2_ERR_OPERATION_DENIED;
u2f_response_writeback(&version,1);
}
break;
#ifdef SOLO_HACKER
case BootBootloader:
printf1(TAG_BOOT, "BootBootloader.\r\n");
flash_option_bytes_init(1);
boot_st_bootloader();
break;
#endif
default:
return CTAP1_ERR_INVALID_COMMAND;
}
return 0;
}
void bootloader_heartbeat()
{
static int state = 0;
static uint32_t val = (LED_MAX_SCALER - LED_MIN_SCALER)/2;
uint8_t r = (LED_INIT_VALUE >> 16) & 0xff;
uint8_t g = (LED_INIT_VALUE >> 8) & 0xff;
uint8_t b = (LED_INIT_VALUE >> 0) & 0xff;
if (state)
{
val--;
}
else
{
val++;
}
if (val > LED_MAX_SCALER || val < LED_MIN_SCALER)
{
state = !state;
}
led_rgb(((val * g)<<8) | ((val*r) << 16) | (val*b));
}

View File

@ -0,0 +1,57 @@
#ifndef _APP_H_
#define _APP_H_
#include <stdint.h>
#define DEBUG_UART USART1
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL 0
#endif
#define NON_BLOCK_PRINTING 0
#define BOOT_TO_DFU 0
#define IS_BOOTLOADER 1
#define ENABLE_U2F_EXTENSIONS
// #define ENABLE_U2F
#define DISABLE_CTAPHID_PING
#define DISABLE_CTAPHID_WINK
#define DISABLE_CTAPHID_CBOR
// 0xRRGGBB
#define LED_INIT_VALUE 0x0a0300
#define LED_MAX_SCALER 40
#define LED_MIN_SCALER 1
// # of ms between each change in LED
#define HEARTBEAT_PERIOD 5
// Each LED channel will be multiplied by a integer between LED_MAX_SCALER
// and LED_MIN_SCALER to cause the slow pulse. E.g.
// #define LED_INIT_VALUE 0x301000
// #define LED_MAX_SCALER 30
// #define LED_MIN_SCALER 1
// #define HEARTBEAT_PERIOD 8
// Will pulse from 0x301000 to 0x903000 to 0x301000 ...
// Which will take ~8 * (30)*2 ms
// Button
#define SOLO_BUTTON_PORT GPIOA
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
#define SKIP_BUTTON_CHECK_FAST 1
void printing_init();
void hw_init(void);
// Trigger software reset
void device_reboot();
int is_authorized_to_boot();
int is_bootloader_disabled();
void bootloader_heartbeat();
#endif

View File

@ -0,0 +1,174 @@
/*
Copyright 2018 Conor Patrick
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "cbor.h"
#include "device.h"
#include "ctaphid.h"
//#include "bsp.h"
#include "util.h"
#include "log.h"
#include "ctap.h"
#include "app.h"
#include "memory_layout.h"
#include "stm32l4xx_ll_rcc.h"
#include "stm32l4xx.h"
uint8_t REBOOT_FLAG = 0;
void BOOT_boot(void)
{
typedef void (*pFunction)(void);
uint32_t *bootAddress = (uint32_t *)(APPLICATION_START_ADDR);
/* Set new vector table */
SCB->VTOR = APPLICATION_START_ADDR;
/* Read new SP and PC from vector table */
__set_MSP(bootAddress[0]);
((pFunction)bootAddress[1])();
}
int main(int argc, char * argv[])
{
uint8_t hidmsg[64];
uint32_t t1 = 0;
uint32_t stboot_time = 0;
uint32_t boot = 1;
set_logging_mask(
/*0*/
TAG_GEN|
// TAG_MC |
// TAG_GA |
// TAG_WALLET |
TAG_STOR |
// TAG_CP |
// TAG_CTAP|
// TAG_HID|
/*TAG_U2F|*/
// TAG_PARSE |
// TAG_TIME|
// TAG_DUMP|
// TAG_DUMP2|
TAG_BOOT|
TAG_EXT|
TAG_GREEN|
TAG_RED|
TAG_ERR
);
device_init();
printf1(TAG_GEN,"init device\n");
t1 = millis();
while(device_is_button_pressed())
{
if ((millis() - t1) > 2000)
{
boot = 0;
break;
}
}
#ifdef SOLO_HACKER
if (!is_bootloader_disabled())
{
stboot_time = millis();
if ( RCC->CSR & (1<<29) )// check if there was independent watchdog reset
{
RCC->CSR |= (1<<23); // clear reset flags
goto start_bootloader;
}
}
#endif
if (is_authorized_to_boot() && (boot || is_bootloader_disabled()))
{
BOOT_boot();
}
else
{
printf1(TAG_RED,"Not authorized to boot (%08x == %08lx)\r\n", AUTH_WORD_ADDR, *(uint32_t*)AUTH_WORD_ADDR);
}
start_bootloader:
usbhid_init();
printf1(TAG_GEN,"init usb\n");
ctaphid_init();
printf1(TAG_GEN,"init ctaphid\n");
memset(hidmsg,0,sizeof(hidmsg));
printf1(TAG_GEN,"recv'ing hid msg \n");
while(1)
{
if (millis() - t1 > HEARTBEAT_PERIOD)
{
bootloader_heartbeat();
t1 = millis();
}
device_manage();
if (usbhid_recv(hidmsg) > 0)
{
ctaphid_handle_packet(hidmsg);
memset(hidmsg, 0, sizeof(hidmsg));
}
else
{
}
ctaphid_check_timeouts();
if (REBOOT_FLAG)
{
delay(250);
device_reboot();
}
#ifdef SOLO_HACKER
// Boot ST bootloader if button is held for 2s
if (!device_is_button_pressed())
{
stboot_time = millis();
}
if ((millis() - stboot_time) > 5000)
{
boot_st_bootloader();
}
#endif
}
// Should never get here
usbhid_close();
printf1(TAG_GREEN, "done\n");
return 0;
}

View File

@ -0,0 +1,361 @@
/**
******************************************************************************
* @file stm32l4xx_ll_iwdg.h
* @author MCD Application Team
* @brief Header file of IWDG LL module.
******************************************************************************
* @attention
*
* <h2><center>&copy; COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32L4xx_LL_IWDG_H
#define __STM32L4xx_LL_IWDG_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx.h"
/** @addtogroup STM32L4xx_LL_Driver
* @{
*/
#if defined(IWDG)
/** @defgroup IWDG_LL IWDG
* @{
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @defgroup IWDG_LL_Private_Constants IWDG Private Constants
* @{
*/
#define LL_IWDG_KEY_RELOAD 0x0000AAAAU /*!< IWDG Reload Counter Enable */
#define LL_IWDG_KEY_ENABLE 0x0000CCCCU /*!< IWDG Peripheral Enable */
#define LL_IWDG_KEY_WR_ACCESS_ENABLE 0x00005555U /*!< IWDG KR Write Access Enable */
#define LL_IWDG_KEY_WR_ACCESS_DISABLE 0x00000000U /*!< IWDG KR Write Access Disable */
/**
* @}
*/
/* Private macros ------------------------------------------------------------*/
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/** @defgroup IWDG_LL_Exported_Constants IWDG Exported Constants
* @{
*/
/** @defgroup IWDG_LL_EC_GET_FLAG Get Flags Defines
* @brief Flags defines which can be used with LL_IWDG_ReadReg function
* @{
*/
#define LL_IWDG_SR_PVU IWDG_SR_PVU /*!< Watchdog prescaler value update */
#define LL_IWDG_SR_RVU IWDG_SR_RVU /*!< Watchdog counter reload value update */
#define LL_IWDG_SR_WVU IWDG_SR_WVU /*!< Watchdog counter window value update */
/**
* @}
*/
/** @defgroup IWDG_LL_EC_PRESCALER Prescaler Divider
* @{
*/
#define LL_IWDG_PRESCALER_4 0x00000000U /*!< Divider by 4 */
#define LL_IWDG_PRESCALER_8 (IWDG_PR_PR_0) /*!< Divider by 8 */
#define LL_IWDG_PRESCALER_16 (IWDG_PR_PR_1) /*!< Divider by 16 */
#define LL_IWDG_PRESCALER_32 (IWDG_PR_PR_1 | IWDG_PR_PR_0) /*!< Divider by 32 */
#define LL_IWDG_PRESCALER_64 (IWDG_PR_PR_2) /*!< Divider by 64 */
#define LL_IWDG_PRESCALER_128 (IWDG_PR_PR_2 | IWDG_PR_PR_0) /*!< Divider by 128 */
#define LL_IWDG_PRESCALER_256 (IWDG_PR_PR_2 | IWDG_PR_PR_1) /*!< Divider by 256 */
/**
* @}
*/
/**
* @}
*/
/* Exported macro ------------------------------------------------------------*/
/** @defgroup IWDG_LL_Exported_Macros IWDG Exported Macros
* @{
*/
/** @defgroup IWDG_LL_EM_WRITE_READ Common Write and read registers Macros
* @{
*/
/**
* @brief Write a value in IWDG register
* @param __INSTANCE__ IWDG Instance
* @param __REG__ Register to be written
* @param __VALUE__ Value to be written in the register
* @retval None
*/
#define LL_IWDG_WriteReg(__INSTANCE__, __REG__, __VALUE__) WRITE_REG(__INSTANCE__->__REG__, (__VALUE__))
/**
* @brief Read a value in IWDG register
* @param __INSTANCE__ IWDG Instance
* @param __REG__ Register to be read
* @retval Register value
*/
#define LL_IWDG_ReadReg(__INSTANCE__, __REG__) READ_REG(__INSTANCE__->__REG__)
/**
* @}
*/
/**
* @}
*/
/* Exported functions --------------------------------------------------------*/
/** @defgroup IWDG_LL_Exported_Functions IWDG Exported Functions
* @{
*/
/** @defgroup IWDG_LL_EF_Configuration Configuration
* @{
*/
/**
* @brief Start the Independent Watchdog
* @note Except if the hardware watchdog option is selected
* @rmtoll KR KEY LL_IWDG_Enable
* @param IWDGx IWDG Instance
* @retval None
*/
__STATIC_INLINE void LL_IWDG_Enable(IWDG_TypeDef *IWDGx)
{
WRITE_REG(IWDGx->KR, LL_IWDG_KEY_ENABLE);
}
/**
* @brief Reloads IWDG counter with value defined in the reload register
* @rmtoll KR KEY LL_IWDG_ReloadCounter
* @param IWDGx IWDG Instance
* @retval None
*/
__STATIC_INLINE void LL_IWDG_ReloadCounter(IWDG_TypeDef *IWDGx)
{
WRITE_REG(IWDGx->KR, LL_IWDG_KEY_RELOAD);
}
/**
* @brief Enable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers
* @rmtoll KR KEY LL_IWDG_EnableWriteAccess
* @param IWDGx IWDG Instance
* @retval None
*/
__STATIC_INLINE void LL_IWDG_EnableWriteAccess(IWDG_TypeDef *IWDGx)
{
WRITE_REG(IWDGx->KR, LL_IWDG_KEY_WR_ACCESS_ENABLE);
}
/**
* @brief Disable write access to IWDG_PR, IWDG_RLR and IWDG_WINR registers
* @rmtoll KR KEY LL_IWDG_DisableWriteAccess
* @param IWDGx IWDG Instance
* @retval None
*/
__STATIC_INLINE void LL_IWDG_DisableWriteAccess(IWDG_TypeDef *IWDGx)
{
WRITE_REG(IWDGx->KR, LL_IWDG_KEY_WR_ACCESS_DISABLE);
}
/**
* @brief Select the prescaler of the IWDG
* @rmtoll PR PR LL_IWDG_SetPrescaler
* @param IWDGx IWDG Instance
* @param Prescaler This parameter can be one of the following values:
* @arg @ref LL_IWDG_PRESCALER_4
* @arg @ref LL_IWDG_PRESCALER_8
* @arg @ref LL_IWDG_PRESCALER_16
* @arg @ref LL_IWDG_PRESCALER_32
* @arg @ref LL_IWDG_PRESCALER_64
* @arg @ref LL_IWDG_PRESCALER_128
* @arg @ref LL_IWDG_PRESCALER_256
* @retval None
*/
__STATIC_INLINE void LL_IWDG_SetPrescaler(IWDG_TypeDef *IWDGx, uint32_t Prescaler)
{
WRITE_REG(IWDGx->PR, IWDG_PR_PR & Prescaler);
}
/**
* @brief Get the selected prescaler of the IWDG
* @rmtoll PR PR LL_IWDG_GetPrescaler
* @param IWDGx IWDG Instance
* @retval Returned value can be one of the following values:
* @arg @ref LL_IWDG_PRESCALER_4
* @arg @ref LL_IWDG_PRESCALER_8
* @arg @ref LL_IWDG_PRESCALER_16
* @arg @ref LL_IWDG_PRESCALER_32
* @arg @ref LL_IWDG_PRESCALER_64
* @arg @ref LL_IWDG_PRESCALER_128
* @arg @ref LL_IWDG_PRESCALER_256
*/
__STATIC_INLINE uint32_t LL_IWDG_GetPrescaler(IWDG_TypeDef *IWDGx)
{
return (READ_REG(IWDGx->PR));
}
/**
* @brief Specify the IWDG down-counter reload value
* @rmtoll RLR RL LL_IWDG_SetReloadCounter
* @param IWDGx IWDG Instance
* @param Counter Value between Min_Data=0 and Max_Data=0x0FFF
* @retval None
*/
__STATIC_INLINE void LL_IWDG_SetReloadCounter(IWDG_TypeDef *IWDGx, uint32_t Counter)
{
WRITE_REG(IWDGx->RLR, IWDG_RLR_RL & Counter);
}
/**
* @brief Get the specified IWDG down-counter reload value
* @rmtoll RLR RL LL_IWDG_GetReloadCounter
* @param IWDGx IWDG Instance
* @retval Value between Min_Data=0 and Max_Data=0x0FFF
*/
__STATIC_INLINE uint32_t LL_IWDG_GetReloadCounter(IWDG_TypeDef *IWDGx)
{
return (READ_REG(IWDGx->RLR));
}
/**
* @brief Specify high limit of the window value to be compared to the down-counter.
* @rmtoll WINR WIN LL_IWDG_SetWindow
* @param IWDGx IWDG Instance
* @param Window Value between Min_Data=0 and Max_Data=0x0FFF
* @retval None
*/
__STATIC_INLINE void LL_IWDG_SetWindow(IWDG_TypeDef *IWDGx, uint32_t Window)
{
WRITE_REG(IWDGx->WINR, IWDG_WINR_WIN & Window);
}
/**
* @brief Get the high limit of the window value specified.
* @rmtoll WINR WIN LL_IWDG_GetWindow
* @param IWDGx IWDG Instance
* @retval Value between Min_Data=0 and Max_Data=0x0FFF
*/
__STATIC_INLINE uint32_t LL_IWDG_GetWindow(IWDG_TypeDef *IWDGx)
{
return (READ_REG(IWDGx->WINR));
}
/**
* @}
*/
/** @defgroup IWDG_LL_EF_FLAG_Management FLAG_Management
* @{
*/
/**
* @brief Check if flag Prescaler Value Update is set or not
* @rmtoll SR PVU LL_IWDG_IsActiveFlag_PVU
* @param IWDGx IWDG Instance
* @retval State of bit (1 or 0).
*/
__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_PVU(IWDG_TypeDef *IWDGx)
{
return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_PVU) == (IWDG_SR_PVU));
}
/**
* @brief Check if flag Reload Value Update is set or not
* @rmtoll SR RVU LL_IWDG_IsActiveFlag_RVU
* @param IWDGx IWDG Instance
* @retval State of bit (1 or 0).
*/
__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_RVU(IWDG_TypeDef *IWDGx)
{
return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_RVU) == (IWDG_SR_RVU));
}
/**
* @brief Check if flag Window Value Update is set or not
* @rmtoll SR WVU LL_IWDG_IsActiveFlag_WVU
* @param IWDGx IWDG Instance
* @retval State of bit (1 or 0).
*/
__STATIC_INLINE uint32_t LL_IWDG_IsActiveFlag_WVU(IWDG_TypeDef *IWDGx)
{
return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_WVU) == (IWDG_SR_WVU));
}
/**
* @brief Check if all flags Prescaler, Reload & Window Value Update are reset or not
* @rmtoll SR PVU LL_IWDG_IsReady\n
* SR WVU LL_IWDG_IsReady\n
* SR RVU LL_IWDG_IsReady
* @param IWDGx IWDG Instance
* @retval State of bits (1 or 0).
*/
__STATIC_INLINE uint32_t LL_IWDG_IsReady(IWDG_TypeDef *IWDGx)
{
return (uint32_t)(READ_BIT(IWDGx->SR, IWDG_SR_PVU | IWDG_SR_RVU | IWDG_SR_WVU) == 0U);
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif /* IWDG) */
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __STM32L4xx_LL_IWDG_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -50,7 +50,7 @@
#define USBD_VID 0x0483
#define USBD_PID 0x5710
#define USBD_PID 0xA2CA
#define USBD_LANGID_STRING 0x409
#define USBD_MANUFACTURER_STRING "Solo Keys"
#define USBD_PRODUCT_FS_STRING "Solo"

View File

@ -76,6 +76,7 @@
#include "usbd_core.h"
#include "fifo.h"
#include "log.h"
extern int usbhid_rdy ;
@ -252,7 +253,7 @@ static void dump_pma()
uint16_t val;
uint32_t offset = (uint32_t)(USB)->BTABLE;
printf("btable: %02lx\r\n",offset);
printf1(TAG_GREEN,"btable: %02lx\r\n",offset);
for (int i = 0; i < 2; i++)
{
@ -261,8 +262,8 @@ static void dump_pma()
uint16_t addr_rx = pma_ptr[i * 4 + 2];
uint16_t cnt_rx = pma_ptr[i * 4 + 3];
printf("EP%d addr_tx == %02x, count_tx == %02x\r\n", i, addr_tx,cnt_tx );
printf("EP%d addr_rx == %02x, count_rx == %02x\r\n", i, addr_rx,cnt_rx );
printf1(TAG_GREEN,"EP%d addr_tx == %02x, count_tx == %02x\r\n", i, addr_tx,cnt_tx );
printf1(TAG_GREEN,"EP%d addr_rx == %02x, count_rx == %02x\r\n", i, addr_rx,cnt_rx );
}
uint16_t ep1_tx = pma_ptr[1 * 4 + 0];
@ -270,9 +271,9 @@ static void dump_pma()
for (int i = 0; i < 32; i++)
{
val = pma_ptr[ep1_tx + i];
printf("%04x ",val);
printf1(TAG_GREEN,"%04x ",val);
}
printf("\r\n");
printf1(TAG_GREEN,"\r\n");
}
/**

View File

@ -41,7 +41,7 @@ _Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 238K /* Leave out 18 Kb for data */
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 20K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}

View File

@ -0,0 +1,201 @@
/*
*****************************************************************************
**
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -0,0 +1,202 @@
/*
*****************************************************************************
**
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
/* First 14 KB is bootloader */
FLASH (rx) : ORIGIN = 0x08005000, LENGTH = 198K-8 /* Leave out 38 Kb at end for data */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -0,0 +1,203 @@
/*
*****************************************************************************
**
** File : LinkerScript.ld
**
** Abstract : Linker script for STM32L432KCUx Device with
** 256KByte FLASH, 64KByte RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
** (c)Copyright Ac6.
** You may use this file as-is or modify it according to the needs of your
** project. Distribution of this file (unmodified or modified) is not
** permitted. Ac6 permit registered System Workbench for MCU users the
** rights to distribute the assembled, compiled & linked contents of this
** file as part of an application binary file, provided that it is built
** using the System Workbench for MCU toolchain.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x2000c000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Specify the memory areas */
MEMORY
{
/* First 32 KB is bootloader */
/*FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 238K-8 [> Leave out 38 Kb at end for data <]*/
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 186K-8 /* Leave out 38 Kb at end for data */
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(8);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(8);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(8);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(8);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(8);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(8);
} >FLASH
.ARM.extab :
{
. = ALIGN(8);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(8);
} >FLASH
.ARM : {
. = ALIGN(8);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(8);
} >FLASH
.preinit_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(8);
} >FLASH
.init_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(8);
} >FLASH
.fini_array :
{
. = ALIGN(8);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(8);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(8);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(8);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_sisram2 = LOADADDR(.sram2);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.sram2 :
{
. = ALIGN(8);
_ssram2 = .; /* create a global symbol at sram2 start */
*(.sram2)
*(.sram2*)
. = ALIGN(8);
_esram2 = .; /* create a global symbol at sram2 end */
} >SRAM2 AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

View File

@ -0,0 +1,33 @@
# Merges bootloader and application into 1 file for ST Solo
#
# Patches settings in flash so bootloader will boot application.
from intelhex import IntelHex
import sys
if len(sys.argv) < 3:
print('usage: %s <file1.hex> <file2.hex> [...] <output.hex>')
sys.exit(1)
def flash_addr(num):
return 0x08000000 + num * 2048
PAGES = 128
APPLICATION_END_PAGE = PAGES - 19
AUTH_WORD_ADDR = (flash_addr(APPLICATION_END_PAGE)-8)
first = IntelHex(sys.argv[1])
for i in range(2, len(sys.argv)-1):
first.merge(IntelHex( sys.argv[i] ), overlap = 'replace')
first[AUTH_WORD_ADDR] = 0
first[AUTH_WORD_ADDR+1] = 0
first[AUTH_WORD_ADDR+2] = 0
first[AUTH_WORD_ADDR+3] = 0
first[AUTH_WORD_ADDR+4] = 0xff
first[AUTH_WORD_ADDR+5] = 0xff
first[AUTH_WORD_ADDR+6] = 0xff
first[AUTH_WORD_ADDR+7] = 0xff
first.tofile(sys.argv[len(sys.argv)-1], format='hex')

View File

@ -0,0 +1 @@
intelhex

View File

@ -4,7 +4,9 @@
#define DEBUG_UART USART1
#define DEBUG_LEVEL 0
#ifndef DEBUG_LEVEL
#define DEBUG_LEVEL 0
#endif
#define NON_BLOCK_PRINTING 0
@ -27,7 +29,20 @@ void hw_init(void);
//#define TEST
//#define TEST_POWER
#define LED_INIT_VALUE 0x001000
// 0xRRGGBB
#define LED_INIT_VALUE 0x000800
#define LED_MAX_SCALER 30
#define LED_MIN_SCALER 1
// # of ms between each change in LED
#define HEARTBEAT_PERIOD 100
// Each LED channel will be multiplied by a integer between LED_MAX_SCALER
// and LED_MIN_SCALER to cause the slow pulse. E.g.
// #define LED_INIT_VALUE 0x301000
// #define LED_MAX_SCALER 30
// #define LED_MIN_SCALER 1
// #define HEARTBEAT_PERIOD 8
// Will pulse from 0x301000 to 0x903000 to 0x301000 ...
// Which will take ~8 * (30)*2 ms
// Button
#define SOLO_BUTTON_PORT GPIOA

View File

@ -18,7 +18,7 @@
#include "aes.h"
#include "ctap.h"
#include "device.h"
#include "app.h"
#include APP_CONFIG
#include "log.h"

View File

@ -7,7 +7,7 @@
#include "stm32l4xx_ll_usart.h"
#include "usbd_hid.h"
#include "app.h"
#include APP_CONFIG
#include "flash.h"
#include "rng.h"
#include "led.h"
@ -17,28 +17,12 @@
#include "log.h"
#include "ctaphid.h"
#include "ctap.h"
#include "crypto.h"
#include "memory_layout.h"
#include "stm32l4xx_ll_iwdg.h"
#define PAGE_SIZE 2048
#define PAGES 128
// Pages 119-127 are data
#define COUNTER2_PAGE (PAGES - 4)
#define COUNTER1_PAGE (PAGES - 3)
#define STATE2_PAGE (PAGES - 2)
#define STATE1_PAGE (PAGES - 1)
#define RK_NUM_PAGES 10
#define RK_START_PAGE (PAGES - 14)
#define RK_END_PAGE (PAGES - 14 + RK_NUM_PAGES)
#define APPLICATION_START_PAGE (0)
#define APPLICATION_START_ADDR flash_addr(APPLICATION_START_PAGE)
#define APPLICATION_END_PAGE ((PAGES - 19)) // 119 is NOT included in application
#define APPLICATION_END_ADDR (flash_addr(APPLICATION_END_PAGE)-4) // NOT included in application
#define AUTH_WORD_ADDR (flash_addr(APPLICATION_END_PAGE)-4)
uint32_t __90_ms = 0;
uint32_t __device_status = 0;
@ -86,6 +70,10 @@ void device_set_status(int status)
__device_status = status;
}
int device_is_button_pressed()
{
return IS_BUTTON_PRESSED();
}
void delay(uint32_t ms)
{
@ -93,26 +81,33 @@ void delay(uint32_t ms)
while ((millis() - time) < ms)
;
}
void device_reboot()
{
NVIC_SystemReset();
}
void device_init()
{
hw_init();
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
#ifndef IS_BOOTLOADER
#if BOOT_TO_DFU
flash_option_bytes_init(1);
#else
flash_option_bytes_init(0);
#endif
#endif
printf1(TAG_GEN,"hello solo\r\n");
}
void usb_init(void);
void usbhid_init()
{
printf1(TAG_GEN,"hello solo\r\n");
usb_init();
}
int usbhid_recv(uint8_t * msg)
{
if (fifo_hidmsg_size())
@ -156,7 +151,10 @@ void main_loop_delay()
void heartbeat()
{
static int state = 0;
static uint32_t val = (LED_INIT_VALUE >> 8) & 0xff;
static uint32_t val = (LED_MAX_SCALER - LED_MIN_SCALER)/2;
uint8_t r = (LED_INIT_VALUE >> 16) & 0xff;
uint8_t g = (LED_INIT_VALUE >> 8) & 0xff;
uint8_t b = (LED_INIT_VALUE >> 0) & 0xff;
int but = IS_BUTTON_PRESSED();
if (state)
@ -168,13 +166,13 @@ void heartbeat()
val++;
}
if (val > 30 || val < 1)
if (val > LED_MAX_SCALER || val < LED_MIN_SCALER)
{
state = !state;
}
if (but) led_rgb(val * 2);
if (but) led_rgb(((val * r)<<8) | ((val*b) << 16) | (val*g));
else
led_rgb((val << 16) | (val*2 << 8));
led_rgb(((val * g)<<8) | ((val*r) << 16) | (val*b));
}
void authenticator_read_state(AuthenticatorState * a)
@ -397,8 +395,10 @@ led_rgb(0x001040);
delay(50);
#if SKIP_BUTTON_CHECK_FAST
done:
return 1;
#endif
fail:
return 0;
@ -488,7 +488,37 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
}
}
void boot_st_bootloader()
{
__disable_irq();
__set_MSP(*((uint32_t *)0x1fff0000));
((void (*)(void)) (*((uint32_t *)0x1fff0004)))();
while(1)
;
}
void boot_solo_bootloader()
{
LL_IWDG_Enable(IWDG);
LL_IWDG_EnableWriteAccess(IWDG);
LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_4);
LL_IWDG_SetWindow(IWDG, 4095);
LL_IWDG_SetReloadCounter(IWDG, 2000); // ~0.25s
while (LL_IWDG_IsReady(IWDG) != 1)
{
}
LL_IWDG_ReloadCounter(IWDG);
}
void _Error_Handler(char *file, int line)
{

View File

@ -2,6 +2,7 @@
#include <stdint.h>
#include <stdio.h>
#include "fifo.h"
#include "log.h"
FIFO_CREATE(debug,4096,1)
@ -16,7 +17,7 @@ void fifo_test()
uint8_t data[10][100];
uint8_t verif[10][100];
printf("init\r\n");
printf1(TAG_GREEN,"init\r\n");
for (int i = 0; i < 10; i++)
{
memset(data[i],i,100);
@ -24,43 +25,43 @@ void fifo_test()
for (int i = 0; i < 10; i++)
{
printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
printf1(TAG_GREEN,"rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
ret = fifo_test_add(data[i]);
printf("%d\r\n",i);
printf1(TAG_GREEN,"%d\r\n",i);
if (ret != 0)
{
printf("fifo_test_add fail\r\n");
printf1(TAG_GREEN,"fifo_test_add fail\r\n");
goto fail;
}
}
for (int i = 0; i < 10; i++)
{
printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
printf1(TAG_GREEN,"rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
ret = fifo_test_take(verif[i]);
printf("%d\r\n",i );
printf1(TAG_GREEN,"%d\r\n",i );
if (ret != 0)
{
printf("fifo_test_take fail\r\n");
printf1(TAG_GREEN,"fifo_test_take fail\r\n");
goto fail;
}
if (memcmp(verif[i], data[i], 100) != 0)
{
printf("fifo_test_take result fail\r\n");
dump_hex(data[i],100);
dump_hex(verif[i],100);
printf1(TAG_GREEN,"fifo_test_take result fail\r\n");
dump_hex1(TAG_GREEN,data[i],100);
dump_hex1(TAG_GREEN,verif[i],100);
goto fail;
}
}
for (int i = 0; i < 10; i++)
{
printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
printf1(TAG_GREEN,"rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
ret = fifo_test_add(data[i]);
if (ret != 0)
{
printf("fifo_test_add 2 fail\r\n");
printf1(TAG_GREEN,"fifo_test_add 2 fail\r\n");
goto fail;
}
}
@ -68,7 +69,7 @@ void fifo_test()
ret = fifo_test_add(data[0]);
if (ret == 0)
{
printf("fifo_test_add should have failed\r\n");
printf1(TAG_GREEN,"fifo_test_add should have failed\r\n");
goto fail;
}
@ -76,17 +77,17 @@ void fifo_test()
for (int i = 0; i < 10; i++)
{
printf("rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
printf1(TAG_GREEN,"rhead: %d, whead: %d\r\n", fifo_test_rhead(), fifo_test_whead());
ret = fifo_test_take(verif[i]);
if (ret != 0)
{
printf("fifo_test_take fail\r\n");
printf1(TAG_GREEN,"fifo_test_take fail\r\n");
goto fail;
}
if (memcmp(verif[i], data[i], 100) != 0)
{
printf("fifo_test_take result fail\r\n");
printf1(TAG_GREEN,"fifo_test_take result fail\r\n");
goto fail;
}
}
@ -94,11 +95,11 @@ void fifo_test()
ret = fifo_test_take(verif[0]);
if (ret == 0)
{
printf("fifo_test_take should have failed\r\n");
printf1(TAG_GREEN,"fifo_test_take should have failed\r\n");
goto fail;
}
printf("test pass!\r\n");
printf1(TAG_GREEN,"test pass!\r\n");
return ;
fail:
while(1)

View File

@ -1,7 +1,7 @@
#ifndef _FIFO_H_
#define _FIFO_H_
#include "app.h"
#include APP_CONFIG
#ifndef TEST_FIFO
#define TEST_FIFO 0

View File

@ -3,7 +3,7 @@
#include <string.h>
#include "stm32l4xx.h"
#include "app.h"
#include APP_CONFIG
#include "flash.h"
#include "log.h"
#include "device.h"
@ -20,8 +20,13 @@ static void flash_unlock()
// Locks flash and turns off DFU
void flash_option_bytes_init(int boot_from_dfu)
{
#if DEBUG_LEVEL
#ifndef FLASH_ROP
#define FLASH_ROP 0
#endif
#if FLASH_ROP == 0
uint32_t val = 0xfffff8aa;
#elif FLASH_ROP == 2
uint32_t val = 0xfffff8cc;
#else
uint32_t val = 0xfffff8b9;
#endif
@ -139,6 +144,39 @@ void flash_write(uint32_t addr, uint8_t * data, size_t sz)
}
// NOT YET working
void flash_write_fast(uint32_t addr, uint32_t * data)
{
__disable_irq();
while (FLASH->SR & (1<<16))
;
FLASH->SR = FLASH->SR;
// Select fast program action
FLASH->CR |= (1<<18);
int i;
for(i = 0; i < 64; i++)
{
*(volatile uint32_t*)addr = (*data);
addr+=4;
data++;
}
while (FLASH->SR & (1<<16))
;
if(FLASH->SR & (1<<1))
{
printf2(TAG_ERR,"program NOT successful %lx\r\n", FLASH->SR);
}
FLASH->SR = (1<<0);
FLASH->CR &= ~(1<<18);
__enable_irq();
}
void flash_lock()
{
FLASH->CR |= (1U<<31);

View File

@ -4,6 +4,7 @@
void flash_erase_page(uint8_t page);
void flash_write_dword(uint32_t addr, uint64_t data);
void flash_write(uint32_t addr, uint8_t * data, size_t sz);
void flash_write_fast(uint32_t addr, uint32_t * data);
void flash_option_bytes_init(int boot_from_dfu);
#define FLASH_PAGE_SIZE 2048

View File

@ -21,6 +21,7 @@
#include "usbd_desc.h"
#include "usbd_hid.h"
#include "device.h"
#include APP_CONFIG
/* USER CODE BEGIN Includes */
@ -38,7 +39,6 @@ static void MX_USART1_UART_Init(void);
static void MX_TIM2_Init(void);
static void MX_TIM6_Init(void);
static void MX_RNG_Init(void);
static void usb_init();
#define Error_Handler() _Error_Handler(__FILE__,__LINE__)
void _Error_Handler(char *file, int line);
@ -46,79 +46,54 @@ void _Error_Handler(char *file, int line);
void hw_init(void)
{
/* USER CODE BEGIN 1 */
#ifdef IS_BOOTLOADER
SCB->VTOR = FLASH_BASE;
#else
#endif
LL_Init();
/* USER CODE END 1 */
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
LL_Init();
SystemClock_Config(); // TODO bootloader should not change clk freq.
// enable power clock
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
MX_GPIO_Init();
MX_TIM2_Init(); // PWM for LEDs
// enable USB power
SET_BIT(PWR->CR2, PWR_CR2_USV);
MX_TIM6_Init(); // ~1 ms timer
// Enable USB Clock
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_USBFSEN);
#if DEBUG_LEVEL > 0
MX_USART1_UART_Init();// debug uart
#endif
MX_RNG_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM6_Init();
#ifndef TEST_SOLO_STM32
MX_USART1_UART_Init();
MX_RNG_Init();
#endif
TIM6->SR = 0;
__enable_irq();
NVIC_EnableIRQ(TIM6_IRQn);
#ifndef TEST_SOLO_STM32
usb_init();
#endif
TIM6->SR = 0;
__enable_irq();
NVIC_EnableIRQ(TIM6_IRQn);
}
static void LL_Init(void)
{
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
NVIC_SetPriorityGrouping(4);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
NVIC_SetPriorityGrouping(4);
/* System interrupt init*/
/* MemoryManagement_IRQn interrupt configuration */
NVIC_SetPriority(MemoryManagement_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* BusFault_IRQn interrupt configuration */
NVIC_SetPriority(BusFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* UsageFault_IRQn interrupt configuration */
NVIC_SetPriority(UsageFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* SVCall_IRQn interrupt configuration */
NVIC_SetPriority(SVCall_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* DebugMonitor_IRQn interrupt configuration */
NVIC_SetPriority(DebugMonitor_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* PendSV_IRQn interrupt configuration */
NVIC_SetPriority(PendSV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* System interrupt init*/
/* MemoryManagement_IRQn interrupt configuration */
NVIC_SetPriority(MemoryManagement_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* BusFault_IRQn interrupt configuration */
NVIC_SetPriority(BusFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* UsageFault_IRQn interrupt configuration */
NVIC_SetPriority(UsageFault_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* SVCall_IRQn interrupt configuration */
NVIC_SetPriority(SVCall_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* DebugMonitor_IRQn interrupt configuration */
NVIC_SetPriority(DebugMonitor_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* PendSV_IRQn interrupt configuration */
NVIC_SetPriority(PendSV_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
/* SysTick_IRQn interrupt configuration */
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
@ -202,8 +177,14 @@ void SystemClock_Config(void)
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
}
static void usb_init()
void usb_init()
{
// enable USB power
SET_BIT(PWR->CR2, PWR_CR2_USV);
// Enable USB Clock
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_USBFSEN);
USBD_Init(&Solo_USBD_Device, &Solo_Desc, 0);
USBD_RegisterClass(&Solo_USBD_Device, &USBD_HID);
USBD_Start(&Solo_USBD_Device);

View File

@ -20,7 +20,7 @@
#include "usbd_hid.h"
/*#include "usbd_hid.h"*/
#include "app.h"
#include APP_CONFIG
#include "flash.h"
#include "rng.h"
#include "led.h"

View File

@ -0,0 +1,36 @@
#ifndef _MEMORY_LAYOUT_H_
#define _MEMORY_LAYOUT_H_
#define PAGE_SIZE 2048
#define PAGES 128
// Pages 119-127 are data
// Location of counter page and it's backup page
// The flash is wear leveled and counter should be fault tolerant
#define COUNTER2_PAGE (PAGES - 4)
#define COUNTER1_PAGE (PAGES - 3)
// State of FIDO2 application
#define STATE2_PAGE (PAGES - 2)
#define STATE1_PAGE (PAGES - 1)
// Storage of FIDO2 resident keys
#define RK_NUM_PAGES 10
#define RK_START_PAGE (PAGES - 14)
#define RK_END_PAGE (PAGES - 14 + RK_NUM_PAGES)
// Start of application code
#ifndef APPLICATION_START_PAGE
#define APPLICATION_START_PAGE (10)
#endif
#define APPLICATION_START_ADDR (0x08000000 + ((APPLICATION_START_PAGE)*PAGE_SIZE))
// End of application code. Leave some extra room for future data storage.
// NOT included in application
#define APPLICATION_END_PAGE ((PAGES - 19))
#define APPLICATION_END_ADDR ((0x08000000 + ((APPLICATION_END_PAGE)*PAGE_SIZE))-8)
// Bootloader state.
#define AUTH_WORD_ADDR (APPLICATION_END_ADDR)
#endif

View File

@ -1,6 +1,6 @@
#include "stm32l4xx_ll_usart.h"
#include "app.h"
#include APP_CONFIG
#include "fifo.h"
#if DEBUG_LEVEL>0

View File

@ -219,12 +219,6 @@ void SystemInit(void)
/* Disable all interrupts */
RCC->CIER = 0x00000000U;
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
/**

View File

@ -74,7 +74,6 @@ class Tester():
#cmd,resp = self.recv_raw()
def send_data(self, cmd, data):
#print('<<', hexlify(data))
if type(data) != type(b''):
data = struct.pack('%dB' % len(data), *[ord(x) for x in data])
with Timeout(1.0) as event:

1
tools/firmware.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -16,6 +16,8 @@ from ecdsa import SigningKey, NIST256p
import socket,json,base64,ssl,array,binascii
from sign_firmware import *
httpport = 8080
udpport = 8111
@ -41,16 +43,7 @@ if __name__ == '__main__':
print(e)
def to_websafe(data):
data = data.replace('+','-')
data = data.replace('/','_')
data = data.replace('=','')
return data
def from_websafe(data):
data = data.replace('-','+')
data = data.replace('_','/')
return data + '=='[:(3*len(data)) % 4]
def write(data):
msg = from_websafe(data)
@ -81,38 +74,6 @@ def read():
msg = to_websafe(pkt)
return msg
def get_firmware_object():
sk = SigningKey.from_pem(open("signing_key.pem").read())
h = open(HEX_FILE,'r').read()
h = base64.b64encode(h.encode())
h = to_websafe(h.decode())
num_pages = 64
START = 0x4000
END = 2048 * (num_pages - 3) - 4
ih = IntelHex(HEX_FILE)
segs = ih.segments()
arr = ih.tobinarray(start = START, size = END-START)
im_size = END-START
print('im_size: ', im_size)
print('firmware_size: ', len(arr))
byts = (arr).tobytes() if hasattr(arr,'tobytes') else (arr).tostring()
sig = sha256(byts)
print('hash', binascii.hexlify(sig))
sig = sk.sign_digest(sig)
print('sig', binascii.hexlify(sig))
sig = base64.b64encode(sig)
sig = to_websafe(sig.decode())
#msg = {'data': read()}
msg = {'firmware': h, 'signature':sig}
return msg
class UDPBridge(BaseHTTPRequestHandler):
def end_headers (self):
self.send_header('Access-Control-Allow-Origin', '*')
@ -147,7 +108,7 @@ class UDPBridge(BaseHTTPRequestHandler):
self.send_response(200)
self.send_header('Content-type','text/json')
msg = get_firmware_object()
msg = get_firmware_object("signing_key.pem",HEX_FILE)
self.end_headers()
@ -162,7 +123,7 @@ try:
certfile='../web/localhost.crt', server_side=True)
print('Saving signed firmware to firmware.json')
msg = get_firmware_object()
msg = get_firmware_object("signing_key.pem",HEX_FILE)
wfile = open('firmware.json','wb+')
wfile.write(json.dumps(msg).encode())
wfile.close()

327
tools/programmer.py Normal file
View File

@ -0,0 +1,327 @@
# Programs solo using the Solo bootloader
# Requires python-fido2, intelhex
import sys,os,time,struct,argparse
import array,struct,socket,json,base64
import tempfile
from binascii import hexlify
from fido2.hid import CtapHidDevice, CTAPHID
from fido2.client import Fido2Client, ClientError
from fido2.ctap import CtapError
from fido2.ctap1 import CTAP1, ApduError
from fido2.utils import Timeout
from intelhex import IntelHex
from sign_firmware import *
class SoloBootloader:
write = 0x40
done = 0x41
check = 0x42
erase = 0x43
version = 0x44
reboot = 0x45
st_dfu = 0x46
disable = 0x47
HIDCommandBoot = 0x50
HIDCommandEnterBoot = 0x51
HIDCommandEnterSTBoot = 0x52
HIDCommandRNG = 0x60
TAG = b'\x8C\x27\x90\xf6'
class Programmer():
def __init__(self,):
self.origin = 'https://example.org'
self.exchange = self.exchange_hid
self.reboot = True
def use_u2f(self,):
self.exchange = self.exchange_u2f
def use_hid(self,):
self.exchange = self.exchange_hid
def set_reboot(self,val):
""" option to reboot after programming """
self.reboot = val
def reboot(self,val):
""" option to reboot after programming """
try:
self.exchange(SoloBootloader.reboot)
except OSError:
pass
def find_device(self,):
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
raise RuntimeError('No FIDO device found')
self.dev = dev
self.ctap1 = CTAP1(dev)
if self.exchange == self.exchange_hid:
self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11')
@staticmethod
def format_request(cmd,addr = 0,data = b'A'*16):
arr = b'\x00'*9
addr = struct.pack('<L', addr)
cmd = struct.pack('B', cmd)
length = struct.pack('>H', len(data))
return cmd + addr[:3] + SoloBootloader.TAG + length + data
def send_only_hid(self, cmd, data):
if type(data) != type(b''):
data = struct.pack('%dB' % len(data), *[ord(x) for x in data])
self.dev._dev.InternalSend(0x80 | cmd, bytearray(data))
def send_data_hid(self, cmd, data):
if type(data) != type(b''):
data = struct.pack('%dB' % len(data), *[ord(x) for x in data])
with Timeout(1.0) as event:
return self.dev.call(cmd, data,event)
def exchange_hid(self,cmd,addr=0,data=b'A'*16):
req = Programmer.format_request(cmd,addr,data)
data = self.send_data_hid(SoloBootloader.HIDCommandBoot, req)
ret = data[0]
if ret != CtapError.ERR.SUCCESS:
raise RuntimeError('Device returned non-success code %02x' % ret)
return data[1:]
def exchange_u2f(self,cmd,addr=0,data=b'A'*16):
appid = b'A'*32
chal = b'B'*32
req = Programmer.format_request(cmd,addr,data)
res = self.ctap1.authenticate(chal,appid, req)
ret = res.signature[0]
if ret != CtapError.ERR.SUCCESS:
raise RuntimeError('Device returned non-success code %02x' % ret)
return res.signature[1:]
def version(self,):
data = self.exchange(SoloBootloader.version)
return data[0]
def write_flash(self,addr,data):
self.exchange(SoloBootloader.write,addr,data)
def get_rng(self,num=0):
ret = self.send_data_hid(SoloBootloader.HIDCommandRNG,struct.pack('B', num))
return ret
def verify_flash(self,sig):
"""
Tells device to check signature against application. If it passes,
the application will boot.
Exception raises if signature fails.
"""
self.exchange(SoloBootloader.done,0,sig)
def enter_solo_bootloader(self,):
"""
If solo is configured as solo hacker or something similar,
this command will tell the token to boot directly to the bootloader
so it can be reprogrammed
"""
if self.exchange != self.exchange_hid:
self.send_data_hid(CTAPHID.INIT, '\x11\x11\x11\x11\x11\x11\x11\x11')
self.send_data_hid(SoloBootloader.HIDCommandEnterBoot, '')
def is_solo_bootloader(self,):
try:
p.version()
return True
except CtapError as e:
if e.code == CtapError.ERR.INVALID_COMMAND:
pass
else:
raise (e)
return False
def enter_st_dfu(self,):
"""
If solo is configured as solo hacker or something similar,
this command will tell the token to boot directly to the st DFU
so it can be reprogrammed. Warning, you could brick your device.
"""
soloboot = self.is_solo_bootloader()
if soloboot or self.exchange == self.exchange_u2f:
req = Programmer.format_request(SoloBootloader.st_dfu)
self.send_only_hid(SoloBootloader.HIDCommandBoot, req)
else:
self.send_only_hid(SoloBootloader.HIDCommandEnterSTBoot, '')
def disable_solo_bootloader(self,):
"""
Disables the Solo bootloader. Only do this if you want to void the possibility
of any updates.
If you've started from a solo hacker, make you you've programmed a final/production build!
"""
ret = self.exchange(SoloBootloader.disable, 0, b'\xcd\xde\xba\xaa') # magic number
if ret[0] != CtapError.ERR.SUCCESS:
print('Failed to disable bootloader')
return False
time.sleep(0.1)
self.exchange(SoloBootloader.reboot)
return True
def program_file(self,name):
if name.lower().endswith('.json'):
data = json.loads(open(name,'r').read())
fw = base64.b64decode(from_websafe(data['firmware']).encode())
sig = base64.b64decode(from_websafe(data['signature']).encode())
ih = IntelHex()
tmp = tempfile.NamedTemporaryFile(delete=False)
tmp.write(fw)
tmp.seek(0)
tmp.close()
ih.fromfile(tmp.name, format='hex')
else:
if not name.lower().endswith('.hex'):
print('Warning, assuming "%s" is an Intel Hex file.' % name)
sig = None
ih = IntelHex()
ih.fromfile(name, format='hex')
if self.exchange == self.exchange_hid:
chunk = 2048
else:
chunk = 240
seg = ih.segments()[0]
size = seg[1] - seg[0]
total = 0
t1 = time.time()*1000
print('erasing...')
for i in range(seg[0], seg[1], chunk):
s = i
e = min(i+chunk,seg[1])
data = ih.tobinarray(start=i,size = e-s)
self.write_flash(i,data)
total += chunk
progress = total/float(size)*100
sys.stdout.write('downloading %.2f%%...\r' % progress)
sys.stdout.write('downloaded 100% \r\n')
t2 = time.time()*1000
print('time: %.2f s' % ((t2-t1)/1000.0))
print('Verifying...')
if self.reboot:
if sig is not None:
self.verify_flash(sig)
else:
self.verify_flash(b'A'*64)
def attempt_to_find_device(p):
found = False
for i in range(0,5):
try:
p.find_device()
found = True
break
except RuntimeError:
time.sleep(0.2)
return found
def attempt_to_boot_bootloader(p):
print('Bootloader not active. Attempting to boot into bootloader mode...')
try:
p.enter_solo_bootloader()
except OSError:
pass
except CtapError as e:
if e.code == CtapError.ERR.INVALID_COMMAND:
print('Solo appears to not be a solo hacker. Try holding down the button for 2 while you plug token in.')
sys.exit(1)
else:
raise(e)
print('Solo rebooted. Reconnecting...')
time.sleep(.500)
if not attempt_to_find_device(p):
print('Failed to reconnect!')
sys.exit(1)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("[firmware]", nargs='?', default='', help = 'firmware file. Either a JSON or hex file. JSON file contains signature while hex does not.')
parser.add_argument("--use-hid", action="store_true", help = 'Programs using custom HID command (default). Quicker than using U2F authenticate which is what a browser has to use.')
parser.add_argument("--use-u2f", action="store_true", help = 'Programs using U2F authenticate. This is what a web application will use.')
parser.add_argument("--no-reset", action="store_true", help = 'Don\'t reset after writing firmware. Stay in bootloader mode.')
parser.add_argument("--reset-only", action="store_true", help = 'Don\'t write anything, try to boot without a signature.')
parser.add_argument("--reboot", action="store_true", help = 'Tell bootloader to reboot.')
parser.add_argument("--enter-bootloader", action="store_true", help = 'Don\'t write anything, try to enter bootloader. Typically only supported by Solo Hacker builds.')
parser.add_argument("--st-dfu", action="store_true", help = 'Don\'t write anything, try to enter ST DFU. Warning, you could brick your Solo if you overwrite everything. You should reprogram the option bytes just to be safe (boot to Solo bootloader first, then run this command).')
parser.add_argument("--disable", action="store_true", help = 'Disable the Solo bootloader. Cannot be undone. No future updates can be applied.')
parser.add_argument("--rng", action="store_true", help = 'Continuously dump random numbers generated from Solo.')
args = parser.parse_args()
print()
p = Programmer()
p.find_device()
if args.use_u2f:
p.use_u2f()
if args.no_reset:
p.set_reboot(False)
if args.enter_bootloader:
attempt_to_boot_bootloader(p)
sys.exit(0)
if args.reboot:
p.reboot()
sys.exit(0)
if args.rng:
while True:
r = p.get_rng(255)
sys.stdout.buffer.write(r)
sys.exit(0)
if args.st_dfu:
print('Sending command to boot into ST DFU...')
p.enter_st_dfu()
sys.exit(0)
if args.disable:
p.disable_solo_bootloader()
sys.exit(0)
try:
p.version()
except CtapError as e:
if e.code == CtapError.ERR.INVALID_COMMAND:
attempt_to_boot_bootloader(p)
else:
raise(e)
except ApduError:
attempt_to_boot_bootloader(p)
if not args.reset_only:
fw = args.__dict__['[firmware]']
if fw == '':
print('Need to supply firmware filename.')
args.print_help()
sys.exit(1)
p.program_file(fw)
else:
p.exchange(SoloBootloader.done,0,b'A'*64)

62
tools/sign_firmware.py Normal file
View File

@ -0,0 +1,62 @@
import sys
import json,base64,array,binascii
from hashlib import sha256
from ecdsa import SigningKey, NIST256p
from intelhex import IntelHex
def to_websafe(data):
data = data.replace('+','-')
data = data.replace('/','_')
data = data.replace('=','')
return data
def from_websafe(data):
data = data.replace('-','+')
data = data.replace('_','/')
return data + '=='[:(3*len(data)) % 4]
def get_firmware_object(sk_name, hex_file):
sk = SigningKey.from_pem(open(sk_name).read())
fw = open(hex_file,'r').read()
fw = base64.b64encode(fw.encode())
fw = to_websafe(fw.decode())
# start of firmware and the size of the flash region allocated for it.
# TODO put this somewhere else.
START = ih.segments()[0][0]
END = ((0x08000000 + ((128-19)*2024))-8)
ih = IntelHex(hex_file)
segs = ih.segments()
arr = ih.tobinarray(start = START, size = END-START)
im_size = END-START
print('im_size: ', im_size)
print('firmware_size: ', len(arr))
byts = (arr).tobytes() if hasattr(arr,'tobytes') else (arr).tostring()
h = sha256()
h.update(byts)
sig = binascii.unhexlify(h.hexdigest())
print('hash', binascii.hexlify(sig))
sig = sk.sign_digest(sig)
print('sig', binascii.hexlify(sig))
sig = base64.b64encode(sig)
sig = to_websafe(sig.decode())
#msg = {'data': read()}
msg = {'firmware': fw, 'signature':sig}
return msg
if __name__ == '__main__':
if len(sys.argv) != 4:
print('usage: %s <signing-key.pem> <app.hex> <output.json>' % sys.argv[0])
msg = get_firmware_object(sys.argv[1],sys.argv[2])
print('Saving signed firmware to firmware.json')
wfile = open(sys.argv[3],'wb+')
wfile.write(json.dumps(msg).encode())
wfile.close()

View File

@ -174,12 +174,12 @@ function toUTF8Array(str) {
var charcode = str.charCodeAt(i);
if (charcode < 0x80) utf8.push(charcode);
else if (charcode < 0x800) {
utf8.push(0xc0 | (charcode >> 6),
utf8.push(0xc0 | (charcode >> 6),
0x80 | (charcode & 0x3f));
}
else if (charcode < 0xd800 || charcode >= 0xe000) {
utf8.push(0xe0 | (charcode >> 12),
0x80 | ((charcode>>6) & 0x3f),
utf8.push(0xe0 | (charcode >> 12),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
// surrogate pair
@ -190,9 +190,9 @@ function toUTF8Array(str) {
// 20 bits of 0x0-0xFFFFF into two halves
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
| (str.charCodeAt(i) & 0x3ff));
utf8.push(0xf0 | (charcode >>18),
0x80 | ((charcode>>12) & 0x3f),
0x80 | ((charcode>>6) & 0x3f),
utf8.push(0xf0 | (charcode >>18),
0x80 | ((charcode>>12) & 0x3f),
0x80 | ((charcode>>6) & 0x3f),
0x80 | (charcode & 0x3f));
}
}
@ -262,6 +262,7 @@ var CMD = {
boot_done: 0x41,
boot_check: 0x42,
boot_erase: 0x43,
boot_version: 0x44,
};
var PIN = {
@ -386,9 +387,7 @@ function send_msg_u2f(data, func, timeout) {
appId: appid
};
window.u2f.sign(appid,chal,[key], function(res){
var d2 = new Date();
t2 = d2.getTime();
if (!res.signatureData)
@ -410,9 +409,6 @@ if (DEVELOPMENT) {
function formatBootRequest(cmd, addr, data) {
var array = new Uint8Array(255);
if (addr == undefined)
addr = 0x8000;
data = data || new Uint8Array(1);
if (data.length > (255 - 9)) {
@ -429,9 +425,10 @@ function formatBootRequest(cmd, addr, data) {
array[6] = 0x90;
array[7] = 0xf6;
array[8] = data.length & 0xff;
array[8] = 0;
array[9] = data.length & 0xff;
var offset = 9;
var offset = 10;
var i;
for (i = 0; i < data.length; i++){
@ -1078,14 +1075,18 @@ async function handleFirmware(files)
var blocks = MemoryMap.fromHex(resp.firmware);
var addresses = blocks.keys();
console.log(blocks);
console.log(addresses);
var addr = addresses.next();
var chunk_size = 244;
var chunk_size = 240;
while(!addr.done) {
var data = blocks.get(addr.value);
var i;
for (i = 0; i < data.length; i += chunk_size) {
var chunk = data.slice(i,i+chunk_size);
console.log('addr ',addr.value + i);
p = await dev.bootloader_write(addr.value + i, chunk);
TEST(p.status == 'CTAP1_SUCCESS', 'Device wrote data');
var progress = (((i/data.length) * 100 * 100) | 0)/100;
document.getElementById('progress').textContent = ''+progress+' %';
@ -1425,7 +1426,8 @@ async function run_tests() {
async function test_bootloader()
{
var addr = 0x4000;
var start = 0x8000;
var size = 186 * 1024 - 8;
var num_pages = 64;
var p = await dev.is_bootloader();
@ -1436,63 +1438,46 @@ async function run_tests() {
p = await dev.bootloader_write(0, randdata);
TEST(p.status == 'CTAP2_ERR_NOT_ALLOWED', 'Denies accessing invalid address');
p = await dev.bootloader_write(addr-4, randdata);
p = await dev.bootloader_write(start-4, randdata);
TEST(p.status == 'CTAP2_ERR_NOT_ALLOWED', 'Denies accessing invalid address');
p = await dev.bootloader_write(2048 * (num_pages-3)-4, randdata);
p = await dev.bootloader_write(start, randdata);
TEST(p.status == 'CTAP1_SUCCESS', 'Allows write to beginning');
p = await dev.bootloader_write(start + size-16, randdata);
TEST(p.status == 'CTAP1_SUCCESS', 'Allows write to end');
p = await dev.bootloader_write(start + size-8, randdata);
TEST(p.status == 'CTAP2_ERR_NOT_ALLOWED', 'Denies overflow');
p = await dev.bootloader_write(start + size, randdata);
TEST(p.status == 'CTAP2_ERR_NOT_ALLOWED', 'Denies accessing invalid address');
p = await dev.bootloader_write(2048 * (num_pages-2), randdata);
p = await dev.bootloader_write(start + size + 1024, randdata);
TEST(p.status == 'CTAP2_ERR_NOT_ALLOWED', 'Denies accessing invalid address');
p = await dev.bootloader_write(2048 * (num_pages+1), randdata);
p = await dev.bootloader_write(start + size + 1024*10, randdata);
TEST(p.status == 'CTAP2_ERR_NOT_ALLOWED', 'Denies accessing invalid address');
p = await get_firmware_http();
var sig = websafe2array(p.signature);
var badsig = websafe2array(p.signature);
var badsig = new Uint8Array(64);
badsig[40] = badsig[40] ^ 1;
var blocks = MemoryMap.fromHex(p.firmware);
var addresses = blocks.keys();
var addr = addresses.next();
var chunk_size = 244;
while(!addr.done) {
var data = blocks.get(addr.value);
var i;
for (i = 0; i < data.length; i += chunk_size) {
var chunk = data.slice(i,i+chunk_size);
p = await dev.bootloader_write(addr.value + i, chunk);
TEST(p.status == 'CTAP1_SUCCESS', 'Device wrote data');
var progress = (((i/data.length) * 100 * 100) | 0)/100;
document.getElementById('progress').textContent = ''+progress+' %';
}
addr = addresses.next();
}
p = await dev.bootloader_finish(badsig);
TEST(p.status == 'CTAP2_ERR_OPERATION_DENIED', 'Device rejected new image with bad signature');
p = await dev.bootloader_finish(sig);
TEST(p.status == 'CTAP1_SUCCESS', 'Device booted new image with correct signature');
document.getElementById('progress').textContent = ''+100+' %';
}
//while(1)
{
await device_start_over();
// await device_start_over();
//await test_pin();
await test_crypto();
// await test_crypto();
//await test_rng();
}
//await benchmark();
//await test_persistence();
//await test_bootloader();
await test_bootloader();
}
@ -1501,4 +1486,3 @@ var test;
EC = elliptic.ec
//run_tests()

View File

@ -1,202 +0,0 @@
#!/bin/bash
# Directories
cur=`pwd`
tmp=`mktemp -d`
scriptName=`basename $0`
# Certificate Variables
OUTPATH="./"
VERBOSE=0
DURATION=3650 # 10 years
safeExit() {
if [ -d $tmp ]; then
if [ $VERBOSE -eq 1 ]; then
echo "Removing temporary directory '${tmp}'"
fi
rm -rf $tmp
fi
trap - INT TERM EXIT
exit
}
# Help Screen
help() {
echo -n "${scriptName} [OPTIONS] -c=US --state=California
Generate self-signed TLS certificate using OpenSSL
Options:
-c|--country Country Name (2 letter code)
-s|--state State or Province Name (full name)
-l|--locality Locality Name (eg, city)
-o|--organization Organization Name (eg, company)
-u|--unit Organizational Unit Name (eg, section)
-n|--common-name Common Name (e.g. server FQDN or YOUR name)
-e|--email Email Address
-p|--path Path to output generated keys
-d|--duration Validity duration of the certificate (in days)
-h|--help Display this help and exit
-v|--verbose Verbose output
"
}
# Test output path is valid
testPath() {
if [ ! -d $OUTPATH ]; then
echo "The specified directory \"${OUTPATH}\" does not exist"
exit 1
fi
}
# Process Arguments
while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | awk -F= '{print $2}'`
case $PARAM in
-h|--help) help; safeExit ;;
-c|--country) C=$VALUE ;;
-s|--state) ST=$VALUE ;;
-l|--locality) L=$VALUE ;;
-o|--organization) O=$VALUE ;;
-u|--unit) OU=$VALUE ;;
-n|--common-name) CN=$VALUE ;;
-e|--email) emailAddress=$VALUE ;;
-p|--path) OUTPATH=$VALUE; testPath ;;
-d|--duration) DURATION=$VALUE ;;
-v|--verbose) VERBOSE=1 ;;
*) echo "ERROR: unknown parameter \"$PARAM\""; help; exit 1 ;;
esac
shift
done
# Prompt for variables that were not provided in arguments
checkVariables() {
# Country
if [ -z $C ]; then
echo -n "Country Name (2 letter code) [AU]:"
read C
fi
# State
if [ -z $ST ]; then
echo -n "State or Province Name (full name) [Some-State]:"
read ST
fi
# Locality
if [ -z $L ]; then
echo -n "Locality Name (eg, city) []:"
read L
fi
# Organization
if [ -z $O ]; then
echo -n "Organization Name (eg, company) [Internet Widgits Pty Ltd]:"
read O
fi
# Organizational Unit
if [ -z $OU ]; then
echo -n "Organizational Unit Name (eg, section) []:"
read OU
fi
# Common Name
if [ -z $CN ]; then
echo -n "Common Name (e.g. server FQDN or YOUR name) []:"
read CN
fi
# Common Name
if [ -z $emailAddress ]; then
echo -n "Email Address []:"
read emailAddress
fi
}
# Show variable values
showVals() {
echo "Country: ${C}";
echo "State: ${ST}";
echo "Locality: ${L}";
echo "Organization: ${O}";
echo "Organization Unit: ${OU}";
echo "Common Name: ${CN}";
echo "Email: ${emailAddress}";
echo "Output Path: ${OUTPATH}";
echo "Certificate Duration (Days): ${DURATION}";
echo "Verbose: ${VERBOSE}";
}
# Init
init() {
cd $tmp
pwd
}
# Cleanup
cleanup() {
echo "Cleaning up"
cd $cur
rm -rf $tmp
}
buildCsrCnf() {
cat << EOF > ${tmp}/tmp.csr.cnf
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[dn]
C=${C}
ST=${ST}
L=${L}
O=${O}
OU=${OU}
CN=${CN}
emailAddress=${emailAddress}
EOF
}
buildExtCnf() {
cat << EOF > ${tmp}/v3.ext
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = ${CN}
EOF
}
# Build TLS Certificate
build() {
# Santizie domain name for file name
FILENAME=${CN/\*\./}
# Generate CA key & crt
openssl genrsa -out ${tmp}/tmp.key 2048
openssl req -x509 -new -nodes -key ${tmp}/tmp.key -sha256 -days ${DURATION} -out ${OUTPATH}${FILENAME}_CA.pem -subj "/C=${C}/ST=${ST}/L=${L}/O=${O}/OU=${OU}/CN=${CN}/emailAddress=${emailAddress}"
# CSR Configuration
buildCsrCnf
# Create v3.ext configuration file
buildExtCnf
# Server key
openssl req -new -sha256 -nodes -out ${OUTPATH}${FILENAME}.csr -newkey rsa:2048 -keyout ${OUTPATH}${FILENAME}.key -config <( cat ${tmp}/tmp.csr.cnf )
# Server certificate
openssl x509 -req -in ${OUTPATH}${FILENAME}.csr -CA ${OUTPATH}${FILENAME}_CA.pem -CAkey ${tmp}/tmp.key -CAcreateserial -out ${OUTPATH}${FILENAME}.crt -days ${DURATION} -sha256 -extfile ${tmp}/v3.ext
}
checkVariables
build
# showVals
safeExit