Compare commits

..

2 Commits

Author SHA1 Message Date
75dbefad73 Merge pull request #205 from m3hm00d/sanitize
Fix minor typo in comment
2019-07-05 10:28:11 -04:00
e14700f398 Fix minor typo in comment
'USBD_HID_DeInit' is written as 'USBD_HID_Init'; likely a copy-paste
error. This patch should fix it.
2019-06-20 16:44:17 +00:00
89 changed files with 3414 additions and 5256 deletions

View File

@ -1,217 +0,0 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "szszszsz",
"name": "Szczepan Zalega",
"avatar_url": "https://avatars0.githubusercontent.com/u/17005426?v=4",
"profile": "https://github.com/szszszsz",
"contributions": [
"code",
"doc",
"ideas"
]
},
{
"login": "Wesseldr",
"name": "Wessel dR",
"avatar_url": "https://avatars1.githubusercontent.com/u/4012809?v=4",
"profile": "https://github.com/Wesseldr",
"contributions": [
"doc"
]
},
{
"login": "agl",
"name": "Adam Langley",
"avatar_url": "https://avatars3.githubusercontent.com/u/21203?v=4",
"profile": "https://www.imperialviolet.org",
"contributions": [
"bug",
"code"
]
},
{
"login": "merlokk",
"name": "Oleg Moiseenko",
"avatar_url": "https://avatars2.githubusercontent.com/u/807634?v=4",
"profile": "http://www.lotteam.com",
"contributions": [
"code"
]
},
{
"login": "aseigler",
"name": "Alex Seigler",
"avatar_url": "https://avatars1.githubusercontent.com/u/6605560?v=4",
"profile": "https://github.com/aseigler",
"contributions": [
"bug"
]
},
{
"login": "dschuermann",
"name": "Dominik Schürmann",
"avatar_url": "https://avatars3.githubusercontent.com/u/321888?v=4",
"profile": "https://www.cotech.de/services/",
"contributions": [
"bug"
]
},
{
"login": "ehershey",
"name": "Ernie Hershey",
"avatar_url": "https://avatars0.githubusercontent.com/u/286008?v=4",
"profile": "https://github.com/ehershey",
"contributions": [
"doc"
]
},
{
"login": "YakBizzarro",
"name": "Andrea Corna",
"avatar_url": "https://avatars1.githubusercontent.com/u/767740?v=4",
"profile": "https://github.com/YakBizzarro",
"contributions": [
"infra"
]
},
{
"login": "pjz",
"name": "Paul Jimenez",
"avatar_url": "https://avatars3.githubusercontent.com/u/11100?v=4",
"profile": "https://place.org/~pj/",
"contributions": [
"infra",
"code"
]
},
{
"login": "yparitcher",
"name": "yparitcher",
"avatar_url": "https://avatars0.githubusercontent.com/u/38916402?v=4",
"profile": "https://github.com/yparitcher",
"contributions": [
"ideas",
"maintenance"
]
},
{
"login": "StoyanDimitrov",
"name": "StoyanDimitrov",
"avatar_url": "https://avatars1.githubusercontent.com/u/10962709?v=4",
"profile": "https://github.com/StoyanDimitrov",
"contributions": [
"doc"
]
},
{
"login": "alphathegeek",
"name": "alphathegeek",
"avatar_url": "https://avatars2.githubusercontent.com/u/51253712?v=4",
"profile": "https://github.com/alphathegeek",
"contributions": [
"ideas"
]
},
{
"login": "rgerganov",
"name": "Radoslav Gerganov",
"avatar_url": "https://avatars2.githubusercontent.com/u/271616?v=4",
"profile": "https://xakcop.com",
"contributions": [
"ideas",
"code"
]
},
{
"login": "manuel-domke",
"name": "Manuel Domke",
"avatar_url": "https://avatars3.githubusercontent.com/u/10274356?v=4",
"profile": "http://13-37.org",
"contributions": [
"ideas",
"code",
"business"
]
},
{
"login": "esden",
"name": "Piotr Esden-Tempski",
"avatar_url": "https://avatars3.githubusercontent.com/u/17334?v=4",
"profile": "http://1bitsquared.com",
"contributions": [
"business"
]
},
{
"login": "m3hm00d",
"name": "f.m3hm00d",
"avatar_url": "https://avatars1.githubusercontent.com/u/42179593?v=4",
"profile": "https://github.com/m3hm00d",
"contributions": [
"doc"
]
},
{
"login": "hughsie",
"name": "Richard Hughes",
"avatar_url": "https://avatars0.githubusercontent.com/u/151380?v=4",
"profile": "http://blogs.gnome.org/hughsie/",
"contributions": [
"ideas",
"code",
"infra",
"tool"
]
},
{
"login": "kimusan",
"name": "Kim Schulz",
"avatar_url": "https://avatars1.githubusercontent.com/u/1150049?v=4",
"profile": "http://www.schulz.dk",
"contributions": [
"business",
"ideas"
]
},
{
"login": "oplik0",
"name": "Jakub",
"avatar_url": "https://avatars2.githubusercontent.com/u/25460763?v=4",
"profile": "https://github.com/oplik0",
"contributions": [
"bug"
]
},
{
"login": "jolo1581",
"name": "Jan A.",
"avatar_url": "https://avatars1.githubusercontent.com/u/53423977?v=4",
"profile": "https://github.com/jolo1581",
"contributions": [
"code",
"doc"
]
},
{
"login": "ccinelli",
"name": "ccinelli",
"avatar_url": "https://avatars0.githubusercontent.com/u/38021940?v=4",
"profile": "https://github.com/ccinelli",
"contributions": [
"infra",
"test"
]
}
],
"contributorsPerLine": 7,
"projectName": "solo",
"projectOwner": "solokeys",
"repoType": "github",
"repoHost": "https://github.com"
}

5
.gitignore vendored
View File

