Compare commits

..

4 Commits

Author SHA1 Message Date
9dfc3d49e8 fix pc build 2019-10-08 16:02:25 -04:00
3487895e2d small errors 2019-10-08 16:02:14 -04:00
6335f16874 add u2f length arg 2019-10-08 15:38:00 -04:00
82551c0b1c check FIDO2 credential IDs in U2F 2019-10-08 15:32:20 -04:00
51 changed files with 1148 additions and 1589 deletions

View File

@ -197,27 +197,6 @@
"code", "code",
"doc" "doc"
] ]
},
{
"login": "ccinelli",
"name": "ccinelli",
"avatar_url": "https://avatars0.githubusercontent.com/u/38021940?v=4",
"profile": "https://github.com/ccinelli",
"contributions": [
"infra",
"test"
]
},
{
"login": "Nitrokey",
"name": "Nitrokey",
"avatar_url": "https://avatars1.githubusercontent.com/u/9438831?v=4",
"profile": "https://www.nitrokey.com",
"contributions": [
"code",
"test",
"ideas"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

3
.gitignore vendored
View File

@ -34,8 +34,7 @@
*.app *.app
*.i*86 *.i*86
*.x86_64 *.x86_64
targets/*/*.hex *.hex
targets/*/*.sha2
# Debug files # Debug files
*.dSYM/ *.dSYM/

View File

@ -6,15 +6,14 @@ addons:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
packages: packages:
- gcc-8 - gcc-7
- cppcheck - cppcheck
services:
- docker
before_install: before_install:
- sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa - sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
- sudo apt-get update -q - sudo apt-get update -q
- sudo apt-get install -y gcc-arm-embedded python3-venv - sudo apt-get install -y gcc-arm-embedded
- sudo apt-get install -y python3-venv
script: script:
- export CC=gcc-8 - export CC=gcc-7
- pyenv shell 3.6.7 - pyenv shell 3.6.7
- make travis - make travis

View File

@ -1,38 +1,33 @@
FROM debian:9.11-slim FROM debian:stretch-slim
MAINTAINER SoloKeys <hello@solokeys.com> MAINTAINER SoloKeys <hello@solokeys.com>
# Install necessary packages RUN apt-get update -qq
RUN apt-get update \ RUN apt-get install -qq bzip2 git make wget >/dev/null
&& apt-get install -y --no-install-recommends \
ca-certificates \
make \
wget \
bzip2 \
git \
&& rm -rf /var/lib/apt/lists/*
# Install ARM compiler # 1. ARM GCC: for compilation
RUN set -eux; \ RUN wget -q -O gcc.tar.bz2 https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2?revision=d830f9dd-cd4f-406d-8672-cca9210dd220?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2018-q4-major
url="https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2019q3/RC1.1/gcc-arm-none-eabi-8-2019-q3-update-linux.tar.bz2?revision=c34d758a-be0c-476e-a2de-af8c6e16a8a2?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2019-q3-update"; \ # from website
wget -O gcc.tar.bz2 "$url"; \ RUN echo "f55f90d483ddb3bcf4dae5882c2094cd gcc.tar.bz2" > gcc.md5
echo "6341f11972dac8de185646d0fbd73bfc gcc.tar.bz2" | md5sum -c -; \ RUN md5sum -c gcc.md5
echo "b50b02b0a16e5aad8620e9d7c31110ef285c1dde28980b1a9448b764d77d8f92 gcc.tar.bz2" | sha256sum -c -; \ # self-generated
tar -C /opt -xf gcc.tar.bz2; \ RUN echo "fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 gcc.tar.bz2" > gcc.sha256
rm gcc.tar.bz2; RUN sha256sum -c gcc.sha256
RUN tar -C /opt -xf gcc.tar.bz2
# Python3.7: for solo-python (merging etc.) # 2. Python3.7: for solo-python (merging etc.)
RUN set -eux; \ RUN wget -q -O miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh
url="https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh"; \ # from website
wget -O miniconda.sh "$url"; \ RUN echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" > miniconda.md5
echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" | md5sum -c -; \ RUN md5sum -c miniconda.md5
echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" | sha256sum -c -; \ # self-generated
bash ./miniconda.sh -b -p /opt/conda; \ RUN echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" > miniconda.sha256
ln -s /opt/conda/bin/python /usr/local/bin/python3; \ RUN sha256sum -c miniconda.sha256
ln -s /opt/conda/bin/python /usr/local/bin/python; \
ln -s /opt/conda/bin/pip /usr/local/bin/pip3; \
ln -s /opt/conda/bin/pip /usr/local/bin/pip; \
rm miniconda.sh; \
pip install -U pip
# solo-python (Python3.7 script for merging etc.) RUN bash ./miniconda.sh -b -p /opt/conda
RUN pip install -U solo-python RUN ln -s /opt/conda/bin/python /usr/local/bin/python3
RUN ln -s /opt/conda/bin/python /usr/local/bin/python
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip3
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip
# 3. Source code
RUN git clone --recurse-submodules https://github.com/solokeys/solo /solo --config core.autocrlf=input

View File

@ -1,5 +1,3 @@
include fido2/version.mk
#define uECC_arch_other 0 #define uECC_arch_other 0
#define uECC_x86 1 #define uECC_x86 1
#define uECC_x86_64 2 #define uECC_x86_64 2
@ -8,34 +6,44 @@ include fido2/version.mk
#define uECC_arm_thumb2 5 #define uECC_arm_thumb2 5
#define uECC_arm64 6 #define uECC_arm64 6
#define uECC_avr 7 #define uECC_avr 7
ecc_platform=2 ecc_platform=2
src = pc/device.c pc/main.c src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard fido2/extensions/*.c) \
$(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
obj = $(src:.c=.o) obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
LIBCBOR = tinycbor/lib/libtinycbor.a LIBCBOR = tinycbor/lib/libtinycbor.a
LIBSOLO = fido2/libsolo.a
ifeq ($(shell uname -s),Darwin) ifeq ($(shell uname -s),Darwin)
export LDFLAGS = -Wl,-dead_strip export LDFLAGS = -Wl,-dead_strip
else else
export LDFLAGS = -Wl,--gc-sections export LDFLAGS = -Wl,--gc-sections
endif endif
LDFLAGS += $(LIBSOLO) $(LIBCBOR) LDFLAGS += $(LIBCBOR)
VERSION:=$(shell git describe --abbrev=0 )
VERSION_FULL:=$(shell git describe)
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
CFLAGS = -O2 -fdata-sections -ffunction-sections -g VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
ECC_CFLAGS = -O2 -fdata-sections -ffunction-sections -DuECC_PLATFORM=$(ecc_platform) -DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
INCLUDES = -I../ -I./fido2/ -I./pc -I../pc -I./tinycbor/src CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS) -g
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
INCLUDES += -I./crypto/cifra/src
CFLAGS += $(INCLUDES) CFLAGS += $(INCLUDES)
CFLAGS += -DAES256=1 -DSOLO_EXPERIMENTAL=1 -DDEBUG_LEVEL=1 # for crypto/tiny-AES-c
CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\" -DSOLO_EXPERIMENTAL=1
name = main name = main
.PHONY: all $(LIBCBOR) $(LIBSOLO) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version .PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
all: main all: main
tinycbor/Makefile crypto/tiny-AES-c/aes.c: tinycbor/Makefile crypto/tiny-AES-c/aes.c:
@ -45,10 +53,7 @@ tinycbor/Makefile crypto/tiny-AES-c/aes.c:
cbor: $(LIBCBOR) cbor: $(LIBCBOR)
$(LIBCBOR): $(LIBCBOR):
cd tinycbor/ && $(MAKE) LDFLAGS='' -j8 cd tinycbor/ && $(MAKE) clean && $(MAKE) LDFLAGS='' -j8
$(LIBSOLO):
cd fido2/ && $(MAKE) CFLAGS="$(CFLAGS)" ECC_CFLAGS="$(ECC_CFLAGS)" APP_CONFIG=app.h -j8
version: version:
@git describe @git describe
@ -57,13 +62,16 @@ test: venv
$(MAKE) clean $(MAKE) clean
$(MAKE) -C . main $(MAKE) -C . main
$(MAKE) clean $(MAKE) clean
$(MAKE) -C ./targets/stm32l432 test PREFIX=$(PREFIX) "VENV=$(VENV)" VERSION_FULL=${SOLO_VERSION_FULL} $(MAKE) -C ./targets/stm32l432 test PREFIX=$(PREFIX) "VENV=$(VENV)"
$(MAKE) clean $(MAKE) clean
$(MAKE) cppcheck $(MAKE) cppcheck
$(name): $(obj) $(LIBCBOR) $(LIBSOLO) $(name): $(obj) $(LIBCBOR)
$(CC) $(LDFLAGS) -o $@ $(obj) $(LDFLAGS) $(CC) $(LDFLAGS) -o $@ $(obj) $(LDFLAGS)
crypto/micro-ecc/uECC.o: ./crypto/micro-ecc/uECC.c
$(CC) -c -o $@ $^ -O2 -fdata-sections -ffunction-sections -DuECC_PLATFORM=$(ecc_platform) -I./crypto/micro-ecc/
venv: venv:
python3 -m venv venv python3 -m venv venv
venv/bin/pip -q install --upgrade pip venv/bin/pip -q install --upgrade pip
@ -80,31 +88,18 @@ wink: venv
fido2-test: venv fido2-test: venv
venv/bin/python tools/ctap_test.py venv/bin/python tools/ctap_test.py
update: DOCKER_IMAGE := "solokeys/solo-firmware:local"
git fetch --tags SOLO_VERSIONISH := "master"
git checkout master docker-build:
git rebase origin/master docker build -t $(DOCKER_IMAGE) .
git submodule update --init --recursive
DOCKER_TOOLCHAIN_IMAGE := "solokeys/solo-firmware-toolchain"
docker-build-toolchain:
docker build -t $(DOCKER_TOOLCHAIN_IMAGE) .
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
uncached-docker-build-toolchain:
docker build --no-cache -t $(DOCKER_TOOLCHAIN_IMAGE) .
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
docker-build-all:
docker run --rm -v "$(CURDIR)/builds:/builds" \ docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR):/solo" \ -v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
-u $(shell id -u ${USER}):$(shell id -g ${USER}) \ $(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
$(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${SOLO_VERSION_FULL} uncached-docker-build:
docker build --no-cache -t $(DOCKER_IMAGE) .
docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
CPPCHECK_FLAGS=--quiet --error-exitcode=2 CPPCHECK_FLAGS=--quiet --error-exitcode=2
@ -121,19 +116,10 @@ clean:
(cd `dirname $$f` ; git checkout -- .) ;\ (cd `dirname $$f` ; git checkout -- .) ;\
fi ;\ fi ;\
done done
cd fido2 && $(MAKE) clean
full-clean: clean full-clean: clean
rm -rf venv rm -rf venv
test-docker:
rm -rf builds/*
$(MAKE) uncached-docker-build-toolchain
# Check if there are 4 docker images/tas named "solokeys/solo-firmware-toolchain"
NTAGS=$$(docker images | grep -c "solokeys/solo-firmware-toolchain") && [ $$NTAGS -eq 4 ]
$(MAKE) docker-build-all
travis: travis:
$(MAKE) test VENV=". ../../venv/bin/activate;" $(MAKE) test VENV=". ../../venv/bin/activate;"
$(MAKE) test-docker $(MAKE) black
$(MAKE) black

View File

@ -1,3 +1,9 @@
**NEW!** We launched a new tiny security key called Somu, it's live on Crowd Supply and you can [pre-order it now](https://solokeys.com/somu)!
[<img src="https://miro.medium.com/max/1400/1*PnzCPLqq_5nt1gjgSEY2LQ.png" width="600">](https://solokeys.com/somu)
Somu is the micro version of Solo. We were inspired to make a secure Tomu, so we took its tiny form factor, we added the secure microcontroller and firmware of Solo, et voilà! Here we have Somu.
[![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://update.solokeys.com/) [![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://update.solokeys.com/)
[![Keybase Chat](https://img.shields.io/badge/chat-on%20keybase-brightgreen.svg)](https://keybase.io/team/solokeys.public) [![Keybase Chat](https://img.shields.io/badge/chat-on%20keybase-brightgreen.svg)](https://keybase.io/team/solokeys.public)
[![Build Status](https://travis-ci.com/solokeys/solo.svg?style=flat-square&branch=master)](https://travis-ci.com/solokeys/solo) [![Build Status](https://travis-ci.com/solokeys/solo.svg?style=flat-square&branch=master)](https://travis-ci.com/solokeys/solo)
@ -32,58 +38,10 @@ Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We support Python3. If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We support Python3.
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
to be a different hex color.
Then recompile, load your new firmware, and enjoy a different LED color Solo.
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
## Checking out the code
```bash ```bash
git clone --recurse-submodules https://github.com/solokeys/solo git clone --recurse-submodules https://github.com/solokeys/solo
cd solo cd solo
```
If you forgot the `--recurse-submodules` while cloning, simply run `git submodule update --init --recursive`.
`make update` will also checkout the latest code on `master` and submodules.
## Checking out the code to build a specific version
You can checkout the code to build a specific version of the firmware with:
```
VERSION_TO_BUILD=2.5.3
git fetch --tags
git checkout ${VERSION_TO_BUILD}
git submodule update --init --recursive
```
## Installing the toolchain
In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the `solo-python` python package. Check our [documentation](https://docs.solokeys.io/solo/) for details
## Installing the toolkit and compiling in Docker
Alternatively, you can use Docker to create a container with the toolchain.
You can run:
```bash
# Build the toolchain container
make docker-build-toolchain
# Build all versions of the firmware in the "builds" folder
make docker-build-all
```
The `builds` folder will contain all the variation on the firmware in `.hex` files.
## Build locally
If you have the toolchain installed on your machine you can build the firmware with:
```bash
cd targets/stm32l432 cd targets/stm32l432
make cbor make cbor
make build-hacker make build-hacker
@ -95,6 +53,19 @@ solo program aux enter-bootloader
solo program bootloader targets/stm32l432/solo.hex solo program bootloader targets/stm32l432/solo.hex
``` ```
Alternatively, run `make docker-build` and use the firmware generated in `/tmp`.
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
to be a different hex color.
Then recompile, load your new firmware, and enjoy a different LED color Solo.
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
# Developing Solo (No Hardware Needed) # Developing Solo (No Hardware Needed)
Clone Solo and build it Clone Solo and build it
@ -166,10 +137,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center"><a href="http://www.schulz.dk"><img src="https://avatars1.githubusercontent.com/u/1150049?v=4" width="100px;" alt="Kim Schulz"/><br /><sub><b>Kim Schulz</b></sub></a><br /><a href="#business-kimusan" title="Business development">💼</a> <a href="#ideas-kimusan" title="Ideas, Planning, & Feedback">🤔</a></td> <td align="center"><a href="http://www.schulz.dk"><img src="https://avatars1.githubusercontent.com/u/1150049?v=4" width="100px;" alt="Kim Schulz"/><br /><sub><b>Kim Schulz</b></sub></a><br /><a href="#business-kimusan" title="Business development">💼</a> <a href="#ideas-kimusan" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/oplik0"><img src="https://avatars2.githubusercontent.com/u/25460763?v=4" width="100px;" alt="Jakub"/><br /><sub><b>Jakub</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aoplik0" title="Bug reports">🐛</a></td> <td align="center"><a href="https://github.com/oplik0"><img src="https://avatars2.githubusercontent.com/u/25460763?v=4" width="100px;" alt="Jakub"/><br /><sub><b>Jakub</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aoplik0" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/jolo1581"><img src="https://avatars1.githubusercontent.com/u/53423977?v=4" width="100px;" alt="Jan A."/><br /><sub><b>Jan A.</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Documentation">📖</a></td> <td align="center"><a href="https://github.com/jolo1581"><img src="https://avatars1.githubusercontent.com/u/53423977?v=4" width="100px;" alt="Jan A."/><br /><sub><b>Jan A.</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/ccinelli"><img src="https://avatars0.githubusercontent.com/u/38021940?v=4" width="100px;" alt="ccinelli"/><br /><sub><b>ccinelli</b></sub></a><br /><a href="#infra-ccinelli" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/solokeys/solo/commits?author=ccinelli" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.nitrokey.com"><img src="https://avatars1.githubusercontent.com/u/9438831?v=4" width="100px;" alt="Nitrokey"/><br /><sub><b>Nitrokey</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=Nitrokey" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=Nitrokey" title="Tests">⚠️</a> <a href="#ideas-Nitrokey" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr> </tr>
</table> </table>
@ -186,7 +153,7 @@ You may use Solo software under the terms of either the Apache 2.0 license or MI
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
All hardware, unless otherwise noted, is dual licensed under CERN and CC-BY-SA. All hardware, unless otherwise noted, is dual licensed under CERN and CC-BY-SA.
You may use Solo hardware under the terms of either the CERN 1.2 license or CC-BY-SA 4.0 license. You may use Solo hardware under the terms of either the CERN 2.1 license or CC-BY-SA 4.0 license.
All documentation, unless otherwise noted, is licensed under CC-BY-SA. All documentation, unless otherwise noted, is licensed under CC-BY-SA.
You may use Solo documentation under the terms of the CC-BY-SA 4.0 license You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
@ -203,7 +170,7 @@ You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solok
<br/> <br/>
[![License](https://img.shields.io/github/license/solokeys/solo.svg)](https://github.com/solokeys/solo/blob/master/LICENSE) [![License](https://img.shields.io/github/license/solokeys/solo.svg)](https://github.com/solokeys/solo/blob/master/LICENSE)
[![All Contributors](https://img.shields.io/badge/all_contributors-22-orange.svg?style=flat-square)](#contributors) [![All Contributors](https://img.shields.io/badge/all_contributors-20-orange.svg?style=flat-square)](#contributors)
[![Build Status](https://travis-ci.com/solokeys/solo.svg?branch=master)](https://travis-ci.com/solokeys/solo) [![Build Status](https://travis-ci.com/solokeys/solo.svg?branch=master)](https://travis-ci.com/solokeys/solo)
[![Discourse Users](https://img.shields.io/discourse/https/discourse.solokeys.com/users.svg)](https://discourse.solokeys.com) [![Discourse Users](https://img.shields.io/discourse/https/discourse.solokeys.com/users.svg)](https://discourse.solokeys.com)
[![Keybase Chat](https://img.shields.io/badge/chat-on%20keybase-brightgreen.svg)](https://keybase.io/team/solokeys.public) [![Keybase Chat](https://img.shields.io/badge/chat-on%20keybase-brightgreen.svg)](https://keybase.io/team/solokeys.public)

View File

@ -1 +1 @@
3.1.0 2.5.3

View File

@ -1,147 +0,0 @@
# Using Solo for passwordless or second factor login on Linux
## Setup on Ubuntu and Manjaro
Before you can use Solo for passwordless or second factor login in your Linux system you have to install some packages.
This was tested on **Linux Mint 19.3** and on **Manjaro 18.x**
First you have to install PAM modules for u2f.
**Ubuntu (Linux Mint):**
```
sudo apt install libpam-u2f pamu2fcfg
```
**Manjaro**
```
pacman -Syu pam-u2f
```
## Setting up key
To use Solo as passwordless or second factor login, you have to setup your system with your Solo.
First create a new folder named **Yubico** in your **.config** folder in your **home** directory
```
mkdir ~/.config/Yubico
```
Then create a new key for PAM U2F module. If it is your first key you want to register use following command:
```
pamu2fcfg > ~/.config/Yubico/u2f_keys
```
If you want to register an additional key use this command instead:
```
pamu2fcfg >> ~/.config/Yubico/u2f_keys
```
Now press the button on your Solo.
<br>
<br>
If you can't generate your key on **Ubuntu** (error message), you may add Yubico Team from PPA and install latest libpam-u2f and pamu2fcfg and try again.
```
sudo add-apt-repository ppa:yubico/stable
sudo apt-get update
sudo apt-get upgrade
```
**Manjaro** should work without problems.
## Login into Linux
### Passwordless
To login passwordless into your Linux system, you have to edit the file **lightdm** (or **gdm** or which display manager you prefered).
In case of lightdm and VIM as editor:
```
sudo vim /etc/pam.d/lightdm
```
**On Ubuntu:**<br>
Search following entry:
```
@include common-auth
```
and add
```
auth sufficient pam_u2f.so
```
**before** *@include common-auth.*
<br>
<br>
**On Manjaro**<br>
Search following enrty
```
auth include system-login
```
and add
```
auth sufficient pam_u2f.so
```
** before** *auth include system-login*.
<br>
<br>
Now save the file and test it.<br>
Insert Solo in your USB port and logout.
Now you should be able to login into Linux without password, only with pressing your button on Solo and press enter.
Why **sufficient**? The difference between the keyword sufficient and required is, if you don't have your Solo available, you can also login, because the system falls back to password mode.
The login mechanism can be also used for additional features like:
- Login after screen timeout - edit /etc/pam.d/mate-screensaver (or kde-screensaver, ...)
- Passwordless sudo - edit /etc/pam.d/sudo
Check out your folder **/etc/pam.d/** and do some experiments.
**But remember:** <br>
The login passwordless won't make your system more secure, but maybe more comfortable. If somebody have access to your Solo, this person will be also able to login into your system.
### Solo as second factor
To use Solo as second factor, for login into your Linux system, is nearly the same.
```
sudo vim /etc/pam.d/lightdm
```
**On Ubuntu**<br>
Search following entry:
```
@include common-auth
```
and add
```
auth required pam_u2f.so
```
**after** *@include common-auth*.
<br>
<br>
**On Manjaro**<br>
Search following entry:
```
auth include system-login
```
Add following entry
```
auth required pam_u2f.so
```
**after** *auth include system-login*.
<br>
<br>
Save the file and test it. <br>
In case your Solo is not present, your password will be incrorrect. If Solo is plugged into your USB port, it will signal pressing the button and you will be able to login into Linux.
Why **required**? If you choose the option **sufficent** your Solo is optional. You could also login without second factor if your Solo is not connected.
**But remember:**<br>
If you loose your Solo you won't be able to login into your system.

View File

@ -1,17 +1,16 @@
# Booting into bootloader mode # Booting into bootloader mode
If you have a recent version of Solo, you can put it into bootloader mode by running this command. You can put Solo into bootloader mode by holding down the button, and plugging in Solo. After 2 seconds, bootloader mode will activate.
You'll see a yellowish flashing light and you can let go of the button.
Now Solo is ready to [accept firmware updates](/solo/signed-updates). If the Solo is a secured model, it can only accept signed updates, typically in the `firmware-*.json` format.
If Solo is running a hacker build, it can be put into bootloader mode on command. This makes it easier for development.
```bash ```bash
solo program aux enter-bootloader solo program aux enter-bootloader
``` ```
If your Solo is a bit older (<=2.5.3) You can put Solo into bootloader mode by using the button method:
Hold down button while plugging in Solo. After 2 seconds, bootloader mode will activate.
You'll see a yellowish flashing light and you can let go of the button.
Now Solo is ready to [accept firmware updates](/solo/signed-updates). If the Solo is a secured model, it can only accept signed updates, typically in the `firmware-*.json` format.
# The boot stages of Solo # The boot stages of Solo
Solo has 3 boot stages. Solo has 3 boot stages.
@ -22,8 +21,7 @@ The first stage is the DFU (Device Firmware Update) which is in a ROM on Solo.
This is what allows the entire firmware of Solo to be programmed. **It's not recommended to develop for Solo using the DFU because This is what allows the entire firmware of Solo to be programmed. **It's not recommended to develop for Solo using the DFU because
if you program broken firmware, you could brick your device**. if you program broken firmware, you could brick your device**.
On hacker/nonverifying-bootloader devices, you can boot into the DFU by holding down the button for 5 seconds, On hacker devices, you can boot into the DFU by holding down the button for 5 seconds, when Solo is already in bootloader mode.
when Solo is already in bootloader mode.
You can also run this command when Solo is in bootloader mode to put it in DFU mode. You can also run this command when Solo is in bootloader mode to put it in DFU mode.
@ -31,7 +29,7 @@ You can also run this command when Solo is in bootloader mode to put it in DFU m
solo program aux enter-dfu solo program aux enter-dfu
``` ```
Note it will stay in DFU mode until you to tell it to boot again. You can boot it again by running the following. Note it will stay in DFU mode until to tell it to boot again. You can boot it again by running the following.
```bash ```bash
solo program aux leave-dfu solo program aux leave-dfu

View File

@ -36,21 +36,17 @@ Enter the `stm32l4xx` target directory.
cd targets/stm32l432 cd targets/stm32l432
``` ```
Now build the Solo application. Now build Solo.
``` ```
make firmware make build-hacker
``` ```
The `firmware` recipe builds the solo application, and outputs `solo.hex`. You can use this The `build-hacker` recipe does a few things. First it builds the bootloader, with
to reprogram any unlocked/hacker Solo model. Note that it does not include the Solo bootloader,
so it is not a full reprogram.
<!-- First it builds the bootloader, with
signature checking disabled. Then it builds the Solo application with "hacker" features signature checking disabled. Then it builds the Solo application with "hacker" features
enabled, like being able to jump to the bootloader on command. It then merges bootloader enabled, like being able to jump to the bootloader on command. It then merges bootloader
and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex` and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex`
into `all.hex`. --> into `all.hex`.
If you're just planning to do development, **please don't try to reprogram the bootloader**, If you're just planning to do development, **please don't try to reprogram the bootloader**,
as this can be risky if done often. Just use `solo.hex`. as this can be risky if done often. Just use `solo.hex`.
@ -61,13 +57,13 @@ If you're developing, you probably want to see debug messages! Solo has a USB
Serial port that it will send debug messages through (from `printf`). You can read them using Serial port that it will send debug messages through (from `printf`). You can read them using
a normal serial terminal like `picocom` or `putty`. a normal serial terminal like `picocom` or `putty`.
Just add `-debug-1` or `-debug-2` to your build recipe, like this. Just add `DEBUG=1` or `DEBUG=2` to your build recipe, like this.
``` ```
make firmware-debug-1 make build-hacker DEBUG=1
``` ```
If you use `debug-2`, that means Solo will not boot until something starts reading If you use `DEBUG=2`, that means Solo will not boot until something starts reading
its debug messages. So it basically waits to tether to a serial terminal so that you don't its debug messages. So it basically waits to tether to a serial terminal so that you don't
miss any debug messages. miss any debug messages.
@ -82,45 +78,27 @@ solo monitor <serial-port>
[See issue 62](https://github.com/solokeys/solo/issues/62). [See issue 62](https://github.com/solokeys/solo/issues/62).
### Building a complete Solo build (application + bootloader + certificate) ### Building a Solo release
To make a complete Solo build, you need to build the bootloader. We provide To build Solo
two easy recipes:
* `bootloader-nonverifying`: bootloader with no signature checking on updates. I.e. "unlocked". If you want to build a release of Solo, we recommend trying a Hacker build first
* `bootloader-verifying`: bootloader with signature checking enforced on updated. I.e. "Locked". just to make sure that it's working. Otherwise it may not be as easy or possible to
fix any mistakes.
To be safe, let's use the `-nonverifying` build. If you're ready to program a full release, run this recipe to build.
``` ```
make bootloader-nonverifying make build-release-locked
``` ```
This outputs `bootloader.hex`. We can then merge the bootloader and application. This outputs bootloader.hex, solo.hex, and the combined all.hex.
``` Programming `all.hex` will cause the device to permanently lock itself. This means debuggers cannot be used and signature checking
solo mergehex bootloader.hex solo.hex bundle.hex will be enforced on all future updates.
```
`bundle.hex` is our complete firmware build. Note it is in this step that you can Note if you program a secured `solo.hex` file onto a Solo Hacker, it will lock the flash, but the bootloader
include a custom attestation certificate or lock the device from debugging/DFU. will still accept unsigned firmware updates. So you can switch it back to being a hacker, but you will
By default the "hacker" attestation certifcate and key is used. not be able to replace the unlocked bootloader anymore, since the permanently locked flash also disables the DFU.
[Read more on Solo's boot stages](/solo/bootloader-mode).
```
solo mergehex \
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
--attestation-cert attestation.der \
--lock \
solo.hex \
bootloader.hex \
bundle.hex
```
See [here for more information on custom attestation](/solo/customization/).
If you use `--lock`, this will permanently lock the device to this new bootloader. You
won't be able to program the bootloader again or be able to connect a hardware debugger.
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode).

View File

@ -45,7 +45,7 @@ email=example@example.com
openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
# generate a "signing request" # generate a "signing request"
openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email" openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
# self sign the request # self sign the request
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256 openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
@ -82,7 +82,7 @@ email=example@example.com
openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
# generate a "signing request" # generate a "signing request"
openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email" openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
# sign the request # sign the request
openssl x509 -req -days 18250 -in device_key.pem.csr -extfile v3.ext -CA root_cert.pem -CAkey root_key.pem -set_serial 01 -out device_cert.pem -sha256 openssl x509 -req -days 18250 -in device_key.pem.csr -extfile v3.ext -CA root_cert.pem -CAkey root_key.pem -set_serial 01 -out device_cert.pem -sha256
@ -114,27 +114,28 @@ If the checks succeed, you are ready to program the device attestation key and c
### Programming an attestation key and certificate ### Programming an attestation key and certificate
First, [Build your solo application and bootloader](/solo/building). Convert the DER format of the device attestation certificate to "C" bytes using our utility script. You may first need to
first install prerequisite python modules (`pip install -r tools/requirements.txt`).
Print your attestation key in a hex string format. Using our utility script:
``` ```
python3 tools/gencert/print_x_y.py device_key.pem python tools/gencert/cbytes.py device_cert.der
``` ```
Merge the `bootloader.hex`, `solo.hex`, attestion key, and certificate into one firmware file. Copy the byte string portion into the [`attestation.c` source file of Solo](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/attestation.c). Overwrite the development or "default" certificate that is already there.
Now [build the Solo firmware](/solo/building), either a secure or hacker build. You will need to produce a `bootloader.hex` file and a `solo.hex` file.
Print your attestation key in a hex string format.
``` ```
solo mergehex \ python tools/print_x_y.py device_key.pem
--attestation-key "(The 32-byte hex string extracted from device_key.pem)" \
--attestation-cert device_cert.der \
--lock \
solo.hex \
bootloader.hex \
bundle.hex
``` ```
Now you have a newly created `bundle.hex` file with a custom attestation key and cert. You can [program this `bundle.hex` file Merge the `bootloader.hex`, `solo.hex`, and attestion key into one firmware file.
```
solo mergehex --attestation-key <attestation-key-hex-string> bootloader.hex solo.hex all.hex
```
Now you have a newly create `all.hex` file with a custom attestation key. You can [program this `all.hex` file
with Solo in DFU mode](/solo/programming#procedure). with Solo in DFU mode](/solo/programming#procedure).
Are you interested in customizing in bulk? Contact hello@solokeys.com and we can help.

View File

@ -1,60 +0,0 @@
# Usage and Porting
Solo is designed to be used as a library or ported to other platforms easily. Here is an example
`main()` function.
```c
int main()
{
uint8_t hidmsg[64];
uint32_t t1 = 0;
device_init();
memset(hidmsg,0,sizeof(hidmsg));
while(1)
{
if (usbhid_recv(hidmsg) > 0)
{
ctaphid_handle_packet(hidmsg); // pass into libsolo!
memset(hidmsg, 0, sizeof(hidmsg));
}
ctaphid_check_timeouts();
}
}
```
`ctaphid_handle_packet(hidmsg);` is the entrance into the HID layer of libsolo, and will buffer packets and pass them
into FIDO2 or U2F layers.
Everything in the library is cross-platform, but it needs some functions implemented that are usually
platform specific. For example, how should libsolo implement an atomic counter? Where should it save state?
For all of these platform specific functions, the library contains it's own `weak` definition, so the library will compile and run.
LibSolo by default will not try to use an atomic
counter or save data persistently -- that needs to be implemented externally.
If you are using libsolo on another platform,
you should take a look at these possibly platform specific functions. They are listed in `fido2/device.h`.
If you'd like to reimplement any of the functions, then simply implement the function and compile normally.
GCC will replace libsolo's `weak` defined functions (everything in `fido2/device.h`) with your functions. By doing this, you
are replacing the function that is used by libsolo.
To get the library to compile
and run, you only need to implement one function for libsolo: `usbhid_send(uint8_t * send)`, which
is called by the library to send a 64 byte packet over a USB HID endpoint. In essence, you are giving
libsolo a function to write to USB.
The rest of the definitions in `fido2/device.h` are not required to compile and run so you can
immediately hit the ground running and iterative add what else you need. You'll definitely want
to continue implementing other functions in `fido2/device.h`. For example, no data will be stored
persistently until you define how it can be done!
For examples, check out the build for STM32L4 and PC (check out `pc/device` and `targets/stm32l432/src/device.c`).
If there's something that doesn't work for you -- send a pull request! It's better if we can
work together off of the same repo and not fork.

View File

@ -22,11 +22,12 @@ solo key update <--secure | --hacker>
You can manually install the [latest release](https://github.com/solokeys/solo/releases), or use a build that you made. You can manually install the [latest release](https://github.com/solokeys/solo/releases), or use a build that you made.
```bash ```bash
# If it's a hacker, it will automatically boot into bootloader mode.
solo program bootloader <firmware.hex | firmware.json> solo program bootloader <firmware.hex | firmware.json>
``` ```
Note you won't be able to use `all.hex` or the `bundle-*.hex` builds, as these include the solo bootloader. You shouldn't Note you won't be able to use `all.hex` or the `bundle-*.hex` builds, as these include the solo bootloader. You shouldn't
risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations](/solo/customization/). risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations]().
## Updating a Hacker to a Secure Solo ## Updating a Hacker to a Secure Solo
@ -36,19 +37,15 @@ So it's important to not mess this up or you may brick your device.
You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use
a build that you made yourself. a build that you made yourself.
You need to use a firmware file that has the combined bootloader, application, and attestation key pair (bootloader + firmware + key). You need to use a firmware file that has the combined bootloader and application (or at the very least just the bootloader).
This means using the `bundle-*.hex` file or the `bundle.hex` from your build. This means using the `bundle-*.hex` file or the `all.hex` from your build. If you overwrite the Solo flash with a missing bootloader,
it will be bricked.
#### *Warning*
* **If you overwrite the Solo flash with a missing bootloader, it will be bricked**.
* **If you program bootloader and firmware with no attestation, you will run into FIDO registration issues**
We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this, We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this,
you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex` you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`
is a secured build that will lock your device and it will behave just like a Secure Solo. The main difference is that is a secured build that will lock your device and it will behave just like a Secure Solo. The main difference is that
it uses a "default" attestation key in the device, rather than the SoloKeys attestation key. There is no security it uses a "default" attestation key in the device, rather than the SoloKeys attestation key. There is no security
concern with using our default attestation key, aside from a small privacy implication that services can distinguish it from Solo Secure. concern with using our default attestation key, aside from a privacy implication that services can distinguish it from Solo Secure.
### Procedure ### Procedure
@ -64,7 +61,7 @@ concern with using our default attestation key, aside from a small privacy impli
2. Program the device 2. Program the device
solo program dfu <bundle-secure-non-solokeys.hex | bundle.hex> solo program dfu <bundle-secure-non-solokeys.hex | all.hex>
Double check you programmed it with bootloader + application (or just bootloader). Double check you programmed it with bootloader + application (or just bootloader).
If you messed it up, simply don't do the next step and repeat this step correctly. If you messed it up, simply don't do the next step and repeat this step correctly.

View File

@ -1,44 +0,0 @@
include version.mk
ifndef APP_CONFIG
APP_CONFIG=example_app.h
endif
INC = -I./ -I./extensions
INC += -I../tinycbor/src
INC += -I../crypto/sha256 -I../crypto/micro-ecc -I../crypto/tiny-AES-c
INC += -I../crypto/cifra/src -I../crypto/cifra/src/ext
INT_CFLAGS = -DAPP_CONFIG=\"$(APP_CONFIG)\"
INT_CFLAGS += $(INC)
INT_CFLAGS += $(SOLO_VERSION_FLAGS)
SRC = apdu.c util.c u2f.c test_power.c
SRC += stubs.c log.c ctaphid.c ctap.c
SRC += ctap_parse.c crypto.c
SRC += device.c
SRC += version.c
SRC += data_migration.c
SRC += extensions/extensions.c extensions/solo.c
SRC += extensions/wallet.c
# Crypto libs
SRC += ../crypto/sha256/sha256.c ../crypto/micro-ecc/uECC.c ../crypto/tiny-AES-c/aes.c
SRC += ../crypto/cifra/src/sha512.c ../crypto/cifra/src/blockwise.c
OBJ = $(SRC:.c=.o)
all: libsolo.a
libsolo.a: $(OBJ)
$(AR) cqs $@ $^
%.o: %.c
$(CC) $^ $(INT_CFLAGS) $(CFLAGS) -c -o $@
../crypto/micro-ecc/uECC.o: ../crypto/micro-ecc/uECC.c
$(CC) $^ $(INT_CFLAGS) $(ECC_CFLAGS) -c -o $@
clean:
rm -f $(OBJ) libsolo.a

View File

@ -5,33 +5,29 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be // http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms. // copied, modified, or distributed except according to those terms.
/* /*
* Wrapper for crypto implementation on device. * Wrapper for crypto implementation on device
*
* Can be replaced with different crypto implementation by
* defining EXTERNAL_SOLO_CRYPTO
* *
* */ * */
#ifndef EXTERNAL_SOLO_CRYPTO
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "util.h" #include "util.h"
#include "crypto.h" #include "crypto.h"
#ifdef USE_SOFTWARE_IMPLEMENTATION
#include "sha256.h" #include "sha256.h"
#include "uECC.h" #include "uECC.h"
#include "aes.h" #include "aes.h"
#include "ctap.h" #include "ctap.h"
#include "device.h" #include "device.h"
// stuff for SHA512
#include "sha2.h"
#include "blockwise.h"
#include APP_CONFIG
#include "log.h" #include "log.h"
#include APP_CONFIG
#ifdef USING_PC
typedef enum typedef enum
{ {
MBEDTLS_ECP_DP_NONE = 0, MBEDTLS_ECP_DP_NONE = 0,
@ -48,56 +44,53 @@ typedef enum
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */ MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */ MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
} mbedtls_ecp_group_id; } mbedtls_ecp_group_id;
#endif
const uint8_t attestation_cert_der[];
const uint16_t attestation_cert_der_size;
const uint8_t attestation_key[];
const uint16_t attestation_key_size;
static SHA256_CTX sha256_ctx; static SHA256_CTX sha256_ctx;
static cf_sha512_context sha512_ctx;
static const struct uECC_Curve_t * _es256_curve = NULL; static const struct uECC_Curve_t * _es256_curve = NULL;
static const uint8_t * _signing_key = NULL; static const uint8_t * _signing_key = NULL;
static int _key_len = 0; static int _key_len = 0;
// Secrets for testing only // Secrets for testing only
static uint8_t master_secret[64]; static uint8_t master_secret[64];
static uint8_t transport_secret[32]; static uint8_t transport_secret[32];
void crypto_sha256_init(void)
void crypto_sha256_init()
{ {
sha256_init(&sha256_ctx); sha256_init(&sha256_ctx);
} }
void crypto_sha512_init(void) void crypto_reset_master_secret()
{ {
cf_sha512_init(&sha512_ctx);
}
void crypto_load_master_secret(uint8_t * key)
{
#if KEY_SPACE_BYTES < 96
#error "need more key bytes"
#endif
memmove(master_secret, key, 64);
memmove(transport_secret, key+64, 32);
}
void crypto_reset_master_secret(void)
{
memset(master_secret, 0, 64);
memset(transport_secret, 0, 32);
ctap_generate_rng(master_secret, 64); ctap_generate_rng(master_secret, 64);
ctap_generate_rng(transport_secret, 32); ctap_generate_rng(transport_secret, 32);
} }
void crypto_load_master_secret(uint8_t * key)
{
#if KEY_SPACE_BYTES < 96
#error "need more key bytes"
#endif
memmove(master_secret, key, 64);
memmove(transport_secret, key+64, 32);
}
void crypto_sha256_update(uint8_t * data, size_t len) void crypto_sha256_update(uint8_t * data, size_t len)
{ {
sha256_update(&sha256_ctx, data, len); sha256_update(&sha256_ctx, data, len);
} }
void crypto_sha512_update(const uint8_t * data, size_t len) {
cf_sha512_update(&sha512_ctx, data, len);
}
void crypto_sha256_update_secret() void crypto_sha256_update_secret()
{ {
sha256_update(&sha256_ctx, master_secret, 32); sha256_update(&sha256_ctx, master_secret, 32);
@ -108,32 +101,26 @@ void crypto_sha256_final(uint8_t * hash)
sha256_final(&sha256_ctx, hash); sha256_final(&sha256_ctx, hash);
} }
void crypto_sha512_final(uint8_t * hash)
{
// NB: there is also cf_sha512_digest
cf_sha512_digest_final(&sha512_ctx, hash);
}
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac) void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
{ {
uint8_t buf[64]; uint8_t buf[64];
unsigned int i; int i;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
if (key == CRYPTO_MASTER_KEY) if (key == CRYPTO_MASTER_KEY)
{ {
key = master_secret; key = master_secret;
klen = sizeof(master_secret)/2; klen = sizeof(master_secret);
} }
else if (key == CRYPTO_TRANSPORT_KEY) else if (key == CRYPTO_TRANSPORT_KEY)
{ {
key = transport_secret; key = transport_secret;
klen = 32; klen = 32;
} }
if(klen > 64) if(klen > 64)
{ {
printf2(TAG_ERR, "Error, key size must be <= 64\n"); printf2(TAG_ERR,"Error, key size must be <= 64\n");
exit(1); exit(1);
} }
@ -151,24 +138,19 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac) void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
{ {
uint8_t buf[64]; uint8_t buf[64];
unsigned int i; int i;
crypto_sha256_final(hmac); crypto_sha256_final(hmac);
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
if (key == CRYPTO_MASTER_KEY) if (key == CRYPTO_MASTER_KEY)
{ {
key = master_secret; key = master_secret;
klen = sizeof(master_secret)/2; klen = sizeof(master_secret);
}
else if (key == CRYPTO_TRANSPORT_KEY2)
{
key = transport_secret;
klen = 32;
} }
if(klen > 64) if(klen > 64)
{ {
printf2(TAG_ERR, "Error, key size must be <= 64\n"); printf2(TAG_ERR,"Error, key size must be <= 64\n");
exit(1); exit(1);
} }
memmove(buf, key, klen); memmove(buf, key, klen);
@ -185,16 +167,16 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
} }
void crypto_ecc256_init(void) void crypto_ecc256_init()
{ {
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng); uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
_es256_curve = uECC_secp256r1(); _es256_curve = uECC_secp256r1();
} }
void crypto_ecc256_load_attestation_key(void) void crypto_ecc256_load_attestation_key()
{ {
_signing_key = device_get_attestation_key(); _signing_key = attestation_key;
_key_len = 32; _key_len = 32;
} }
@ -202,7 +184,7 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
{ {
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0) if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
{ {
printf2(TAG_ERR, "error, uECC failed\n"); printf2(TAG_ERR,"error, uECC failed\n");
exit(1); exit(1);
} }
} }
@ -239,19 +221,19 @@ void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_I
if (_key_len != 32) goto fail; if (_key_len != 32) goto fail;
break; break;
default: default:
printf2(TAG_ERR, "error, invalid ECDSA alg specifier\n"); printf2(TAG_ERR,"error, invalid ECDSA alg specifier\n");
exit(1); exit(1);
} }
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0) if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
{ {
printf2(TAG_ERR, "error, uECC failed\n"); printf2(TAG_ERR,"error, uECC failed\n");
exit(1); exit(1);
} }
return; return;
fail: fail:
printf2(TAG_ERR, "error, invalid key length\n"); printf2(TAG_ERR,"error, invalid key length\n");
exit(1); exit(1);
} }
@ -261,11 +243,8 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey); crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
crypto_sha256_update(data, len); crypto_sha256_update(data, len);
crypto_sha256_update(data2, len2); crypto_sha256_update(data2, len2);
crypto_sha256_update(master_secret, 32); // TODO AES crypto_sha256_update(master_secret, 32);
crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey); crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
crypto_aes256_init(master_secret + 32, NULL);
crypto_aes256_encrypt(privkey, 32);
} }
@ -282,12 +261,12 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
memmove(x,pubkey,32); memmove(x,pubkey,32);
memmove(y,pubkey+32,32); memmove(y,pubkey+32,32);
} }
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey) void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
{ {
uECC_compute_public_key(privkey, pubkey, _es256_curve); uECC_compute_public_key(privkey, pubkey, _es256_curve);
} }
void crypto_load_external_key(uint8_t * key, int len) void crypto_load_external_key(uint8_t * key, int len)
{ {
_signing_key = key; _signing_key = key;
@ -299,7 +278,7 @@ void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
{ {
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1) if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
{ {
printf2(TAG_ERR, "Error, uECC_make_key failed\n"); printf2(TAG_ERR,"Error, uECC_make_key failed\n");
exit(1); exit(1);
} }
} }
@ -308,7 +287,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) if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
{ {
printf2(TAG_ERR, "Error, uECC_shared_secret failed\n"); printf2(TAG_ERR,"Error, uECC_shared_secret failed\n");
exit(1); exit(1);
} }
@ -359,4 +338,42 @@ void crypto_aes256_encrypt(uint8_t * buf, int length)
} }
const uint8_t attestation_cert_der[] =
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
const uint8_t attestation_key[] = "\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46\xb7\x2e\x5f\xe7\x5d\x30";
const uint16_t attestation_key_size = sizeof(attestation_key)-1;
#else
#error "No crypto implementation defined"
#endif #endif

