Skip to content

Commit

Permalink
Support toboot API 2.0
Browse files Browse the repository at this point in the history
- Support toboot API 2.0
- Allow injecting private key into firmware
  • Loading branch information
gl-sergei committed Apr 7, 2018
1 parent afa83c1 commit b69ee61
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 146 deletions.
16 changes: 11 additions & 5 deletions .travis.yml
@@ -1,18 +1,24 @@
language: c
sudo: required
dist: trusty

addons:
apt:
packages:
- gcc-arm-none-eabi
- libnewlib-arm-none-eabi
- python-pip
- openssl

install:
- pip install asn1crypto --user
- sudo apt-add-repository -y ppa:team-gcc-arm-embedded/ppa
- sudo apt-get update
- sudo apt-get install -yy gcc-arm-embedded

script:
- 'cd u2f && make && cd ..'
- 'cd u2f && make clean certclean && cd ..'
- 'cd u2f && make ENFORCE_DEBUG_LOCK=1 && cd ..'
- 'cd u2f'
- 'make'
- 'make clean certclean'
- 'make ENFORCE_DEBUG_LOCK=1'
- 'openssl ecparam -name prime256v1 -genkey -noout -outform der -out key.der'
- './inject_key.py --key key.der --ctr 1001'
- 'cd ..'
6 changes: 1 addition & 5 deletions mcu/sys-efm32.c
Expand Up @@ -35,11 +35,7 @@ void reset (void);

static uint32_t
stack_entry[] __attribute__ ((section(".first_page.first_words"),used)) = {
(uint32_t)&__ram_end__, (uint32_t)reset,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0x0070b0 /* don't allow to enter toboot by shorting outer pads */,
0x106fb0 /* let toboot know that starting offset is 0x4000 */
(uint32_t)&__ram_end__, (uint32_t)reset
};

typedef void (*handler)(void);
Expand Down
66 changes: 33 additions & 33 deletions u2f/README.md
@@ -1,6 +1,6 @@
# U2F firmware for STM32
# U2F firmware for Tomu

U2F firmware for STM32F103 (ARM Cortex-M3).
U2F firmware for Tomu.

## Installing

Expand Down Expand Up @@ -70,62 +70,62 @@ sudo apt install openocd
### Building

``` sh
cd u2f-token/u2f
cd u2f
make
```

Chopstx comes with support for multiple STM32 boards. This firmware is known to
work on Maple Mini and Blue Pill. Default target is Maple Mini. To build
firmware for Blue Pill run:
### Flashing

Providing you have Toboot installed:

``` sh
ln -s ../board/board-blue-pill.h board.h
dfu-util -v -d 1209:70b1 -D build/u2f.bin
```

Then

```
make
```
### Readout protection

Make sure to enable readout protection if you are going to use Tomu as 2FA for
your accounts. Build firmware with `ENFORCE_DEBUG_LOCK=1`:

will produce firmware file `build/u2f.elf`.
``` sh
make clean
make ENFORCE_DEBUG_LOCK=1
```


### Flashing
### Injecting private key

#### Using ST-LINK/V2 and OpenOCD
Firmware generates EC private key on its first boot and erases it when it
enters the bootloader. You may want to backup your private key and make it
survive firmware upgrade. To achieve this, generate the key on your host machine
and inject it into the firmware binary.

Start OpenOCD:
Generate your private key:

``` sh
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg
openssl ecparam -name prime256v1 -genkey -noout -outform der -out key.der
```

On other terminal run:
You may want to encrypt your `key.der` and back it up.

``` sh
telnet localhost 4444
> reset halt
> stm32f1x unlock 0
> reset halt
> flash write_image erase build/u2f.elf
> stm32f1x lock 0
> reset halt
> shutdown
```
Check device's authentication counter if you are going to perform the firmware
upgrade. You can see it in Yubikey demo site output. For the new device, you can
skip `ctr` parameter all together or set it to 1. Let's say the current counter
value is 1000.

Do not flash two devices with the same binary. Currently all certificates are
built into that binary. Before flashing new device run:
Use this command to patch firmware binary:

``` sh
make certclean
make
./inject_key.py --key key.der --ctr 1001
```


## License

Great thanks to Niibe Yutaka author of Chopstx and Gnuk.
This project is using code components of Chopstx and Gnuk written by Niibe Yutaka.

Copyright © 2017 Sergei Glushchenko
Copyright © 2017, 2018 Sergei Glushchenko

This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
Expand Down
4 changes: 1 addition & 3 deletions u2f/cert/dump-der.py
Expand Up @@ -9,9 +9,7 @@ def pk_to_c_array(name, pk_der):
pk_native = pk['private_key'].native

# translate to hex string
pk_hex = format(pk_native, 'x')
# pad with zeros to 32 bytes
pk_hex = ("0" * (64 - len(pk_hex))) + pk_hex
pk_hex = format(pk_native, '064x')

# split by pairs of characters
hex_bytes = ["0x" + pk_hex[i:i + 2] for i in range(0, len(pk_hex), 2)]
Expand Down
68 changes: 68 additions & 0 deletions u2f/inject_key.py
@@ -0,0 +1,68 @@
#!/usr/bin/env python