@ -34,8 +34,7 @@
*.app
*.i*86
*.x86_64
targets/*/*.hex
targets/*/*.sha2
*.hex
# Debug files
*.dSYM/
@ -84,5 +83,3 @@ targets/*/docs/
main
builds/*
tools/testing/.idea/*
tools/testing/tests/__pycache__/*

View File

@ -6,15 +6,14 @@ addons:
sources:
- ubuntu-toolchain-r-test
packages:
- gcc-8
- gcc-7
- cppcheck
services:
- docker
before_install:
- sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
- 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:
- export CC=gcc-8
- export CC=gcc-7
- pyenv shell 3.6.7
- make travis

View File

@ -1,38 +1,33 @@
FROM debian:9.11-slim
FROM debian:stretch-slim
MAINTAINER SoloKeys <hello@solokeys.com>
# Install necessary packages
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
make \
wget \
bzip2 \
git \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update -qq
RUN apt-get install -qq bzip2 git make wget >/dev/null
# Install ARM compiler
RUN set -eux; \
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"; \
wget -O gcc.tar.bz2 "$url"; \
echo "6341f11972dac8de185646d0fbd73bfc gcc.tar.bz2" | md5sum -c -; \
echo "b50b02b0a16e5aad8620e9d7c31110ef285c1dde28980b1a9448b764d77d8f92 gcc.tar.bz2" | sha256sum -c -; \
tar -C /opt -xf gcc.tar.bz2; \
rm gcc.tar.bz2;
# 1. ARM GCC: for compilation
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
# from website
RUN echo "f55f90d483ddb3bcf4dae5882c2094cd gcc.tar.bz2" > gcc.md5
RUN md5sum -c gcc.md5
# self-generated
RUN echo "fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 gcc.tar.bz2" > gcc.sha256
RUN sha256sum -c gcc.sha256
RUN tar -C /opt -xf gcc.tar.bz2
# Python3.7: for solo-python (merging etc.)
RUN set -eux; \
url="https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh"; \
wget -O miniconda.sh "$url"; \
echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" | md5sum -c -; \
echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" | sha256sum -c -; \
bash ./miniconda.sh -b -p /opt/conda; \
ln -s /opt/conda/bin/python /usr/local/bin/python3; \
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
# 2. Python3.7: for solo-python (merging etc.)
RUN wget -q -O miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh
# from website
RUN echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" > miniconda.md5
RUN md5sum -c miniconda.md5
# self-generated
RUN echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a miniconda.sh" > miniconda.sha256
RUN sha256sum -c miniconda.sha256
# solo-python (Python3.7 script for merging etc.)
RUN pip install -U solo-python
RUN bash ./miniconda.sh -b -p /opt/conda
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

@ -23,8 +23,8 @@ else
endif
LDFLAGS += $(LIBCBOR)
VERSION:=$(shell git describe --abbrev=0 )
VERSION_FULL:=$(shell git describe)
VERSION:=$(shell python -c 'print("$(VERSION_FULL)".split("-")[0])')
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])')
@ -39,7 +39,7 @@ INCLUDES += -I./crypto/cifra/src
CFLAGS += $(INCLUDES)
# for crypto/tiny-AES-c
CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\" -DSOLO_EXPERIMENTAL=1
CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\"
name = main
@ -62,7 +62,7 @@ test: venv
$(MAKE) clean
$(MAKE) -C . main
$(MAKE) clean
$(MAKE) -C ./targets/stm32l432 test PREFIX=$(PREFIX) "VENV=$(VENV)" VERSION_FULL=${VERSION_FULL}
$(MAKE) -C ./targets/stm32l432 test PREFIX=$(PREFIX) "VENV=$(VENV)"
$(MAKE) clean
$(MAKE) cppcheck
@ -88,30 +88,18 @@ wink: venv
fido2-test: venv
venv/bin/python tools/ctap_test.py
update:
git fetch --tags
git checkout master
git rebase origin/master
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):${VERSION}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${VERSION_MAJ}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${VERSION_MAJ}.${VERSION_MIN}
uncached-docker-build-toolchain:
docker build --no-cache -t $(DOCKER_TOOLCHAIN_IMAGE) .
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${VERSION}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${VERSION_MAJ}
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${VERSION_MAJ}.${VERSION_MIN}
docker-build-all:
DOCKER_IMAGE := "solokeys/solo-firmware:local"
SOLO_VERSIONISH := "master"
docker-build:
docker build -t $(DOCKER_IMAGE) .
docker run --rm -v "$(CURDIR)/builds:/builds" \
-v "$(CURDIR):/solo" \
$(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${VERSION_FULL}
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
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
@ -132,14 +120,6 @@ clean:
full-clean: clean
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:
$(MAKE) test VENV=". ../../venv/bin/activate;"
$(MAKE) test-docker
$(MAKE) black
$(MAKE) black

148
README.md
View File

@ -1,13 +1,24 @@
[![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://update.solokeys.com/)
[![License](https://img.shields.io/github/license/solokeys/solo.svg)](https://github.com/solokeys/solo/blob/master/LICENSE)
[![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)
[![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)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
[![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://github.com/solokeys/solo/releases)
[![commits since last release](https://img.shields.io/github/commits-since/solokeys/solo/latest.svg)](https://github.com/solokeys/solo/commits/master)
[![last commit](https://img.shields.io/github/last-commit/solokeys/solo.svg)](https://github.com/solokeys/solo/commits/master)
[![commit activity](https://img.shields.io/github/commit-activity/m/solokeys/solo.svg)](https://github.com/solokeys/solo/commits/master)
[![contributors](https://img.shields.io/github/contributors/solokeys/solo.svg)](https://github.com/solokeys/solo/graphs/contributors)
# Solo
Solo is an open source security key, and you can get one at [solokeys.com](https://solokeys.com).
[<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">](https://solokeys.com)
Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
<img src="https://static.solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, but it is easily portable.
For development no hardware is needed, Solo also runs as a standalone application for Windows, Linux, and Mac OSX. If you like (or want to learn) hardware instead, you can run Solo on the NUCLEO-L432KC development board, or we make Solo for Hacker, an unlocked version of Solo that lets you customize its firmware.
@ -30,60 +41,12 @@ Solo for Hacker is a special version of Solo that let you customize its firmware
Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo. Solo Hacker can be converted to a secure version, but normal Solo cannot be converted to a Hacker version.
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 only 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
git clone --recurse-submodules https://github.com/solokeys/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
make cbor
make build-hacker
@ -95,6 +58,19 @@ solo program aux enter-bootloader
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)
Clone Solo and build it
@ -118,7 +94,10 @@ Run the Solo application:
./main
```
In another shell, you can run our [test suite](https://github.com/solokeys/fido2-tests).
In another shell, you can run client software, for example our tests:
```bash
python tools/ctap_test.py sim fido2
```
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
@ -128,50 +107,14 @@ You can find more details in our [documentation](https://docs.solokeys.io/solo/)
Check out our [official documentation](https://docs.solokeys.io/solo/).
# Contributors
# Contributors
Solo is an upgrade to [U2F Zero](https://github.com/conorpp/u2f-zero). It was born from Conor's passion for making secure hardware, and from our shared belief that security should be open to be trustworthy, in hardware like in software.
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
Contributors are welcome. The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
Look at the issues to see what is currently being worked on. Feel free to add issues as well.
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore -->
<table>
<tr>
<td align="center"><a href="https://github.com/szszszsz"><img src="https://avatars0.githubusercontent.com/u/17005426?v=4" width="100px;" alt="Szczepan Zalega"/><br /><sub><b>Szczepan Zalega</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=szszszsz" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=szszszsz" title="Documentation">📖</a> <a href="#ideas-szszszsz" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/Wesseldr"><img src="https://avatars1.githubusercontent.com/u/4012809?v=4" width="100px;" alt="Wessel dR"/><br /><sub><b>Wessel dR</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=Wesseldr" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.imperialviolet.org"><img src="https://avatars3.githubusercontent.com/u/21203?v=4" width="100px;" alt="Adam Langley"/><br /><sub><b>Adam Langley</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aagl" title="Bug reports">🐛</a> <a href="https://github.com/solokeys/solo/commits?author=agl" title="Code">💻</a></td>
<td align="center"><a href="http://www.lotteam.com"><img src="https://avatars2.githubusercontent.com/u/807634?v=4" width="100px;" alt="Oleg Moiseenko"/><br /><sub><b>Oleg Moiseenko</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=merlokk" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/aseigler"><img src="https://avatars1.githubusercontent.com/u/6605560?v=4" width="100px;" alt="Alex Seigler"/><br /><sub><b>Alex Seigler</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Aaseigler" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://www.cotech.de/services/"><img src="https://avatars3.githubusercontent.com/u/321888?v=4" width="100px;" alt="Dominik Schürmann"/><br /><sub><b>Dominik Schürmann</b></sub></a><br /><a href="https://github.com/solokeys/solo/issues?q=author%3Adschuermann" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/ehershey"><img src="https://avatars0.githubusercontent.com/u/286008?v=4" width="100px;" alt="Ernie Hershey"/><br /><sub><b>Ernie Hershey</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=ehershey" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/YakBizzarro"><img src="https://avatars1.githubusercontent.com/u/767740?v=4" width="100px;" alt="Andrea Corna"/><br /><sub><b>Andrea Corna</b></sub></a><br /><a href="#infra-YakBizzarro" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://place.org/~pj/"><img src="https://avatars3.githubusercontent.com/u/11100?v=4" width="100px;" alt="Paul Jimenez"/><br /><sub><b>Paul Jimenez</b></sub></a><br /><a href="#infra-pjz" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/solokeys/solo/commits?author=pjz" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yparitcher"><img src="https://avatars0.githubusercontent.com/u/38916402?v=4" width="100px;" alt="yparitcher"/><br /><sub><b>yparitcher</b></sub></a><br /><a href="#ideas-yparitcher" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-yparitcher" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/StoyanDimitrov"><img src="https://avatars1.githubusercontent.com/u/10962709?v=4" width="100px;" alt="StoyanDimitrov"/><br /><sub><b>StoyanDimitrov</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=StoyanDimitrov" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/alphathegeek"><img src="https://avatars2.githubusercontent.com/u/51253712?v=4" width="100px;" alt="alphathegeek"/><br /><sub><b>alphathegeek</b></sub></a><br /><a href="#ideas-alphathegeek" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://xakcop.com"><img src="https://avatars2.githubusercontent.com/u/271616?v=4" width="100px;" alt="Radoslav Gerganov"/><br /><sub><b>Radoslav Gerganov</b></sub></a><br /><a href="#ideas-rgerganov" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=rgerganov" title="Code">💻</a></td>
<td align="center"><a href="http://13-37.org"><img src="https://avatars3.githubusercontent.com/u/10274356?v=4" width="100px;" alt="Manuel Domke"/><br /><sub><b>Manuel Domke</b></sub></a><br /><a href="#ideas-manuel-domke" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=manuel-domke" title="Code">💻</a> <a href="#business-manuel-domke" title="Business development">💼</a></td>
</tr>
<tr>
<td align="center"><a href="http://1bitsquared.com"><img src="https://avatars3.githubusercontent.com/u/17334?v=4" width="100px;" alt="Piotr Esden-Tempski"/><br /><sub><b>Piotr Esden-Tempski</b></sub></a><br /><a href="#business-esden" title="Business development">💼</a></td>
<td align="center"><a href="https://github.com/m3hm00d"><img src="https://avatars1.githubusercontent.com/u/42179593?v=4" width="100px;" alt="f.m3hm00d"/><br /><sub><b>f.m3hm00d</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=m3hm00d" title="Documentation">📖</a></td>
<td align="center"><a href="http://blogs.gnome.org/hughsie/"><img src="https://avatars0.githubusercontent.com/u/151380?v=4" width="100px;" alt="Richard Hughes"/><br /><sub><b>Richard Hughes</b></sub></a><br /><a href="#ideas-hughsie" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/solokeys/solo/commits?author=hughsie" title="Code">💻</a> <a href="#infra-hughsie" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#tool-hughsie" title="Tools">🔧</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/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>
</table>
<!-- ALL-CONTRIBUTORS-LIST:END -->
# License
@ -180,8 +123,6 @@ Solo is fully open source.
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
You may use Solo software under the terms of either the Apache 2.0 license or MIT license.
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.
You may use Solo hardware under the terms of either the CERN 2.1 license or CC-BY-SA 4.0 license.
@ -194,20 +135,3 @@ You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
# Where To Buy Solo
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
<br/>
<hr/>
<br/>
[![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-21-orange.svg?style=flat-square)](#contributors)
[![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)
[![Keybase Chat](https://img.shields.io/badge/chat-on%20keybase-brightgreen.svg)](https://keybase.io/team/solokeys.public)
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
[![latest release](https://img.shields.io/github/release/solokeys/solo.svg)](https://github.com/solokeys/solo/releases)
[![commits since last release](https://img.shields.io/github/commits-since/solokeys/solo/latest.svg)](https://github.com/solokeys/solo/commits/master)
[![last commit](https://img.shields.io/github/last-commit/solokeys/solo.svg)](https://github.com/solokeys/solo/commits/master)
[![commit activity](https://img.shields.io/github/commit-activity/m/solokeys/solo.svg)](https://github.com/solokeys/solo/commits/master)
[![contributors](https://img.shields.io/github/contributors/solokeys/solo.svg)](https://github.com/solokeys/solo/graphs/contributors)

View File

@ -1,32 +0,0 @@
# Security Policy
## Supported Versions
We fix security issues as soon as they are found, and release firmware updates.
Each such release is accompanied by release notes, see <https://github.com/solokeys/solo/releases>.
The latest version can be determined using the file <https://github.com/solokeys/solo/blob/master/STABLE_VERSION>.
To update your key:
- either visit <https://update.solokeys.com>, or
- use our commandline tool <https://github.com/solokeys/solo-python>:
```
solo key update [--secure|--hacker]
```
## Reporting a Vulnerability
To report vulnerabilities you have found:
- preferably contact [@conor1](https://keybase.io/conor1), [@0x0ece](https://keybase.io/0x0ece) or [@nickray](https://keybase.io/nickray) via Keybase, or
- send us e-mail using OpenPGP to [security@solokeys.com](mailto:security@solokeys.com).
<https://keys.openpgp.org/vks/v1/by-fingerprint/85AFA2769F4381E5712C36A04DDFC46FEF1F7F3F>
We do not currently run a paid bug bounty program, but are happy to provide you with a bunch of Solo keys in recognition of your findings.
## Mailing List
Join our release notification mailing list to be informed about each release:
https://sendy.solokeys.com/subscription?f=9MLIqMDmox1Ucz89C892Kq09IqYMM7OB8UrBrkvtTkDI763QF3L5PMYlRhlVNo2AI892mO

View File

@ -1 +1 @@
3.0.0
2.2.2

View File

@ -1,99 +0,0 @@
# Using Solo for passwordless or second factor login on Linux
## Setup on Ubuntu 18.04
Before you can use Solo for passwordless or second factor login in your Linux system you have to install some packages.
This was tested under **Linux Mint 19.2**.
First you have to install PAM modules for u2f.
```
sudo apt install libpam-u2f pamu2fcfg
```
## 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.
If you can't generate your key (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
```
## 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:
```
sudo vim /etc/pam.d/lightdm
```
Now search following entry:
```
@include common-auth
```
and add
```
auth sufficient pam_u2f.so
```
**before** @include common-auth.
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
```
Now search following entry:
```
@include common-auth
```
and add
```
auth required pam_u2f.so
```
**after** @include common-auth.
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,53 +0,0 @@
# Booting into bootloader mode
If you have a recent version of Solo, you can put it into bootloader mode by running this command.
```bash
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
Solo has 3 boot stages.
## DFU
The first stage is the DFU (Device Firmware Update) which is in a ROM on Solo. It is baked into the chip and is not implemented by us.
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**.
On hacker/nonverifying-bootloader devices, you can boot into the DFU by holding down the button for 5 seconds,
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.
```bash
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.
```bash
solo program aux leave-dfu
```
*Warning*: If you change the firmware to something broken, and you tell the DFU to boot it, you could brick your device.
## Solo Bootloader
The next boot stage is the "Solo bootloader". So when we say to put your Solo into bootloader mode, it is this stage.
This bootloader is written by us and allows signed firmware updates to be written. On Solo Hackers, there is no signature checking
and will allow any firmware updates.
It is safe to develop for Solo using our Solo bootloader. If broken firmware is uploaded to the device, then the Solo
bootloader can always be booted again by holding down the button when plugging in.
## Solo application
This is what contains all the important functionality of Solo. FIDO2, U2F, etc. This is what Solo will boot to by default.

View File

@ -1,34 +1,22 @@
# Building solo
To build, develop and debug the firmware for the STM32L432. This will work
for Solo Hacker, the Nucleo development board, or your own homemade Solo.
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
## Prerequisites
# Prerequisites
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`,
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
To program your build, you'll need one of the following programs.
## Obtain source code and solo tool
- [openocd](http://openocd.org)
- [stlink](https://github.com/texane/stlink)
- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
Source code can be downloaded from:
- [github releases list](https://github.com/solokeys/solo/releases)
- [github repository](https://github.com/solokeys/solo)
**solo** tool can be downloaded from:
- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
- from installing prerequisites `pip3 install -r tools/requirements.txt`
- github repository: [repository](https://github.com/solokeys/solo-python)
- installation python enviroment with command `make venv` from root directory of source code
## Compilation
# Compilation
Enter the `stm32l4xx` target directory.
@ -36,23 +24,19 @@ Enter the `stm32l4xx` target directory.
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
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
The `build-hacker` recipe does a few things. First it builds the bootloader, with
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
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`.
### Building with debug messages
@ -61,13 +45,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
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
miss any debug messages.
@ -82,45 +66,118 @@ solo monitor <serial-port>
[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
two easy recipes:
If you want to build a release of Solo, we recommend trying a Hacker build first
just to make sure that it's working. Otherwise it may not be as easy or possible to
fix any mistakes.
* `bootloader-nonverifying`: bootloader with no signature checking on updates. I.e. "unlocked".
* `bootloader-verifying`: bootloader with signature checking enforced on updated. I.e. "Locked".
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.
Programming `all.hex` will cause the device to permanently lock itself.
# Programming
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
We recommend using our `solo` tool to manage programming. It is cross platform. First you must
install the prerequisites:
```
solo mergehex bootloader.hex solo.hex bundle.hex
pip3 install -r tools/requirements.txt
```
`bundle.hex` is our complete firmware build. Note it is in this step that you can
include a custom attestation certificate or lock the device from debugging/DFU.
By default the "hacker" attestation certifcate and key is used.
If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
## Pre-programmed Solo Hacker
If your Solo device is already programmed (it flashes green when powered), we recommend
programming it using the Solo bootloader.
```
solo mergehex \
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
--attestation-cert attestation.der \
--lock \
solo.hex \
bootloader.hex \
bundle.hex
solo program aux enter-bootloader
solo program bootloader solo.hex
```
See [here for more information on custom attestation](/solo/customization/).
Make sure to program `solo.hex` and not `all.hex`. Nothing bad would happen, but you'd
see errors.
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.
If something bad happens, you can always boot the Solo bootloader by doing the following.
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode).
1. Unplug device.
2. Hold down button.
3. Plug in device while holding down button.
4. Wait about 2 seconds for flashing yellow light. Release button.
If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
Don't use the ST DFU unless you know what you're doing.
## ST USB DFU
If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
off and it enumerates as "STM BOOTLOADER".
You can program it by running the following.
```
solo program aux enter-bootloader
solo program aux enter-dfu
# powercycle key
solo program dfu all.hex
```
Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
If all goes well, you should see a slow-flashing green light.
## Solo Hacker vs Solo
A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
Solo (locked) needs the button to be held down when plugged in to boot to the bootloader.
A locked Solo will only accept signed updates.
## Signed updates
If this is not a device with a hacker build, you can only program signed updates.
```
solo program bootloader /path/to/firmware.json
```
If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
firmware by running the following command.
```
solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
```
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
to the token.
# Permanently locking the device
If you plan to be using your Solo for real, you should lock it permanently. This prevents
someone from connecting a debugger to your token and stealing credentials.
To do this, build the locked release firmware.
```
make build-release-locked
```
Now when you program `all.hex`, the device will lock itself when it first boots. You can only update it
with signed updates.
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```
# WARNING: No more signed updates.
solo program disable-bootloader
```

View File

@ -1,140 +0,0 @@
# Customization
If you are interested in customizing parts of your Solo, and you have a Solo Hacker, this page is for you.
## Custom Attestation key
The attestation key is used in the FIDO2 *makeCredential* or U2F *register* requests. It signs
newly generated credentials. The certificate associated with the attestation key is output with newly created credentials.
Platforms or services can use the attestation feature to enforce specific authenticators to be used.
This is typically a use case for organizations and isn't seen in the wild for consumer use cases.
Attestation keys are typically the same for at least 100K units of a particular authenticator model.
This is so they don't contribute a significant fingerprint that platforms could use to identify the user.
If you don't want to use the default attestation key that Solo builds with, you can create your own
and program it.
### Creating your attestation key pair
Since we are generating keys, it's important to use a good entropy source.
You can use the [True RNG on your Solo](/solo/solo-extras) to generate some good random numbers.
```
# Run for 1 second, then hit control-c
solo key rng raw > seed.bin
```
First we will create a self signed key pair that acts as the root of trust. This
won't go on the authenticator, but will sign the keypair that does.
Please change the root certification information as needed. You may change the ECC curve.
```
curve=prime256v1
country=US
state=Maine
organization=OpenSourceSecurity
unit="Root CA"
CN=example.com
email=example@example.com
# generate EC private key
openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
# 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=example.com/emailAddress=$email"
# self sign the request
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
# convert to smaller size format DER
openssl x509 -in root_cert.pem -outform der -out root_cert.der
# print out information and verify
openssl x509 -in root_cert.pem -text -noout
```
You need to create a extended certificate for the device certificate to work with FIDO2. You need to create this
file, `v3.ext`, and add these options to it.
```
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
```
Now to generate & sign the attestation key pair that will go on your device, or maybe 100,000 devices :).
Note you must use a prime256v1 curve for this step, and you must leave the unit/OU as "Authenticator Attestation".
```
country=US
state=Maine
organization=OpenSourceSecurity
unit="Authenticator Attestation"
CN=example.com
email=example@example.com
# generate EC private key
openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
# 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=example.com/emailAddress=$email"
# 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
# convert to smaller size format DER
openssl x509 -in device_cert.pem -outform der -out device_cert.der
# Verify the device certificate details
openssl x509 -in device_cert.pem -text -noout
```
Let's verify that the attestation key and certificate are valid, and that they can be verified with the root key pair.
```
echo 'challenge $RANDOM' > chal.txt
# check that they are valid key pairs
openssl dgst -sha256 -sign device_key.pem -out sig.txt chal.txt
openssl dgst -sha256 -verify <(openssl x509 -in device_cert.pem -pubkey -noout) -signature sig.txt chal.txt
openssl dgst -sha256 -sign "root_key.pem" -out sig.txt chal.txt
openssl dgst -sha256 -verify <(openssl x509 -in root_cert.pem -pubkey -noout) -signature sig.txt chal.txt
# Check they are a chain
openssl verify -verbose -CAfile "root_cert.pem" "device_cert.pem"
```
If the checks succeed, you are ready to program the device attestation key and certificate.
### Programming an attestation key and certificate
First, [Build your solo application and bootloader](/solo/building).
Print your attestation key in a hex string format. Using our utility script:
```
python tools/print_x_y.py device_key.pem
```
Merge the `bootloader.hex`, `solo.hex`, attestion key, and certificate into one firmware file.
```
solo mergehex \
--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
with Solo in DFU mode](/solo/programming#procedure).
Are you interested in customizing in bulk? Contact hello@solokeys.com and we can help.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

View File

@ -1,257 +0,0 @@
# Nucleo32 board preparation
Additional steps are required to run the firmware on the Nucleo32 board.
## USB-A cable
Board does not provide an USB cable / socket for the target MCU communication.
Own provided USB plug has to be connected in the following way:
| PIN / Arduino PIN | MCU leg | USB wire color | Signal |
| ----------------- | ------- | -------------- | ------ |
| D10 / PA11 | 21 | white | D- |
| D2 / PA12 | 22 | green | D+ |
| GND (near D2) | ------- | black | GND |
| **not connected** | ------- | red | 5V |
Each USB plug pin should be connected via the wire in a color defined by the standard. It might be confirmed with a
multimeter for additional safety. USB plug description:
| PIN | USB wire color | Signal |
| --- | -------------- | ------ |
| 4 | black | GND |
| 3 | green | D+ |
| 2 | white | D- |
| 1 | red | 5V |
See this [USB plug] image, and Wikipedia's [USB plug description].
Plug in [USB-A_schematic.pdf] has wrong wire order, registered as [solo-hw#1].
The power is taken from the debugger / board (unless the board is configured in another way).
Make sure 5V is not connected, and is covered from contacting with the board elements.
Based on [USB-A_schematic.pdf].
## Firmware modification
Following patch has to be applied to skip the user presence confirmation, for tests. Might be applied at a later stage.
```text
diff --git a/targets/stm32l432/src/app.h b/targets/stm32l432/src/app.h
index c14a7ed..c89c3b5 100644
--- a/targets/stm32l432/src/app.h
+++ b/targets/stm32l432/src/app.h
@@ -71,6 +71,6 @@ void hw_init(void);
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
-#define SKIP_BUTTON_CHECK_FAST 0
+#define SKIP_BUTTON_CHECK_FAST 1
#endif
```
It is possible to provide a button and connect it to the MCU pins, as instructed in [USB-A_schematic.pdf]&#x3A;
```text
PA0 / pin 6 --> button --> GND
```
In that case the mentioned patch would not be required.
## Development environment setup
Environment: Fedora 29 x64, Linux 4.19.9
See <https://docs.solokeys.io/solo/building/> for the original guide. Here details not included there will be covered.
### Install ARM tools Linux
1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2].
2. Extract the archive.
3. Add full path to the `./bin` directory as first entry to the `$PATH` variable,
as in `~/gcc-arm/gcc-arm-none-eabi-8-2018-q4-major/bin/:$PATH`.
### Install ARM tools OsX using brew package manager
```bash
brew tap ArmMbed/homebrew-formulae
brew install arm-none-eabi-gcc
```
### Install flashing software
ST provides a CLI flashing tool - `STM32_Programmer_CLI`. It can be downloaded directly from the vendor's site:
1. Go to [download site URL](https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-programmers/stm32cubeprog.html), go to bottom page and from STM32CubeProg row select Download button.
2. Unzip contents of the archive.
3. Run \*Linux setup
4. In installation directory go to `./bin` - there the `./STM32_Programmer_CLI` is located
5. Add symlink to the STM32 CLI binary to `.local/bin`. Make sure the latter it is in `$PATH`.
If you're on MacOS X and installed the STM32CubeProg, you need to add the following to your path:
```bash
# ~/.bash_profile
export PATH="/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin/":$PATH
```
## Building and flashing
### Building
Please follow <https://docs.solokeys.io/solo/building/>, as the build way changes rapidly.
Currently (8.1.19) to build the firmware, following lines should be executed
```bash
# while in the main project directory
cd targets/stm32l432
make cbor
make build-hacker DEBUG=1
```
Note: `DEBUG=2` stops the device initialization, until a serial client will be attached to its virtual port.
Do not use it, if you do not plan to do so.
### Flashing via the Makefile command
```bash
# while in the main project directory
# create Python virtual environment with required packages, and activate
make venv
. venv/bin/activate
# Run flashing
cd ./targets/stm32l432
make flash
# which runs:
# flash: solo.hex bootloader.hex
# python merge_hex.py solo.hex bootloader.hex all.hex (intelhex library required)
# STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
# STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
```
### Manual flashing
In case you already have a firmware to flash (named `all.hex`), please run the following:
```bash
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
```
## Testing
### Internal
Project-provided tests.
#### Simulated device
A simulated device is provided to test the HID layer.
##### Build
```bash
make clean
cd tinycbor
make
cd ..
make env2
```
##### Execution
```bash
# run simulated device (will create a network UDP server)
./main
# run test 1
./env2/bin/python tools/ctap_test.py
# run test 2 (or other files in the examples directory)
./env2/bin/python python-fido2/examples/credential.py
```
#### Real device
```bash
# while in the main project directory
# not passing as of 8.1.19, due to test solution issues
make fido2-test
```
### External
#### FIDO2 test sites
1. <https://www.passwordless.dev/overview>
2. <https://webauthn.bin.coffee/>
3. <https://webauthn.org/>
#### U2F test sites
1. <https://u2f.bin.coffee/>
2. <https://demo.yubico.com/u2f>
#### FIDO2 standalone clients
1. <https://github.com/Nitrokey/u2f-ref-code>
2. <https://github.com/Yubico/libfido2>
3. <https://github.com/Yubico/python-fido2>
4. <https://github.com/google/pyu2f>
## USB serial console reading
Device opens an USB-emulated serial port to output its messages. While Nucleo board offers such already,
the Solo device provides its own.
- Provided Python tool
```bash
python3 ../../tools/solotool.py monitor /dev/solokey-serial
```
- External application
```bash
sudo picocom -b 115200 /dev/solokey-serial
```
where `/dev/solokey-serial` is an udev symlink to `/dev/ttyACM1`.
## Other
### Dumping firmware
Size is calculated using bash arithmetic.
```bash
STM32_Programmer_CLI -c port=SWD -halt -u 0x0 $((256*1024)) current.hex
```
### Software reset
```bash
STM32_Programmer_CLI -c port=SWD -rst
```
### Installing required Python packages
Client script requires some Python packages, which could be easily installed locally to the project
via the Makefile command. It is sufficient to run:
```bash
make env3
```
[solo-hw#1]: https://github.com/solokeys/solo-hw/issues/1
[usb plug]: https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/USB.svg/1200px-USB.svg.png
[usb plug description]: https://en.wikipedia.org/wiki/USB#Receptacle_(socket)_identification
[usb-a_schematic.pdf]: https://github.com/solokeys/solo-hw/releases/download/1.2/USB-A_schematic.pdf
[arm tools]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
[gcc-arm-none-eabi-8-2018-q4-major-linux.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

View File

@ -1,112 +0,0 @@
# Programming
This page documents how to update or program your Solo.
## Prerequisites
To program Solo, you'll likely only need to use our Solo tool.
```python
pip3 install solo-python
```
## Updating the firmware
If you just want to update the firmware, you can run one of the following commands.
Make sure your key [is in bootloader mode](/solo/bootloader-mode#solo-bootloader) first.
```bash
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.
```bash
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
risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations](/solo/customization/).
## Updating a Hacker to a Secure Solo
Updating a hacker to be a secure build overwrites the [Solo bootloader](/solo/bootloader-mode#solo-bootloader).
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
a build that you made yourself.
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. If you overwrite the Solo flash with a missing bootloader,
it will be bricked.
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`
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
concern with using our default attestation key, aside from a small privacy implication that services can distinguish it from Solo Secure.
### Procedure
1. Boot into DFU mode.
# Enter Solo bootloader
solo program aux enter-bootloader
# Enter DFU
solo program aux enter-dfu
The device should be turned off.
2. Program the device
solo program dfu <bundle-secure-non-solokeys.hex | bundle.hex>
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.
3. Boot the device
Once Solo boots a secure build, it will lock the flash permantly from debugger access. Also the bootloader
will only accept signed firmware updates.
solo program aux leave-dfu
If you are having problems with solo tool and DFU mode, you could alternatively try booting into DFU
by holding down the button while Solo is in bootloader mode. Then try another programming tool that works
with ST DFU:
* STM32CubeProg
* openocd
* stlink
Windows users need to install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
for solo-python to work with Solo's DFU.
## Programming a Solo that hasn't been programmed
A Solo that hasn't been programmed will boot into DFU mode. You can program
it by following a bootloader, or combined bootloader + application.
```
solo program dfu <bundle-*.hex | all.hex>
```
Then boot the device. Make sure it has a bootloader to boot to.
```
solo program aux leave-dfu
```
## Disable signed firmware updates
If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
```bash
# WARNING: No more signed updates.
solo program disable-bootloader
```
You won't be able to update to any new releases.

View File

@ -1,19 +0,0 @@
# Solo Extras
## Random number generation
Solo contains a True Random Number Generator (TRNG). A TRNG is a hardware based mechanism
that leverages natural phenomenon to generate random numbers, which can be better than a traditional
RNG that has state and updates deterministically using cryptographic methods.
You can easily access the TRNG stream on Solo using our python tool [`solo-python`](https://github.com/solokeys/solo-python).
```
solo key rng raw > random.bin
```
Or you can seed the state of the RNG on your kernel (`/dev/random`).
```
solo key rng feedkernel
```

View File

@ -1,33 +1,30 @@
# Summary
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed.
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
For some users, things will work automatically:
- Fedora seems to use a ["universal" udev rule for FIDO devices](https://github.com/amluto/u2f-hidraw-policy)
- Our udev rule made it into [libu2f-host](https://github.com/Yubico/libu2f-host/) v1.1.10
- Arch Linux [has this package](https://www.archlinux.org/packages/community/x86_64/libu2f-host/)
- [Debian sid](https://packages.debian.org/sid/libu2f-udev) and [Ubuntu Eon](https://packages.ubuntu.com/eoan/libu2f-udev) can use the `libu2f-udev` package
- Debian Buster and Ubuntu Disco still distribute v1.1.10, so need the manual rule
- FreeBSD has support in [u2f-devd](https://github.com/solokeys/solo/issues/144#issuecomment-500216020)
There is hope that `udev` itself will adopt the Fedora approach (which is to check for HID usage page `F1D0`, and avoids manually whitelisting each U2F/FIDO2 key): <https://github.com/systemd/systemd/issues/11996>.
Further progress is tracked in: <https://github.com/solokeys/solo/issues/144>.
If you still need to setup a rule, a simple way to do it is:
Create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory, for instance the following rule should cover normal access (it has to be on one line):
```
git clone https://github.com/solokeys/solo.git
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
```
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
```
sudo udevadm control --reload-rules && sudo udevadm trigger
```
A simple way to setup both the udev rule and the udevadm reload is:
```
git clone git@github.com:solokeys/solo.git
cd solo/udev
make setup
```
Or, manually, create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory.
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
```
sudo udevadm control --reload-rules && sudo udevadm trigger
```
We are working on getting user access to Solo keys enabled automatically in common Linux distributions: <https://github.com/solokeys/solo/issues/144>.
# How do udev rules work and why are they needed

View File

@ -1,136 +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.
// iso7816:2013. 5.3.2 Decoding conventions for command bodies
#include "apdu.h"
uint16_t apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu)
{
EXT_APDU_HEADER *hapdu = (EXT_APDU_HEADER *)data;
apdu->cla = hapdu->cla & 0xef; // mask chaining bit if any
apdu->ins = hapdu->ins;
apdu->p1 = hapdu->p1;
apdu->p2 = hapdu->p2;
apdu->lc = 0;
apdu->data = NULL;
apdu->le = 0;
apdu->extended_apdu = false;
apdu->case_type = 0x00;
uint8_t b0 = hapdu->lc[0];
// case 1
if (len == 4)
{
apdu->case_type = 0x01;
}
// case 2S (Le)
if (len == 5)
{
apdu->case_type = 0x02;
apdu->le = b0;
if (!apdu->le)
apdu->le = 0x100;
}
// case 3S (Lc + data)
if (len == 5U + b0 && b0 != 0)
{
apdu->case_type = 0x03;
apdu->lc = b0;
}
// case 4S (Lc + data + Le)
if (len == 5U + b0 + 1U && b0 != 0)
{
apdu->case_type = 0x04;
apdu->lc = b0;
apdu->le = data[len - 1];
if (!apdu->le)
apdu->le = 0x100;
}
// extended length apdu
if (len >= 7 && b0 == 0)
{
uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];
if (len - 7 < extlen)
{
return SW_WRONG_LENGTH;
}
// case 2E (Le) - extended
if (len == 7)
{
apdu->case_type = 0x12;
apdu->extended_apdu = true;
apdu->le = extlen;
if (!apdu->le)
apdu->le = 0x10000;
}
// case 3E (Lc + data) - extended
if (len == 7U + extlen)
{
apdu->case_type = 0x13;
apdu->extended_apdu = true;
apdu->lc = extlen;
}
// case 4E (Lc + data + Le) - extended 2-byte Le
if (len == 7U + extlen + 2U)
{
apdu->case_type = 0x14;
apdu->extended_apdu = true;
apdu->lc = extlen;
apdu->le = (data[len - 2] << 8) + data[len - 1];
if (!apdu->le)
apdu->le = 0x10000;
}
// case 4E (Lc + data + Le) - extended 3-byte Le
if (len == 7U + extlen + 3U && data[len - 3] == 0)
{
apdu->case_type = 0x24;
apdu->extended_apdu = true;
apdu->lc = extlen;
apdu->le = (data[len - 2] << 8) + data[len - 1];
if (!apdu->le)
apdu->le = 0x10000;
}
}
else
{
if ((len > 5) && (len - 5 < hapdu->lc[0]))
{
return SW_WRONG_LENGTH;
}
}
if (!apdu->case_type)
{
return SW_COND_USE_NOT_SATISFIED;
}
if (apdu->lc)
{
if (apdu->extended_apdu)
{
apdu->data = data + 7;
} else {
apdu->data = data + 5;
}
}
return 0;
}

View File

@ -2,8 +2,6 @@
#define _APDU_H_
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
typedef struct
{
@ -14,50 +12,19 @@ typedef struct
uint8_t lc;
} __attribute__((packed)) APDU_HEADER;
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint8_t lc[3];
} __attribute__((packed)) EXT_APDU_HEADER;
typedef struct
{
uint8_t cla;
uint8_t ins;
uint8_t p1;
uint8_t p2;
uint16_t lc;
uint8_t *data;
uint32_t le;
bool extended_apdu;
uint8_t case_type;
} __attribute__((packed)) APDU_STRUCT;
extern uint16_t apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu);
#define APDU_FIDO_U2F_REGISTER 0x01
#define APDU_FIDO_U2F_AUTHENTICATE 0x02
#define APDU_FIDO_U2F_VERSION 0x03
#define APDU_FIDO_NFCCTAP_MSG 0x10
#define APDU_FIDO_U2F_VENDOR_FIRST 0xc0 // First vendor defined command
#define APDU_FIDO_U2F_VENDOR_LAST 0xff // Last vendor defined command
#define APDU_SOLO_RESET 0xee
#define APDU_INS_SELECT 0xA4
#define APDU_INS_READ_BINARY 0xB0
#define APDU_GET_RESPONSE 0xC0
#define SW_SUCCESS 0x9000
#define SW_GET_RESPONSE 0x6100 // Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE.
#define SW_WRONG_LENGTH 0x6700
#define SW_COND_USE_NOT_SATISFIED 0x6985
#define SW_FILE_NOT_FOUND 0x6a82
#define SW_INCORRECT_P1P2 0x6a86
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
#define SW_CLA_INVALID 0x6e00
#define SW_INTERNAL_EXCEPTION 0x6f00
#endif //_APDU_H_

View File

@ -47,7 +47,7 @@ typedef enum
#endif
const uint8_t * attestation_cert_der;
const uint8_t attestation_cert_der[];
const uint16_t attestation_cert_der_size;
const uint8_t attestation_key[];
const uint16_t attestation_key_size;
@ -262,11 +262,6 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
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;
@ -338,7 +333,7 @@ void crypto_aes256_encrypt(uint8_t * buf, int length)
}
const uint8_t _attestation_cert_der[] =
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"
@ -365,11 +360,9 @@ const uint8_t _attestation_cert_der[] =
"\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 uint8_t * attestation_cert_der = (const uint8_t *)_attestation_cert_der;
uint16_t attestation_cert_der_get_size(){
return sizeof(_attestation_cert_der)-1;
}
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";

View File

@ -26,7 +26,6 @@ void crypto_sha512_final(uint8_t * hash);
void crypto_ecc256_init();
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_load_key(uint8_t * data, int len, uint8_t * data2, int len2);
void crypto_ecc256_load_attestation_key();
@ -39,7 +38,6 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey);
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret);
#define CRYPTO_TRANSPORT_KEY2 ((uint8_t*)2)
#define CRYPTO_TRANSPORT_KEY ((uint8_t*)1)
#define CRYPTO_MASTER_KEY ((uint8_t*)0)
@ -54,7 +52,10 @@ void crypto_reset_master_secret();
void crypto_load_master_secret(uint8_t * key);
extern const uint8_t * attestation_cert_der;
uint16_t attestation_cert_der_get_size();
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

View File

@ -25,11 +25,11 @@
#include "extensions.h"
#include "device.h"
#include "data_migration.h"
uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
uint8_t KEY_AGREEMENT_PUB[64];
static uint8_t KEY_AGREEMENT_PRIV[32];
static uint8_t PIN_CODE_HASH[32];
static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
AuthenticatorState STATE;
@ -256,9 +256,7 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
switch(algtype)
{
case COSE_ALG_ES256:
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
crypto_ecc256_derive_public_key(hmac_input, len, x, y);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
break;
default:
printf2(TAG_ERR,"Error, COSE alg %d not supported\n", algtype);
@ -357,9 +355,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
}
// Generate credRandom
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
// Decrypt saltEnc
crypto_aes256_init(shared_secret, NULL);
@ -434,28 +432,6 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
return sizeof(CredentialId);
}
static int ctap2_user_presence_test()
{
device_set_status(CTAPHID_STATUS_UPNEEDED);
int ret = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
if ( ret > 1 )
{
return CTAP2_ERR_PROCESSING;
}
else if ( ret > 0 )
{
return CTAP1_ERR_SUCCESS;
}
else if (ret < 0)
{
return CTAP2_ERR_KEEPALIVE_CANCEL;
}
else
{
return CTAP2_ERR_ACTION_TIMEOUT;
}
}
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
{
CborEncoder cose_key;
@ -483,22 +459,23 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
count = auth_data_update_count(&authData->head);
device_set_status(CTAPHID_STATUS_UPNEEDED);
int but;
but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
if (CTAP2_ERR_PROCESSING == but)
but = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
if (!but)
{
authData->head.flags = (0 << 0); // User presence disabled
return CTAP2_ERR_OPERATION_DENIED;
}
else
else if (but < 0) // Cancel
{
check_retr(but);
authData->head.flags = (1 << 0); // User presence
return CTAP2_ERR_KEEPALIVE_CANCEL;
}
device_set_status(CTAPHID_STATUS_PROCESSING);
authData->head.flags = (but << 0);
authData->head.flags |= (ctap_is_pin_set() << 2);
@ -628,6 +605,7 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
crypto_sha256_final(hashbuf);
crypto_ecc256_sign(hashbuf, 32, sigbuf);
return ctap_encode_der_sig(sigbuf,sigder);
}
@ -661,7 +639,7 @@ uint8_t ctap_add_attest_statement(CborEncoder * map, uint8_t * sigder, int len)
ret = cbor_encoder_create_array(&stmtmap, &x5carr, 1);
check_ret(ret);
{
ret = cbor_encode_byte_string(&x5carr, attestation_cert_der, attestation_cert_der_get_size());
ret = cbor_encode_byte_string(&x5carr, attestation_cert_der, attestation_cert_der_size);
check_ret(ret);
ret = cbor_encoder_close_container(&stmtmap, &x5carr);
check_ret(ret);
@ -682,23 +660,14 @@ int ctap_authenticate_credential(struct rpId * rp, CTAP_credentialDescriptor * d
switch(desc->type)
{
case PUB_KEY_CRED_PUB_KEY:
crypto_sha256_init();
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(rpIdHash);
printf1(TAG_RED,"rpId: %s\r\n", rp->id); dump_hex1(TAG_RED,rp->id, rp->size);
if (memcmp(desc->credential.id.rpIdHash, rpIdHash, 32) != 0)
{
return 0;
}
make_auth_tag(rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag);
make_auth_tag(desc->credential.id.rpIdHash, desc->credential.id.nonce, desc->credential.id.count, tag);
return (memcmp(desc->credential.id.tag, tag, CREDENTIAL_TAG_SIZE) == 0);
break;
case PUB_KEY_CRED_CTAP1:
crypto_sha256_init();
crypto_sha256_update(rp->id, rp->size);
crypto_sha256_final(rpIdHash);
return u2f_authenticate_credential((struct u2f_key_handle *)&desc->credential.id, U2F_KEY_HANDLE_SIZE,rpIdHash);
return u2f_authenticate_credential((struct u2f_key_handle *)&desc->credential.id, rpIdHash);
break;
case PUB_KEY_CRED_CUSTOM:
return is_extension_request(getAssertionState.customCredId, getAssertionState.customCredIdSize);
@ -732,8 +701,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
if (MC.pinAuthEmpty)
{
check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS))
{
return CTAP2_ERR_OPERATION_DENIED;
}
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
{
@ -755,7 +727,7 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
}
if (MC.up == 1 || MC.up == 0)
if (MC.up)
{
return CTAP2_ERR_INVALID_OPTION;
}
@ -1084,7 +1056,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
else
#endif
{
sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
}
{
@ -1165,8 +1137,11 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
if (GA.pinAuthEmpty)
{
check_retr( ctap2_user_presence_test(CTAP2_UP_DELAY_MS) );
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS))
{
return CTAP2_ERR_OPERATION_DENIED;
}
return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if (GA.pinAuthPresent)
{
@ -1248,9 +1223,8 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
else
#endif
{
device_disable_up(GA.up == 0);
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, &auth_data_buf_sz, NULL);
device_disable_up(false);
check_retr(ret);
((CTAP_authDataHeader *)auth_data_buf)->flags &= ~(1 << 2);
@ -1308,13 +1282,11 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
uint8_t hmac[32];
int ret;
// Validate incoming data packet len
if (len < 64)
{
return CTAP1_ERR_OTHER;
}
// Validate device's state
if (ctap_is_pin_set()) // Check first, prevent SCA
{
if (ctap_device_locked())
@ -1327,7 +1299,6 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
}
}
// calculate shared_secret
crypto_ecc256_shared_secret(platform_pubkey, KEY_AGREEMENT_PRIV, shared_secret);
crypto_sha256_init();
@ -1350,7 +1321,6 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
return CTAP2_ERR_PIN_AUTH_INVALID;
}
// decrypt new PIN with shared secret
crypto_aes256_init(shared_secret, NULL);
while((len & 0xf) != 0) // round up to nearest AES block size multiple
@ -1360,7 +1330,7 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
crypto_aes256_decrypt(pinEnc, len);
// validate new PIN (length)
ret = trailing_zeros(pinEnc, NEW_PIN_ENC_MIN_SIZE - 1);
ret = NEW_PIN_ENC_MIN_SIZE - ret;
@ -1376,8 +1346,6 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
dump_hex1(TAG_CP, pinEnc, ret);
}
// validate device's state, decrypt and compare pinHashEnc (user provided current PIN hash) with stored PIN_CODE_HASH
if (ctap_is_pin_set())
{
if (ctap_device_locked())
@ -1390,14 +1358,7 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
}
crypto_aes256_reset_iv(NULL);
crypto_aes256_decrypt(pinHashEnc, 16);
uint8_t pinHashEncSalted[32];
crypto_sha256_init();
crypto_sha256_update(pinHashEnc, 16);
crypto_sha256_update(STATE.PIN_SALT, sizeof(STATE.PIN_SALT));
crypto_sha256_final(pinHashEncSalted);
if (memcmp(pinHashEncSalted, STATE.PIN_CODE_HASH, 16) != 0)
if (memcmp(pinHashEnc, PIN_CODE_HASH, 16) != 0)
{
ctap_reset_key_agreement();
ctap_decrement_pin_attempts();
@ -1413,7 +1374,6 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
}
}
// set new PIN (update and store PIN_CODE_HASH)
ctap_update_pin(pinEnc, ret);
return 0;
@ -1433,16 +1393,12 @@ uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubke
crypto_aes256_decrypt(pinHashEnc, 16);
uint8_t pinHashEncSalted[32];
crypto_sha256_init();
crypto_sha256_update(pinHashEnc, 16);
crypto_sha256_update(STATE.PIN_SALT, sizeof(STATE.PIN_SALT));
crypto_sha256_final(pinHashEncSalted);
if (memcmp(pinHashEncSalted, STATE.PIN_CODE_HASH, 16) != 0)
if (memcmp(pinHashEnc, PIN_CODE_HASH, 16) != 0)
{
printf2(TAG_ERR,"Pin does not match!\n");
printf2(TAG_ERR,"platform-pin-hash: "); dump_hex1(TAG_ERR, pinHashEnc, 16);
printf2(TAG_ERR,"authentic-pin-hash: "); dump_hex1(TAG_ERR, STATE.PIN_CODE_HASH, 16);
printf2(TAG_ERR,"authentic-pin-hash: "); dump_hex1(TAG_ERR, PIN_CODE_HASH, 16);
printf2(TAG_ERR,"shared-secret: "); dump_hex1(TAG_ERR, shared_secret, 32);
printf2(TAG_ERR,"platform-pubkey: "); dump_hex1(TAG_ERR, platform_pubkey, 64);
printf2(TAG_ERR,"device-pubkey: "); dump_hex1(TAG_ERR, KEY_AGREEMENT_PUB, 64);
@ -1519,11 +1475,6 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encode_int(&map, RESP_keyAgreement);
check_ret(ret);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
crypto_ecc256_compute_public_key(KEY_AGREEMENT_PRIV, KEY_AGREEMENT_PUB);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256);
check_retr(ret);
@ -1653,6 +1604,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
switch(cmd)
{
case CTAP_MAKE_CREDENTIAL:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
timestamp();
status = ctap_make_credential(&encoder, pkt_raw, length);
@ -1663,6 +1615,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_GET_ASSERTION:
device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
timestamp();
status = ctap_get_assertion(&encoder, pkt_raw, length);
@ -1694,11 +1647,14 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_RESET:
printf1(TAG_CTAP,"CTAP_RESET\n");
status = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
if (status == CTAP1_ERR_SUCCESS)
if (ctap_user_presence_test(CTAP2_UP_DELAY_MS))
{
ctap_reset();
}
else
{
status = CTAP2_ERR_OPERATION_DENIED;
}
break;
case GET_NEXT_ASSERTION:
printf1(TAG_CTAP,"CTAP_NEXT_ASSERTION\n");
@ -1720,7 +1676,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
default:
status = CTAP1_ERR_INVALID_COMMAND;
printf2(TAG_ERR,"error, invalid cmd: 0x%02x\n", cmd);
printf2(TAG_ERR,"error, invalid cmd\n");
}
done:
@ -1750,39 +1706,12 @@ static void ctap_state_init()
STATE.remaining_tries = PIN_LOCKOUT_ATTEMPTS;
STATE.is_pin_set = 0;
STATE.rk_stored = 0;
STATE.data_version = STATE_VERSION;
ctap_reset_rk();
if (ctap_generate_rng(STATE.PIN_SALT, sizeof(STATE.PIN_SALT)) != 1) {
printf2(TAG_ERR, "Error, rng failed\n");
exit(1);
}
printf1(TAG_STOR, "Generated PIN SALT: ");
dump_hex1(TAG_STOR, STATE.PIN_SALT, sizeof STATE.PIN_SALT);
}
/** Overwrite master secret from external source.
* @param keybytes an array of KEY_SPACE_BYTES length.
*
* This function should only be called from a privilege mode.
*/
void ctap_load_external_keys(uint8_t * keybytes){
memmove(STATE.key_space, keybytes, KEY_SPACE_BYTES);
authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1);
crypto_load_master_secret(STATE.key_space);
}
#include "version.h"
void ctap_init()
{
printf1(TAG_ERR,"Current firmware version address: %p\r\n", &firmware_version);
printf1(TAG_ERR,"Current firmware version: %d.%d.%d.%d (%02x.%02x.%02x.%02x)\r\n",
firmware_version.major, firmware_version.minor, firmware_version.patch, firmware_version.reserved,
firmware_version.major, firmware_version.minor, firmware_version.patch, firmware_version.reserved
);
crypto_ecc256_init();
authenticator_read_state(&STATE);
@ -1811,12 +1740,14 @@ void ctap_init()
}
}
do_migration_if_required(&STATE);
crypto_load_master_secret(STATE.key_space);
if (ctap_is_pin_set())
{
printf1(TAG_STOR,"pin code: \"%s\"\n", STATE.pin_code);
crypto_sha256_init();
crypto_sha256_update(STATE.pin_code, STATE.pin_code_length);
crypto_sha256_final(PIN_CODE_HASH);
printf1(TAG_STOR, "attempts_left: %d\n", STATE.remaining_tries);
}
else
@ -1834,7 +1765,10 @@ void ctap_init()
exit(1);
}
ctap_reset_key_agreement();
if (device_is_nfc() != NFC_IS_ACTIVE)
{
ctap_reset_key_agreement();
}
#ifdef BRIDGE_TO_WALLET
wallet_init();
@ -1848,38 +1782,34 @@ uint8_t ctap_is_pin_set()
return STATE.is_pin_set == 1;
}
/**
* Set new PIN, by updating PIN hash. Save state.
* Globals: STATE
* @param pin new PIN (raw)
* @param len pin array length
*/
uint8_t ctap_pin_matches(uint8_t * pin, int len)
{
return memcmp(pin, STATE.pin_code, len) == 0;
}
void ctap_update_pin(uint8_t * pin, int len)
{
if (len >= NEW_PIN_ENC_MIN_SIZE || len < 4)
if (len > NEW_PIN_ENC_MIN_SIZE || len < 4)
{
printf2(TAG_ERR, "Update pin fail length\n");
exit(1);
}
memset(STATE.pin_code, 0, NEW_PIN_ENC_MIN_SIZE);
memmove(STATE.pin_code, pin, len);
STATE.pin_code_length = len;
STATE.pin_code[NEW_PIN_ENC_MIN_SIZE - 1] = 0;
crypto_sha256_init();
crypto_sha256_update(pin, len);
uint8_t intermediateHash[32];
crypto_sha256_final(intermediateHash);
crypto_sha256_init();
crypto_sha256_update(intermediateHash, 16);
memset(intermediateHash, 0, sizeof(intermediateHash));
crypto_sha256_update(STATE.PIN_SALT, sizeof(STATE.PIN_SALT));
crypto_sha256_final(STATE.PIN_CODE_HASH);
crypto_sha256_update(STATE.pin_code, len);
crypto_sha256_final(PIN_CODE_HASH);
STATE.is_pin_set = 1;
authenticator_write_state(&STATE, 1);
authenticator_write_state(&STATE, 0);
printf1(TAG_CTAP, "New pin set: %s [%d]\n", pin, len);
dump_hex1(TAG_ERR, STATE.PIN_CODE_HASH, sizeof(STATE.PIN_CODE_HASH));
printf1(TAG_CTAP, "New pin set: %s\n", STATE.pin_code);
}
uint8_t ctap_decrement_pin_attempts()
@ -1896,7 +1826,9 @@ uint8_t ctap_decrement_pin_attempts()
if (ctap_device_locked())
{
lock_device_permanently();
memset(PIN_TOKEN,0,sizeof(PIN_TOKEN));
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
printf1(TAG_CP, "Device locked!\n");
}
}
else
@ -2035,7 +1967,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
static void ctap_reset_key_agreement()
{
ctap_generate_rng(KEY_AGREEMENT_PRIV, sizeof(KEY_AGREEMENT_PRIV));
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
}
void ctap_reset()
@ -2052,17 +1984,8 @@ void ctap_reset()
}
ctap_reset_state();
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
ctap_reset_key_agreement();
crypto_load_master_secret(STATE.key_space);
}
void lock_device_permanently() {
memset(PIN_TOKEN, 0, sizeof(PIN_TOKEN));
memset(STATE.PIN_CODE_HASH, 0, sizeof(STATE.PIN_CODE_HASH));
printf1(TAG_CP, "Device locked!\n");
authenticator_write_state(&STATE, 0);
authenticator_write_state(&STATE, 1);
crypto_reset_master_secret();
}