View File

@ -9,6 +9,8 @@
#include <stddef.h> #include <stddef.h>
#define USE_SOFTWARE_IMPLEMENTATION
void crypto_sha256_init(); void crypto_sha256_init();
void crypto_sha256_update(uint8_t * data, size_t len); void crypto_sha256_update(uint8_t * data, size_t len);
void crypto_sha256_update_secret(); void crypto_sha256_update_secret();
@ -21,6 +23,7 @@ void crypto_sha512_init();
void crypto_sha512_update(const uint8_t * data, size_t len); void crypto_sha512_update(const uint8_t * data, size_t len);
void crypto_sha512_final(uint8_t * hash); void crypto_sha512_final(uint8_t * hash);
void crypto_ecc256_init(); void crypto_ecc256_init();
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y); void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y);
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey); void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey);
@ -51,4 +54,10 @@ void crypto_reset_master_secret();
void crypto_load_master_secret(uint8_t * key); void crypto_load_master_secret(uint8_t * key);
extern const uint8_t attestation_cert_der[];
extern const uint16_t attestation_cert_der_size;
extern const uint8_t attestation_key[];
extern const uint16_t attestation_key_size;
#endif #endif

View File

@ -69,8 +69,6 @@ uint8_t ctap_get_info(CborEncoder * encoder)
CborEncoder map; CborEncoder map;
CborEncoder options; CborEncoder options;
CborEncoder pins; CborEncoder pins;
uint8_t aaguid[16];
device_read_aaguid(aaguid);
ret = cbor_encoder_create_map(encoder, &map, 6); ret = cbor_encoder_create_map(encoder, &map, 6);
check_ret(ret); check_ret(ret);
@ -107,7 +105,7 @@ uint8_t ctap_get_info(CborEncoder * encoder)
ret = cbor_encode_uint(&map, RESP_aaguid); ret = cbor_encode_uint(&map, RESP_aaguid);
check_ret(ret); check_ret(ret);
{ {
ret = cbor_encode_byte_string(&map, aaguid, 16); ret = cbor_encode_byte_string(&map, CTAP_AAGUID, 16);
check_ret(ret); check_ret(ret);
} }
@ -284,9 +282,13 @@ void make_auth_tag(uint8_t * rpIdHash, uint8_t * nonce, uint32_t count, uint8_t
memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE); memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
} }
void ctap_flush_state() void ctap_flush_state(int backup)
{ {
authenticator_write_state(&STATE); authenticator_write_state(&STATE, 0);
if (backup)
{
authenticator_write_state(&STATE, 1);
}
} }
static uint32_t auth_data_update_count(CTAP_authDataHeader * authData) static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
@ -310,7 +312,7 @@ static uint32_t auth_data_update_count(CTAP_authDataHeader * authData)
static void ctap_increment_rk_store() static void ctap_increment_rk_store()
{ {
STATE.rk_stored++; STATE.rk_stored++;
ctap_flush_state(); ctap_flush_state(1);
} }
static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2) static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
@ -507,7 +509,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0); cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0);
device_read_aaguid(authData->attest.aaguid); memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF; authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
authData->attest.credLenH = (sizeof(CredentialId) & 0xFF00) >> 8; authData->attest.credLenH = (sizeof(CredentialId) & 0xFF00) >> 8;
@ -632,17 +634,11 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len) uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len)
{ {
int ret; int ret;
uint8_t cert[1024];
uint16_t cert_size = device_attestation_cert_der_get_size();
if (cert_size > sizeof(cert)){
printf2(TAG_ERR,"Certificate is too large for CTAP2 buffer\r\n");
return CTAP2_ERR_PROCESSING;
}
device_attestation_read_cert_der(cert);
CborEncoder stmtmap; CborEncoder stmtmap;
CborEncoder x5carr; CborEncoder x5carr;
ret = cbor_encode_int(map,RESP_attStmt); ret = cbor_encode_int(map,RESP_attStmt);
check_ret(ret); check_ret(ret);
ret = cbor_encoder_create_map(map, &stmtmap, 3); ret = cbor_encoder_create_map(map, &stmtmap, 3);
@ -665,7 +661,7 @@ uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len)
ret = cbor_encoder_create_array(&stmtmap, &x5carr, 1); ret = cbor_encoder_create_array(&stmtmap, &x5carr, 1);
check_ret(ret); check_ret(ret);
{ {
ret = cbor_encode_byte_string(&x5carr, cert, device_attestation_cert_der_get_size()); ret = cbor_encode_byte_string(&x5carr, attestation_cert_der, attestation_cert_der_size);
check_ret(ret); check_ret(ret);
ret = cbor_encoder_close_container(&stmtmap, &x5carr); ret = cbor_encoder_close_container(&stmtmap, &x5carr);
check_ret(ret); check_ret(ret);
@ -1774,7 +1770,8 @@ static void ctap_state_init()
*/ */
void ctap_load_external_keys(uint8_t * keybytes){ void ctap_load_external_keys(uint8_t * keybytes){
memmove(STATE.key_space, keybytes, KEY_SPACE_BYTES); memmove(STATE.key_space, keybytes, KEY_SPACE_BYTES);
authenticator_write_state(&STATE); authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1);
crypto_load_master_secret(STATE.key_space); crypto_load_master_secret(STATE.key_space);
} }
@ -1788,18 +1785,30 @@ void ctap_init()
); );
crypto_ecc256_init(); crypto_ecc256_init();
int is_init = authenticator_read_state(&STATE); authenticator_read_state(&STATE);
device_set_status(CTAPHID_STATUS_IDLE); device_set_status(CTAPHID_STATUS_IDLE);
if (is_init) if (STATE.is_initialized == INITIALIZED_MARKER)
{ {
printf1(TAG_STOR,"Auth state is initialized\n"); printf1(TAG_STOR,"Auth state is initialized\n");
} }
else else
{ {
ctap_state_init(); printf1(TAG_STOR,"Auth state is NOT initialized. Initializing..\n");
authenticator_write_state(&STATE); if (authenticator_is_backup_initialized())
{
printf1(TAG_ERR,"Warning: memory corruption detected. restoring from backup..\n");
authenticator_read_backup_state(&STATE);
authenticator_write_state(&STATE, 0);
}
else
{
ctap_state_init();
authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1);
}
} }
do_migration_if_required(&STATE); do_migration_if_required(&STATE);
@ -1866,7 +1875,8 @@ void ctap_update_pin(uint8_t * pin, int len)
STATE.is_pin_set = 1; STATE.is_pin_set = 1;
authenticator_write_state(&STATE); authenticator_write_state(&STATE, 1);
authenticator_write_state(&STATE, 0);
printf1(TAG_CTAP, "New pin set: %s [%d]\n", pin, len); printf1(TAG_CTAP, "New pin set: %s [%d]\n", pin, len);
dump_hex1(TAG_ERR, STATE.PIN_CODE_HASH, sizeof(STATE.PIN_CODE_HASH)); dump_hex1(TAG_ERR, STATE.PIN_CODE_HASH, sizeof(STATE.PIN_CODE_HASH));
@ -1881,7 +1891,7 @@ uint8_t ctap_decrement_pin_attempts()
if (! ctap_device_locked()) if (! ctap_device_locked())
{ {
STATE.remaining_tries--; STATE.remaining_tries--;
ctap_flush_state(); ctap_flush_state(0);
printf1(TAG_CP, "ATTEMPTS left: %d\n", STATE.remaining_tries); printf1(TAG_CP, "ATTEMPTS left: %d\n", STATE.remaining_tries);
if (ctap_device_locked()) if (ctap_device_locked())
@ -1916,7 +1926,7 @@ void ctap_reset_pin_attempts()
{ {
STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS; STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS;
PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS; PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
ctap_flush_state(); ctap_flush_state(0);
} }
void ctap_reset_state() void ctap_reset_state()
@ -1990,7 +2000,7 @@ int8_t ctap_store_key(uint8_t index, uint8_t * key, uint16_t len)
memmove(STATE.key_space + offset, key, len); memmove(STATE.key_space + offset, key, len);
ctap_flush_state(); ctap_flush_state(1);
return 0; return 0;
} }
@ -2032,7 +2042,8 @@ void ctap_reset()
{ {
ctap_state_init(); ctap_state_init();
authenticator_write_state(&STATE); authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1);
if (ctap_generate_rng(PIN_TOKEN, PIN_TOKEN_SIZE) != 1) if (ctap_generate_rng(PIN_TOKEN, PIN_TOKEN_SIZE) != 1)
{ {
@ -2052,5 +2063,6 @@ void lock_device_permanently() {
printf1(TAG_CP, "Device locked!\n"); printf1(TAG_CP, "Device locked!\n");
authenticator_write_state(&STATE); authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1);
} }

View File

@ -19,6 +19,9 @@
#define CTAP_VENDOR_FIRST 0x40 #define CTAP_VENDOR_FIRST 0x40
#define CTAP_VENDOR_LAST 0xBF #define CTAP_VENDOR_LAST 0xBF
// AAGUID For Solo
#define CTAP_AAGUID ((uint8_t*)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79")
#define MC_clientDataHash 0x01 #define MC_clientDataHash 0x01
#define MC_rp 0x02 #define MC_rp 0x02
#define MC_user 0x03 #define MC_user 0x03

View File

@ -275,7 +275,7 @@ static void ctaphid_write(CTAPHID_WRITE_BUFFER * wb, void * _data, int len)
if (wb->offset > 0) if (wb->offset > 0)
{ {
memset(wb->buf + wb->offset, 0, HID_MESSAGE_SIZE - wb->offset); memset(wb->buf + wb->offset, 0, HID_MESSAGE_SIZE - wb->offset);
usbhid_send(wb->buf); ctaphid_write_block(wb->buf);
} }
return; return;
} }
@ -304,7 +304,7 @@ static void ctaphid_write(CTAPHID_WRITE_BUFFER * wb, void * _data, int len)
wb->bytes_written += 1; wb->bytes_written += 1;
if (wb->offset == HID_MESSAGE_SIZE) if (wb->offset == HID_MESSAGE_SIZE)
{ {
usbhid_send(wb->buf); ctaphid_write_block(wb->buf);
wb->offset = 0; wb->offset = 0;
} }
} }
@ -542,9 +542,6 @@ extern void _check_ret(CborError ret, int line, const char * filename);
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb); uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb);
extern void solo_lock_if_not_already();
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw) uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
{ {
uint8_t cmd = 0; uint8_t cmd = 0;
@ -638,9 +635,6 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
status = ctap_request(ctap_buffer, len, &ctap_resp); status = ctap_request(ctap_buffer, len, &ctap_resp);
wb.bcnt = (ctap_resp.length+1); wb.bcnt = (ctap_resp.length+1);
wb.cid = cid;
wb.cmd = cmd;
timestamp(); timestamp();
@ -671,9 +665,6 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp); u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp);
wb.bcnt = (ctap_resp.length); wb.bcnt = (ctap_resp.length);
wb.cid = cid;
wb.cmd = cmd;
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length); ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
ctaphid_write(&wb, NULL, 0); ctaphid_write(&wb, NULL, 0);
@ -705,7 +696,7 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
{ {
ctap_response_init(ctap_resp); ctap_response_init(ctap_resp);
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL)) #if !defined(IS_BOOTLOADER) && (defined(SOLO_HACKER) || defined(SOLO_EXPERIMENTAL))
uint32_t param; uint32_t param;
#endif #endif
#if defined(IS_BOOTLOADER) #if defined(IS_BOOTLOADER)
@ -719,20 +710,23 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
printf1(TAG_HID,"CTAPHID_BOOT\n"); printf1(TAG_HID,"CTAPHID_BOOT\n");
u2f_set_writeback_buffer(ctap_resp); u2f_set_writeback_buffer(ctap_resp);
is_busy = bootloader_bridge(len, ctap_buffer); is_busy = bootloader_bridge(len, ctap_buffer);
wb->bcnt = 1 + ctap_resp->length;
ctaphid_write(wb, &is_busy, 1); ctaphid_write(wb, &is_busy, 1);
ctaphid_write(wb, ctap_resp->data, ctap_resp->length); ctaphid_write(wb, ctap_resp->data, ctap_resp->length);
ctaphid_write(wb, NULL, 0); ctaphid_write(wb, NULL, 0);
return 1; return 1;
#endif #endif
#if defined(SOLO) #if defined(SOLO_HACKER)
case CTAPHID_ENTERBOOT: case CTAPHID_ENTERBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n"); printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_solo_bootloader(); boot_solo_bootloader();
wb->bcnt = 0; wb->bcnt = 0;
ctaphid_write(wb, NULL, 0); ctaphid_write(wb, NULL, 0);
return 1; return 1;
case CTAPHID_ENTERSTBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_st_bootloader();
return 1;
#endif #endif
#if !defined(IS_BOOTLOADER) #if !defined(IS_BOOTLOADER)
@ -751,31 +745,16 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
case CTAPHID_GETVERSION: case CTAPHID_GETVERSION:
printf1(TAG_HID,"CTAPHID_GETVERSION\n"); printf1(TAG_HID,"CTAPHID_GETVERSION\n");
wb->bcnt = 4; wb->bcnt = 3;
ctap_buffer[0] = SOLO_VERSION_MAJ; ctap_buffer[0] = SOLO_VERSION_MAJ;
ctap_buffer[1] = SOLO_VERSION_MIN; ctap_buffer[1] = SOLO_VERSION_MIN;
ctap_buffer[2] = SOLO_VERSION_PATCH; ctap_buffer[2] = SOLO_VERSION_PATCH;
#if defined(SOLO) ctaphid_write(wb, ctap_buffer, 3);
ctap_buffer[3] = solo_is_locked();
#else
ctap_buffer[3] = 0;
#endif
ctaphid_write(wb, ctap_buffer, 4);
ctaphid_write(wb, NULL, 0); ctaphid_write(wb, NULL, 0);
return 1; return 1;
break; break;
// Remove on next release #if !defined(IS_BOOTLOADER) && (defined(SOLO_HACKER) || defined(SOLO_EXPERIMENTAL))
#if !defined(IS_BOOTLOADER) && defined(SOLO)
case 0x99:
solo_lock_if_not_already();
wb->bcnt = 0;
ctaphid_write(wb, NULL, 0);
return 1;
break;
#endif
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
case CTAPHID_LOADKEY: case CTAPHID_LOADKEY:
/** /**
* Load external key. Useful for enabling backups. * Load external key. Useful for enabling backups.

View File

@ -59,8 +59,6 @@
#define CTAP_CAPABILITIES (CAPABILITY_WINK | CAPABILITY_CBOR) #define CTAP_CAPABILITIES (CAPABILITY_WINK | CAPABILITY_CBOR)
#define HID_MESSAGE_SIZE 64
typedef struct typedef struct
{ {
uint32_t cid; uint32_t cid;

View File

@ -56,7 +56,8 @@ bool migrate_from_FF_to_01(AuthenticatorState_0xFF* state_prev_0xff, Authenticat
void save_migrated_state(AuthenticatorState *state_tmp_ptr) { void save_migrated_state(AuthenticatorState *state_tmp_ptr) {
memmove(&STATE, state_tmp_ptr, sizeof(AuthenticatorState)); memmove(&STATE, state_tmp_ptr, sizeof(AuthenticatorState));
authenticator_write_state(state_tmp_ptr); authenticator_write_state(state_tmp_ptr, 0);
authenticator_write_state(state_tmp_ptr, 1);
} }
void do_migration_if_required(AuthenticatorState* state_current){ void do_migration_if_required(AuthenticatorState* state_current){

View File

@ -1,209 +0,0 @@
// Copyright 2019 SoloKeys Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
/** device.c
*
* This contains (weak) implementations
* to get FIDO2 working initially on a device. They probably
* aren't what you want to keep, but are designed to be replaced
* with some other platform specific implementation.
*
* For real examples, see the STM32L4 implementation and the PC implementation of device.c.
*
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "ctaphid.h"
#include "log.h"
#include APP_CONFIG
#define RK_NUM 50
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
} RK_STORE;
static bool _up_disabled = false;
static uint8_t _attestation_cert_der[] =
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
__attribute__((weak)) void device_attestation_read_cert_der(uint8_t * dst){
memmove(dst, _attestation_cert_der, device_attestation_cert_der_get_size());
}
__attribute__((weak)) uint8_t * device_get_attestation_key(){
static uint8_t attestation_key[] =
"\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa"
"\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46"
"\xb7\x2e\x5f\xe7\x5d\x30";
return attestation_key;
}
__attribute__((weak)) uint16_t device_attestation_cert_der_get_size(){
return sizeof(_attestation_cert_der)-1;
}
__attribute__((weak)) void device_reboot()
{
printf1(TAG_RED, "REBOOT command recieved!\r\n");
exit(100);
}
__attribute__((weak)) void device_set_status(uint32_t status)
{
static uint32_t __device_status = 0;
if (status != CTAPHID_STATUS_IDLE && __device_status != status)
{
ctaphid_update_status(status);
}
__device_status = status;
}
__attribute__((weak)) void usbhid_close(){/**/}
__attribute__((weak)) void device_init(int argc, char *argv[]){/**/}
__attribute__((weak)) void device_disable_up(bool disable)
{
_up_disabled = disable;
}
__attribute__((weak)) int ctap_user_presence_test(uint32_t d)
{
if (_up_disabled)
{
return 2;
}
return 1;
}
__attribute__((weak)) int ctap_user_verification(uint8_t arg)
{
return 1;
}
__attribute__((weak)) uint32_t ctap_atomic_count(uint32_t amount)
{
static uint32_t counter1 = 25;
counter1 += (amount + 1);
return counter1;
}
__attribute__((weak)) int ctap_generate_rng(uint8_t * dst, size_t num)
{
int i;
printf1(TAG_ERR, "Insecure RNG being used.\r\n");
for (i = 0; i < num; i++){
dst[i] = (uint8_t)rand();
}
}
__attribute__((weak)) int device_is_nfc()
{
return 0;
}
__attribute__((weak)) void device_wink()
{
printf1(TAG_GREEN,"*WINK*\n");
}
__attribute__((weak)) void device_set_clock_rate(DEVICE_CLOCK_RATE param){/**/}
static AuthenticatorState _tmp_state = {0};
__attribute__((weak)) int authenticator_read_state(AuthenticatorState * s){
if (_tmp_state.is_initialized != INITIALIZED_MARKER){
return 0;
}
else {
memmove(s, &_tmp_state, sizeof(AuthenticatorState));
return 1;
}
}
__attribute__((weak)) void authenticator_write_state(AuthenticatorState * s){
memmove(&_tmp_state, s, sizeof(AuthenticatorState));
}
__attribute__((weak)) void ctap_reset_rk()
{
memset(&RK_STORE,0xff,sizeof(RK_STORE));
}
__attribute__((weak)) uint32_t ctap_rk_size()
{
return RK_NUM;
}
__attribute__((weak)) void ctap_store_rk(int index, CTAP_residentKey * rk)
{
if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
}
else
{
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
}
}
__attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk)
{
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
}
__attribute__((weak)) void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
{
if (index < RK_NUM)
{
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
}
else
{
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
}
}
__attribute__((weak)) void device_read_aaguid(uint8_t * dst){
uint8_t * aaguid = (uint8_t *)"\x00\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
memmove(dst, aaguid, 16);
}