#
# Use this script to inject your own private key and authentication counter
# into U2F binary. Might be useful if you want keys to survive firmware updates.
#
# Example:
#
# Generate EC private key with openssl:
# > openssl ecparam -name prime256v1 -genkey -noout -outform der > key.der
#
# Inject generated key into u2f.bin and set auth counter to 100:
# > python3 inject_key.py --key key.der --ctr 100
#

from __future__ import print_function
from asn1crypto.keys import ECPrivateKey
import hashlib
import argparse
import sys
import struct
import os
import tempfile
import subprocess

parser = argparse.ArgumentParser()
parser.add_argument("--elf", default="build/u2f.elf",
help=".elf file to inject keys into")
parser.add_argument("--key", help="EC private key in DER format")
parser.add_argument("--ctr", default=1, type=int, help="value of auth counter")
args = parser.parse_args()

# load and parse private key
if args.key:
with open(args.key, "rb") as f:
der = f.read()
else:
stdin = sys.stdin.buffer if hasattr(sys.stdin, "buffer") else sys.stdin
der = stdin.read()
key = ECPrivateKey.load(der)

# convert key into raw bytes and calculate it's sha256
key_bytes = bytearray.fromhex(format(key["private_key"].native, '064x'))
key_hash = hashlib.sha256(key_bytes).digest()

# fill authentication counter
ctr_bytes = struct.pack("<I", args.ctr) * 256

# pad key and append ctr to produce 2k output blob
blob = (key_bytes + key_hash).ljust(1024, b"\x00") + ctr_bytes

assert len(blob) == 2048
fname, fext = os.path.splitext(args.elf)
assert fext == ".elf"

with tempfile.NamedTemporaryFile(delete=True) as f:
f.write(blob)
f.flush()
# replace contents of .flash_storage section with desired key and counter
ret = subprocess.call(["arm-none-eabi-objcopy", "--update-section",
".flash_storage=" + f.name, args.elf])
if ret != 0:
raise Exception("Failed to patch .elf file!")
# generate binary for bootloader
ret = subprocess.call(["arm-none-eabi-objcopy", "-O", "binary",
args.elf, fname + ".bin"])
if ret != 0:
raise Exception("Failed to create .bin file!")
89 changes: 14 additions & 75 deletions u2f/platform.c
Expand Up @@ -33,19 +33,23 @@
#include <string.h>
#include "board.h"
#include "sys.h"
#include "toboot.h"

#include <mcu/efm32.h>

#define SYSRESETREQ 0x04

/* Perform reset (common for all Cortex-M* processors?) */
static void
nvic_system_reset (void)
{
SCB->AIRCR = (0x05FA0000 | (SCB->AIRCR & 0x70) | SYSRESETREQ);
asm volatile ("dsb");
for (;;);
}
static struct toboot_configuration
__attribute__ ((used, section(".toboot.config")))
toboot_config = {
.align = 0,
.magic = TOBOOT_V2_MAGIC,
.reserved_gen = 0,
.start = 16,
.config = TOBOOT_CONFIG_FLAG_AUTORUN,
.lock_entry = 0,
.erase_mask_lo = 0x00000000,
.erase_mask_hi = 0xc0000000,
.reserved_hash = 0
};

#define LOCKBITS_BASE (0x0FE04000UL) /* Lock-bits page base address */
#define DEBUG_LOCK_WORD (LOCKBITS_BASE + (127 * 4))
Expand All @@ -63,69 +67,6 @@ debug_lock_maybe (void)
}
#endif

static void
busy_wait (void)
{
int i;
for (i = 0; i < 20000; i++)
asm ("nop");
}

extern uint32_t _device_key_base;
extern uint32_t _auth_ctr_base;

uint32_t *boot_vectors = 0x0;

/* Erase app secrets and enter bootloader if outer PADs (PC1 and PE12)
are shorted. */
static void
erase_sercets_maybe (void)
{
int in[4];
int i;

/* Setup PC1 as push-pull output */
GPIO->P[2].MODEL &= ~0xF0UL;
GPIO->P[2].MODEL |= 0x00000004UL << 4;

/* Setup PE12 as input with pull-down */
GPIO->P[4].MODEH &= ~0xF0000UL;
GPIO->P[4].MODEH |= 0x00000002UL << 16;

busy_wait ();

/* toggle PC1 output couple of times to see if PE12 input changes
accordingly */
GPIO->P[2].DOUTSET = 2;

for (i = 0; i < 4; i++)
{
busy_wait ();
in[i] = GPIO->P[4].DIN & (1 << 12);
GPIO->P[2].DOUTTGL = 2;
}

GPIO->P[2].DOUTTGL = 2;

if (in[0] && !in[1] && in[2] && !in[3])
{
/* erase device key and auth counter */
flash_erase_page ((uintptr_t) &_device_key_base);
flash_erase_page ((uintptr_t) &_auth_ctr_base);

/* erase first app page to make bootloader enter DFU mode */
flash_erase_page ((uintptr_t) 0x4000);

/* reset and boot bootloader */
nvic_system_reset ();
}

GPIO->P[2].MODEL = 0;
GPIO->P[2].MODEH = 0;
GPIO->P[4].MODEL = 0;
GPIO->P[4].MODEH = 0;
}

/* Preform platform-specific actions */
void
platform_init (void)
Expand All @@ -134,6 +75,4 @@ platform_init (void)
#if defined(ENFORCE_DEBUG_LOCK)
debug_lock_maybe ();
#endif

erase_sercets_maybe ();
}

0 comments on commit b69ee61

Please sign in to comment.