View File

@ -131,7 +131,7 @@
#define PIN_LOCKOUT_ATTEMPTS 8 // Number of attempts total
#define PIN_BOOT_ATTEMPTS 3 // number of attempts per boot
#define CTAP2_UP_DELAY_MS 29000
#define CTAP2_UP_DELAY_MS 5000
typedef struct
{
@ -359,8 +359,5 @@ uint16_t ctap_key_len(uint8_t index);
extern uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
extern uint8_t KEY_AGREEMENT_PUB[64];
void lock_device_permanently();
void ctap_load_external_keys(uint8_t * keybytes);
#endif

View File

@ -49,7 +49,6 @@
#define CTAP2_ERR_PIN_POLICY_VIOLATION 0x37
#define CTAP2_ERR_PIN_TOKEN_EXPIRED 0x38
#define CTAP2_ERR_REQUEST_TOO_LARGE 0x39
#define CTAP2_ERR_ACTION_TIMEOUT 0x3A
#define CTAP1_ERR_OTHER 0x7F
#define CTAP2_ERR_SPEC_LAST 0xDF
#define CTAP2_ERR_EXTENSION_FIRST 0xE0

View File

@ -715,7 +715,6 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
CborValue it,map;
memset(MC, 0, sizeof(CTAP_makeCredential));
MC->up = 0xff;
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_retr(ret);
@ -930,15 +929,7 @@ uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor *
buflen = sizeof(type);
ret = cbor_value_copy_text_string(&val, type, &buflen, NULL);
if (ret == CborErrorOutOfMemory)
{
cred->type = PUB_KEY_CRED_UNKNOWN;
}
else
{
check_ret(ret);
}
check_ret(ret);
if (strncmp(type, "public-key",11) == 0)
{
@ -1011,7 +1002,6 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
memset(GA, 0, sizeof(CTAP_getAssertion));
GA->creds = getAssertionState.creds; // Save stack memory
GA->up = 0xff;
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
check_ret(ret);

View File

@ -16,7 +16,6 @@
#include "util.h"
#include "log.h"
#include "extensions.h"
#include "version.h"
// move custom SHA512 command out,
// and the following headers too
@ -539,14 +538,11 @@ extern void _check_ret(CborError ret, int line, const char * filename);
#define check_hardcore(r) _check_ret(r,__LINE__, __FILE__);\
if ((r) != CborNoError) exit(1);
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb);
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
{
uint8_t cmd = 0;
uint8_t cmd;
uint32_t cid;
int len = 0;
int len;
#ifndef DISABLE_CTAPHID_CBOR
int status;
#endif
@ -556,10 +552,6 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
CTAP_RESPONSE ctap_resp;
int bufstatus = ctaphid_buffer_packet(pkt_raw, &cmd, &cid, &len);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = cmd;
if (bufstatus == HID_IGNORE)
{
@ -595,6 +587,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
case CTAPHID_PING:
printf1(TAG_HID,"CTAPHID_PING\n");
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_PING;
wb.bcnt = len;
timestamp();
ctaphid_write(&wb, ctap_buffer, len);
@ -607,9 +602,13 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
case CTAPHID_WINK:
printf1(TAG_HID,"CTAPHID_WINK\n");
ctaphid_write_buffer_init(&wb);
device_wink();
wb.cid = cid;
wb.cmd = CTAPHID_WINK;
ctaphid_write(&wb,NULL,0);
break;
@ -634,6 +633,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
ctap_response_init(&ctap_resp);
status = ctap_request(ctap_buffer, len, &ctap_resp);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_CBOR;
wb.bcnt = (ctap_resp.length+1);
@ -664,6 +666,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
ctap_response_init(&ctap_resp);
u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_MSG;
wb.bcnt = (ctap_resp.length);
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
@ -674,14 +679,209 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
printf1(TAG_HID,"CTAPHID_CANCEL\n");
is_busy = 0;
break;
#if defined(IS_BOOTLOADER)
case CTAPHID_BOOT:
printf1(TAG_HID,"CTAPHID_BOOT\n");
ctap_response_init(&ctap_resp);
u2f_set_writeback_buffer(&ctap_resp);
is_busy = bootloader_bridge(len, ctap_buffer);
default:
if (ctaphid_custom_command(len, &ctap_resp, &wb) != 0){
is_busy = 0;
}else{
printf2(TAG_ERR, "error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_BOOT;
wb.bcnt = (ctap_resp.length + 1);
ctaphid_write(&wb, &is_busy, 1);
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
#endif
#if defined(SOLO_HACKER)
case CTAPHID_ENTERBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_solo_bootloader();
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_ENTERBOOT;
wb.bcnt = 0;
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
case CTAPHID_ENTERSTBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_st_bootloader();
break;
#endif
#if !defined(IS_BOOTLOADER)
case CTAPHID_GETRNG:
printf1(TAG_HID,"CTAPHID_GETRNG\n");
ctap_response_init(&ctap_resp);
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_GETRNG;
wb.bcnt = ctap_buffer[0];
if (!wb.bcnt)
wb.bcnt = 57;
memset(ctap_buffer,0,wb.bcnt);
ctap_generate_rng(ctap_buffer, wb.bcnt);
ctaphid_write(&wb, &ctap_buffer, wb.bcnt);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
#endif
#if defined(SOLO_HACKER) && (DEBUG_LEVEL > 0) && (!IS_BOOTLOADER == 1)
case CTAPHID_PROBE:
/*
* Expects CBOR-serialized data of the form
* {"subcommand": "hash_type", "data": b"the_data"}
* with hash_type in SHA256, SHA512
*/
// some random logging
printf1(TAG_HID,"CTAPHID_PROBE\n");
// initialise CTAP response object
ctap_response_init(&ctap_resp);
// initialise write buffer
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_PROBE;
// prepare parsing (or halt)
int ret;
CborParser parser;
CborValue it, map;
ret = cbor_parser_init(
ctap_buffer, (size_t) buffer_len(),
// strictly speaking, CTAP is not RFC canonical...
CborValidateCanonicalFormat,
&parser, &it);
check_hardcore(ret);
CborType type = cbor_value_get_type(&it);
if (type != CborMapType) exit(1);
ret = cbor_value_enter_container(&it,&map);
check_hardcore(ret);
size_t map_length = 0;
ret = cbor_value_get_map_length(&it, &map_length);
if (map_length != 2) exit(1);
// parse subcommand (or halt)
CborValue val;
ret = cbor_value_map_find_value(&it, "subcommand", &val);
check_hardcore(ret);
if (!cbor_value_is_text_string(&val))
exit(1);
int sha_version = 0;
bool found = false;
if (!found) {
ret = cbor_value_text_string_equals(
&val, "SHA256", &found);
check_hardcore(ret);
if (found)
sha_version = 256;
}
if (!found) {
ret = cbor_value_text_string_equals(
&val, "SHA512", &found);
check_hardcore(ret);
if (found)
sha_version = 512;
}
if (sha_version == 0)
exit(1);
// parse data (or halt)
ret = cbor_value_map_find_value(&it, "data", &val);
check_hardcore(ret);
if (!cbor_value_is_byte_string(&val))
exit(1);
size_t data_length = 0;
ret = cbor_value_calculate_string_length(&val, &data_length);
check_hardcore(ret);
if (data_length > 6*1024)
exit(1);
unsigned char data[6*1024];
ret = cbor_value_copy_byte_string (
&val, &data[0], &data_length, &val);
check_hardcore(ret);
// execute subcommand
if (sha_version == 256) {
// calculate hash
crypto_sha256_init();
crypto_sha256_update(data, data_length);
crypto_sha256_final(ctap_buffer);
// write output
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
}
if (sha_version == 512) {
// calculate hash
crypto_sha512_init();
crypto_sha512_update(data, data_length);
crypto_sha512_final(ctap_buffer);
// write output
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
}
// finalize
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
/*
case CTAPHID_SHA256:
// some random logging
printf1(TAG_HID,"CTAPHID_SHA256\n");
// initialise CTAP response object
ctap_response_init(&ctap_resp);
// initialise write buffer
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_SHA256;
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
// calculate hash
crypto_sha256_init();
crypto_sha256_update(ctap_buffer, buffer_len());
crypto_sha256_final(ctap_buffer);
// copy to output
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
case CTAPHID_SHA512:
// some random logging
printf1(TAG_HID,"CTAPHID_SHA512\n");
// initialise CTAP response object
ctap_response_init(&ctap_resp);
// initialise write buffer
ctaphid_write_buffer_init(&wb);
wb.cid = cid;
wb.cmd = CTAPHID_SHA512;
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
// calculate hash
crypto_sha512_init();
crypto_sha512_update(ctap_buffer, buffer_len());
crypto_sha512_final(ctap_buffer);
// copy to output
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
ctaphid_write(&wb, NULL, 0);
is_busy = 0;
break;
*/
#endif
default:
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
ctaphid_send_error(cid, CTAP1_ERR_INVALID_COMMAND);
break;
}
cid_del(cid);
buffer_reset();
@ -691,114 +891,3 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
else return 0;
}
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb)
{
ctap_response_init(ctap_resp);
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
uint32_t param;
#endif
#if defined(IS_BOOTLOADER)
uint8_t is_busy;
#endif
switch(wb->cmd)
{
#if defined(IS_BOOTLOADER)
case CTAPHID_BOOT:
printf1(TAG_HID,"CTAPHID_BOOT\n");
u2f_set_writeback_buffer(ctap_resp);
is_busy = bootloader_bridge(len, ctap_buffer);
wb->bcnt = 1 + ctap_resp->length;
ctaphid_write(wb, &is_busy, 1);
ctaphid_write(wb, ctap_resp->data, ctap_resp->length);
ctaphid_write(wb, NULL, 0);
return 1;
#endif
#if defined(SOLO)
case CTAPHID_ENTERBOOT:
printf1(TAG_HID,"CTAPHID_ENTERBOOT\n");
boot_solo_bootloader();
wb->bcnt = 0;
ctaphid_write(wb, NULL, 0);
return 1;
#endif
#if !defined(IS_BOOTLOADER)
case CTAPHID_GETRNG:
printf1(TAG_HID,"CTAPHID_GETRNG\n");
wb->bcnt = ctap_buffer[0];
if (!wb->bcnt)
wb->bcnt = 57;
memset(ctap_buffer,0,wb->bcnt);
ctap_generate_rng(ctap_buffer, wb->bcnt);
ctaphid_write(wb, ctap_buffer, wb->bcnt);
ctaphid_write(wb, NULL, 0);
return 1;
break;
#endif
case CTAPHID_GETVERSION:
printf1(TAG_HID,"CTAPHID_GETVERSION\n");
wb->bcnt = 4;
ctap_buffer[0] = SOLO_VERSION_MAJ;
ctap_buffer[1] = SOLO_VERSION_MIN;
ctap_buffer[2] = SOLO_VERSION_PATCH;
#if defined(SOLO)
ctap_buffer[3] = solo_is_locked();
#else
ctap_buffer[3] = 0;
#endif
ctaphid_write(wb, ctap_buffer, 4);
ctaphid_write(wb, NULL, 0);
return 1;
break;
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
case CTAPHID_LOADKEY:
/**
* Load external key. Useful for enabling backups.
* bytes: 4 96
* payload: | counter_increase (BE) | master_key |
*
* Counter should be increased by a large amount, e.g. (0x10000000)
* to outdo any previously lost/broken keys.
*/
printf1(TAG_HID,"CTAPHID_LOADKEY\n");
if (len != 100)
{
printf2(TAG_ERR,"Error, invalid length.\n");
ctaphid_send_error(wb->cid, CTAP1_ERR_INVALID_LENGTH);
return 1;
}
// Ask for THREE button presses
if (ctap_user_presence_test(8000) > 0)
if (ctap_user_presence_test(8000) > 0)
if (ctap_user_presence_test(8000) > 0)
{
ctap_load_external_keys(ctap_buffer + 4);
param = ctap_buffer[3];
param |= ctap_buffer[2] << 8;
param |= ctap_buffer[1] << 16;
param |= ctap_buffer[0] << 24;
ctap_atomic_count(param);
wb->bcnt = 0;
ctaphid_write(wb, NULL, 0);
return 1;
}
printf2(TAG_ERR, "Error, invalid length.\n");
ctaphid_send_error(wb->cid, CTAP2_ERR_OPERATION_DENIED);
return 1;
#endif
}
return 0;
}

View File

@ -28,8 +28,6 @@
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
#define CTAPHID_GETVERSION (TYPE_INIT | 0x61)
#define CTAPHID_LOADKEY (TYPE_INIT | 0x62)
// reserved for debug, not implemented except for HACKER and DEBUG_LEVEl > 0
#define CTAPHID_PROBE (TYPE_INIT | 0x70)

View File

@ -1,91 +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.
#include "data_migration.h"
#include "log.h"
#include "device.h"
#include "crypto.h"
// TODO move from macro to function/assert for better readability?
#define check(x) assert(state_prev_0xff->x == state_tmp_ptr->x);
#define check_buf(x) assert(memcmp(state_prev_0xff->x, state_tmp_ptr->x, sizeof(state_tmp_ptr->x)) == 0);
bool migrate_from_FF_to_01(AuthenticatorState_0xFF* state_prev_0xff, AuthenticatorState_0x01* state_tmp_ptr){
// Calculate PIN hash, and replace PIN raw storage with it; add version to structure
// other ingredients do not change
if (state_tmp_ptr->data_version != 0xFF)
return false;
static_assert(sizeof(AuthenticatorState_0xFF) <= sizeof(AuthenticatorState_0x01), "New state structure is smaller, than current one, which is not handled");
if (ctap_generate_rng(state_tmp_ptr->PIN_SALT, sizeof(state_tmp_ptr->PIN_SALT)) != 1) {
printf2(TAG_ERR, "Error, rng failed\n");
return false;
}
if (state_prev_0xff->is_pin_set){
crypto_sha256_init();
crypto_sha256_update(state_prev_0xff->pin_code, state_prev_0xff->pin_code_length);
uint8_t intermediateHash[32];
crypto_sha256_final(intermediateHash);
crypto_sha256_init();
crypto_sha256_update(intermediateHash, 16);
memset(intermediateHash, 0, sizeof(intermediateHash));
crypto_sha256_update(state_tmp_ptr->PIN_SALT, sizeof(state_tmp_ptr->PIN_SALT));
crypto_sha256_final(state_tmp_ptr->PIN_CODE_HASH);
}
assert(state_tmp_ptr->_reserved == state_prev_0xff->pin_code_length);
state_tmp_ptr->_reserved = 0xFF;
state_tmp_ptr->data_version = 1;
check(is_initialized);
check(is_pin_set);
check(remaining_tries);
check(rk_stored);
check_buf(key_lens);
check_buf(key_space);
assert(state_tmp_ptr->data_version != 0xFF);
return true;
}
void save_migrated_state(AuthenticatorState *state_tmp_ptr) {
memmove(&STATE, state_tmp_ptr, sizeof(AuthenticatorState));
authenticator_write_state(state_tmp_ptr, 0);
authenticator_write_state(state_tmp_ptr, 1);
}
void do_migration_if_required(AuthenticatorState* state_current){
// Currently handles only state structures with the same size, or bigger
// FIXME rework to raw buffers with fixed size to allow state structure size decrease
if(!state_current->is_initialized)
return;
AuthenticatorState state_tmp;
AuthenticatorState state_previous;
authenticator_read_state(&state_previous);
authenticator_read_state(&state_tmp);
if(state_current->data_version == 0xFF){
printf2(TAG_ERR, "Running migration\n");
bool success = migrate_from_FF_to_01((AuthenticatorState_0xFF *) &state_previous, &state_tmp);
if (!success){
printf2(TAG_ERR, "Failed migration from 0xFF to 1\n");
// FIXME discuss migration failure behavior
goto return_cleanup;
}
dump_hex1(TAG_ERR, (void*)&state_tmp, sizeof(state_tmp));
dump_hex1(TAG_ERR, (void*)&state_previous, sizeof(state_previous));
save_migrated_state(&state_tmp);
}
assert(state_current->data_version == STATE_VERSION);
return_cleanup:
memset(&state_tmp, 0, sizeof(AuthenticatorState));
memset(&state_previous, 0, sizeof(AuthenticatorState));
}

View File

@ -1,15 +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 FIDO2_PR_DATA_MIGRATION_H
#define FIDO2_PR_DATA_MIGRATION_H
#include "storage.h"
void do_migration_if_required(AuthenticatorState* state_current);
#endif //FIDO2_PR_DATA_MIGRATION_H

View File

@ -30,7 +30,6 @@ void main_loop_delay();
void heartbeat();
void device_reboot();
void authenticator_read_state(AuthenticatorState * );
@ -53,7 +52,7 @@ void device_set_status(uint32_t status);
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 1 for user is present, 0 user not present, -1 if cancel is requested.
int ctap_user_presence_test(uint32_t delay);
// Generate @num bytes of random numbers to @dest
@ -61,8 +60,8 @@ int ctap_user_presence_test(uint32_t delay);
int ctap_generate_rng(uint8_t * dst, size_t num);
// Increment atomic counter and return it.
// @param amount the amount to increase the counter by.
uint32_t ctap_atomic_count(uint32_t amount);
// Must support two counters, @sel selects counter0 or counter1.
uint32_t ctap_atomic_count(int sel);
// Verify the user
// return 1 if user is verified, 0 if not
@ -106,8 +105,6 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param);
#define NFC_IS_AVAILABLE 2
int device_is_nfc();
void device_disable_up(bool request_active);
void device_init_button();
#endif

View File

@ -95,7 +95,7 @@ int8_t wallet_pin(uint8_t subcmd, uint8_t * pinAuth, uint8_t * arg1, uint8_t * a
if (ret != 0)
return ret;
// printf1(TAG_WALLET,"Success. Pin = %s\n", STATE.pin_code);
printf1(TAG_WALLET,"Success. Pin = %s\n", STATE.pin_code);
break;
case CP_cmdChangePin:

View File

@ -50,7 +50,6 @@ struct logtag tagtable[] = {
{TAG_EXT,"EXT"},
{TAG_NFC,"NFC"},
{TAG_NFC_APDU, "NAPDU"},
{TAG_CCID, "CCID"},
};

View File

@ -44,7 +44,6 @@ typedef enum
TAG_EXT = (1 << 18),
TAG_NFC = (1 << 19),
TAG_NFC_APDU = (1 << 20),
TAG_CCID = (1 << 21),
TAG_NO_TAG = (1UL << 30),
TAG_FILENO = (1UL << 31)

View File

@ -46,7 +46,6 @@ int main(int argc, char *argv[])
TAG_GREEN|
TAG_RED|
TAG_EXT|
TAG_CCID|
TAG_ERR
);

View File

@ -11,9 +11,6 @@
#define KEY_SPACE_BYTES 128
#define MAX_KEYS (1)
#define PIN_SALT_LEN (32)
#define STATE_VERSION (1)
#define BACKUP_MARKER 0x5A
#define INITIALIZED_MARKER 0xA5
@ -22,40 +19,20 @@
#define ERR_KEY_SPACE_TAKEN (-2)
#define ERR_KEY_SPACE_EMPTY (-2)
typedef struct
{
// Pin information
uint8_t is_initialized;
uint8_t is_pin_set;
uint8_t pin_code[NEW_PIN_ENC_MIN_SIZE];
int pin_code_length;
int8_t remaining_tries;
uint16_t rk_stored;
uint16_t key_lens[MAX_KEYS];
uint8_t key_space[KEY_SPACE_BYTES];
} AuthenticatorState_0xFF;
typedef struct
{
// Pin information
uint8_t is_initialized;
uint8_t is_pin_set;
uint8_t PIN_CODE_HASH[32];
uint8_t PIN_SALT[PIN_SALT_LEN];
int _reserved;
uint8_t pin_code[NEW_PIN_ENC_MIN_SIZE];
int pin_code_length;
int8_t remaining_tries;
uint16_t rk_stored;
uint16_t key_lens[MAX_KEYS];
uint8_t key_space[KEY_SPACE_BYTES];
uint8_t data_version;
} AuthenticatorState_0x01;
typedef AuthenticatorState_0x01 AuthenticatorState;
} AuthenticatorState;
typedef struct