View File

@ -9,159 +9,82 @@
#include "storage.h" #include "storage.h"
/** Return a millisecond timestamp. Does not need to be synchronized to anything. void device_init(int argc, char *argv[]);
* *Optional* to compile, but will not calculate delays correctly without a correct implementation.
*/
uint32_t millis(); uint32_t millis();
void delay(uint32_t ms);
// HID message size in bytes
#define HID_MESSAGE_SIZE 64
void usbhid_init();
int usbhid_recv(uint8_t * msg);
/** Called by HIDUSB layer to write bytes to the USB HID interface endpoint.
* Will write 64 bytes at a time.
*
* @param msg Pointer to a 64 byte buffer containing a payload to be sent via USB HID.
*
* **Required** to compile and work for FIDO application.
*/
void usbhid_send(uint8_t * msg); void usbhid_send(uint8_t * msg);
void usbhid_close();
void main_loop_delay();
void heartbeat();
/** Reboot / power reset the device.
* **Optional** this is not used for FIDO2, and simply won't do anything if not implemented.
*/
void device_reboot(); void device_reboot();
/** Read AuthenticatorState from nonvolatile memory. void authenticator_read_state(AuthenticatorState * );
* @param s pointer to AuthenticatorState buffer to be overwritten with contents from NV memory.
* @return 0 - state stored is NOT initialized.
* 1 - state stored is initialized.
*
* *Optional* this is required to make persistant updates to FIDO2 State (PIN and device master secret).
* Without it, changes simply won't be persistant.
*/
int authenticator_read_state(AuthenticatorState * s);
/** Store changes in the authenticator state to nonvolatile memory. void authenticator_read_backup_state(AuthenticatorState * );
* @param s pointer to valid Authenticator state to write to NV memory.
* // Return 1 yes backup is init'd, else 0
* *Optional* this is required to make persistant updates to FIDO2 State (PIN and device master secret). //void authenticator_initialize()
* Without it, changes simply won't be persistant. int authenticator_is_backup_initialized();
*/
void authenticator_write_state(AuthenticatorState * s); void authenticator_write_state(AuthenticatorState *, int backup);
// Called each main loop. Doesn't need to do anything.
void device_manage();
// sets status that's uses for sending status updates ~100ms. // sets status that's uses for sending status updates ~100ms.
// A timer should be set up to call `ctaphid_update_status` // A timer should be set up to call `ctaphid_update_status`
/** Updates status of the status of the FIDO2 layer application, which
* can be used for polling updates in the USBHID layer.
*
* @param status is one of the following, which can be used appropriately by USB HID layer.
#define CTAPHID_STATUS_IDLE 0
#define CTAPHID_STATUS_PROCESSING 1
#define CTAPHID_STATUS_UPNEEDED 2
*
* *Optional* to compile and run, but will be required to be used for proper FIDO2 operation with some platforms.
*/
void device_set_status(uint32_t status); void device_set_status(uint32_t status);
/** Returns true if button is currently pressed. Debouncing does not need to be handled. Should not block. // Returns if button is currently pressed
* @return 1 if button is currently pressed.
*
* *Optional* to compile and run, but just returns one by default.
*/
int device_is_button_pressed(); int device_is_button_pressed();
// // Test for user presence
// Return 2 for disabled, 1 for user is present, 0 user not present, -1 if cancel is requested. // Return 2 for disabled, 1 for user is present, 0 user not present, -1 if cancel is requested.
/** Test for user presence.
* Perform test that user is present. Returns status on user presence. This is used by FIDO and U2F layer
* to check if an operation should continue, or if the UP flag should be set.
*
* @param delay number of milliseconds to delay waiting for user before timeout.
*
* @return 2 - User presence is disabled. Operation should continue, but UP flag not set.
* 1 - User presence confirmed. Operation should continue, and UP flag is set.
* 0 - User presence is not confirmed. Operation should be denied.
* -1 - Operation was canceled. Do not continue, reset transaction state.
*
* *Optional*, the default implementation will return 1, unless a FIDO2 operation calls for no UP, where this will then return 2.
*/
int ctap_user_presence_test(uint32_t delay); int ctap_user_presence_test(uint32_t delay);
/** Disable the next user presence test. This is called by FIDO2 layer when a transaction // Generate @num bytes of random numbers to @dest
* requests UP to be disabled. The next call to ctap_user_presence_test should return 2, // return 1 if success, error otherwise
* and then UP should be enabled again.
*
* @param request_active indicate to activate (true) or disable (false) UP.
*
* *Optional*, the default implementation will provide expected behaviour with the default ctap_user_presence_test(...).
*/
void device_disable_up(bool request_active);
/** Generate random numbers. Random numbers should be good enough quality for
* cryptographic use.
*
* @param dst the buffer to write into.
* @param num the number of bytes to generate and write to dst.
*
* @return 1 if successful, or else the RNG failed.
*
* *Optional*, if not implemented, the random numbers will be from rand() and an error will be logged.
*/
int ctap_generate_rng(uint8_t * dst, size_t num); int ctap_generate_rng(uint8_t * dst, size_t num);
/** Increment an atomic (non-volatile) counter and return the value. // Increment atomic counter and return it.
* // @param amount the amount to increase the counter by.
* @param amount a non-zero amount to increment the counter by.
*
* *Optional*, if not implemented, the counter will not be persistant.
*/
uint32_t ctap_atomic_count(uint32_t amount); uint32_t ctap_atomic_count(uint32_t amount);
/** Delete all resident keys. // Verify the user
* // return 1 if user is verified, 0 if not
* *Optional*, if not implemented, operates on non-persistant RK's. int ctap_user_verification(uint8_t arg);
*/
// Must be implemented by application
// data is HID_MESSAGE_SIZE long in bytes
void ctaphid_write_block(uint8_t * data);
// Resident key
void ctap_reset_rk(); void ctap_reset_rk();
/** Return the maximum amount of resident keys that can be stored.
* @return max number of resident keys that can be stored, including already stored RK's.
*
* *Optional*, if not implemented, returns 50.
*/
uint32_t ctap_rk_size(); uint32_t ctap_rk_size();
/** Store a resident key into an index between [ 0, ctap_rk_size() ).
* Storage should be in non-volatile memory.
*
* @param index between RK index range.
* @param rk pointer to valid rk structure that should be written to NV memory.
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
void ctap_store_rk(int index,CTAP_residentKey * rk); void ctap_store_rk(int index,CTAP_residentKey * rk);
/** Read a resident key from an index into memory
* @param index to read resident key from.
* @param rk pointer to resident key structure to write into with RK.
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
void ctap_load_rk(int index,CTAP_residentKey * rk); void ctap_load_rk(int index,CTAP_residentKey * rk);
/** Overwrite the RK located in index with a new RK.
* @param index to write resident key to.
* @param rk pointer to valid rk structure that should be written to NV memory, and replace existing RK there.
*
* *Optional*, if not implemented, operates on non-persistant RK's.
*/
void ctap_overwrite_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();
/** Called by HID layer to indicate that a wink behavior should be performed. // HID wink command
* Should not block, and the wink behavior should occur in parallel to FIDO operations.
*
* *Optional*.
*/
void device_wink(); void device_wink();
typedef enum { typedef enum {
@ -170,50 +93,21 @@ typedef enum {
DEVICE_FAST = 2, DEVICE_FAST = 2,
} DEVICE_CLOCK_RATE; } DEVICE_CLOCK_RATE;
/** // Set the clock rate for the device.
* Set the clock rate for the device. This gets called only when the device is running in NFC mode. // Three modes are targetted for Solo.
* Before Register and authenticate operations, the clock rate will be set to (1), and otherwise back to (0). // 0: Lowest clock rate for NFC.
* @param param // 1: fastest clock rate supported at a low power setting for NFC FIDO.
0: Lowest clock rate for NFC. // 2: fastest clock rate. Generally for USB interface.
1: fastest clock rate supported at a low power setting for NFC FIDO.
2: fastest clock rate. Generally for USB interface.
* *Optional*, by default nothing happens.
*/
void device_set_clock_rate(DEVICE_CLOCK_RATE param); void device_set_clock_rate(DEVICE_CLOCK_RATE param);
// Returns NFC_IS_NA, NFC_IS_ACTIVE, or NFC_IS_AVAILABLE // Returns NFC_IS_NA, NFC_IS_ACTIVE, or NFC_IS_AVAILABLE
#define NFC_IS_NA 0 #define NFC_IS_NA 0
#define NFC_IS_ACTIVE 1 #define NFC_IS_ACTIVE 1
#define NFC_IS_AVAILABLE 2 #define NFC_IS_AVAILABLE 2
/** Returns NFC status of the device.
* @return 0 - NFC is not available.
* 1 - NFC is active, and is powering the chip for a transaction.
* 2 - NFC is available, but not currently being used.
*/
int device_is_nfc(); int device_is_nfc();
void device_disable_up(bool request_active);
/** Return pointer to attestation key. void device_init_button();
* @return pointer to attestation private key, raw encoded. For P256, this is 32 bytes.
*/
uint8_t * device_get_attestation_key();
/** Read the device's attestation certificate into buffer @dst.
* @param dst the destination to write the certificate.
*
* The size of the certificate can be retrieved using `device_attestation_cert_der_get_size()`.
*/
void device_attestation_read_cert_der(uint8_t * dst);
/** Returns the size in bytes of attestation_cert_der.
* @return number of bytes in attestation_cert_der, not including any C string null byte.
*/
uint16_t device_attestation_cert_der_get_size();
/** Read the device's 16 byte AAGUID into a buffer.
* @param dst buffer to write 16 byte AAGUID into.
* */
void device_read_aaguid(uint8_t * dst);
#endif #endif

View File

@ -1,41 +0,0 @@
// Copyright 2019 SoloKeys Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
#ifndef SRC_APP_H_
#define SRC_APP_H_
#include <stdbool.h>
#define USING_DEV_BOARD
#define USING_PC
#define ENABLE_U2F
#define ENABLE_U2F_EXTENSIONS
//#define BRIDGE_TO_WALLET
void printing_init();
extern bool use_udp;
// 0xRRGGBB
#define LED_INIT_VALUE 0x000800
#define LED_WINK_VALUE 0x000008
#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

@ -7,10 +7,7 @@
#ifndef _LOG_H #ifndef _LOG_H
#define _LOG_H #define _LOG_H
#ifdef APP_CONFIG
#include APP_CONFIG #include APP_CONFIG
#endif
#include <stdint.h> #include <stdint.h>
#ifndef DEBUG_LEVEL #ifndef DEBUG_LEVEL
@ -53,7 +50,7 @@ typedef enum
TAG_FILENO = (1UL << 31) TAG_FILENO = (1UL << 31)
} LOG_TAG; } LOG_TAG;
#if defined(DEBUG_LEVEL) && DEBUG_LEVEL > 0 #if DEBUG_LEVEL > 0
void set_logging_mask(uint32_t mask); void set_logging_mask(uint32_t mask);
#define printf1(tag,fmt, ...) LOG(tag & ~(TAG_FILENO), NULL, 0, fmt, ##__VA_ARGS__) #define printf1(tag,fmt, ...) LOG(tag & ~(TAG_FILENO), NULL, 0, fmt, ##__VA_ARGS__)

