Compare commits
109 Commits
Author | SHA1 | Date | |
---|---|---|---|
ba6da64605 | |||
393051f407 | |||
c9894ab68a | |||
65dc8de490 | |||
d0124c615a | |||
![]() |
8b91ec7c53 | ||
![]() |
b86f0ee4e5 | ||
![]() |
13c882b532 | ||
![]() |
483edcb56d | ||
![]() |
b7b5d51cc2 | ||
![]() |
c52af54e8f | ||
![]() |
cc4cd340da | ||
![]() |
ca885941f0 | ||
![]() |
dc27b48101 | ||
![]() |
1bf071f8b4 | ||
![]() |
cc3d3c63ec | ||
![]() |
c248b5d569 | ||
![]() |
85cf25559c | ||
![]() |
299e91b91b | ||
![]() |
cbf40f4ec7 | ||
![]() |
8d93f88631 | ||
![]() |
5f8a9a44fc | ||
![]() |
8aa1f4ad01 | ||
![]() |
04cffb6509 | ||
![]() |
f002d08071 | ||
![]() |
e53b83257d | ||
![]() |
05e149fb17 | ||
![]() |
530e175ad1 | ||
![]() |
6cd3873b37 | ||
![]() |
241f58657b | ||
![]() |
3b42289cce | ||
![]() |
b3712b57fc | ||
![]() |
37769bb735 | ||
![]() |
d677f8c346 | ||
![]() |
98bcf647c4 | ||
![]() |
682a443f4e | ||
![]() |
a28a05673f | ||
![]() |
3a70ee0ec6 | ||
![]() |
872a320abc | ||
![]() |
3cbf7ec451 | ||
![]() |
748c552eea | ||
![]() |
98f996fcfe | ||
![]() |
97eb6bba8a | ||
![]() |
fdc5a68fcd | ||
![]() |
1c1005a0e8 | ||
![]() |
4831410111 | ||
![]() |
05bc8bee55 | ||
![]() |
7112633779 | ||
![]() |
79b43a90fd | ||
![]() |
ec7a6fd740 | ||
![]() |
f2d6698066 | ||
![]() |
3c9315e34c | ||
![]() |
8ed7157bfe | ||
![]() |
28a1b1cc06 | ||
![]() |
5738bcc7a3 | ||
![]() |
4fb166631d | ||
![]() |
1b862d3b0c | ||
![]() |
094420b32b | ||
![]() |
349cbc39f2 | ||
![]() |
e294cb3458 | ||
![]() |
da31f984dd | ||
![]() |
9d3e8c06fc | ||
![]() |
b378bbf61d | ||
![]() |
04b89a9739 | ||
![]() |
0d28a7bcf6 | ||
![]() |
aeafd09007 | ||
![]() |
8b6148ac90 | ||
![]() |
15a4fdfa66 | ||
![]() |
e713daba26 | ||
![]() |
b78f2cd2e7 | ||
![]() |
601c98000a | ||
![]() |
ab1c9417b1 | ||
![]() |
f6d96013e1 | ||
![]() |
f74dba7ff0 | ||
![]() |
794accf3dc | ||
![]() |
2ca0ced808 | ||
![]() |
17b430fd44 | ||
![]() |
0d4197fb2c | ||
![]() |
f74a77d80b | ||
![]() |
5f1d61a3ba | ||
![]() |
46f2920e63 | ||
![]() |
53427c4279 | ||
![]() |
ac10933379 | ||
![]() |
8a44d14fef | ||
![]() |
1d59bbfdd4 | ||
![]() |
54c66d80b6 | ||
![]() |
6217fc34b9 | ||
![]() |
af23e84a8d | ||
![]() |
9650d99b34 | ||
![]() |
145b04750e | ||
![]() |
078acbc4b4 | ||
![]() |
670a4e5d62 | ||
![]() |
de55e521cc | ||
![]() |
bbfe51499f | ||
![]() |
6cb15a6482 | ||
![]() |
85ddc40036 | ||
![]() |
1d63154699 | ||
![]() |
ee55bf3ba0 | ||
![]() |
3b4b6dd4fe | ||
![]() |
28e607ddac | ||
![]() |
dcd256faf4 | ||
![]() |
85365c635d | ||
![]() |
a388607dab | ||
![]() |
d266e7927c | ||
![]() |
0ac074e8a8 | ||
![]() |
67eb721da2 | ||
![]() |
6b5d353501 | ||
![]() |
bacce7d978 | ||
![]() |
975cdf02f2 |
@ -207,6 +207,17 @@
|
||||
"infra",
|
||||
"test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "Nitrokey",
|
||||
"name": "Nitrokey",
|
||||
"avatar_url": "https://avatars1.githubusercontent.com/u/9438831?v=4",
|
||||
"profile": "https://www.nitrokey.com",
|
||||
"contributions": [
|
||||
"code",
|
||||
"test",
|
||||
"ideas"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
58
Makefile
@ -1,3 +1,5 @@
|
||||
include fido2/version.mk
|
||||
|
||||
#define uECC_arch_other 0
|
||||
#define uECC_x86 1
|
||||
#define uECC_x86_64 2
|
||||
@ -6,44 +8,34 @@
|
||||
#define uECC_arm_thumb2 5
|
||||
#define uECC_arm64 6
|
||||
#define uECC_avr 7
|
||||
|
||||
ecc_platform=2
|
||||
|
||||
src = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard fido2/extensions/*.c) \
|
||||
$(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
|
||||
src = pc/device.c pc/main.c
|
||||
|
||||
obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
|
||||
obj = $(src:.c=.o)
|
||||
|
||||
LIBCBOR = tinycbor/lib/libtinycbor.a
|
||||
LIBSOLO = fido2/libsolo.a
|
||||
|
||||
ifeq ($(shell uname -s),Darwin)
|
||||
export LDFLAGS = -Wl,-dead_strip
|
||||
else
|
||||
export LDFLAGS = -Wl,--gc-sections
|
||||
endif
|
||||
LDFLAGS += $(LIBCBOR)
|
||||
LDFLAGS += $(LIBSOLO) $(LIBCBOR)
|
||||
|
||||
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])')
|
||||
|
||||
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
|
||||
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
|
||||
CFLAGS = -O2 -fdata-sections -ffunction-sections -g
|
||||
ECC_CFLAGS = -O2 -fdata-sections -ffunction-sections -DuECC_PLATFORM=$(ecc_platform)
|
||||
|
||||
CFLAGS = -O2 -fdata-sections -ffunction-sections $(VERSION_FLAGS) -g
|
||||
|
||||
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
|
||||
INCLUDES += -I./crypto/cifra/src
|
||||
INCLUDES = -I../ -I./fido2/ -I./pc -I../pc -I./tinycbor/src
|
||||
|
||||
CFLAGS += $(INCLUDES)
|
||||
# for crypto/tiny-AES-c
|
||||
CFLAGS += -DAES256=1 -DAPP_CONFIG=\"app.h\" -DSOLO_EXPERIMENTAL=1
|
||||
CFLAGS += -DAES256=1 -DSOLO_EXPERIMENTAL=1 -DDEBUG_LEVEL=1
|
||||
|
||||
name = main
|
||||
|
||||
.PHONY: all $(LIBCBOR) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
|
||||
.PHONY: all $(LIBCBOR) $(LIBSOLO) black blackcheck cppcheck wink fido2-test clean full-clean travis test clean version
|
||||
all: main
|
||||
|
||||
tinycbor/Makefile crypto/tiny-AES-c/aes.c:
|
||||
@ -53,7 +45,10 @@ tinycbor/Makefile crypto/tiny-AES-c/aes.c:
|
||||
cbor: $(LIBCBOR)
|
||||
|
||||
$(LIBCBOR):
|
||||
cd tinycbor/ && $(MAKE) clean && $(MAKE) LDFLAGS='' -j8
|
||||
cd tinycbor/ && $(MAKE) LDFLAGS='' -j8
|
||||
|
||||
$(LIBSOLO):
|
||||
cd fido2/ && $(MAKE) CFLAGS="$(CFLAGS)" ECC_CFLAGS="$(ECC_CFLAGS)" APP_CONFIG=app.h -j8
|
||||
|
||||
version:
|
||||
@git describe
|
||||
@ -62,16 +57,13 @@ 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)" VERSION_FULL=${SOLO_VERSION_FULL}
|
||||
$(MAKE) clean
|
||||
$(MAKE) cppcheck
|
||||
|
||||
$(name): $(obj) $(LIBCBOR)
|
||||
$(name): $(obj) $(LIBCBOR) $(LIBSOLO)
|
||||
$(CC) $(LDFLAGS) -o $@ $(obj) $(LDFLAGS)
|
||||
|
||||
crypto/micro-ecc/uECC.o: ./crypto/micro-ecc/uECC.c
|
||||
$(CC) -c -o $@ $^ -O2 -fdata-sections -ffunction-sections -DuECC_PLATFORM=$(ecc_platform) -I./crypto/micro-ecc/
|
||||
|
||||
venv:
|
||||
python3 -m venv venv
|
||||
venv/bin/pip -q install --upgrade pip
|
||||
@ -98,20 +90,21 @@ 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}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
|
||||
|
||||
uncached-docker-build-toolchain:
|
||||
docker build --no-cache -t $(DOCKER_TOOLCHAIN_IMAGE) .
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${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 tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}
|
||||
docker tag $(DOCKER_TOOLCHAIN_IMAGE):latest $(DOCKER_TOOLCHAIN_IMAGE):${SOLO_VERSION_MAJ}.${SOLO_VERSION_MIN}
|
||||
|
||||
docker-build-all:
|
||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||
-v "$(CURDIR):/solo" \
|
||||
$(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${VERSION_FULL}
|
||||
-u $(shell id -u ${USER}):$(shell id -g ${USER}) \
|
||||
$(DOCKER_TOOLCHAIN_IMAGE) "solo/in-docker-build.sh" ${SOLO_VERSION_FULL}
|
||||
|
||||
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
||||
|
||||
@ -128,6 +121,7 @@ clean:
|
||||
(cd `dirname $$f` ; git checkout -- .) ;\
|
||||
fi ;\
|
||||
done
|
||||
cd fido2 && $(MAKE) clean
|
||||
|
||||
full-clean: clean
|
||||
rm -rf venv
|
||||
|
19
README.md
@ -30,7 +30,7 @@ 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.dev/building/). We support Python3.
|
||||
|
||||
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
|
||||
to be a different hex color.
|
||||
@ -61,9 +61,11 @@ git checkout ${VERSION_TO_BUILD}
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Installing the toolchain
|
||||
## Installing the toolchain and applying updates
|
||||
|
||||
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
|
||||
In order to compile ARM code, you need the ARM compiler and other things like bundling bootloader and firmware require the [solo-python](https://github.com/solokeys/solo-python) python package. Check our [documentation](https://docs.solokeys.dev/) for details.
|
||||
|
||||
You can update your solokey after running `pip3 install solo-python` with `solo key update` for the latest version. To apply a custom image use `solo program bootloader <file>(.json|.hex)`.
|
||||
|
||||
## Installing the toolkit and compiling in Docker
|
||||
Alternatively, you can use Docker to create a container with the toolchain.
|
||||
@ -120,12 +122,12 @@ Run the Solo application:
|
||||
|
||||
In another shell, you can run our [test suite](https://github.com/solokeys/fido2-tests).
|
||||
|
||||
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
|
||||
You can find more details in our [documentation](https://docs.solokeys.dev/), including how to build on the the NUCLEO-L432KC development board.
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
Check out our [official documentation](https://docs.solokeys.io/solo/).
|
||||
Check out our [official documentation](https://docs.solokeys.dev/).
|
||||
|
||||
|
||||
# Contributors ✨
|
||||
@ -168,6 +170,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center"><a href="https://github.com/jolo1581"><img src="https://avatars1.githubusercontent.com/u/53423977?v=4" width="100px;" alt="Jan A."/><br /><sub><b>Jan A.</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=jolo1581" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/ccinelli"><img src="https://avatars0.githubusercontent.com/u/38021940?v=4" width="100px;" alt="ccinelli"/><br /><sub><b>ccinelli</b></sub></a><br /><a href="#infra-ccinelli" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/solokeys/solo/commits?author=ccinelli" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://www.nitrokey.com"><img src="https://avatars1.githubusercontent.com/u/9438831?v=4" width="100px;" alt="Nitrokey"/><br /><sub><b>Nitrokey</b></sub></a><br /><a href="https://github.com/solokeys/solo/commits?author=Nitrokey" title="Code">💻</a> <a href="https://github.com/solokeys/solo/commits?author=Nitrokey" title="Tests">⚠️</a> <a href="#ideas-Nitrokey" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
@ -183,7 +188,7 @@ You may use Solo software under the terms of either the Apache 2.0 license or MI
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
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.
|
||||
You may use Solo hardware under the terms of either the CERN 1.2 license or CC-BY-SA 4.0 license.
|
||||
|
||||
All documentation, unless otherwise noted, is licensed under CC-BY-SA.
|
||||
You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
|
||||
@ -200,7 +205,7 @@ You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solok
|
||||
<br/>
|
||||
|
||||
[](https://github.com/solokeys/solo/blob/master/LICENSE)
|
||||
[](#contributors)
|
||||
[](#contributors)
|
||||
[](https://travis-ci.com/solokeys/solo)
|
||||
[](https://discourse.solokeys.com)
|
||||
[](https://keybase.io/team/solokeys.public)
|
||||
|
@ -21,7 +21,7 @@ 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>
|
||||
<https://keys.openpgp.org/vks/v1/by-fingerprint/BFA3F5387D025E8945CF907FC2F2505A63868DFA>
|
||||
|
||||
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.
|
||||
|
||||
|
@ -1 +1 @@
|
||||
2.5.3
|
||||
4.0.0
|
||||
|
27
default.nix
Normal file
@ -0,0 +1,27 @@
|
||||
let
|
||||
pkgs = import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/20.03.tar.gz"; sha256 = "0182ys095dfx02vl2a20j1hz92dx3mfgz2a6fhn31bqlp1wa8hlq"; }) { };
|
||||
pyPackages = (python-packages: with python-packages; ([
|
||||
solo-python
|
||||
pytest
|
||||
] ++ (with builtins; map (d: getAttr d python-packages) (filter (d: stringLength d > 0) (pkgs.lib.splitString "\n" (builtins.readFile ./tools/requirements.txt))))));
|
||||
python-with-my-packages = pkgs.python3.withPackages pyPackages;
|
||||
src = with pkgs.lib; builtins.filterSource (path: type: !(hasSuffix path "hex" || hasSuffix path "sha256" || baseNameOf path == "result")) ./.;
|
||||
in
|
||||
with pkgs; stdenv.mkDerivation {
|
||||
name = "solo";
|
||||
outputs = [ "out" ];
|
||||
inherit src;
|
||||
buildInputs = [ src gnumake gcc gcc-arm-embedded-8 git python-with-my-packages ];
|
||||
phases = [ "unpackPhase" "configurePhase" "buildPhase" "installPhase" ];
|
||||
installPhase = ''
|
||||
mkdir -p $out/firmware $out/standalone/bin
|
||||
make
|
||||
cp main $out/standalone/bin/
|
||||
cd targets/stm32l432
|
||||
make cbor
|
||||
make build-hacker
|
||||
cp *.hex *.sha256 *.elf cubeconfig_stm32l442.ioc $out/firmware/
|
||||
ln -s ${src} $out/src
|
||||
'';
|
||||
keepDebugInfo = true;
|
||||
}
|
1
docs/_redirects
Normal file
@ -0,0 +1 @@
|
||||
/solo/* /:splat 302
|
@ -1,16 +1,23 @@
|
||||
# Using Solo for passwordless or second factor login on Linux
|
||||
|
||||
## Setup on Ubuntu 18.04
|
||||
## Setup on Ubuntu and Manjaro
|
||||
Before you can use Solo for passwordless or second factor login in your Linux system you have to install some packages.
|
||||
|
||||
This was tested under **Linux Mint 19.2**.
|
||||
This was tested on **Linux Mint 19.3** and on **Manjaro 18.x**
|
||||
|
||||
First you have to install PAM modules for u2f.
|
||||
|
||||
**Ubuntu (Linux Mint):**
|
||||
```
|
||||
sudo apt install libpam-u2f pamu2fcfg
|
||||
```
|
||||
|
||||
**Manjaro**
|
||||
```
|
||||
pacman -Syu pam-u2f
|
||||
```
|
||||
|
||||
|
||||
## Setting up key
|
||||
To use Solo as passwordless or second factor login, you have to setup your system with your Solo.
|
||||
First create a new folder named **Yubico** in your **.config** folder in your **home** directory
|
||||
@ -28,35 +35,57 @@ If you want to register an additional key use this command instead:
|
||||
pamu2fcfg >> ~/.config/Yubico/u2f_keys
|
||||
```
|
||||
Now press the button on your Solo.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
|
||||
If you can't generate your key (error message), you may add Yubico Team from PPA and install latest libpam-u2f and pamu2fcfg and try again.
|
||||
If you can't generate your key on **Ubuntu** (error message), you may add Yubico Team from PPA and install latest libpam-u2f and pamu2fcfg and try again.
|
||||
```
|
||||
sudo add-apt-repository ppa:yubico/stable
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
```
|
||||
|
||||
**Manjaro** should work without problems.
|
||||
|
||||
|
||||
## Login into Linux
|
||||
### Passwordless
|
||||
To login passwordless into your Linux system, you have to edit the file **lightdm** (or **gdm** or which display manager you prefered).
|
||||
In case of lightdm:
|
||||
In case of lightdm and VIM as editor:
|
||||
|
||||
```
|
||||
sudo vim /etc/pam.d/lightdm
|
||||
sudo vim /etc/pam.d/lightdm
|
||||
```
|
||||
Now search following entry:
|
||||
|
||||
**On Ubuntu:**<br>
|
||||
Search following entry:
|
||||
```
|
||||
@include common-auth
|
||||
@include common-auth
|
||||
```
|
||||
and add
|
||||
```
|
||||
auth sufficient pam_u2f.so
|
||||
auth sufficient pam_u2f.so
|
||||
```
|
||||
**before** @include common-auth.
|
||||
**before** *@include common-auth.*
|
||||
<br>
|
||||
<br>
|
||||
|
||||
Save the file and test it.<br>
|
||||
**On Manjaro**<br>
|
||||
Search following enrty
|
||||
```
|
||||
auth include system-login
|
||||
```
|
||||
|
||||
and add
|
||||
```
|
||||
auth sufficient pam_u2f.so
|
||||
```
|
||||
|
||||
** before** *auth include system-login*.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
Now save the file and test it.<br>
|
||||
Insert Solo in your USB port and logout.
|
||||
Now you should be able to login into Linux without password, only with pressing your button on Solo and press enter.
|
||||
|
||||
@ -65,7 +94,7 @@ Why **sufficient**? The difference between the keyword sufficient and required i
|
||||
|
||||
The login mechanism can be also used for additional features like:
|
||||
|
||||
: - Login after screen timeout - edit /etc/pam.d/mate-screensaver (or kde-screensaver, ...)
|
||||
- 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.
|
||||
@ -78,17 +107,36 @@ The login passwordless won't make your system more secure, but maybe more comfor
|
||||
To use Solo as second factor, for login into your Linux system, is nearly the same.
|
||||
|
||||
```
|
||||
sudo vim /etc/pam.d/lightdm
|
||||
sudo vim /etc/pam.d/lightdm
|
||||
```
|
||||
Now search following entry:
|
||||
|
||||
**On Ubuntu**<br>
|
||||
Search following entry:
|
||||
```
|
||||
@include common-auth
|
||||
@include common-auth
|
||||
```
|
||||
and add
|
||||
```
|
||||
auth required pam_u2f.so
|
||||
auth required pam_u2f.so
|
||||
```
|
||||
**after** @include common-auth.
|
||||
**after** *@include common-auth*.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
**On Manjaro**<br>
|
||||
Search following entry:
|
||||
```
|
||||
auth include system-login
|
||||
```
|
||||
|
||||
Add following entry
|
||||
```
|
||||
auth required pam_u2f.so
|
||||
```
|
||||
|
||||
**after** *auth include system-login*.
|
||||
<br>
|
||||
<br>
|
||||
|
||||
Save the file and test it. <br>
|
||||
In case your Solo is not present, your password will be incrorrect. If Solo is plugged into your USB port, it will signal pressing the button and you will be able to login into Linux.
|
@ -10,7 +10,7 @@ If your Solo is a bit older (<=2.5.3) You can put Solo into bootloader mode by u
|
||||
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.
|
||||
Now Solo is ready to [accept firmware updates](/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
|
||||
|
@ -104,9 +104,24 @@ solo mergehex bootloader.hex solo.hex bundle.hex
|
||||
|
||||
`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.
|
||||
By default the "hacker" attestation certifcate and key is used. Use the `--lock` flag
|
||||
to make this permanent.
|
||||
|
||||
```
|
||||
solo mergehex \
|
||||
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
|
||||
--attestation-cert attestation.der \
|
||||
solo.hex \
|
||||
bootloader.hex \
|
||||
bundle.hex
|
||||
```
|
||||
|
||||
**Warning**: 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.
|
||||
|
||||
```
|
||||
# Permanent!
|
||||
solo mergehex \
|
||||
--attestation-key "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF" \
|
||||
--attestation-cert attestation.der \
|
||||
@ -116,11 +131,7 @@ solo mergehex \
|
||||
bundle.hex
|
||||
```
|
||||
|
||||
See [here for more information on custom attestation](/solo/customization/).
|
||||
See [here for more information on custom attestation](/customization/).
|
||||
|
||||
If you use `--lock`, this will permanently lock the device to this new bootloader. You
|
||||
won't be able to program the bootloader again or be able to connect a hardware debugger.
|
||||
The new bootloader may be able to accept (signed) updates still, depending on how you configured it.
|
||||
|
||||
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/solo/bootloader-mode).
|
||||
To learn more about normal updates or a "full" update, you should [read more on Solo's boot stages](/bootloader-mode).
|
||||
|
@ -19,7 +19,7 @@ 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.
|
||||
You can use the [True RNG on your Solo](/solo-extras) to generate some good random numbers.
|
||||
|
||||
```
|
||||
# Run for 1 second, then hit control-c
|
||||
@ -45,7 +45,7 @@ email=example@example.com
|
||||
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"
|
||||
openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email"
|
||||
|
||||
# self sign the request
|
||||
openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
|
||||
@ -74,7 +74,7 @@ Note you must use a prime256v1 curve for this step, and you must leave the unit/
|
||||
country=US
|
||||
state=Maine
|
||||
organization=OpenSourceSecurity
|
||||
unit="Authenticator Attestation"
|
||||
unit="Authenticator Attestation" # MUST KEEP THIS AS "Authenticator Attestation" for FIDO2.
|
||||
CN=example.com
|
||||
email=example@example.com
|
||||
|
||||
@ -82,7 +82,7 @@ email=example@example.com
|
||||
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"
|
||||
openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=$CN/emailAddress=$email"
|
||||
|
||||
# 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
|
||||
@ -114,12 +114,12 @@ If the checks succeed, you are ready to program the device attestation key and c
|
||||
|
||||
### Programming an attestation key and certificate
|
||||
|
||||
First, [Build your solo application and bootloader](/solo/building).
|
||||
First, [Build your solo application and bootloader](/building).
|
||||
|
||||
Print your attestation key in a hex string format. Using our utility script:
|
||||
|
||||
```
|
||||
python tools/print_x_y.py device_key.pem
|
||||
python3 tools/gencert/print_x_y.py device_key.pem
|
||||
```
|
||||
|
||||
Merge the `bootloader.hex`, `solo.hex`, attestion key, and certificate into one firmware file.
|
||||
@ -134,7 +134,9 @@ solo mergehex \
|
||||
bundle.hex
|
||||
```
|
||||
|
||||
**Warning**: Using the `--lock` flag prevents the DFU from being accessed on the device again. It's recommended to try first without the `--lock` flag to make sure it works.
|
||||
|
||||
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).
|
||||
with Solo in DFU mode](/programming#procedure).
|
||||
|
||||
Are you interested in customizing in bulk? Contact hello@solokeys.com and we can help.
|
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
@ -64,7 +64,7 @@ In that case the mentioned patch would not be required.
|
||||
|
||||
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.
|
||||
See <https://docs.solokeys.dev/building/> for the original guide. Here details not included there will be covered.
|
||||
|
||||
### Install ARM tools Linux
|
||||
|
||||
@ -102,7 +102,7 @@ export PATH="/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM3
|
||||
|
||||
### Building
|
||||
|
||||
Please follow <https://docs.solokeys.io/solo/building/>, as the build way changes rapidly.
|
||||
Please follow <https://docs.solokeys.dev/building/>, as the build way changes rapidly.
|
||||
Currently (8.1.19) to build the firmware, following lines should be executed
|
||||
|
||||
```bash
|
60
docs/porting.md
Normal file
@ -0,0 +1,60 @@
|
||||
# Usage and Porting
|
||||
|
||||
Solo is designed to be used as a library or ported to other platforms easily. Here is an example
|
||||
`main()` function.
|
||||
|
||||
```c
|
||||
int main()
|
||||
{
|
||||
uint8_t hidmsg[64];
|
||||
uint32_t t1 = 0;
|
||||
|
||||
device_init();
|
||||
memset(hidmsg,0,sizeof(hidmsg));
|
||||
|
||||
while(1)
|
||||
{
|
||||
|
||||
if (usbhid_recv(hidmsg) > 0)
|
||||
{
|
||||
ctaphid_handle_packet(hidmsg); // pass into libsolo!
|
||||
memset(hidmsg, 0, sizeof(hidmsg));
|
||||
}
|
||||
|
||||
|
||||
ctaphid_check_timeouts();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
`ctaphid_handle_packet(hidmsg);` is the entrance into the HID layer of libsolo, and will buffer packets and pass them
|
||||
into FIDO2 or U2F layers.
|
||||
|
||||
Everything in the library is cross-platform, but it needs some functions implemented that are usually
|
||||
platform specific. For example, how should libsolo implement an atomic counter? Where should it save state?
|
||||
For all of these platform specific functions, the library contains it's own `weak` definition, so the library will compile and run.
|
||||
LibSolo by default will not try to use an atomic
|
||||
counter or save data persistently -- that needs to be implemented externally.
|
||||
|
||||
If you are using libsolo on another platform,
|
||||
you should take a look at these possibly platform specific functions. They are listed in `fido2/device.h`.
|
||||
If you'd like to reimplement any of the functions, then simply implement the function and compile normally.
|
||||
GCC will replace libsolo's `weak` defined functions (everything in `fido2/device.h`) with your functions. By doing this, you
|
||||
are replacing the function that is used by libsolo.
|
||||
|
||||
To get the library to compile
|
||||
and run, you only need to implement one function for libsolo: `usbhid_send(uint8_t * send)`, which
|
||||
is called by the library to send a 64 byte packet over a USB HID endpoint. In essence, you are giving
|
||||
libsolo a function to write to USB.
|
||||
|
||||
The rest of the definitions in `fido2/device.h` are not required to compile and run so you can
|
||||
immediately hit the ground running and iterative add what else you need. You'll definitely want
|
||||
to continue implementing other functions in `fido2/device.h`. For example, no data will be stored
|
||||
persistently until you define how it can be done!
|
||||
|
||||
For examples, check out the build for STM32L4 and PC (check out `pc/device` and `targets/stm32l432/src/device.c`).
|
||||
|
||||
If there's something that doesn't work for you -- send a pull request! It's better if we can
|
||||
work together off of the same repo and not fork.
|
@ -13,7 +13,7 @@ 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.
|
||||
Make sure your key [is in bootloader mode](/bootloader-mode#solo-bootloader) first.
|
||||
|
||||
```bash
|
||||
solo key update <--secure | --hacker>
|
||||
@ -26,19 +26,23 @@ 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/).
|
||||
risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations](/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).
|
||||
Updating a hacker to be a secure build overwrites the [Solo bootloader](/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.
|
||||
You need to use a firmware file that has the combined bootloader, application, and attestation key pair (bootloader + firmware + key).
|
||||
This means using the `bundle-*.hex` file or the `bundle.hex` from your build.
|
||||
|
||||
#### *Warning*
|
||||
|
||||
* **Any DFU update erases everything! If you overwrite the Solo flash with a missing bootloader, it will be bricked.**
|
||||
* **If you program bootloader and firmware with no attestation, you will run into FIDO registration issues.**
|
||||
|
||||
We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this,
|
||||
you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`
|
1
docs/repo-readme.md
Symbolic link
@ -0,0 +1 @@
|
||||
../README.md
|
@ -1 +0,0 @@
|
||||
../../README.md
|
219
docs/tutorial-getting-started.md
Executable file
@ -0,0 +1,219 @@
|
||||
# Tutorial: Getting started with the solo hacker
|
||||
|
||||
This is small guide to let you get started with the solo hacker key. In the end you will have set up everything you need and changed the LED from green to red.
|
||||
|
||||
## Some additional ressources
|
||||
|
||||
This tutorial will take you through all the necessary steps needed to install and get the solo key running. Before we start, I will just list you additional ressources, which might have important information for you:
|
||||
|
||||
* [The git repository](https://github.com/solokeys/solo): Here you will find all the code and a quick readme.
|
||||
* [The Documenation](https://docs.solokeys.io/solo/building/): The official documentation. Especially the [build instructions](https://docs.solokeys.io/solo/building/) are worth a look, if you got stuck.
|
||||
|
||||
## Getting the prerequisites
|
||||
|
||||
There are two main tools you will need to work on your solo hacker:
|
||||
|
||||
* ARM Compiler tool chain
|
||||
* Solo python tool
|
||||
|
||||
The ARM Compiler is used to compile your C-code to a hex file, which can then be deployed onto your solo hacker. The solo tool helps with deploying, updating etc. of the solo hacker. It is a python3 tool. So make sure, that you got Python3 installed on your system \([pip](https://pip.pypa.io/en/stable/) might also come in handy\).
|
||||
|
||||
Besides that, you will also need to get the [solo code](https://github.com/solokeys/solo).
|
||||
|
||||
### Get the code
|
||||
|
||||
The codebase for the solo hacker and other solo keys, can be found at this [git repository](https://github.com/solokeys/solo). So just clone this into your development folder. Make sure, that all the submodules are loaded by using the following command. I forgot to get all the submoules at my first try and the make command failed \(I got an error message telling me, that no solo.elf target can be found\).
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/solokeys/solo
|
||||
```
|
||||
|
||||
### Getting the ARM Compiler tool chain
|
||||
|
||||
Download the Compiler tool chain for your system [here](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads). After you have downloaded it, you will have to unzip it and add the path to the installation folder.
|
||||
|
||||
**Readme**
|
||||
There is a readme.txt __ in _gcc-arm-none-eabi-x-yyyy-dd-major/share/doc/gcc-arm-none-eabi_. It contains installation guides for Linux, Windows and Mac.
|
||||
|
||||
**Installation**
|
||||
As I used Mac, I will guide you through the installation using MacOS. If you have unpacked the folder already, you can skip the first step.
|
||||
|
||||
```bash
|
||||
#Unzip the tarball
|
||||
cd $install_dir && tar xjf gcc-arm-none-eabi-*-yyyymmdd-mac.tar.bz2
|
||||
|
||||
#Set path
|
||||
export PATH=$PATH:$install_dir/gcc-arm-none-eabi-*/bin
|
||||
|
||||
#Test if it works
|
||||
arm-none-eabi-gcc
|
||||
```
|
||||
|
||||
If everything worked your output should like this:
|
||||
|
||||
```bash
|
||||
arm-none-eabi-gcc: fatal error: no input files
|
||||
compilation terminated.
|
||||
```
|
||||
|
||||
### Getting the solo tool
|
||||
|
||||
There are several ways, which are listed at the [build instructions](https://docs.solokeys.io/solo/building/). If you are familiar with pip, just use this.
|
||||
|
||||
```bash
|
||||
pip install solo-python
|
||||
|
||||
#Or
|
||||
pip3 install solo-python
|
||||
```
|
||||
|
||||
**Install all other requirements**
|
||||
|
||||
To do this either do it in the virtual env or directly on your machine. The requirements can be found in the source folder in requirements.txt.
|
||||
|
||||
```bash
|
||||
#Move to source folder
|
||||
cd solo
|
||||
|
||||
#Install requirements, use pip3 otherwise
|
||||
pip install -r solo/tools/requirements.txt
|
||||
```
|
||||
|
||||
## Let's get a red light blinking
|
||||
|
||||
You will find the code for the key in _/solo/targets/stm32l432_ \(The target might have another id for you, so just use that id\). The LED colors can be found in [_/solo/targets/stm32l432 /src/app.h_](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h)_._ To change the color we will just have to change the hex-value. Out of the box it should look like this:
|
||||
|
||||
```c
|
||||
// 0xRRGGBB
|
||||
#define LED_INIT_VALUE 0x000800
|
||||
#define LED_WINK_VALUE 0x000010
|
||||
#define LED_MAX_SCALER 15
|
||||
#define LED_MIN_SCALER 1
|
||||
```
|
||||
|
||||
_LED\_INIT\_VALUE_ is the color, that the LED shows whenever it is plugged in. It normally is a green light. So let's change it to red:
|
||||
|
||||
```c
|
||||
// 0xRRGGBB
|
||||
#define LED_INIT_VALUE 0xFF0800
|
||||
#define LED_WINK_VALUE 0x000010
|
||||
#define LED_MAX_SCALER 15
|
||||
#define LED_MIN_SCALER 1
|
||||
```
|
||||
|
||||
_LED\_WINK\_VALUE_ is the color, which is shown, whenever the bottom is pressed. It normally is a blue tone, but let's change it to a yellow:
|
||||
|
||||
```c
|
||||
// 0xRRGGBB
|
||||
#define LED_INIT_VALUE 0xFF0800
|
||||
#define LED_WINK_VALUE 0xFFFF00
|
||||
#define LED_MAX_SCALER 15
|
||||
#define LED_MIN_SCALER 1
|
||||
```
|
||||
|
||||
Save the file and then let's try to get the code onto the stick.
|
||||
|
||||
## Move code to solo hacker
|
||||
|
||||
First we have to build cbor. To do this change into the target folder and use the corresponding command.
|
||||
|
||||
```bash
|
||||
#Change into correct directory
|
||||
cd solo/targets/stm32l432/
|
||||
|
||||
#Make cbor
|
||||
make cbor
|
||||
```
|
||||
|
||||
You should also make sure to check, that your key has the newest solo firmware installed. To check the firmware on the device, use this command:
|
||||
|
||||
```bash
|
||||
solo key version
|
||||
```
|
||||
|
||||
To update to the newest version, use this command:
|
||||
|
||||
```bash
|
||||
solo key version
|
||||
```
|
||||
|
||||
**Note:** Sometimes the connection between Mac and key seemed to be broken and you might get an error stating: _No solo found_. Just unplug the key and plug it back in.
|
||||
|
||||
### General deployment cycle
|
||||
|
||||
In general we will always have to go through these steps:
|
||||
|
||||
* Compile code and generate new firmware
|
||||
* Change device into bootloader mode
|
||||
* Deploy code to device
|
||||
|
||||
#### Compile code
|
||||
|
||||
To compile the code, we will again have to change into our target directory:
|
||||
|
||||
```bash
|
||||
#Change into correct directory
|
||||
cd solo/targets/stm32l432/
|
||||
```
|
||||
|
||||
It is important to choose the correct build target. Most explanations focus on the build of the firmware and use:
|
||||
|
||||
```bash
|
||||
make firmware
|
||||
```
|
||||
|
||||
As we are using the solo hacker, we will need to use:
|
||||
|
||||
```bash
|
||||
make build-hacker
|
||||
```
|
||||
|
||||
This will generate a file _solo.hex_, which has the compiled code on it. If you later need to change the bootloader itself, please refer to [the documentation](https://docs.solokeys.io/solo/building/).
|
||||
|
||||
#### Deploy code
|
||||
|
||||
To deploy the code make sure you are back at the source root.
|
||||
|
||||
```bash
|
||||
cd ../..
|
||||
```
|
||||
|
||||
First we will have to change into bootload modus:
|
||||
|
||||
```bash
|
||||
solo program aux enter-bootloader
|
||||
```
|
||||
|
||||
This is needed to be able to load the new firmware on the device. If we forget this step, the solo tool will do it for us in the next step.
|
||||
|
||||
This is the moment of truth. We delete the old firmware and deploy the new one with the changed LED lights to the solo key. For this step we will also stay in the source root.
|
||||
|
||||
```bash
|
||||
solo program bootloader targets/stm32l432/solo.hex
|
||||
```
|
||||
|
||||
If there is another hex-File, that you want to load, you can just exchange the last argument.
|
||||
|
||||
And that's it, now your LED should be red.
|
||||
|
||||
To summarize, here are again the steps to update your solo:
|
||||
|
||||
1. Change code
|
||||
2. Run these commands
|
||||
|
||||
```bash
|
||||
#Change into correct directory
|
||||
cd solo/targets/stm32l432/
|
||||
|
||||
#Compile code
|
||||
make build-hacker
|
||||
|
||||
#Change to root
|
||||
cd ../..
|
||||
|
||||
#Enter bootload mode
|
||||
solo program aux enter-bootloader
|
||||
|
||||
#Deploy code
|
||||
solo program bootloader targets/stm32l432/solo.hex
|
||||
```
|
205
docs/tutorial-writing-extensions.md
Executable file
@ -0,0 +1,205 @@
|
||||
# Tutorial: Writing an extension for the solo stick
|
||||
A short overview about, where and how you should implement your extension into the solo stick code base. In this tutorial we will add a small extension, that will engage in a "ping"-"pong" exchange.
|
||||
|
||||
## Make it visible
|
||||
We need to make it visible, that the key now supports a new extension.
|
||||
This is done in the function _ctap_get_info_ in [ctap.c](https://github.com/solokeys/solo/blob/master/fido2/ctap.c). This function creates a map with all the information about the solo key. You should therefore add your extension identifier here, too.
|
||||
```c
|
||||
uint8_t ctap_get_info(CborEncoder * encoder){
|
||||
//[...]
|
||||
ret = cbor_encode_uint(&map, RESP_extensions);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encoder_create_array(&map, &array, 3);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&array, "hmac-secret");
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encode_text_stringz(&array, "credProtect");
|
||||
check_ret(ret);
|
||||
|
||||
//Add it here
|
||||
}
|
||||
ret = cbor_encoder_close_container(&map, &array);
|
||||
check_ret(ret);
|
||||
}
|
||||
//[...]
|
||||
}
|
||||
```
|
||||
After you have added your identifier it should look similiar to this:
|
||||
```c
|
||||
uint8_t ctap_get_info(CborEncoder * encoder){
|
||||
//[...]
|
||||
ret = cbor_encode_uint(&map, RESP_extensions);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encoder_create_array(&map, &array, 3); //This number should reflect the number of supported extensions
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&array, "hmac-secret");
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encode_text_stringz(&array, "credProtect");
|
||||
check_ret(ret);
|
||||
|
||||
//Add it here
|
||||
ret = cbor_encode_text_stringz(&array, "ping-pong");
|
||||
check_ret(ret);
|
||||
}
|
||||
ret = cbor_encoder_close_container(&map, &array);
|
||||
check_ret(ret);
|
||||
}
|
||||
//[...]
|
||||
}
|
||||
|
||||
```
|
||||
Important: make sure to change the size of the created array to the correct number of elements.
|
||||
|
||||
## Let's get our extension parsed
|
||||
As with all incoming messages, the extension has to be parsed and depending on the incoming message the reply has to be constructed. For this the function _ctap_parse_extensions_ in [ctap_parse.c](https://github.com/solokeys/solo/blob/master/fido2/ctap_parse.c) is used.
|
||||
In this function the extension identifier is checked. So, if we want to add our ping-pong extension, we need to compare the incoming identifier to our identifier "ping-pong".
|
||||
```c
|
||||
uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext){
|
||||
//[...]
|
||||
|
||||
if (strncmp(key, "hmac-secret",11) == 0){
|
||||
//[...]
|
||||
}else if (strncmp(key, "credProtect",11) == 0) {
|
||||
//[...]
|
||||
else if (strncmp(key, "ping-pong",9) == 0) {
|
||||
//Logic should be placed here
|
||||
}
|
||||
//[...]
|
||||
}
|
||||
```
|
||||
What happens then, depends on your extension. You should make sure, to check incoming values for correctness, though. As hmac-secret and credProtect are already implemented, you could have a look at their implementations for a kind of guideline.
|
||||
At this stage we can use the extension struct, which can be found in [ctap.h](https://github.com/solokeys/solo/blob/master/fido2/ctap.h).
|
||||
```c
|
||||
typedef struct
|
||||
{
|
||||
uint8_t hmac_secret_present;
|
||||
CTAP_hmac_secret hmac_secret;
|
||||
|
||||
uint32_t cred_protect;
|
||||
} CTAP_extensions;
|
||||
```
|
||||
This struct already contains important values for the other extensions, so we are going to add two for our extension. The first "ping_pong_present" should indicate if the key received a message with a ping-pong extension. The response should then contain the correct response.
|
||||
```c
|
||||
typedef struct
|
||||
{
|
||||
uint8_t hmac_secret_present;
|
||||
CTAP_hmac_secret hmac_secret;
|
||||
|
||||
uint32_t cred_protect;
|
||||
|
||||
uint8_t ping_pong_present;
|
||||
char ping_pong_response[4];
|
||||
} CTAP_extensions;
|
||||
```
|
||||
Now we have to parse our message accordingly.
|
||||
```c
|
||||
uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext){
|
||||
//[...]
|
||||
|
||||
if (strncmp(key, "hmac-secret",11) == 0){
|
||||
//[...]
|
||||
}else if (strncmp(key, "credProtect",11) == 0) {
|
||||
//[...]
|
||||
else if (strncmp(key, "ping-pong",9) == 0) {
|
||||
if (cbor_value_get_type(&map) == CborTextStringType)
|
||||
{
|
||||
//Cop incoming message
|
||||
uint8_t txt[5];
|
||||
sz = sizeof(txt);
|
||||
ret = cbor_value_copy_text_string(&map, (char *)txt, &sz, NULL);
|
||||
check_ret(ret);
|
||||
|
||||
if(strcmp((const char*)txt, "ping") == 0) {
|
||||
ext->ping_pong_present = 0x01;
|
||||
strcpy((char *)ext->ping_pong_response, "pong");
|
||||
}else if(strcmp((const char*)txt, "pong") == 0) {
|
||||
ext->ping_pong_present = 0x01;
|
||||
strcpy((char *)ext->ping_pong_response, "ping");
|
||||
}else{
|
||||
printf2(TAG_ERR, "Wrong parameter requested. Got %s.\r\n", txt);
|
||||
return CTAP2_ERR_INVALID_OPTION;
|
||||
}
|
||||
}else{
|
||||
printf1(TAG_RED, "warning: ping-pong request ignored for being wrong type\r\n");
|
||||
}
|
||||
}
|
||||
//[...]
|
||||
}
|
||||
```
|
||||
Here we are doing the following:
|
||||
1. Check if we got a message with either "ping" or "pong"
|
||||
2. Set the correct value, to note, that we received a message using the ping-pong extension
|
||||
3. Set the correct response ("pong" for "ping" and vice versa)
|
||||
|
||||
|
||||
## Create a reply
|
||||
Now, that we have parsed the correct message, we have to construct the correct reply. That is done in the function _ctap_make_extensions_ in [ctap.c](https://github.com/solokeys/solo/blob/master/fido2/ctap.c). We will use the before filled _CTAP_extensions_ in here.
|
||||
We have to do two things here:
|
||||
1. Check, if a message using the ping-pong extension
|
||||
2. Set the correct response according to our parsed incoming message
|
||||
```c
|
||||
static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf, unsigned int * ext_encoder_buf_size){
|
||||
//[...]
|
||||
|
||||
if (ext->hmac_secret_present == EXT_HMAC_SECRET_PARSED)
|
||||
{
|
||||
//[...]
|
||||
}
|
||||
else if (ext->hmac_secret_present == EXT_HMAC_SECRET_REQUESTED)
|
||||
{
|
||||
//[...]
|
||||
}
|
||||
if (ext->cred_protect != EXT_CRED_PROTECT_INVALID) {
|
||||
//[...]
|
||||
}
|
||||
|
||||
if(ext->ping_pong_present){
|
||||
extensions_used += 1;
|
||||
ping_pong_is_valid = 1;
|
||||
}
|
||||
|
||||
if (extensions_used > 0)
|
||||
{
|
||||
//[...]
|
||||
if (hmac_secret_output_is_valid) {
|
||||
{
|
||||
//[...]
|
||||
}
|
||||
}
|
||||
if (hmac_secret_requested_is_valid) {
|
||||
{
|
||||
//[...]
|
||||
}
|
||||
}
|
||||
if (cred_protect_is_valid) {
|
||||
{
|
||||
//[...]
|
||||
}
|
||||
}
|
||||
if (ping_pong_is_valid) {
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&extension_output_map, "ping-pong");
|
||||
check_ret(ret);
|
||||
|
||||
//Set the response message
|
||||
ret = cbor_encode_text_stringz(&extension_output_map, (const char*)ext->ping_pong_response);
|
||||
check_ret(ret);
|
||||
}
|
||||
}
|
||||
//[...]
|
||||
}
|
||||
//[...]
|
||||
}
|
||||
```
|
||||
|
||||
## Recap
|
||||
To create a new extension, you would have to take the following three steps:
|
||||
- Make sure, that the new extension will be made visible through a call of get_info
|
||||
- Parse incoming messages correctly
|
||||
- Construct the correct reply
|
@ -4,17 +4,12 @@ On Linux, by default USB dongles can't be accessed by users, for security reason
|
||||
|
||||
For some users, things will work automatically:
|
||||
|
||||
- Recent Linux distributions (such as Ubuntu Focal, Fedora 32, [Arch Linux](https://wiki.archlinux.org/index.php/Solo)) with systemd 244 or higher automatically detect FIDO devices (check with `systemctl --version`)
|
||||
- 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:
|
||||
|
||||
```
|
44
fido2/Makefile
Normal file
@ -0,0 +1,44 @@
|
||||
include version.mk
|
||||
|
||||
ifndef APP_CONFIG
|
||||
APP_CONFIG=example_app.h
|
||||
endif
|
||||
|
||||
INC = -I./ -I./extensions
|
||||
INC += -I../tinycbor/src
|
||||
INC += -I../crypto/sha256 -I../crypto/micro-ecc -I../crypto/tiny-AES-c
|
||||
INC += -I../crypto/cifra/src -I../crypto/cifra/src/ext
|
||||
|
||||
INT_CFLAGS = -DAPP_CONFIG=\"$(APP_CONFIG)\"
|
||||
INT_CFLAGS += $(INC)
|
||||
INT_CFLAGS += $(SOLO_VERSION_FLAGS)
|
||||
|
||||
SRC = apdu.c util.c u2f.c test_power.c
|
||||
SRC += stubs.c log.c ctaphid.c ctap.c
|
||||
SRC += ctap_parse.c crypto.c
|
||||
SRC += device.c
|
||||
SRC += version.c
|
||||
SRC += data_migration.c
|
||||
SRC += extensions/extensions.c extensions/solo.c
|
||||
SRC += extensions/wallet.c
|
||||
|
||||
# Crypto libs
|
||||
SRC += ../crypto/sha256/sha256.c ../crypto/micro-ecc/uECC.c ../crypto/tiny-AES-c/aes.c
|
||||
SRC += ../crypto/cifra/src/sha512.c ../crypto/cifra/src/blockwise.c
|
||||
|
||||
OBJ = $(SRC:.c=.o)
|
||||
|
||||
all: libsolo.a
|
||||
|
||||
libsolo.a: $(OBJ)
|
||||
$(AR) cqs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $^ $(INT_CFLAGS) $(CFLAGS) -c -o $@
|
||||
|
||||
../crypto/micro-ecc/uECC.o: ../crypto/micro-ecc/uECC.c
|
||||
$(CC) $^ $(INT_CFLAGS) $(ECC_CFLAGS) -c -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) libsolo.a
|
||||
|
143
fido2/crypto.c
@ -5,29 +5,33 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
/*
|
||||
* Wrapper for crypto implementation on device
|
||||
* Wrapper for crypto implementation on device.
|
||||
*
|
||||
* Can be replaced with different crypto implementation by
|
||||
* defining EXTERNAL_SOLO_CRYPTO
|
||||
*
|
||||
* */
|
||||
#ifndef EXTERNAL_SOLO_CRYPTO
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
#include "util.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef USE_SOFTWARE_IMPLEMENTATION
|
||||
|
||||
#include "sha256.h"
|
||||
#include "uECC.h"
|
||||
#include "aes.h"
|
||||
#include "ctap.h"
|
||||
#include "device.h"
|
||||
#include "log.h"
|
||||
// stuff for SHA512
|
||||
#include "sha2.h"
|
||||
#include "blockwise.h"
|
||||
#include APP_CONFIG
|
||||
#include "log.h"
|
||||
|
||||
|
||||
#ifdef USING_PC
|
||||
typedef enum
|
||||
{
|
||||
MBEDTLS_ECP_DP_NONE = 0,
|
||||
@ -44,53 +48,56 @@ typedef enum
|
||||
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
|
||||
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
|
||||
} mbedtls_ecp_group_id;
|
||||
#endif
|
||||
|
||||
|
||||
const uint8_t * attestation_cert_der;
|
||||
const uint16_t attestation_cert_der_size;
|
||||
const uint8_t attestation_key[];
|
||||
const uint16_t attestation_key_size;
|
||||
|
||||
|
||||
|
||||
static SHA256_CTX sha256_ctx;
|
||||
static cf_sha512_context sha512_ctx;
|
||||
static const struct uECC_Curve_t * _es256_curve = NULL;
|
||||
static const uint8_t * _signing_key = NULL;
|
||||
static int _key_len = 0;
|
||||
|
||||
// Secrets for testing only
|
||||
static uint8_t master_secret[64];
|
||||
|
||||
static uint8_t transport_secret[32];
|
||||
|
||||
|
||||
|
||||
void crypto_sha256_init()
|
||||
void crypto_sha256_init(void)
|
||||
{
|
||||
sha256_init(&sha256_ctx);
|
||||
}
|
||||
|
||||
void crypto_reset_master_secret()
|
||||
void crypto_sha512_init(void)
|
||||
{
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
cf_sha512_init(&sha512_ctx);
|
||||
}
|
||||
|
||||
void crypto_load_master_secret(uint8_t * key)
|
||||
{
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
memmove(master_secret, key, 64);
|
||||
memmove(transport_secret, key+64, 32);
|
||||
}
|
||||
|
||||
void crypto_reset_master_secret(void)
|
||||
{
|
||||
memset(master_secret, 0, 64);
|
||||
memset(transport_secret, 0, 32);
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
}
|
||||
|
||||
|
||||
void crypto_sha256_update(uint8_t * data, size_t len)
|
||||
{
|
||||
sha256_update(&sha256_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha512_update(const uint8_t * data, size_t len) {
|
||||
cf_sha512_update(&sha512_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha256_update_secret()
|
||||
{
|
||||
sha256_update(&sha256_ctx, master_secret, 32);
|
||||
@ -101,16 +108,22 @@ void crypto_sha256_final(uint8_t * hash)
|
||||
sha256_final(&sha256_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha512_final(uint8_t * hash)
|
||||
{
|
||||
// NB: there is also cf_sha512_digest
|
||||
cf_sha512_digest_final(&sha512_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
int i;
|
||||
unsigned int i;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret);
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
@ -120,7 +133,7 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, key size must be <= 64\n");
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -138,19 +151,24 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
int i;
|
||||
unsigned int i;
|
||||
crypto_sha256_final(hmac);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret);
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY2)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, key size must be <= 64\n");
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
memmove(buf, key, klen);
|
||||
@ -167,16 +185,16 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_init()
|
||||
void crypto_ecc256_init(void)
|
||||
{
|
||||
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
|
||||
_es256_curve = uECC_secp256r1();
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_load_attestation_key()
|
||||
void crypto_ecc256_load_attestation_key(void)
|
||||
{
|
||||
_signing_key = attestation_key;
|
||||
_signing_key = device_get_attestation_key();
|
||||
_key_len = 32;
|
||||
}
|
||||
|
||||
@ -184,7 +202,7 @@ void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
|
||||
{
|
||||
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR,"error, uECC failed\n");
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -221,19 +239,19 @@ void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_I
|
||||
if (_key_len != 32) goto fail;
|
||||
break;
|
||||
default:
|
||||
printf2(TAG_ERR,"error, invalid ECDSA alg specifier\n");
|
||||
printf2(TAG_ERR, "error, invalid ECDSA alg specifier\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR,"error, uECC failed\n");
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
printf2(TAG_ERR,"error, invalid key length\n");
|
||||
printf2(TAG_ERR, "error, invalid key length\n");
|
||||
exit(1);
|
||||
|
||||
}
|
||||
@ -243,8 +261,11 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
|
||||
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
crypto_sha256_update(data, len);
|
||||
crypto_sha256_update(data2, len2);
|
||||
crypto_sha256_update(master_secret, 32);
|
||||
crypto_sha256_update(master_secret, 32); // TODO AES
|
||||
crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
|
||||
crypto_aes256_init(master_secret + 32, NULL);
|
||||
crypto_aes256_encrypt(privkey, 32);
|
||||
}
|
||||
|
||||
|
||||
@ -261,12 +282,12 @@ 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)
|
||||
{
|
||||
_signing_key = key;
|
||||
@ -278,7 +299,7 @@ void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
|
||||
{
|
||||
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, uECC_make_key failed\n");
|
||||
printf2(TAG_ERR, "Error, uECC_make_key failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@ -287,7 +308,7 @@ void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey
|
||||
{
|
||||
if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, uECC_shared_secret failed\n");
|
||||
printf2(TAG_ERR, "Error, uECC_shared_secret failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -338,44 +359,4 @@ void crypto_aes256_encrypt(uint8_t * buf, int length)
|
||||
}
|
||||
|
||||
|
||||
const uint8_t _attestation_cert_der[] =
|
||||
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
|
||||
"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
|
||||
"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
|
||||
"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
|
||||
"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
|
||||
"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
|
||||
"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
|
||||
"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
|
||||
"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
|
||||
"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
|
||||
"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
|
||||
"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
|
||||
"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
|
||||
"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
|
||||
"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
|
||||
"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
|
||||
"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
|
||||
"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
|
||||
"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
|
||||
"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
|
||||
"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
|
||||
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
|
||||
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
|
||||
const 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 uint8_t attestation_key[] = "\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46\xb7\x2e\x5f\xe7\x5d\x30";
|
||||
const uint16_t attestation_key_size = sizeof(attestation_key)-1;
|
||||
|
||||
|
||||
#else
|
||||
#error "No crypto implementation defined"
|
||||
#endif
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define USE_SOFTWARE_IMPLEMENTATION
|
||||
|
||||
void crypto_sha256_init();
|
||||
void crypto_sha256_update(uint8_t * data, size_t len);
|
||||
void crypto_sha256_update_secret();
|
||||
@ -23,7 +21,6 @@ void crypto_sha512_init();
|
||||
void crypto_sha512_update(const uint8_t * data, size_t len);
|
||||
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);
|
||||
@ -54,7 +51,4 @@ 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();
|
||||
|
||||
#endif
|
||||
|
896
fido2/ctap.c
66
fido2/ctap.h
@ -16,12 +16,11 @@
|
||||
#define CTAP_CLIENT_PIN 0x06
|
||||
#define CTAP_RESET 0x07
|
||||
#define GET_NEXT_ASSERTION 0x08
|
||||
#define CTAP_CBOR_CRED_MGMT 0x0A
|
||||
#define CTAP_VENDOR_FIRST 0x40
|
||||
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
|
||||
#define CTAP_VENDOR_LAST 0xBF
|
||||
|
||||
// AAGUID For Solo
|
||||
#define CTAP_AAGUID ((uint8_t*)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79")
|
||||
|
||||
#define MC_clientDataHash 0x01
|
||||
#define MC_rp 0x02
|
||||
#define MC_user 0x03
|
||||
@ -40,6 +39,19 @@
|
||||
#define GA_pinAuth 0x06
|
||||
#define GA_pinProtocol 0x07
|
||||
|
||||
#define CM_cmd 0x01
|
||||
#define CM_cmdMetadata 0x01
|
||||
#define CM_cmdRPBegin 0x02
|
||||
#define CM_cmdRPNext 0x03
|
||||
#define CM_cmdRKBegin 0x04
|
||||
#define CM_cmdRKNext 0x05
|
||||
#define CM_cmdRKDelete 0x06
|
||||
#define CM_subCommandParams 0x02
|
||||
#define CM_subCommandRpId 0x01
|
||||
#define CM_subCommandCred 0x02
|
||||
#define CM_pinProtocol 0x03
|
||||
#define CM_pinAuth 0x04
|
||||
|
||||
#define CP_pinProtocol 0x01
|
||||
#define CP_subCommand 0x02
|
||||
#define CP_cmdGetRetries 0x01
|
||||
@ -61,6 +73,11 @@
|
||||
#define EXT_HMAC_SECRET_REQUESTED 0x01
|
||||
#define EXT_HMAC_SECRET_PARSED 0x02
|
||||
|
||||
#define EXT_CRED_PROTECT_INVALID 0x00
|
||||
#define EXT_CRED_PROTECT_OPTIONAL 0x01
|
||||
#define EXT_CRED_PROTECT_OPTIONAL_WITH_CREDID 0x02
|
||||
#define EXT_CRED_PROTECT_REQUIRED 0x03
|
||||
|
||||
#define RESP_versions 0x1
|
||||
#define RESP_extensions 0x2
|
||||
#define RESP_aaguid 0x3
|
||||
@ -144,16 +161,29 @@ typedef struct
|
||||
|
||||
typedef struct {
|
||||
uint8_t tag[CREDENTIAL_TAG_SIZE];
|
||||
union {
|
||||
uint8_t nonce[CREDENTIAL_NONCE_SIZE];
|
||||
struct {
|
||||
uint8_t _pad[CREDENTIAL_NONCE_SIZE - 4];
|
||||
uint32_t value;
|
||||
}__attribute__((packed)) metadata;
|
||||
}__attribute__((packed)) entropy;
|
||||
uint8_t rpIdHash[32];
|
||||
uint32_t count;
|
||||
}__attribute__((packed)) CredentialId;
|
||||
|
||||
struct Credential {
|
||||
struct __attribute__((packed)) Credential {
|
||||
CredentialId id;
|
||||
CTAP_userEntity user;
|
||||
};
|
||||
typedef struct Credential CTAP_residentKey;
|
||||
typedef struct {
|
||||
CredentialId id;
|
||||
CTAP_userEntity user;
|
||||
|
||||
// Maximum amount of "extra" space in resident key.
|
||||
uint8_t rpId[48];
|
||||
uint8_t rpIdSize;
|
||||
} __attribute__((packed)) CTAP_residentKey;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -220,6 +250,7 @@ typedef struct
|
||||
{
|
||||
uint8_t hmac_secret_present;
|
||||
CTAP_hmac_secret hmac_secret;
|
||||
uint32_t cred_protect;
|
||||
} CTAP_extensions;
|
||||
|
||||
typedef struct
|
||||
@ -288,6 +319,26 @@ typedef struct
|
||||
|
||||
} CTAP_getAssertion;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int cmd;
|
||||
struct {
|
||||
uint8_t rpIdHash[32];
|
||||
CTAP_credentialDescriptor credentialDescriptor;
|
||||
} subCommandParams;
|
||||
|
||||
struct {
|
||||
uint8_t cmd;
|
||||
uint8_t subCommandParamsCborCopy[sizeof(CTAP_credentialDescriptor) + 16];
|
||||
} hashed;
|
||||
uint32_t subCommandParamsCborSize;
|
||||
|
||||
uint8_t pinAuth[16];
|
||||
uint8_t pinAuthPresent;
|
||||
int pinProtocol;
|
||||
} CTAP_credMgmt;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int pinProtocol;
|
||||
@ -306,7 +357,12 @@ typedef struct
|
||||
|
||||
|
||||
struct _getAssertionState {
|
||||
// Room for both authData struct and extensions
|
||||
struct {
|
||||
CTAP_authDataHeader authData;
|
||||
uint8_t extensions[80];
|
||||
} __attribute__((packed)) buf;
|
||||
CTAP_extensions extensions;
|
||||
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
||||
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
|
||||
uint8_t lastcmd;
|
||||
|
@ -666,8 +666,8 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
|
||||
cbor_value_advance(&map);
|
||||
cbor_value_advance(&map);
|
||||
check_ret( cbor_value_advance(&map) );
|
||||
check_ret( cbor_value_advance(&map) );
|
||||
continue;
|
||||
}
|
||||
check_ret(ret);
|
||||
@ -698,6 +698,14 @@ uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
|
||||
}
|
||||
}
|
||||
else if (strncmp(key, "credProtect",11) == 0) {
|
||||
if (cbor_value_get_type(&map) == CborIntegerType) {
|
||||
ret = cbor_value_get_int(&map, (int*)&ext->cred_protect);
|
||||
check_ret(ret);
|
||||
} else {
|
||||
printf1(TAG_RED, "warning: credProtect request ignored for being wrong type\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
@ -871,7 +879,7 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
cbor_value_advance(&map);
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
@ -999,6 +1007,163 @@ uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t parse_cred_mgmt_subcommandparams(CborValue * val, CTAP_credMgmt * CM)
|
||||
{
|
||||
size_t map_length;
|
||||
int key;
|
||||
int ret;
|
||||
unsigned int i;
|
||||
CborValue map;
|
||||
size_t sz = 32;
|
||||
|
||||
if (cbor_value_get_type(val) != CborMapType)
|
||||
{
|
||||
printf2(TAG_ERR,"error, wrong type\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
|
||||
|
||||
ret = cbor_value_enter_container(val,&map);
|
||||
check_ret(ret);
|
||||
|
||||
const uint8_t * start_byte = cbor_value_get_next_byte(&map) - 1;
|
||||
|
||||
ret = cbor_value_get_map_length(val, &map_length);
|
||||
check_ret(ret);
|
||||
|
||||
for (i = 0; i < map_length; i++)
|
||||
{
|
||||
if (cbor_value_get_type(&map) != CborIntegerType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting integer type for map key, got %s\n", cbor_value_get_type_string(&map));
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_int(&map, &key);
|
||||
check_ret(ret);
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
switch(key)
|
||||
{
|
||||
case CM_subCommandRpId:
|
||||
ret = cbor_value_copy_byte_string(&map, CM->subCommandParams.rpIdHash, &sz, NULL);
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, map key is too large\n");
|
||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||
}
|
||||
check_ret(ret);
|
||||
break;
|
||||
case CM_subCommandCred:
|
||||
ret = parse_credential_descriptor(&map, &CM->subCommandParams.credentialDescriptor);
|
||||
check_ret(ret);;
|
||||
break;
|
||||
}
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
const uint8_t * end_byte = cbor_value_get_next_byte(&map);
|
||||
|
||||
uint32_t length = (uint32_t)end_byte - (uint32_t)start_byte;
|
||||
if (length > sizeof(CM->hashed.subCommandParamsCborCopy))
|
||||
{
|
||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||
}
|
||||
// Copy the details that were hashed so they can be verified later.
|
||||
memmove(CM->hashed.subCommandParamsCborCopy, start_byte, length);
|
||||
CM->subCommandParamsCborSize = length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length)
|
||||
{
|
||||
int ret;
|
||||
unsigned int i;
|
||||
int key;
|
||||
size_t map_length;
|
||||
CborParser parser;
|
||||
CborValue it,map;
|
||||
|
||||
memset(CM, 0, sizeof(CTAP_credMgmt));
|
||||
ret = cbor_parser_init(request, length, CborValidateCanonicalFormat, &parser, &it);
|
||||
check_ret(ret);
|
||||
|
||||
CborType type = cbor_value_get_type(&it);
|
||||
if (type != CborMapType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting cbor map\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
|
||||
|
||||
ret = cbor_value_enter_container(&it,&map);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_value_get_map_length(&it, &map_length);
|
||||
check_ret(ret);
|
||||
|
||||
printf1(TAG_PARSE, "CM map has %d elements\n", map_length);
|
||||
|
||||
for (i = 0; i < map_length; i++)
|
||||
{
|
||||
type = cbor_value_get_type(&map);
|
||||
if (type != CborIntegerType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting int for map key\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_int_checked(&map, &key);
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case CM_cmd:
|
||||
printf1(TAG_PARSE, "CM_cmd\n");
|
||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||
{
|
||||
ret = cbor_value_get_int_checked(&map, &CM->cmd);
|
||||
check_ret(ret);
|
||||
CM->hashed.cmd = CM->cmd;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
break;
|
||||
case CM_subCommandParams:
|
||||
printf1(TAG_PARSE, "CM_subCommandParams\n");
|
||||
ret = parse_cred_mgmt_subcommandparams(&map, CM);
|
||||
check_ret(ret);
|
||||
break;
|
||||
case CM_pinProtocol:
|
||||
printf1(TAG_PARSE, "CM_pinProtocol\n");
|
||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||
{
|
||||
ret = cbor_value_get_int_checked(&map, &CM->pinProtocol);
|
||||
check_ret(ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
break;
|
||||
case CM_pinAuth:
|
||||
printf1(TAG_PARSE, "CM_pinAuth\n");
|
||||
ret = parse_fixed_byte_string(&map, CM->pinAuth, 16);
|
||||
check_retr(ret);
|
||||
CM->pinAuthPresent = 1;
|
||||
break;
|
||||
}
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length)
|
||||
{
|
||||
@ -1132,7 +1297,7 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
||||
return ret;
|
||||
}
|
||||
|
||||
cbor_value_advance(&map);
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
@ -1353,11 +1518,21 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
|
||||
break;
|
||||
case CP_getKeyAgreement:
|
||||
printf1(TAG_CP,"CP_getKeyAgreement\n");
|
||||
if (cbor_value_get_type(&map) != CborBooleanType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_boolean(&map, &CP->getKeyAgreement);
|
||||
check_ret(ret);
|
||||
break;
|
||||
case CP_getRetries:
|
||||
printf1(TAG_CP,"CP_getRetries\n");
|
||||
if (cbor_value_get_type(&map) != CborBooleanType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting cbor boolean\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = cbor_value_get_boolean(&map, &CP->getRetries);
|
||||
check_ret(ret);
|
||||
break;
|
||||
|
@ -35,6 +35,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
|
||||
|
||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
||||
uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int length);
|
||||
uint8_t ctap_parse_cred_mgmt(CTAP_credMgmt * CM, uint8_t * request, int length);
|
||||
uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length);
|
||||
uint8_t parse_credential_descriptor(CborValue * arr, CTAP_credentialDescriptor * cred);
|
||||
|
||||
|
@ -275,7 +275,7 @@ static void ctaphid_write(CTAPHID_WRITE_BUFFER * wb, void * _data, int len)
|
||||
if (wb->offset > 0)
|
||||
{
|
||||
memset(wb->buf + wb->offset, 0, HID_MESSAGE_SIZE - wb->offset);
|
||||
ctaphid_write_block(wb->buf);
|
||||
usbhid_send(wb->buf);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -304,7 +304,7 @@ static void ctaphid_write(CTAPHID_WRITE_BUFFER * wb, void * _data, int len)
|
||||
wb->bytes_written += 1;
|
||||
if (wb->offset == HID_MESSAGE_SIZE)
|
||||
{
|
||||
ctaphid_write_block(wb->buf);
|
||||
usbhid_send(wb->buf);
|
||||
wb->offset = 0;
|
||||
}
|
||||
}
|
||||
@ -542,6 +542,9 @@ extern void _check_ret(CborError ret, int line, const char * filename);
|
||||
|
||||
uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE_BUFFER * wb);
|
||||
|
||||
|
||||
extern void solo_lock_if_not_already();
|
||||
|
||||
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
{
|
||||
uint8_t cmd = 0;
|
||||
@ -635,6 +638,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
status = ctap_request(ctap_buffer, len, &ctap_resp);
|
||||
|
||||
wb.bcnt = (ctap_resp.length+1);
|
||||
wb.cid = cid;
|
||||
wb.cmd = cmd;
|
||||
|
||||
|
||||
|
||||
timestamp();
|
||||
@ -665,6 +671,9 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
u2f_request((struct u2f_request_apdu*)ctap_buffer, &ctap_resp);
|
||||
|
||||
wb.bcnt = (ctap_resp.length);
|
||||
wb.cid = cid;
|
||||
wb.cmd = cmd;
|
||||
|
||||
|
||||
ctaphid_write(&wb, ctap_resp.data, ctap_resp.length);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
@ -725,6 +734,11 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
#endif
|
||||
#if defined(SOLO)
|
||||
case CTAPHID_REBOOT:
|
||||
device_reboot();
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
case CTAPHID_GETRNG:
|
||||
@ -756,34 +770,51 @@ uint8_t ctaphid_custom_command(int len, CTAP_RESPONSE * ctap_resp, CTAPHID_WRITE
|
||||
return 1;
|
||||
break;
|
||||
|
||||
// Remove on next release
|
||||
#if !defined(IS_BOOTLOADER) && defined(SOLO)
|
||||
case 0x99:
|
||||
solo_lock_if_not_already();
|
||||
wb->bcnt = 0;
|
||||
ctaphid_write(wb, NULL, 0);
|
||||
return 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if !defined(IS_BOOTLOADER) && (defined(SOLO_EXPERIMENTAL))
|
||||
case CTAPHID_LOADKEY:
|
||||
/**
|
||||
* Load external key. Useful for enabling backups.
|
||||
* bytes: 4 96
|
||||
* payload: | counter_increase (BE) | master_key |
|
||||
* bytes: 4 4 96
|
||||
* payload: version [maj rev patch RFU]| counter_replacement (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)
|
||||
if (len != 104)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, invalid length.\n");
|
||||
ctaphid_send_error(wb->cid, CTAP1_ERR_INVALID_LENGTH);
|
||||
return 1;
|
||||
}
|
||||
param = ctap_buffer[0] << 16;
|
||||
param |= ctap_buffer[1] << 8;
|
||||
param |= ctap_buffer[2] << 0;
|
||||
if (param != 0){
|
||||
ctaphid_send_error(wb->cid, CTAP2_ERR_UNSUPPORTED_OPTION);
|
||||
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)
|
||||
if (ctap_user_presence_test(2000) > 0)
|
||||
if (ctap_user_presence_test(2000) > 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_load_external_keys(ctap_buffer + 8);
|
||||
param = ctap_buffer[7];
|
||||
param |= ctap_buffer[6] << 8;
|
||||
param |= ctap_buffer[5] << 16;
|
||||
param |= ctap_buffer[4] << 24;
|
||||
ctap_atomic_count(param);
|
||||
|
||||
wb->bcnt = 0;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#define CTAPHID_BOOT (TYPE_INIT | 0x50)
|
||||
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
|
||||
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
|
||||
#define CTAPHID_REBOOT (TYPE_INIT | 0x53)
|
||||
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
|
||||
#define CTAPHID_GETVERSION (TYPE_INIT | 0x61)
|
||||
#define CTAPHID_LOADKEY (TYPE_INIT | 0x62)
|
||||
@ -59,6 +60,8 @@
|
||||
|
||||
#define CTAP_CAPABILITIES (CAPABILITY_WINK | CAPABILITY_CBOR)
|
||||
|
||||
#define HID_MESSAGE_SIZE 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cid;
|
||||
|
@ -56,8 +56,7 @@ bool migrate_from_FF_to_01(AuthenticatorState_0xFF* state_prev_0xff, Authenticat
|
||||
|
||||
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);
|
||||
authenticator_write_state(state_tmp_ptr);
|
||||
}
|
||||
|
||||
void do_migration_if_required(AuthenticatorState* state_current){
|
||||
|
225
fido2/device.c
Normal file
@ -0,0 +1,225 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
/** device.c
|
||||
*
|
||||
* This contains (weak) implementations
|
||||
* to get FIDO2 working initially on a device. They probably
|
||||
* aren't what you want to keep, but are designed to be replaced
|
||||
* with some other platform specific implementation.
|
||||
*
|
||||
* For real examples, see the STM32L4 implementation and the PC implementation of device.c.
|
||||
*
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "ctaphid.h"
|
||||
#include "log.h"
|
||||
#include APP_CONFIG
|
||||
|
||||
#define RK_NUM 50
|
||||
|
||||
struct ResidentKeyStore {
|
||||
CTAP_residentKey rks[RK_NUM];
|
||||
} RK_STORE;
|
||||
|
||||
|
||||
static bool _up_disabled = false;
|
||||
|
||||
static uint8_t _attestation_cert_der[] =
|
||||
"\x30\x82\x01\xfb\x30\x82\x01\xa1\xa0\x03\x02\x01\x02\x02\x01\x00\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e"
|
||||
"\x06\x03\x55\x04\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x30\x20\x17\x0d\x31\x38"
|
||||
"\x30\x35\x31\x30\x30\x33\x30\x36\x32\x30\x5a\x18\x0f\x32\x30\x36\x38\x30\x34\x32"
|
||||
"\x37\x30\x33\x30\x36\x32\x30\x5a\x30\x7c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13"
|
||||
"\x02\x55\x53\x31\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x0f\x30\x0d"
|
||||
"\x06\x03\x55\x04\x07\x0c\x06\x4c\x61\x75\x72\x65\x6c\x31\x15\x30\x13\x06\x03\x55"
|
||||
"\x04\x0a\x0c\x0c\x54\x45\x53\x54\x20\x43\x4f\x4d\x50\x41\x4e\x59\x31\x22\x30\x20"
|
||||
"\x06\x03\x55\x04\x0b\x0c\x19\x41\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72"
|
||||
"\x20\x41\x74\x74\x65\x73\x74\x61\x74\x69\x6f\x6e\x31\x14\x30\x12\x06\x03\x55\x04"
|
||||
"\x03\x0c\x0b\x63\x6f\x6e\x6f\x72\x70\x70\x2e\x63\x6f\x6d\x30\x59\x30\x13\x06\x07"
|
||||
"\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42\x00"
|
||||
"\x04\x45\xa9\x02\xc1\x2e\x9c\x0a\x33\xfa\x3e\x84\x50\x4a\xb8\x02\xdc\x4d\xb9\xaf"
|
||||
"\x15\xb1\xb6\x3a\xea\x8d\x3f\x03\x03\x55\x65\x7d\x70\x3f\xb4\x02\xa4\x97\xf4\x83"
|
||||
"\xb8\xa6\xf9\x3c\xd0\x18\xad\x92\x0c\xb7\x8a\x5a\x3e\x14\x48\x92\xef\x08\xf8\xca"
|
||||
"\xea\xfb\x32\xab\x20\xa3\x62\x30\x60\x30\x46\x06\x03\x55\x1d\x23\x04\x3f\x30\x3d"
|
||||
"\xa1\x30\xa4\x2e\x30\x2c\x31\x0b\x30\x09\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31"
|
||||
"\x0b\x30\x09\x06\x03\x55\x04\x08\x0c\x02\x4d\x44\x31\x10\x30\x0e\x06\x03\x55\x04"
|
||||
"\x0a\x0c\x07\x54\x45\x53\x54\x20\x43\x41\x82\x09\x00\xf7\xc9\xec\x89\xf2\x63\x94"
|
||||
"\xd9\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04"
|
||||
"\x04\x03\x02\x04\xf0\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x48\x00"
|
||||
"\x30\x45\x02\x20\x18\x38\xb0\x45\x03\x69\xaa\xa7\xb7\x38\x62\x01\xaf\x24\x97\x5e"
|
||||
"\x7e\x74\x64\x1b\xa3\x7b\xf7\xe6\xd3\xaf\x79\x28\xdb\xdc\xa5\x88\x02\x21\x00\xcd"
|
||||
"\x06\xf1\xe3\xab\x16\x21\x8e\xd8\xc0\x14\xaf\x09\x4f\x5b\x73\xef\x5e\x9e\x4b\xe7"
|
||||
"\x35\xeb\xdd\x9b\x6d\x8f\x7d\xf3\xc4\x3a\xd7";
|
||||
|
||||
|
||||
__attribute__((weak)) void device_attestation_read_cert_der(uint8_t * dst){
|
||||
memmove(dst, _attestation_cert_der, device_attestation_cert_der_get_size());
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint8_t * device_get_attestation_key(){
|
||||
static uint8_t attestation_key[] =
|
||||
"\xcd\x67\xaa\x31\x0d\x09\x1e\xd1\x6e\x7e\x98\x92\xaa"
|
||||
"\x07\x0e\x19\x94\xfc\xd7\x14\xae\x7c\x40\x8f\xb9\x46"
|
||||
"\xb7\x2e\x5f\xe7\x5d\x30";
|
||||
return attestation_key;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint16_t device_attestation_cert_der_get_size(){
|
||||
return sizeof(_attestation_cert_der)-1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_reboot()
|
||||
{
|
||||
printf1(TAG_RED, "REBOOT command recieved!\r\n");
|
||||
exit(100);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_set_status(uint32_t status)
|
||||
{
|
||||
static uint32_t __device_status = 0;
|
||||
if (status != CTAPHID_STATUS_IDLE && __device_status != status)
|
||||
{
|
||||
ctaphid_update_status(status);
|
||||
}
|
||||
__device_status = status;
|
||||
}
|
||||
|
||||
|
||||
__attribute__((weak)) void usbhid_close(){/**/}
|
||||
|
||||
|
||||
__attribute__((weak)) void device_init(int argc, char *argv[]){/**/}
|
||||
|
||||
__attribute__((weak)) void device_disable_up(bool disable)
|
||||
{
|
||||
_up_disabled = disable;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int ctap_user_presence_test(uint32_t d)
|
||||
{
|
||||
if (_up_disabled)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) int ctap_user_verification(uint8_t arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t ctap_atomic_count(uint32_t amount)
|
||||
{
|
||||
static uint32_t counter1 = 25;
|
||||
counter1 += (amount + 1);
|
||||
return counter1;
|
||||
}
|
||||
|
||||
|
||||
__attribute__((weak)) int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
{
|
||||
int i;
|
||||
printf1(TAG_ERR, "Insecure RNG being used.\r\n");
|
||||
for (i = 0; i < num; i++){
|
||||
dst[i] = (uint8_t)rand();
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) int device_is_nfc()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_wink()
|
||||
{
|
||||
printf1(TAG_GREEN,"*WINK*\n");
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_set_clock_rate(DEVICE_CLOCK_RATE param){/**/}
|
||||
|
||||
static AuthenticatorState _tmp_state = {0};
|
||||
__attribute__((weak)) int authenticator_read_state(AuthenticatorState * s){
|
||||
if (_tmp_state.is_initialized != INITIALIZED_MARKER){
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
memmove(s, &_tmp_state, sizeof(AuthenticatorState));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void authenticator_write_state(AuthenticatorState * s){
|
||||
memmove(&_tmp_state, s, sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_reset_rk()
|
||||
{
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
}
|
||||
|
||||
__attribute__((weak)) uint32_t ctap_rk_size()
|
||||
{
|
||||
return RK_NUM;
|
||||
}
|
||||
|
||||
|
||||
__attribute__((weak)) void ctap_store_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_delete_rk(int index)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
memset(&rk, 0xff, sizeof(CTAP_residentKey));
|
||||
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for delete_rk\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_load_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
|
||||
}
|
||||
|
||||
__attribute__((weak)) void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__((weak)) void device_read_aaguid(uint8_t * dst){
|
||||
uint8_t * aaguid = (uint8_t *)"\x00\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
|
||||
memmove(dst, aaguid, 16);
|
||||
}
|
||||
|
219
fido2/device.h
@ -9,82 +9,166 @@
|
||||
|
||||
#include "storage.h"
|
||||
|
||||
void device_init(int argc, char *argv[]);
|
||||
|
||||
/** Return a millisecond timestamp. Does not need to be synchronized to anything.
|
||||
* *Optional* to compile, but will not calculate delays correctly without a correct implementation.
|
||||
*/
|
||||
uint32_t millis();
|
||||
|
||||
void delay(uint32_t ms);
|
||||
|
||||
// HID message size in bytes
|
||||
#define HID_MESSAGE_SIZE 64
|
||||
|
||||
void usbhid_init();
|
||||
|
||||
int usbhid_recv(uint8_t * msg);
|
||||
|
||||
/** Called by HIDUSB layer to write bytes to the USB HID interface endpoint.
|
||||
* Will write 64 bytes at a time.
|
||||
*
|
||||
* @param msg Pointer to a 64 byte buffer containing a payload to be sent via USB HID.
|
||||
*
|
||||
* **Required** to compile and work for FIDO application.
|
||||
*/
|
||||
void usbhid_send(uint8_t * msg);
|
||||
|
||||
void usbhid_close();
|
||||
|
||||
void main_loop_delay();
|
||||
|
||||
void heartbeat();
|
||||
|
||||
/** Reboot / power reset the device.
|
||||
* **Optional** this is not used for FIDO2, and simply won't do anything if not implemented.
|
||||
*/
|
||||
void device_reboot();
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * );
|
||||
/** Read AuthenticatorState from nonvolatile memory.
|
||||
* @param s pointer to AuthenticatorState buffer to be overwritten with contents from NV memory.
|
||||
* @return 0 - state stored is NOT initialized.
|
||||
* 1 - state stored is initialized.
|
||||
*
|
||||
* *Optional* this is required to make persistant updates to FIDO2 State (PIN and device master secret).
|
||||
* Without it, changes simply won't be persistant.
|
||||
*/
|
||||
int authenticator_read_state(AuthenticatorState * s);
|
||||
|
||||
void authenticator_read_backup_state(AuthenticatorState * );
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
//void authenticator_initialize()
|
||||
int authenticator_is_backup_initialized();
|
||||
|
||||
void authenticator_write_state(AuthenticatorState *, int backup);
|
||||
|
||||
// Called each main loop. Doesn't need to do anything.
|
||||
void device_manage();
|
||||
/** Store changes in the authenticator state to nonvolatile memory.
|
||||
* @param s pointer to valid Authenticator state to write to NV memory.
|
||||
*
|
||||
* *Optional* this is required to make persistant updates to FIDO2 State (PIN and device master secret).
|
||||
* Without it, changes simply won't be persistant.
|
||||
*/
|
||||
void authenticator_write_state(AuthenticatorState * s);
|
||||
|
||||
// sets status that's uses for sending status updates ~100ms.
|
||||
// A timer should be set up to call `ctaphid_update_status`
|
||||
|
||||
/** Updates status of the status of the FIDO2 layer application, which
|
||||
* can be used for polling updates in the USBHID layer.
|
||||
*
|
||||
* @param status is one of the following, which can be used appropriately by USB HID layer.
|
||||
#define CTAPHID_STATUS_IDLE 0
|
||||
#define CTAPHID_STATUS_PROCESSING 1
|
||||
#define CTAPHID_STATUS_UPNEEDED 2
|
||||
*
|
||||
* *Optional* to compile and run, but will be required to be used for proper FIDO2 operation with some platforms.
|
||||
*/
|
||||
void device_set_status(uint32_t status);
|
||||
|
||||
// Returns if button is currently pressed
|
||||
/** Returns true if button is currently pressed. Debouncing does not need to be handled. Should not block.
|
||||
* @return 1 if button is currently pressed.
|
||||
*
|
||||
* *Optional* to compile and run, but just returns one by default.
|
||||
*/
|
||||
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.
|
||||
/** Test for user presence.
|
||||
* Perform test that user is present. Returns status on user presence. This is used by FIDO and U2F layer
|
||||
* to check if an operation should continue, or if the UP flag should be set.
|
||||
*
|
||||
* @param delay number of milliseconds to delay waiting for user before timeout.
|
||||
*
|
||||
* @return 2 - User presence is disabled. Operation should continue, but UP flag not set.
|
||||
* 1 - User presence confirmed. Operation should continue, and UP flag is set.
|
||||
* 0 - User presence is not confirmed. Operation should be denied.
|
||||
* -1 - Operation was canceled. Do not continue, reset transaction state.
|
||||
*
|
||||
* *Optional*, the default implementation will return 1, unless a FIDO2 operation calls for no UP, where this will then return 2.
|
||||
*/
|
||||
int ctap_user_presence_test(uint32_t delay);
|
||||
|
||||
// Generate @num bytes of random numbers to @dest
|
||||
// return 1 if success, error otherwise
|
||||
/** Disable the next user presence test. This is called by FIDO2 layer when a transaction
|
||||
* requests UP to be disabled. The next call to ctap_user_presence_test should return 2,
|
||||
* and then UP should be enabled again.
|
||||
*
|
||||
* @param request_active indicate to activate (true) or disable (false) UP.
|
||||
*
|
||||
* *Optional*, the default implementation will provide expected behaviour with the default ctap_user_presence_test(...).
|
||||
*/
|
||||
void device_disable_up(bool request_active);
|
||||
|
||||
/** Generate random numbers. Random numbers should be good enough quality for
|
||||
* cryptographic use.
|
||||
*
|
||||
* @param dst the buffer to write into.
|
||||
* @param num the number of bytes to generate and write to dst.
|
||||
*
|
||||
* @return 1 if successful, or else the RNG failed.
|
||||
*
|
||||
* *Optional*, if not implemented, the random numbers will be from rand() and an error will be logged.
|
||||
*/
|
||||
int ctap_generate_rng(uint8_t * dst, size_t num);
|
||||
|
||||
// Increment atomic counter and return it.
|
||||
// @param amount the amount to increase the counter by.
|
||||
/** Increment an atomic (non-volatile) counter and return the value.
|
||||
*
|
||||
* @param amount a non-zero amount to increment the counter by.
|
||||
*
|
||||
* *Optional*, if not implemented, the counter will not be persistant.
|
||||
*/
|
||||
uint32_t ctap_atomic_count(uint32_t amount);
|
||||
|
||||
// Verify the user
|
||||
// return 1 if user is verified, 0 if not
|
||||
int ctap_user_verification(uint8_t arg);
|
||||
|
||||
// Must be implemented by application
|
||||
// data is HID_MESSAGE_SIZE long in bytes
|
||||
void ctaphid_write_block(uint8_t * data);
|
||||
|
||||
|
||||
// Resident key
|
||||
/** Delete all resident keys.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_reset_rk();
|
||||
|
||||
/** Return the maximum amount of resident keys that can be stored.
|
||||
* @return max number of resident keys that can be stored, including already stored RK's.
|
||||
*
|
||||
* *Optional*, if not implemented, returns 50.
|
||||
*/
|
||||
uint32_t ctap_rk_size();
|
||||
|
||||
/** Store a resident key into an index between [ 0, ctap_rk_size() ).
|
||||
* Storage should be in non-volatile memory.
|
||||
*
|
||||
* @param index between RK index range.
|
||||
* @param rk pointer to valid rk structure that should be written to NV memory.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
/** Delete a resident key from an index.
|
||||
* @param index to delete resident key from. Has no effect if no RK exists at index.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_delete_rk(int index);
|
||||
|
||||
/** Read a resident key from an index into memory
|
||||
* @param index to read resident key from.
|
||||
* @param rk pointer to resident key structure to write into with RK.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
/** Overwrite the RK located in index with a new RK.
|
||||
* @param index to write resident key to.
|
||||
* @param rk pointer to valid rk structure that should be written to NV memory, and replace existing RK there.
|
||||
*
|
||||
* *Optional*, if not implemented, operates on non-persistant RK's.
|
||||
*/
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk);
|
||||
|
||||
// For Solo hacker
|
||||
void boot_solo_bootloader();
|
||||
void boot_st_bootloader();
|
||||
|
||||
// HID wink command
|
||||
/** Called by HID layer to indicate that a wink behavior should be performed.
|
||||
* Should not block, and the wink behavior should occur in parallel to FIDO operations.
|
||||
*
|
||||
* *Optional*.
|
||||
*/
|
||||
void device_wink();
|
||||
|
||||
typedef enum {
|
||||
@ -93,21 +177,50 @@ typedef enum {
|
||||
DEVICE_FAST = 2,
|
||||
} DEVICE_CLOCK_RATE;
|
||||
|
||||
// Set the clock rate for the device.
|
||||
// Three modes are targetted for Solo.
|
||||
// 0: Lowest clock rate for NFC.
|
||||
// 1: fastest clock rate supported at a low power setting for NFC FIDO.
|
||||
// 2: fastest clock rate. Generally for USB interface.
|
||||
/**
|
||||
* Set the clock rate for the device. This gets called only when the device is running in NFC mode.
|
||||
* Before Register and authenticate operations, the clock rate will be set to (1), and otherwise back to (0).
|
||||
* @param param
|
||||
0: Lowest clock rate for NFC.
|
||||
1: fastest clock rate supported at a low power setting for NFC FIDO.
|
||||
2: fastest clock rate. Generally for USB interface.
|
||||
* *Optional*, by default nothing happens.
|
||||
*/
|
||||
void device_set_clock_rate(DEVICE_CLOCK_RATE param);
|
||||
|
||||
// Returns NFC_IS_NA, NFC_IS_ACTIVE, or NFC_IS_AVAILABLE
|
||||
#define NFC_IS_NA 0
|
||||
#define NFC_IS_ACTIVE 1
|
||||
#define NFC_IS_AVAILABLE 2
|
||||
|
||||
/** Returns NFC status of the device.
|
||||
* @return 0 - NFC is not available.
|
||||
* 1 - NFC is active, and is powering the chip for a transaction.
|
||||
* 2 - NFC is available, but not currently being used.
|
||||
*/
|
||||
int device_is_nfc();
|
||||
|
||||
void device_disable_up(bool request_active);
|
||||
|
||||
void device_init_button();
|
||||
/** Return pointer to attestation key.
|
||||
* @return pointer to attestation private key, raw encoded. For P256, this is 32 bytes.
|
||||
*/
|
||||
uint8_t * device_get_attestation_key();
|
||||
|
||||
/** Read the device's attestation certificate into buffer @dst.
|
||||
* @param dst the destination to write the certificate.
|
||||
*
|
||||
* The size of the certificate can be retrieved using `device_attestation_cert_der_get_size()`.
|
||||
*/
|
||||
void device_attestation_read_cert_der(uint8_t * dst);
|
||||
|
||||
/** Returns the size in bytes of attestation_cert_der.
|
||||
* @return number of bytes in attestation_cert_der, not including any C string null byte.
|
||||
*/
|
||||
uint16_t device_attestation_cert_der_get_size();
|
||||
|
||||
/** Read the device's 16 byte AAGUID into a buffer.
|
||||
* @param dst buffer to write 16 byte AAGUID into.
|
||||
* */
|
||||
void device_read_aaguid(uint8_t * dst);
|
||||
|
||||
#endif
|
||||
|
41
fido2/example_app.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2019 SoloKeys Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
#ifndef SRC_APP_H_
|
||||
#define SRC_APP_H_
|
||||
#include <stdbool.h>
|
||||
|
||||
#define USING_DEV_BOARD
|
||||
|
||||
#define USING_PC
|
||||
|
||||
#define ENABLE_U2F
|
||||
#define ENABLE_U2F_EXTENSIONS
|
||||
//#define BRIDGE_TO_WALLET
|
||||
|
||||
void printing_init();
|
||||
|
||||
extern bool use_udp;
|
||||
|
||||
// 0xRRGGBB
|
||||
#define LED_INIT_VALUE 0x000800
|
||||
#define LED_WINK_VALUE 0x000008
|
||||
#define LED_MAX_SCALER 30
|
||||
#define LED_MIN_SCALER 1
|
||||
// # of ms between each change in LED
|
||||
#define HEARTBEAT_PERIOD 100
|
||||
// Each LED channel will be multiplied by a integer between LED_MAX_SCALER
|
||||
// and LED_MIN_SCALER to cause the slow pulse. E.g.
|
||||
// #define LED_INIT_VALUE 0x301000
|
||||
// #define LED_MAX_SCALER 30
|
||||
// #define LED_MIN_SCALER 1
|
||||
// #define HEARTBEAT_PERIOD 8
|
||||
// Will pulse from 0x301000 to 0x903000 to 0x301000 ...
|
||||
// Which will take ~8 * (30)*2 ms
|
||||
|
||||
|
||||
#endif /* SRC_APP_H_ */
|
@ -51,6 +51,7 @@ struct logtag tagtable[] = {
|
||||
{TAG_NFC,"[1;38mNFC[0m"},
|
||||
{TAG_NFC_APDU, "NAPDU"},
|
||||
{TAG_CCID, "CCID"},
|
||||
{TAG_CM, "CRED_MGMT"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -7,7 +7,10 @@
|
||||
#ifndef _LOG_H
|
||||
#define _LOG_H
|
||||
|
||||
#ifdef APP_CONFIG
|
||||
#include APP_CONFIG
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef DEBUG_LEVEL
|
||||
@ -45,12 +48,13 @@ typedef enum
|
||||
TAG_NFC = (1 << 19),
|
||||
TAG_NFC_APDU = (1 << 20),
|
||||
TAG_CCID = (1 << 21),
|
||||
TAG_CM = (1 << 22),
|
||||
|
||||
TAG_NO_TAG = (1UL << 30),
|
||||
TAG_FILENO = (1UL << 31)
|
||||
} LOG_TAG;
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
#if defined(DEBUG_LEVEL) && DEBUG_LEVEL > 0
|
||||
|
||||
void set_logging_mask(uint32_t mask);
|
||||
#define printf1(tag,fmt, ...) LOG(tag & ~(TAG_FILENO), NULL, 0, fmt, ##__VA_ARGS__)
|
||||
|
14
fido2/u2f.c
@ -204,7 +204,7 @@ int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t key_handl
|
||||
printf1(TAG_U2F, "APPID does not match rpIdHash.\n");
|
||||
return 0;
|
||||
}
|
||||
make_auth_tag(appid, cred->nonce, cred->count, tag);
|
||||
make_auth_tag(appid, (uint8_t*)&cred->entropy, cred->count, tag);
|
||||
|
||||
if (memcmp(cred->tag, tag, CREDENTIAL_TAG_SIZE) == 0){
|
||||
return 1;
|
||||
@ -299,14 +299,19 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
static int16_t u2f_register(struct u2f_register_request * req)
|
||||
{
|
||||
uint8_t i[] = {0x0,U2F_EC_FMT_UNCOMPRESSED};
|
||||
|
||||
uint8_t cert[1024];
|
||||
struct u2f_key_handle key_handle;
|
||||
uint8_t pubkey[64];
|
||||
uint8_t hash[32];
|
||||
uint8_t * sig = (uint8_t*)req;
|
||||
|
||||
|
||||
const uint16_t attest_size = attestation_cert_der_get_size();
|
||||
const uint16_t attest_size = device_attestation_cert_der_get_size();
|
||||
|
||||
if (attest_size > sizeof(cert)){
|
||||
printf2(TAG_ERR,"Certificate is too large for buffer\r\n");
|
||||
return U2F_SW_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
|
||||
if ( ! ctap_user_presence_test(750))
|
||||
{
|
||||
@ -341,7 +346,8 @@ static int16_t u2f_register(struct u2f_register_request * req)
|
||||
u2f_response_writeback(i,1);
|
||||
u2f_response_writeback((uint8_t*)&key_handle,U2F_KEY_HANDLE_SIZE);
|
||||
|
||||
u2f_response_writeback(attestation_cert_der,attest_size);
|
||||
device_attestation_read_cert_der(cert);
|
||||
u2f_response_writeback(cert,attest_size);
|
||||
|
||||
dump_signature_der(sig);
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
#include "version.h"
|
||||
#include "app.h"
|
||||
|
||||
|
||||
const version_t firmware_version __attribute__ ((section (".flag"))) __attribute__ ((__used__)) = {
|
||||
const version_t firmware_version
|
||||
#ifdef SOLO
|
||||
__attribute__ ((section (".flag"))) __attribute__ ((__used__))
|
||||
#endif
|
||||
= {
|
||||
.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>
|
||||
|
9
fido2/version.mk
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
SOLO_VERSION_FULL?=$(shell git describe)
|
||||
SOLO_VERSION:=$(shell python -c 'print("$(SOLO_VERSION_FULL)".split("-")[0])')
|
||||
SOLO_VERSION_MAJ:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[0])')
|
||||
SOLO_VERSION_MIN:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[1])')
|
||||
SOLO_VERSION_PAT:=$(shell python -c 'print("$(SOLO_VERSION)".split(".")[2])')
|
||||
|
||||
SOLO_VERSION_FLAGS := -DSOLO_VERSION_MAJ=$(SOLO_VERSION_MAJ) -DSOLO_VERSION_MIN=$(SOLO_VERSION_MIN) \
|
||||
-DSOLO_VERSION_PATCH=$(SOLO_VERSION_PAT) -DSOLO_VERSION=\"$(SOLO_VERSION_FULL)\"
|
@ -15,8 +15,7 @@
|
||||
"authenticationAlgorithm": 1,
|
||||
"publicKeyAlgAndEncoding": 260,
|
||||
"attestationTypes": [
|
||||
15879,
|
||||
15880
|
||||
15879
|
||||
],
|
||||
"userVerificationDetails": [
|
||||
[
|
||||
|
41
metadata/SoloTap-FIDO2-CTAP2-Authenticator.json
Normal file
41
metadata/Somu-FIDO2-CTAP2-Authenticator.json
Normal file
41
mkdocs.yml
Normal file → Executable file
@ -1,33 +1,36 @@
|
||||
site_name: Solo Technical Documentation
|
||||
site_author: SoloKeys
|
||||
site_description: 'Documentation for the SoloKeys solo software'
|
||||
site_url: 'https://docs.solokeys.io/solo/'
|
||||
site_url: 'https://docs.solokeys.dev/'
|
||||
repo_url: 'https://github.com/solokeys/solo'
|
||||
repo_name: 'solokeys/solo'
|
||||
copyright: 'Copyright © 2018 - 2019 SoloKeys'
|
||||
|
||||
nav:
|
||||
- Home: solo/index.md
|
||||
- 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
|
||||
- Contributing Docs: solo/documenting.md
|
||||
- udev Rules: solo/udev.md
|
||||
- About: solo/repo-readme.md
|
||||
- Home: index.md
|
||||
- FIDO2 Implementation: fido2-impl.md
|
||||
- Metadata Statements: metadata-statements.md
|
||||
- Build instructions: building.md
|
||||
- Programming instructions: programming.md
|
||||
- Bootloader mode: bootloader-mode.md
|
||||
- Customization: customization.md
|
||||
- Solo Extras: solo-extras.md
|
||||
- Application Ideas: application-ideas.md
|
||||
- Running on Nucleo32 board: nucleo32-board.md
|
||||
- Signed update process: signed-updates.md
|
||||
- Usage and Porting guide: porting.md
|
||||
- Tutorial – Getting Started: tutorial-getting-started.md
|
||||
- Tutorial - Writing extensions: tutorial-writing-extensions.md
|
||||
- Code documentation: code-overview.md
|
||||
- Contributing Code: contributing.md
|
||||
- Contributing Docs: documenting.md
|
||||
- udev Rules: udev.md
|
||||
- About: repo-readme.md
|
||||
|
||||
theme:
|
||||
name: material
|
||||
logo: 'solo/images/logo.svg'
|
||||
favicon: 'solo/images/favicon.ico'
|
||||
logo: 'images/logo.svg'
|
||||
favicon: 'images/favicon.ico'
|
||||
|
||||
markdown_extensions:
|
||||
- markdown_include.include
|
||||
|
197
pc/device.c
@ -11,7 +11,6 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
@ -25,8 +24,7 @@
|
||||
|
||||
#define RK_NUM 50
|
||||
|
||||
bool use_udp = true;
|
||||
static bool _up_disabled = false;
|
||||
static bool use_udp = true;
|
||||
|
||||
struct ResidentKeyStore {
|
||||
CTAP_residentKey rks[RK_NUM];
|
||||
@ -34,21 +32,6 @@ struct ResidentKeyStore {
|
||||
|
||||
void authenticator_initialize();
|
||||
|
||||
uint32_t __device_status = 0;
|
||||
void device_set_status(uint32_t status)
|
||||
{
|
||||
if (status != CTAPHID_STATUS_IDLE && __device_status != status)
|
||||
{
|
||||
ctaphid_update_status(status);
|
||||
}
|
||||
__device_status = status;
|
||||
}
|
||||
|
||||
void device_reboot()
|
||||
{
|
||||
printf1(TAG_RED, "REBOOT command recieved!\r\n");
|
||||
exit(100);
|
||||
}
|
||||
|
||||
int udp_server()
|
||||
{
|
||||
@ -192,7 +175,6 @@ int usbhid_recv(uint8_t * msg)
|
||||
return l;
|
||||
}
|
||||
|
||||
// Send 64 byte USB HID message
|
||||
void usbhid_send(uint8_t * msg)
|
||||
{
|
||||
if (use_udp)
|
||||
@ -209,6 +191,8 @@ void usbhid_send(uint8_t * msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void usbhid_close()
|
||||
{
|
||||
close(fd);
|
||||
@ -272,14 +256,6 @@ void device_init(int argc, char *argv[])
|
||||
}
|
||||
|
||||
|
||||
void main_loop_delay()
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000*1000*100;
|
||||
nanosleep(&ts,NULL);
|
||||
}
|
||||
|
||||
void delay(uint32_t ms)
|
||||
{
|
||||
struct timespec ts;
|
||||
@ -289,40 +265,6 @@ void delay(uint32_t ms)
|
||||
}
|
||||
|
||||
|
||||
void heartbeat()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ctaphid_write_block(uint8_t * data)
|
||||
{
|
||||
/*printf("<< "); dump_hex(data, 64);*/
|
||||
usbhid_send(data);
|
||||
}
|
||||
|
||||
|
||||
int ctap_user_presence_test(uint32_t d)
|
||||
{
|
||||
if (_up_disabled)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ctap_user_verification(uint8_t arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
uint32_t ctap_atomic_count(uint32_t amount)
|
||||
{
|
||||
static uint32_t counter1 = 25;
|
||||
counter1 += (amount + 1);
|
||||
return counter1;
|
||||
}
|
||||
|
||||
int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
{
|
||||
int ret;
|
||||
@ -344,10 +286,9 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
|
||||
|
||||
const char * state_file = "authenticator_state.bin";
|
||||
const char * backup_file = "authenticator_state2.bin";
|
||||
const char * rk_file = "resident_keys.bin";
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * state)
|
||||
int authenticator_read_state(AuthenticatorState * state)
|
||||
{
|
||||
FILE * f;
|
||||
int ret;
|
||||
@ -366,37 +307,19 @@ void authenticator_read_state(AuthenticatorState * state)
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
if (state->is_initialized == INITIALIZED_MARKER)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void authenticator_read_backup_state(AuthenticatorState * state )
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * state)
|
||||
{
|
||||
FILE * f;
|
||||
int ret;
|
||||
|
||||
f = fopen(backup_file, "rb");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = fread(state, 1, sizeof(AuthenticatorState), f);
|
||||
fclose(f);
|
||||
if(ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * state, int backup)
|
||||
{
|
||||
FILE * f;
|
||||
int ret;
|
||||
|
||||
if (! backup)
|
||||
{
|
||||
f = fopen(state_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
@ -410,60 +333,9 @@ void authenticator_write_state(AuthenticatorState * state, int backup)
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
f = fopen(backup_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
ret = fwrite(state, 1, sizeof(AuthenticatorState), f);
|
||||
fclose(f);
|
||||
if (ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
int authenticator_is_backup_initialized()
|
||||
{
|
||||
uint8_t header[16];
|
||||
AuthenticatorState * state = (AuthenticatorState*) header;
|
||||
FILE * f;
|
||||
int ret;
|
||||
|
||||
printf("state file exists\n");
|
||||
f = fopen(backup_file, "rb");
|
||||
if (f== NULL)
|
||||
{
|
||||
printf("Warning, backup file doesn't exist\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fread(header, 1, sizeof(header), f);
|
||||
fclose(f);
|
||||
if(ret != sizeof(header))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return state->is_initialized == INITIALIZED_MARKER;
|
||||
|
||||
}
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
/*int authenticator_is_initialized()*/
|
||||
/*{*/
|
||||
|
||||
|
||||
/*}*/
|
||||
|
||||
static void sync_rk()
|
||||
{
|
||||
@ -543,44 +415,18 @@ void authenticator_initialize()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
f = fopen(backup_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
mem = malloc(sizeof(AuthenticatorState));
|
||||
memset(mem,0xff,sizeof(AuthenticatorState));
|
||||
ret = fwrite(mem, 1, sizeof(AuthenticatorState), f);
|
||||
free(mem);
|
||||
fclose(f);
|
||||
if (ret != sizeof(AuthenticatorState))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// resident_keys
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
sync_rk();
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void device_manage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ctap_reset_rk()
|
||||
{
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
sync_rk();
|
||||
|
||||
}
|
||||
|
||||
uint32_t ctap_rk_size()
|
||||
@ -603,6 +449,12 @@ void ctap_store_rk(int index, CTAP_residentKey * rk)
|
||||
|
||||
}
|
||||
|
||||
void ctap_delete_rk(int index)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
memset(&rk, 0xff, sizeof(CTAP_residentKey));
|
||||
memmove(RK_STORE.rks + index, &rk, sizeof(CTAP_residentKey));
|
||||
}
|
||||
|
||||
void ctap_load_rk(int index, CTAP_residentKey * rk)
|
||||
{
|
||||
@ -622,22 +474,9 @@ void ctap_overwrite_rk(int index, CTAP_residentKey * rk)
|
||||
}
|
||||
}
|
||||
|
||||
void device_wink()
|
||||
{
|
||||
printf("*WINK*\n");
|
||||
}
|
||||
|
||||
int device_is_nfc()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_disable_up(bool disable)
|
||||
{
|
||||
_up_disabled = disable;
|
||||
}
|
||||
|
||||
void device_set_clock_rate(DEVICE_CLOCK_RATE param)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "cbor.h"
|
||||
#include "device.h"
|
||||
@ -17,9 +18,12 @@
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "ctap.h"
|
||||
#include APP_CONFIG
|
||||
#include "app.h"
|
||||
|
||||
|
||||
void device_init(int argc, char *argv[]);
|
||||
int usbhid_recv(uint8_t * msg);
|
||||
|
||||
#if !defined(TEST)
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -29,20 +33,21 @@ int main(int argc, char *argv[])
|
||||
|
||||
set_logging_mask(
|
||||
/*0*/
|
||||
//TAG_GEN|
|
||||
// TAG_GEN|
|
||||
// TAG_MC |
|
||||
// TAG_GA |
|
||||
TAG_WALLET |
|
||||
TAG_STOR |
|
||||
//TAG_NFC_APDU |
|
||||
TAG_NFC |
|
||||
//TAG_CP |
|
||||
// TAG_CP |
|
||||
// TAG_CTAP|
|
||||
//TAG_HID|
|
||||
// TAG_HID|
|
||||
TAG_U2F|
|
||||
//TAG_PARSE |
|
||||
// TAG_PARSE |
|
||||
//TAG_TIME|
|
||||
// TAG_DUMP|
|
||||
// TAG_DUMP2|
|
||||
TAG_GREEN|
|
||||
TAG_RED|
|
||||
TAG_EXT|
|
||||
@ -57,13 +62,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
while(1)
|
||||
{
|
||||
if (millis() - t1 > HEARTBEAT_PERIOD)
|
||||
{
|
||||
heartbeat();
|
||||
t1 = millis();
|
||||
}
|
||||
|
||||
device_manage();
|
||||
|
||||
if (usbhid_recv(hidmsg) > 0)
|
||||
{
|
||||
@ -73,14 +71,16 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
ctaphid_check_timeouts();
|
||||
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000*1000*10;
|
||||
nanosleep(&ts,NULL);
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
usbhid_close();
|
||||
printf1(TAG_GREEN, "done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
13
shell.nix
Normal file
@ -0,0 +1,13 @@
|
||||
let
|
||||
nixpkgs_tar = fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/20.03.tar.gz"; sha256 = "0182ys095dfx02vl2a20j1hz92dx3mfgz2a6fhn31bqlp1wa8hlq"; };
|
||||
pkgs = import "${nixpkgs_tar}" {};
|
||||
pyPackages = (python-packages: with python-packages; ([
|
||||
solo-python pytest
|
||||
] ++ (with builtins; map (d: getAttr d python-packages) (filter (d: stringLength d > 0) (pkgs.lib.splitString "\n" (builtins.readFile ./tools/requirements.txt))))));
|
||||
python-with-my-packages = pkgs.python3.withPackages pyPackages;
|
||||
in
|
||||
with pkgs;
|
||||
stdenv.mkDerivation {
|
||||
name = "solo";
|
||||
buildInputs = [ gnumake gcc gcc-arm-embedded-8 python-with-my-packages ];
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
# STM32L432 Solo
|
||||
|
||||
Check out our [official documentation](https://docs.solokeys.io/solo/building/)
|
||||
Check out our [official documentation](https://docs.solokeys.dev/building/)
|
||||
for instructions on building and programming!
|
||||
|
@ -50,12 +50,15 @@ typedef struct {
|
||||
uint8_t payload[255 - 10];
|
||||
} __attribute__((packed)) BootloaderReq;
|
||||
|
||||
uint8_t * last_written_app_address = 0;
|
||||
|
||||
/**
|
||||
* Erase all application pages. **APPLICATION_END_PAGE excluded**.
|
||||
*/
|
||||
static void erase_application()
|
||||
{
|
||||
int page;
|
||||
last_written_app_address = (uint8_t*) 0;
|
||||
for(page = APPLICATION_START_PAGE; page < APPLICATION_END_PAGE; page++)
|
||||
{
|
||||
flash_erase_page(page);
|
||||
@ -106,17 +109,20 @@ 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()
|
||||
{
|
||||
|
||||
if (last_written_app_address == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
volatile version_t * new_version = ((volatile version_t *) (last_written_app_address-8+4));
|
||||
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
|
||||
@ -170,6 +176,7 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
uint32_t addr = ((*((uint32_t*)req->addr)) & 0xffffff) | 0x8000000;
|
||||
|
||||
uint32_t * ptr = (uint32_t *)addr;
|
||||
uint32_t current_address;
|
||||
|
||||
switch(req->op){
|
||||
case BootWrite:
|
||||
@ -196,9 +203,16 @@ int bootloader_bridge(int klen, uint8_t * keyh)
|
||||
printf2(TAG_ERR, "Error, boot check bypassed\n");
|
||||
exit(1);
|
||||
}
|
||||
current_address = addr + len;
|
||||
if (current_address < (uint32_t) last_written_app_address) {
|
||||
printf2(TAG_ERR, "Error, only ascending writes allowed.\n");
|
||||
has_erased = 0;
|
||||
return CTAP2_ERR_NOT_ALLOWED;
|
||||
}
|
||||
last_written_app_address = (uint8_t*) current_address;
|
||||
|
||||
// Do the actual write
|
||||
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.
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define _APP_H_
|
||||
#include <stdint.h>
|
||||
#include "version.h"
|
||||
#include "solo.h"
|
||||
#define DEBUG_UART USART1
|
||||
|
||||
#ifndef DEBUG_LEVEL
|
||||
|
@ -2,14 +2,14 @@ include build/common.mk
|
||||
|
||||
# ST related
|
||||
SRC = src/main.c src/init.c src/redirect.c src/flash.c src/rng.c src/led.c src/device.c
|
||||
SRC += src/fifo.c src/crypto.c src/attestation.c src/nfc.c src/ams.c src/sense.c
|
||||
SRC += src/fifo.c src/attestation.c src/nfc.c src/ams.c src/sense.c
|
||||
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/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
|
||||
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
|
||||
SRC += ../../fido2/ctap_parse.c ../../fido2/crypto.c
|
||||
SRC += ../../fido2/version.c
|
||||
SRC += ../../fido2/data_migration.c
|
||||
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
|
||||
@ -22,7 +22,9 @@ SRC += ../../crypto/cifra/src/sha512.c ../../crypto/cifra/src/blockwise.c
|
||||
OBJ1=$(SRC:.c=.o)
|
||||
OBJ=$(OBJ1:.s=.o)
|
||||
|
||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
|
||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/
|
||||
|
||||
INC+= -I../../fido2/ -I../../fido2/extensions
|
||||
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
|
||||
INC += -I../../crypto/tiny-AES-c
|
||||
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
|
||||
@ -66,9 +68,6 @@ all: $(TARGET).elf
|
||||
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
|
||||
$(CC) $^ $(HW) -O3 $(ECC_CFLAGS) -o $@
|
||||
|
||||
%.o: %.s
|
||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
||||
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $^ $(HW) $(LDFLAGS) -o $@
|
||||
@echo "Built version: $(VERSION_FLAGS)"
|
||||
@ -85,4 +84,5 @@ cbor:
|
||||
cd ../../tinycbor/ && make clean
|
||||
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
|
||||
LDFLAGS="$(LDFLAGS_LIB)" \
|
||||
CFLAGS="$(CFLAGS) -Os"
|
||||
CFLAGS="$(CFLAGS) -Os -DCBOR_PARSER_MAX_RECURSIONS=3"
|
||||
|
||||
|
@ -4,13 +4,14 @@ include build/common.mk
|
||||
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/fifo.c src/attestation.c src/sense.c
|
||||
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
|
||||
SRC += $(DRIVER_LIBS) $(USB_LIB)
|
||||
|
||||
# FIDO2 lib
|
||||
SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/extensions/extensions.c
|
||||
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
|
||||
SRC += ../../fido2/crypto.c
|
||||
|
||||
# Crypto libs
|
||||
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c
|
||||
|
@ -1,7 +1,10 @@
|
||||
include ../../fido2/version.mk
|
||||
|
||||
CC=$(PREFIX)arm-none-eabi-gcc
|
||||
CP=$(PREFIX)arm-none-eabi-objcopy
|
||||
SZ=$(PREFIX)arm-none-eabi-size
|
||||
AR=$(PREFIX)arm-none-eabi-ar
|
||||
AS=$(PREFIX)arm-none-eabi-as
|
||||
|
||||
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
|
||||
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
|
||||
@ -13,17 +16,20 @@ USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c
|
||||
lib/usbd/usbd_ctlreq.c lib/usbd/usbd_desc.c lib/usbd/usbd_hid.c \
|
||||
lib/usbd/usbd_ccid.c
|
||||
|
||||
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])')
|
||||
VERSION_FULL?=$(SOLO_VERSION_FULL)
|
||||
VERSION:=$(SOLO_VERSION)
|
||||
VERSION_MAJ:=$(SOLO_VERSION_MAJ)
|
||||
VERSION_MIN:=$(SOLO_VERSION_MIN)
|
||||
VERSION_PAT:=$(SOLO_VERSION_PAT)
|
||||
|
||||
VERSION_FLAGS= -DSOLO_VERSION_MAJ=$(VERSION_MAJ) -DSOLO_VERSION_MIN=$(VERSION_MIN) \
|
||||
-DSOLO_VERSION_PATCH=$(VERSION_PAT) -DSOLO_VERSION=\"$(VERSION_FULL)\"
|
||||
|
||||
_all:
|
||||
echo $(VERSION_FULL)
|
||||
echo $(VERSION_MAJ)
|
||||
echo $(VERSION_MIN)
|
||||
echo $(VERSION_PAT)
|
||||
echo $(SOLO_VERSION_FULL)
|
||||
echo $(SOLO_VERSION_MAJ)
|
||||
echo $(SOLO_VERSION_MIN)
|
||||
echo $(SOLO_VERSION_PAT)
|
||||
|
||||
%.o: %.s
|
||||
$(AS) -o $@ $^
|
@ -61,7 +61,7 @@ SECTIONS
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
} >sram2 AT> flash
|
||||
|
||||
.flag :
|
||||
{
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define _APP_H_
|
||||
#include <stdint.h>
|
||||
#include "version.h"
|
||||
#include "solo.h"
|
||||
|
||||
#define SOLO
|
||||
|
||||
|
@ -5,12 +5,16 @@
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "crypto.h"
|
||||
#include "memory_layout.h"
|
||||
#include "device.h"
|
||||
#include "sense.h"
|
||||
#include "log.h"
|
||||
|
||||
|
||||
const uint8_t attestation_solo_cert_der[] =
|
||||
"\x30\x82\x02\xe1\x30\x82\x02\x88\xa0\x03\x02\x01\x02\x02\x01\x01\x30\x0a\x06\x08"
|
||||
"\x30\x82\x03\x03\x30\x82\x02\xaa\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"
|
||||
"\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20\x4b"
|
||||
@ -18,36 +22,37 @@ const uint8_t attestation_solo_cert_der[] =
|
||||
"\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79\x73"
|
||||
"\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16"
|
||||
"\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d\x30"
|
||||
"\x20\x17\x0d\x31\x38\x31\x31\x31\x31\x31\x32\x35\x32\x30\x30\x5a\x18\x0f\x32\x30"
|
||||
"\x36\x38\x31\x30\x32\x39\x31\x32\x35\x32\x30\x30\x5a\x30\x81\x92\x31\x0b\x30\x09"
|
||||
"\x20\x17\x0d\x31\x39\x31\x32\x30\x31\x31\x39\x32\x33\x34\x39\x5a\x18\x0f\x32\x30"
|
||||
"\x36\x39\x31\x31\x31\x38\x31\x39\x32\x33\x34\x39\x5a\x30\x81\x91\x31\x0b\x30\x09"
|
||||
"\x06\x03\x55\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08"
|
||||
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53"
|
||||
"\x6f\x6c\x6f\x20\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41"
|
||||
"\x75\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61"
|
||||
"\x74\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b"
|
||||
"\x65\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
|
||||
"\x09\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63"
|
||||
"\x6f\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a"
|
||||
"\x28\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5"
|
||||
"\xd2\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6"
|
||||
"\xac\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xdc\x30\x81\xd9\x30"
|
||||
"\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d"
|
||||
"\x9e\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81"
|
||||
"\x97\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55"
|
||||
"\x04\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72"
|
||||
"\x79\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f"
|
||||
"\x20\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74"
|
||||
"\x20\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
|
||||
"\x4d\x61\x72\x79\x6c\x61\x6e\x64\x31\x11\x30\x0f\x06\x03\x55\x04\x0a\x0c\x08\x53"
|
||||
"\x6f\x6c\x6f\x4b\x65\x79\x73\x31\x22\x30\x20\x06\x03\x55\x04\x0b\x0c\x19\x41\x75"
|
||||
"\x74\x68\x65\x6e\x74\x69\x63\x61\x74\x6f\x72\x20\x41\x74\x74\x65\x73\x74\x61\x74"
|
||||
"\x69\x6f\x6e\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65"
|
||||
"\x79\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09"
|
||||
"\x01\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f"
|
||||
"\x6d\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04"
|
||||
"\x02\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x0a\x06\x08"
|
||||
"\x2a\x86\x48\xce\x3d\x04\x03\x02\x03\x47\x00\x30\x44\x02\x20\x71\x10\x46\x2c\xf5"
|
||||
"\x16\x18\x97\x55\xca\x64\x50\x3b\x69\xb2\xdf\x17\x71\xab\xad\x8e\xc0\xd6\xa6\x07"
|
||||
"\x3d\x66\x8a\x3b\xbb\xfe\x61\x02\x20\x1e\x82\xef\xeb\x5e\x4e\x3a\x00\x84\x64\xd2"
|
||||
"\xf8\x84\xc3\x78\x35\x93\x63\x81\x2e\xbe\xa6\x12\x32\x6e\x29\x90\xc8\x91\x4b\x71"
|
||||
"\x52"
|
||||
"\x6d\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48\xce"
|
||||
"\x3d\x03\x01\x07\x03\x42\x00\x04\x22\xfe\x0f\xb5\x2a\x78\xbe\xc6\x45\x37\x1a\x28"
|
||||
"\xa7\x57\x43\x49\xa4\x6f\x85\x4d\xca\x4e\x25\x1c\x9f\x75\x30\x3d\xbf\x10\xd5\xd2"
|
||||
"\xd2\x0b\xb9\x69\x2c\xdd\xb2\x5c\x14\xd8\x39\x85\x12\xf6\x23\xee\x91\xba\xc6\xac"
|
||||
"\xff\x4a\x1a\x27\xef\xe0\xc1\x54\x3f\xd4\xd9\xc5\xa3\x81\xff\x30\x81\xfc\x30\x1d"
|
||||
"\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x3b\xe6\xd2\xc0\x6f\xf2\xe7\xb0\x7c\x9d\x9e"
|
||||
"\x28\xc0\x20\xb0\x0d\x07\xc8\x15\xc8\x30\x81\x9f\x06\x03\x55\x1d\x23\x04\x81\x97"
|
||||
"\x30\x81\x94\xa1\x81\x86\xa4\x81\x83\x30\x81\x80\x31\x0b\x30\x09\x06\x03\x55\x04"
|
||||
"\x06\x13\x02\x55\x53\x31\x11\x30\x0f\x06\x03\x55\x04\x08\x0c\x08\x4d\x61\x72\x79"
|
||||
"\x6c\x61\x6e\x64\x31\x12\x30\x10\x06\x03\x55\x04\x0a\x0c\x09\x53\x6f\x6c\x6f\x20"
|
||||
"\x4b\x65\x79\x73\x31\x10\x30\x0e\x06\x03\x55\x04\x0b\x0c\x07\x52\x6f\x6f\x74\x20"
|
||||
"\x43\x41\x31\x15\x30\x13\x06\x03\x55\x04\x03\x0c\x0c\x73\x6f\x6c\x6f\x6b\x65\x79"
|
||||
"\x73\x2e\x63\x6f\x6d\x31\x21\x30\x1f\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01"
|
||||
"\x16\x12\x68\x65\x6c\x6c\x6f\x40\x73\x6f\x6c\x6f\x6b\x65\x79\x73\x2e\x63\x6f\x6d"
|
||||
"\x82\x09\x00\xc4\x47\x63\x92\x8f\xf4\xbe\x8c\x30\x09\x06\x03\x55\x1d\x13\x04\x02"
|
||||
"\x30\x00\x30\x0b\x06\x03\x55\x1d\x0f\x04\x04\x03\x02\x04\xf0\x30\x21\x06\x0b\x2b"
|
||||
"\x06\x01\x04\x01\x82\xe5\x1c\x01\x01\x04\x04\x12\x04\x10\x88\x76\x63\x1b\xd4\xa0"
|
||||
"\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79\x30\x0a\x06\x08\x2a\x86\x48\xce\x3d\x04"
|
||||
"\x03\x02\x03\x47\x00\x30\x44\x02\x20\x4d\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d"
|
||||
"\x87\xc3\xfa\xb6\x6b\x29\x9b\xfb\x8b\x4d\x4d\x29\x77\x5b\xa1\x04\x4c\x7f\x12\x8d"
|
||||
"\x71\x02\x20\x47\x4c\x3d\xb2\xa1\x74\xe3\x9c\xfe\xe1\x23\xbf\xec\x47\x96\xf4\xe5"
|
||||
"\x9b\x65\x76\xac\xc8\x69\x1d\xe2\x74\xff\x4e\xa4\xcf\x02\x6d"
|
||||
;
|
||||
|
||||
const uint8_t attestation_hacker_cert_der[] =
|
||||
@ -95,12 +100,32 @@ const uint8_t attestation_hacker_cert_der[] =
|
||||
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(){
|
||||
uint8_t * device_get_attestation_key(){
|
||||
flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR;
|
||||
return page->attestation_key;
|
||||
}
|
||||
|
||||
uint16_t device_attestation_cert_der_get_size(){
|
||||
uint16_t sz = (uint16_t)((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert_size;
|
||||
return sz;
|
||||
}
|
||||
|
||||
void device_attestation_read_cert_der(uint8_t * dst){
|
||||
const uint8_t * der = ((flash_attestation_page *)ATTESTATION_PAGE_ADDR)->attestation_cert;
|
||||
uint16_t sz = device_attestation_cert_der_get_size();
|
||||
memmove(dst, der, sz);
|
||||
|
||||
// Overwrite respective x509 fields if Tap or Somu.
|
||||
if (memcmp(dst + 0x2c6, "\xea\x09\x15\x6c\x86\x48\x57\x2a\xa8\x8d", 10) == 0){
|
||||
if (device_is_nfc()){
|
||||
dst[0x2a2] = 0x89;//tap aaguid byte
|
||||
memmove(dst + 0xac, "\x34\x33\x38\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x34\x33\x38", 20);//tap-id
|
||||
memmove(dst + 0x2c5, "\x6d\x7b\x41\x2b\xff\x57\xf0\x03\xbd\x5b\x39\x4a\xf7\xa9\x2d\x6d\xcb\x9e\x2d\x88\xbf\xb3\x93\xc5\x66\x3b\xd1\xbc\x34\xfa\x5c\x4c\x02\x20\x59\x01\x49\x39\x1b\xb7\xa9\x1c\xed\x49\x78\x4f\x92\xa9\x61\x14\xa5\x6e\x96\x3f\x29\x02\x93\xe0\x5d\xe2\x75\xd0\x60\xd9\x74\xc2", 66);//tap-sig
|
||||
} else if (tsc_sensor_exists()) {
|
||||
dst[0x2a2] = 0x98;//somu aaguid byte
|
||||
memmove(dst + 0xac, "\x35\x30\x32\x5a\x18\x0f\x32\x30\x36\x39\x31\x31\x31\x38\x31\x39\x32\x35\x30\x32", 20);//somu-id
|
||||
memmove(dst + 0x2c5, "\x4d\x08\xc8\x9d\xc4\x50\x49\x70\x48\x4d\xd0\x12\xd9\x7c\x62\x5e\x6b\xd3\x84\xd5\x36\x42\xfe\x86\x8e\x7a\x23\x59\xa0\x20\xf0\xc5\x02\x20\x5f\x70\x93\x61\x5a\xe4\x20\xcf\xb9\x8a\xf5\xdd\x87\xd0\x48\x6d\x7d\x59\xef\x9e\x0e\x11\xa3\x8e\xf7\xe3\xe2\xf5\x35\x37\x99\x1a", 66);//somu-sig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,369 +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.
|
||||
/*
|
||||
* Wrapper for crypto implementation on device
|
||||
*
|
||||
* */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
|
||||
#include "util.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#ifdef USE_SOFTWARE_IMPLEMENTATION
|
||||
|
||||
#include "sha256.h"
|
||||
#include "uECC.h"
|
||||
#include "aes.h"
|
||||
#include "ctap.h"
|
||||
#include "device.h"
|
||||
// stuff for SHA512
|
||||
#include "sha2.h"
|
||||
#include "blockwise.h"
|
||||
#include APP_CONFIG
|
||||
#include "log.h"
|
||||
#include "memory_layout.h"
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MBEDTLS_ECP_DP_NONE = 0,
|
||||
MBEDTLS_ECP_DP_SECP192R1, /*!< 192-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP224R1, /*!< 224-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP256R1, /*!< 256-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP384R1, /*!< 384-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_SECP521R1, /*!< 521-bits NIST curve */
|
||||
MBEDTLS_ECP_DP_BP256R1, /*!< 256-bits Brainpool curve */
|
||||
MBEDTLS_ECP_DP_BP384R1, /*!< 384-bits Brainpool curve */
|
||||
MBEDTLS_ECP_DP_BP512R1, /*!< 512-bits Brainpool curve */
|
||||
MBEDTLS_ECP_DP_CURVE25519, /*!< Curve25519 */
|
||||
MBEDTLS_ECP_DP_SECP192K1, /*!< 192-bits "Koblitz" curve */
|
||||
MBEDTLS_ECP_DP_SECP224K1, /*!< 224-bits "Koblitz" curve */
|
||||
MBEDTLS_ECP_DP_SECP256K1, /*!< 256-bits "Koblitz" curve */
|
||||
} mbedtls_ecp_group_id;
|
||||
|
||||
|
||||
static SHA256_CTX sha256_ctx;
|
||||
static cf_sha512_context sha512_ctx;
|
||||
static const struct uECC_Curve_t * _es256_curve = NULL;
|
||||
static const uint8_t * _signing_key = NULL;
|
||||
static int _key_len = 0;
|
||||
|
||||
// Secrets for testing only
|
||||
static uint8_t master_secret[64];
|
||||
static uint8_t transport_secret[32];
|
||||
|
||||
|
||||
void crypto_sha256_init(void)
|
||||
{
|
||||
sha256_init(&sha256_ctx);
|
||||
}
|
||||
|
||||
void crypto_sha512_init(void)
|
||||
{
|
||||
cf_sha512_init(&sha512_ctx);
|
||||
}
|
||||
|
||||
void crypto_load_master_secret(uint8_t * key)
|
||||
{
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
memmove(master_secret, key, 64);
|
||||
memmove(transport_secret, key+64, 32);
|
||||
}
|
||||
|
||||
void crypto_reset_master_secret(void)
|
||||
{
|
||||
memset(master_secret, 0, 64);
|
||||
memset(transport_secret, 0, 32);
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
}
|
||||
|
||||
|
||||
void crypto_sha256_update(uint8_t * data, size_t len)
|
||||
{
|
||||
sha256_update(&sha256_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha512_update(const uint8_t * data, size_t len) {
|
||||
cf_sha512_update(&sha512_ctx, data, len);
|
||||
}
|
||||
|
||||
void crypto_sha256_update_secret()
|
||||
{
|
||||
sha256_update(&sha256_ctx, master_secret, 32);
|
||||
}
|
||||
|
||||
void crypto_sha256_final(uint8_t * hash)
|
||||
{
|
||||
sha256_final(&sha256_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha512_final(uint8_t * hash)
|
||||
{
|
||||
// NB: there is also cf_sha512_digest
|
||||
cf_sha512_digest_final(&sha512_ctx, hash);
|
||||
}
|
||||
|
||||
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
unsigned int i;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memmove(buf, key, klen);
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
buf[i] = buf[i] ^ 0x36;
|
||||
}
|
||||
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(buf, 64);
|
||||
}
|
||||
|
||||
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
{
|
||||
uint8_t buf[64];
|
||||
unsigned int i;
|
||||
crypto_sha256_final(hmac);
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (key == CRYPTO_MASTER_KEY)
|
||||
{
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret)/2;
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY2)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, key size must be <= 64\n");
|
||||
exit(1);
|
||||
}
|
||||
memmove(buf, key, klen);
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++)
|
||||
{
|
||||
buf[i] = buf[i] ^ 0x5c;
|
||||
}
|
||||
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(buf, 64);
|
||||
crypto_sha256_update(hmac, 32);
|
||||
crypto_sha256_final(hmac);
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_init(void)
|
||||
{
|
||||
uECC_set_rng((uECC_RNG_Function)ctap_generate_rng);
|
||||
_es256_curve = uECC_secp256r1();
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_load_attestation_key(void)
|
||||
{
|
||||
// static uint8_t _key [32];
|
||||
flash_attestation_page * page =(flash_attestation_page *)ATTESTATION_PAGE_ADDR;
|
||||
// memmove(_key, (uint8_t *)ATTESTATION_KEY_ADDR, 32);
|
||||
_signing_key = page->attestation_key;
|
||||
_key_len = 32;
|
||||
}
|
||||
|
||||
void crypto_ecc256_sign(uint8_t * data, int len, uint8_t * sig)
|
||||
{
|
||||
if ( uECC_sign(_signing_key, data, len, sig, _es256_curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2)
|
||||
{
|
||||
static uint8_t privkey[32];
|
||||
generate_private_key(data,len,data2,len2,privkey);
|
||||
_signing_key = privkey;
|
||||
_key_len = 32;
|
||||
}
|
||||
|
||||
void crypto_ecdsa_sign(uint8_t * data, int len, uint8_t * sig, int MBEDTLS_ECP_ID)
|
||||
{
|
||||
|
||||
const struct uECC_Curve_t * curve = NULL;
|
||||
|
||||
switch(MBEDTLS_ECP_ID)
|
||||
{
|
||||
case MBEDTLS_ECP_DP_SECP192R1:
|
||||
curve = uECC_secp192r1();
|
||||
if (_key_len != 24) goto fail;
|
||||
break;
|
||||
case MBEDTLS_ECP_DP_SECP224R1:
|
||||
curve = uECC_secp224r1();
|
||||
if (_key_len != 28) goto fail;
|
||||
break;
|
||||
case MBEDTLS_ECP_DP_SECP256R1:
|
||||
curve = uECC_secp256r1();
|
||||
if (_key_len != 32) goto fail;
|
||||
break;
|
||||
case MBEDTLS_ECP_DP_SECP256K1:
|
||||
curve = uECC_secp256k1();
|
||||
if (_key_len != 32) goto fail;
|
||||
break;
|
||||
default:
|
||||
printf2(TAG_ERR, "error, invalid ECDSA alg specifier\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( uECC_sign(_signing_key, data, len, sig, curve) == 0)
|
||||
{
|
||||
printf2(TAG_ERR, "error, uECC failed\n");
|
||||
exit(1);
|
||||
}
|
||||
return;
|
||||
|
||||
fail:
|
||||
printf2(TAG_ERR, "error, invalid key length\n");
|
||||
exit(1);
|
||||
|
||||
}
|
||||
|
||||
void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, uint8_t * privkey)
|
||||
{
|
||||
crypto_sha256_hmac_init(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
crypto_sha256_update(data, len);
|
||||
crypto_sha256_update(data2, len2);
|
||||
crypto_sha256_update(master_secret, 32); // TODO AES
|
||||
crypto_sha256_hmac_final(CRYPTO_MASTER_KEY, 0, privkey);
|
||||
|
||||
crypto_aes256_init(master_secret + 32, NULL);
|
||||
crypto_aes256_encrypt(privkey, 32);
|
||||
}
|
||||
|
||||
|
||||
/*int uECC_compute_public_key(const uint8_t *private_key, uint8_t *public_key, uECC_Curve curve);*/
|
||||
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y)
|
||||
{
|
||||
uint8_t privkey[32];
|
||||
uint8_t pubkey[64];
|
||||
|
||||
generate_private_key(data,len,NULL,0,privkey);
|
||||
|
||||
memset(pubkey,0,sizeof(pubkey));
|
||||
uECC_compute_public_key(privkey, pubkey, _es256_curve);
|
||||
memmove(x,pubkey,32);
|
||||
memmove(y,pubkey+32,32);
|
||||
}
|
||||
void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
|
||||
{
|
||||
uECC_compute_public_key(privkey, pubkey, _es256_curve);
|
||||
}
|
||||
|
||||
|
||||
void crypto_load_external_key(uint8_t * key, int len)
|
||||
{
|
||||
_signing_key = key;
|
||||
_key_len = len;
|
||||
}
|
||||
|
||||
|
||||
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey)
|
||||
{
|
||||
if (uECC_make_key(pubkey, privkey, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, uECC_make_key failed\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret)
|
||||
{
|
||||
if (uECC_shared_secret(pubkey, privkey, shared_secret, _es256_curve) != 1)
|
||||
{
|
||||
printf2(TAG_ERR, "Error, uECC_shared_secret failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct AES_ctx aes_ctx;
|
||||
void crypto_aes256_init(uint8_t * key, uint8_t * nonce)
|
||||
{
|
||||
if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
AES_init_ctx(&aes_ctx, transport_secret);
|
||||
}
|
||||
else
|
||||
{
|
||||
AES_init_ctx(&aes_ctx, key);
|
||||
}
|
||||
if (nonce == NULL)
|
||||
{
|
||||
memset(aes_ctx.Iv, 0, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(aes_ctx.Iv, nonce, 16);
|
||||
}
|
||||
}
|
||||
|
||||
// prevent round key recomputation
|
||||
void crypto_aes256_reset_iv(uint8_t * nonce)
|
||||
{
|
||||
if (nonce == NULL)
|
||||
{
|
||||
memset(aes_ctx.Iv, 0, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(aes_ctx.Iv, nonce, 16);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto_aes256_decrypt(uint8_t * buf, int length)
|
||||
{
|
||||
AES_CBC_decrypt_buffer(&aes_ctx, buf, length);
|
||||
}
|
||||
|
||||
void crypto_aes256_encrypt(uint8_t * buf, int length)
|
||||
{
|
||||
AES_CBC_encrypt_buffer(&aes_ctx, buf, length);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
#error "No crypto implementation defined"
|
||||
#endif
|
@ -199,6 +199,20 @@ int solo_is_locked(){
|
||||
return tag == ATTESTATION_CONFIGURED_TAG && (device_settings & SOLO_FLAG_LOCKED) != 0;
|
||||
}
|
||||
|
||||
// Locks solo flash from debugging. Locks on next reboot.
|
||||
// This should be removed in next Solo release.
|
||||
void solo_lock_if_not_already() {
|
||||
uint8_t buf[2048];
|
||||
|
||||
memmove(buf, (uint8_t*)ATTESTATION_PAGE_ADDR, 2048);
|
||||
|
||||
((flash_attestation_page *)buf)->device_settings |= SOLO_FLAG_LOCKED;
|
||||
|
||||
flash_erase_page(ATTESTATION_PAGE);
|
||||
|
||||
flash_write(ATTESTATION_PAGE_ADDR, buf, 2048);
|
||||
}
|
||||
|
||||
/** device_migrate
|
||||
* Depending on version of device, migrates:
|
||||
* * Moves attestation certificate to data segment.
|
||||
@ -285,7 +299,7 @@ static void device_migrate(){
|
||||
}
|
||||
}
|
||||
|
||||
void device_init(int argc, char *argv[])
|
||||
void device_init()
|
||||
{
|
||||
|
||||
hw_init(LOW_FREQUENCY);
|
||||
@ -468,20 +482,8 @@ void heartbeat(void)
|
||||
|
||||
}
|
||||
|
||||
void authenticator_read_state(AuthenticatorState * a)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(STATE1_PAGE);
|
||||
memmove(a,ptr,sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
void authenticator_read_backup_state(AuthenticatorState * a)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
|
||||
memmove(a,ptr,sizeof(AuthenticatorState));
|
||||
}
|
||||
|
||||
// Return 1 yes backup is init'd, else 0
|
||||
int authenticator_is_backup_initialized(void)
|
||||
static int authenticator_is_backup_initialized(void)
|
||||
{
|
||||
uint8_t header[16];
|
||||
uint32_t * ptr = (uint32_t *)flash_addr(STATE2_PAGE);
|
||||
@ -490,20 +492,35 @@ int authenticator_is_backup_initialized(void)
|
||||
return state->is_initialized == INITIALIZED_MARKER;
|
||||
}
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * a, int backup)
|
||||
int authenticator_read_state(AuthenticatorState * a)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *) flash_addr(STATE1_PAGE);
|
||||
memmove(a, ptr, sizeof(AuthenticatorState));
|
||||
|
||||
if (a->is_initialized != INITIALIZED_MARKER){
|
||||
|
||||
if (authenticator_is_backup_initialized()){
|
||||
printf1(TAG_ERR,"Warning: memory corruption detected. restoring from backup..\n");
|
||||
ptr = (uint32_t *) flash_addr(STATE2_PAGE);
|
||||
memmove(a, ptr, sizeof(AuthenticatorState));
|
||||
authenticator_write_state(a);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void authenticator_write_state(AuthenticatorState * a)
|
||||
{
|
||||
if (! backup)
|
||||
{
|
||||
flash_erase_page(STATE1_PAGE);
|
||||
|
||||
flash_write(flash_addr(STATE1_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
|
||||
}
|
||||
else
|
||||
{
|
||||
flash_erase_page(STATE2_PAGE);
|
||||
|
||||
flash_erase_page(STATE2_PAGE);
|
||||
flash_write(flash_addr(STATE2_PAGE), (uint8_t*)a, sizeof(AuthenticatorState));
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
@ -560,7 +577,11 @@ uint32_t ctap_atomic_count(uint32_t amount)
|
||||
return lastc;
|
||||
}
|
||||
|
||||
if (amount > 256){
|
||||
lastc = amount;
|
||||
} else {
|
||||
lastc += amount;
|
||||
}
|
||||
|
||||
if (lastc/256 > erases)
|
||||
{
|
||||
@ -752,11 +773,6 @@ int ctap_generate_rng(uint8_t * dst, size_t num)
|
||||
}
|
||||
|
||||
|
||||
int ctap_user_verification(uint8_t arg)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ctap_reset_rk(void)
|
||||
{
|
||||
int i;
|
||||
@ -774,33 +790,28 @@ uint32_t ctap_rk_size(void)
|
||||
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
|
||||
ctap_overwrite_rk(index, rk);
|
||||
}
|
||||
|
||||
printf1(TAG_GREEN, "storing RK %d @ %04x\r\n", index,addr);
|
||||
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
flash_write(addr, (uint8_t*)rk, sizeof(CTAP_residentKey));
|
||||
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
void ctap_delete_rk(int index)
|
||||
{
|
||||
CTAP_residentKey rk;
|
||||
memset(&rk, 0xff, sizeof(CTAP_residentKey));
|
||||
ctap_overwrite_rk(index, &rk);
|
||||
}
|
||||
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + ((sizeof(CTAP_residentKey)*index) % PAGE_SIZE);
|
||||
int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
|
||||
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
|
||||
|
||||
uint32_t addr = flash_addr(page_offset + RK_START_PAGE) + byte_offset_into_page;
|
||||
|
||||
printf1(TAG_GREEN, "reading RK %d @ %04x\r\n", index, addr);
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
uint32_t * ptr = (uint32_t *)addr;
|
||||
memmove((uint8_t*)rk,ptr,sizeof(CTAP_residentKey));
|
||||
//dump_hex1(TAG_GREEN,rk,sizeof(CTAP_residentKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -811,22 +822,28 @@ void ctap_load_rk(int index,CTAP_residentKey * rk)
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
uint8_t tmppage[PAGE_SIZE];
|
||||
int page_offset = (sizeof(CTAP_residentKey) * index) / PAGE_SIZE;
|
||||
int page = page_offset + RK_START_PAGE;
|
||||
|
||||
printf1(TAG_GREEN, "overwriting RK %d\r\n", index);
|
||||
int byte_offset_into_page = (sizeof(CTAP_residentKey) * (index % (PAGE_SIZE/sizeof(CTAP_residentKey))));
|
||||
int page_offset = (index)/(PAGE_SIZE/sizeof(CTAP_residentKey));
|
||||
|
||||
printf1(TAG_GREEN, "overwriting RK %d @ page %d @ addr 0x%08x-0x%08x\r\n",
|
||||
index, RK_START_PAGE + page_offset,
|
||||
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page,
|
||||
flash_addr(RK_START_PAGE + page_offset) + byte_offset_into_page + sizeof(CTAP_residentKey)
|
||||
);
|
||||
if (page_offset < RK_NUM_PAGES)
|
||||
{
|
||||
memmove(tmppage, (uint8_t*)flash_addr(page), PAGE_SIZE);
|
||||
memmove(tmppage, (uint8_t*)flash_addr(RK_START_PAGE + page_offset), PAGE_SIZE);
|
||||
|
||||
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
|
||||
flash_erase_page(page);
|
||||
flash_write(flash_addr(page), tmppage, PAGE_SIZE);
|
||||
memmove(tmppage + byte_offset_into_page, rk, sizeof(CTAP_residentKey));
|
||||
flash_erase_page(RK_START_PAGE + page_offset);
|
||||
flash_write(flash_addr(RK_START_PAGE + page_offset), tmppage, PAGE_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Out of bounds reading index %d for rk\n", index);
|
||||
}
|
||||
printf1(TAG_GREEN, "4\r\n");
|
||||
}
|
||||
|
||||
void boot_st_bootloader(void)
|
||||
@ -861,6 +878,17 @@ void boot_solo_bootloader(void)
|
||||
|
||||
}
|
||||
|
||||
void device_read_aaguid(uint8_t * dst){
|
||||
uint8_t * aaguid = (uint8_t *)"\x88\x76\x63\x1b\xd4\xa0\x42\x7f\x57\x73\x0e\xc7\x1c\x9e\x02\x79";
|
||||
memmove(dst, aaguid, 16);
|
||||
if (device_is_nfc()){
|
||||
dst[0] = 0x89;
|
||||
}
|
||||
else if (tsc_sensor_exists()){
|
||||
dst[0] = 0x98;
|
||||
}
|
||||
dump_hex1(TAG_GREEN,dst, 16);
|
||||
}
|
||||
|
||||
|
||||
void _Error_Handler(char *file, int line)
|
||||
|
@ -146,12 +146,14 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param)
|
||||
case DEVICE_LOW_POWER_IDLE:
|
||||
SET_CLOCK_RATE0();
|
||||
break;
|
||||
#if !defined(IS_BOOTLOADER)
|
||||
case DEVICE_LOW_POWER_FAST:
|
||||
SET_CLOCK_RATE1();
|
||||
break;
|
||||
case DEVICE_FAST:
|
||||
SET_CLOCK_RATE2();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,105 +4,83 @@
|
||||
// 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 <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "stm32l4xx.h"
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
#include "stm32l4xx_ll_rcc.h"
|
||||
#include "stm32l4xx_ll_system.h"
|
||||
#include "stm32l4xx_ll_pwr.h"
|
||||
#include "stm32l4xx_ll_utils.h"
|
||||
#include "stm32l4xx_ll_cortex.h"
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
#include "stm32l4xx_ll_usart.h"
|
||||
#include "stm32l4xx_ll_bus.h"
|
||||
#include "stm32l4xx_ll_usb.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "stm32l4xx_hal_pcd.h"
|
||||
|
||||
#include "usbd_core.h"
|
||||
#include "usbd_desc.h"
|
||||
#include "usbd_hid.h"
|
||||
/*#include "usbd_hid.h"*/
|
||||
|
||||
#include APP_CONFIG
|
||||
#include "flash.h"
|
||||
#include "rng.h"
|
||||
#include "led.h"
|
||||
#include "cbor.h"
|
||||
#include "device.h"
|
||||
#include "ctaphid.h"
|
||||
//#include "bsp.h"
|
||||
#include "util.h"
|
||||
#include "fifo.h"
|
||||
#include "log.h"
|
||||
#include "ctap.h"
|
||||
#include APP_CONFIG
|
||||
|
||||
#ifdef TEST_SOLO_STM32
|
||||
#define Error_Handler() _Error_Handler(__FILE__,__LINE__)
|
||||
#define PAGE_SIZE 2048
|
||||
#define PAGES 128
|
||||
// Pages 119-127 are data
|
||||
#define COUNTER2_PAGE (PAGES - 4)
|
||||
#define COUNTER1_PAGE (PAGES - 3)
|
||||
#define STATE2_PAGE (PAGES - 2)
|
||||
#define STATE1_PAGE (PAGES - 1)
|
||||
#if !defined(TEST)
|
||||
|
||||
|
||||
uint32_t __90_ms = 0;
|
||||
#define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
|
||||
|
||||
// Timer6 overflow handler. happens every ~90ms.
|
||||
void TIM6_DAC_IRQHandler()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// timer is only 16 bits, so roll it over here
|
||||
TIM6->SR = 0;
|
||||
__90_ms += 1;
|
||||
}
|
||||
uint8_t hidmsg[64];
|
||||
uint32_t t1 = 0;
|
||||
|
||||
set_logging_mask(
|
||||
/*0*/
|
||||
//TAG_GEN|
|
||||
// TAG_MC |
|
||||
// TAG_GA |
|
||||
TAG_WALLET |
|
||||
TAG_STOR |
|
||||
//TAG_NFC_APDU |
|
||||
TAG_NFC |
|
||||
//TAG_CP |
|
||||
// TAG_CTAP|
|
||||
//TAG_HID|
|
||||
TAG_U2F|
|
||||
//TAG_PARSE |
|
||||
//TAG_TIME|
|
||||
// TAG_DUMP|
|
||||
TAG_GREEN|
|
||||
TAG_RED|
|
||||
TAG_EXT|
|
||||
TAG_CCID|
|
||||
TAG_ERR
|
||||
);
|
||||
|
||||
device_init(argc, argv);
|
||||
|
||||
memset(hidmsg,0,sizeof(hidmsg));
|
||||
|
||||
uint32_t millis(void)
|
||||
{
|
||||
return (((uint32_t)TIM6->CNT) + (__90_ms * 90));
|
||||
}
|
||||
|
||||
void _Error_Handler(char *file, int line)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
if (millis() - t1 > HEARTBEAT_PERIOD)
|
||||
{
|
||||
heartbeat();
|
||||
t1 = millis();
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint32_t i = 5;
|
||||
device_manage();
|
||||
|
||||
hw_init();
|
||||
|
||||
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
|
||||
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
|
||||
flash_option_bytes_init(1);
|
||||
|
||||
while (1)
|
||||
if (usbhid_recv(hidmsg) > 0)
|
||||
{
|
||||
uint32_t t0 = millis() % 750;
|
||||
if (! IS_BUTTON_PRESSED())
|
||||
{
|
||||
if (t0 < 750*1/3)
|
||||
{
|
||||
led_rgb(0 | (0 << 8) | (i << 17));
|
||||
}
|
||||
else if (t0 < 750*2/3)
|
||||
{
|
||||
led_rgb(0 | (i << 8) | (0 << 16));
|
||||
ctaphid_handle_packet(hidmsg);
|
||||
memset(hidmsg, 0, sizeof(hidmsg));
|
||||
}
|
||||
else
|
||||
{
|
||||
led_rgb(i | (0 << 8) | (0 << 16));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
led_rgb(0x151515);
|
||||
}
|
||||
ctaphid_check_timeouts();
|
||||
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
usbhid_close();
|
||||
printf1(TAG_GREEN, "done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -67,7 +67,7 @@ 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
|
||||
#define ATTESTATION_CONFIGURED_TAG 0xaa551e79
|
||||
|
||||
struct flash_attestation_page{
|
||||
uint8_t attestation_key[32];
|
||||
|
25
targets/stm32l432/src/solo.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef _SOLO_H_
|
||||
#define _SOLO_H_
|
||||
|
||||
void device_init();
|
||||
|
||||
void main_loop_delay();
|
||||
void usbhid_init();
|
||||
void usbhid_close();
|
||||
int usbhid_recv(uint8_t * msg);
|
||||
|
||||
void heartbeat();
|
||||
|
||||
// Called each main loop. Doesn't need to do anything.
|
||||
void device_manage();
|
||||
|
||||
void device_init_button();
|
||||
|
||||
// For Solo hacker
|
||||
void boot_solo_bootloader();
|
||||
void boot_st_bootloader();
|
||||
|
||||
|
||||
void delay(uint32_t ms);
|
||||
|
||||
#endif
|
@ -1,5 +1,4 @@
|
||||
ecdsa
|
||||
fido2
|
||||
intelhex
|
||||
pyserial
|
||||
solo-python
|
||||
|