View File

@ -26,7 +26,6 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response();
void make_auth_tag(uint8_t * rpIdHash, uint8_t * nonce, uint32_t count, uint8_t * tag);
static CTAP_RESPONSE * _u2f_resp = NULL;
@ -114,14 +113,14 @@ end:
printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length);
}
void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp)
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp)
{
if (!header)
if (len < 5 || !req)
return;
device_disable_up(true); // disable presence test
u2f_request_ex((APDU_HEADER *)header, data, datalen, resp);
device_disable_up(false); // enable presence test
uint32_t alen = req[4];
u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp);
}
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
@ -161,9 +160,9 @@ static void dump_signature_der(uint8_t * sig)
len = ctap_encode_der_sig(sig, sigder);
u2f_response_writeback(sigder, len);
}
static int8_t u2f_load_key(struct u2f_key_handle * kh, uint8_t khl, uint8_t * appid)
static int8_t u2f_load_key(struct u2f_key_handle * kh, uint8_t * appid)
{
crypto_ecc256_load_key((uint8_t*)kh, khl, NULL, 0);
crypto_ecc256_load_key((uint8_t*)kh, U2F_KEY_HANDLE_SIZE, NULL, 0);
return 0;
}
@ -188,41 +187,21 @@ int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pu
// Return 1 if authenticate, 0 if not.
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handle_len, uint8_t * appid)
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid)
{
printf1(TAG_U2F, "checked CRED SIZE %d. (FIDO2: %d)\n", key_handle_len, sizeof(CredentialId));
uint8_t tag[U2F_KEY_HANDLE_TAG_SIZE];
if (key_handle_len == sizeof(CredentialId))
u2f_make_auth_tag(kh, appid, tag);
if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0)
{
printf1(TAG_U2F, "FIDO2 key handle detected.\n");
CredentialId * cred = (CredentialId *) kh;
// FIDO2 credential.
if (memcmp(cred->rpIdHash, appid, 32) != 0)
{
printf1(TAG_U2F, "APPID does not match rpIdHash.\n");
return 0;
}
make_auth_tag(appid, cred->nonce, cred->count, tag);
if (memcmp(cred->tag, tag, CREDENTIAL_TAG_SIZE) == 0){
return 1;
}
}else if (key_handle_len == U2F_KEY_HANDLE_SIZE)
{
u2f_make_auth_tag(kh, appid, tag);
if (memcmp(kh->tag, tag, U2F_KEY_HANDLE_TAG_SIZE) == 0)
{
return 1;
}
return 1;
}
else
{
printf1(TAG_U2F, "key handle + appid not authentic\n");
printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE);
printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE);
return 0;
}
printf1(TAG_U2F, "key handle + appid not authentic\n");
printf1(TAG_U2F, "calc tag: \n"); dump_hex1(TAG_U2F,tag, U2F_KEY_HANDLE_TAG_SIZE);
printf1(TAG_U2F, "inp tag: \n"); dump_hex1(TAG_U2F,kh->tag, U2F_KEY_HANDLE_TAG_SIZE);
return 0;
}
@ -237,7 +216,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
if (control == U2F_AUTHENTICATE_CHECK)
{
printf1(TAG_U2F, "CHECK-ONLY\r\n");
if (u2f_authenticate_credential(&req->kh, req->khl, req->app))
if (u2f_authenticate_credential(&req->kh, req->app))
{
return U2F_SW_CONDITIONS_NOT_SATISFIED;
}
@ -248,8 +227,9 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
}
if (
(control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) ||
(!u2f_authenticate_credential(&req->kh, req->khl, req->app)) || // Order of checks is important
u2f_load_key(&req->kh, req->khl, req->app) != 0
req->khl != U2F_KEY_HANDLE_SIZE ||
(!u2f_authenticate_credential(&req->kh, req->app)) || // Order of checks is important
u2f_load_key(&req->kh, req->app) != 0
)
{
@ -306,7 +286,7 @@ static int16_t u2f_register(struct u2f_register_request * req)
uint8_t * sig = (uint8_t*)req;
const uint16_t attest_size = attestation_cert_der_get_size();
const uint16_t attest_size = attestation_cert_der_size;
if ( ! ctap_user_presence_test(750))
{

View File

@ -101,9 +101,9 @@ void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
// u2f_request send a U2F message to NFC protocol
// @req data with iso7816 apdu message
// @len data length
void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp);
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp);
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handle_len, uint8_t * appid);
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid);
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
void u2f_reset_response();

View File

@ -1,13 +0,0 @@
#include "version.h"
const version_t firmware_version __attribute__ ((section (".flag"))) __attribute__ ((__used__)) = {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
// from tinycbor, for a quick static_assert
#include <compilersupport_p.h>
cbor_static_assert(sizeof(version_t) == 4);

View File

@ -17,23 +17,5 @@
#define SOLO_VERSION __STR(SOLO_VERSION_MAJ) "." __STR(SOLO_VERSION_MIN) "." __STR(SOLO_VERSION_PATCH)
#endif
#include <stdint.h>
#include <stdbool.h>
typedef struct {
union{
uint32_t raw;
struct {
uint8_t major;
uint8_t minor;
uint8_t patch;
uint8_t reserved;
};
};
} version_t;
bool is_newer(const version_t* const newer, const version_t* const older);
const version_t firmware_version ;
#endif

View File

@ -1,10 +1,14 @@
#!/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
ls
git fetch --tags
git checkout ${version}
git submodule update --init --recursive
version=$(git describe)
make cbor
@ -12,12 +16,13 @@ out_dir="/builds"
function build() {
part=${1}
output=${2}
what="${part}"
variant=${2}
output=${3:-${part}}
what="${part}-${variant}"
make full-clean
make ${what} VERSION_FULL=${version}
make ${what}
out_hex="${what}-${version}.hex"
out_sha2="${what}-${version}.sha2"
@ -27,27 +32,20 @@ function build() {
cp ${out_hex} ${out_sha2} ${out_dir}
}
build bootloader-nonverifying bootloader
build bootloader-verifying bootloader
build firmware solo
build firmware-debug-1 solo
build firmware-debug-2 solo
build firmware solo
build bootloader nonverifying
build bootloader verifying
build firmware hacker solo
build firmware hacker-debug-1 solo
build firmware hacker-debug-2 solo
build firmware secure solo
pip install -U pip
pip install -U solo-python
cd ${out_dir}
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
bundle="bundle-hacker-debug-1-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-debug-1-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
bundle="bundle-hacker-debug-2-${version}"
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-debug-2-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2
bundle="bundle-secure-non-solokeys-${version}"
/opt/conda/bin/solo mergehex --lock bootloader-verifying-${version}.hex firmware-${version}.hex ${bundle}.hex
sha256sum ${bundle}.hex > ${bundle}.sha2
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex

View File

@ -20,9 +20,6 @@
],
"userVerificationDetails": [
[
{
"userVerification": 1
},
{
"userVerification": 4
}

View File

@ -11,12 +11,6 @@ nav:
- FIDO2 Implementation: solo/fido2-impl.md
- Metadata Statements: solo/metadata-statements.md
- Build instructions: solo/building.md
- Programming instructions: solo/programming.md
- Bootloader mode: solo/bootloader-mode.md
- Customization: solo/customization.md
- Solo Extras: solo/solo-extras.md
- Application Ideas: solo/application-ideas.md
- Running on Nucleo32 board: solo/nucleo32-board.md
- Signed update process: solo/signed-updates.md
- Code documentation: solo/code-overview.md
- Contributing Code: solo/contributing.md

View File

@ -26,7 +26,6 @@
#define RK_NUM 50
bool use_udp = true;
static bool _up_disabled = false;
struct ResidentKeyStore {
CTAP_residentKey rks[RK_NUM];
@ -44,11 +43,7 @@ void device_set_status(uint32_t status)
__device_status = status;
}
void device_reboot()
{
printf1(TAG_RED, "REBOOT command recieved!\r\n");
exit(100);
}
int udp_server()
{
@ -108,7 +103,6 @@ int udp_recv(int fd, uint8_t * buf, int size)
perror( "recvfrom failed" );
exit(1);
}
printf1(TAG_DUMP, ">>"); dump_hex1(TAG_DUMP, buf, length);
return length;
}
@ -125,8 +119,6 @@ void udp_send(int fd, uint8_t * buf, int size)
perror( "sendto failed" );
exit(1);
}
printf1(TAG_DUMP, "<<"); dump_hex1(TAG_DUMP, buf, size);
}
@ -303,10 +295,6 @@ void ctaphid_write_block(uint8_t * data)
int ctap_user_presence_test(uint32_t d)
{
if (_up_disabled)
{
return 2;
}
return 1;
}
@ -316,11 +304,20 @@ int ctap_user_verification(uint8_t arg)
}
uint32_t ctap_atomic_count(uint32_t amount)
uint32_t ctap_atomic_count(int sel)
{
static uint32_t counter1 = 25;
counter1 += (amount + 1);
return counter1;
/*return 713;*/
if (sel == 0)
{
printf1(TAG_RED,"counter1: %d\n", counter1);
return counter1++;
}
else
{
printf2(TAG_ERR,"counter2 not imple\n");
exit(1);
}
}
int ctap_generate_rng(uint8_t * dst, size_t num)
@ -631,13 +628,3 @@ 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
endif
VERSION_FULL?=$(shell git describe)
APPMAKE=build/application.mk VERSION_FULL=${VERSION_FULL}
BOOTMAKE=build/bootloader.mk VERSION_FULL=${VERSION_FULL}
APPMAKE=build/application.mk
BOOTMAKE=build/bootloader.mk
merge_hex=solo mergehex
@ -13,14 +12,17 @@ merge_hex=solo mergehex
# The following are the main targets for reproducible builds.
# TODO: better explanation
firmware:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0
firmware-hacker:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-debug-1:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1
firmware-hacker-debug-1:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2
firmware-hacker-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
bootloader-nonverifying:
$(MAKE) -f $(BOOTMAKE) -j8 bootloader.hex PREFIX=$(PREFIX) EXTRA_DEFINES='-DSOLO_HACKER' DEBUG=0
@ -57,6 +59,7 @@ boot-no-sig:
build-release-locked: cbor clean2 boot-sig-checking clean all-locked
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
rm -f solo.hex bootloader.hex # don't program solo.hex ...
build-release: cbor clean2 boot-sig-checking clean all
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
@ -85,12 +88,13 @@ flash_dfu: solo.hex bootloader.hex
# STM32_Programmer_CLI -c port=usb1 -halt -e all --readunprotect
STM32_Programmer_CLI -c port=usb1 -halt -rdu -d all.hex
flashboot: bootloader.hex
flashboot: solo.hex bootloader.hex
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
flash-firmware:
$(SZ) -A solo.elf
arm-none-eabi-size -A solo.elf
solo program aux enter-bootloader
solo program bootloader solo.hex

View File

@ -19,12 +19,6 @@
#include "ctap_errors.h"
#include "log.h"
volatile version_t current_firmware_version __attribute__ ((section (".flag2"))) __attribute__ ((__used__)) = {
.major = SOLO_VERSION_MAJ,
.minor = SOLO_VERSION_MIN,
.patch = SOLO_VERSION_PATCH,
.reserved = 0
};
extern uint8_t REBOOT_FLAG;
@ -62,6 +56,8 @@ static void erase_application()
}
}
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
#define LAST_PAGE (APPLICATION_END_PAGE-1)
static void disable_bootloader()
{
// Clear last 4 bytes of the last application page-1, which is 108th
@ -106,38 +102,6 @@ int is_bootloader_disabled()
uint32_t * auth = (uint32_t *)(AUTH_WORD_ADDR+4);
return *auth == 0;
}
uint8_t * last_written_app_address;
#include "version.h"
bool is_firmware_version_newer_or_equal()
{
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
);
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",
new_version->major, new_version->minor, new_version->patch, new_version->reserved,
new_version->major, new_version->minor, new_version->patch, new_version->reserved
);
const bool allowed = is_newer((const version_t *)new_version, (const version_t *)&current_firmware_version) || current_firmware_version.raw == 0xFFFFFFFF;
if (allowed){
printf1(TAG_BOOT, "Update allowed, setting new firmware version as current.\r\n");
// current_firmware_version.raw = new_version.raw;
uint8_t page[PAGE_SIZE];
memmove(page, (uint8_t*)BOOT_VERSION_ADDR, PAGE_SIZE);
memmove(page, (version_t *)new_version, 4);
printf1(TAG_BOOT, "Writing\r\n");
flash_erase_page(BOOT_VERSION_PAGE);
flash_write(BOOT_VERSION_ADDR, page, PAGE_SIZE);
printf1(TAG_BOOT, "Finish\r\n");
} else {
printf1(TAG_BOOT, "Firmware older - update not allowed.\r\n");
}
return allowed;
}
/**
* Execute bootloader commands
@ -161,7 +125,10 @@ int bootloader_bridge(int klen, uint8_t * keyh)
return CTAP1_ERR_INVALID_LENGTH;
}
#ifndef SOLO_HACKER
extern uint8_t *pubkey_boot;
uint8_t * pubkey = (uint8_t*)"\xd2\xa4\x2f\x8f\xb2\x31\x1c\xc1\xf7\x0c\x7e\x64\x32\xfb\xbb\xb4\xa3\xdd\x32\x20"
"\x0f\x1b\x88\x9c\xda\x62\xc2\x83\x25\x93\xdd\xb8\x75\x9d\xf9\x86\xee\x03\x6c\xce"
"\x34\x47\x71\x36\xb3\xb2\xad\x6d\x12\xb7\xbe\x49\x3e\x20\xa4\x61\xac\xc7\x71\xc7"
"\x1f\xa8\x14\xf2";
const struct uECC_Curve_t * curve = NULL;
#endif
@ -198,11 +165,12 @@ int bootloader_bridge(int klen, uint8_t * keyh)
}
// Do the actual write
flash_write((uint32_t)ptr,req->payload, len);
last_written_app_address = (uint8_t *)ptr + len - 8 + 4;
break;
case BootDone:
// Writing to flash finished. Request code validation.
printf1(TAG_BOOT, "BootDone: \r\n");
printf1(TAG_BOOT, "BootDone: ");
#ifndef SOLO_HACKER
if (len != 64)
{
@ -217,23 +185,17 @@ int bootloader_bridge(int klen, uint8_t * keyh)
crypto_sha256_final(hash);
curve = uECC_secp256r1();
// Verify incoming signature made over the SHA256 hash
if (
!uECC_verify(pubkey_boot, hash, 32, req->payload, curve)
)
if (! uECC_verify(pubkey,
hash,
32,
req->payload,
curve))
{
printf1(TAG_BOOT, "Signature invalid\r\n");
return CTAP2_ERR_OPERATION_DENIED;
}
if (!is_firmware_version_newer_or_equal()){
printf1(TAG_BOOT, "Firmware older - update not allowed.\r\n");
printf1(TAG_BOOT, "Rebooting...\r\n");
REBOOT_FLAG = 1;
return CTAP2_ERR_OPERATION_DENIED;
}
#endif
// Set the application validated, and mark for reboot.
authorize_application();
REBOOT_FLAG = 1;
break;
case BootCheck:
@ -256,7 +218,6 @@ int bootloader_bridge(int klen, uint8_t * keyh)
break;
case BootReboot:
printf1(TAG_BOOT, "BootReboot.\r\n");
printf1(TAG_BOOT, "Application authorized: %d.\r\n", is_authorized_to_boot());
REBOOT_FLAG = 1;
break;
case BootDisable:
@ -316,10 +277,3 @@ void bootloader_heartbeat()
led_rgb(((val * g)<<8) | ((val*r) << 16) | (val*b));
}
uint32_t ctap_atomic_count(uint32_t amount)
{
static uint32_t count = 1000;
count += (amount + 1);
return count;
}

View File

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

View File

@ -46,7 +46,9 @@ int main()
{
uint8_t hidmsg[64];
uint32_t t1 = 0;
#ifdef SOLO_HACKER
uint32_t stboot_time = 0;
#endif
uint32_t boot = 1;
set_logging_mask(
@ -96,6 +98,7 @@ int main()
}
#ifdef SOLO_HACKER
if (!is_bootloader_disabled())
{
stboot_time = millis();
@ -105,6 +108,7 @@ int main()
goto start_bootloader;
}
}
#endif
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);
}
#ifdef SOLO_HACKER
start_bootloader:
#endif
SystemClock_Config();
init_gpio();
init_millisecond_timer(0);
@ -133,14 +138,6 @@ int main()
printf1(TAG_GEN,"recv'ing hid msg \n");
extern volatile version_t current_firmware_version;
printf1(TAG_BOOT,"Current firmware version address: %p\r\n", &current_firmware_version);
printf1(TAG_BOOT,"Current firmware version: %d.%d.%d.%d (%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
);
dump_hex1(TAG_BOOT, (uint8_t*)(&current_firmware_version) - 16, 32);
while(1)
{

View File

@ -1,6 +0,0 @@
#include "stdint.h"
uint8_t * pubkey_boot = (uint8_t*)"\xd2\xa4\x2f\x8f\xb2\x31\x1c\xc1\xf7\x0c\x7e\x64\x32\xfb\xbb\xb4\xa3\xdd\x32\x20"
"\x0f\x1b\x88\x9c\xda\x62\xc2\x83\x25\x93\xdd\xb8\x75\x9d\xf9\x86\xee\x03\x6c\xce"
"\x34\x47\x71\x36\xb3\xb2\xad\x6d\x12\xb7\xbe\x49\x3e\x20\xa4\x61\xac\xc7\x71\xc7"
"\x1f\xa8\x14\xf2";

View File

@ -1,8 +0,0 @@
#include "version.h"
// FIXME test version check function
bool is_newer(const version_t* const newer, const version_t* const older){
return (newer->major > older->major) ||
(newer->major == older->major && newer->minor > older->minor) ||
(newer->major == older->major && newer->minor == older->minor && newer->patch >= older->patch);
}

View File

@ -7,11 +7,9 @@ SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib
SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/version.c
SRC += ../../fido2/data_migration.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
SRC += ../../fido2/extensions/wallet.c
@ -71,7 +69,6 @@ all: $(TARGET).elf
%.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@
@echo "Built version: $(VERSION_FLAGS)"
%.hex: %.elf
$(SZ) $^

View File

@ -2,7 +2,6 @@ include build/common.mk
# ST related
SRC = bootloader/main.c bootloader/bootloader.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/fifo.c src/crypto.c src/attestation.c src/sense.c
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
@ -66,7 +65,6 @@ all: $(TARGET).elf
%.elf: $(OBJ)
$(CC) $^ $(HW) $(LDFLAGS) -o $@
$(SZ) $@
%.hex: %.elf
$(CP) -O ihex $^ $(TARGET).hex

View File

@ -6,15 +6,14 @@ AR=$(PREFIX)arm-none-eabi-ar
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_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c lib/stm32l4xx_ll_exti.c
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c
USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \
lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c \
lib/usbd/usbd_ccid.c
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c
VERSION_FULL?=$(shell git describe)
VERSION:=$(shell python -c 'print("$(VERSION_FULL)".split("-")[0])')
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])')

View File

@ -1,290 +0,0 @@
/**
******************************************************************************
* @file stm32l4xx_ll_exti.c
* @author MCD Application Team
* @brief EXTI LL module driver.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2017 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
#if defined(USE_FULL_LL_DRIVER)
/* Includes ------------------------------------------------------------------*/
#include "stm32l4xx_ll_exti.h"
#ifdef USE_FULL_ASSERT
#include "stm32_assert.h"
#else
#define assert_param(expr) ((void)0U)
#endif
/** @addtogroup STM32L4xx_LL_Driver
* @{
*/
#if defined (EXTI)
/** @defgroup EXTI_LL EXTI
* @{
*/
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/** @addtogroup EXTI_LL_Private_Macros
* @{
*/
#define IS_LL_EXTI_LINE_0_31(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_0_31) == 0x00000000U)
#define IS_LL_EXTI_LINE_32_63(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_32_63) == 0x00000000U)
#define IS_LL_EXTI_MODE(__VALUE__) (((__VALUE__) == LL_EXTI_MODE_IT) \
|| ((__VALUE__) == LL_EXTI_MODE_EVENT) \
|| ((__VALUE__) == LL_EXTI_MODE_IT_EVENT))
#define IS_LL_EXTI_TRIGGER(__VALUE__) (((__VALUE__) == LL_EXTI_TRIGGER_NONE) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_FALLING) \
|| ((__VALUE__) == LL_EXTI_TRIGGER_RISING_FALLING))
/**
* @}
*/
/* Private function prototypes -----------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/** @addtogroup EXTI_LL_Exported_Functions
* @{
*/
/** @addtogroup EXTI_LL_EF_Init
* @{
*/
/**
* @brief De-initialize the EXTI registers to their default reset values.
* @retval An ErrorStatus enumeration value:
* - 0x00: EXTI registers are de-initialized
*/
uint32_t LL_EXTI_DeInit(void)
{
/* Interrupt mask register set to default reset values */
LL_EXTI_WriteReg(IMR1, 0xFF820000U);
/* Event mask register set to default reset values */
LL_EXTI_WriteReg(EMR1, 0x00000000U);
/* Rising Trigger selection register set to default reset values */
LL_EXTI_WriteReg(RTSR1, 0x00000000U);
/* Falling Trigger selection register set to default reset values */
LL_EXTI_WriteReg(FTSR1, 0x00000000U);
/* Software interrupt event register set to default reset values */
LL_EXTI_WriteReg(SWIER1, 0x00000000U);
/* Pending register clear */
LL_EXTI_WriteReg(PR1, 0x007DFFFFU);
/* Interrupt mask register 2 set to default reset values */
#if defined(LL_EXTI_LINE_40)
LL_EXTI_WriteReg(IMR2, 0x00000187U);
#else
LL_EXTI_WriteReg(IMR2, 0x00000087U);
#endif
/* Event mask register 2 set to default reset values */
LL_EXTI_WriteReg(EMR2, 0x00000000U);
/* Rising Trigger selection register 2 set to default reset values */
LL_EXTI_WriteReg(RTSR2, 0x00000000U);
/* Falling Trigger selection register 2 set to default reset values */
LL_EXTI_WriteReg(FTSR2, 0x00000000U);
/* Software interrupt event register 2 set to default reset values */
LL_EXTI_WriteReg(SWIER2, 0x00000000U);
/* Pending register 2 clear */
LL_EXTI_WriteReg(PR2, 0x00000078U);
return 0x00u;
}
/**
* @brief Initialize the EXTI registers according to the specified parameters in EXTI_InitStruct.
* @param EXTI_InitStruct pointer to a @ref LL_EXTI_InitTypeDef structure.
* @retval An ErrorStatus enumeration value:
* - 0x00: EXTI registers are initialized
* - any other calue : wrong configuration
*/
uint32_t LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
uint32_t status = 0x00u;
/* Check the parameters */
assert_param(IS_LL_EXTI_LINE_0_31(EXTI_InitStruct->Line_0_31));
assert_param(IS_LL_EXTI_LINE_32_63(EXTI_InitStruct->Line_32_63));
assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->LineCommand));
assert_param(IS_LL_EXTI_MODE(EXTI_InitStruct->Mode));
/* ENABLE LineCommand */
if (EXTI_InitStruct->LineCommand != DISABLE)
{
assert_param(IS_LL_EXTI_TRIGGER(EXTI_InitStruct->Trigger));
/* Configure EXTI Lines in range from 0 to 31 */
if (EXTI_InitStruct->Line_0_31 != LL_EXTI_LINE_NONE)
{
switch (EXTI_InitStruct->Mode)
{
case LL_EXTI_MODE_IT:
/* First Disable Event on provided Lines */
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_MODE_EVENT:
/* First Disable IT on provided Lines */
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Event on provided Lines */
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_MODE_IT_EVENT:
/* Directly Enable IT & Event on provided Lines */
LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
break;
default:
status = 0x01u;
break;
}
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
{
switch (EXTI_InitStruct->Trigger)
{
case LL_EXTI_TRIGGER_RISING:
/* First Disable Falling Trigger on provided Lines */
LL_EXTI_DisableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Rising Trigger on provided Lines */
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_TRIGGER_FALLING:
/* First Disable Rising Trigger on provided Lines */
LL_EXTI_DisableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
/* Then Enable Falling Trigger on provided Lines */
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
case LL_EXTI_TRIGGER_RISING_FALLING:
LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
break;
default:
status |= 0x02u;
break;
}
}
}
/* Configure EXTI Lines in range from 32 to 63 */
if (EXTI_InitStruct->Line_32_63 != LL_EXTI_LINE_NONE)
{
switch (EXTI_InitStruct->Mode)
{
case LL_EXTI_MODE_IT:
/* First Disable Event on provided Lines */
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_MODE_EVENT:
/* First Disable IT on provided Lines */
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable Event on provided Lines */
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_MODE_IT_EVENT:
/* Directly Enable IT & Event on provided Lines */
LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
break;
default:
status |= 0x04u;
break;
}
if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
{
switch (EXTI_InitStruct->Trigger)
{
case LL_EXTI_TRIGGER_RISING:
/* First Disable Falling Trigger on provided Lines */
LL_EXTI_DisableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable IT on provided Lines */
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_TRIGGER_FALLING:
/* First Disable Rising Trigger on provided Lines */
LL_EXTI_DisableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
/* Then Enable Falling Trigger on provided Lines */
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
case LL_EXTI_TRIGGER_RISING_FALLING:
LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
break;
default:
status = ERROR;
break;
}
}
}
}
/* DISABLE LineCommand */
else
{
/* De-configure EXTI Lines in range from 0 to 31 */
LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
/* De-configure EXTI Lines in range from 32 to 63 */
LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
}
return status;
}
/**
* @brief Set each @ref LL_EXTI_InitTypeDef field to default value.
* @param EXTI_InitStruct Pointer to a @ref LL_EXTI_InitTypeDef structure.
* @retval None
*/
void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
EXTI_InitStruct->Line_0_31 = LL_EXTI_LINE_NONE;
EXTI_InitStruct->Line_32_63 = LL_EXTI_LINE_NONE;
EXTI_InitStruct->LineCommand = DISABLE;
EXTI_InitStruct->Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct->Trigger = LL_EXTI_TRIGGER_FALLING;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
#endif /* defined (EXTI) */
/**
* @}
*/
#endif /* USE_FULL_LL_DRIVER */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