View File

@ -9,7 +9,6 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <unistd.h> #include <unistd.h>
#include <time.h>
#include "cbor.h" #include "cbor.h"
#include "device.h" #include "device.h"
@ -18,12 +17,9 @@
#include "util.h" #include "util.h"
#include "log.h" #include "log.h"
#include "ctap.h" #include "ctap.h"
#include "app.h" #include APP_CONFIG
void device_init(int argc, char *argv[]);
int usbhid_recv(uint8_t * msg);
#if !defined(TEST)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -33,21 +29,20 @@ int main(int argc, char *argv[])
set_logging_mask( set_logging_mask(
/*0*/ /*0*/
// TAG_GEN| //TAG_GEN|
// TAG_MC | // TAG_MC |
// TAG_GA | // TAG_GA |
TAG_WALLET | TAG_WALLET |
TAG_STOR | TAG_STOR |
//TAG_NFC_APDU | //TAG_NFC_APDU |
TAG_NFC | TAG_NFC |
// TAG_CP | //TAG_CP |
// TAG_CTAP| // TAG_CTAP|
// TAG_HID| //TAG_HID|
TAG_U2F| TAG_U2F|
// TAG_PARSE | //TAG_PARSE |
//TAG_TIME| //TAG_TIME|
// TAG_DUMP| // TAG_DUMP|
// TAG_DUMP2|
TAG_GREEN| TAG_GREEN|
TAG_RED| TAG_RED|
TAG_EXT| TAG_EXT|
@ -62,6 +57,13 @@ int main(int argc, char *argv[])
while(1) while(1)
{ {
if (millis() - t1 > HEARTBEAT_PERIOD)
{
heartbeat();
t1 = millis();
}
device_manage();
if (usbhid_recv(hidmsg) > 0) if (usbhid_recv(hidmsg) > 0)
{ {
@ -71,16 +73,14 @@ int main(int argc, char *argv[])
else else
{ {
} }
ctaphid_check_timeouts(); ctaphid_check_timeouts();
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 1000*1000*10;
nanosleep(&ts,NULL);
} }
// Should never get here // Should never get here
usbhid_close();
printf1(TAG_GREEN, "done\n"); printf1(TAG_GREEN, "done\n");
return 0; return 0;
} }
#endif

View File

@ -299,19 +299,14 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
static int16_t u2f_register(struct u2f_register_request * req) static int16_t u2f_register(struct u2f_register_request * req)
{ {
uint8_t i[] = {0x0,U2F_EC_FMT_UNCOMPRESSED}; uint8_t i[] = {0x0,U2F_EC_FMT_UNCOMPRESSED};
uint8_t cert[1024];
struct u2f_key_handle key_handle; struct u2f_key_handle key_handle;
uint8_t pubkey[64]; uint8_t pubkey[64];
uint8_t hash[32]; uint8_t hash[32];
uint8_t * sig = (uint8_t*)req; uint8_t * sig = (uint8_t*)req;
const uint16_t attest_size = device_attestation_cert_der_get_size(); const uint16_t attest_size = attestation_cert_der_size;
if (attest_size > sizeof(cert)){
printf2(TAG_ERR,"Certificate is too large for buffer\r\n");
return U2F_SW_INSUFFICIENT_MEMORY;
}
if ( ! ctap_user_presence_test(750)) if ( ! ctap_user_presence_test(750))
{ {
@ -346,8 +341,7 @@ static int16_t u2f_register(struct u2f_register_request * req)
u2f_response_writeback(i,1); u2f_response_writeback(i,1);
u2f_response_writeback((uint8_t*)&key_handle,U2F_KEY_HANDLE_SIZE); u2f_response_writeback((uint8_t*)&key_handle,U2F_KEY_HANDLE_SIZE);
device_attestation_read_cert_der(cert); u2f_response_writeback(attestation_cert_der,attest_size);
u2f_response_writeback(cert,attest_size);
dump_signature_der(sig); dump_signature_der(sig);

View File

@ -1,9 +0,0 @@
SOLO_VERSION_FULL?=$(shell git describe)
SOLO_VERSION:=$(shell python -c 'print("$(SOLO_VERSION_FULL)".split("-")[0])')
SOLO_VERSION_MAJ:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[0])')
SOLO_VERSION_MIN:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[1])')
SOLO_VERSION_PAT:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[2])')
SOLO_VERSION_FLAGS := -DSOLO_VERSION_MAJ=$(SOLO_VERSION_MAJ) -DSOLO_VERSION_MIN=$(SOLO_VERSION_MIN) \
-DSOLO_VERSION_PATCH=$(SOLO_VERSION_PAT) -DSOLO_VERSION=\"$(SOLO_VERSION_FULL)\"

View File

@ -1,10 +1,14 @@
#!/bin/bash -xe #!/bin/bash -xe
version=$1
export PREFIX=/opt/gcc-arm-none-eabi-8-2019-q3-update/bin/ version=${1:-master}
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
cd /solo/targets/stm32l432 cd /solo/targets/stm32l432
ls git fetch --tags
git checkout ${version}
git submodule update --init --recursive
version=$(git describe)
make cbor make cbor
@ -12,12 +16,13 @@ out_dir="/builds"
function build() { function build() {
part=${1} part=${1}
output=${2} variant=${2}
what="${part}" output=${3:-${part}}
what="${part}-${variant}"
make full-clean make full-clean
make ${what} VERSION_FULL=${version} make ${what}
out_hex="${what}-${version}.hex" out_hex="${what}-${version}.hex"
out_sha2="${what}-${version}.sha2" out_sha2="${what}-${version}.sha2"
@ -27,27 +32,24 @@ function build() {
cp ${out_hex} ${out_sha2} ${out_dir} cp ${out_hex} ${out_sha2} ${out_dir}
} }
build bootloader-nonverifying bootloader build bootloader nonverifying
build bootloader-verifying bootloader build bootloader verifying
build firmware solo build firmware hacker solo
build firmware-debug-1 solo build firmware hacker-debug-1 solo
build firmware-debug-2 solo build firmware hacker-debug-2 solo
build firmware solo build firmware secure solo
build firmware secure-non-solokeys solo
pip install -U pip
pip install -U solo-python
cd ${out_dir} cd ${out_dir}
bundle="bundle-hacker-${version}" bundle="bundle-hacker-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-${version}.hex ${bundle}.hex /opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2 sha256sum ${bundle}.hex > ${bundle}.sha2
bundle="bundle-hacker-debug-1-${version}" bundle="bundle-hacker-debug-1-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-debug-1-${version}.hex ${bundle}.hex /opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2
bundle="bundle-hacker-debug-2-${version}" bundle="bundle-hacker-debug-2-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-debug-2-${version}.hex ${bundle}.hex /opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2
bundle="bundle-secure-non-solokeys-${version}" bundle="bundle-secure-non-solokeys-${version}"
/opt/conda/bin/solo mergehex --lock bootloader-verifying-${version}.hex firmware-${version}.hex ${bundle}.hex /opt/conda/bin/solo mergehex bootloader-verifying-${version}.hex firmware-secure-non-solokeys-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2 sha256sum ${bundle}.hex > ${bundle}.sha2

View File

@ -15,7 +15,8 @@
"authenticationAlgorithm": 1, "authenticationAlgorithm": 1,
"publicKeyAlgAndEncoding": 260, "publicKeyAlgAndEncoding": 260,
"attestationTypes": [ "attestationTypes": [
15879 15879,
15880
], ],
"userVerificationDetails": [ "userVerificationDetails": [
[ [

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -15,10 +15,8 @@ nav:
- Bootloader mode: solo/bootloader-mode.md - Bootloader mode: solo/bootloader-mode.md
- Customization: solo/customization.md - Customization: solo/customization.md
- Solo Extras: solo/solo-extras.md - Solo Extras: solo/solo-extras.md
- Application Ideas: solo/application-ideas.md
- Running on Nucleo32 board: solo/nucleo32-board.md - Running on Nucleo32 board: solo/nucleo32-board.md
- Signed update process: solo/signed-updates.md - Signed update process: solo/signed-updates.md
- Usage and Porting guide: solo/porting.md
- Code documentation: solo/code-overview.md - Code documentation: solo/code-overview.md
- Contributing Code: solo/contributing.md - Contributing Code: solo/contributing.md
- Contributing Docs: solo/documenting.md - Contributing Docs: solo/documenting.md

View File

@ -11,6 +11,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/types.h> #include <sys/types.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <time.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <signal.h> #include <signal.h>
@ -24,7 +25,8 @@
#define RK_NUM 50 #define RK_NUM 50
static bool use_udp = true; bool use_udp = true;
static bool _up_disabled = false;
struct ResidentKeyStore { struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM]; CTAP_residentKey rks[RK_NUM];
@ -32,6 +34,21 @@ struct ResidentKeyStore {
void authenticator_initialize(); void authenticator_initialize();
uint32_t __device_status = 0;
void device_set_status(uint32_t status)
{
if (status != CTAPHID_STATUS_IDLE && __device_status != status)
{
ctaphid_update_status(status);
}
__device_status = status;
}
void device_reboot()
{
printf1(TAG_RED, "REBOOT command recieved!\r\n");
exit(100);
}
int udp_server() int udp_server()
{ {
@ -175,6 +192,7 @@ int usbhid_recv(uint8_t * msg)
return l; return l;
} }
// Send 64 byte USB HID message
void usbhid_send(uint8_t * msg) void usbhid_send(uint8_t * msg)
{ {
if (use_udp) if (use_udp)
@ -191,8 +209,6 @@ void usbhid_send(uint8_t * msg)
} }
} }
void usbhid_close() void usbhid_close()
{ {
close(fd); close(fd);
@ -256,6 +272,14 @@ void device_init(int argc, char *argv[])
} }
void main_loop_delay()
{
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 1000*1000*100;
nanosleep(&ts,NULL);
}
void delay(uint32_t ms) void delay(uint32_t ms)
{ {
struct timespec ts; struct timespec ts;
@ -265,6 +289,40 @@ void delay(uint32_t ms)
} }
void heartbeat()
{
}
void ctaphid_write_block(uint8_t * data)
{
/*printf("<< "); dump_hex(data, 64);*/
usbhid_send(data);
}
int ctap_user_presence_test(uint32_t d)
{
if (_up_disabled)
{
return 2;
}
return 1;
}
int ctap_user_verification(uint8_t arg)
{
return 1;
}
uint32_t ctap_atomic_count(uint32_t amount)
{
static uint32_t counter1 = 25;
counter1 += (amount + 1);
return counter1;
}
int ctap_generate_rng(uint8_t * dst, size_t num) int ctap_generate_rng(uint8_t * dst, size_t num)
{ {
int ret; int ret;
@ -286,9 +344,10 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
const char * state_file = "authenticator_state.bin"; const char * state_file = "authenticator_state.bin";
const char * backup_file = "authenticator_state2.bin";
const char * rk_file = "resident_keys.bin"; const char * rk_file = "resident_keys.bin";
int authenticator_read_state(AuthenticatorState * state) void authenticator_read_state(AuthenticatorState * state)
{ {
FILE * f; FILE * f;
int ret; int ret;
@ -307,35 +366,104 @@ int authenticator_read_state(AuthenticatorState * state)
perror("fwrite"); perror("fwrite");
exit(1); exit(1);
} }
if (state->is_initialized == INITIALIZED_MARKER)
return 1;
else
return 0;
} }
void authenticator_read_backup_state(AuthenticatorState * state )
void authenticator_write_state(AuthenticatorState * state)
{ {
FILE * f; FILE * f;
int ret; int ret;
f = fopen(state_file, "wb+"); f = fopen(backup_file, "rb");
if (f== NULL) if (f== NULL)
{ {
perror("fopen"); perror("fopen");
exit(1); exit(1);
} }
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
ret = fread(state, 1, sizeof(AuthenticatorState), f);
fclose(f); fclose(f);
if (ret != sizeof(AuthenticatorState)) if(ret != sizeof(AuthenticatorState))
{
perror("fwrite");
exit(1);
}
}
void authenticator_write_state(AuthenticatorState * state, int backup)
{
FILE * f;
int ret;
if (! backup)
{
f = fopen(state_file, "wb+");
if (f== NULL)
{
perror("fopen");
exit(1);
}
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
fclose(f);
if (ret != sizeof(AuthenticatorState))
{
perror("fwrite");
exit(1);
}
}
else
{
f = fopen(backup_file, "wb+");
if (f== NULL)
{
perror("fopen");
exit(1);
}
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
fclose(f);
if (ret != sizeof(AuthenticatorState))
{
perror("fwrite");
exit(1);
}
}
}
// Return 1 yes backup is init'd, else 0
int authenticator_is_backup_initialized()
{
uint8_t header[16];
AuthenticatorState * state = (AuthenticatorState*) header;
FILE * f;
int ret;
printf("state file exists\n");
f = fopen(backup_file, "rb");
if (f== NULL)
{
printf("Warning, backup file doesn't exist\n");
return 0;
}
ret = fread(header, 1, sizeof(header), f);
fclose(f);
if(ret != sizeof(header))
{ {
perror("fwrite"); perror("fwrite");
exit(1); exit(1);
} }
return state->is_initialized == INITIALIZED_MARKER;
} }
// Return 1 yes backup is init'd, else 0
/*int authenticator_is_initialized()*/
/*{*/
/*}*/
static void sync_rk() static void sync_rk()
{ {
@ -415,18 +543,44 @@ void authenticator_initialize()
exit(1); exit(1);
} }
f = fopen(backup_file, "wb+");
if (f== NULL)
{
perror("fopen");
exit(1);
}
mem = malloc(sizeof(AuthenticatorState));
memset(mem,0xff,sizeof(AuthenticatorState));
ret = fwrite(mem, 1, sizeof(AuthenticatorState), f);
free(mem);
fclose(f);
if (ret != sizeof(AuthenticatorState))
{
perror("fwrite");
exit(1);
}
// resident_keys // resident_keys
memset(&RK_STORE,0xff,sizeof(RK_STORE)); memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk(); sync_rk();
} }
} }
void device_manage()
{
}
void ctap_reset_rk() void ctap_reset_rk()
{ {
memset(&RK_STORE,0xff,sizeof(RK_STORE)); memset(&RK_STORE,0xff,sizeof(RK_STORE));
sync_rk(); sync_rk();
} }
uint32_t ctap_rk_size() uint32_t ctap_rk_size()
@ -468,9 +622,22 @@ void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
} }
} }
void device_wink()
{
printf("*WINK*\n");
}
int device_is_nfc()
{
return 0;
}
void device_disable_up(bool disable)
{
_up_disabled = disable;
}
void device_set_clock_rate(DEVICE_CLOCK_RATE param)
{
}

View File

@ -2,9 +2,8 @@ ifndef DEBUG
DEBUG=0 DEBUG=0
endif endif
VERSION_FULL?=$(shell git describe) APPMAKE=build/application.mk
APPMAKE=build/application.mk VERSION_FULL=${VERSION_FULL} BOOTMAKE=build/bootloader.mk
BOOTMAKE=build/bootloader.mk VERSION_FULL=${VERSION_FULL}
merge_hex=solo mergehex merge_hex=solo mergehex
@ -13,14 +12,20 @@ merge_hex=solo mergehex
# The following are the main targets for reproducible builds. # The following are the main targets for reproducible builds.
# TODO: better explanation # TODO: better explanation
firmware: firmware-hacker:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-debug-1: firmware-hacker-debug-1:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1 $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-debug-2: firmware-hacker-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-secure-non-solokeys:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DFLASH_ROP=2'
firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
bootloader-nonverifying: bootloader-nonverifying:
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) EXTRA_DEFINES='-DSOLO_HACKER' DEBUG=0 $(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) EXTRA_DEFINES='-DSOLO_HACKER' DEBUG=0
@ -90,7 +95,7 @@ flashboot: bootloader.hex
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
flash-firmware: flash-firmware:
$(SZ) -A solo.elf arm-none-eabi-size -A solo.elf
solo program aux enter-bootloader solo program aux enter-bootloader
solo program bootloader solo.hex solo program bootloader solo.hex