File diff suppressed because it is too large Load Diff

View File

@ -1,319 +0,0 @@
#include <stdint.h>
#include "usbd_ccid.h"
#include "usbd_ctlreq.h"
#include "usbd_conf.h"
#include "usbd_core.h"
#include "log.h"
static uint8_t USBD_CCID_Init (USBD_HandleTypeDef *pdev,
uint8_t cfgidx);
static uint8_t USBD_CCID_DeInit (USBD_HandleTypeDef *pdev,
uint8_t cfgidx);
static uint8_t USBD_CCID_Setup (USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req);
static uint8_t USBD_CCID_DataIn (USBD_HandleTypeDef *pdev,
uint8_t epnum);
static uint8_t USBD_CCID_DataOut (USBD_HandleTypeDef *pdev,
uint8_t epnum);
static uint8_t USBD_CCID_EP0_RxReady (USBD_HandleTypeDef *pdev);
USBD_ClassTypeDef USBD_CCID =
{
USBD_CCID_Init,
USBD_CCID_DeInit,
USBD_CCID_Setup,
NULL, /* EP0_TxSent, */
USBD_CCID_EP0_RxReady,
USBD_CCID_DataIn,
USBD_CCID_DataOut,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
static uint8_t ccidmsg_buf[CCID_DATA_PACKET_SIZE];
static uint8_t USBD_CCID_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
uint8_t ret = 0U;
USBD_CCID_HandleTypeDef *hcdc;
//Y
USBD_LL_OpenEP(pdev, CCID_IN_EP, USBD_EP_TYPE_BULK,
CCID_DATA_PACKET_SIZE);
USBD_LL_OpenEP(pdev, CCID_OUT_EP, USBD_EP_TYPE_BULK,
CCID_DATA_PACKET_SIZE);
pdev->ep_in[CCID_IN_EP & 0xFU].is_used = 1U;
pdev->ep_out[CCID_OUT_EP & 0xFU].is_used = 1U;
USBD_LL_OpenEP(pdev, CCID_CMD_EP, USBD_EP_TYPE_INTR, CCID_DATA_PACKET_SIZE);
pdev->ep_in[CCID_CMD_EP & 0xFU].is_used = 1U;
// dump_pma_header("ccid.c");
static USBD_CCID_HandleTypeDef mem;
pdev->pClassData = &mem;
hcdc = (USBD_CCID_HandleTypeDef*) pdev->pClassData;
// init transfer states
hcdc->TxState = 0U;
hcdc->RxState = 0U;
USBD_LL_PrepareReceive(&Solo_USBD_Device, CCID_OUT_EP, ccidmsg_buf,
CCID_DATA_PACKET_SIZE);
return ret;
}
static uint8_t USBD_CCID_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
uint8_t ret = 0U;
//N
USBD_LL_CloseEP(pdev, CCID_IN_EP);
pdev->ep_in[CCID_IN_EP & 0xFU].is_used = 0U;
USBD_LL_CloseEP(pdev, CCID_OUT_EP);
pdev->ep_out[CCID_OUT_EP & 0xFU].is_used = 0U;
USBD_LL_CloseEP(pdev, CCID_CMD_EP);
pdev->ep_in[CCID_CMD_EP & 0xFU].is_used = 0U;
/* DeInit physical Interface components */
if(pdev->pClassData != NULL)
{
pdev->pClassData = NULL;
}
return ret;
}
/**
* @brief USBD_CDC_Setup
* Handle the CDC specific requests
* @param pdev: instance
* @param req: usb requests
* @retval status
*/
static uint8_t USBD_CCID_Setup (USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req)
{
USBD_CCID_HandleTypeDef *hcdc = (USBD_CCID_HandleTypeDef*) pdev->pClassData;
uint8_t ifalt = 0U;
uint16_t status_info = 0U;
uint8_t ret = USBD_OK;
//N
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
case USB_REQ_TYPE_CLASS :
if (req->wLength)
{
if (req->bmRequest & 0x80U)
{
USBD_CtlSendData (pdev, (uint8_t *)(void *)hcdc->data, req->wLength);
}
else
{
hcdc->CmdOpCode = req->bRequest;
hcdc->CmdLength = (uint8_t)req->wLength;
USBD_CtlPrepareRx (pdev, (uint8_t *)(void *)hcdc->data, req->wLength);
}
}
else
{
}
break;
case USB_REQ_TYPE_STANDARD:
switch (req->bRequest)
{
case USB_REQ_GET_STATUS:
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
USBD_CtlSendData (pdev, (uint8_t *)(void *)&status_info, 2U);
}
else
{
USBD_CtlError (pdev, req);
ret = USBD_FAIL;
}
break;
case USB_REQ_GET_INTERFACE:
if (pdev->dev_state == USBD_STATE_CONFIGURED)
{
USBD_CtlSendData (pdev, &ifalt, 1U);
}
else
{
USBD_CtlError (pdev, req);
ret = USBD_FAIL;
}
break;
case USB_REQ_SET_INTERFACE:
if (pdev->dev_state != USBD_STATE_CONFIGURED)
{
USBD_CtlError (pdev, req);
ret = USBD_FAIL;
}
break;
case USB_REQ_GET_DESCRIPTOR:
break;
default:
USBD_CtlError (pdev, req);
ret = USBD_FAIL;
break;
}
break;
default:
USBD_CtlError (pdev, req);
ret = USBD_FAIL;
break;
}
return ret;
}
/**
* @brief USBD_CDC_DataIn
* Data sent on non-control IN endpoint
* @param pdev: device instance
* @param epnum: endpoint number
* @retval status
*/
static uint8_t USBD_CCID_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
return USBD_OK;
}
static uint8_t USBD_CCID_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_CCID_HandleTypeDef *hcdc = (USBD_CCID_HandleTypeDef*)pdev->pClassData;
hcdc->TxState = 0U;
return USBD_OK;
}
uint8_t USBD_CCID_TransmitPacket(uint8_t * msg, int len)
{
/* Update the packet total length */
Solo_USBD_Device.ep_in[CCID_IN_EP & 0xFU].total_length = len;
while (PCD_GET_EP_TX_STATUS(USB, CCID_IN_EP & 0x0f) == USB_EP_TX_VALID)
;
/* Transmit next packet */
USBD_LL_Transmit(&Solo_USBD_Device, CCID_IN_EP, msg,
len);
printf1(TAG_CCID,"<< ");
dump_hex1(TAG_CCID, msg, len);
return USBD_OK;
}
void ccid_send_status(CCID_HEADER * c, uint8_t status)
{
uint8_t msg[CCID_HEADER_SIZE];
memset(msg,0,sizeof(msg));
msg[0] = CCID_SLOT_STATUS_RES;
msg[6] = c->seq;
msg[7] = status;
USBD_CCID_TransmitPacket(msg, sizeof(msg));
}
void ccid_send_data_block(CCID_HEADER * c, uint8_t status)
{
uint8_t msg[CCID_HEADER_SIZE];
memset(msg,0,sizeof(msg));
msg[0] = CCID_DATA_BLOCK_RES;
msg[6] = c->seq;
msg[7] = status;
USBD_CCID_TransmitPacket(msg, sizeof(msg));
}
void handle_ccid(uint8_t * msg, int len)
{
CCID_HEADER * h = (CCID_HEADER *) msg;
switch(h->type)
{
case CCID_SLOT_STATUS:
ccid_send_status(h, CCID_STATUS_ON);
break;
case CCID_POWER_ON:
ccid_send_data_block(h, CCID_STATUS_ON);
break;
case CCID_POWER_OFF:
ccid_send_status(h, CCID_STATUS_OFF);
break;
default:
ccid_send_status(h, CCID_STATUS_ON);
break;
}
}
/**
* @brief USBD_CDC_DataOut
* Data received on non-control Out endpoint
* @param pdev: device instance
* @param epnum: endpoint number
* @retval status
*/
uint8_t usb_ccid_recieve_callback(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_CCID_HandleTypeDef *hcdc = (USBD_CCID_HandleTypeDef*) pdev->pClassData;
/* Get the received data length */
hcdc->RxLength = USBD_LL_GetRxDataSize (pdev, epnum);
printf1(TAG_CCID, ">> ");
dump_hex1(TAG_CCID, ccidmsg_buf, hcdc->RxLength);
handle_ccid(ccidmsg_buf, hcdc->RxLength);
USBD_LL_PrepareReceive(&Solo_USBD_Device, CCID_OUT_EP, ccidmsg_buf,
CCID_DATA_PACKET_SIZE);
return USBD_OK;
}
/**
* @brief USBD_CDC_EP0_RxReady
* Handle EP0 Rx Ready event
* @param pdev: device instance
* @retval status
*/
static uint8_t USBD_CCID_EP0_RxReady (USBD_HandleTypeDef *pdev)
{
return USBD_OK;
}

View File

@ -1,58 +0,0 @@
#ifndef _USBD_H_
#define _USBD_H_
#include "usbd_ioreq.h"
#define CCID_HEADER_SIZE 10
typedef struct
{
uint8_t type;
uint32_t len;
uint8_t slot;
uint8_t seq;
uint8_t rsvd;
uint16_t param;
} __attribute__((packed)) CCID_HEADER;
#define CCID_IN_EP 0x86U /* EP1 for data IN */
#define CCID_OUT_EP 0x04U /* EP1 for data OUT */
#define CCID_CMD_EP 0x85U /* EP2 for CDC commands */
#define CCID_DATA_PACKET_SIZE 64
#define CCID_SET_PARAMS 0x61
#define CCID_POWER_ON 0x62
#define CCID_POWER_OFF 0x63
#define CCID_SLOT_STATUS 0x65
#define CCID_SECURE 0x69
#define CCID_GET_PARAMS 0x6C
#define CCID_RESET_PARAMS 0x6D
#define CCID_XFR_BLOCK 0x6F
#define CCID_STATUS_ON 0x00
#define CCID_STATUS_OFF 0x02
#define CCID_DATA_BLOCK_RES 0x80
#define CCID_SLOT_STATUS_RES 0x81
#define CCID_PARAMS_RES 0x82
extern USBD_ClassTypeDef USBD_CCID;
typedef struct
{
uint32_t data[CCID_DATA_PACKET_SIZE / 4U];
uint8_t CmdOpCode;
uint8_t CmdLength;
uint8_t *RxBuffer;
uint8_t *TxBuffer;
uint32_t RxLength;
uint32_t TxLength;
__IO uint32_t TxState;
__IO uint32_t RxState;
}
USBD_CCID_HandleTypeDef;
uint8_t usb_ccid_recieve_callback(USBD_HandleTypeDef *pdev, uint8_t epnum);
#endif

View File

@ -195,9 +195,302 @@ USBD_ClassTypeDef USBD_CDC =
NULL,
NULL,
NULL,
// USBD_CDC_GetHSCfgDesc,
// USBD_CDC_GetFSCfgDesc,
// USBD_CDC_GetOtherSpeedCfgDesc,
// USBD_CDC_GetDeviceQualifierDescriptor,
};
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
0x00,
0x02, /* bNumInterfaces: 2 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xC0, /* bmAttributes: self powered */
0x32, /* MaxPower 0 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
0x01, /* bDataInterface: 1 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
CDC_HS_BINTERVAL, /* bInterval: */
/*---------------------------------------------------------------------------*/
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
0x01, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer */
} ;
/* USB CDC device Configuration Descriptor */
__ALIGN_BEGIN uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */
0x00,
0x02, /* bNumInterfaces: 2 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xC0, /* bmAttributes: self powered */
0x32, /* MaxPower 0 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
0x01, /* bDataInterface: 1 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
CDC_FS_BINTERVAL, /* bInterval: */
/*---------------------------------------------------------------------------*/
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
0x01, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer */
} ;
__ALIGN_BEGIN uint8_t USBD_CDC_OtherSpeedCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuation Descriptor size */
USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION,
USB_CDC_CONFIG_DESC_SIZ,
0x00,
0x02, /* bNumInterfaces: 2 interfaces */
0x01, /* bConfigurationValue: */
0x04, /* iConfiguration: */
0xC0, /* bmAttributes: */
0x32, /* MaxPower 100 mA */
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: One endpoints used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x01, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
0x01, /* bDataInterface: 1 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
0x00, /* bMasterInterface: Communication class interface */
0x01, /* bSlaveInterface0: Data Class Interface */
/*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT , /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
CDC_FS_BINTERVAL, /* bInterval: */
/*---------------------------------------------------------------------------*/
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
0x01, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x02, /* bNumEndpoints: Two endpoints used */
0x0A, /* bInterfaceClass: CDC */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
0x40, /* wMaxPacketSize: */
0x00,
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
0x40, /* wMaxPacketSize: */
0x00,
0x00 /* bInterval */
};
/**
* @}
*/
/** @defgroup USBD_CDC_Private_Functions
* @{
*/
/**
* @brief USBD_CDC_Init
@ -489,7 +782,45 @@ static uint8_t USBD_CDC_EP0_RxReady (USBD_HandleTypeDef *pdev)
return USBD_OK;
}
/**
* @brief USBD_CDC_GetFSCfgDesc
* Return configuration descriptor
* @param speed : current device speed
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
/*static uint8_t *USBD_CDC_GetFSCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_CDC_CfgFSDesc);
return USBD_CDC_CfgFSDesc;
}
*/
/**
* @brief USBD_CDC_GetHSCfgDesc
* Return configuration descriptor
* @param speed : current device speed
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
/*static uint8_t *USBD_CDC_GetHSCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_CDC_CfgHSDesc);
return USBD_CDC_CfgHSDesc;
}
*/
/**
* @brief USBD_CDC_GetCfgDesc
* Return configuration descriptor
* @param speed : current device speed
* @param length : pointer data length
* @retval pointer to descriptor buffer
*/
/*static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc (uint16_t *length)
{
*length = sizeof (USBD_CDC_OtherSpeedCfgDesc);
return USBD_CDC_OtherSpeedCfgDesc;
}
*/
/**
* @brief DeviceQualifierDescriptor
* return Device Qualifier descriptor
@ -608,10 +939,22 @@ uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
/* Suspend or Resume USB Out process */
if(pdev->pClassData != NULL)
{
if(pdev->dev_speed == USBD_SPEED_HIGH )
{
/* Prepare Out endpoint to receive next packet */
USBD_LL_PrepareReceive(pdev,
CDC_OUT_EP,
hcdc->RxBuffer,
CDC_DATA_HS_OUT_PACKET_SIZE);
}
else
{
/* Prepare Out endpoint to receive next packet */
USBD_LL_PrepareReceive(pdev,
CDC_OUT_EP,
hcdc->RxBuffer,
CDC_DATA_FS_OUT_PACKET_SIZE);
}
return USBD_OK;
}
else

View File

@ -2,9 +2,7 @@
#include "usbd_desc.h"
#include "usbd_hid.h"
#include "usbd_cdc.h"
#include "usbd_ccid.h"
#include "usbd_ctlreq.h"
#include "app.h"
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx);
@ -28,265 +26,151 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
#ifdef ENABLE_CCID
#define CCID_SIZE 84
#define CCID_NUM_INTERFACE 1
#define NUM_INTERFACES 2
#if NUM_INTERFACES>1
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90)
#else
#define CCID_NUM_INTERFACE 0
#define CCID_SIZE 0
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
#endif
#if DEBUG_LEVEL > 0
#define CDC_SIZE (49 + 8 + 9 + 4)
#define CDC_NUM_INTERFACE 2
#else
#define CDC_SIZE 0
#define CDC_NUM_INTERFACE 0
#endif
#define HID_SIZE 41
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (HID_SIZE + CDC_SIZE + CCID_SIZE)
#define NUM_INTERFACES (1 + CDC_NUM_INTERFACE + CCID_NUM_INTERFACE)
#define NUM_CLASSES 3
#define HID_INTF_NUM 0
#define CDC_MASTER_INTF_NUM 1
#define CDC_SLAVE_INTF_NUM 2
#define CCID_INTF_NUM 3
#define HID_INTF_NUM 0
#define CDC_INTF_NUM 1
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
0x00,
NUM_INTERFACES, /* bNumInterfaces */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0x80, /* bmAttributes: self powered */
0x32, /* MaxPower 100 mA */
{
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
0x00,
NUM_INTERFACES, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0x80, /* bmAttributes: self powered */
0x32, /* MaxPower 100 mA */
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* */
/* HID */
/* */
/* */
/* HID */
/* */
/************** Descriptor of Joystick Mouse interface ****************/
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
2, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Joystick Mouse HID ********************/
0x09, /*bLength: HID Descriptor size*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x11, /*bcdHID: HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
HID_FIDO_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/
0,
/******************** Descriptor of Mouse endpoint ********************/
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
0x00,
HID_BINTERVAL, /*bInterval: Polling Interval */
/************** Descriptor of Joystick Mouse interface ****************/
0x09, /*bLength: Interface Descriptor size*/
USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x02, /*bNumEndpoints*/
0x03, /*bInterfaceClass: HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
2, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Joystick Mouse HID ********************/
0x09, /*bLength: HID Descriptor size*/
HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
0x11, /*bcdHID: HID Class Spec release number*/
0x01,
0x00, /*bCountryCode: Hardware target country*/
0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
0x22, /*bDescriptorType*/
HID_FIDO_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
0,
/******************** Descriptor of Mouse endpoint ********************/
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
0x00,
HID_BINTERVAL, /*bInterval: Polling Interval */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
0x00,
HID_BINTERVAL, /*bInterval: Polling Interval */
0x07, /*bLength: Endpoint Descriptor size*/
USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
0x00,
HID_BINTERVAL, /*bInterval: Polling Interval */
#if DEBUG_LEVEL > 0
/* */
/* CDC */
/* */
// This "IAD" is needed for Windows since it ignores the standard Union Functional Descriptor
0x08, // bLength
0x0B, // IAD type
CDC_MASTER_INTF_NUM, // First interface
CDC_SLAVE_INTF_NUM, // Next interface
0x02, // bInterfaceClass of the first interface
0x02, // bInterfaceSubClass of the first interface
0x00, // bInterfaceProtocol of the first interface
0x00, // Interface string index
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
/*!*/ CDC_MASTER_INTF_NUM, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x01, /* bNumEndpoints: 1 endpoint used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x00, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
#if NUM_INTERFACES>1
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/* */
/* CDC */
/* */
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
/*!*/ CDC_SLAVE_INTF_NUM, /* bDataInterface: 0 */
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
/* Interface descriptor type */
/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */
0x02, /* bInterfaceClass: Communication Interface Class */
0x02, /* bInterfaceSubClass: Abstract Control Model */
0x00, /* bInterfaceProtocol: Common AT commands */
0x00, /* iInterface: */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
/*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
/*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
/*Header Functional Descriptor*/
0x05, /* bLength: Endpoint Descriptor size */
0x24, /* bDescriptorType: CS_INTERFACE */
0x00, /* bDescriptorSubtype: Header Func Desc */
0x10, /* bcdCDC: spec release number */
0x01,
/* Control Endpoint Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
0x10, /* bInterval: */
/*Call Management Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x01, /* bDescriptorSubtype: Call Management Func Desc */
0x00, /* bmCapabilities: D0+D1 */
/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */
/* Interface descriptor */
0x09, /* bLength */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x02, /* bNumEndpoints */
0x0A, /* bInterfaceClass: Communication class data */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00,
/*ACM Functional Descriptor*/
0x04, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x02, /* bDescriptorSubtype: Abstract Control Management desc */
0x02, /* bmCapabilities */
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/*Union Functional Descriptor*/
0x05, /* bFunctionLength */
0x24, /* bDescriptorType: CS_INTERFACE */
0x06, /* bDescriptorSubtype: Union func desc */
/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */
/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint 2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_CMD_EP, /* bEndpointAddress */
0x03, /* bmAttributes: Interrupt */
LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_CMD_PACKET_SIZE),
0x10, /* bInterval: */
4, /* Descriptor size */
3, /* Descriptor type */
0x09,
0x04,
#endif
/*Endpoint OUT Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
#ifdef ENABLE_CCID
/* CCID Interface Descriptor */
9, /* bLength: Interface Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
CCID_INTF_NUM, /* bInterfaceNumber: CCID Interface */
0, /* Alternate setting for this interface */
3, /* bNumEndpoints: Bulk-IN, Bulk-OUT, Intr-IN */
0x0B, /* CCID class */
0x00, /* CCID subclass */
0x00, /* CCID protocol */
0, /* string index for interface */
/* ICC Descriptor */
54, /* bLength: */
0x21, /* bDescriptorType: USBDESCR_ICC */
0x10, 0x01, /* bcdCCID: revision 1.1 (of CCID) */
0, /* bMaxSlotIndex: */
1, /* bVoltageSupport: 5V-only */
0x02, 0, 0, 0, /* dwProtocols: T=1 */
0xa0, 0x0f, 0, 0, /* dwDefaultClock: 4000 */
0xa0, 0x0f, 0, 0, /* dwMaximumClock: 4000 */
0, /* bNumClockSupported: 0x00 */
0x80, 0x25, 0, 0, /* dwDataRate: 9600 */
0x80, 0x25, 0, 0, /* dwMaxDataRate: 9600 */
0, /* bNumDataRateSupported: 0x00 */
0xfe, 0, 0, 0, /* dwMaxIFSD: 254 */
0, 0, 0, 0, /* dwSynchProtocols: 0 */
0, 0, 0, 0, /* dwMechanical: 0 */
0x7a, 0x04, 0x02, 0x00, /* dwFeatures:
* Short and extended APDU level: 0x40000 ----
* Short APDU level : 0x20000 *
* (ICCD?) : 0x00800 ----
* Automatic IFSD : 0x00400 *
* NAD value other than 0x00 : 0x00200
* Can set ICC in clock stop : 0x00100
* Automatic PPS CUR : 0x00080
* Automatic PPS PROP : 0x00040 *
* Auto baud rate change : 0x00020 *
* Auto clock change : 0x00010 *
* Auto voltage selection : 0x00008 *
* Auto activaction of ICC : 0x00004
* Automatic conf. based on ATR : 0x00002 *
*/
0x0f, 0x01, 0, 0, /* dwMaxCCIDMessageLength: 271 */
0xff, /* bClassGetResponse: 0xff */
0x00, /* bClassEnvelope: 0 */
0, 0, /* wLCDLayout: 0 */
0, /* bPinSupport: No PIN pad */
1, /* bMaxCCIDBusySlots: 1 */
/*Endpoint IN1 Descriptor*/
7, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CCID_IN_EP, /* bEndpointAddress: (IN1) */
0x02, /* bmAttributes: Bulk */
CCID_DATA_PACKET_SIZE, 0x00, /* wMaxPacketSize: */
0x00, /* bInterval */
/*Endpoint OUT1 Descriptor*/
7, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CCID_OUT_EP, /* bEndpointAddress: (OUT1) */
0x02, /* bmAttributes: Bulk */
CCID_DATA_PACKET_SIZE, 0x00, /* wMaxPacketSize: */
0x00, /* bInterval */
/*Endpoint IN2 Descriptor*/
7, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CCID_CMD_EP, /* bEndpointAddress: (IN2) */
0x03, /* bmAttributes: Interrupt */
CCID_DATA_PACKET_SIZE, 0x00, /* wMaxPacketSize: 4 */
0xFF, /* bInterval (255ms) */
/*Endpoint IN Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDC_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
#endif
};
USBD_ClassTypeDef USBD_Composite =
{
USBD_Composite_Init,
@ -305,57 +189,32 @@ USBD_ClassTypeDef USBD_Composite =
USBD_Composite_GetDeviceQualifierDescriptor,
};
static USBD_ClassTypeDef * USBD_Classes[MAX_CLASSES];
static USBD_ClassTypeDef *USBD_Classes[MAX_CLASSES];
int in_endpoint_to_class[MAX_ENDPOINTS];
int out_endpoint_to_class[MAX_ENDPOINTS];
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *ccid_class, USBD_ClassTypeDef *cdc_class) {
memset(USBD_Classes, 0 , sizeof(USBD_Classes));
USBD_Classes[0] = hid_class;
#ifdef ENABLE_CCID
USBD_Classes[1] = ccid_class;
#endif
#if DEBUG_LEVEL > 0
USBD_Classes[2] = cdc_class;
#endif
}
static USBD_ClassTypeDef * getClass(uint8_t index)
{
switch(index)
{
case HID_INTF_NUM:
return USBD_Classes[0];
#ifdef ENABLE_CCID
case CCID_INTF_NUM:
return USBD_Classes[1];
#endif
#if DEBUG_LEVEL > 0
case CDC_MASTER_INTF_NUM:
case CDC_SLAVE_INTF_NUM:
return USBD_Classes[2];
#endif
}
return NULL;
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1) {
USBD_Classes[0] = class0;
USBD_Classes[1] = class1;
}
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i;
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i] != NULL && USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
for(i = 0; i < NUM_INTERFACES; i++) {
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL;
}
}
//N
return USBD_OK;
}
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i;
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i] != NULL && USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
for(i = 0; i < NUM_INTERFACES; i++) {
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL;
}
}
@ -365,13 +224,10 @@ static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) {
int i;
USBD_ClassTypeDef * device_class;
device_class = getClass(req->wIndex);
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
case USB_REQ_TYPE_CLASS :
if (device_class != NULL)
return device_class->Setup(pdev, req);
if (req->wIndex < NUM_INTERFACES)
return USBD_Classes[req->wIndex]->Setup(pdev, req);
else
return USBD_FAIL;
@ -380,8 +236,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
switch (req->bRequest) {
case USB_REQ_GET_DESCRIPTOR :
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i] != NULL && USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
for(i = 0; i < NUM_INTERFACES; i++) {
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
return USBD_FAIL;
}
}
@ -390,8 +246,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
case USB_REQ_GET_INTERFACE :
case USB_REQ_SET_INTERFACE :
if (device_class != NULL)
return device_class->Setup(pdev, req);
if (req->wIndex < NUM_INTERFACES)
return USBD_Classes[req->wIndex]->Setup(pdev, req);
else
return USBD_FAIL;
}
@ -404,8 +260,6 @@ static uint8_t USBD_Composite_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum) {
i = in_endpoint_to_class[epnum];
if (USBD_Classes[i] == NULL) return USBD_FAIL;
return USBD_Classes[i]->DataIn(pdev, epnum);
}
@ -414,16 +268,14 @@ static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
i = out_endpoint_to_class[epnum];
if (USBD_Classes[i] == NULL) return USBD_FAIL;
return USBD_Classes[i]->DataOut(pdev, epnum);
}
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
int i;
for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i] != NULL && USBD_Classes[i]->EP0_RxReady != NULL) {
for(i = 0; i < NUM_INTERFACES; i++) {
if (USBD_Classes[i]->EP0_RxReady != NULL) {
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
return USBD_FAIL;
}
@ -433,19 +285,16 @@ static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
}
static uint8_t *USBD_Composite_GetFSCfgDesc (uint16_t *length) {
//Y
*length = COMPOSITE_CDC_HID_DESCRIPTOR_SIZE;
return COMPOSITE_CDC_HID_DESCRIPTOR;
}
static uint8_t *USBD_Composite_GetHSCfgDesc (uint16_t *length) {
//N
*length = COMPOSITE_CDC_HID_DESCRIPTOR_SIZE;
return COMPOSITE_CDC_HID_DESCRIPTOR;
}
static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length) {
*length = COMPOSITE_CDC_HID_DESCRIPTOR_SIZE;
return COMPOSITE_CDC_HID_DESCRIPTOR;
}
@ -466,7 +315,6 @@ __ALIGN_BEGIN static uint8_t USBD_Composite_DeviceQualifierDesc[USB_LEN_DEV_QUAL
};
uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length) {
//N
*length = sizeof (USBD_Composite_DeviceQualifierDesc);
return USBD_Composite_DeviceQualifierDesc;
*length = sizeof (USBD_Composite_DeviceQualifierDesc);
return USBD_Composite_DeviceQualifierDesc;
}

View File

@ -17,7 +17,7 @@ extern int in_endpoint_to_class[MAX_ENDPOINTS];
extern int out_endpoint_to_class[MAX_ENDPOINTS];
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1, USBD_ClassTypeDef *class2);
void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1);
#ifdef __cplusplus
}

View File

@ -50,9 +50,6 @@
#include "stm32l4xx_hal.h"
#include "usbd_core.h"
#include "usbd_hid.h"
#include "usbd_cdc.h"
#include "usbd_ccid.h"
#include "log.h"
void SystemClock_Config(void);
@ -120,14 +117,9 @@ void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
switch(epnum)
{
case HID_EPOUT_ADDR:
case HID_ENDPOINT:
usb_hid_recieve_callback(epnum);
break;
#ifdef ENABLE_CCID
case CCID_OUT_EP:
usb_ccid_recieve_callback((USBD_HandleTypeDef*)hpcd->pData, epnum);
break;
#endif
}
}
@ -226,6 +218,7 @@ void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
{
USBD_LL_DevDisconnected((USBD_HandleTypeDef*)hpcd->pData);
}
/**
* @brief Initializes the low level portion of the device driver.
* @param pdev: Device handle
@ -259,20 +252,14 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x80 , PCD_SNG_BUF, 0x58);
// HID
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPOUT_ADDR , PCD_SNG_BUF, 0x98);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , HID_EPIN_ADDR , PCD_SNG_BUF, 0xd8);
// CCID
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CCID_OUT_EP , PCD_SNG_BUF, 0xd8 + 64); // data OUT
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CCID_IN_EP , PCD_SNG_BUF, 0xd8 + 64*2); // data IN
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CCID_CMD_EP , PCD_SNG_BUF, 0xd8 + 64*3); // commands
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x01 , PCD_SNG_BUF, 0x98);
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x81 , PCD_SNG_BUF, 0xd8);
// CDC / uart
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_CMD_EP , PCD_SNG_BUF, 0xd8 + 64*4); // commands
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_OUT_EP , PCD_SNG_BUF, 0xd8 + 64*5); // data OUT
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , CDC_IN_EP , PCD_SNG_BUF, 0xd8 + 64*6); // data IN
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x02 , PCD_SNG_BUF, 0xd8 + 64); // data OUT
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x82 , PCD_SNG_BUF, 0xd8 + 64*2); // data IN
HAL_PCDEx_PMAConfig((PCD_HandleTypeDef*)pdev->pData , 0x83 , PCD_SNG_BUF, 0xd8 + 64*3); // commands
// dump_pma_header("usbd_conf");
return USBD_OK;
}
@ -323,7 +310,6 @@ USBD_StatusTypeDef USBD_LL_OpenEP(USBD_HandleTypeDef *pdev,
uint8_t ep_type,
uint16_t ep_mps)
{
// printf1(TAG_RED,"LL_Open. ep: %x, %x\r\n", ep_addr, ep_type);
HAL_PCD_EP_Open((PCD_HandleTypeDef*) pdev->pData,
ep_addr,
ep_mps,

View File

@ -309,7 +309,7 @@ static uint8_t USBD_HID_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
}
/**
* @brief USBD_HID_Init
* @brief USBD_HID_DeInit
* DeInitialize the HID layer
* @param pdev: device instance
* @param cfgidx: Configuration index
@ -342,7 +342,6 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
uint8_t *pbuf = NULL;
uint16_t status_info = 0U;
USBD_StatusTypeDef ret = USBD_OK;
req->wLength = req->wLength & 0x7f;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
@ -387,7 +386,6 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
break;
case USB_REQ_GET_DESCRIPTOR:
req->wLength = req->wLength & 0x7f;
if(req->wValue >> 8 == HID_REPORT_DESC)
{
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);

View File

@ -12,17 +12,9 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/*
flash_cfg is for storing bootloader data, like last used firmware version.
bootloader_configuration should be equal to (APPLICATION_END_PAGE) page address, from targets/stm32l432/src/memory_layout.h:30; and equal to flash_cfg origin
*/
bootloader_configuration = 0x08000000 + 216*1024+8;
MEMORY
{
flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K
flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
@ -47,11 +39,6 @@ SECTIONS
_etext = .;
} >flash
.flag2 bootloader_configuration :
{
KEEP(*(.flag2)) ;
} > flash_cfg
_sidata = LOADADDR(.data);
.data :