View File

@ -50,15 +50,12 @@ typedef struct {
uint8_t payload[255 - 10]; uint8_t payload[255 - 10];
} __attribute__((packed)) BootloaderReq; } __attribute__((packed)) BootloaderReq;
uint8_t * last_written_app_address = 0;
/** /**
* Erase all application pages. **APPLICATION_END_PAGE excluded**. * Erase all application pages. **APPLICATION_END_PAGE excluded**.
*/ */
static void erase_application() static void erase_application()
{ {
int page; int page;
last_written_app_address = (uint8_t*) 0;
for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++) for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++)
{ {
flash_erase_page(page); flash_erase_page(page);
@ -109,20 +106,17 @@ int is_bootloader_disabled()
uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4); uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4);
return *auth == 0; return *auth == 0;
} }
uint8_t * last_written_app_address;
#include "version.h" #include "version.h"
bool is_firmware_version_newer_or_equal() bool is_firmware_version_newer_or_equal()
{ {
if (last_written_app_address == 0) {
return false;
}
printf1(TAG_BOOT,"Current firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n", printf1(TAG_BOOT,"Current firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved, current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved,
current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved current_firmware_version.major, current_firmware_version.minor, current_firmware_version.patch, current_firmware_version.reserved
); );
volatile version_t * new_version = ((volatile version_t *) (last_written_app_address-8+4)); volatile version_t * new_version = ((volatile version_t *) last_written_app_address);
printf1(TAG_BOOT,"Uploaded firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n", printf1(TAG_BOOT,"Uploaded firmware version: %u.%u.%u.%u (%02x.%02x.%02x.%02x)\r\n",
new_version->major, new_version->minor, new_version->patch, new_version->reserved, new_version->major, new_version->minor, new_version->patch, new_version->reserved,
new_version->major, new_version->minor, new_version->patch, new_version->reserved new_version->major, new_version->minor, new_version->patch, new_version->reserved
@ -176,7 +170,6 @@ int bootloader_bridge(int klen, uint8_t * keyh)
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000; uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
uint32_t * ptr = (uint32_t *)addr; uint32_t * ptr = (uint32_t *)addr;
uint32_t current_address;
switch(req->op){ switch(req->op){
case BootWrite: case BootWrite:
@ -203,16 +196,9 @@ int bootloader_bridge(int klen, uint8_t * keyh)
printf2(TAG_ERR, "Error, boot check bypassed\n"); printf2(TAG_ERR, "Error, boot check bypassed\n");
exit(1); exit(1);
} }
current_address = addr + len;
if (current_address < (uint32_t) last_written_app_address) {
printf2(TAG_ERR, "Error, only ascending writes allowed.\n");
has_erased = 0;
return CTAP2_ERR_NOT_ALLOWED;
}
last_written_app_address = (uint8_t*) current_address;
// Do the actual write // Do the actual write
flash_write((uint32_t)ptr,req->payload, len); flash_write((uint32_t)ptr,req->payload, len);
last_written_app_address = (uint8_t *)ptr + len - 8 + 4;
break; break;
case BootDone: case BootDone:
// Writing to flash finished. Request code validation. // Writing to flash finished. Request code validation.

View File

@ -9,7 +9,7 @@
#define _APP_H_ #define _APP_H_
#include <stdint.h> #include <stdint.h>
#include "version.h" #include "version.h"
#include "solo.h"
#define DEBUG_UART USART1 #define DEBUG_UART USART1
#ifndef DEBUG_LEVEL #ifndef DEBUG_LEVEL
@ -21,7 +21,6 @@
#define BOOT_TO_DFU 0 #define BOOT_TO_DFU 0
#define SOLO 1
#define IS_BOOTLOADER 1 #define IS_BOOTLOADER 1
#define ENABLE_U2F_EXTENSIONS #define ENABLE_U2F_EXTENSIONS
@ -65,9 +64,4 @@ int is_authorized_to_boot();
int is_bootloader_disabled(); int is_bootloader_disabled();
void bootloader_heartbeat(); void bootloader_heartbeat();
// Return 1 if Solo is secure/locked.
int solo_is_locked();
#endif #endif

View File

@ -46,7 +46,9 @@ int main()
{ {
uint8_t hidmsg[64]; uint8_t hidmsg[64];
uint32_t t1 = 0; uint32_t t1 = 0;
#ifdef SOLO_HACKER
uint32_t stboot_time = 0; uint32_t stboot_time = 0;
#endif
uint32_t boot = 1; uint32_t boot = 1;
set_logging_mask( set_logging_mask(
@ -96,6 +98,7 @@ int main()
} }
#ifdef SOLO_HACKER
if (!is_bootloader_disabled()) if (!is_bootloader_disabled())
{ {
stboot_time = millis(); stboot_time = millis();
@ -105,6 +108,7 @@ int main()
goto start_bootloader; goto start_bootloader;
} }
} }
#endif
if (is_authorized_to_boot() && (boot || is_bootloader_disabled())) if (is_authorized_to_boot() && (boot || is_bootloader_disabled()))
{ {
@ -115,8 +119,9 @@ int main()
printf1(TAG_RED,"Not authorized to boot (%08x == %08lx)\r\n", AUTH_WORD_ADDR, *(uint32_t*)AUTH_WORD_ADDR); printf1(TAG_RED,"Not authorized to boot (%08x == %08lx)\r\n", AUTH_WORD_ADDR, *(uint32_t*)AUTH_WORD_ADDR);
} }
#ifdef SOLO_HACKER
start_bootloader: start_bootloader:
#endif
SystemClock_Config(); SystemClock_Config();
init_gpio(); init_gpio();
init_millisecond_timer(0); init_millisecond_timer(0);

View File

@ -2,14 +2,14 @@ include build/common.mk
# ST related # 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/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/attestation.c src/nfc.c src/ams.c src/sense.c SRC += src/fifo.c src/crypto.c src/attestation.c src/nfc.c src/ams.c src/sense.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB) SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib # FIDO2 lib
SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c SRC += ../../fido2/apdu.c ../../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/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/crypto.c SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/version.c SRC += ../../fido2/version.c
SRC += ../../fido2/data_migration.c SRC += ../../fido2/data_migration.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
@ -22,9 +22,7 @@ SRC += ../../crypto/cifra/src/sha512.c ../../crypto/cifra/src/blockwise.c
OBJ1=$(SRC:.c=.o) OBJ1=$(SRC:.c=.o)
OBJ=$(OBJ1:.s=.o) OBJ=$(OBJ1:.s=.o)
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
INC+= -I../../fido2/ -I../../fido2/extensions
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
INC += -I../../crypto/tiny-AES-c INC += -I../../crypto/tiny-AES-c
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
@ -68,6 +66,9 @@ all: $(TARGET).elf
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c ../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
$(CC) $^ $(HW) -O3 $(ECC_CFLAGS) -o $@ $(CC) $^ $(HW) -O3 $(ECC_CFLAGS) -o $@
%.o: %.s
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
%.elf: $(OBJ) %.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@ $(CC) $^ $(HW) $(LDFLAGS) -o $@
@echo "Built version: $(VERSION_FLAGS)" @echo "Built version: $(VERSION_FLAGS)"

View File

@ -4,14 +4,13 @@ include build/common.mk
SRC = bootloader/main.c bootloader/bootloader.c SRC = bootloader/main.c bootloader/bootloader.c
SRC += bootloader/pubkey_bootloader.c bootloader/version_check.c SRC += bootloader/pubkey_bootloader.c bootloader/version_check.c
SRC += src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.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/attestation.c src/sense.c SRC += src/fifo.c src/crypto.c src/attestation.c src/sense.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB) SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib # FIDO2 lib
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/crypto.c
# Crypto libs # Crypto libs
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c
@ -67,7 +66,7 @@ all: $(TARGET).elf
%.elf: $(OBJ) %.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@ $(CC) $^ $(HW) $(LDFLAGS) -o $@
$(SZ) $@ arm-none-eabi-size $@
%.hex: %.elf %.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex $(CP) -O ihex $^ $(TARGET).hex

View File

@ -1,10 +1,7 @@
include ../../fido2/version.mk
CC=$(PREFIX)arm-none-eabi-gcc CC=$(PREFIX)arm-none-eabi-gcc
CP=$(PREFIX)arm-none-eabi-objcopy CP=$(PREFIX)arm-none-eabi-objcopy
SZ=$(PREFIX)arm-none-eabi-size SZ=$(PREFIX)arm-none-eabi-size
AR=$(PREFIX)arm-none-eabi-ar AR=$(PREFIX)arm-none-eabi-ar
AS=$(PREFIX)arm-none-eabi-as
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \ DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \ lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
@ -16,20 +13,17 @@ USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c \ lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c \
lib/usbd/usbd_ccid.c lib/usbd/usbd_ccid.c
VERSION_FULL?=$(SOLO_VERSION_FULL) VERSION:=$(shell git describe --abbrev=0 )
VERSION:=$(SOLO_VERSION) VERSION_FULL:=$(shell git describe)
VERSION_MAJ:=$(SOLO_VERSION_MAJ) VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
VERSION_MIN:=$(SOLO_VERSION_MIN) VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
VERSION_PAT:=$(SOLO_VERSION_PAT) VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \ VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\" -DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
_all: _all:
echo $(SOLO_VERSION_FULL) echo $(VERSION_FULL)
echo $(SOLO_VERSION_MAJ) echo $(VERSION_MAJ)
echo $(SOLO_VERSION_MIN) echo $(VERSION_MIN)
echo $(SOLO_VERSION_PAT) echo $(VERSION_PAT)
%.o: %.s
$(AS) -o $@ $^

View File

@ -8,9 +8,6 @@
#define _APP_H_ #define _APP_H_
#include <stdint.h> #include <stdint.h>
#include "version.h" #include "version.h"
#include "solo.h"
#define SOLO
#define DEBUG_UART USART1 #define DEBUG_UART USART1
@ -49,9 +46,6 @@
void printing_init(); void printing_init();
void hw_init(int lf); void hw_init(int lf);
// Return 1 if Solo is secure/locked.
int solo_is_locked();
//#define TEST //#define TEST
//#define TEST_POWER //#define TEST_POWER

View File

@ -5,16 +5,12 @@
// http://opensource.org/licenses/MIT>, at your option. This file may not be // http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms. // copied, modified, or distributed except according to those terms.
#include <stdint.h> #include <stdint.h>
#include <string.h>
#include "crypto.h" #include "crypto.h"
#include "memory_layout.h"
#include "device.h"
#include "sense.h"
#include "log.h"
#ifdef USE_SOLOKEYS_CERT
const uint8_t attestation_solo_cert_der[] = const uint8_t attestation_cert_der[] =
"\x30\x82\x03\x03\x30\x82\x02\xaa\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08" "\x30\x82\x02\xe1\x30\x82\x02\x88\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04\x06" "\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04\x06"
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c" "\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
"\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b" "\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b"
@ -22,40 +18,42 @@ const uint8_t attestation_solo_cert_der[] =
"\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73" "\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73"
"\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16" "\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16"
"\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30" "\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30"
"\x20\x17\x0d\x31\x39\x31\x32\x30\x31\x31\x39\x32\x33\x34\x39\x5a\x18\x0f\x32\x30" "\x20\x17\x0d\x31\x38\x31\x31\x31\x31\x31\x32\x35\x32\x30\x30\x5a\x18\x0f\x32\x30"
"\x36\x39\x31\x31\x31\x38\x31\x39\x32\x33\x34\x39\x5a\x30\x81\x91\x31\x0b\x30\x09" "\x36\x38\x31\x30\x32\x39\x31\x32\x35\x32\x30\x30\x5a\x30\x81\x92\x31\x0b\x30\x09"
"\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08" "\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08"
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x11\x30\x0f\x06\x03\x55\x04\x0a\x0c\x08\x53" "\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53"
"\x6f\x6c\x6f\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41\x75" "\x6f\x6c\x6f\x20\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41"
"\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61\x74" "\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61"
"\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65" "\x74\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b"
"\x65\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
"\x09\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63"
"\x6f\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a"
"\x28\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5"
"\xd2\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6"
"\xac\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xdc\x30\x81\xd9\x30"
"\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d"
"\x9e\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81"
"\x97\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55"
"\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72"
"\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f"
"\x20\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74"
"\x20\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
"\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09" "\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09"
"\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f" "\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f"
"\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce" "\x6d\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04"
"\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a\x28" "\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x0a\x06\x08"
"\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5\xd2" "\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x71\x10\x46\x2c\xf5"
"\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6\xac" "\x16\x18\x97\x55\xca\x64\x50\x3b\x69\xb2\xdf\x17\x71\xab\xad\x8e\xc0\xd6\xa6\x07"
"\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xff\x30\x81\xfc\x30\x1d" "\x3d\x66\x8a\x3b\xbb\xfe\x61\x02\x20\x1e\x82\xef\xeb\x5e\x4e\x3a\x00\x84\x64\xd2"
"\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d\x9e" "\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71"
"\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81\x97" "\x52"
"\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04"
"\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79"
"\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20"
"\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74\x20"
"\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79"
"\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01"
"\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d"
"\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04\x02"
"\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x21\x06\x0b\x2b"
"\x06\x01\x04\x01\x82\xe5\x1c\x01\x01\x04\x04\x12\x04\x10\x88\x76\x63\x1b\xd4\xa0"
"\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04"
"\x03\x02\x03\x47\x00\x30\x44\x02\x20\x4d\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d"
"\x87\xc3\xfa\xb6\x6b\x29\x9b\xfb\x8b\x4d\x4d\x29\x77\x5b\xa1\x04\x4c\x7f\x12\x8d"
"\x71\x02\x20\x47\x4c\x3d\xb2\xa1\x74\xe3\x9c\xfe\xe1\x23\xbf\xec\x47\x96\xf4\xe5"
"\x9b\x65\x76\xac\xc8\x69\x1d\xe2\x74\xff\x4e\xa4\xcf\x02\x6d"
; ;
#else
const uint8_t attestation_hacker_cert_der[] = // For testing/development only
const uint8_t attestation_cert_der[] =
"\x30\x82\x02\xe9\x30\x82\x02\x8e\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08" "\x30\x82\x02\xe9\x30\x82\x02\x8e\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x82\x31\x0b\x30\x09\x06\x03\x55\x04\x06" "\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x81\x82\x31\x0b\x30\x09\x06\x03\x55\x04\x06"
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c" "\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
@ -96,36 +94,8 @@ const uint8_t attestation_hacker_cert_der[] =
"\xf3\x87\x61\x82\xd8\xcd\x48\xfc\x57" "\xf3\x87\x61\x82\xd8\xcd\x48\xfc\x57"
; ;
#endif
const uint16_t attestation_solo_cert_der_size = sizeof(attestation_solo_cert_der)-1; const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
const uint16_t attestation_hacker_cert_der_size = sizeof(attestation_hacker_cert_der)-1;
const uint16_t attestation_key_size = 32;
uint8_t * device_get_attestation_key(){
flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR;
return page->attestation_key;
}
uint16_t device_attestation_cert_der_get_size(){
uint16_t sz = (uint16_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size;
return sz;
}
void device_attestation_read_cert_der(uint8_t * dst){
const uint8_t * der = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert;
uint16_t sz = device_attestation_cert_der_get_size();
memmove(dst, der, sz);
// Overwrite respective x509 fields if Tap or Somu.
if (memcmp(dst + 0x2c6, "\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d", 10) == 0){
if (device_is_nfc()){
dst[0x2a2] = 0x89;//tap aaguid byte
memmove(dst + 0xac, "\x34\x33\x38\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x34\x33\x38", 20);//tap-id
memmove(dst + 0x2c5, "\x6d\x7b\x41\x2b\xff\x57\xf0\x03\xbd\x5b\x39\x4a\xf7\xa9\x2d\x6d\xcb\x9e\x2d\x88\xbf\xb3\x93\xc5\x66\x3b\xd1\xbc\x34\xfa\x5c\x4c\x02\x20\x59\x01\x49\x39\x1b\xb7\xa9\x1c\xed\x49\x78\x4f\x92\xa9\x61\x14\xa5\x6e\x96\x3f\x29\x02\x93\xe0\x5d\xe2\x75\xd0\x60\xd9\x74\xc2", 66);//tap-sig
} else if (tsc_sensor_exists()) {
dst[0x2a2] = 0x98;//somu aaguid byte
memmove(dst + 0xac, "\x35\x30\x32\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x35\x30\x32", 20);//somu-id
memmove(dst + 0x2c5, "\x4d\x08\xc8\x9d\xc4\x50\x49\x70\x48\x4d\xd0\x12\xd9\x7c\x62\x5e\x6b\xd3\x84\xd5\x36\x42\xfe\x86\x8e\x7a\x23\x59\xa0\x20\xf0\xc5\x02\x20\x5f\x70\x93\x61\x5a\xe4\x20\xcf\xb9\x8a\xf5\xdd\x87\xd0\x48\x6d\x7d\x59\xef\x9e\x0e\x11\xa3\x8e\xf7\xe3\xe2\xf5\x35\x37\x99\x1a", 66);//somu-sig
}
}
}

View File

@ -0,0 +1,368 @@
// Copyright 2019 SoloKeys Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
/*
* Wrapper for crypto implementation on device
*
* */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "crypto.h"
#ifdef USE_SOFTWARE_IMPLEMENTATION
#include "sha256.h"
#include "uECC.h"
#include "aes.h"
#include "ctap.h"
#include "device.h"
// stuff for SHA512
#include "sha2.h"
#include "blockwise.h"
#include APP_CONFIG
#include "log.h"
#include "memory_layout.h"
typedef enum
{
MBEDTLS_ECP_DP_NONE = 0,
MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */
MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */
MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */
MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */
MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */
MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */
MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */
MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */
MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */
MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
} mbedtls_ecp_group_id;
static SHA256_CTX sha256_ctx;
static cf_sha512_context sha512_ctx;
static const struct uECC_Curve_t * _es256_curve = NULL;
static const uint8_t * _signing_key = NULL;
static int _key_len = 0;
// Secrets for testing only
static uint8_t master_secret[64];
static uint8_t transport_secret[32];
void crypto_sha256_init(void)
{
sha256_init(&sha256_ctx);
}
void crypto_sha512_init(void)
{
cf_sha512_init(&sha512_ctx);
}
void crypto_load_master_secret(uint8_t * key)
{
#if KEY_SPACE_BYTES < 96
#error "need more key bytes"
#endif
memmove(master_secret, key, 64);
memmove(transport_secret, key+64, 32);
}
void crypto_reset_master_secret(void)
{
memset(master_secret, 0, 64);
memset(transport_secret, 0, 32);
ctap_generate_rng(master_secret, 64);
ctap_generate_rng(transport_secret, 32);
}
void crypto_sha256_update(uint8_t * data, size_t len)
{
sha256_update(&sha256_ctx, data, len);
}
void crypto_sha512_update(const uint8_t * data, size_t len) {
cf_sha512_update(&sha512_ctx, data, len);
}
void crypto_sha256_update_secret()
{
sha256_update(&sha256_ctx, master_secret, 32);
}
void crypto_sha256_final(uint8_t * hash)
{
sha256_final(&sha256_ctx, hash);
}
void crypto_sha512_final(uint8_t * hash)
{
// NB: there is also cf_sha512_digest
cf_sha512_digest_final(&sha512_ctx, hash);
}
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
{
uint8_t buf[64];
unsigned int i;
memset(buf, 0, sizeof(buf));
if (key == CRYPTO_MASTER_KEY)
{
key = master_secret;
klen = sizeof(master_secret)/2;
}
else if (key == CRYPTO_TRANSPORT_KEY)
{
key = transport_secret;
klen = 32;
}
if(klen > 64)
{
printf2(TAG_ERR, "Error, key size must be <= 64\n");
exit(1);
}
memmove(buf, key, klen);
for (i = 0; i < sizeof(buf); i++)
{
buf[i] = buf[i] ^ 0x36;
}
crypto_sha256_init();
crypto_sha256_update(buf, 64);
}
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
{
uint8_t buf[64];
unsigned int i;
crypto_sha256_final(hmac);
memset(buf, 0, sizeof(buf));
if (key == CRYPTO_MASTER_KEY)
{
key = master_secret;
klen = sizeof(master_secret)/2;
}
else if (key == CRYPTO_TRANSPORT_KEY2)
{
key = transport_secret;
klen = 32;
}
if(klen > 64)
{
printf2(TAG_ERR, "Error, key size must be <= 64\n");
exit(1);
}
memmove(buf, key, klen);
for (i = 0; i < sizeof(buf); i++)
{
buf[i] = buf[i] ^ 0x5c;
}
crypto_sha256_init();
crypto_sha256_update(buf, 64);
crypto_sha256_update(hmac, 32);
crypto_sha256_final(hmac);
}
void crypto_ecc256_init(void)
{
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
_es256_curve = uECC_secp256r1();
}
void crypto_ecc256_load_attestation_key(void)
{
static uint8_t _key [32];
memmove(_key, (uint8_t*)ATTESTATION_KEY_ADDR, 32);
_signing_key = _key;
_key_len = 32;
}
void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
{
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
{
printf2(TAG_ERR, "error, uECC failed\n");
exit(1);
}
}
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2)
{
static uint8_t privkey[32];
generate_private_key(data,len,data2,len2,privkey);
_signing_key = privkey;
_key_len = 32;
}
void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID)
{
const struct uECC_Curve_t * curve = NULL;
switch(MBEDTLS_ECP_ID)
{
case MBEDTLS_ECP_DP_SECP192R1:
curve = uECC_secp192r1();
if (_key_len != 24) goto fail;
break;
case MBEDTLS_ECP_DP_SECP224R1:
curve = uECC_secp224r1();
if (_key_len != 28) goto fail;
break;
case MBEDTLS_ECP_DP_SECP256R1:
curve = uECC_secp256r1();
if (_key_len != 32) goto fail;
break;
case MBEDTLS_ECP_DP_SECP256K1:
curve = uECC_secp256k1();
if (_key_len != 32) goto fail;
break;
default:
printf2(TAG_ERR, "error, invalid ECDSA alg specifier\n");
exit(1);
}
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
{
printf2(TAG_ERR, "error, uECC failed\n");
exit(1);
}
return;
fail:
printf2(TAG_ERR, "error, invalid key length\n");
exit(1);
}
void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey)
{
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
crypto_sha256_update(data, len);
crypto_sha256_update(data2, len2);
crypto_sha256_update(master_secret, 32); // TODO AES
crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
crypto_aes256_init(master_secret + 32, NULL);
crypto_aes256_encrypt(privkey, 32);
}
/*int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);*/
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y)
{
uint8_t privkey[32];
uint8_t pubkey[64];
generate_private_key(data,len,NULL,0,privkey);
memset(pubkey,0,sizeof(pubkey));
uECC_compute_public_key(privkey, pubkey, _es256_curve);
memmove(x,pubkey,32);
memmove(y,pubkey+32,32);
}
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
{
uECC_compute_public_key(privkey, pubkey, _es256_curve);
}
void crypto_load_external_key(uint8_t * key, int len)
{
_signing_key = key;
_key_len = len;
}
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
{
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
{
printf2(TAG_ERR, "Error, uECC_make_key failed\n");
exit(1);
}
}
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret)
{
if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
{
printf2(TAG_ERR, "Error, uECC_shared_secret failed\n");
exit(1);
}
}
struct AES_ctx aes_ctx;
void crypto_aes256_init(uint8_t * key, uint8_t * nonce)
{
if (key == CRYPTO_TRANSPORT_KEY)
{
AES_init_ctx(&aes_ctx, transport_secret);
}
else
{
AES_init_ctx(&aes_ctx, key);
}
if (nonce == NULL)
{
memset(aes_ctx.Iv, 0, 16);
}
else
{
memmove(aes_ctx.Iv, nonce, 16);
}
}
// prevent round key recomputation
void crypto_aes256_reset_iv(uint8_t * nonce)
{
if (nonce == NULL)
{
memset(aes_ctx.Iv, 0, 16);
}
else
{
memmove(aes_ctx.Iv, nonce, 16);
}
}
void crypto_aes256_decrypt(uint8_t * buf, int length)
{
AES_CBC_decrypt_buffer(&aes_ctx, buf, length);
}
void crypto_aes256_encrypt(uint8_t * buf, int length)
{
AES_CBC_encrypt_buffer(&aes_ctx, buf, length);
}
#else
#error "No crypto implementation defined"
#endif