View File

@ -12,17 +12,9 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/*
flash_cfg is for storing bootloader data, like last used firmware version.
bootloader_configuration should be equal to (APPLICATION_END_PAGE) page address, from targets/stm32l432/src/memory_layout.h:30; and equal to flash_cfg origin
*/
bootloader_configuration = 0x08000000 + 216*1024+8;
MEMORY
{
flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K
flash_cfg (rx) : ORIGIN = 0x08000000 + 216*1024+8, LENGTH = 2K-8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
@ -47,11 +39,6 @@ SECTIONS
_etext = .;
} >flash
.flag2 bootloader_configuration :
{
KEEP(*(.flag2)) ;
} > flash_cfg
_sidata = LOADADDR(.data);
.data :

View File

@ -13,21 +13,14 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/*
len | 20 KB/10p| 196KB-8-8/98p | 2kB/1p | 38 KB/19p |
pos | 0->20 KB | 20->216KB-8-8 | 216kB -> 218 kB | 218->256 KB |
posp | 0-10 | 10-113 | 113-114 | 113-128 |
desc | bootloader | application | bootloader data | secrets/data |
Last 8 bytes in application space are occupied by bootloader flags - app
authorization and bootloader activation flag.
Memory layout of device:
20 KB 198KB-8 38 KB
| bootloader | application | secrets/data |
*/
/* Current firmware version number is concatenated to the firmware code - see .flag marker */
/* flash length is (APPLICATION_END_PAGE-20*1024), where 20K is bootloader */
MEMORY
{
flash (rx) : ORIGIN = 0x08000000 + 20K, LENGTH = 216K - 20K - 8
flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
@ -63,12 +56,6 @@ SECTIONS
_edata = .;
} >ram AT> flash
.flag :
{
. = ALIGN(8);
KEEP(*(.flag)) ;
} > flash
.bss :
{
. = ALIGN(4);

View File

@ -12,22 +12,9 @@ _estack = 0x2000c000;
_MIN_STACK_SIZE = 0x400;
/*
len | 32 KB/16p| 184KB-8-8/92p | 2kB/1p | 38 KB/19p |
pos | 0->32 KB | 32->216KB-8-8 | 216kB -> 218 kB | 218->256 KB |
posp | 0-16 | 16-113 | 113-114 | 113-128 |
desc | bootloader | application | bootloader data | secrets/data |
Last 8 bytes in application space are occupied by bootloader flags - app
authorization and bootloader activation flag.
*/
/* Current firmware version number is concatenated to the firmware code - see .flag marker */
/* flash length is (APPLICATION_END_PAGE-20*1024), where 20K is bootloader */
MEMORY
{
flash (rx) : ORIGIN = 0x08000000 + 20K + 12K, LENGTH = 216K - 20K - 12K - 8
flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
}
@ -63,12 +50,6 @@ SECTIONS
_edata = .;
} >ram AT> flash
.flag :
{
. = ALIGN(8);
KEEP(*(.flag)) ;
} > flash
.bss :
{
. = ALIGN(4);

View File

@ -8,25 +8,21 @@
#include "device.h"
#include "nfc.h"
static void flush_rx(void)
static void flush_rx()
{
while(LL_SPI_IsActiveFlag_RXNE(SPI1) != 0)
{
LL_SPI_ReceiveData8(SPI1);
}
}
static void wait_for_tx(void)
static void wait_for_tx()
{
// while (LL_SPI_IsActiveFlag_BSY(SPI1) == 1)
// ;
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY)
;
}
static void wait_for_rx(void)
static void wait_for_rx()
{
while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0)
;
@ -274,7 +270,7 @@ void ams_print_int1(uint8_t int0)
#endif
}
int ams_init(void)
int ams_init()
{
LL_GPIO_SetPinMode(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN,LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN);
@ -296,7 +292,7 @@ int ams_init(void)
return 0;
}
void ams_configure(void)
void ams_configure()
{
// Should not be used during passive operation.
uint8_t block[4];

View File

@ -39,8 +39,8 @@ typedef union
#define SELECT() LL_GPIO_ResetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
#define UNSELECT() LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
int ams_init(void);
void ams_configure(void);
int ams_init();
void ams_configure();
void ams_read_buffer(uint8_t * data, int len);
void ams_write_buffer(uint8_t * data, int len);

View File

@ -9,18 +9,12 @@
#include <stdint.h>
#include "version.h"
#define SOLO
#define DEBUG_UART USART1
#ifndef DEBUG_LEVEL
// Enable the CDC ACM USB interface & debug logs (DEBUG_LEVEL > 0)
#define DEBUG_LEVEL 0
#endif
// Enable the CCID USB interface
// #define ENABLE_CCID
#define NON_BLOCK_PRINTING 0
@ -37,7 +31,7 @@
// #define DISABLE_CTAPHID_WINK
// #define DISABLE_CTAPHID_CBOR
// #define ENABLE_SERIAL_PRINTING
#define ENABLE_SERIAL_PRINTING
#if defined(SOLO_HACKER)
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION
@ -48,9 +42,6 @@
void printing_init();
void hw_init(int lf);
// Return 1 if Solo is secure/locked.
int solo_is_locked();
//#define TEST
//#define TEST_POWER

View File

@ -6,10 +6,10 @@
// copied, modified, or distributed except according to those terms.
#include <stdint.h>
#include "crypto.h"
#include "memory_layout.h"
#ifdef USE_SOLOKEYS_CERT
const uint8_t attestation_solo_cert_der[] =
const uint8_t attestation_cert_der[] =
"\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"
"\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79\x6c"
@ -49,8 +49,11 @@ const uint8_t attestation_solo_cert_der[] =
"\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71"
"\x52"
;
#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"
"\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"
@ -91,16 +94,8 @@ const uint8_t attestation_hacker_cert_der[] =
"\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_hacker_cert_der_size = sizeof(attestation_hacker_cert_der)-1;
// const uint16_t attestation_key_size = 32;
const uint8_t * attestation_cert_der = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert;
#include "log.h"
uint16_t attestation_cert_der_get_size(){
uint16_t sz = (uint16_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size;
return sz;
}
const uint16_t attestation_cert_der_size = sizeof(attestation_cert_der)-1;
const uint16_t attestation_key_size = 32;

View File

@ -61,13 +61,12 @@ static uint8_t master_secret[64];
static uint8_t transport_secret[32];
void crypto_sha256_init(void)
void crypto_sha256_init()
{
sha256_init(&sha256_ctx);
}
void crypto_sha512_init(void)
{
void crypto_sha512_init() {
cf_sha512_init(&sha512_ctx);
}
@ -80,7 +79,7 @@ void crypto_load_master_secret(uint8_t * key)
memmove(transport_secret, key+64, 32);
}
void crypto_reset_master_secret(void)
void crypto_reset_master_secret()
{
memset(master_secret, 0, 64);
memset(transport_secret, 0, 32);
@ -108,8 +107,7 @@ void crypto_sha256_final(uint8_t * hash)
sha256_final(&sha256_ctx, hash);
}
void crypto_sha512_final(uint8_t * hash)
{
void crypto_sha512_final(uint8_t * hash) {
// NB: there is also cf_sha512_digest
cf_sha512_digest_final(&sha512_ctx, hash);
}
@ -159,11 +157,6 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
key = master_secret;
klen = sizeof(master_secret)/2;
}
else if (key == CRYPTO_TRANSPORT_KEY2)
{
key = transport_secret;
klen = 32;
}
if(klen > 64)
@ -185,19 +178,18 @@ 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);
_es256_curve = uECC_secp256r1();
}
void crypto_ecc256_load_attestation_key(void)
void crypto_ecc256_load_attestation_key()
{
// static uint8_t _key [32];
flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR;
// memmove(_key, (uint8_t *)ATTESTATION_KEY_ADDR, 32);
_signing_key = page->attestation_key;
static uint8_t _key [32];
memmove(_key, (uint8_t*)ATTESTATION_KEY_ADDR, 32);
_signing_key = _key;
_key_len = 32;
}
@ -285,11 +277,6 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
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)
{

View File

@ -34,73 +34,31 @@
#define LOW_FREQUENCY 1
#define HIGH_FREQUENCY 0
#define SOLO_FLAG_LOCKED 0x2
void wait_for_usb_tether(void);
void wait_for_usb_tether();
uint32_t __90_ms = 0;
uint32_t __last_button_press_time = 0;
uint32_t __last_button_bounce_time = 0;
uint32_t __device_status = 0;
uint32_t __last_update = 0;
extern PCD_HandleTypeDef hpcd;
static int _NFC_status = 0;
static bool isLowFreq = 0;
static bool _up_disabled = false;
// #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
static int is_physical_button_pressed(void)
static int is_physical_button_pressed()
{
return (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN));
}
static int is_touch_button_pressed(void)
static int is_touch_button_pressed()
{
int is_pressed = (tsc_read_button(0) || tsc_read_button(1));
#ifndef IS_BOOTLOADER
if (is_pressed)
{
// delay for debounce, and longer than polling timer period.
delay(95);
return (tsc_read_button(0) || tsc_read_button(1));
}
#endif
return is_pressed;
return tsc_read_button(0) || tsc_read_button(1);
}
int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed;
static void edge_detect_touch_button(void)
{
static uint8_t last_touch = 0;
uint8_t current_touch = 0;
if (is_touch_button_pressed == IS_BUTTON_PRESSED)
{
current_touch = (tsc_read_button(0) || tsc_read_button(1));
// 1 sample per 25 ms
if ((millis() - __last_button_bounce_time) > 25)
{
// Detect "touch / rising edge"
if (!last_touch && current_touch)
{
__last_button_press_time = millis();
}
__last_button_bounce_time = millis();
last_touch = current_touch;
}
}
}
void device_disable_up(bool disable)
{
_up_disabled = disable;
}
// Timer6 overflow handler. happens every ~90ms.
void TIM6_DAC_IRQHandler(void)
void TIM6_DAC_IRQHandler()
{
// timer is only 16 bits, so roll it over here
TIM6->SR = 0;
@ -112,9 +70,6 @@ void TIM6_DAC_IRQHandler(void)
ctaphid_update_status(__device_status);
}
}
edge_detect_touch_button();
#ifndef IS_BOOTLOADER
// NFC sending WTX if needs
if (device_is_nfc() == NFC_IS_ACTIVE)
@ -124,28 +79,13 @@ void TIM6_DAC_IRQHandler(void)
#endif
}
// Interrupt on rising edge of button (button released)
void EXTI0_IRQHandler(void)
{
EXTI->PR1 = EXTI->PR1;
if (is_physical_button_pressed == IS_BUTTON_PRESSED)
{
// Only allow 1 press per 25 ms.
if ((millis() - __last_button_bounce_time) > 25)
{
__last_button_press_time = millis();
}
__last_button_bounce_time = millis();
}
}
// Global USB interrupt handler
void USB_IRQHandler(void)
{
HAL_PCD_IRQHandler(&hpcd);
}
uint32_t millis(void)
uint32_t millis()
{
return (((uint32_t)TIM6->CNT) + (__90_ms * 90));
}
@ -163,8 +103,9 @@ void device_set_status(uint32_t status)
__device_status = status;
}
int device_is_button_pressed(void)
int device_is_button_pressed()
{
return IS_BUTTON_PRESSED();
}
@ -174,13 +115,12 @@ void delay(uint32_t ms)
while ((millis() - time) < ms)
;
}
void device_reboot(void)
void device_reboot()
{
NVIC_SystemReset();
}
void device_init_button(void)
void device_init_button()
{
if (tsc_sensor_exists())
{
@ -193,98 +133,6 @@ void device_init_button(void)
}
}
int solo_is_locked(){
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;
}
/** 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(int argc, char *argv[])
{
@ -313,8 +161,6 @@ void device_init(int argc, char *argv[])
ctaphid_init();
ctap_init();
device_migrate();
#if BOOT_TO_DFU
flash_option_bytes_init(1);
#else
@ -324,12 +170,12 @@ void device_init(int argc, char *argv[])
}
int device_is_nfc(void)
int device_is_nfc()
{
return _NFC_status;
}
void wait_for_usb_tether(void)
void wait_for_usb_tether()
{
while (USBD_OK != CDC_Transmit_FS((uint8_t*)"tethered\r\n", 10) )
;
@ -340,7 +186,7 @@ void wait_for_usb_tether(void)
;
}
void usbhid_init(void)
void usbhid_init()
{
if (!isLowFreq)
{
@ -390,12 +236,12 @@ void ctaphid_write_block(uint8_t * data)
}
void usbhid_close(void)
void usbhid_close()
{
}
void main_loop_delay(void)
void main_loop_delay()
{
}
@ -405,14 +251,13 @@ static uint32_t winkt1 = 0;
#ifdef LED_WINK_VALUE
static uint32_t winkt2 = 0;
#endif
void device_wink(void)
void device_wink()
{
wink_time = 10;
winkt1 = 0;
}
void heartbeat(void)
void heartbeat()
{
static int state = 0;
static uint32_t val = (LED_MAX_SCALER - LED_MIN_SCALER)/2;
@ -481,7 +326,7 @@ void authenticator_read_backup_state(AuthenticatorState * a)
}
// Return 1 yes backup is init'd, else 0
int authenticator_is_backup_initialized(void)
int authenticator_is_backup_initialized()
{
uint8_t header[16];
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
@ -506,8 +351,7 @@ void authenticator_write_state(AuthenticatorState * a, int backup)
}
}
#if !defined(IS_BOOTLOADER)
uint32_t ctap_atomic_count(uint32_t amount)
uint32_t ctap_atomic_count(int sel)
{
int offset = 0;
uint32_t * ptr = (uint32_t *)flash_addr(COUNTER1_PAGE);
@ -522,12 +366,10 @@ uint32_t ctap_atomic_count(uint32_t amount)
uint32_t lastc = 0;
if (amount == 0)
if (sel != 0)
{
// Use a random count [1-16].
uint8_t rng[1];
ctap_generate_rng(rng, 1);
amount = (rng[0] & 0x0f) + 1;
printf2(TAG_ERR,"counter2 not imple\n");
exit(1);
}
for (offset = 0; offset < PAGE_SIZE/4; offset += 2) // wear-level the flash
@ -560,7 +402,7 @@ uint32_t ctap_atomic_count(uint32_t amount)
return lastc;
}
lastc += amount;
lastc++;
if (lastc/256 > erases)
{
@ -598,10 +440,10 @@ uint32_t ctap_atomic_count(uint32_t amount)
return lastc;
}
#endif
void device_manage(void)
void device_manage()
{
#if NON_BLOCK_PRINTING
int i = 10;
@ -627,7 +469,7 @@ void device_manage(void)
#endif
}
static int handle_packets(void)
static int handle_packets()
{
static uint8_t hidmsg[HID_PACKET_SIZE];
memset(hidmsg,0, sizeof(hidmsg));
@ -646,56 +488,13 @@ static int handle_packets(void)
return 0;
}
static int wait_for_button_activate(uint32_t wait)
{
int ret;
uint32_t start = millis();
do
{
if ((start + wait) < millis())
{
return 0;
}
delay(1);
ret = handle_packets();
if (ret)
return ret;
} while (!IS_BUTTON_PRESSED());
return 0;
}
static int wait_for_button_release(uint32_t wait)
{
int ret;
uint32_t start = millis();
do
{
if ((start + wait) < millis())
{
return 0;
}
delay(1);
ret = handle_packets();
if (ret)
return ret;
} while (IS_BUTTON_PRESSED());
return 0;
}
int ctap_user_presence_test(uint32_t up_delay)
{
int ret;
if (device_is_nfc() == NFC_IS_ACTIVE)
{
return 1;
}
if (_up_disabled)
{
return 2;
}
#if SKIP_BUTTON_CHECK_WITH_DELAY
int i=500;
while(i--)
@ -708,41 +507,53 @@ int ctap_user_presence_test(uint32_t up_delay)
#elif SKIP_BUTTON_CHECK_FAST
delay(2);
ret = handle_packets();
if (ret)
return ret;
if (ret) return ret;
goto done;
#endif
// If button was pressed within last [2] seconds, succeed.
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
{
goto done;
}
// Set LED status and wait.
uint32_t t1 = millis();
led_rgb(0xff3520);
// Block and wait for some time.
ret = wait_for_button_activate(up_delay);
if (ret) return ret;
ret = wait_for_button_release(up_delay);
if (ret) return ret;
// If button was pressed within last [2] seconds, succeed.
if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
if (IS_BUTTON_PRESSED == is_touch_button_pressed)
{
// Wait for user to release touch button if it's already pressed
while (IS_BUTTON_PRESSED())
{
goto done;
if (t1 + up_delay < millis())
{
printf1(TAG_GEN,"Button not pressed\n");
goto fail;
}
ret = handle_packets();
if (ret) return ret;
}
}
t1 = millis();
do
{
if (t1 + up_delay < millis())
{
goto fail;
}
delay(1);
ret = handle_packets();
if (ret) return ret;
}
while (! IS_BUTTON_PRESSED());
led_rgb(0x001040);
delay(50);
return 0;
#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST
done:
ret = wait_for_button_release(up_delay);
__last_button_press_time = 0;
return 1;
#endif
return 1;
fail:
return 0;
}
int ctap_generate_rng(uint8_t * dst, size_t num)
@ -757,7 +568,7 @@ int ctap_user_verification(uint8_t arg)
return 1;
}
void ctap_reset_rk(void)
void ctap_reset_rk()
{
int i;
printf1(TAG_GREEN, "resetting RK \r\n");
@ -767,7 +578,7 @@ void ctap_reset_rk(void)
}
}
uint32_t ctap_rk_size(void)
uint32_t ctap_rk_size()
{
return RK_NUM_PAGES * (PAGE_SIZE / sizeof(CTAP_residentKey));
}
@ -829,7 +640,7 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
}
}
void boot_st_bootloader(void)
void boot_st_bootloader()
{
__disable_irq();
@ -841,7 +652,7 @@ void boot_st_bootloader(void)
;
}
void boot_solo_bootloader(void)
void boot_solo_bootloader()
{
LL_IWDG_Enable(IWDG);

View File

@ -14,12 +14,12 @@
#include "log.h"
#include "device.h"
static void flash_lock(void)
static void flash_lock()
{
FLASH->CR |= (1U<<31);
}
static void flash_unlock(void)
static void flash_unlock()
{
if (FLASH->CR & FLASH_CR_LOCK)
{
@ -31,18 +31,21 @@ static void flash_unlock(void)
// Locks flash and turns off 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;
#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)
}
else {
if (solo_is_locked())
{
val = 0xfffff8cc;
}
}
val &= ~(1<<26); // nSWBOOT0 = 0 (boot from nBoot0)
val &= ~(1<<25); // SRAM2_RST = 1 (erase sram on reset)
val &= ~(1<<24); // SRAM2_PE = 1 (parity check en)

View File

@ -20,7 +20,6 @@
#include "stm32l4xx_ll_rng.h"
#include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_usb.h"
#include "stm32l4xx_ll_exti.h"
#include "stm32l4xx_hal_pcd.h"
#include "stm32l4xx_hal.h"
@ -28,7 +27,6 @@
#include "usbd_desc.h"
#include "usbd_hid.h"
#include "usbd_cdc.h"
#include "usbd_ccid.h"
#include "usbd_composite.h"
#include "usbd_cdc_if.h"
#include "device.h"
@ -699,33 +697,33 @@ void SystemClock_Config_LF20(void)
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
}
void init_usb(void)
void init_usb()
{
// enable USB power
SET_BIT(PWR->CR2, PWR_CR2_USV);
// Enable USB Clock
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_USBFSEN);
#ifndef IS_BOOTLOADER
USBD_Composite_Set_Classes(&USBD_HID, &USBD_CCID, &USBD_CDC);
#if DEBUG_LEVEL > 0
USBD_Composite_Set_Classes(&USBD_HID, &USBD_CDC);
in_endpoint_to_class[HID_EPIN_ADDR & 0x7F] = 0;
out_endpoint_to_class[HID_EPOUT_ADDR & 0x7F] = 0;
in_endpoint_to_class[CCID_IN_EP & 0x7F] = 1;
out_endpoint_to_class[CCID_OUT_EP & 0x7F] = 1;
in_endpoint_to_class[CDC_IN_EP & 0x7F] = 2;
out_endpoint_to_class[CDC_OUT_EP & 0x7F] = 2;
in_endpoint_to_class[CDC_IN_EP & 0x7F] = 1;
out_endpoint_to_class[CDC_OUT_EP & 0x7F] = 1;
USBD_Init(&Solo_USBD_Device, &Solo_Desc, 0);
USBD_RegisterClass(&Solo_USBD_Device, &USBD_Composite);
#if DEBUG_LEVEL > 0
// USBD_RegisterClass(&Solo_USBD_Device, &USBD_HID);
//
// USBD_RegisterClass(&Solo_USBD_Device, &USBD_CDC);
USBD_CDC_RegisterInterface(&Solo_USBD_Device, &USBD_Interface_fops_FS);
#endif
#else
USBD_Init(&Solo_USBD_Device, &Solo_Desc, 0);
USBD_RegisterClass(&Solo_USBD_Device, &USBD_HID);
#endif
USBD_Start(&Solo_USBD_Device);
}
@ -851,17 +849,19 @@ void init_gpio(void)
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
#ifndef IS_BOOTLOADER
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
LL_EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0; // GPIOA_0
EXTI_InitStruct.Line_32_63 = LL_EXTI_LINE_NONE;
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);
NVIC_EnableIRQ(EXTI0_IRQn);
#ifdef SOLO_AMS_IRQ_PORT
// SAVE POWER
// LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
// /**/
// LL_GPIO_InitTypeDef GPIO_InitStruct;
// GPIO_InitStruct.Pin = SOLO_AMS_IRQ_PIN;
// GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
// LL_GPIO_Init(SOLO_AMS_IRQ_PORT, &GPIO_InitStruct);
//
//
// LL_GPIO_SetPinMode(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_MODE_INPUT);
// LL_GPIO_SetPinPull(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_PULL_UP);
#endif
}