View File

@ -34,8 +34,6 @@
#define LOW_FREQUENCY 1 #define LOW_FREQUENCY 1
#define HIGH_FREQUENCY 0 #define HIGH_FREQUENCY 0
#define SOLO_FLAG_LOCKED 0x2
void wait_for_usb_tether(void); void wait_for_usb_tether(void);
@ -193,113 +191,7 @@ void device_init_button(void)
} }
} }
int solo_is_locked(){ void device_init(int argc, char *argv[])
uint64_t device_settings = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->device_settings;
uint32_t tag = (uint32_t)(device_settings >> 32ull);
return tag == ATTESTATION_CONFIGURED_TAG && (device_settings & SOLO_FLAG_LOCKED) != 0;
}
// Locks solo flash from debugging. Locks on next reboot.
// This should be removed in next Solo release.
void solo_lock_if_not_already() {
uint8_t buf[2048];
memmove(buf, (uint8_t*)ATTESTATION_PAGE_ADDR, 2048);
((flash_attestation_page *)buf)->device_settings |= SOLO_FLAG_LOCKED;
flash_erase_page(ATTESTATION_PAGE);
flash_write(ATTESTATION_PAGE_ADDR, buf, 2048);
}
/** device_migrate
* Depending on version of device, migrates:
* * Moves attestation certificate to data segment.
* * Creates locked variable and stores in data segment.
*
* Once in place, this allows all devices to accept same firmware,
* rather than using "hacker" and "secure" builds.
*/
static void device_migrate(){
extern const uint16_t attestation_solo_cert_der_size;
extern const uint16_t attestation_hacker_cert_der_size;
extern uint8_t attestation_solo_cert_der[];
extern uint8_t attestation_hacker_cert_der[];
uint64_t device_settings = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->device_settings;
uint32_t configure_tag = (uint32_t)(device_settings >> 32);
if (configure_tag != ATTESTATION_CONFIGURED_TAG)
{
printf1(TAG_RED,"Migrating certificate and lock information to data segment.\r\n");
device_settings = ATTESTATION_CONFIGURED_TAG;
device_settings <<= 32;
// Read current device lock level.
uint32_t optr = FLASH->OPTR;
if ((optr & 0xff) != 0xAA){
device_settings |= SOLO_FLAG_LOCKED;
}
uint8_t tmp_attestation_key[32];
memmove(tmp_attestation_key,
((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_key,
32);
flash_erase_page(ATTESTATION_PAGE);
flash_write(
(uint32_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_key,
tmp_attestation_key,
32
);
// Check if this is Solo Hacker attestation (not confidential)
// then write solo or hacker attestation cert to flash page.
uint8_t solo_hacker_attestation_key[32] = "\x1b\x26\x26\xec\xc8\xf6\x9b\x0f\x69\xe3\x4f"
"\xb2\x36\xd7\x64\x66\xba\x12\xac\x16\xc3\xab"
"\x57\x50\xba\x06\x4e\x8b\x90\xe0\x24\x48";
if (memcmp(solo_hacker_attestation_key,
tmp_attestation_key,
32) == 0)
{
printf1(TAG_GREEN,"Updating solo hacker cert\r\n");
flash_write_dword(
(uint32_t)&((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size,
(uint64_t)attestation_hacker_cert_der_size
);
flash_write(
(uint32_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert,
attestation_hacker_cert_der,
attestation_hacker_cert_der_size
);
}
else
{
printf1(TAG_GREEN,"Updating solo secure cert\r\n");
flash_write_dword(
(uint32_t)&((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size,
(uint64_t)attestation_solo_cert_der_size
);
flash_write(
(uint32_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert,
attestation_solo_cert_der,
attestation_solo_cert_der_size
);
}
// Save / done.
flash_write_dword(
(uint32_t) & ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->device_settings,
(uint64_t)device_settings);
}
}
void device_init()
{ {
hw_init(LOW_FREQUENCY); hw_init(LOW_FREQUENCY);
@ -327,8 +219,6 @@ void device_init()
ctaphid_init(); ctaphid_init();
ctap_init(); ctap_init();
device_migrate();
#if BOOT_TO_DFU #if BOOT_TO_DFU
flash_option_bytes_init(1); flash_option_bytes_init(1);
#else #else
@ -482,8 +372,20 @@ void heartbeat(void)
} }
void authenticator_read_state(AuthenticatorState * a)
{
uint32_t * ptr = (uint32_t *)flash_addr(STATE1_PAGE);
memmove(a,ptr,sizeof(AuthenticatorState));
}
static int authenticator_is_backup_initialized(void) void authenticator_read_backup_state(AuthenticatorState * a)
{
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
memmove(a,ptr,sizeof(AuthenticatorState));
}
// Return 1 yes backup is init'd, else 0
int authenticator_is_backup_initialized(void)
{ {
uint8_t header[16]; uint8_t header[16];
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE); uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
@ -492,35 +394,20 @@ static int authenticator_is_backup_initialized(void)
return state->is_initialized == INITIALIZED_MARKER; return state->is_initialized == INITIALIZED_MARKER;
} }
int authenticator_read_state(AuthenticatorState * a) void authenticator_write_state(AuthenticatorState * a, int backup)
{ {
uint32_t * ptr = (uint32_t *) flash_addr(STATE1_PAGE); if (! backup)
memmove(a, ptr, sizeof(AuthenticatorState)); {
flash_erase_page(STATE1_PAGE);
if (a->is_initialized != INITIALIZED_MARKER){ flash_write(flash_addr(STATE1_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
if (authenticator_is_backup_initialized()){
printf1(TAG_ERR,"Warning: memory corruption detected. restoring from backup..\n");
ptr = (uint32_t *) flash_addr(STATE2_PAGE);
memmove(a, ptr, sizeof(AuthenticatorState));
authenticator_write_state(a);
return 1;
}
return 0;
} }
else
{
flash_erase_page(STATE2_PAGE);
return 1; flash_write(flash_addr(STATE2_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
} }
void authenticator_write_state(AuthenticatorState * a)
{
flash_erase_page(STATE1_PAGE);
flash_write(flash_addr(STATE1_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
flash_erase_page(STATE2_PAGE);
flash_write(flash_addr(STATE2_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
} }
#if !defined(IS_BOOTLOADER) #if !defined(IS_BOOTLOADER)
@ -769,6 +656,11 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
} }
int ctap_user_verification(uint8_t arg)
{
return 1;
}
void ctap_reset_rk(void) void ctap_reset_rk(void)
{ {
int i; int i;
@ -873,17 +765,6 @@ void boot_solo_bootloader(void)
} }
void device_read_aaguid(uint8_t * dst){
uint8_t * aaguid = (uint8_t *)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
memmove(dst, aaguid, 16);
if (device_is_nfc()){
dst[0] = 0x89;
}
else if (tsc_sensor_exists()){
dst[0] = 0x98;
}
dump_hex1(TAG_GREEN,dst, 16);
}
void _Error_Handler(char *file, int line) void _Error_Handler(char *file, int line)

View File

@ -31,18 +31,21 @@ static void flash_unlock(void)
// Locks flash and turns off DFU // Locks flash and turns off DFU
void flash_option_bytes_init(int boot_from_dfu) void flash_option_bytes_init(int boot_from_dfu)
{ {
#ifndef FLASH_ROP
#define FLASH_ROP 0
#endif
#if FLASH_ROP == 0
uint32_t val = 0xfffff8aa; uint32_t val = 0xfffff8aa;
#elif FLASH_ROP == 2
uint32_t val = 0xfffff8cc;
#else
uint32_t val = 0xfffff8b9;
#endif
if (boot_from_dfu){ if (boot_from_dfu)
{
val &= ~(1<<27); // nBOOT0 = 0 (boot from system rom) val &= ~(1<<27); // nBOOT0 = 0 (boot from system rom)
} }
else {
if (solo_is_locked())
{
val = 0xfffff8cc;
}
}
val &= ~(1<<26); // nSWBOOT0 = 0 (boot from nBoot0) val &= ~(1<<26); // nSWBOOT0 = 0 (boot from nBoot0)
val &= ~(1<<25); // SRAM2_RST = 1 (erase sram on reset) val &= ~(1<<25); // SRAM2_RST = 1 (erase sram on reset)
val &= ~(1<<24); // SRAM2_PE = 1 (parity check en) val &= ~(1<<24); // SRAM2_PE = 1 (parity check en)

View File

@ -146,14 +146,12 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param)
case DEVICE_LOW_POWER_IDLE: case DEVICE_LOW_POWER_IDLE:
SET_CLOCK_RATE0(); SET_CLOCK_RATE0();
break; break;
#if !defined(IS_BOOTLOADER)
case DEVICE_LOW_POWER_FAST: case DEVICE_LOW_POWER_FAST:
SET_CLOCK_RATE1(); SET_CLOCK_RATE1();
break; break;
case DEVICE_FAST: case DEVICE_FAST:
SET_CLOCK_RATE2(); SET_CLOCK_RATE2();
break; break;
#endif
} }
} }

View File

@ -4,83 +4,105 @@
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be // http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms. // copied, modified, or distributed except according to those terms.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdio.h>
#include <unistd.h> #include <string.h>
#include "stm32l4xx.h"
#include "stm32l4xx_ll_gpio.h"
#include "stm32l4xx_ll_rcc.h"
#include "stm32l4xx_ll_system.h"
#include "stm32l4xx_ll_pwr.h"
#include "stm32l4xx_ll_utils.h"
#include "stm32l4xx_ll_cortex.h"
#include "stm32l4xx_ll_gpio.h"
#include "stm32l4xx_ll_usart.h"
#include "stm32l4xx_ll_bus.h"
#include "stm32l4xx_ll_usb.h"
#include "stm32l4xx_hal_pcd.h"
#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_hid.h"
/*#include "usbd_hid.h"*/
#include "cbor.h"
#include "device.h"
#include "ctaphid.h"
//#include "bsp.h"
#include "util.h"
#include "log.h"
#include "ctap.h"
#include APP_CONFIG #include APP_CONFIG
#include "flash.h"
#include "rng.h"
#include "led.h"
#include "device.h"
#include "util.h"
#include "fifo.h"
#include "log.h"
#if !defined(TEST) #ifdef TEST_SOLO_STM32
#define Error_Handler() _Error_Handler(__FILE__,__LINE__)
#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)
int main(int argc, char *argv[]) uint32_t __90_ms = 0;
#define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
// Timer6 overflow handler. happens every ~90ms.
void TIM6_DAC_IRQHandler()
{ {
uint8_t hidmsg[64]; // timer is only 16 bits, so roll it over here
uint32_t t1 = 0; TIM6->SR = 0;
__90_ms += 1;
set_logging_mask( }
/*0*/
//TAG_GEN|
// TAG_MC |
// TAG_GA |
TAG_WALLET |
TAG_STOR |
//TAG_NFC_APDU |
TAG_NFC |
//TAG_CP |
// TAG_CTAP|
//TAG_HID|
TAG_U2F|
//TAG_PARSE |
//TAG_TIME|
// TAG_DUMP|
TAG_GREEN|
TAG_RED|
TAG_EXT|
TAG_CCID|
TAG_ERR
);
device_init(argc, argv);
memset(hidmsg,0,sizeof(hidmsg));
uint32_t millis(void)
{
return (((uint32_t)TIM6->CNT) + (__90_ms * 90));
}
void _Error_Handler(char *file, int line)
{
while(1) while(1)
{ {
if (millis() - t1 > HEARTBEAT_PERIOD) }
{ }
heartbeat();
t1 = millis();
}
device_manage(); int main(void)
{
uint32_t i = 5;
if (usbhid_recv(hidmsg) > 0) 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);
flash_option_bytes_init(1);
while (1)
{
uint32_t t0 = millis() % 750;
if (! IS_BUTTON_PRESSED())
{ {
ctaphid_handle_packet(hidmsg); if (t0 < 750*1/3)
memset(hidmsg, 0, sizeof(hidmsg)); {
led_rgb(0 | (0 << 8) | (i << 17));
}
else if (t0 < 750*2/3)
{
led_rgb(0 | (i << 8) | (0 << 16));
}
else
{
led_rgb(i | (0 << 8) | (0 << 16));
}
} }
else else
{ {
led_rgb(0x151515);
} }
ctaphid_check_timeouts();
} }
// Should never get here
usbhid_close();
printf1(TAG_GREEN, "done\n");
return 0;
} }
#endif #endif

View File

@ -17,11 +17,8 @@
#define COUNTER1_PAGE (PAGES - 3) #define COUNTER1_PAGE (PAGES - 3)
// State of FIDO2 application // State of FIDO2 application
#define STATE2_PAGE (PAGES - 2) #define STATE2_PAGE (PAGES - 2)
#define STATE1_PAGE (PAGES - 1) #define STATE1_PAGE (PAGES - 1)
#define STATE1_PAGE_ADDR (0x08000000 + ((STATE1_PAGE)*PAGE_SIZE))
#define STATE2_PAGE_ADDR (0x08000000 + ((STATE2_PAGE)*PAGE_SIZE))
// Storage of FIDO2 resident keys // Storage of FIDO2 resident keys
#define RK_NUM_PAGES 10 #define RK_NUM_PAGES 10
@ -35,8 +32,8 @@
#define APPLICATION_START_ADDR (0x08000000 + ((APPLICATION_START_PAGE)*PAGE_SIZE)) #define APPLICATION_START_ADDR (0x08000000 + ((APPLICATION_START_PAGE)*PAGE_SIZE))
// where attestation key is located // where attestation key is located
#define ATTESTATION_PAGE (PAGES - 15) #define ATTESTATION_KEY_PAGE (PAGES - 15)
#define ATTESTATION_PAGE_ADDR (0x08000000 + ATTESTATION_PAGE*PAGE_SIZE) #define ATTESTATION_KEY_ADDR (0x08000000 + ATTESTATION_KEY_PAGE*PAGE_SIZE)
// End of application code. Leave some extra room for future data storage. // End of application code. Leave some extra room for future data storage.
// NOT included in application // NOT included in application
@ -51,6 +48,7 @@
#define BOOT_VERSION_ADDR (0x08000000 + BOOT_VERSION_PAGE*FLASH_PAGE_SIZE + 8) #define BOOT_VERSION_ADDR (0x08000000 + BOOT_VERSION_PAGE*FLASH_PAGE_SIZE + 8)
#define LAST_PAGE (APPLICATION_END_PAGE-1) #define LAST_PAGE (APPLICATION_END_PAGE-1)
struct flash_memory_st{ struct flash_memory_st{
uint8_t bootloader[APPLICATION_START_PAGE*2*1024]; uint8_t bootloader[APPLICATION_START_PAGE*2*1024];
uint8_t application[(APPLICATION_END_PAGE-APPLICATION_START_PAGE)*2*1024-8]; uint8_t application[(APPLICATION_END_PAGE-APPLICATION_START_PAGE)*2*1024-8];
@ -67,19 +65,5 @@ typedef struct flash_memory_st flash_memory_st;
#include <assert.h> #include <assert.h>
static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size"); static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size");
#define ATTESTATION_CONFIGURED_TAG 0xaa551e79
struct flash_attestation_page{
uint8_t attestation_key[32];
// DWORD padded.
uint64_t device_settings;
uint64_t attestation_cert_size;
uint8_t attestation_cert[2048 - 32 - 8 - 8];
} __attribute__((packed));
typedef struct flash_attestation_page flash_attestation_page;
static_assert(sizeof(flash_attestation_page) == 2048, "Data structure doesn't match flash size");
#endif #endif

View File

@ -1,25 +0,0 @@
#ifndef _SOLO_H_
#define _SOLO_H_
void device_init();
void main_loop_delay();
void usbhid_init();
void usbhid_close();
int usbhid_recv(uint8_t * msg);
void heartbeat();
// Called each main loop. Doesn't need to do anything.
void device_manage();
void device_init_button();
// For Solo hacker
void boot_solo_bootloader();
void boot_st_bootloader();
void delay(uint32_t ms);
#endif

View File

@ -1,4 +1,5 @@
ecdsa ecdsa
fido2
intelhex intelhex
pyserial pyserial
solo-python solo-python