View File

@ -22,7 +22,7 @@
#ifndef _INIT_H_
#define _INIT_H_
void init_usb(void);
void init_usb();
void init_gpio(void);
void init_debug_uart(void);
void init_pwm(void);

View File

@ -57,11 +57,10 @@ void TIM6_DAC_IRQHandler()
__90_ms += 1;
}
uint32_t millis(void)
uint32_t millis()
{
return (((uint32_t)TIM6->CNT) + (__90_ms * 90));
}
void _Error_Handler(char *file, int line)
{
while(1)

View File

@ -17,11 +17,8 @@
#define COUNTER1_PAGE (PAGES - 3)
// State of FIDO2 application
#define STATE2_PAGE (PAGES - 2)
#define STATE1_PAGE (PAGES - 1)
#define STATE1_PAGE_ADDR (0x08000000 + ((STATE1_PAGE)*PAGE_SIZE))
#define STATE2_PAGE_ADDR (0x08000000 + ((STATE2_PAGE)*PAGE_SIZE))
#define STATE2_PAGE (PAGES - 2)
#define STATE1_PAGE (PAGES - 1)
// Storage of FIDO2 resident keys
#define RK_NUM_PAGES 10
@ -35,51 +32,15 @@
#define APPLICATION_START_ADDR (0x08000000 + ((APPLICATION_START_PAGE)*PAGE_SIZE))
// where attestation key is located
#define ATTESTATION_PAGE (PAGES - 15)
#define ATTESTATION_PAGE_ADDR (0x08000000 + ATTESTATION_PAGE*PAGE_SIZE)
#define ATTESTATION_KEY_PAGE (PAGES - 15)
#define ATTESTATION_KEY_ADDR (0x08000000 + ATTESTATION_KEY_PAGE*PAGE_SIZE)
// End of application code. Leave some extra room for future data storage.
// NOT included in application
#define APPLICATION_END_PAGE ((PAGES - 20))
#define APPLICATION_END_PAGE ((PAGES - 19))
#define APPLICATION_END_ADDR ((0x08000000 + ((APPLICATION_END_PAGE)*PAGE_SIZE))-8)
// Bootloader state.
#define AUTH_WORD_ADDR (APPLICATION_END_ADDR)
#define LAST_ADDR (APPLICATION_END_ADDR-2048 + 8)
#define BOOT_VERSION_PAGE (APPLICATION_END_PAGE)
#define BOOT_VERSION_ADDR (0x08000000 + BOOT_VERSION_PAGE*FLASH_PAGE_SIZE + 8)
#define LAST_PAGE (APPLICATION_END_PAGE-1)
struct flash_memory_st{
uint8_t bootloader[APPLICATION_START_PAGE*2*1024];
uint8_t application[(APPLICATION_END_PAGE-APPLICATION_START_PAGE)*2*1024-8];
uint8_t auth_word[4];
uint8_t bootloader_disabled[4];
// place for more user data
uint8_t _reserved_application_end_mark[8];
uint8_t bootloader_data[2*1024-8];
uint8_t user_data[38*1024];
} __attribute__((packed));
typedef struct flash_memory_st flash_memory_st;
#include <assert.h>
static_assert(sizeof(flash_memory_st) == 256*1024, "Data structure doesn't match flash size");
#define ATTESTATION_CONFIGURED_TAG 0xaa551e78
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

View File

@ -14,33 +14,6 @@
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
// chain buffer for 61XX responses
static uint8_t chain_buffer[2048] = {0};
static size_t chain_buffer_len = 0;
static bool chain_buffer_tx = false;
static uint8_t current_cid = 0;
// forward declarations
void rblock_acknowledge(uint8_t req0, bool ack);
uint8_t p14443_have_cid(uint8_t pcb) {
// CID
if (pcb & 0x08)
return true;
else
return false;
}
uint8_t p14443_block_offset(uint8_t pcb) {
uint8_t offset = 1;
// NAD following
if (pcb & 0x04) offset++;
// CID following
if (pcb & 0x08) offset++;
return offset;
}
// Capability container
const CAPABILITY_CONTAINER NFC_CC = {
.cclen_hi = 0x00, .cclen_lo = 0x0f,
@ -109,27 +82,19 @@ int nfc_init()
return NFC_IS_NA;
}
static uint8_t gl_int0 = 0;
void process_int0(uint8_t int0)
{
gl_int0 = int0;
}
bool ams_wait_for_tx(uint32_t timeout_ms)
{
if (gl_int0 & AMS_INT_TXE) {
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
return true;
}
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
if (int0) process_int0(int0);
if (int0 & AMS_INT_TXE)
return true;
delay(1);
@ -146,13 +111,7 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
uint8_t int0 = 0;
if (gl_int0 & AMS_INT_RXE) {
int0 = gl_int0;
} else {
int0 = ams_read_reg(AMS_REG_INT0);
process_int0(int0);
}
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
if (buffer_status2 && (int0 & AMS_INT_RXE))
@ -202,25 +161,14 @@ bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t r
if (len > 32 - 3)
return false;
res[0] = NFC_CMD_IBLOCK | (req0 & 0x0f);
res[1] = current_cid;
res[2] = 0;
uint8_t block_offset = p14443_block_offset(req0);
res[0] = NFC_CMD_IBLOCK | (req0 & 3);
if (len && data)
memcpy(&res[block_offset], data, len);
memcpy(&res[1], data, len);
res[len + block_offset + 0] = resp >> 8;
res[len + block_offset + 1] = resp & 0xff;
nfc_write_frame(res, block_offset + len + 2);
if (!ams_wait_for_tx(1))
{
printf1(TAG_NFC, "TX resp timeout. len: %d \r\n", len);
return false;
}
res[len + 1] = resp >> 8;
res[len + 2] = resp & 0xff;
nfc_write_frame(res, 3 + len);
return true;
}
@ -230,30 +178,25 @@ bool nfc_write_response(uint8_t req0, uint16_t resp)
return nfc_write_response_ex(req0, NULL, 0, resp);
}
void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
{
uint8_t res[32 + 2];
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
uint8_t block_offset = p14443_block_offset(req0);
int sendlen = 0;
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3);
if (len <= 31)
{
uint8_t res[32] = {0};
res[0] = iBlock;
res[1] = current_cid;
res[2] = 0;
res[0] = iBlock;
if (len && data)
memcpy(&res[block_offset], data, len);
nfc_write_frame(res, len + block_offset);
memcpy(&res[1], data, len);
nfc_write_frame(res, len + 1);
} else {
int sendlen = 0;
do {
// transmit I block
int vlen = MIN(32 - block_offset, len - sendlen);
res[0] = iBlock;
res[1] = current_cid;
res[2] = 0;
memcpy(&res[block_offset], &data[sendlen], vlen);
int vlen = MIN(31, len - sendlen);
res[0] = iBlock;
memcpy(&res[1], &data[sendlen], vlen);
// if not a last block
if (vlen + sendlen < len)
@ -262,15 +205,15 @@ void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
}
// send data
nfc_write_frame(res, vlen + block_offset);
nfc_write_frame(res, vlen + 1);
sendlen += vlen;
// wait for transmit (32 bytes aprox 2,5ms)
if (!ams_wait_for_tx(5))
{
printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
break;
}
// if (!ams_wait_for_tx(10))
// {
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
// break;
// }
// if needs to receive R block (not a last block)
if (res[0] & 0x10)
@ -282,25 +225,10 @@ void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
printf1(TAG_NFC, "R block RX timeout %d/%d.\r\n",sendlen,len);
break;
}
if (!IS_RBLOCK(recbuf[0]))
{
printf1(TAG_NFC, "R block RX error. Not a R block(0x%02x) %d/%d.\r\n", recbuf[0], sendlen, len);
break;
}
// NAK check
if (recbuf[0] & NFC_CMD_RBLOCK_ACK)
{
rblock_acknowledge(recbuf[0], true);
printf1(TAG_NFC, "R block RX error. NAK received. %d/%d.\r\n", recbuf[0], sendlen, len);
break;
}
uint8_t rblock_offset = p14443_block_offset(recbuf[0]);
if (reclen != rblock_offset)
if (reclen != 1)
{
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen, sendlen, len);
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len);
dump_hex1(TAG_NFC, recbuf, reclen);
break;
}
@ -317,38 +245,6 @@ void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
}
}
void append_get_response(uint8_t *data, size_t rest_len)
{
data[0] = 0x61;
data[1] = 0x00;
if (rest_len <= 0xff)
data[1] = rest_len & 0xff;
}
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len, bool extapdu)
{
chain_buffer_len = 0;
chain_buffer_tx = true;
// if we dont need to break data to parts that need to exchange via GET RESPONSE command (ISO 7816-4 7.1.3)
if (len <= 255 || extapdu)
{
nfc_write_response_chaining_plain(req0, data, len);
} else {
size_t pcklen = MIN(253, len);
chain_buffer_len = len - pcklen;
printf1(TAG_NFC, "61XX chaining %d/%d.\r\n", pcklen, chain_buffer_len);
memmove(chain_buffer, data, pcklen);
append_get_response(&chain_buffer[pcklen], chain_buffer_len);
nfc_write_response_chaining_plain(req0, chain_buffer, pcklen + 2); // 2 for 61XX
// put the rest data into chain buffer
memmove(chain_buffer, &data[pcklen], chain_buffer_len);
}
}
// WTX on/off:
// sends/receives WTX frame to reader every `WTX_time` time in ms
// works via timer interrupts
@ -359,7 +255,7 @@ static uint32_t WTX_timer;
bool WTX_process(int read_timeout);
void WTX_clear(void)
void WTX_clear()
{
WTX_sent = false;
WTX_fail = false;
@ -374,7 +270,7 @@ bool WTX_on(int WTX_time)
return true;
}
bool WTX_off(void)
bool WTX_off()
{
WTX_timer = 0;
@ -398,10 +294,10 @@ bool WTX_off(void)
return true;
}
void WTX_timer_exec(void)
void WTX_timer_exec()
{
// condition: (timer on) or (not expired[300ms])
if ((WTX_timer == 0) || WTX_timer + 300 > millis())
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
return;
WTX_process(10);
@ -412,12 +308,12 @@ void WTX_timer_exec(void)
// read timeout must be 10 ms to call from interrupt
bool WTX_process(int read_timeout)
{
uint8_t wtx[] = {0xf2, 0x01};
if (WTX_fail)
return false;
if (!WTX_sent)
{
uint8_t wtx[] = {0xf2, 0x01};
nfc_write_frame(wtx, sizeof(wtx));
WTX_sent = true;
return true;
@ -475,74 +371,39 @@ int answer_rats(uint8_t parameter)
nfc_write_frame(res, sizeof(res));
if (!ams_wait_for_tx(10))
{
printf1(TAG_NFC, "RATS TX timeout.\r\n");
ams_write_command(AMS_CMD_DEFAULT);
return 1;
}
ams_wait_for_tx(10);
return 0;
}
void rblock_acknowledge(uint8_t req0, bool ack)
void rblock_acknowledge()
{
uint8_t buf[32] = {0};
uint8_t block_offset = p14443_block_offset(req0);
uint8_t buf[32];
NFC_STATE.block_num = !NFC_STATE.block_num;
buf[0] = NFC_CMD_RBLOCK | (req0 & 0x0f);
buf[1] = current_cid;
// iso14443-4:2001 page 16. ACK, if bit is set to 0, NAK, if bit is set to 1
if (!ack)
buf[0] |= NFC_CMD_RBLOCK_ACK;
nfc_write_frame(buf, block_offset);
}
// international AID = RID:PIX
// RID length == 5 bytes
// usually aid length must be between 5 and 16 bytes
int applet_cmp(uint8_t * aid, int len, uint8_t * const_aid, int const_len)
{
if (len > const_len)
return 10;
// if international AID
if ((const_aid[0] & 0xf0) == 0xa0)
{
if (len < 5)
return 11;
return memcmp(aid, const_aid, MIN(len, const_len));
} else {
if (len != const_len)
return 11;
return memcmp(aid, const_aid, const_len);
}
buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
nfc_write_frame(buf,1);
}
// Selects application. Returns 1 if success, 0 otherwise
int select_applet(uint8_t * aid, int len)
{
if (applet_cmp(aid, len, (uint8_t *)AID_FIDO, sizeof(AID_FIDO) - 1) == 0)
if (memcmp(aid,AID_FIDO,sizeof(AID_FIDO)) == 0)
{
NFC_STATE.selected_applet = APP_FIDO;
return APP_FIDO;
}
else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TYPE_4, sizeof(AID_NDEF_TYPE_4) - 1) == 0)
else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0)
{
NFC_STATE.selected_applet = APP_NDEF_TYPE_4;
return APP_NDEF_TYPE_4;
}
else if (applet_cmp(aid, len, (uint8_t *)AID_CAPABILITY_CONTAINER, sizeof(AID_CAPABILITY_CONTAINER) - 1) == 0)
else if (memcmp(aid,AID_CAPABILITY_CONTAINER,sizeof(AID_CAPABILITY_CONTAINER)) == 0)
{
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
return APP_CAPABILITY_CONTAINER;
}
else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TAG, sizeof(AID_NDEF_TAG) - 1) == 0)
else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0)
{
NFC_STATE.selected_applet = APP_NDEF_TAG;
return APP_NDEF_TAG;
@ -550,71 +411,27 @@ int select_applet(uint8_t * aid, int len)
return APP_NOTHING;
}
void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
void nfc_process_iblock(uint8_t * buf, int len)
{
APDU_HEADER * apdu = (APDU_HEADER *)(buf + 1);
uint8_t * payload = buf + 1 + 5;
uint8_t plen = apdu->lc;
int selected;
CTAP_RESPONSE ctap_resp;
int status;
uint16_t reslen;
// check CLA
if (apdu->cla != 0x00 && apdu->cla != 0x80) {
printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu->cla);
nfc_write_response(buf0, SW_CLA_INVALID);
return;
}
printf1(TAG_NFC,"Iblock: ");
dump_hex1(TAG_NFC, buf, len);
// TODO this needs to be organized better
switch(apdu->ins)
{
// ISO 7816. 7.1 GET RESPONSE command
case APDU_GET_RESPONSE:
if (apdu->p1 != 0x00 || apdu->p2 != 0x00)
{
nfc_write_response(buf0, SW_INCORRECT_P1P2);
printf1(TAG_NFC, "P1 or P2 error\r\n");
return;
}
// too many bytes needs. 0x00 and 0x100 - any length
if (apdu->le != 0 && apdu->le != 0x100 && apdu->le > chain_buffer_len)
{
uint16_t wlresp = SW_WRONG_LENGTH; // here can be 6700, 6C00, 6FXX. but the most standard way - 67XX or 6700
if (chain_buffer_len <= 0xff)
wlresp += chain_buffer_len & 0xff;
nfc_write_response(buf0, wlresp);
printf1(TAG_NFC, "buffer length less than requesteds\r\n");
return;
}
// create temporary packet
uint8_t pck[255] = {0};
size_t pcklen = 253;
if (apdu->le)
pcklen = apdu->le;
if (pcklen > chain_buffer_len)
pcklen = chain_buffer_len;
printf1(TAG_NFC, "GET RESPONSE. pck len: %d buffer len: %d\r\n", pcklen, chain_buffer_len);
// create packet and add 61XX there if we have another portion(s) of data
memmove(pck, chain_buffer, pcklen);
size_t dlen = 0;
if (chain_buffer_len - pcklen)
{
append_get_response(&pck[pcklen], chain_buffer_len - pcklen);
dlen = 2;
}
// send
nfc_write_response_chaining_plain(buf0, pck, pcklen + dlen); // dlen for 61XX
// shift the buffer
chain_buffer_len -= pcklen;
memmove(chain_buffer, &chain_buffer[pcklen], chain_buffer_len);
break;
case APDU_INS_SELECT:
if (plen > len - 6)
{
printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
plen = len-6;
}
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
// {
// printf1(TAG_NFC,"Select NDEF\r\n");
@ -629,49 +446,53 @@ void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
// }
// else
{
selected = select_applet(apdu->data, apdu->lc);
selected = select_applet(payload, plen);
if (selected == APP_FIDO)
{
nfc_write_response_ex(buf0, (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
// block = buf[0] & 1;
// block = NFC_STATE.block_num;
// block = !block;
// NFC_STATE.block_num = block;
// NFC_STATE.block_num = block;
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
printf1(TAG_NFC, "FIDO applet selected.\r\n");
}
else if (selected != APP_NOTHING)
{
nfc_write_response(buf0, SW_SUCCESS);
nfc_write_response(buf[0], SW_SUCCESS);
printf1(TAG_NFC, "SELECTED %d\r\n", selected);
}
else
{
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu->data, apdu->lc);
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
printf1(TAG_NFC, "NOT selected\r\n"); dump_hex1(TAG_NFC,payload, plen);
}
}
break;
case APDU_FIDO_U2F_VERSION:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf0, SW_INS_INVALID);
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F GetVersion command.\r\n");
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
break;
case APDU_FIDO_U2F_REGISTER:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf0, SW_INS_INVALID);
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F Register command.\r\n");
if (apdu->lc != 64)
if (plen != 64)
{
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu->lc);
nfc_write_response(buf0, SW_WRONG_LENGTH);
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
@ -681,64 +502,66 @@ void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
// WTX_on(WTX_TIME_DEFAULT);
// SystemClock_Config_LF32();
// delay(300);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
u2f_request_nfc(&buf[1], len, &ctap_resp);
if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
// if (!WTX_off())
// return;
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
// printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
break;
case APDU_FIDO_U2F_AUTHENTICATE:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf0, SW_INS_INVALID);
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
if (apdu->lc != 64 + 1 + apdu->data[64])
if (plen != 64 + 1 + buf[6 + 64])
{
delay(5);
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu->lc, apdu->data[64]);
nfc_write_response(buf0, SW_WRONG_LENGTH);
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", plen, buf[6 + 64]);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
timestamp();
// WTX_on(WTX_TIME_DEFAULT);
u2f_request_nfc(apduptr, apdu->data, apdu->lc, &ctap_resp);
u2f_request_nfc(&buf[1], len, &ctap_resp);
// if (!WTX_off())
// return;
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Authenticate processing %d (took %d)\r\n", millis(), timestamp());
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
printf1(TAG_NFC,"U2F Authenticate answered %d (took %d)\r\n", millis(), timestamp);
break;
case APDU_FIDO_NFCCTAP_MSG:
if (NFC_STATE.selected_applet != APP_FIDO) {
nfc_write_response(buf0, SW_INS_INVALID);
return;
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
// WTX_on(WTX_TIME_DEFAULT);
device_disable_up(true);
WTX_on(WTX_TIME_DEFAULT);
ctap_response_init(&ctap_resp);
status = ctap_request(apdu->data, apdu->lc, &ctap_resp);
device_disable_up(false);
// if (!WTX_off())
// return;
status = ctap_request(payload, plen, &ctap_resp);
if (!WTX_off())
return;
printf1(TAG_NFC, "CTAP resp: 0x%02x len: %d\r\n", status, ctap_resp.length);
printf1(TAG_NFC, "CTAP resp: 0x%02<EFBFBD> len: %d\r\n", status, ctap_resp.length);
if (status == CTAP1_ERR_SUCCESS)
{
@ -752,111 +575,55 @@ void apdu_process(uint8_t buf0, uint8_t *apduptr, APDU_STRUCT *apdu)
ctap_resp.data[ctap_resp.length - 1] = SW_SUCCESS & 0xff;
printf1(TAG_NFC,"CTAP processing %d (took %d)\r\n", millis(), timestamp());
nfc_write_response_chaining(buf0, ctap_resp.data, ctap_resp.length, apdu->extended_apdu);
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
break;
case APDU_INS_READ_BINARY:
// response length
reslen = apdu->le & 0xffff;
switch(NFC_STATE.selected_applet)
{
case APP_CAPABILITY_CONTAINER:
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
if (reslen == 0 || reslen > sizeof(NFC_CC))
reslen = sizeof(NFC_CC);
nfc_write_response_ex(buf0, (uint8_t *)&NFC_CC, reslen, SW_SUCCESS);
if (plen > 15)
{
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
plen = 15;
}
nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, plen, SW_SUCCESS);
ams_wait_for_tx(10);
break;
case APP_NDEF_TAG:
printf1(TAG_NFC,"APP_NDEF_TAG\r\n");
if (reslen == 0 || reslen > sizeof(NDEF_SAMPLE) - 1)
reslen = sizeof(NDEF_SAMPLE) - 1;
nfc_write_response_ex(buf0, NDEF_SAMPLE, reslen, SW_SUCCESS);
if (plen > (sizeof(NDEF_SAMPLE) - 1))
{
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
plen = sizeof(NDEF_SAMPLE) - 1;
}
nfc_write_response_ex(buf[0], NDEF_SAMPLE, plen, SW_SUCCESS);
ams_wait_for_tx(10);
break;
default:
nfc_write_response(buf0, SW_FILE_NOT_FOUND);
printf1(TAG_ERR, "No binary applet selected!\r\n");
return;
break;
}
break;
case APDU_SOLO_RESET:
if (apdu->lc == 4 && !memcmp(apdu->data, "\x12\x56\xab\xf0", 4)) {
printf1(TAG_NFC, "Reset...\r\n");
nfc_write_response(buf0, SW_SUCCESS);
delay(20);
device_reboot();
while(1);
} else {
printf1(TAG_NFC, "Reset FAIL\r\n");
nfc_write_response(buf0, SW_INS_INVALID);
}
break;
default:
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
nfc_write_response(buf0, SW_INS_INVALID);
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
}
void nfc_process_iblock(uint8_t * buf, int len)
{
uint8_t block_offset = p14443_block_offset(buf[0]);
// clear tx chain buffer if we have some other command than GET RESPONSE
if (chain_buffer_tx && buf[block_offset + 1] != APDU_GET_RESPONSE) {
chain_buffer_len = 0;
chain_buffer_tx = false;
}
APDU_STRUCT apdu;
uint16_t ret = apdu_decode(buf + block_offset, len - block_offset, &apdu);
if (ret != 0) {
printf1(TAG_NFC,"apdu decode error\r\n");
nfc_write_response(buf[0], ret);
return;
}
printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
// APDU level chaining. ISO7816-4, 5.1.1. class byte
if (!chain_buffer_tx && buf[block_offset] & 0x10) {
if (chain_buffer_len + len > sizeof(chain_buffer)) {
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
memmove(&chain_buffer[chain_buffer_len], apdu.data, apdu.lc);
chain_buffer_len += apdu.lc;
nfc_write_response(buf[0], SW_SUCCESS);
printf1(TAG_NFC, "APDU chaining ok. %d/%d\r\n", apdu.lc, chain_buffer_len);
return;
}
// if we have ISO 7816 APDU chain - move there all the data
if (!chain_buffer_tx && chain_buffer_len > 0) {
memmove(&apdu.data[chain_buffer_len], apdu.data, apdu.lc);
memmove(apdu.data, chain_buffer, chain_buffer_len);
apdu.lc += chain_buffer_len; // here apdu struct does not match with memory!
printf1(TAG_NFC, "APDU chaining merge. %d/%d\r\n", chain_buffer_len, apdu.lc);
}
apdu_process(buf[0], &buf[block_offset], &apdu);
printf1(TAG_NFC,"prev.Iblock: ");
dump_hex1(TAG_NFC, buf, len);
}
static uint8_t ibuf[1024];
static int ibuflen = 0;
void clear_ibuf(void)
void clear_ibuf()
{
ibuflen = 0;
memset(ibuf, 0, sizeof(ibuf));
@ -864,29 +631,19 @@ void clear_ibuf(void)
void nfc_process_block(uint8_t * buf, unsigned int len)
{
printf1(TAG_NFC, "-----\r\n");
if (!len)
return;
if (IS_PPSS_CMD(buf[0]))
{
printf1(TAG_NFC, "NFC_CMD_PPSS [%d] 0x%02x\r\n", len, (len > 2) ? buf[2] : 0);
if (buf[1] == 0x11 && (buf[2] & 0x0f) == 0x00) {
nfc_write_frame(buf, 1); // ack with correct start byte
} else {
printf1(TAG_NFC, "NFC_CMD_PPSS ERROR!!!\r\n");
nfc_write_frame((uint8_t*)"\x00", 1); // this should not happend. but iso14443-4 dont have NACK here, so just 0x00
}
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
}
else if (IS_IBLOCK(buf[0]))
{
uint8_t block_offset = p14443_block_offset(buf[0]);
if (p14443_have_cid(buf[0]))
current_cid = buf[1];
if (buf[0] & 0x10)
{
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d offs=%d\r\n", ibuflen, len, block_offset);
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
if (ibuflen + len > sizeof(ibuf))
{
printf1(TAG_NFC, "I block memory error! must have %d but have only %d\r\n", ibuflen + len, sizeof(ibuf));
@ -897,46 +654,44 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len);
if (len > block_offset)
if (len)
{
memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
ibuflen += len - block_offset;
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
ibuflen += len - 1;
}
// send R block
rblock_acknowledge(buf[0], true);
uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3);
nfc_write_frame(&rb, 1);
} else {
if (ibuflen)
{
if (len > block_offset)
if (len)
{
memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
ibuflen += len - block_offset;
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
ibuflen += len - 1;
}
// add last chaining to top of the block
memmove(&ibuf[block_offset], ibuf, ibuflen);
memmove(ibuf, buf, block_offset);
ibuflen += block_offset;
memmove(&ibuf[1], ibuf, ibuflen);
ibuf[0] = buf[0];
ibuflen++;
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d offset=%d\r\n", ibuflen, len, block_offset);
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len);
nfc_process_iblock(ibuf, ibuflen);
} else {
memcpy(ibuf, buf, len); // because buf only 32b
nfc_process_iblock(ibuf, len);
// printf1(TAG_NFC, "NFC_CMD_IBLOCK\r\n");
nfc_process_iblock(buf, len);
}
clear_ibuf();
}
}
else if (IS_RBLOCK(buf[0]))
{
if (p14443_have_cid(buf[0]))
current_cid = buf[1];
rblock_acknowledge(buf[0], true);
rblock_acknowledge();
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
}
else if (IS_SBLOCK(buf[0]))
@ -945,10 +700,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
if ((buf[0] & NFC_SBLOCK_DESELECT) == 0)
{
printf1(TAG_NFC, "NFC_CMD_SBLOCK, DESELECTED\r\n");
uint8_t block_offset = p14443_block_offset(buf[0]);
if (p14443_have_cid(buf[0]))
current_cid = buf[1];
nfc_write_frame(buf, block_offset);
nfc_write_frame(buf, 1);
ams_wait_for_tx(2);
ams_write_command(AMS_CMD_SLEEP);
nfc_state_init();
@ -958,7 +710,6 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
else
{
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
}
dump_hex1(TAG_NFC, buf, len);
}
@ -969,7 +720,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
}
}
int nfc_loop(void)
int nfc_loop()
{
uint8_t buf[32];
AMS_DEVICE ams;
@ -977,8 +728,6 @@ int nfc_loop(void)
read_reg_block(&ams);
uint8_t old_int0 = gl_int0;
process_int0(ams.regs.int0);
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
@ -992,7 +741,7 @@ int nfc_loop(void)
// if (state != AMS_STATE_SENSE)
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
}
if (ams.regs.int0 & AMS_INT_INIT || old_int0 & AMS_INT_INIT)
if (ams.regs.int0 & AMS_INT_INIT)
{
nfc_state_init();
}
@ -1001,7 +750,7 @@ int nfc_loop(void)
// ams_print_int1(ams.regs.int1);
}
if (ams.regs.int0 & AMS_INT_RXE || old_int0 & AMS_INT_RXE)
if ((ams.regs.int0 & AMS_INT_RXE))
{
if (ams.regs.buffer_status2)
{
@ -1030,7 +779,6 @@ int nfc_loop(void)
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
break;
case NFC_CMD_HLTA:
ams_write_command(AMS_CMD_SLEEP);
printf1(TAG_NFC, "HLTA/Halt\r\n");
break;
case NFC_CMD_RATS:

View File

@ -6,9 +6,9 @@
#include "apdu.h"
// Return number of bytes read if any.
int nfc_loop(void);
int nfc_loop();
int nfc_init(void);
int nfc_init();
typedef struct
{
@ -34,14 +34,12 @@ typedef struct
#define IS_PPSS_CMD(x) (((x) & 0xf0) == NFC_CMD_PPSS)
#define NFC_CMD_IBLOCK 0x00
#define IS_IBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_IBLOCK) && (((x) & 0x02) == 0x02) )
#define NFC_CMD_RBLOCK 0xa0
#define NFC_CMD_RBLOCK_ACK 0x10
#define IS_RBLOCK(x) ( (((x) & 0xe0) == NFC_CMD_RBLOCK) && (((x) & 0x02) == 0x02) )
#define NFC_CMD_RBLOCK 0x80
#define NFC_CMD_RBLOCK_ACK 0x20
#define IS_RBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_RBLOCK) && (((x) & 0x02) == 0x02) )
#define NFC_CMD_SBLOCK 0xc0
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
extern uint8_t p14443_block_offset(uint8_t pcb);
#define NFC_SBLOCK_DESELECT 0x30
#define NFC_SBLOCK_WTX 0x30
@ -61,6 +59,6 @@ typedef enum
APP_FIDO,
} APPLETS;
void WTX_timer_exec(void);
void WTX_timer_exec();
#endif

View File

@ -8,7 +8,7 @@
#define ELECTRODE_0 TSC_GROUP2_IO1
#define ELECTRODE_1 TSC_GROUP2_IO2
void tsc_init(void)
void tsc_init()
{
LL_GPIO_InitTypeDef GPIO_InitStruct;
// Enable TSC clock
@ -74,7 +74,7 @@ void tsc_set_electrode(uint32_t channel_ids)
TSC->IOCCR = (channel_ids);
}
void tsc_start_acq(void)
void tsc_start_acq()
{
TSC->CR &= ~(TSC_CR_START);
@ -86,7 +86,7 @@ void tsc_start_acq(void)
TSC->CR |= TSC_CR_START;
}
void tsc_wait_on_acq(void)
void tsc_wait_on_acq()
{
while ( ! (TSC->ISR & TSC_FLAG_EOA) )
;
@ -117,7 +117,7 @@ uint32_t tsc_read_button(uint32_t index)
return tsc_read(1) < 45;
}
int tsc_sensor_exists(void)
int tsc_sensor_exists()
{
static uint8_t does = 0;
if (does) return 1;

View File

@ -3,9 +3,9 @@
#include <stdint.h>
void tsc_init(void);
void tsc_init();
int tsc_sensor_exists(void);
int tsc_sensor_exists();
// Read button0 or button1
// Returns 1 if pressed, 0 if not.

58
tools/testing/main.py Normal file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# 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.
#
# Script for testing correctness of CTAP2/CTAP1 security token
import sys
from solo.fido2 import force_udp_backend
from tests import Tester, FIDO2Tests, U2FTests, HIDTests, SoloTests
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
sys.exit(0)
t = Tester()
t.set_user_count(3)
if "sim" in sys.argv:
print("Using UDP backend.")
force_udp_backend()
t.set_sim(True)
t.set_user_count(10)
t.find_device()
if "solo" in sys.argv:
SoloTests(t).run()
if "u2f" in sys.argv:
U2FTests(t).run()
if "fido2" in sys.argv:
# t.test_fido2()
FIDO2Tests(t).run()
# hid tests are a bit invasive and should be done last
if "hid" in sys.argv:
HIDTests(t).run()
if "bootloader" in sys.argv:
if t.is_sim:
raise RuntimeError("Cannot test bootloader in simulation yet.")
# print("Put device in bootloader mode and then hit enter")
# input()
# t.test_bootloader()
# t.test_responses()
# t.test_fido2_brute_force()

View File

@ -0,0 +1,11 @@
from . import fido2
from . import hid
from . import solo
from . import u2f
from . import tester
FIDO2Tests = fido2.FIDO2Tests
HIDTests = hid.HIDTests
U2FTests = u2f.U2FTests
SoloTests = solo.SoloTests
Tester = tester.Tester

1278
tools/testing/tests/fido2.py Normal file

File diff suppressed because it is too large Load Diff

252
tools/testing/tests/hid.py Normal file
View File

@ -0,0 +1,252 @@
import sys, os, time
from binascii import hexlify
from fido2.hid import CTAPHID
from fido2.ctap import CtapError
from .tester import Tester, Test
class HIDTests(Tester):
def __init__(self, tester=None):
super().__init__(tester)
self.check_timeouts = False
def set_check_timeouts(self, en):
self.check_timeouts = en
def run(self,):
self.test_long_ping()
self.test_hid(self.check_timeouts)
def test_long_ping(self):
amt = 1000
pingdata = os.urandom(amt)
with Test("Send %d byte ping" % amt):
try:
t1 = time.time() * 1000
r = self.send_data(CTAPHID.PING, pingdata)
t2 = time.time() * 1000
delt = t2 - t1
# if (delt < 140 ):
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
if delt > 555 * (amt / 1000):
raise RuntimeError("Fob is too slow (%d ms)" % delt)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError:
raise RuntimeError("ping failed")
sys.stdout.flush()
def test_hid(self, check_timeouts=False):
if check_timeouts:
with Test("idle"):
try:
cmd, resp = self.recv_raw()
except socket.timeout:
pass
with Test("init"):
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
with Test("100 byte ping"):
pingdata = os.urandom(100)
try:
r = self.send_data(CTAPHID.PING, pingdata)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError as e:
print("100 byte Ping failed:", e)
raise RuntimeError("ping failed")
self.test_long_ping()
with Test("Wink"):
r = self.send_data(CTAPHID.WINK, "")
with Test("CBOR msg with no data"):
try:
r = self.send_data(CTAPHID.CBOR, "")
if len(r) > 1 or r[0] == 0:
raise RuntimeError("Cbor is supposed to have payload")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_LENGTH
with Test("No data in U2F msg"):
try:
r = self.send_data(CTAPHID.MSG, "")
print(hexlify(r))
if len(r) > 2:
raise RuntimeError("MSG is supposed to have payload")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_LENGTH
with Test("Use init command to resync"):
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
with Test("Invalid HID command"):
try:
r = self.send_data(0x66, "")
raise RuntimeError("Invalid command did not return error")
except CtapError as e:
assert e.code == CtapError.ERR.INVALID_COMMAND
with Test("Sending packet with too large of a length."):
self.send_raw("\x81\x1d\xba\x00")
cmd, resp = self.recv_raw()
Tester.check_error(resp, CtapError.ERR.INVALID_LENGTH)
r = self.send_data(CTAPHID.PING, "\x44" * 200)
with Test("Sending packets that skip a sequence number."):
self.send_raw("\x81\x04\x90")
self.send_raw("\x00")
self.send_raw("\x01")
# skip 2
self.send_raw("\x03")
cmd, resp = self.recv_raw()
Tester.check_error(resp, CtapError.ERR.INVALID_SEQ)
with Test("Resync and send ping"):
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
pingdata = os.urandom(100)
r = self.send_data(CTAPHID.PING, pingdata)
if r != pingdata:
raise ValueError("Ping data not echo'd")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
with Test("Send ping and abort it"):
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
try:
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
except CtapError as e:
raise RuntimeError("resync fail: ", e)
with Test("Send ping and abort it with different cid, expect timeout"):
oldcid = self.cid()
newcid = "\x11\x22\x33\x44"
self.send_raw("\x81\x10\x00")
self.send_raw("\x00")
self.send_raw("\x01")
self.set_cid(newcid)
self.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
) # init from different cid
print("wait for init response")
cmd, r = self.recv_raw() # init response
assert cmd == 0x86
self.set_cid(oldcid)
if check_timeouts:
# print('wait for timeout')
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
with Test("Test timeout"):
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
t1 = time.time() * 1000
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
cmd, r = self.recv_raw() # timeout response
t2 = time.time() * 1000
delt = t2 - t1
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
assert delt < 1000 and delt > 400
with Test("Test not cont"):
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x81\x04\x00")
self.send_raw("\x00")
self.send_raw("\x01")
self.send_raw("\x81\x10\x00") # init packet
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_SEQ
if check_timeouts:
with Test("Check random cont ignored"):
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x01\x10\x00")
try:
cmd, r = self.recv_raw() # timeout response
except socket.timeout:
pass
with Test("Check busy"):
t1 = time.time() * 1000
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
oldcid = self.cid()
newcid = "\x11\x22\x33\x44"
self.send_raw("\x81\x04\x00")
self.set_cid(newcid)
self.send_raw("\x81\x04\x00")
cmd, r = self.recv_raw() # busy response
t2 = time.time() * 1000
assert t2 - t1 < 100
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
self.set_cid(oldcid)
cmd, r = self.recv_raw() # timeout response
assert cmd == 0xBF
assert r[0] == CtapError.ERR.TIMEOUT
with Test("Check busy interleaved"):
cid1 = "\x11\x22\x33\x44"
cid2 = "\x01\x22\x33\x44"
self.set_cid(cid2)
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.set_cid(cid1)
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
self.set_cid(cid2) # send ping on 2nd channel
self.send_raw("\x81\x00\x63")
Tester.delay(0.1)
self.send_raw("\x00")
cmd, r = self.recv_raw() # busy response
self.set_cid(cid1) # finish 1st channel ping
self.send_raw("\x00")
self.set_cid(cid2)
assert cmd == 0xBF
assert r[0] == CtapError.ERR.CHANNEL_BUSY
self.set_cid(cid1)
cmd, r = self.recv_raw() # ping response
assert cmd == 0x81
assert len(r) == 0x63
if check_timeouts:
with Test("Test idle, wait for timeout"):
sys.stdout.flush()
try:
cmd, resp = self.recv_raw()
except socket.timeout:
pass
with Test("Test cid 0 is invalid"):
self.set_cid("\x00\x00\x00\x00")
self.send_raw(
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
)
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL
with Test("Test invalid broadcast cid use"):
self.set_cid("\xff\xff\xff\xff")
self.send_raw(
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
)
cmd, r = self.recv_raw() # timeout
assert cmd == 0xBF
assert r[0] == CtapError.ERR.INVALID_CHANNEL

View File

@ -0,0 +1,83 @@
from solo.client import SoloClient
from solo.commands import SoloExtension
from fido2.ctap1 import ApduError
from fido2.utils import sha256
from .util import shannon_entropy
from .tester import Tester, Test
class SoloTests(Tester):
def __init__(self, tester=None):
super().__init__(tester)
def run(self,):
self.test_solo()
def test_solo(self,):
"""
Solo specific tests
"""
# RNG command
sc = SoloClient()
sc.find_device(self.dev)
sc.use_u2f()
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
total = 1024 * 16
with Test("Gathering %d random bytes..." % total):
entropy = b""
while len(entropy) < total:
entropy += sc.get_rng()
with Test("Test entropy is close to perfect"):
s = shannon_entropy(entropy)
assert s > 7.98
print("Entropy is %.5f bits per byte." % s)
with Test("Test Solo version command"):
assert len(sc.solo_version()) == 3
with Test("Test bootloader is not active"):
try:
sc.write_flash(memmap[0], b"1234")
except ApduError:
pass
sc.exchange = sc.exchange_fido2
req = SoloClient.format_request(SoloExtension.version, 0, b"A" * 16)
a = sc.ctap2.get_assertion(
sc.host, b"B" * 32, [{"id": req, "type": "public-key"}]
)
with Test("Test custom command returned valid assertion"):
assert a.auth_data.rp_id_hash == sha256(sc.host.encode("utf8"))
assert a.credential["id"] == req
assert (a.auth_data.flags & 0x5) == 0x5
with Test("Test Solo version and random commands with fido2 layer"):
assert len(sc.solo_version()) == 3
sc.get_rng()
def test_bootloader(self,):
sc = SoloClient()
sc.find_device(self.dev)
sc.use_u2f()
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
data = b"A" * 64
with Test("Test version command"):
assert len(sc.bootloader_version()) == 3
with Test("Test write command"):
sc.write_flash(memmap[0], data)
for addr in (memmap[0] - 8, memmap[0] - 4, memmap[1], memmap[1] - 8):
with Test("Test out of bounds write command at 0x%04x" % addr):
try:
sc.write_flash(addr, data)
except CtapError as e:
assert e.code == CtapError.ERR.NOT_ALLOWED

View File

@ -0,0 +1,204 @@
import time, struct
from fido2.hid import CtapHidDevice
from fido2.client import Fido2Client
from fido2.ctap1 import CTAP1
from fido2.utils import Timeout
from fido2.ctap import CtapError
def ForceU2F(client, device):
client.ctap = CTAP1(device)
client.pin_protocol = None
client._do_make_credential = client._ctap1_make_credential
client._do_get_assertion = client._ctap1_get_assertion
class Packet(object):
def __init__(self, data):
self.data = data
def ToWireFormat(self,):
return self.data
@staticmethod
def FromWireFormat(pkt_size, data):
return Packet(data)
class Test:
def __init__(self, msg, catch=None):
self.msg = msg
self.catch = catch
def __enter__(self,):
print(self.msg)
def __exit__(self, a, b, c):
if self.catch is None:
print("Pass")
elif isinstance(b, self.catch):
print("Pass")
return b
else:
raise RuntimeError(f"Expected exception {self.catch} did not occur.")
class Tester:
def __init__(self, tester=None):
self.origin = "https://examplo.org"
self.host = "examplo.org"
self.user_count = 10
self.is_sim = False
if tester:
self.initFromTester(tester)
def initFromTester(self, tester):
self.user_count = tester.user_count
self.is_sim = tester.is_sim
self.dev = tester.dev
self.ctap = tester.ctap
self.ctap1 = tester.ctap1
self.client = tester.client
def find_device(self,):
print(list(CtapHidDevice.list_devices()))
dev = next(CtapHidDevice.list_devices(), None)
if not dev:
raise RuntimeError("No FIDO device found")
self.dev = dev
self.client = Fido2Client(dev, self.origin)
self.ctap = self.client.ctap2
self.ctap1 = CTAP1(dev)
# consume timeout error
# cmd,resp = self.recv_raw()
def set_user_count(self, count):
self.user_count = count
def set_sim(self, b):
self.is_sim = b
def reboot(self,):
if self.is_sim:
print("Sending restart command...")
self.send_magic_reboot()
Tester.delay(0.25)
else:
print("Please reboot authentictor and hit enter")
input()
self.find_device()
def send_data(self, cmd, data):
if not isinstance(data, bytes):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
with Timeout(1.0) as event:
return self.dev.call(cmd, data, event)
def send_raw(self, data, cid=None):
if cid is None:
cid = self.dev._dev.cid
elif not isinstance(cid, bytes):
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
if not isinstance(data, bytes):
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
data = cid + data
l = len(data)
if l != 64:
pad = "\x00" * (64 - l)
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
data = data + pad
data = list(data)
assert len(data) == 64
self.dev._dev.InternalSendPacket(Packet(data))
def send_magic_reboot(self,):
"""
For use in simulation and testing. Random bytes that authentictor should detect
and then restart itself.
"""
magic_cmd = (
b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
+ b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
+ b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
+ b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
+ b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
+ b"\x9a\x72\x50\xdc"
)
self.dev._dev.InternalSendPacket(Packet(magic_cmd))
def cid(self,):
return self.dev._dev.cid
def set_cid(self, cid):
if not isinstance(cid, (bytes, bytearray)):
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
self.dev._dev.cid = cid
def recv_raw(self,):
with Timeout(1.0):
cmd, payload = self.dev._dev.InternalRecv()
return cmd, payload
def check_error(data, err=None):
assert len(data) == 1
if err is None:
if data[0] != 0:
raise CtapError(data[0])
elif data[0] != err:
raise ValueError("Unexpected error: %02x" % data[0])
def testFunc(self, func, test, *args, **kwargs):
with Test(test):
res = None
expectedError = kwargs.get("expectedError", None)
otherArgs = kwargs.get("other", {})
try:
res = func(*args, **otherArgs)
if expectedError != CtapError.ERR.SUCCESS:
raise RuntimeError("Expected error to occur for test: %s" % test)
except CtapError as e:
if expectedError is not None:
cond = e.code != expectedError
if isinstance(expectedError, list):
cond = e.code not in expectedError
else:
expectedError = [expectedError]
if cond:
raise RuntimeError(
f"Got error code {hex(e.code)}, expected {[hex(x) for x in expectedError]}"
)
else:
print(e)
return res
def testReset(self,):
print("Resetting Authenticator...")
try:
self.ctap.reset()
except CtapError:
# Some authenticators need a power cycle
print("You must power cycle authentictor. Hit enter when done.")
input()
time.sleep(0.2)
self.find_device()
self.ctap.reset()
def testMC(self, test, *args, **kwargs):
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
def testGA(self, test, *args, **kwargs):
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
def testCP(self, test, *args, **kwargs):
return self.testFunc(self.ctap.client_pin, test, *args, **kwargs)
def testPP(self, test, *args, **kwargs):
return self.testFunc(
self.client.pin_protocol.get_pin_token, test, *args, **kwargs
)
def delay(secs):
time.sleep(secs)

121
tools/testing/tests/u2f.py Normal file
View File

@ -0,0 +1,121 @@
from fido2.ctap1 import CTAP1, ApduError, APDU
from fido2.utils import sha256
from fido2.client import _call_polling
from .tester import Tester, Test
class U2FTests(Tester):
def __init__(self, tester=None):
super().__init__(tester)
def run(self,):
self.test_u2f()
def register(self, chal, appid):
reg_data = _call_polling(0.25, None, None, self.ctap1.register, chal, appid)
return reg_data
def authenticate(self, chal, appid, key_handle, check_only=False):
auth_data = _call_polling(
0.25,
None,
None,
self.ctap1.authenticate,
chal,
appid,
key_handle,
check_only=check_only,
)
return auth_data
def test_u2f(self,):
chal = sha256(b"AAA")
appid = sha256(b"BBB")
lastc = 0
regs = []
with Test("Check version"):
assert self.ctap1.get_version() == "U2F_V2"
with Test("Check bad INS"):
try:
self.ctap1.send_apdu(0, 0, 0, 0, b"")
except ApduError as e:
assert e.code == 0x6D00
with Test("Check bad CLA"):
try:
self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
except ApduError as e:
assert e.code == 0x6E00
for i in range(0, self.user_count):
with Test(
"U2F reg + auth %d/%d (count: %02x)" % (i + 1, self.user_count, lastc)
):
reg = self.register(chal, appid)
reg.verify(appid, chal)
auth = self.authenticate(chal, appid, reg.key_handle)
auth.verify(appid, chal, reg.public_key)
regs.append(reg)
# check endianness
if lastc:
assert (auth.counter - lastc) < 10
lastc = auth.counter
if lastc > 0x80000000:
print("WARNING: counter is unusually high: %04x" % lastc)
assert 0
for i in range(0, self.user_count):
with Test(
"Checking previous registration %d/%d" % (i + 1, self.user_count)
):
auth = self.authenticate(chal, appid, regs[i].key_handle)
auth.verify(appid, chal, regs[i].public_key)
print("Check that all previous credentials are registered...")
for i in range(0, self.user_count):
with Test("Check that previous credential %d is registered" % i):
try:
auth = self.ctap1.authenticate(
chal, appid, regs[i].key_handle, check_only=True
)
except ApduError as e:
# Indicates that key handle is registered
assert e.code == APDU.USE_NOT_SATISFIED
with Test("Check an incorrect key handle is not registered"):
kh = bytearray(regs[0].key_handle)
kh[0] = kh[0] ^ (0x40)
try:
self.ctap1.authenticate(chal, appid, kh, check_only=True)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
with Test("Try to sign with incorrect key handle"):
try:
self.ctap1.authenticate(chal, appid, kh)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
with Test("Try to sign using an incorrect keyhandle length"):
try:
kh = regs[0].key_handle
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA
with Test("Try to sign using an incorrect appid"):
badid = bytearray(appid)
badid[0] = badid[0] ^ (0x40)
try:
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
assert 0
except ApduError as e:
assert e.code == APDU.WRONG_DATA

View File

@ -0,0 +1,12 @@
import math
def shannon_entropy(data):
s = 0.0
total = len(data)
for x in range(0, 256):
freq = data.count(x)
p = freq / total
if p > 0:
s -= p * math.log2(p)
return s