Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
6a92423f25 |
10
.gitignore
vendored
10
.gitignore
vendored
@ -81,5 +81,15 @@ env3/
|
||||
.tags*
|
||||
targets/*/docs/
|
||||
main
|
||||
targets/efm32/.project
|
||||
targets/efm32/.settings/com.silabs.ss.framework.ide.project.sls.core.prefs
|
||||
targets/efm32/.settings/org.eclipse.cdt.codan.core.prefs
|
||||
targets/efm32/CMSIS/EFM32PG1B/startup_gcc_efm32pg1b.s
|
||||
targets/efm32/CMSIS/EFM32PG1B/system_efm32pg1b.c
|
||||
targets/efm32/EFM32.hwconf
|
||||
targets/efm32/EFM32_EFM32JG1B200F128GM32.hwconf
|
||||
targets/efm32/emlib/em_adc.c
|
||||
targets/efm32/emlib/em_assert.c
|
||||
targets/efm32/emlib/em_cmu.c
|
||||
|
||||
builds/*
|
||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,6 +1,9 @@
|
||||
[submodule "tinycbor"]
|
||||
path = tinycbor
|
||||
url = https://github.com/intel/tinycbor
|
||||
[submodule "python-fido2"]
|
||||
path = python-fido2
|
||||
url = https://github.com/solokeys/python-fido2
|
||||
[submodule "crypto/micro-ecc"]
|
||||
path = crypto/micro-ecc
|
||||
url = https://github.com/kmackay/micro-ecc.git
|
||||
@ -10,6 +13,3 @@
|
||||
[submodule "targets/stm32l442/dfuse-tool"]
|
||||
path = targets/stm32l442/dfuse-tool
|
||||
url = https://github.com/solokeys/dfuse-tool
|
||||
[submodule "crypto/cifra"]
|
||||
path = crypto/cifra
|
||||
url = https://github.com/solokeys/cifra.git
|
||||
|
@ -1 +0,0 @@
|
||||
udev/70-solokeys-access.rules
|
28
99-solo.rules
Normal file
28
99-solo.rules
Normal file
@ -0,0 +1,28 @@
|
||||
# Notify ModemManager this device should be ignored
|
||||
ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
|
||||
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
|
||||
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
|
||||
|
||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
|
||||
LABEL="mm_usb_device_blacklist_end"
|
||||
|
||||
|
||||
# Solo
|
||||
|
||||
## bootloader + firmware access
|
||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev"
|
||||
|
||||
## DFU access
|
||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess", GROUP="plugdev"
|
||||
|
||||
## Solo Secure symlink
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo [1-9]*", SYMLINK+="solokey"
|
||||
## Solo Hacker symlink
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo Hacker [1-9]*", SYMLINK+="solohacker"
|
||||
## Solo Serial access + symlink
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", SYMLINK+="soloserial"
|
||||
|
||||
|
||||
# U2F Zero
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", SYMLINK+="u2fzero"
|
@ -1 +0,0 @@
|
||||
2.0.0
|
10
CHANGELOG.md
10
CHANGELOG.md
@ -15,13 +15,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Extension interface to U2F and FIDO2
|
||||
- Read firmware version
|
||||
- Read RNG bytes
|
||||
|
||||
## [1.1.1] - 2019-03-01
|
||||
|
||||
- This version fixes an incorrect error code returned in U2F.
|
||||
|
||||
## [2.0.0] - 2019-03-01
|
||||
|
||||
- Merge of NFC functionality branch
|
||||
- Bug fix with compiled USB name being too long causing buffer overrun
|
||||
- Change upper byte of counter from `0xff` to `0x7f` to fix issues with some websites.
|
||||
|
@ -14,7 +14,7 @@ RUN echo "fb31fbdfe08406ece43eef5df623c0b2deb8b53e405e2c878300f7a1f303ee52 gcc.
|
||||
RUN sha256sum -c gcc.sha256
|
||||
RUN tar -C /opt -xf gcc.tar.bz2
|
||||
|
||||
# 2. Python3.7: for solo-python (merging etc.)
|
||||
# 2. Python3.7: for solotool (merging etc.)
|
||||
RUN wget -q -O miniconda.sh https://repo.anaconda.com/miniconda/Miniconda3-4.5.12-Linux-x86_64.sh
|
||||
# from website
|
||||
RUN echo "866ae9dff53ad0874e1d1a60b1ad1ef8 miniconda.sh" > miniconda.md5
|
||||
@ -24,10 +24,8 @@ RUN echo "e5e5b4cd2a918e0e96b395534222773f7241dc59d776db1b9f7fedfcb489157a mini
|
||||
RUN sha256sum -c miniconda.sha256
|
||||
|
||||
RUN bash ./miniconda.sh -b -p /opt/conda
|
||||
RUN ln -s /opt/conda/bin/python /usr/local/bin/python3
|
||||
RUN ln -s /opt/conda/bin/python /usr/local/bin/python
|
||||
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip3
|
||||
RUN ln -s /opt/conda/bin/pip /usr/local/bin/pip
|
||||
RUN ln -s /opt/conda/bin/python3 /usr/local/bin/python3
|
||||
RUN ln -s /opt/conda/bin/python3 /usr/local/bin/python
|
||||
|
||||
# 3. Source code
|
||||
RUN git clone --recurse-submodules https://github.com/solokeys/solo /solo --config core.autocrlf=input
|
||||
|
27
Makefile
27
Makefile
@ -9,9 +9,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 = $(wildcard pc/*.c) $(wildcard fido2/*.c) $(wildcard crypto/sha256/*.c) crypto/tiny-AES-c/aes.c
|
||||
obj = $(src:.c=.o) crypto/micro-ecc/uECC.o
|
||||
|
||||
LIBCBOR = tinycbor/lib/libtinycbor.a
|
||||
@ -22,20 +20,9 @@ else
|
||||
export LDFLAGS = -Wl,--gc-sections
|
||||
endif
|
||||
LDFLAGS += $(LIBCBOR)
|
||||
|
||||
VERSION:=$(shell git describe --abbrev=0 )
|
||||
VERSION_FULL:=$(shell git describe)
|
||||
VERSION_MAJ:=$(shell python -c 'print("$(VERSION)".split(".")[0])')
|
||||
VERSION_MIN:=$(shell python -c 'print("$(VERSION)".split(".")[1])')
|
||||
VERSION_PAT:=$(shell python -c 'print("$(VERSION)".split(".")[2])')
|
||||
|
||||
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 $(VERSION_FLAGS)
|
||||
CFLAGS = -O2 -fdata-sections -ffunction-sections
|
||||
|
||||
INCLUDES = -I./tinycbor/src -I./crypto/sha256 -I./crypto/micro-ecc/ -Icrypto/tiny-AES-c/ -I./fido2/ -I./pc -I./fido2/extensions
|
||||
INCLUDES += -I./crypto/cifra/src
|
||||
|
||||
CFLAGS += $(INCLUDES)
|
||||
# for crypto/tiny-AES-c
|
||||
@ -74,7 +61,6 @@ crypto/micro-ecc/uECC.o: ./crypto/micro-ecc/uECC.c
|
||||
|
||||
venv:
|
||||
python3 -m venv venv
|
||||
venv/bin/pip -q install --upgrade pip
|
||||
venv/bin/pip -q install --upgrade -r tools/requirements.txt
|
||||
venv/bin/pip -q install --upgrade black
|
||||
|
||||
@ -83,7 +69,7 @@ black: venv
|
||||
venv/bin/black --skip-string-normalization --check tools/
|
||||
|
||||
wink: venv
|
||||
venv/bin/solo key wink
|
||||
venv/bin/python tools/solotool.py solo --wink
|
||||
|
||||
fido2-test: venv
|
||||
venv/bin/python tools/ctap_test.py
|
||||
@ -94,12 +80,7 @@ docker-build:
|
||||
docker build -t $(DOCKER_IMAGE) .
|
||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||
uncached-docker-build:
|
||||
docker build --no-cache -t $(DOCKER_IMAGE) .
|
||||
docker run --rm -v "$(CURDIR)/builds:/builds" \
|
||||
-v "$(CURDIR)/in-docker-build.sh:/in-docker-build.sh" \
|
||||
$(DOCKER_IMAGE) "./in-docker-build.sh" $(SOLO_VERSIONISH)
|
||||
$(DOCKER_IMAGE) /in-docker-build.sh $(SOLO_VERSIONISH)
|
||||
|
||||
CPPCHECK_FLAGS=--quiet --error-exitcode=2
|
||||
|
||||
|
35
README.md
35
README.md
@ -4,12 +4,6 @@
|
||||
[](https://keybase.io/team/solokeys.public)
|
||||
[](https://app.fossa.io/projects/git%2Bgithub.com%2Fsolokeys%2Fsolo?ref=badge_shield)
|
||||
|
||||
[](https://github.com/solokeys/solo/releases)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/commits/master)
|
||||
[](https://github.com/solokeys/solo/graphs/contributors)
|
||||
|
||||
|
||||
# Solo
|
||||
|
||||
@ -19,7 +13,7 @@ Solo supports FIDO2 and U2F standards for strong two-factor authentication and p
|
||||
|
||||
<img src="https://solokeys.com/images/photos/hero-on-white-cropped.png" width="600">
|
||||
|
||||
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, but it is easily portable.
|
||||
This repo contains the Solo firmware, including implementations of FIDO2 and U2F (CTAP2 and CTAP) over USB and NFC. The main implementation is for STM32L432, and it's ported to NRF52840 and EFM32J.
|
||||
|
||||
For development no hardware is needed, Solo also runs as a standalone application for Windows, Linux, and Mac OSX. If you like (or want to learn) hardware instead, you can run Solo on the NUCLEO-L432KC development board, or we make Solo for Hacker, an unlocked version of Solo that lets you customize its firmware.
|
||||
|
||||
@ -39,7 +33,7 @@ Solo is based on the STM32L432 microcontroller. It offers the following security
|
||||
|
||||
Solo for Hacker is a special version of Solo that let you customize its firmware, for example you can change the LED color, and even build advanced applications.
|
||||
|
||||
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.
|
||||
You can only buy Solo for Hacker at [solokeys.com](https://solokeys.com), as we don't sell it on Amazon and other places to avoid confusing customers. If you buy a Hacker, you can permanently lock it into a regular Solo, but viceversa you can NOT take a regular Solo and turn it a Hacker.
|
||||
|
||||
If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We only support Python3.
|
||||
|
||||
@ -54,22 +48,24 @@ cd ../..
|
||||
|
||||
make venv
|
||||
source venv/bin/activate
|
||||
solo program aux enter-bootloader
|
||||
solo program bootloader targets/stm32l432/solo.hex
|
||||
python tools/solotool.py program targets/stm32l432/solo.hex
|
||||
```
|
||||
|
||||
Alternatively, run `make docker-build` and use the firmware generated in `/tmp`.
|
||||
|
||||
If you forgot the `--recurse-submodules` when cloning, simply `git submodule update --init --recursive`.
|
||||
|
||||
For example, if you want to turn off any blue light emission, you can edit [`led_rgb()`](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/app.h#L48) and change `LED_INIT_VALUE`
|
||||
to be a different hex color.
|
||||
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/led.c#L15) and force:
|
||||
```
|
||||
uint32_t b = 0;
|
||||
```
|
||||
|
||||
Then recompile, load your new firmware, and enjoy a different LED color Solo.
|
||||
Then recompile, load your new firmware, and enjoy a blue-light-free version of Solo.
|
||||
|
||||
In the Hacker version, hardware is the same but the firmware is unlocked, so you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
|
||||
In the Hacker version, hardware is the same and firmware is unlocked, in the sense that you can 1) load an unsigned application, or 2) entirely reflash the key. By contrast, in a regular Solo you can only upgrade to a firmware signed by SoloKeys, and flash is locked and debug disabled permanently.
|
||||
|
||||
A frequently asked question is whether Solo for Hacker is less secure than regular Solo. The answer is certainly yes, and therefore we only recommend to use Solo for Hacker for development, experimentation, and fun. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
|
||||
|
||||
Hacker Solo isn't really secure so you should only use it for development. An attacker with physical access to a Solo for Hacker can reflash it following the steps above, and even a malware on your computer could possibly reflash it.
|
||||
|
||||
# Developing Solo (No Hardware Needed)
|
||||
|
||||
@ -86,7 +82,7 @@ This builds Solo as a standalone application. Solo application is set up to send
|
||||
Testing can be done using our fork of Yubico's client software, python-fido2. Our fork of python-fido2 has small changes to make it send USB HID over UDP to the authenticator application. You can install our fork by running the following:
|
||||
|
||||
```bash
|
||||
pip install -r tools/requirements.txt
|
||||
cd python-fido2 && python setup.py install
|
||||
```
|
||||
|
||||
Run the Solo application:
|
||||
@ -96,7 +92,12 @@ Run the Solo application:
|
||||
|
||||
In another shell, you can run client software, for example our tests:
|
||||
```bash
|
||||
python tools/ctap_test.py sim fido2
|
||||
python tools/ctap_test.py
|
||||
```
|
||||
|
||||
Or any client example such as:
|
||||
```bash
|
||||
python python-fido2/examples/credential.py
|
||||
```
|
||||
|
||||
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
|
||||
|
@ -1 +0,0 @@
|
||||
2.0.0
|
Submodule crypto/cifra deleted from d04dd31860
@ -55,11 +55,11 @@ If you use `DEBUG=2`, that means Solo will not boot until something starts readi
|
||||
it's debug messages. So it basically it waits to tether to a serial terminal so that you don't
|
||||
miss any debug messages.
|
||||
|
||||
We recommend using our `solo` tool as a serial emulator since it will automatically
|
||||
We recommend using our `solotool.py` as a serial emulator since it will automatically
|
||||
reconnect each time you program Solo.
|
||||
|
||||
```
|
||||
solo monitor <serial-port>
|
||||
python tools/solotool.py monitor <serial-port>
|
||||
```
|
||||
|
||||
#### Linux Users:
|
||||
@ -86,7 +86,7 @@ Programming `all.hex` will cause the device to permanently lock itself.
|
||||
It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
|
||||
Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
|
||||
|
||||
We recommend using our `solo` tool to manage programming. It is cross platform. First you must
|
||||
We recommend using our `solotool.py` to manage programming. It is cross platform. First you must
|
||||
install the prerequisites:
|
||||
|
||||
```
|
||||
@ -101,8 +101,7 @@ If your Solo device is already programmed (it flashes green when powered), we re
|
||||
programming it using the Solo bootloader.
|
||||
|
||||
```
|
||||
solo program aux enter-bootloader
|
||||
solo program bootloader solo.hex
|
||||
python tools/solotool.py program solo.hex
|
||||
```
|
||||
|
||||
Make sure to program `solo.hex` and not `all.hex`. Nothing bad would happen, but you'd
|
||||
@ -126,10 +125,7 @@ off and it enumerates as "STM BOOTLOADER".
|
||||
You can program it by running the following.
|
||||
|
||||
```
|
||||
solo program aux enter-bootloader
|
||||
solo program aux enter-dfu
|
||||
# powercycle key
|
||||
solo program dfu all.hex
|
||||
python tools/solotool.py program all.hex --use-dfu --detach
|
||||
```
|
||||
|
||||
Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
|
||||
@ -149,14 +145,14 @@ A locked Solo will only accept signed updates.
|
||||
If this is not a device with a hacker build, you can only program signed updates.
|
||||
|
||||
```
|
||||
solo program bootloader /path/to/firmware.json
|
||||
python tools/solotool.py program /path/to/firmware.json
|
||||
```
|
||||
|
||||
If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
|
||||
firmware by running the following command.
|
||||
|
||||
```
|
||||
solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
|
||||
python tools/solotool.py sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
|
||||
```
|
||||
|
||||
If your Solo isn't locked, you can always reprogram it using a debugger connected directly
|
||||
@ -179,5 +175,5 @@ If you'd like to also permanently disable signed updates, plug in your programme
|
||||
|
||||
```
|
||||
# WARNING: No more signed updates.
|
||||
solo program disable-bootloader
|
||||
python tools/programmer.py --disable
|
||||
```
|
||||
|
@ -1,31 +1,21 @@
|
||||
# Summary
|
||||
# tl;dr
|
||||
|
||||
On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
|
||||
|
||||
Create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory, for instance the following rule should cover normal access (it has to be on one line):
|
||||
Create [`/etc/udev/rules.d/99-solo.rules`](https://github.com/solokeys/solo/blob/master/99-solo.rules) and add the following (which assumes your user is in group `plugdev`):
|
||||
|
||||
```
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
|
||||
# Solo
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", GROUP="plugdev", SYMLINK+="solokey"
|
||||
|
||||
# U2F Zero
|
||||
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess", GROUP="plugdev", SYMLINK+="u2fzero"
|
||||
```
|
||||
|
||||
Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
|
||||
Then run
|
||||
|
||||
```
|
||||
sudo udevadm control --reload-rules && sudo udevadm trigger
|
||||
```
|
||||
|
||||
A simple way to setup both the udev rule and the udevadm reload is:
|
||||
|
||||
```
|
||||
git clone git@github.com:solokeys/solo.git
|
||||
cd solo/udev
|
||||
make setup
|
||||
```
|
||||
|
||||
We are working on getting user access to Solo keys enabled automatically in common Linux distributions: <https://github.com/solokeys/solo/issues/144>.
|
||||
|
||||
|
||||
|
||||
# How do udev rules work and why are they needed
|
||||
|
||||
In Linux, `udev` (part of `systemd`, read `man 7 udev`) handles "hot-pluggable" devices, of which Solo and U2F Zero are examples. In particular, it creates nodes in the `/dev` filesystem (in Linux, everything is a file), which allow accessing the device.
|
||||
|
30
fido2/apdu.h
30
fido2/apdu.h
@ -1,30 +0,0 @@
|
||||
#ifndef _APDU_H_
|
||||
#define _APDU_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t lc;
|
||||
} __attribute__((packed)) APDU_HEADER;
|
||||
|
||||
#define APDU_FIDO_U2F_REGISTER 0x01
|
||||
#define APDU_FIDO_U2F_AUTHENTICATE 0x02
|
||||
#define APDU_FIDO_U2F_VERSION 0x03
|
||||
#define APDU_FIDO_NFCCTAP_MSG 0x10
|
||||
#define APDU_INS_SELECT 0xA4
|
||||
#define APDU_INS_READ_BINARY 0xB0
|
||||
|
||||
#define SW_SUCCESS 0x9000
|
||||
#define SW_GET_RESPONSE 0x6100 // Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE.
|
||||
#define SW_WRONG_LENGTH 0x6700
|
||||
#define SW_COND_USE_NOT_SATISFIED 0x6985
|
||||
#define SW_FILE_NOT_FOUND 0x6a82
|
||||
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
|
||||
#define SW_INTERNAL_EXCEPTION 0x6f00
|
||||
|
||||
#endif //_APDU_H_
|
@ -60,7 +60,7 @@ 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 master_secret[32];
|
||||
|
||||
static uint8_t transport_secret[32];
|
||||
|
||||
@ -73,17 +73,13 @@ void crypto_sha256_init()
|
||||
|
||||
void crypto_reset_master_secret()
|
||||
{
|
||||
ctap_generate_rng(master_secret, 64);
|
||||
ctap_generate_rng(transport_secret, 32);
|
||||
ctap_generate_rng(master_secret, 32);
|
||||
}
|
||||
|
||||
void crypto_load_master_secret(uint8_t * key)
|
||||
{
|
||||
#if KEY_SPACE_BYTES < 96
|
||||
#error "need more key bytes"
|
||||
#endif
|
||||
memmove(master_secret, key, 64);
|
||||
memmove(transport_secret, key+64, 32);
|
||||
memmove(master_secret, key, 32);
|
||||
memmove(transport_secret, key+32, 32);
|
||||
}
|
||||
|
||||
void crypto_sha256_update(uint8_t * data, size_t len)
|
||||
@ -112,11 +108,6 @@ void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac)
|
||||
key = master_secret;
|
||||
klen = sizeof(master_secret);
|
||||
}
|
||||
else if (key == CRYPTO_TRANSPORT_KEY)
|
||||
{
|
||||
key = transport_secret;
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
if(klen > 64)
|
||||
{
|
||||
|
@ -19,10 +19,6 @@ void crypto_sha256_final(uint8_t * hash);
|
||||
void crypto_sha256_hmac_init(uint8_t * key, uint32_t klen, uint8_t * hmac);
|
||||
void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac);
|
||||
|
||||
void crypto_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);
|
||||
|
329
fido2/ctap.c
329
fido2/ctap.c
@ -25,6 +25,7 @@
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#define PIN_TOKEN_SIZE 16
|
||||
uint8_t PIN_TOKEN[PIN_TOKEN_SIZE];
|
||||
uint8_t KEY_AGREEMENT_PUB[64];
|
||||
static uint8_t KEY_AGREEMENT_PRIV[32];
|
||||
@ -33,8 +34,6 @@ static int8_t PIN_BOOT_ATTEMPTS_LEFT = PIN_BOOT_ATTEMPTS;
|
||||
|
||||
AuthenticatorState STATE;
|
||||
|
||||
static void ctap_reset_key_agreement();
|
||||
|
||||
static struct {
|
||||
CTAP_authDataHeader authData;
|
||||
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
||||
@ -68,8 +67,6 @@ uint8_t verify_pin_auth(uint8_t * pinAuth, uint8_t * clientDataHash)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
uint8_t ctap_get_info(CborEncoder * encoder)
|
||||
{
|
||||
int ret;
|
||||
@ -78,14 +75,16 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
||||
CborEncoder options;
|
||||
CborEncoder pins;
|
||||
|
||||
ret = cbor_encoder_create_map(encoder, &map, 6);
|
||||
const int number_of_versions = 2;
|
||||
|
||||
ret = cbor_encoder_create_map(encoder, &map, 5);
|
||||
check_ret(ret);
|
||||
{
|
||||
|
||||
ret = cbor_encode_uint(&map, RESP_versions); // versions key
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encoder_create_array(&map, &array, 2);
|
||||
ret = cbor_encoder_create_array(&map, &array, number_of_versions);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&array, "U2F_V2");
|
||||
@ -97,19 +96,6 @@ uint8_t ctap_get_info(CborEncoder * encoder)
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
ret = cbor_encode_uint(&map, RESP_extensions);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encoder_create_array(&map, &array, 1);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&array, "hmac-secret");
|
||||
check_ret(ret);
|
||||
}
|
||||
ret = cbor_encoder_close_container(&map, &array);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
ret = cbor_encode_uint(&map, RESP_aaguid);
|
||||
check_ret(ret);
|
||||
{
|
||||
@ -323,123 +309,18 @@ static int is_matching_rk(CTAP_residentKey * rk, CTAP_residentKey * rk2)
|
||||
(rk->user.id_size == rk2->user.id_size);
|
||||
}
|
||||
|
||||
static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf, unsigned int * ext_encoder_buf_size)
|
||||
{
|
||||
CborEncoder extensions;
|
||||
int ret;
|
||||
uint8_t output[64];
|
||||
uint8_t shared_secret[32];
|
||||
uint8_t hmac[32];
|
||||
uint8_t credRandom[32];
|
||||
|
||||
if (ext->hmac_secret_present == EXT_HMAC_SECRET_PARSED)
|
||||
{
|
||||
printf1(TAG_CTAP, "Processing hmac-secret..\r\n");
|
||||
|
||||
crypto_ecc256_shared_secret((uint8_t*) &ext->hmac_secret.keyAgreement.pubkey,
|
||||
KEY_AGREEMENT_PRIV,
|
||||
shared_secret);
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(shared_secret, 32);
|
||||
crypto_sha256_final(shared_secret);
|
||||
|
||||
crypto_sha256_hmac_init(shared_secret, 32, hmac);
|
||||
crypto_sha256_update(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
|
||||
crypto_sha256_hmac_final(shared_secret, 32, hmac);
|
||||
|
||||
if (memcmp(ext->hmac_secret.saltAuth, hmac, 16) == 0)
|
||||
{
|
||||
printf1(TAG_CTAP, "saltAuth is valid\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_CTAP, "saltAuth is invalid\r\n");
|
||||
return CTAP2_ERR_EXTENSION_FIRST;
|
||||
}
|
||||
|
||||
// Generate credRandom
|
||||
crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
|
||||
crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
|
||||
|
||||
// Decrypt saltEnc
|
||||
crypto_aes256_init(shared_secret, NULL);
|
||||
crypto_aes256_decrypt(ext->hmac_secret.saltEnc, ext->hmac_secret.saltLen);
|
||||
|
||||
// Generate outputs
|
||||
crypto_sha256_hmac_init(credRandom, 32, output);
|
||||
crypto_sha256_update(ext->hmac_secret.saltEnc, 32);
|
||||
crypto_sha256_hmac_final(credRandom, 32, output);
|
||||
|
||||
if (ext->hmac_secret.saltLen == 64)
|
||||
{
|
||||
crypto_sha256_hmac_init(credRandom, 32, output + 32);
|
||||
crypto_sha256_update(ext->hmac_secret.saltEnc + 32, 32);
|
||||
crypto_sha256_hmac_final(credRandom, 32, output + 32);
|
||||
}
|
||||
|
||||
// Encrypt for final output
|
||||
crypto_aes256_init(shared_secret, NULL);
|
||||
crypto_aes256_encrypt(output, ext->hmac_secret.saltLen);
|
||||
|
||||
// output
|
||||
printf1(TAG_GREEN, "have %d bytes for Extenstions encoder\r\n",*ext_encoder_buf_size);
|
||||
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
|
||||
{
|
||||
CborEncoder hmac_secret_map;
|
||||
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encode_byte_string(&hmac_secret_map, output, ext->hmac_secret.saltLen);
|
||||
check_ret(ret);
|
||||
}
|
||||
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
|
||||
check_ret(ret);
|
||||
}
|
||||
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
|
||||
}
|
||||
else if (ext->hmac_secret_present == EXT_HMAC_SECRET_REQUESTED)
|
||||
{
|
||||
cbor_encoder_init(&extensions, ext_encoder_buf, *ext_encoder_buf_size, 0);
|
||||
{
|
||||
CborEncoder hmac_secret_map;
|
||||
ret = cbor_encoder_create_map(&extensions, &hmac_secret_map, 1);
|
||||
check_ret(ret);
|
||||
{
|
||||
ret = cbor_encode_text_stringz(&hmac_secret_map, "hmac-secret");
|
||||
check_ret(ret);
|
||||
|
||||
ret = cbor_encode_boolean(&hmac_secret_map, 1);
|
||||
check_ret(ret);
|
||||
}
|
||||
ret = cbor_encoder_close_container(&extensions, &hmac_secret_map);
|
||||
check_ret(ret);
|
||||
}
|
||||
*ext_encoder_buf_size = cbor_encoder_get_buffer_size(&extensions, ext_encoder_buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
*ext_encoder_buf_size = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
|
||||
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, unsigned int len, CTAP_userEntity * user, uint8_t credtype, int32_t algtype, int32_t * sz, int store)
|
||||
{
|
||||
CborEncoder cose_key;
|
||||
|
||||
unsigned int auth_data_sz = sizeof(CTAP_authDataHeader);
|
||||
int auth_data_sz, ret;
|
||||
uint32_t count;
|
||||
CTAP_residentKey rk, rk2;
|
||||
CTAP_authData * authData = (CTAP_authData *)auth_data_buf;
|
||||
|
||||
uint8_t * cose_key_buf = auth_data_buf + sizeof(CTAP_authData);
|
||||
|
||||
if((sizeof(CTAP_authDataHeader)) > *len)
|
||||
if((sizeof(CTAP_authDataHeader)) > len)
|
||||
{
|
||||
printf1(TAG_ERR,"assertion fail, auth_data_buf must be at least %d bytes\n", sizeof(CTAP_authData) - sizeof(CTAP_attestHeader));
|
||||
exit(1);
|
||||
@ -455,12 +336,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
||||
count = auth_data_update_count(&authData->head);
|
||||
|
||||
device_set_status(CTAPHID_STATUS_UPNEEDED);
|
||||
// if NFC - not need to click a button
|
||||
int but = 1;
|
||||
if(!device_is_nfc())
|
||||
{
|
||||
but = ctap_user_presence_test();
|
||||
}
|
||||
int but = ctap_user_presence_test();
|
||||
|
||||
if (!but)
|
||||
{
|
||||
@ -476,12 +352,13 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
||||
authData->head.flags |= (ctap_is_pin_set() << 2);
|
||||
|
||||
|
||||
if (credInfo != NULL)
|
||||
|
||||
if (credtype != 0)
|
||||
{
|
||||
// add attestedCredentialData
|
||||
authData->head.flags |= (1 << 6);//include attestation data
|
||||
|
||||
cbor_encoder_init(&cose_key, cose_key_buf, *len - sizeof(CTAP_authData), 0);
|
||||
cbor_encoder_init(&cose_key, cose_key_buf, len - sizeof(CTAP_authData), 0);
|
||||
|
||||
memmove(authData->attest.aaguid, CTAP_AAGUID, 16);
|
||||
authData->attest.credLenL = sizeof(CredentialId) & 0x00FF;
|
||||
@ -499,10 +376,10 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
||||
make_auth_tag(authData->head.rpIdHash, authData->attest.id.nonce, count, authData->attest.id.tag);
|
||||
|
||||
// resident key
|
||||
if (credInfo->rk)
|
||||
if (store)
|
||||
{
|
||||
memmove(&rk.id, &authData->attest.id, sizeof(CredentialId));
|
||||
memmove(&rk.user, &credInfo->user, sizeof(CTAP_userEntity));
|
||||
memmove(&rk.user, user, sizeof(CTAP_userEntity));
|
||||
|
||||
unsigned int index = STATE.rk_stored;
|
||||
unsigned int i;
|
||||
@ -526,19 +403,29 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
|
||||
}
|
||||
done_rk:
|
||||
|
||||
// DELETE
|
||||
//crypto_aes256_init(CRYPTO_TRANSPORT_KEY, NULL);
|
||||
//crypto_aes256_encrypt((uint8_t*)&authData->attest.credential.user, CREDENTIAL_ENC_SIZE);
|
||||
printf1(TAG_GREEN, "MADE credId: "); dump_hex1(TAG_GREEN, (uint8_t*) &authData->attest.id, sizeof(CredentialId));
|
||||
|
||||
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credInfo->publicKeyCredentialType, credInfo->COSEAlgorithmIdentifier);
|
||||
ctap_generate_cose_key(&cose_key, (uint8_t*)&authData->attest.id, sizeof(CredentialId), credtype, algtype);
|
||||
|
||||
auth_data_sz = sizeof(CTAP_authData) + cbor_encoder_get_buffer_size(&cose_key, cose_key_buf);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
auth_data_sz = sizeof(CTAP_authDataHeader);
|
||||
}
|
||||
|
||||
{
|
||||
ret = cbor_encode_int(map,RESP_authData);
|
||||
check_ret(ret);
|
||||
ret = cbor_encode_byte_string(map, auth_data_buf, auth_data_sz);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
*len = auth_data_sz;
|
||||
if (sz) *sz = auth_data_sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -665,13 +552,12 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
||||
CTAP_makeCredential MC;
|
||||
int ret;
|
||||
unsigned int i;
|
||||
uint8_t auth_data_buf[310];
|
||||
uint8_t auth_data_buf[300];
|
||||
CTAP_credentialDescriptor * excl_cred = (CTAP_credentialDescriptor *) auth_data_buf;
|
||||
uint8_t * sigbuf = auth_data_buf + 32;
|
||||
uint8_t * sigder = auth_data_buf + 32 + 64;
|
||||
|
||||
ret = ctap_parse_make_credential(&MC,encoder,request,length);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
printf2(TAG_ERR,"error, parse_make_credential failed\n");
|
||||
@ -726,38 +612,19 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
|
||||
CborEncoder map;
|
||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||
check_ret(ret);
|
||||
uint32_t auth_data_sz = sizeof(auth_data_buf);
|
||||
int32_t auth_data_sz;
|
||||
|
||||
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, sizeof(auth_data_buf),
|
||||
&MC.user, MC.publicKeyCredentialType, MC.COSEAlgorithmIdentifier, &auth_data_sz, MC.rk);
|
||||
|
||||
ret = ctap_make_auth_data(&MC.rp, &map, auth_data_buf, &auth_data_sz,
|
||||
&MC.credInfo);
|
||||
check_retr(ret);
|
||||
|
||||
{
|
||||
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - auth_data_sz;
|
||||
uint8_t * ext_encoder_buf = auth_data_buf + auth_data_sz;
|
||||
|
||||
ret = ctap_make_extensions(&MC.extensions, ext_encoder_buf, &ext_encoder_buf_size);
|
||||
check_retr(ret);
|
||||
if (ext_encoder_buf_size)
|
||||
{
|
||||
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
|
||||
auth_data_sz += ext_encoder_buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret = cbor_encode_int(&map,RESP_authData);
|
||||
check_ret(ret);
|
||||
ret = cbor_encode_byte_string(&map, auth_data_buf, auth_data_sz);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
crypto_ecc256_load_attestation_key();
|
||||
int sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_sz, MC.clientDataHash, auth_data_buf, sigbuf, sigder);
|
||||
|
||||
printf1(TAG_MC,"der sig [%d]: ", sigder_sz); dump_hex1(TAG_MC, sigder, sigder_sz);
|
||||
|
||||
ret = ctap_add_attest_statement(&map, sigder, sigder_sz);
|
||||
@ -948,7 +815,7 @@ int ctap_filter_invalid_credentials(CTAP_getAssertion * GA)
|
||||
printf1(TAG_GA, "RK %d is a rpId match!\r\n", i);
|
||||
if (count == ALLOW_LIST_MAX_SIZE-1)
|
||||
{
|
||||
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d). Skipping.\r\n", count);
|
||||
printf2(TAG_ERR, "not enough ram allocated for matching RK's (%d)\r\n", count);
|
||||
break;
|
||||
}
|
||||
GA->creds[count].type = PUB_KEY_CRED_PUB_KEY;
|
||||
@ -977,7 +844,6 @@ static void save_credential_list(CTAP_authDataHeader * head, uint8_t * clientDat
|
||||
memmove(getAssertionState.clientDataHash, clientDataHash, CLIENT_DATA_HASH_SIZE);
|
||||
memmove(&getAssertionState.authData, head, sizeof(CTAP_authDataHeader));
|
||||
memmove(getAssertionState.creds, creds, sizeof(CTAP_credentialDescriptor) * (count));
|
||||
|
||||
}
|
||||
getAssertionState.count = count;
|
||||
printf1(TAG_GA,"saved %d credentials\n",count);
|
||||
@ -1042,6 +908,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
||||
CborEncoder map;
|
||||
CTAP_authDataHeader authData;
|
||||
memmove(&authData, &getAssertionState.authData, sizeof(CTAP_authDataHeader));
|
||||
// CTAP_authDataHeader * authData = &getAssertionState.authData;
|
||||
|
||||
CTAP_credentialDescriptor * cred = pop_credential();
|
||||
|
||||
@ -1064,7 +931,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
||||
ret = cbor_encoder_create_map(encoder, &map, 3);
|
||||
}
|
||||
|
||||
|
||||
check_ret(ret);
|
||||
printf1(TAG_RED, "RPID hash: "); dump_hex1(TAG_RED, authData.rpIdHash, 32);
|
||||
|
||||
@ -1075,7 +941,6 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
|
||||
// if only one account for this RP, null out the user details
|
||||
if (!getAssertionState.user_verified)
|
||||
{
|
||||
@ -1096,7 +961,7 @@ uint8_t ctap_get_next_assertion(CborEncoder * encoder)
|
||||
uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
{
|
||||
CTAP_getAssertion GA;
|
||||
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader) + 80];
|
||||
uint8_t auth_data_buf[sizeof(CTAP_authDataHeader)];
|
||||
int ret = ctap_parse_get_assertion(&GA,request,length);
|
||||
|
||||
if (ret != 0)
|
||||
@ -1105,15 +970,19 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (GA.pinAuthPresent)
|
||||
if (ctap_is_pin_set() && GA.pinAuthPresent == 0)
|
||||
{
|
||||
printf2(TAG_ERR,"pinAuth is required\n");
|
||||
return CTAP2_ERR_PIN_REQUIRED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctap_is_pin_set() || (GA.pinAuthPresent))
|
||||
{
|
||||
ret = verify_pin_auth(GA.pinAuth, GA.clientDataHash);
|
||||
check_retr(ret);
|
||||
getAssertionState.user_verified = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
getAssertionState.user_verified = 0;
|
||||
}
|
||||
|
||||
if (!GA.rp.size || !GA.clientDataHashPresent)
|
||||
@ -1138,15 +1007,47 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
map_size += 1;
|
||||
}
|
||||
|
||||
if (GA.extensions.hmac_secret_present == EXT_HMAC_SECRET_PARSED)
|
||||
{
|
||||
printf1(TAG_GA, "hmac-secret is present\r\n");
|
||||
}
|
||||
|
||||
ret = cbor_encoder_create_map(encoder, &map, map_size);
|
||||
check_ret(ret);
|
||||
|
||||
if (validCredCount == 0)
|
||||
#ifdef ENABLE_U2F_EXTENSIONS
|
||||
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
|
||||
{
|
||||
ret = cbor_encode_int(&map,RESP_authData);
|
||||
check_ret(ret);
|
||||
memset(auth_data_buf,0,sizeof(auth_data_buf));
|
||||
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(auth_data_buf));
|
||||
check_ret(ret);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, sizeof(auth_data_buf), NULL, 0,0,NULL, 0);
|
||||
check_retr(ret);
|
||||
}
|
||||
|
||||
/*for (int j = 0; j < GA.credLen; j++)*/
|
||||
/*{*/
|
||||
/*printf1(TAG_GA,"CRED ID (# %d): ", GA.creds[j].credential.enc.count);*/
|
||||
/*dump_hex1(TAG_GA, (uint8_t*)&GA.creds[j].credential, sizeof(struct Credential));*/
|
||||
/*if (ctap_authenticate_credential(&GA.rp, &GA.creds[j])) // warning encryption will break this*/
|
||||
/*{*/
|
||||
/*printf1(TAG_GA," Authenticated.\n");*/
|
||||
/*}*/
|
||||
/*else*/
|
||||
/*{*/
|
||||
/*printf1(TAG_GA," NOT authentic.\n");*/
|
||||
/*}*/
|
||||
/*}*/
|
||||
|
||||
// Decrypt here
|
||||
|
||||
//
|
||||
if (validCredCount > 0)
|
||||
{
|
||||
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
||||
}
|
||||
else
|
||||
{
|
||||
printf2(TAG_ERR,"Error, no authentic credential\n");
|
||||
return CTAP2_ERR_NO_CREDENTIALS;
|
||||
@ -1176,50 +1077,6 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
|
||||
|
||||
CTAP_credentialDescriptor * cred = &GA.creds[validCredCount - 1];
|
||||
|
||||
GA.extensions.hmac_secret.credential = &cred->credential;
|
||||
|
||||
#ifdef ENABLE_U2F_EXTENSIONS
|
||||
if ( is_extension_request((uint8_t*)&GA.creds[validCredCount - 1].credential.id, sizeof(CredentialId)) )
|
||||
{
|
||||
ret = cbor_encode_int(&map,RESP_authData);
|
||||
check_ret(ret);
|
||||
memset(auth_data_buf,0,sizeof(CTAP_authDataHeader));
|
||||
ret = cbor_encode_byte_string(&map, auth_data_buf, sizeof(CTAP_authDataHeader));
|
||||
check_ret(ret);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
uint32_t len = sizeof(auth_data_buf);
|
||||
ret = ctap_make_auth_data(&GA.rp, &map, auth_data_buf, &len, NULL);
|
||||
check_retr(ret);
|
||||
|
||||
((CTAP_authData *)auth_data_buf)->head.flags &= ~(1 << 2);
|
||||
((CTAP_authData *)auth_data_buf)->head.flags |= (getAssertionState.user_verified << 2);
|
||||
|
||||
{
|
||||
unsigned int ext_encoder_buf_size = sizeof(auth_data_buf) - len;
|
||||
uint8_t * ext_encoder_buf = auth_data_buf + len;
|
||||
|
||||
ret = ctap_make_extensions(&GA.extensions, ext_encoder_buf, &ext_encoder_buf_size);
|
||||
check_retr(ret);
|
||||
if (ext_encoder_buf_size)
|
||||
{
|
||||
((CTAP_authData *)auth_data_buf)->head.flags |= (1 << 7);
|
||||
len += ext_encoder_buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ret = cbor_encode_int(&map,RESP_authData);
|
||||
check_ret(ret);
|
||||
ret = cbor_encode_byte_string(&map, auth_data_buf, len);
|
||||
check_ret(ret);
|
||||
}
|
||||
}
|
||||
|
||||
save_credential_list((CTAP_authDataHeader*)auth_data_buf, GA.clientDataHash, GA.creds, validCredCount-1); // skip last one
|
||||
|
||||
ret = ctap_end_get_assertion(&map, cred, auth_data_buf, GA.clientDataHash, add_user_info);
|
||||
check_retr(ret);
|
||||
|
||||
@ -1325,7 +1182,7 @@ uint8_t ctap_update_pin_if_verified(uint8_t * pinEnc, int len, uint8_t * platfor
|
||||
crypto_aes256_decrypt(pinHashEnc, 16);
|
||||
if (memcmp(pinHashEnc, PIN_CODE_HASH, 16) != 0)
|
||||
{
|
||||
ctap_reset_key_agreement();
|
||||
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
|
||||
ctap_decrement_pin_attempts();
|
||||
if (ctap_device_boot_locked())
|
||||
{
|
||||
@ -1368,7 +1225,7 @@ uint8_t ctap_add_pin_if_verified(uint8_t * pinTokenEnc, uint8_t * platform_pubke
|
||||
printf2(TAG_ERR,"platform-pubkey: "); dump_hex1(TAG_ERR, platform_pubkey, 64);
|
||||
printf2(TAG_ERR,"device-pubkey: "); dump_hex1(TAG_ERR, KEY_AGREEMENT_PUB, 64);
|
||||
// Generate new keyAgreement pair
|
||||
ctap_reset_key_agreement();
|
||||
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
|
||||
ctap_decrement_pin_attempts();
|
||||
if (ctap_device_boot_locked())
|
||||
{
|
||||
@ -1393,7 +1250,6 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
|
||||
uint8_t pinTokenEnc[PIN_TOKEN_SIZE];
|
||||
int ret = ctap_parse_client_pin(&CP,request,length);
|
||||
|
||||
|
||||
switch(CP.subCommand)
|
||||
{
|
||||
case CP_cmdSetPin:
|
||||
@ -1730,16 +1586,12 @@ void ctap_init()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (! device_is_nfc())
|
||||
{
|
||||
ctap_reset_key_agreement();
|
||||
}
|
||||
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
|
||||
|
||||
#ifdef BRIDGE_TO_WALLET
|
||||
wallet_init();
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
uint8_t ctap_is_pin_set()
|
||||
@ -1930,10 +1782,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ctap_reset_key_agreement()
|
||||
{
|
||||
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
|
||||
}
|
||||
|
||||
|
||||
void ctap_reset()
|
||||
{
|
||||
@ -1950,7 +1799,7 @@ void ctap_reset()
|
||||
|
||||
ctap_reset_state();
|
||||
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
|
||||
ctap_reset_key_agreement();
|
||||
crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
|
||||
|
||||
crypto_reset_master_secret();
|
||||
}
|
||||
|
73
fido2/ctap.h
73
fido2/ctap.h
@ -54,13 +54,6 @@
|
||||
#define CP_getKeyAgreement 0x07
|
||||
#define CP_getRetries 0x08
|
||||
|
||||
#define EXT_HMAC_SECRET_COSE_KEY 0x01
|
||||
#define EXT_HMAC_SECRET_SALT_ENC 0x02
|
||||
#define EXT_HMAC_SECRET_SALT_AUTH 0x03
|
||||
|
||||
#define EXT_HMAC_SECRET_REQUESTED 0x01
|
||||
#define EXT_HMAC_SECRET_PARSED 0x02
|
||||
|
||||
#define RESP_versions 0x1
|
||||
#define RESP_extensions 0x2
|
||||
#define RESP_aaguid 0x3
|
||||
@ -149,13 +142,9 @@ struct Credential {
|
||||
CredentialId id;
|
||||
CTAP_userEntity user;
|
||||
};
|
||||
|
||||
typedef struct Credential CTAP_residentKey;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type;
|
||||
struct Credential credential;
|
||||
} CTAP_credentialDescriptor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -192,62 +181,34 @@ struct rpId
|
||||
uint8_t name[RP_NAME_LIMIT];
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct{
|
||||
uint8_t x[32];
|
||||
uint8_t y[32];
|
||||
} pubkey;
|
||||
|
||||
int kty;
|
||||
int crv;
|
||||
} COSE_key;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t saltLen;
|
||||
uint8_t saltEnc[64];
|
||||
uint8_t saltAuth[32];
|
||||
COSE_key keyAgreement;
|
||||
struct Credential * credential;
|
||||
} CTAP_hmac_secret;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t hmac_secret_present;
|
||||
CTAP_hmac_secret hmac_secret;
|
||||
} CTAP_extensions;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
CTAP_userEntity user;
|
||||
uint8_t publicKeyCredentialType;
|
||||
int32_t COSEAlgorithmIdentifier;
|
||||
uint8_t rk;
|
||||
} CTAP_credInfo;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t paramsParsed;
|
||||
uint8_t clientDataHash[CLIENT_DATA_HASH_SIZE];
|
||||
struct rpId rp;
|
||||
CTAP_userEntity user;
|
||||
|
||||
CTAP_credInfo credInfo;
|
||||
uint8_t publicKeyCredentialType;
|
||||
int32_t COSEAlgorithmIdentifier;
|
||||
|
||||
CborValue excludeList;
|
||||
size_t excludeListSize;
|
||||
|
||||
uint8_t rk;
|
||||
uint8_t uv;
|
||||
uint8_t up;
|
||||
|
||||
uint8_t pinAuth[16];
|
||||
uint8_t pinAuthPresent;
|
||||
int pinProtocol;
|
||||
CTAP_extensions extensions;
|
||||
|
||||
} CTAP_makeCredential;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t type;
|
||||
struct Credential credential;
|
||||
} CTAP_credentialDescriptor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@ -269,16 +230,22 @@ typedef struct
|
||||
|
||||
CTAP_credentialDescriptor creds[ALLOW_LIST_MAX_SIZE];
|
||||
uint8_t allowListPresent;
|
||||
|
||||
CTAP_extensions extensions;
|
||||
|
||||
} CTAP_getAssertion;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int pinProtocol;
|
||||
int subCommand;
|
||||
COSE_key keyAgreement;
|
||||
struct
|
||||
{
|
||||
struct{
|
||||
uint8_t x[32];
|
||||
uint8_t y[32];
|
||||
} pubkey;
|
||||
|
||||
int kty;
|
||||
int crv;
|
||||
} keyAgreement;
|
||||
uint8_t keyAgreementPresent;
|
||||
uint8_t pinAuth[16];
|
||||
uint8_t pinAuthPresent;
|
||||
|
@ -128,14 +128,14 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
||||
}
|
||||
|
||||
sz = USER_ID_MAX_SIZE;
|
||||
ret = cbor_value_copy_byte_string(&map, MC->credInfo.user.id, &sz, NULL);
|
||||
ret = cbor_value_copy_byte_string(&map, MC->user.id, &sz, NULL);
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, USER_ID is too large\n");
|
||||
return CTAP2_ERR_LIMIT_EXCEEDED;
|
||||
}
|
||||
MC->credInfo.user.id_size = sz;
|
||||
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->credInfo.user.id_size);
|
||||
MC->user.id_size = sz;
|
||||
printf1(TAG_GREEN,"parsed id_size: %d\r\n", MC->user.id_size);
|
||||
check_ret(ret);
|
||||
}
|
||||
else if (strcmp((const char *)key, "name") == 0)
|
||||
@ -146,12 +146,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
sz = USER_NAME_LIMIT;
|
||||
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.name, &sz, NULL);
|
||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.name, &sz, NULL);
|
||||
if (ret != CborErrorOutOfMemory)
|
||||
{ // Just truncate the name it's okay
|
||||
check_ret(ret);
|
||||
}
|
||||
MC->credInfo.user.name[USER_NAME_LIMIT - 1] = 0;
|
||||
MC->user.name[USER_NAME_LIMIT - 1] = 0;
|
||||
}
|
||||
else if (strcmp((const char *)key, "displayName") == 0)
|
||||
{
|
||||
@ -161,12 +161,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
sz = DISPLAY_NAME_LIMIT;
|
||||
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.displayName, &sz, NULL);
|
||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.displayName, &sz, NULL);
|
||||
if (ret != CborErrorOutOfMemory)
|
||||
{ // Just truncate the name it's okay
|
||||
check_ret(ret);
|
||||
}
|
||||
MC->credInfo.user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
|
||||
MC->user.displayName[DISPLAY_NAME_LIMIT - 1] = 0;
|
||||
}
|
||||
else if (strcmp((const char *)key, "icon") == 0)
|
||||
{
|
||||
@ -176,12 +176,12 @@ uint8_t parse_user(CTAP_makeCredential * MC, CborValue * val)
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
sz = ICON_LIMIT;
|
||||
ret = cbor_value_copy_text_string(&map, (char *)MC->credInfo.user.icon, &sz, NULL);
|
||||
ret = cbor_value_copy_text_string(&map, (char *)MC->user.icon, &sz, NULL);
|
||||
if (ret != CborErrorOutOfMemory)
|
||||
{ // Just truncate the name it's okay
|
||||
check_ret(ret);
|
||||
}
|
||||
MC->credInfo.user.icon[ICON_LIMIT - 1] = 0;
|
||||
MC->user.icon[ICON_LIMIT - 1] = 0;
|
||||
|
||||
}
|
||||
else
|
||||
@ -305,8 +305,8 @@ uint8_t parse_pub_key_cred_params(CTAP_makeCredential * MC, CborValue * val)
|
||||
{
|
||||
if (pub_key_cred_param_supported(cred_type, alg_type) == CREDENTIAL_IS_SUPPORTED)
|
||||
{
|
||||
MC->credInfo.publicKeyCredentialType = cred_type;
|
||||
MC->credInfo.COSEAlgorithmIdentifier = alg_type;
|
||||
MC->publicKeyCredentialType = cred_type;
|
||||
MC->COSEAlgorithmIdentifier = alg_type;
|
||||
MC->paramsParsed |= PARAM_pubKeyCredParams;
|
||||
return 0;
|
||||
}
|
||||
@ -521,7 +521,7 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
|
||||
|
||||
if (cbor_value_get_type(&map) != CborBooleanType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting bool type for option map value\n");
|
||||
printf2(TAG_ERR,"Error, expecting text string type for rp map value\n");
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
|
||||
@ -556,154 +556,6 @@ uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up)
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ctap_parse_hmac_secret(CborValue * val, CTAP_hmac_secret * hs)
|
||||
{
|
||||
size_t map_length;
|
||||
size_t salt_len;
|
||||
uint8_t parsed_count = 0;
|
||||
int key;
|
||||
int ret;
|
||||
unsigned int i;
|
||||
CborValue map;
|
||||
|
||||
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);
|
||||
|
||||
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 CborIntegerTypefor hmac-secret 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 EXT_HMAC_SECRET_COSE_KEY:
|
||||
ret = parse_cose_key(&map, &hs->keyAgreement);
|
||||
check_retr(ret);
|
||||
parsed_count++;
|
||||
break;
|
||||
case EXT_HMAC_SECRET_SALT_ENC:
|
||||
salt_len = 64;
|
||||
ret = cbor_value_copy_byte_string(&map, hs->saltEnc, &salt_len, NULL);
|
||||
if ((salt_len != 32 && salt_len != 64) || ret == CborErrorOutOfMemory)
|
||||
{
|
||||
return CTAP1_ERR_INVALID_LENGTH;
|
||||
}
|
||||
check_ret(ret);
|
||||
hs->saltLen = salt_len;
|
||||
parsed_count++;
|
||||
break;
|
||||
case EXT_HMAC_SECRET_SALT_AUTH:
|
||||
salt_len = 32;
|
||||
ret = cbor_value_copy_byte_string(&map, hs->saltAuth, &salt_len, NULL);
|
||||
check_ret(ret);
|
||||
parsed_count++;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
|
||||
if (parsed_count != 3)
|
||||
{
|
||||
printf2(TAG_ERR, "ctap_parse_hmac_secret missing parameter. Got %d.\r\n", parsed_count);
|
||||
return CTAP2_ERR_MISSING_PARAMETER;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint8_t ctap_parse_extensions(CborValue * val, CTAP_extensions * ext)
|
||||
{
|
||||
CborValue map;
|
||||
size_t sz, map_length;
|
||||
char key[16];
|
||||
int ret;
|
||||
unsigned int i;
|
||||
bool b;
|
||||
|
||||
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);
|
||||
|
||||
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) != CborTextStringType)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, expecting text string type for options map key, got %s\n", cbor_value_get_type_string(&map));
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
sz = sizeof(key);
|
||||
ret = cbor_value_copy_text_string(&map, key, &sz, NULL);
|
||||
|
||||
if (ret == CborErrorOutOfMemory)
|
||||
{
|
||||
printf2(TAG_ERR,"Error, rp map key is too large. Ignoring.\n");
|
||||
cbor_value_advance(&map);
|
||||
cbor_value_advance(&map);
|
||||
continue;
|
||||
}
|
||||
check_ret(ret);
|
||||
key[sizeof(key) - 1] = 0;
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
|
||||
|
||||
if (strncmp(key, "hmac-secret",11) == 0)
|
||||
{
|
||||
if (cbor_value_get_type(&map) == CborBooleanType)
|
||||
{
|
||||
ret = cbor_value_get_boolean(&map, &b);
|
||||
check_ret(ret);
|
||||
if (b) ext->hmac_secret_present = EXT_HMAC_SECRET_REQUESTED;
|
||||
printf1(TAG_CTAP, "set hmac_secret_present to %d\r\n", b);
|
||||
}
|
||||
else if (cbor_value_get_type(&map) == CborMapType)
|
||||
{
|
||||
ret = ctap_parse_hmac_secret(&map, &ext->hmac_secret);
|
||||
check_retr(ret);
|
||||
ext->hmac_secret_present = EXT_HMAC_SECRET_PARSED;
|
||||
printf1(TAG_CTAP, "parsed hmac_secret request\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_RED, "warning: hmac_secret request ignored for being wrong type\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length)
|
||||
{
|
||||
int ret;
|
||||
@ -779,8 +631,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
||||
|
||||
ret = parse_user(MC, &map);
|
||||
|
||||
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->credInfo.user.id, MC->credInfo.user.id_size);
|
||||
printf1(TAG_MC," name: %s\n", MC->credInfo.user.name);
|
||||
printf1(TAG_MC," ID: "); dump_hex1(TAG_MC, MC->user.id, MC->user.id_size);
|
||||
printf1(TAG_MC," name: %s\n", MC->user.name);
|
||||
|
||||
break;
|
||||
case MC_pubKeyCredParams:
|
||||
@ -788,8 +640,8 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
||||
|
||||
ret = parse_pub_key_cred_params(MC, &map);
|
||||
|
||||
printf1(TAG_MC," cred_type: 0x%02x\n", MC->credInfo.publicKeyCredentialType);
|
||||
printf1(TAG_MC," alg_type: %d\n", MC->credInfo.COSEAlgorithmIdentifier);
|
||||
printf1(TAG_MC," cred_type: 0x%02x\n", MC->publicKeyCredentialType);
|
||||
printf1(TAG_MC," alg_type: %d\n", MC->COSEAlgorithmIdentifier);
|
||||
|
||||
break;
|
||||
case MC_excludeList:
|
||||
@ -813,13 +665,11 @@ uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encod
|
||||
{
|
||||
return CTAP2_ERR_INVALID_CBOR_TYPE;
|
||||
}
|
||||
ret = ctap_parse_extensions(&map, &MC->extensions);
|
||||
check_retr(ret);
|
||||
break;
|
||||
|
||||
case MC_options:
|
||||
printf1(TAG_MC,"CTAP_options\n");
|
||||
ret = parse_options(&map, &MC->credInfo.rk, &MC->uv, &MC->up);
|
||||
ret = parse_options(&map, &MC->rk, &MC->uv, &MC->up);
|
||||
check_retr(ret);
|
||||
break;
|
||||
case MC_pinAuth:
|
||||
@ -1036,8 +886,6 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
||||
break;
|
||||
case GA_extensions:
|
||||
printf1(TAG_GA,"GA_extensions\n");
|
||||
ret = ctap_parse_extensions(&map, &GA->extensions);
|
||||
check_retr(ret);
|
||||
break;
|
||||
|
||||
case GA_options:
|
||||
@ -1092,15 +940,15 @@ uint8_t ctap_parse_get_assertion(CTAP_getAssertion * GA, uint8_t * request, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv)
|
||||
{
|
||||
CborValue map;
|
||||
size_t map_length;
|
||||
int ret,key;
|
||||
unsigned int i;
|
||||
int xkey = 0,ykey = 0;
|
||||
cose->kty = 0;
|
||||
cose->crv = 0;
|
||||
*kty = 0;
|
||||
*crv = 0;
|
||||
|
||||
|
||||
CborType type = cbor_value_get_type(it);
|
||||
@ -1138,7 +986,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_KTY\n");
|
||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||
{
|
||||
ret = cbor_value_get_int_checked(&map, &cose->kty);
|
||||
ret = cbor_value_get_int_checked(&map, kty);
|
||||
check_ret(ret);
|
||||
}
|
||||
else
|
||||
@ -1153,7 +1001,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_CRV\n");
|
||||
if (cbor_value_get_type(&map) == CborIntegerType)
|
||||
{
|
||||
ret = cbor_value_get_int_checked(&map, &cose->crv);
|
||||
ret = cbor_value_get_int_checked(&map, crv);
|
||||
check_ret(ret);
|
||||
}
|
||||
else
|
||||
@ -1163,14 +1011,14 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||
break;
|
||||
case COSE_KEY_LABEL_X:
|
||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_X\n");
|
||||
ret = parse_fixed_byte_string(&map, cose->pubkey.x, 32);
|
||||
ret = parse_fixed_byte_string(&map, x, 32);
|
||||
check_retr(ret);
|
||||
xkey = 1;
|
||||
|
||||
break;
|
||||
case COSE_KEY_LABEL_Y:
|
||||
printf1(TAG_PARSE,"COSE_KEY_LABEL_Y\n");
|
||||
ret = parse_fixed_byte_string(&map, cose->pubkey.y, 32);
|
||||
ret = parse_fixed_byte_string(&map, y, 32);
|
||||
check_retr(ret);
|
||||
ykey = 1;
|
||||
|
||||
@ -1182,7 +1030,7 @@ uint8_t parse_cose_key(CborValue * it, COSE_key * cose)
|
||||
ret = cbor_value_advance(&map);
|
||||
check_ret(ret);
|
||||
}
|
||||
if (xkey == 0 || ykey == 0 || cose->kty == 0 || cose->crv == 0)
|
||||
if (xkey == 0 || ykey == 0 || *kty == 0 || *crv == 0)
|
||||
{
|
||||
return CTAP2_ERR_MISSING_PARAMETER;
|
||||
}
|
||||
@ -1262,7 +1110,7 @@ uint8_t ctap_parse_client_pin(CTAP_clientPin * CP, uint8_t * request, int length
|
||||
break;
|
||||
case CP_keyAgreement:
|
||||
printf1(TAG_CP,"CP_keyAgreement\n");
|
||||
ret = parse_cose_key(&map, &CP->keyAgreement);
|
||||
ret = parse_cose_key(&map, CP->keyAgreement.pubkey.x, CP->keyAgreement.pubkey.y, &CP->keyAgreement.kty, &CP->keyAgreement.crv);
|
||||
check_retr(ret);
|
||||
CP->keyAgreementPresent = 1;
|
||||
break;
|
||||
|
@ -30,7 +30,7 @@ uint8_t parse_rp(struct rpId * rp, CborValue * val);
|
||||
uint8_t parse_options(CborValue * val, uint8_t * rk, uint8_t * uv, uint8_t * up);
|
||||
|
||||
uint8_t parse_allow_list(CTAP_getAssertion * GA, CborValue * it);
|
||||
uint8_t parse_cose_key(CborValue * it, COSE_key * cose);
|
||||
uint8_t parse_cose_key(CborValue * it, uint8_t * x, uint8_t * y, int * kty, int * crv);
|
||||
|
||||
|
||||
uint8_t ctap_parse_make_credential(CTAP_makeCredential * MC, CborEncoder * encoder, uint8_t * request, int length);
|
||||
|
159
fido2/ctaphid.c
159
fido2/ctaphid.c
@ -16,12 +16,6 @@
|
||||
#include "util.h"
|
||||
#include "log.h"
|
||||
#include "extensions.h"
|
||||
|
||||
// move custom SHA512 command out,
|
||||
// and the following headers too
|
||||
#include "sha2.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#include APP_CONFIG
|
||||
|
||||
typedef enum
|
||||
@ -534,10 +528,6 @@ static int ctaphid_buffer_packet(uint8_t * pkt_raw, uint8_t * cmd, uint32_t * ci
|
||||
return buffer_status();
|
||||
}
|
||||
|
||||
extern void _check_ret(CborError ret, int line, const char * filename);
|
||||
#define check_hardcore(r) _check_ret(r,__LINE__, __FILE__);\
|
||||
if ((r) != CborNoError) exit(1);
|
||||
|
||||
uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
{
|
||||
uint8_t cmd;
|
||||
@ -728,155 +718,6 @@ uint8_t ctaphid_handle_packet(uint8_t * pkt_raw)
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
#endif
|
||||
#if defined(SOLO_HACKER) && (DEBUG_LEVEL > 0) && (!IS_BOOTLOADER == 1)
|
||||
case CTAPHID_PROBE:
|
||||
|
||||
/*
|
||||
* Expects CBOR-serialized data of the form
|
||||
* {"subcommand": "hash_type", "data": b"the_data"}
|
||||
* with hash_type in SHA256, SHA512
|
||||
*/
|
||||
|
||||
// some random logging
|
||||
printf1(TAG_HID,"CTAPHID_PROBE\n");
|
||||
// initialise CTAP response object
|
||||
ctap_response_init(&ctap_resp);
|
||||
// initialise write buffer
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_PROBE;
|
||||
|
||||
// prepare parsing (or halt)
|
||||
int ret;
|
||||
CborParser parser;
|
||||
CborValue it, map;
|
||||
ret = cbor_parser_init(
|
||||
ctap_buffer, (size_t) buffer_len(),
|
||||
// strictly speaking, CTAP is not RFC canonical...
|
||||
CborValidateCanonicalFormat,
|
||||
&parser, &it);
|
||||
check_hardcore(ret);
|
||||
|
||||
CborType type = cbor_value_get_type(&it);
|
||||
if (type != CborMapType) exit(1);
|
||||
|
||||
ret = cbor_value_enter_container(&it,&map);
|
||||
check_hardcore(ret);
|
||||
|
||||
size_t map_length = 0;
|
||||
ret = cbor_value_get_map_length(&it, &map_length);
|
||||
if (map_length != 2) exit(1);
|
||||
|
||||
// parse subcommand (or halt)
|
||||
CborValue val;
|
||||
ret = cbor_value_map_find_value(&it, "subcommand", &val);
|
||||
check_hardcore(ret);
|
||||
if (!cbor_value_is_text_string(&val))
|
||||
exit(1);
|
||||
|
||||
int sha_version = 0;
|
||||
bool found = false;
|
||||
if (!found) {
|
||||
ret = cbor_value_text_string_equals(
|
||||
&val, "SHA256", &found);
|
||||
check_hardcore(ret);
|
||||
if (found)
|
||||
sha_version = 256;
|
||||
}
|
||||
if (!found) {
|
||||
ret = cbor_value_text_string_equals(
|
||||
&val, "SHA512", &found);
|
||||
check_hardcore(ret);
|
||||
if (found)
|
||||
sha_version = 512;
|
||||
}
|
||||
if (sha_version == 0)
|
||||
exit(1);
|
||||
|
||||
// parse data (or halt)
|
||||
ret = cbor_value_map_find_value(&it, "data", &val);
|
||||
check_hardcore(ret);
|
||||
if (!cbor_value_is_byte_string(&val))
|
||||
exit(1);
|
||||
|
||||
size_t data_length = 0;
|
||||
ret = cbor_value_calculate_string_length(&val, &data_length);
|
||||
check_hardcore(ret);
|
||||
if (data_length > 6*1024)
|
||||
exit(1);
|
||||
|
||||
unsigned char data[6*1024];
|
||||
ret = cbor_value_copy_byte_string (
|
||||
&val, &data[0], &data_length, &val);
|
||||
check_hardcore(ret);
|
||||
|
||||
// execute subcommand
|
||||
if (sha_version == 256) {
|
||||
// calculate hash
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(data, data_length);
|
||||
crypto_sha256_final(ctap_buffer);
|
||||
// write output
|
||||
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
|
||||
}
|
||||
|
||||
if (sha_version == 512) {
|
||||
// calculate hash
|
||||
crypto_sha512_init();
|
||||
crypto_sha512_update(data, data_length);
|
||||
crypto_sha512_final(ctap_buffer);
|
||||
// write output
|
||||
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
|
||||
}
|
||||
|
||||
// finalize
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
|
||||
/*
|
||||
case CTAPHID_SHA256:
|
||||
// some random logging
|
||||
printf1(TAG_HID,"CTAPHID_SHA256\n");
|
||||
// initialise CTAP response object
|
||||
ctap_response_init(&ctap_resp);
|
||||
// initialise write buffer
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_SHA256;
|
||||
wb.bcnt = CF_SHA256_HASHSZ; // 32 bytes
|
||||
// calculate hash
|
||||
crypto_sha256_init();
|
||||
crypto_sha256_update(ctap_buffer, buffer_len());
|
||||
crypto_sha256_final(ctap_buffer);
|
||||
// copy to output
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA256_HASHSZ);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
case CTAPHID_SHA512:
|
||||
// some random logging
|
||||
printf1(TAG_HID,"CTAPHID_SHA512\n");
|
||||
// initialise CTAP response object
|
||||
ctap_response_init(&ctap_resp);
|
||||
// initialise write buffer
|
||||
ctaphid_write_buffer_init(&wb);
|
||||
wb.cid = cid;
|
||||
wb.cmd = CTAPHID_SHA512;
|
||||
wb.bcnt = CF_SHA512_HASHSZ; // 64 bytes
|
||||
// calculate hash
|
||||
crypto_sha512_init();
|
||||
crypto_sha512_update(ctap_buffer, buffer_len());
|
||||
crypto_sha512_final(ctap_buffer);
|
||||
// copy to output
|
||||
ctaphid_write(&wb, &ctap_buffer, CF_SHA512_HASHSZ);
|
||||
ctaphid_write(&wb, NULL, 0);
|
||||
is_busy = 0;
|
||||
break;
|
||||
*/
|
||||
#endif
|
||||
default:
|
||||
printf2(TAG_ERR,"error, unimplemented HID cmd: %02x\r\n", buffer_cmd());
|
||||
|
@ -28,8 +28,6 @@
|
||||
#define CTAPHID_ENTERBOOT (TYPE_INIT | 0x51)
|
||||
#define CTAPHID_ENTERSTBOOT (TYPE_INIT | 0x52)
|
||||
#define CTAPHID_GETRNG (TYPE_INIT | 0x60)
|
||||
// reserved for debug, not implemented except for HACKER and DEBUG_LEVEl > 0
|
||||
#define CTAPHID_PROBE (TYPE_INIT | 0x70)
|
||||
|
||||
#define ERR_INVALID_CMD 0x01
|
||||
#define ERR_INVALID_PAR 0x02
|
||||
|
@ -86,22 +86,5 @@ void boot_st_bootloader();
|
||||
// HID wink command
|
||||
void device_wink();
|
||||
|
||||
typedef enum {
|
||||
DEVICE_LOW_POWER_IDLE = 0,
|
||||
DEVICE_LOW_POWER_FAST = 1,
|
||||
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.
|
||||
void device_set_clock_rate(DEVICE_CLOCK_RATE param);
|
||||
|
||||
// Returns 1 if operating in NFC mode.
|
||||
// 0 otherwise.
|
||||
bool device_is_nfc();
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -91,10 +91,10 @@ int16_t extend_fido2(CredentialId * credid, uint8_t * output)
|
||||
}
|
||||
}
|
||||
|
||||
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len)
|
||||
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len)
|
||||
{
|
||||
|
||||
struct u2f_authenticate_request * auth = (struct u2f_authenticate_request *) payload;
|
||||
struct u2f_authenticate_request * auth = (struct u2f_authenticate_request *) req->payload;
|
||||
uint16_t rcode;
|
||||
|
||||
if (req->ins == U2F_AUTHENTICATE)
|
||||
@ -118,7 +118,7 @@ int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len)
|
||||
{
|
||||
if ( ! is_extension_request((uint8_t *) &auth->kh, auth->khl)) // Pin requests
|
||||
{
|
||||
rcode = U2F_SW_WRONG_DATA;
|
||||
rcode = U2F_SW_WRONG_PAYLOAD;
|
||||
printf1(TAG_EXT, "Ignoring U2F auth request\n");
|
||||
dump_hex1(TAG_EXT, (uint8_t *) &auth->kh, auth->khl);
|
||||
goto end;
|
||||
|
@ -7,9 +7,8 @@
|
||||
#ifndef EXTENSIONS_H_
|
||||
#define EXTENSIONS_H_
|
||||
#include "u2f.h"
|
||||
#include "apdu.h"
|
||||
|
||||
int16_t extend_u2f(APDU_HEADER * req, uint8_t * payload, uint32_t len);
|
||||
int16_t extend_u2f(struct u2f_request_apdu* req, uint32_t len);
|
||||
|
||||
int16_t extend_fido2(CredentialId * credid, uint8_t * output);
|
||||
|
||||
|
@ -48,8 +48,6 @@ struct logtag tagtable[] = {
|
||||
{TAG_STOR,"[1;35mSTOR[0m"},
|
||||
{TAG_BOOT,"[1;36mBOOT[0m"},
|
||||
{TAG_EXT,"[1;37mEXT[0m"},
|
||||
{TAG_NFC,"[1;38mNFC[0m"},
|
||||
{TAG_NFC_APDU, "NAPDU"},
|
||||
};
|
||||
|
||||
|
||||
@ -70,7 +68,7 @@ void LOG(uint32_t tag, const char * filename, int num, const char * fmt, ...)
|
||||
{
|
||||
if (tag & tagtable[i].tagn)
|
||||
{
|
||||
if (tagtable[i].tag[0] && !(tag & TAG_NO_TAG)) printf("[%s] ", tagtable[i].tag);
|
||||
if (tagtable[i].tag[0]) printf("[%s] ", tagtable[i].tag);
|
||||
i = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -42,11 +42,8 @@ typedef enum
|
||||
TAG_DUMP2 = (1 << 16),
|
||||
TAG_BOOT = (1 << 17),
|
||||
TAG_EXT = (1 << 18),
|
||||
TAG_NFC = (1 << 19),
|
||||
TAG_NFC_APDU = (1 << 20),
|
||||
|
||||
TAG_NO_TAG = (1UL << 30),
|
||||
TAG_FILENO = (1UL << 31)
|
||||
TAG_FILENO = (1u << 31)
|
||||
} LOG_TAG;
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
|
18
fido2/main.c
18
fido2/main.c
@ -29,14 +29,12 @@ int main()
|
||||
// TAG_GEN|
|
||||
// TAG_MC |
|
||||
// TAG_GA |
|
||||
//TAG_WALLET |
|
||||
TAG_WALLET |
|
||||
TAG_STOR |
|
||||
//TAG_NFC_APDU |
|
||||
TAG_NFC |
|
||||
// TAG_CP |
|
||||
// TAG_CTAP|
|
||||
// TAG_HID|
|
||||
//TAG_U2F|
|
||||
/*TAG_U2F|*/
|
||||
// TAG_PARSE |
|
||||
// TAG_TIME|
|
||||
// TAG_DUMP|
|
||||
@ -46,12 +44,21 @@ int main()
|
||||
);
|
||||
|
||||
device_init();
|
||||
printf1(TAG_GEN,"init device\n");
|
||||
|
||||
usbhid_init();
|
||||
printf1(TAG_GEN,"init usb\n");
|
||||
|
||||
|
||||
ctaphid_init();
|
||||
printf1(TAG_GEN,"init ctaphid\n");
|
||||
|
||||
ctap_init();
|
||||
printf1(TAG_GEN,"init ctap\n");
|
||||
|
||||
memset(hidmsg,0,sizeof(hidmsg));
|
||||
|
||||
// printf1(TAG_GEN,"recv'ing hid msg \n");
|
||||
printf1(TAG_GEN,"recv'ing hid msg \n");
|
||||
|
||||
|
||||
while(1)
|
||||
@ -73,7 +80,6 @@ int main()
|
||||
{
|
||||
}
|
||||
ctaphid_check_timeouts();
|
||||
|
||||
}
|
||||
|
||||
// Should never get here
|
||||
|
48
fido2/u2f.c
48
fido2/u2f.c
@ -10,7 +10,6 @@
|
||||
#include "crypto.h"
|
||||
#include "log.h"
|
||||
#include "device.h"
|
||||
#include "apdu.h"
|
||||
#include "wallet.h"
|
||||
#ifdef ENABLE_U2F_EXTENSIONS
|
||||
#include "extensions.h"
|
||||
@ -28,12 +27,12 @@ void u2f_reset_response();
|
||||
|
||||
static CTAP_RESPONSE * _u2f_resp = NULL;
|
||||
|
||||
void u2f_request_ex(APDU_HEADER *req, uint8_t *payload, uint32_t len, CTAP_RESPONSE * resp)
|
||||
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
||||
{
|
||||
uint16_t rcode = 0;
|
||||
uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
|
||||
uint8_t byte;
|
||||
|
||||
ctap_response_init(resp);
|
||||
u2f_set_writeback_buffer(resp);
|
||||
|
||||
if (req->cla != 0)
|
||||
@ -43,7 +42,7 @@ void u2f_request_ex(APDU_HEADER *req, uint8_t *payload, uint32_t len, CTAP_RESPO
|
||||
goto end;
|
||||
}
|
||||
#ifdef ENABLE_U2F_EXTENSIONS
|
||||
rcode = extend_u2f(req, payload, len);
|
||||
rcode = extend_u2f(req, len);
|
||||
#endif
|
||||
if (rcode != U2F_SW_NO_ERROR && rcode != U2F_SW_CONDITIONS_NOT_SATISFIED) // If the extension didn't do anything...
|
||||
{
|
||||
@ -60,7 +59,7 @@ void u2f_request_ex(APDU_HEADER *req, uint8_t *payload, uint32_t len, CTAP_RESPO
|
||||
{
|
||||
|
||||
timestamp();
|
||||
rcode = u2f_register((struct u2f_register_request*)payload);
|
||||
rcode = u2f_register((struct u2f_register_request*)req->payload);
|
||||
printf1(TAG_TIME,"u2f_register time: %d ms\n", timestamp());
|
||||
|
||||
}
|
||||
@ -68,7 +67,7 @@ void u2f_request_ex(APDU_HEADER *req, uint8_t *payload, uint32_t len, CTAP_RESPO
|
||||
case U2F_AUTHENTICATE:
|
||||
printf1(TAG_U2F, "U2F_AUTHENTICATE\n");
|
||||
timestamp();
|
||||
rcode = u2f_authenticate((struct u2f_authenticate_request*)payload, req->p1);
|
||||
rcode = u2f_authenticate((struct u2f_authenticate_request*)req->payload, req->p1);
|
||||
printf1(TAG_TIME,"u2f_authenticate time: %d ms\n", timestamp());
|
||||
break;
|
||||
case U2F_VERSION:
|
||||
@ -110,22 +109,6 @@ end:
|
||||
printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length);
|
||||
}
|
||||
|
||||
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp)
|
||||
{
|
||||
if (len < 5 || !req)
|
||||
return;
|
||||
|
||||
uint32_t alen = req[4];
|
||||
|
||||
u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp);
|
||||
}
|
||||
|
||||
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
|
||||
{
|
||||
uint32_t len = ((req->LC3) | ((uint32_t)req->LC2 << 8) | ((uint32_t)req->LC1 << 16));
|
||||
|
||||
u2f_request_ex((APDU_HEADER *)req, req->payload, len, resp);
|
||||
}
|
||||
|
||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len)
|
||||
{
|
||||
@ -173,7 +156,7 @@ static void u2f_make_auth_tag(struct u2f_key_handle * kh, uint8_t * appid, uint8
|
||||
memmove(tag, hashbuf, CREDENTIAL_TAG_SIZE);
|
||||
}
|
||||
|
||||
int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pubkey)
|
||||
static int8_t u2f_new_keypair(struct u2f_key_handle * kh, uint8_t * appid, uint8_t * pubkey)
|
||||
{
|
||||
ctap_generate_rng(kh->key, U2F_KEY_HANDLE_KEY_SIZE);
|
||||
u2f_make_auth_tag(kh, appid, kh->tag);
|
||||
@ -213,7 +196,6 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
|
||||
if (control == U2F_AUTHENTICATE_CHECK)
|
||||
{
|
||||
printf1(TAG_U2F, "CHECK-ONLY\r\n");
|
||||
if (u2f_appid_eq(&req->kh, req->app) == 0)
|
||||
{
|
||||
return U2F_SW_CONDITIONS_NOT_SATISFIED;
|
||||
@ -224,30 +206,25 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
}
|
||||
}
|
||||
if (
|
||||
(control != U2F_AUTHENTICATE_SIGN && control != U2F_AUTHENTICATE_SIGN_NO_USER) ||
|
||||
control != U2F_AUTHENTICATE_SIGN ||
|
||||
req->khl != U2F_KEY_HANDLE_SIZE ||
|
||||
u2f_appid_eq(&req->kh, req->app) != 0 || // Order of checks is important
|
||||
u2f_load_key(&req->kh, req->app) != 0
|
||||
|
||||
)
|
||||
{
|
||||
return U2F_SW_WRONG_DATA;
|
||||
return U2F_SW_WRONG_PAYLOAD;
|
||||
}
|
||||
|
||||
// dont-enforce-user-presence-and-sign
|
||||
if (control == U2F_AUTHENTICATE_SIGN_NO_USER)
|
||||
up = 0;
|
||||
|
||||
if(!device_is_nfc() && up)
|
||||
{
|
||||
|
||||
if (ctap_user_presence_test() == 0)
|
||||
{
|
||||
return U2F_SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
}
|
||||
|
||||
count = ctap_atomic_count(0);
|
||||
hash[0] = (count >> 24) & 0xff;
|
||||
hash[0] = 0xff;
|
||||
hash[1] = (count >> 16) & 0xff;
|
||||
hash[2] = (count >> 8) & 0xff;
|
||||
hash[3] = (count >> 0) & 0xff;
|
||||
@ -264,7 +241,7 @@ static int16_t u2f_authenticate(struct u2f_authenticate_request * req, uint8_t c
|
||||
crypto_ecc256_sign(hash, 32, sig);
|
||||
|
||||
u2f_response_writeback(&up,1);
|
||||
hash[0] = (count >> 24) & 0xff;
|
||||
hash[0] = 0xff;
|
||||
hash[1] = (count >> 16) & 0xff;
|
||||
hash[2] = (count >> 8) & 0xff;
|
||||
hash[3] = (count >> 0) & 0xff;
|
||||
@ -286,13 +263,10 @@ static int16_t u2f_register(struct u2f_register_request * req)
|
||||
|
||||
const uint16_t attest_size = attestation_cert_der_size;
|
||||
|
||||
if(!device_is_nfc())
|
||||
{
|
||||
if ( ! ctap_user_presence_test())
|
||||
{
|
||||
return U2F_SW_CONDITIONS_NOT_SATISFIED;
|
||||
}
|
||||
}
|
||||
|
||||
if ( u2f_new_keypair(&key_handle, req->app, pubkey) == -1)
|
||||
{
|
||||
|
@ -38,16 +38,16 @@
|
||||
// U2F Authenticate
|
||||
#define U2F_AUTHENTICATE_CHECK 0x7
|
||||
#define U2F_AUTHENTICATE_SIGN 0x3
|
||||
#define U2F_AUTHENTICATE_SIGN_NO_USER 0x8
|
||||
|
||||
|
||||
// Command status responses
|
||||
#define U2F_SW_NO_ERROR 0x9000
|
||||
#define U2F_SW_WRONG_DATA 0x6984
|
||||
#define U2F_SW_CONDITIONS_NOT_SATISFIED 0x6985
|
||||
#define U2F_SW_INS_NOT_SUPPORTED 0x6d00
|
||||
#define U2F_SW_WRONG_LENGTH 0x6700
|
||||
#define U2F_SW_CLASS_NOT_SUPPORTED 0x6E00
|
||||
#define U2F_SW_WRONG_DATA 0x6a80
|
||||
#define U2F_SW_WRONG_PAYLOAD 0x6a80
|
||||
#define U2F_SW_INSUFFICIENT_MEMORY 0x9210
|
||||
|
||||
// Delay in milliseconds to wait for user input
|
||||
@ -98,11 +98,6 @@ struct u2f_authenticate_request
|
||||
// @req U2F message
|
||||
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp);
|
||||
|
||||
// u2f_request send a U2F message to NFC protocol
|
||||
// @req data with iso7816 apdu message
|
||||
// @len data length
|
||||
void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp);
|
||||
|
||||
|
||||
int8_t u2f_response_writeback(const uint8_t * buf, uint16_t len);
|
||||
void u2f_reset_response();
|
||||
|
@ -5,9 +5,8 @@ version=${1:-master}
|
||||
export PREFIX=/opt/gcc-arm-none-eabi-8-2018-q4-major/bin/
|
||||
|
||||
cd /solo/targets/stm32l432
|
||||
git fetch --tags
|
||||
git fetch
|
||||
git checkout ${version}
|
||||
git submodule update --init --recursive
|
||||
version=$(git describe)
|
||||
|
||||
make cbor
|
||||
@ -35,17 +34,4 @@ function build() {
|
||||
build bootloader nonverifying
|
||||
build bootloader verifying
|
||||
build firmware hacker solo
|
||||
build firmware hacker-debug-1 solo
|
||||
build firmware hacker-debug-2 solo
|
||||
build firmware secure solo
|
||||
|
||||
pip install -U pip
|
||||
pip install -U solo-python
|
||||
cd ${out_dir}
|
||||
bundle="bundle-hacker-${version}"
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-${version}.hex ${bundle}.hex
|
||||
sha256sum ${bundle}.hex > ${bundle}.sha2
|
||||
bundle="bundle-hacker-debug-1-${version}"
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-1-${version}.hex ${bundle}.hex
|
||||
bundle="bundle-hacker-debug-2-${version}"
|
||||
/opt/conda/bin/solo mergehex bootloader-nonverifying-${version}.hex firmware-hacker-debug-2-${version}.hex ${bundle}.hex
|
||||
|
2
pc/app.h
2
pc/app.h
@ -15,7 +15,7 @@
|
||||
#define DEBUG_LEVEL 1
|
||||
|
||||
#define ENABLE_U2F
|
||||
#define ENABLE_U2F_EXTENSIONS
|
||||
|
||||
//#define BRIDGE_TO_WALLET
|
||||
|
||||
void printing_init();
|
||||
|
112
pc/device.c
112
pc/device.c
@ -22,11 +22,6 @@
|
||||
#include "log.h"
|
||||
#include "ctaphid.h"
|
||||
|
||||
#define RK_NUM 50
|
||||
|
||||
struct ResidentKeyStore {
|
||||
CTAP_residentKey rks[RK_NUM];
|
||||
} RK_STORE;
|
||||
|
||||
void authenticator_initialize();
|
||||
|
||||
@ -146,20 +141,11 @@ void usbhid_init()
|
||||
int usbhid_recv(uint8_t * msg)
|
||||
{
|
||||
int l = udp_recv(serverfd, msg, HID_MESSAGE_SIZE);
|
||||
uint8_t magic_cmd[] = "\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
|
||||
"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
|
||||
"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
||||
"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
|
||||
"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
|
||||
"\x9a\x72\x50\xdc";
|
||||
if ( memcmp(magic_cmd, msg, 64) == 0 )
|
||||
{
|
||||
printf1(TAG_RED, "MAGIC REBOOT command recieved!\r\n");
|
||||
memset(msg,0,64);
|
||||
exit(100);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*if (l && l != HID_MESSAGE_SIZE)*/
|
||||
/*{*/
|
||||
/*printf("Error, recv'd message of wrong size %d", l);*/
|
||||
/*exit(1);*/
|
||||
/*}*/
|
||||
return l;
|
||||
}
|
||||
|
||||
@ -188,10 +174,6 @@ void device_init()
|
||||
usbhid_init();
|
||||
|
||||
authenticator_initialize();
|
||||
|
||||
ctaphid_init();
|
||||
|
||||
ctap_init( 1 );
|
||||
}
|
||||
|
||||
|
||||
@ -199,7 +181,7 @@ void main_loop_delay()
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 1000*1000*100;
|
||||
ts.tv_nsec = 1000*1000*25;
|
||||
nanosleep(&ts,NULL);
|
||||
}
|
||||
|
||||
@ -265,7 +247,6 @@ 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)
|
||||
{
|
||||
@ -385,24 +366,6 @@ int authenticator_is_backup_initialized()
|
||||
|
||||
/*}*/
|
||||
|
||||
static void sync_rk()
|
||||
{
|
||||
FILE * f = fopen(rk_file, "wb+");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int ret = fwrite(&RK_STORE, 1, sizeof(RK_STORE), f);
|
||||
fclose(f);
|
||||
if (ret != sizeof(RK_STORE))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void authenticator_initialize()
|
||||
{
|
||||
uint8_t header[16];
|
||||
@ -426,22 +389,6 @@ void authenticator_initialize()
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// resident_keys
|
||||
f = fopen(rk_file, "rb");
|
||||
if (f== NULL)
|
||||
{
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
ret = fread(&RK_STORE, 1, sizeof(RK_STORE), f);
|
||||
fclose(f);
|
||||
if(ret != sizeof(RK_STORE))
|
||||
{
|
||||
perror("fwrite");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -480,12 +427,6 @@ void authenticator_initialize()
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// resident_keys
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
sync_rk();
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -494,60 +435,29 @@ void device_manage()
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ctap_reset_rk()
|
||||
{
|
||||
memset(&RK_STORE,0xff,sizeof(RK_STORE));
|
||||
sync_rk();
|
||||
|
||||
}
|
||||
|
||||
uint32_t ctap_rk_size()
|
||||
{
|
||||
return RK_NUM;
|
||||
printf("Warning: rk not implemented\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void ctap_store_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||
sync_rk();
|
||||
printf("Warning: rk not implemented\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void ctap_load_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
memmove(rk, RK_STORE.rks + index, sizeof(CTAP_residentKey));
|
||||
printf("Warning: rk not implemented\n");
|
||||
}
|
||||
|
||||
void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
||||
{
|
||||
if (index < RK_NUM)
|
||||
{
|
||||
memmove(RK_STORE.rks + index, rk, sizeof(CTAP_residentKey));
|
||||
sync_rk();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_ERR,"Out of bounds for store_rk\r\n");
|
||||
}
|
||||
printf("Warning: rk not implemented\n");
|
||||
}
|
||||
|
||||
void device_wink()
|
||||
{
|
||||
printf("*WINK*\n");
|
||||
}
|
||||
|
||||
bool device_is_nfc()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
1
python-fido2
Submodule
1
python-fido2
Submodule
Submodule python-fido2 added at 329434fdd4
@ -5,7 +5,7 @@ endif
|
||||
APPMAKE=build/application.mk
|
||||
BOOTMAKE=build/bootloader.mk
|
||||
|
||||
merge_hex=solo mergehex
|
||||
merge_hex=../../tools/solotool.py mergehex
|
||||
|
||||
.PHONY: all all-hacker all-locked debugboot-app debugboot-boot boot-sig-checking boot-no-sig build-release-locked build-release build-release build-hacker build-debugboot clean clean2 flash flash_dfu flashboot detach cbor test
|
||||
|
||||
@ -15,12 +15,6 @@ merge_hex=solo mergehex
|
||||
firmware-hacker:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
|
||||
firmware-hacker-debug-1:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=1 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
|
||||
firmware-hacker-debug-2:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
|
||||
|
||||
firmware-secure:
|
||||
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
|
||||
|
||||
@ -93,11 +87,6 @@ flashboot: solo.hex bootloader.hex
|
||||
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
||||
STM32_Programmer_CLI -c port=SWD -halt -d bootloader.hex -rst
|
||||
|
||||
flash-firmware:
|
||||
arm-none-eabi-size -A solo.elf
|
||||
solo program aux enter-bootloader
|
||||
solo program bootloader solo.hex
|
||||
|
||||
# tell ST DFU to enter application
|
||||
detach:
|
||||
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
|
||||
|
73
targets/stm32l432/Makefile.test.mk
Normal file
73
targets/stm32l432/Makefile.test.mk
Normal file
@ -0,0 +1,73 @@
|
||||
CC=arm-none-eabi-gcc
|
||||
CP=arm-none-eabi-objcopy
|
||||
SZ=arm-none-eabi-size
|
||||
AR=arm-none-eabi-ar
|
||||
|
||||
# ST related
|
||||
SRC = src/main.c src/init.c src/flash.c src/led.c
|
||||
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
|
||||
SRC += lib/stm32l4xx_ll_gpio.c lib/stm32l4xx_ll_pwr.c lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_tim.c lib/stm32l4xx_ll_utils.c
|
||||
|
||||
OBJ1=$(SRC:.c=.o)
|
||||
OBJ=$(OBJ1:.s=.o)
|
||||
|
||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
|
||||
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
|
||||
INC += -I../../crypto/tiny-AES-c
|
||||
|
||||
SEARCH=-L../../tinycbor/lib
|
||||
|
||||
LDSCRIPT=stm32l432xx.ld
|
||||
|
||||
CFLAGS= $(INC)
|
||||
|
||||
TARGET=solo
|
||||
HW=-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb
|
||||
|
||||
# Solo or Nucleo board
|
||||
CHIP=STM32L432xx
|
||||
|
||||
DEFINES = -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER
|
||||
DEFINES += -DTEST_SOLO_STM32 -DTEST
|
||||
|
||||
CFLAGS=$(INC) -c $(DEFINES) -Wall -fdata-sections -ffunction-sections $(HW)
|
||||
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
|
||||
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref
|
||||
|
||||
|
||||
.PRECIOUS: %.o
|
||||
|
||||
all: $(TARGET).elf
|
||||
$(SZ) $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
||||
|
||||
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
|
||||
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
|
||||
|
||||
%.o: %.s
|
||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
||||
|
||||
%.elf: $(OBJ)
|
||||
$(CC) $^ $(HW) $(LDFLAGS) -o $@
|
||||
|
||||
%.hex: %.elf
|
||||
$(CP) -O ihex $^ $(TARGET).hex
|
||||
$(CP) -O binary $^ $(TARGET).bin
|
||||
|
||||
clean:
|
||||
rm -f *.o src/*.o src/*.elf *.elf *.hex $(OBJ)
|
||||
|
||||
flash: $(TARGET).hex
|
||||
STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
|
||||
STM32_Programmer_CLI -c port=SWD -halt -d $(TARGET).hex -rst
|
||||
|
||||
detach:
|
||||
STM32_Programmer_CLI -c port=usb1 -ob nBOOT0=1
|
||||
|
||||
cbor:
|
||||
cd ../../tinycbor/ && make clean
|
||||
cd ../../tinycbor/ && make CC="$(CC)" AR=$(AR) \
|
||||
LDFLAGS="$(LDFLAGS_LIB)" \
|
||||
CFLAGS="$(CFLAGS)"
|
@ -55,7 +55,7 @@
|
||||
#define SOLO_PRODUCT_NAME "Solo Bootloader " SOLO_VERSION
|
||||
|
||||
void printing_init();
|
||||
void hw_init(int lf);
|
||||
void hw_init(void);
|
||||
|
||||
// Trigger software reset
|
||||
void device_reboot();
|
||||
|
@ -8,10 +8,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "stm32l4xx_ll_rcc.h"
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
#include "stm32l4xx.h"
|
||||
|
||||
#include "cbor.h"
|
||||
#include "device.h"
|
||||
#include "ctaphid.h"
|
||||
@ -21,12 +17,12 @@
|
||||
#include "ctap.h"
|
||||
#include "app.h"
|
||||
#include "memory_layout.h"
|
||||
#include "init.h"
|
||||
#include "stm32l4xx_ll_rcc.h"
|
||||
|
||||
#include "stm32l4xx.h"
|
||||
|
||||
uint8_t REBOOT_FLAG = 0;
|
||||
|
||||
void SystemClock_Config(void);
|
||||
|
||||
void BOOT_boot(void)
|
||||
{
|
||||
@ -73,16 +69,7 @@ int main()
|
||||
TAG_ERR
|
||||
);
|
||||
|
||||
// device_init();
|
||||
|
||||
init_gpio();
|
||||
|
||||
init_millisecond_timer(1);
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
init_debug_uart();
|
||||
#endif
|
||||
|
||||
device_init();
|
||||
printf1(TAG_GEN,"init device\n");
|
||||
|
||||
t1 = millis();
|
||||
@ -120,13 +107,7 @@ int main()
|
||||
#ifdef SOLO_HACKER
|
||||
start_bootloader:
|
||||
#endif
|
||||
SystemClock_Config();
|
||||
init_gpio();
|
||||
init_millisecond_timer(0);
|
||||
init_pwm();
|
||||
init_rng();
|
||||
usbhid_init();
|
||||
|
||||
printf1(TAG_GEN,"init usb\n");
|
||||
|
||||
ctaphid_init();
|
||||
|
@ -2,7 +2,7 @@ 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 += src/fifo.c src/crypto.c src/attestation.c
|
||||
SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
|
||||
SRC += $(DRIVER_LIBS) $(USB_LIB)
|
||||
|
||||
@ -14,7 +14,6 @@ SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.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
|
||||
|
||||
OBJ1=$(SRC:.c=.o)
|
||||
OBJ=$(OBJ1:.s=.o)
|
||||
@ -22,7 +21,6 @@ OBJ=$(OBJ1:.s=.o)
|
||||
INC = -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
|
||||
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
|
||||
INC += -I../../crypto/tiny-AES-c
|
||||
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
|
||||
|
||||
SEARCH=-L../../tinycbor/lib
|
||||
|
||||
@ -43,14 +41,12 @@ DEBUG=0
|
||||
endif
|
||||
|
||||
DEFINES = -DDEBUG_LEVEL=$(DEBUG) -D$(CHIP) -DAES256=1 -DUSE_FULL_LL_DRIVER -DAPP_CONFIG=\"app.h\" $(EXTRA_DEFINES)
|
||||
# DEFINES += -DTEST_SOLO_STM32 -DTEST -DTEST_FIFO=1
|
||||
|
||||
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections \
|
||||
-fomit-frame-pointer $(HW) -g $(VERSION_FLAGS)
|
||||
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -lnosys
|
||||
CFLAGS=$(INC) -c $(DEFINES) -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -fdata-sections -ffunction-sections $(HW) -g $(VERSION_FLAGS)
|
||||
LDFLAGS_LIB=$(HW) $(SEARCH) -specs=nano.specs -specs=nosys.specs -Wl,--gc-sections -u _printf_float -lnosys
|
||||
LDFLAGS=$(HW) $(LDFLAGS_LIB) -T$(LDSCRIPT) -Wl,-Map=$(TARGET).map,--cref -Wl,-Bstatic -ltinycbor
|
||||
|
||||
ECC_CFLAGS = $(CFLAGS) -DuECC_PLATFORM=5 -DuECC_OPTIMIZATION_LEVEL=4 -DuECC_SQUARE_FUNC=1 -DuECC_SUPPORT_COMPRESSED_POINT=0
|
||||
|
||||
|
||||
.PRECIOUS: %.o
|
||||
|
||||
@ -61,7 +57,7 @@ all: $(TARGET).elf
|
||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
||||
|
||||
../../crypto/micro-ecc/uECC.o: ../../crypto/micro-ecc/uECC.c
|
||||
$(CC) $^ $(HW) -O3 $(ECC_CFLAGS) -o $@
|
||||
$(CC) $^ $(HW) -O3 $(CFLAGS) -o $@
|
||||
|
||||
%.o: %.s
|
||||
$(CC) $^ $(HW) -Os $(CFLAGS) -o $@
|
||||
@ -70,7 +66,6 @@ all: $(TARGET).elf
|
||||
$(CC) $^ $(HW) $(LDFLAGS) -o $@
|
||||
|
||||
%.hex: %.elf
|
||||
$(SZ) $^
|
||||
$(CP) -O ihex $^ $(TARGET).hex
|
||||
|
||||
clean:
|
||||
|
@ -13,7 +13,6 @@ SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2
|
||||
|
||||
# Crypto libs
|
||||
SRC += ../../crypto/sha256/sha256.c ../../crypto/micro-ecc/uECC.c
|
||||
SRC += ../../crypto/cifra/src/sha512.c ../../crypto/cifra/src/blockwise.c
|
||||
|
||||
OBJ1=$(SRC:.c=.o)
|
||||
OBJ=$(OBJ1:.s=.o)
|
||||
@ -22,7 +21,6 @@ OBJ=$(OBJ1:.s=.o)
|
||||
INC = -Ibootloader/ -Isrc/ -Isrc/cmsis/ -Ilib/ -Ilib/usbd/ -I../../fido2/ -I../../fido2/extensions
|
||||
INC += -I../../tinycbor/src -I../../crypto/sha256 -I../../crypto/micro-ecc
|
||||
INC += -I../../crypto/tiny-AES-c
|
||||
INC += -I../../crypto/cifra/src -I../../crypto/cifra/src/ext
|
||||
|
||||
ifndef LDSCRIPT
|
||||
LDSCRIPT=linker/bootloader_stm32l4xx.ld
|
||||
|
@ -6,7 +6,7 @@ AR=$(PREFIX)arm-none-eabi-ar
|
||||
DRIVER_LIBS := lib/stm32l4xx_hal_pcd.c lib/stm32l4xx_hal_pcd_ex.c lib/stm32l4xx_ll_gpio.c \
|
||||
lib/stm32l4xx_ll_rcc.c lib/stm32l4xx_ll_rng.c lib/stm32l4xx_ll_tim.c \
|
||||
lib/stm32l4xx_ll_usb.c lib/stm32l4xx_ll_utils.c lib/stm32l4xx_ll_pwr.c \
|
||||
lib/stm32l4xx_ll_usart.c lib/stm32l4xx_ll_spi.c
|
||||
lib/stm32l4xx_ll_usart.c
|
||||
|
||||
USB_LIB := lib/usbd/usbd_cdc.c lib/usbd/usbd_cdc_if.c lib/usbd/usbd_composite.c \
|
||||
lib/usbd/usbd_conf.c lib/usbd/usbd_core.c lib/usbd/usbd_ioreq.c \
|
||||
|
@ -1,307 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32l4xx_ll_spi.c
|
||||
* @author MCD Application Team
|
||||
* @brief SPI LL module driver.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2017 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
#if defined(USE_FULL_LL_DRIVER)
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32l4xx_ll_spi.h"
|
||||
#include "stm32l4xx_ll_bus.h"
|
||||
|
||||
#ifdef USE_FULL_ASSERT
|
||||
#include "stm32_assert.h"
|
||||
#else
|
||||
#define assert_param(expr) ((void)0U)
|
||||
#endif
|
||||
|
||||
/** @addtogroup STM32L4xx_LL_Driver
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if defined (SPI1) || defined (SPI2) || defined (SPI3)
|
||||
|
||||
/** @addtogroup SPI_LL
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Private types -------------------------------------------------------------*/
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
|
||||
/* Private constants ---------------------------------------------------------*/
|
||||
/** @defgroup SPI_LL_Private_Constants SPI Private Constants
|
||||
* @{
|
||||
*/
|
||||
/* SPI registers Masks */
|
||||
#define SPI_CR1_CLEAR_MASK (SPI_CR1_CPHA | SPI_CR1_CPOL | SPI_CR1_MSTR | \
|
||||
SPI_CR1_BR | SPI_CR1_LSBFIRST | SPI_CR1_SSI | \
|
||||
SPI_CR1_SSM | SPI_CR1_RXONLY | SPI_CR1_CRCL | \
|
||||
SPI_CR1_CRCNEXT | SPI_CR1_CRCEN | SPI_CR1_BIDIOE | \
|
||||
SPI_CR1_BIDIMODE)
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* Private macros ------------------------------------------------------------*/
|
||||
/** @defgroup SPI_LL_Private_Macros SPI Private Macros
|
||||
* @{
|
||||
*/
|
||||
#define IS_LL_SPI_TRANSFER_DIRECTION(__VALUE__) (((__VALUE__) == LL_SPI_FULL_DUPLEX) \
|
||||
|| ((__VALUE__) == LL_SPI_SIMPLEX_RX) \
|
||||
|| ((__VALUE__) == LL_SPI_HALF_DUPLEX_RX) \
|
||||
|| ((__VALUE__) == LL_SPI_HALF_DUPLEX_TX))
|
||||
|
||||
#define IS_LL_SPI_MODE(__VALUE__) (((__VALUE__) == LL_SPI_MODE_MASTER) \
|
||||
|| ((__VALUE__) == LL_SPI_MODE_SLAVE))
|
||||
|
||||
#define IS_LL_SPI_DATAWIDTH(__VALUE__) (((__VALUE__) == LL_SPI_DATAWIDTH_4BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_5BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_6BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_7BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_8BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_9BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_10BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_11BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_12BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_13BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_14BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_15BIT) \
|
||||
|| ((__VALUE__) == LL_SPI_DATAWIDTH_16BIT))
|
||||
|
||||
#define IS_LL_SPI_POLARITY(__VALUE__) (((__VALUE__) == LL_SPI_POLARITY_LOW) \
|
||||
|| ((__VALUE__) == LL_SPI_POLARITY_HIGH))
|
||||
|
||||
#define IS_LL_SPI_PHASE(__VALUE__) (((__VALUE__) == LL_SPI_PHASE_1EDGE) \
|
||||
|| ((__VALUE__) == LL_SPI_PHASE_2EDGE))
|
||||
|
||||
#define IS_LL_SPI_NSS(__VALUE__) (((__VALUE__) == LL_SPI_NSS_SOFT) \
|
||||
|| ((__VALUE__) == LL_SPI_NSS_HARD_INPUT) \
|
||||
|| ((__VALUE__) == LL_SPI_NSS_HARD_OUTPUT))
|
||||
|
||||
#define IS_LL_SPI_BAUDRATE(__VALUE__) (((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV2) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV4) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV8) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV16) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV32) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV64) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV128) \
|
||||
|| ((__VALUE__) == LL_SPI_BAUDRATEPRESCALER_DIV256))
|
||||
|
||||
#define IS_LL_SPI_BITORDER(__VALUE__) (((__VALUE__) == LL_SPI_LSB_FIRST) \
|
||||
|| ((__VALUE__) == LL_SPI_MSB_FIRST))
|
||||
|
||||
#define IS_LL_SPI_CRCCALCULATION(__VALUE__) (((__VALUE__) == LL_SPI_CRCCALCULATION_ENABLE) \
|
||||
|| ((__VALUE__) == LL_SPI_CRCCALCULATION_DISABLE))
|
||||
|
||||
#define IS_LL_SPI_CRC_POLYNOMIAL(__VALUE__) ((__VALUE__) >= 0x1U)
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
|
||||
/* Exported functions --------------------------------------------------------*/
|
||||
/** @addtogroup SPI_LL_Exported_Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup SPI_LL_EF_Init
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief De-initialize the SPI registers to their default reset values.
|
||||
* @param SPIx SPI Instance
|
||||
* @retval An ErrorStatus enumeration value:
|
||||
* - SUCCESS: SPI registers are de-initialized
|
||||
* - ERROR: SPI registers are not de-initialized
|
||||
*/
|
||||
ErrorStatus LL_SPI_DeInit(SPI_TypeDef *SPIx)
|
||||
{
|
||||
ErrorStatus status = ERROR;
|
||||
|
||||
/* Check the parameters */
|
||||
assert_param(IS_SPI_ALL_INSTANCE(SPIx));
|
||||
|
||||
#if defined(SPI1)
|
||||
if (SPIx == SPI1)
|
||||
{
|
||||
/* Force reset of SPI clock */
|
||||
LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1);
|
||||
|
||||
/* Release reset of SPI clock */
|
||||
LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1);
|
||||
|
||||
status = SUCCESS;
|
||||
}
|
||||
#endif /* SPI1 */
|
||||
#if defined(SPI2)
|
||||
if (SPIx == SPI2)
|
||||
{
|
||||
/* Force reset of SPI clock */
|
||||
LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2);
|
||||
|
||||
/* Release reset of SPI clock */
|
||||
LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2);
|
||||
|
||||
status = SUCCESS;
|
||||
}
|
||||
#endif /* SPI2 */
|
||||
#if defined(SPI3)
|
||||
if (SPIx == SPI3)
|
||||
{
|
||||
/* Force reset of SPI clock */
|
||||
LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI3);
|
||||
|
||||
/* Release reset of SPI clock */
|
||||
LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI3);
|
||||
|
||||
status = SUCCESS;
|
||||
}
|
||||
#endif /* SPI3 */
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the SPI registers according to the specified parameters in SPI_InitStruct.
|
||||
* @note As some bits in SPI configuration registers can only be written when the SPI is disabled (SPI_CR1_SPE bit =0),
|
||||
* SPI IP should be in disabled state prior calling this function. Otherwise, ERROR result will be returned.
|
||||
* @param SPIx SPI Instance
|
||||
* @param SPI_InitStruct pointer to a @ref LL_SPI_InitTypeDef structure
|
||||
* @retval An ErrorStatus enumeration value. (Return always SUCCESS)
|
||||
*/
|
||||
ErrorStatus LL_SPI_Init(SPI_TypeDef *SPIx, LL_SPI_InitTypeDef *SPI_InitStruct)
|
||||
{
|
||||
ErrorStatus status = ERROR;
|
||||
|
||||
/* Check the SPI Instance SPIx*/
|
||||
assert_param(IS_SPI_ALL_INSTANCE(SPIx));
|
||||
|
||||
/* Check the SPI parameters from SPI_InitStruct*/
|
||||
assert_param(IS_LL_SPI_TRANSFER_DIRECTION(SPI_InitStruct->TransferDirection));
|
||||
assert_param(IS_LL_SPI_MODE(SPI_InitStruct->Mode));
|
||||
assert_param(IS_LL_SPI_DATAWIDTH(SPI_InitStruct->DataWidth));
|
||||
assert_param(IS_LL_SPI_POLARITY(SPI_InitStruct->ClockPolarity));
|
||||
assert_param(IS_LL_SPI_PHASE(SPI_InitStruct->ClockPhase));
|
||||
assert_param(IS_LL_SPI_NSS(SPI_InitStruct->NSS));
|
||||
assert_param(IS_LL_SPI_BAUDRATE(SPI_InitStruct->BaudRate));
|
||||
assert_param(IS_LL_SPI_BITORDER(SPI_InitStruct->BitOrder));
|
||||
assert_param(IS_LL_SPI_CRCCALCULATION(SPI_InitStruct->CRCCalculation));
|
||||
|
||||
if (LL_SPI_IsEnabled(SPIx) == 0x00000000U)
|
||||
{
|
||||
/*---------------------------- SPIx CR1 Configuration ------------------------
|
||||
* Configure SPIx CR1 with parameters:
|
||||
* - TransferDirection: SPI_CR1_BIDIMODE, SPI_CR1_BIDIOE and SPI_CR1_RXONLY bits
|
||||
* - Master/Slave Mode: SPI_CR1_MSTR bit
|
||||
* - ClockPolarity: SPI_CR1_CPOL bit
|
||||
* - ClockPhase: SPI_CR1_CPHA bit
|
||||
* - NSS management: SPI_CR1_SSM bit
|
||||
* - BaudRate prescaler: SPI_CR1_BR[2:0] bits
|
||||
* - BitOrder: SPI_CR1_LSBFIRST bit
|
||||
* - CRCCalculation: SPI_CR1_CRCEN bit
|
||||
*/
|
||||
MODIFY_REG(SPIx->CR1,
|
||||
SPI_CR1_CLEAR_MASK,
|
||||
SPI_InitStruct->TransferDirection | SPI_InitStruct->Mode |
|
||||
SPI_InitStruct->ClockPolarity | SPI_InitStruct->ClockPhase |
|
||||
SPI_InitStruct->NSS | SPI_InitStruct->BaudRate |
|
||||
SPI_InitStruct->BitOrder | SPI_InitStruct->CRCCalculation);
|
||||
|
||||
/*---------------------------- SPIx CR2 Configuration ------------------------
|
||||
* Configure SPIx CR2 with parameters:
|
||||
* - DataWidth: DS[3:0] bits
|
||||
* - NSS management: SSOE bit
|
||||
*/
|
||||
MODIFY_REG(SPIx->CR2,
|
||||
SPI_CR2_DS | SPI_CR2_SSOE,
|
||||
SPI_InitStruct->DataWidth | (SPI_InitStruct->NSS >> 16U));
|
||||
|
||||
/*---------------------------- SPIx CRCPR Configuration ----------------------
|
||||
* Configure SPIx CRCPR with parameters:
|
||||
* - CRCPoly: CRCPOLY[15:0] bits
|
||||
*/
|
||||
if (SPI_InitStruct->CRCCalculation == LL_SPI_CRCCALCULATION_ENABLE)
|
||||
{
|
||||
assert_param(IS_LL_SPI_CRC_POLYNOMIAL(SPI_InitStruct->CRCPoly));
|
||||
LL_SPI_SetCRCPolynomial(SPIx, SPI_InitStruct->CRCPoly);
|
||||
}
|
||||
status = SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set each @ref LL_SPI_InitTypeDef field to default value.
|
||||
* @param SPI_InitStruct pointer to a @ref LL_SPI_InitTypeDef structure
|
||||
* whose fields will be set to default values.
|
||||
* @retval None
|
||||
*/
|
||||
void LL_SPI_StructInit(LL_SPI_InitTypeDef *SPI_InitStruct)
|
||||
{
|
||||
/* Set SPI_InitStruct fields to default values */
|
||||
SPI_InitStruct->TransferDirection = LL_SPI_FULL_DUPLEX;
|
||||
SPI_InitStruct->Mode = LL_SPI_MODE_SLAVE;
|
||||
SPI_InitStruct->DataWidth = LL_SPI_DATAWIDTH_8BIT;
|
||||
SPI_InitStruct->ClockPolarity = LL_SPI_POLARITY_LOW;
|
||||
SPI_InitStruct->ClockPhase = LL_SPI_PHASE_1EDGE;
|
||||
SPI_InitStruct->NSS = LL_SPI_NSS_HARD_INPUT;
|
||||
SPI_InitStruct->BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;
|
||||
SPI_InitStruct->BitOrder = LL_SPI_MSB_FIRST;
|
||||
SPI_InitStruct->CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
|
||||
SPI_InitStruct->CRCPoly = 7U;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* defined (SPI1) || defined (SPI2) || defined (SPI3) */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* USE_FULL_LL_DRIVER */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
File diff suppressed because it is too large
Load Diff
@ -110,7 +110,7 @@ __ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_
|
||||
0x03, /* bNumEndpoints: 3 endpoints used */
|
||||
0x02, /* bInterfaceClass: Communication Interface Class */
|
||||
0x02, /* bInterfaceSubClass: Abstract Control Model */
|
||||
0x00, /* bInterfaceProtocol: Common AT commands */
|
||||
0x01, /* bInterfaceProtocol: Common AT commands */
|
||||
0x00, /* iInterface: */
|
||||
|
||||
/*Header Functional Descriptor*/
|
||||
|
@ -821,16 +821,12 @@ void USBD_CtlError( USBD_HandleTypeDef *pdev ,
|
||||
* @param len : descriptor length
|
||||
* @retval None
|
||||
*/
|
||||
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t unicode_size, uint16_t *len)
|
||||
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t *len)
|
||||
{
|
||||
uint8_t idx = 0U;
|
||||
|
||||
if (desc != NULL)
|
||||
{
|
||||
if ((idx + 4) >= unicode_size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
*len = (uint16_t)USBD_GetLen(desc) * 2U + 2U;
|
||||
unicode[idx++] = *(uint8_t *)(void *)len;
|
||||
unicode[idx++] = USB_DESC_TYPE_STRING;
|
||||
|
@ -108,7 +108,7 @@ void USBD_CtlError (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
|
||||
|
||||
void USBD_ParseSetupRequest (USBD_SetupReqTypedef *req, uint8_t *pdata);
|
||||
|
||||
void USBD_GetString(uint8_t *desc, uint8_t *unicode, uint16_t unicode_size, uint16_t *len);
|
||||
void USBD_GetString (uint8_t *desc, uint8_t *unicode, uint16_t *len);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
@ -108,7 +108,7 @@ const uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC]=
|
||||
HIBYTE(USBD_LANGID_STRING),
|
||||
};
|
||||
|
||||
uint8_t USBD_StrDesc[48];
|
||||
uint8_t USBD_StrDesc[32];
|
||||
|
||||
/**
|
||||
* @brief Returns the device descriptor.
|
||||
@ -142,7 +142,7 @@ uint8_t *USBD_HID_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
*/
|
||||
uint8_t *USBD_HID_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
USBD_GetString((uint8_t *)USBD_PRODUCT_FS_STRING, USBD_StrDesc, sizeof(USBD_StrDesc), length);
|
||||
USBD_GetString((uint8_t *)USBD_PRODUCT_FS_STRING, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ uint8_t *USBD_HID_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length
|
||||
*/
|
||||
uint8_t *USBD_HID_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
{
|
||||
USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, sizeof(USBD_StrDesc), length);
|
||||
USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
||||
@ -192,6 +192,6 @@ uint8_t *USBD_HID_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
|
||||
}
|
||||
|
||||
|
||||
USBD_GetString((uint8_t *)uuid_str, USBD_StrDesc, sizeof(USBD_StrDesc), length);
|
||||
USBD_GetString((uint8_t *)uuid_str, USBD_StrDesc, length);
|
||||
return USBD_StrDesc;
|
||||
}
|
||||
|
@ -1,74 +1,201 @@
|
||||
/* 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. */
|
||||
/*
|
||||
*****************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Abstract : Linker script for STM32L432KCUx Device with
|
||||
** 256KByte FLASH, 64KByte RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
**
|
||||
** Distribution: The file is distributed as is, without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
** (c)Copyright Ac6.
|
||||
** You may use this file as-is or modify it according to the needs of your
|
||||
** project. Distribution of this file (unmodified or modified) is not
|
||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||
** rights to distribute the assembled, compiled & linked contents of this
|
||||
** file as part of an application binary file, provided that it is built
|
||||
** using the System Workbench for MCU toolchain.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* End of RAM */
|
||||
_estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = 0x2000c000; /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08000000, LENGTH = 20K
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 20K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.isr_vector))
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(8);
|
||||
} >flash
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
KEEP(*(.init))
|
||||
KEEP(*(.finit))
|
||||
. = ALIGN(8);
|
||||
_etext = .;
|
||||
} >flash
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(8);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.ARM : {
|
||||
. = ALIGN(8);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_sdata = .;
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(8);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
_sisram2 = LOADADDR(.sram2);
|
||||
|
||||
/* CCM-RAM section
|
||||
*
|
||||
* IMPORTANT NOTE!
|
||||
* If initialized variables will be placed in this section,
|
||||
* the startup code needs to be modified to copy the init-values.
|
||||
*/
|
||||
.sram2 :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
||||
*(.sram2)
|
||||
*(.sram2*)
|
||||
|
||||
. = ALIGN(8);
|
||||
_esram2 = .; /* create a global symbol at sram2 end */
|
||||
} >SRAM2 AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} > ram
|
||||
|
||||
._stack :
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
end = .;
|
||||
_end = .;
|
||||
. = . + _MIN_STACK_SIZE;
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} > ram
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
@ -1,74 +1,201 @@
|
||||
/* 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. */
|
||||
/*
|
||||
*****************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Abstract : Linker script for STM32L432KCUx Device with
|
||||
** 256KByte FLASH, 64KByte RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
**
|
||||
** Distribution: The file is distributed as is, without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
** (c)Copyright Ac6.
|
||||
** You may use this file as-is or modify it according to the needs of your
|
||||
** project. Distribution of this file (unmodified or modified) is not
|
||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||
** rights to distribute the assembled, compiled & linked contents of this
|
||||
** file as part of an application binary file, provided that it is built
|
||||
** using the System Workbench for MCU toolchain.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* End of RAM */
|
||||
_estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = 0x2000c000; /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 32K
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.isr_vector))
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(8);
|
||||
} >flash
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
KEEP(*(.init))
|
||||
KEEP(*(.finit))
|
||||
. = ALIGN(8);
|
||||
_etext = .;
|
||||
} >flash
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(8);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.ARM : {
|
||||
. = ALIGN(8);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_sdata = .;
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(8);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
_sisram2 = LOADADDR(.sram2);
|
||||
|
||||
/* CCM-RAM section
|
||||
*
|
||||
* IMPORTANT NOTE!
|
||||
* If initialized variables will be placed in this section,
|
||||
* the startup code needs to be modified to copy the init-values.
|
||||
*/
|
||||
.sram2 :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
||||
*(.sram2)
|
||||
*(.sram2*)
|
||||
|
||||
. = ALIGN(8);
|
||||
_esram2 = .; /* create a global symbol at sram2 end */
|
||||
} >SRAM2 AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} > ram
|
||||
|
||||
._stack :
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
end = .;
|
||||
_end = .;
|
||||
. = . + _MIN_STACK_SIZE;
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} > ram
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
@ -1,80 +1,202 @@
|
||||
/* 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. */
|
||||
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* End of RAM */
|
||||
_estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
|
||||
/*
|
||||
Memory layout of device:
|
||||
20 KB 198KB-8 38 KB
|
||||
| bootloader | application | secrets/data |
|
||||
*****************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Abstract : Linker script for STM32L432KCUx Device with
|
||||
** 256KByte FLASH, 64KByte RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
**
|
||||
** Distribution: The file is distributed as is, without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
** (c)Copyright Ac6.
|
||||
** You may use this file as-is or modify it according to the needs of your
|
||||
** project. Distribution of this file (unmodified or modified) is not
|
||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||
** rights to distribute the assembled, compiled & linked contents of this
|
||||
** file as part of an application binary file, provided that it is built
|
||||
** using the System Workbench for MCU toolchain.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = 0x2000c000; /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08005000, LENGTH = 198K - 8
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
/* First 20 KB is bootloader */
|
||||
FLASH (rx) : ORIGIN = 0x08005000, LENGTH = 198K-8 /* Leave out 38 Kb at end for data */
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.isr_vector))
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(8);
|
||||
} >flash
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
KEEP(*(.init))
|
||||
KEEP(*(.finit))
|
||||
. = ALIGN(8);
|
||||
_etext = .;
|
||||
} >flash
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(8);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.ARM : {
|
||||
. = ALIGN(8);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_sdata = .;
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(8);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
_sisram2 = LOADADDR(.sram2);
|
||||
|
||||
/* CCM-RAM section
|
||||
*
|
||||
* IMPORTANT NOTE!
|
||||
* If initialized variables will be placed in this section,
|
||||
* the startup code needs to be modified to copy the init-values.
|
||||
*/
|
||||
.sram2 :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
||||
*(.sram2)
|
||||
*(.sram2*)
|
||||
|
||||
. = ALIGN(8);
|
||||
_esram2 = .; /* create a global symbol at sram2 end */
|
||||
} >SRAM2 AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} > ram
|
||||
|
||||
._stack :
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
end = .;
|
||||
_end = .;
|
||||
. = . + _MIN_STACK_SIZE;
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} > ram
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
@ -1,74 +1,203 @@
|
||||
/* 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. */
|
||||
/*
|
||||
*****************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Abstract : Linker script for STM32L432KCUx Device with
|
||||
** 256KByte FLASH, 64KByte RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
**
|
||||
** Distribution: The file is distributed as is, without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
** (c)Copyright Ac6.
|
||||
** You may use this file as-is or modify it according to the needs of your
|
||||
** project. Distribution of this file (unmodified or modified) is not
|
||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||
** rights to distribute the assembled, compiled & linked contents of this
|
||||
** file as part of an application binary file, provided that it is built
|
||||
** using the System Workbench for MCU toolchain.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* End of RAM */
|
||||
_estack = 0x2000c000;
|
||||
|
||||
_MIN_STACK_SIZE = 0x400;
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = 0x2000c000; /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
flash (rx) : ORIGIN = 0x08008000, LENGTH = 186K - 8
|
||||
ram (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
sram2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
/* First 32 KB is bootloader */
|
||||
/*FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 238K-8 [> Leave out 38 Kb at end for data <]*/
|
||||
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 186K-8 /* Leave out 38 Kb at end for data */
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K
|
||||
SRAM2 (rw) : ORIGIN = 0x10000000, LENGTH = 16K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.isr_vector))
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(8);
|
||||
} >flash
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
KEEP(*(.init))
|
||||
KEEP(*(.finit))
|
||||
. = ALIGN(8);
|
||||
_etext = .;
|
||||
} >flash
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(8);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.ARM : {
|
||||
. = ALIGN(8);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_sdata = .;
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
_edata = .;
|
||||
} >ram AT> flash
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(8);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
_sisram2 = LOADADDR(.sram2);
|
||||
|
||||
/* CCM-RAM section
|
||||
*
|
||||
* IMPORTANT NOTE!
|
||||
* If initialized variables will be placed in this section,
|
||||
* the startup code needs to be modified to copy the init-values.
|
||||
*/
|
||||
.sram2 :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_ssram2 = .; /* create a global symbol at sram2 start */
|
||||
*(.sram2)
|
||||
*(.sram2*)
|
||||
|
||||
. = ALIGN(8);
|
||||
_esram2 = .; /* create a global symbol at sram2 end */
|
||||
} >SRAM2 AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
__bss_end__ = _ebss;
|
||||
} > ram
|
||||
|
||||
._stack :
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
end = .;
|
||||
_end = .;
|
||||
. = . + _MIN_STACK_SIZE;
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} > ram
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
@ -1,366 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "stm32l4xx_ll_spi.h"
|
||||
|
||||
#include "ams.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "device.h"
|
||||
#include "nfc.h"
|
||||
|
||||
static void flush_rx()
|
||||
{
|
||||
while(LL_SPI_IsActiveFlag_RXNE(SPI1) != 0)
|
||||
{
|
||||
LL_SPI_ReceiveData8(SPI1);
|
||||
}
|
||||
}
|
||||
static void wait_for_tx()
|
||||
{
|
||||
// while (LL_SPI_IsActiveFlag_BSY(SPI1) == 1)
|
||||
// ;
|
||||
while(LL_SPI_GetTxFIFOLevel(SPI1) != LL_SPI_TX_FIFO_EMPTY)
|
||||
;
|
||||
}
|
||||
static void wait_for_rx()
|
||||
{
|
||||
while(LL_SPI_IsActiveFlag_RXNE(SPI1) == 0)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void ams_print_device(AMS_DEVICE * dev)
|
||||
{
|
||||
printf1(TAG_NFC, "AMS_DEVICE:\r\n");
|
||||
printf1(TAG_NFC, " io_conf: %02x\r\n",dev->regs.io_conf);
|
||||
printf1(TAG_NFC, " ic_conf0: %02x\r\n",dev->regs.ic_conf0);
|
||||
printf1(TAG_NFC, " ic_conf1: %02x\r\n",dev->regs.ic_conf1);
|
||||
printf1(TAG_NFC, " ic_conf2: %02x\r\n",dev->regs.ic_conf2);
|
||||
printf1(TAG_NFC, " rfid_status: %02x\r\n",dev->regs.rfid_status);
|
||||
printf1(TAG_NFC, " ic_status: %02x\r\n",dev->regs.ic_status);
|
||||
printf1(TAG_NFC, " mask_int0: %02x\r\n",dev->regs.mask_int0);
|
||||
printf1(TAG_NFC, " mask_int1: %02x\r\n",dev->regs.mask_int1);
|
||||
printf1(TAG_NFC, " int0: %02x\r\n",dev->regs.int0);
|
||||
printf1(TAG_NFC, " int1: %02x\r\n",dev->regs.int1);
|
||||
printf1(TAG_NFC, " buffer_status2: %02x\r\n",dev->regs.buffer_status2);
|
||||
printf1(TAG_NFC, " buffer_status1: %02x\r\n",dev->regs.buffer_status1);
|
||||
printf1(TAG_NFC, " last_nfc_addr: %02x\r\n",dev->regs.last_nfc_addr);
|
||||
printf1(TAG_NFC, " product_type: %02x\r\n",dev->regs.product_type);
|
||||
printf1(TAG_NFC, " product_subtype:%02x\r\n",dev->regs.product_subtype);
|
||||
printf1(TAG_NFC, " version_maj: %02x\r\n",dev->regs.version_maj);
|
||||
printf1(TAG_NFC, " version_min: %02x\r\n",dev->regs.version_min);
|
||||
}
|
||||
|
||||
static uint8_t send_recv(uint8_t b)
|
||||
{
|
||||
wait_for_tx();
|
||||
LL_SPI_TransmitData8(SPI1, b);
|
||||
wait_for_rx();
|
||||
b = LL_SPI_ReceiveData8(SPI1);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void ams_write_reg(uint8_t addr, uint8_t tx)
|
||||
{
|
||||
send_recv(0x00| addr);
|
||||
send_recv(tx);
|
||||
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
|
||||
uint8_t ams_read_reg(uint8_t addr)
|
||||
{
|
||||
send_recv(0x20| (addr & 0x1f));
|
||||
uint8_t data = send_recv(0);
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
// data must be 14 bytes long
|
||||
void read_reg_block(AMS_DEVICE * dev)
|
||||
{
|
||||
int i;
|
||||
uint8_t mode = 0x20 | (4 );
|
||||
flush_rx();
|
||||
|
||||
send_recv(mode);
|
||||
for (i = 0x04; i < 0x0d; i++)
|
||||
{
|
||||
dev->buf[i] = send_recv(0);
|
||||
}
|
||||
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
void ams_read_buffer(uint8_t * data, int len)
|
||||
{
|
||||
send_recv(0xa0);
|
||||
while(len--)
|
||||
{
|
||||
*data++ = send_recv(0x00);
|
||||
}
|
||||
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
void ams_write_buffer(uint8_t * data, int len)
|
||||
{
|
||||
send_recv(0x80);
|
||||
while(len--)
|
||||
{
|
||||
send_recv(*data++);
|
||||
}
|
||||
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
// data must be 4 bytes
|
||||
void ams_read_eeprom_block(uint8_t block, uint8_t * data)
|
||||
{
|
||||
send_recv(0x7f);
|
||||
send_recv(block << 1);
|
||||
|
||||
data[0] = send_recv(0);
|
||||
data[1] = send_recv(0);
|
||||
data[2] = send_recv(0);
|
||||
data[3] = send_recv(0);
|
||||
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
|
||||
// data must be 4 bytes
|
||||
void ams_write_eeprom_block(uint8_t block, uint8_t * data)
|
||||
{
|
||||
send_recv(0x40);
|
||||
send_recv(block << 1);
|
||||
|
||||
send_recv(data[0]);
|
||||
send_recv(data[1]);
|
||||
send_recv(data[2]);
|
||||
send_recv(data[3]);
|
||||
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
void ams_write_command(uint8_t cmd)
|
||||
{
|
||||
send_recv(0xc0 | cmd);
|
||||
UNSELECT();
|
||||
SELECT();
|
||||
}
|
||||
|
||||
const char * ams_get_state_string(uint8_t regval)
|
||||
{
|
||||
if (regval & AMS_STATE_INVALID)
|
||||
{
|
||||
return "STATE_INVALID";
|
||||
}
|
||||
switch (regval & AMS_STATE_MASK)
|
||||
{
|
||||
case AMS_STATE_OFF:
|
||||
return "STATE_OFF";
|
||||
case AMS_STATE_SENSE:
|
||||
return "STATE_SENSE";
|
||||
case AMS_STATE_RESOLUTION:
|
||||
return "STATE_RESOLUTION";
|
||||
case AMS_STATE_RESOLUTION_L2:
|
||||
return "STATE_RESOLUTION_L2";
|
||||
case AMS_STATE_SELECTED:
|
||||
return "STATE_SELECTED";
|
||||
case AMS_STATE_SECTOR2:
|
||||
return "STATE_SECTOR2";
|
||||
case AMS_STATE_SECTORX_2:
|
||||
return "STATE_SECTORX_2";
|
||||
case AMS_STATE_SELECTEDX:
|
||||
return "STATE_SELECTEDX";
|
||||
case AMS_STATE_SENSEX_L2:
|
||||
return "STATE_SENSEX_L2";
|
||||
case AMS_STATE_SENSEX:
|
||||
return "STATE_SENSEX";
|
||||
case AMS_STATE_SLEEP:
|
||||
return "STATE_SLEEP";
|
||||
}
|
||||
return "STATE_WRONG";
|
||||
}
|
||||
|
||||
int ams_state_is_valid(uint8_t regval)
|
||||
{
|
||||
if (regval & AMS_STATE_INVALID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
switch (regval & AMS_STATE_MASK)
|
||||
{
|
||||
case AMS_STATE_OFF:
|
||||
case AMS_STATE_SENSE:
|
||||
case AMS_STATE_RESOLUTION:
|
||||
case AMS_STATE_RESOLUTION_L2:
|
||||
case AMS_STATE_SELECTED:
|
||||
case AMS_STATE_SECTOR2:
|
||||
case AMS_STATE_SECTORX_2:
|
||||
case AMS_STATE_SELECTEDX:
|
||||
case AMS_STATE_SENSEX_L2:
|
||||
case AMS_STATE_SENSEX:
|
||||
case AMS_STATE_SLEEP:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ams_print_int0(uint8_t int0)
|
||||
{
|
||||
#if DEBUG_LEVEL
|
||||
uint32_t tag = (TAG_NFC)|(TAG_NO_TAG);
|
||||
printf1(TAG_NFC," ");
|
||||
if (int0 & AMS_INT_XRF)
|
||||
printf1(tag," XRF");
|
||||
if (int0 & AMS_INT_TXE)
|
||||
printf1(tag," TXE");
|
||||
if (int0 & AMS_INT_RXE)
|
||||
printf1(tag," RXE");
|
||||
if (int0 & AMS_INT_EER_RF)
|
||||
printf1(tag," EER_RF");
|
||||
if (int0 & AMS_INT_EEW_RF)
|
||||
printf1(tag," EEW_RF");
|
||||
if (int0 & AMS_INT_SLP)
|
||||
printf1(tag," SLP");
|
||||
if (int0 & AMS_INT_WU_A)
|
||||
printf1(tag," WU_A");
|
||||
if (int0 & AMS_INT_INIT)
|
||||
printf1(tag," INIT");
|
||||
|
||||
printf1(tag,"\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ams_print_int1(uint8_t int0)
|
||||
{
|
||||
#if DEBUG_LEVEL
|
||||
uint32_t tag = (TAG_NFC)|(TAG_NO_TAG);
|
||||
printf1(TAG_NFC," ");
|
||||
if (int0 & AMS_INT_ACC_ERR)
|
||||
printf1(tag," ACC_ERR");
|
||||
if (int0 & AMS_INT_EEAC_ERR)
|
||||
printf1(tag," EEAC_ERR");
|
||||
if (int0 & AMS_INT_IO_EEWR)
|
||||
printf1(tag," IO_EEWR");
|
||||
if (int0 & AMS_INT_BF_ERR)
|
||||
printf1(tag," BF_ERR");
|
||||
if (int0 & AMS_INT_CRC_ERR)
|
||||
printf1(tag," CRC_ERR");
|
||||
if (int0 & AMS_INT_PAR_ERR)
|
||||
printf1(tag," PAR_ERR");
|
||||
if (int0 & AMS_INT_FRM_ERR)
|
||||
printf1(tag," FRM_ERR");
|
||||
if (int0 & AMS_INT_RXS)
|
||||
printf1(tag," RXS");
|
||||
|
||||
printf1(tag,"\r\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ams_init()
|
||||
{
|
||||
LL_GPIO_SetPinMode(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN,LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN);
|
||||
|
||||
LL_SPI_SetClockPolarity(SPI1,LL_SPI_POLARITY_LOW);
|
||||
LL_SPI_SetClockPhase(SPI1,LL_SPI_PHASE_2EDGE);
|
||||
LL_SPI_SetRxFIFOThreshold(SPI1,LL_SPI_RX_FIFO_TH_QUARTER);
|
||||
LL_SPI_Enable(SPI1);
|
||||
|
||||
// delay(10);
|
||||
SELECT();
|
||||
delay(1);
|
||||
}
|
||||
|
||||
void ams_configure()
|
||||
{
|
||||
// Should not be used during passive operation.
|
||||
uint8_t block[4];
|
||||
|
||||
// check connection
|
||||
uint8_t productType = ams_read_reg(AMS_REG_PRODUCT_TYPE);
|
||||
if (productType != 0x14)
|
||||
{
|
||||
printf1(TAG_ERR, "Have wrong product type [0x%02x]. AMS3956 connection error.\n", productType);
|
||||
}
|
||||
|
||||
printf1(TAG_NFC,"AMS3956 product type 0x%02x.\n", productType);
|
||||
|
||||
ams_read_eeprom_block(AMS_CONFIG_UID_ADDR, block);
|
||||
printf1(TAG_NFC,"UID: 3F 14 02 - "); dump_hex1(TAG_NFC,block,4);
|
||||
|
||||
ams_read_eeprom_block(AMS_CONFIG_BLOCK0_ADDR, block);
|
||||
printf1(TAG_NFC,"conf0: "); dump_hex1(TAG_NFC,block,4);
|
||||
|
||||
uint8_t sense1 = 0x44;
|
||||
uint8_t sense2 = 0x00;
|
||||
uint8_t selr = 0x20; // SAK
|
||||
|
||||
if(block[0] != sense1 || block[1] != sense2 || block[2] != selr)
|
||||
{
|
||||
printf1(TAG_NFC,"Writing config block 0\r\n");
|
||||
block[0] = sense1;
|
||||
block[1] = sense2;
|
||||
block[2] = selr;
|
||||
block[3] = 0x00;
|
||||
|
||||
ams_write_eeprom_block(AMS_CONFIG_BLOCK0_ADDR, block);
|
||||
UNSELECT();
|
||||
delay(10);
|
||||
SELECT();
|
||||
delay(10);
|
||||
|
||||
ams_read_eeprom_block(AMS_CONFIG_BLOCK0_ADDR, block);
|
||||
printf1(TAG_NFC,"conf0: "); dump_hex1(TAG_NFC,block,4);
|
||||
}
|
||||
|
||||
ams_read_eeprom_block(AMS_CONFIG_BLOCK1_ADDR, block);
|
||||
printf1(TAG_NFC,"conf1: "); dump_hex1(TAG_NFC,block,4);
|
||||
|
||||
uint8_t ic_cfg1 = AMS_CFG1_OUTPUT_RESISTANCE_100 | AMS_CFG1_VOLTAGE_LEVEL_2V0;
|
||||
uint8_t ic_cfg2 = AMS_CFG2_TUN_MOD;
|
||||
|
||||
if (block[0] != ic_cfg1 || block[1] != ic_cfg2)
|
||||
{
|
||||
|
||||
printf1(TAG_NFC,"Writing config block 1\r\n");
|
||||
|
||||
ams_write_reg(AMS_REG_IC_CONF1,ic_cfg1);
|
||||
ams_write_reg(AMS_REG_IC_CONF2,ic_cfg2);
|
||||
|
||||
// set IC_CFG1
|
||||
block[0] = ic_cfg1;
|
||||
|
||||
// set IC_CFG2
|
||||
block[1] = ic_cfg2;
|
||||
|
||||
// mask interrupt bits
|
||||
block[2] = 0x80;
|
||||
block[3] = 0;
|
||||
|
||||
ams_write_eeprom_block(AMS_CONFIG_BLOCK1_ADDR, block);
|
||||
|
||||
UNSELECT();
|
||||
delay(10);
|
||||
SELECT();
|
||||
delay(10);
|
||||
|
||||
ams_read_eeprom_block(0x7F, block);
|
||||
printf1(TAG_NFC,"conf1: "); dump_hex1(TAG_NFC,block,4);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,162 +0,0 @@
|
||||
// AS3956 interface
|
||||
// https://ams.com/as3956
|
||||
// https://ams.com/documents/20143/36005/AS3956_DS000546_7-00.pdf
|
||||
|
||||
#ifndef _AMS_H_
|
||||
#define _AMS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
|
||||
|
||||
typedef union
|
||||
{
|
||||
uint8_t buf[0x20];
|
||||
struct {
|
||||
uint8_t io_conf; // 0x00
|
||||
uint8_t ic_conf0; // 0x01
|
||||
uint8_t ic_conf1; // 0x02
|
||||
uint8_t ic_conf2; // 0x03
|
||||
uint8_t rfid_status; // 0x04
|
||||
uint8_t ic_status; // 0x05
|
||||
uint8_t _nc0[2]; // 0x06 - 0x07
|
||||
uint8_t mask_int0; // 0x08
|
||||
uint8_t mask_int1; // 0x09
|
||||
uint8_t int0; // 0x0a
|
||||
uint8_t int1; // 0x0b
|
||||
uint8_t buffer_status2; // 0x0c
|
||||
uint8_t buffer_status1; // 0x0d
|
||||
uint8_t last_nfc_addr; // 0x0e
|
||||
uint8_t _nc1[0x1b - 0x0f + 1]; // 0x0f - 0x1b
|
||||
uint8_t product_type; // 0x1c
|
||||
uint8_t product_subtype; // 0x1d
|
||||
uint8_t version_maj; // 0x1e
|
||||
uint8_t version_min; // 0x1f
|
||||
} regs;
|
||||
} __attribute__((packed)) AMS_DEVICE;
|
||||
|
||||
#define SELECT() LL_GPIO_ResetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
|
||||
#define UNSELECT() LL_GPIO_SetOutputPin(SOLO_AMS_CS_PORT,SOLO_AMS_CS_PIN)
|
||||
|
||||
void ams_init();
|
||||
void ams_configure();
|
||||
|
||||
void ams_read_buffer(uint8_t * data, int len);
|
||||
void ams_write_buffer(uint8_t * data, int len);
|
||||
|
||||
void ams_write_command(uint8_t cmd);
|
||||
|
||||
void read_reg_block(AMS_DEVICE * dev);
|
||||
|
||||
uint8_t ams_read_reg(uint8_t addr);
|
||||
|
||||
void ams_write_reg(uint8_t addr, uint8_t tx);
|
||||
|
||||
const char * ams_get_state_string(uint8_t regval);
|
||||
int ams_state_is_valid(uint8_t regval);
|
||||
|
||||
|
||||
#define AMS_REG_IO_CONF 0x00
|
||||
#define AMS_REG_IC_CONF0 0x01
|
||||
#define AMS_REG_IC_CONF1 0x02
|
||||
#define AMS_REG_IC_CONF2 0x03
|
||||
#define AMS_RFCFG_EN 0x80
|
||||
#define AMS_TUN_MOD 0x40
|
||||
#define AMS_REG_RFID_STATUS 0x04
|
||||
#define AMS_HF_PON 0x80
|
||||
#define AMS_STATE_MASK 0x78
|
||||
#define AMS_STATE_INVALID 0x04
|
||||
#define AMS_STATE_OFF (0 << 3)
|
||||
#define AMS_STATE_SENSE (1 << 3)
|
||||
#define AMS_STATE_RESOLUTION (3 << 3)
|
||||
#define AMS_STATE_RESOLUTION_L2 (2 << 3)
|
||||
#define AMS_STATE_SELECTED (6 << 3)
|
||||
#define AMS_STATE_SECTOR2 (7 << 3)
|
||||
#define AMS_STATE_SECTORX_2 (0xf << 3)
|
||||
#define AMS_STATE_SELECTEDX (0xe << 3)
|
||||
#define AMS_STATE_SENSEX_L2 (0xa << 3)
|
||||
#define AMS_STATE_SENSEX (0xb << 3)
|
||||
#define AMS_STATE_SLEEP (0x9 << 3)
|
||||
// ... //
|
||||
#define AMS_REG_MASK_INT0 0x08
|
||||
#define AMS_MASK0_PU (1<<7) // power up
|
||||
#define AMS_MASK0_WU_A (1<<6) // selected INT
|
||||
#define AMS_MASK0_SLP (1<<5)
|
||||
#define AMS_MASK0_EEW_RF (1<<4)
|
||||
#define AMS_MASK0_EER_RF (1<<3)
|
||||
#define AMS_MASK0_RXE (1<<2)
|
||||
#define AMS_MASK0_TXE (1<<1)
|
||||
#define AMS_MASK0_XRF (1<<0)
|
||||
#define AMS_REG_MASK_INT1 0x09
|
||||
#define AMS_REG_INT0 0x0a
|
||||
#define AMS_INT_XRF (1<<0)
|
||||
#define AMS_INT_TXE (1<<1)
|
||||
#define AMS_INT_RXE (1<<2)
|
||||
#define AMS_INT_EER_RF (1<<3)
|
||||
#define AMS_INT_EEW_RF (1<<4)
|
||||
#define AMS_INT_SLP (1<<5)
|
||||
#define AMS_INT_WU_A (1<<6)
|
||||
#define AMS_INT_INIT (1<<7)
|
||||
#define AMS_REG_INT1 0x0b
|
||||
#define AMS_INT_ACC_ERR (1<<0)
|
||||
#define AMS_INT_EEAC_ERR (1<<1)
|
||||
#define AMS_INT_IO_EEWR (1<<2)
|
||||
#define AMS_INT_BF_ERR (1<<3)
|
||||
#define AMS_INT_CRC_ERR (1<<4)
|
||||
#define AMS_INT_PAR_ERR (1<<5)
|
||||
#define AMS_INT_FRM_ERR (1<<6)
|
||||
#define AMS_INT_RXS (1<<7)
|
||||
#define AMS_REG_BUF2 0x0c
|
||||
#define AMS_BUF_LEN_MASK 0x1f
|
||||
#define AMS_BUF_INVALID 0x80
|
||||
#define AMS_REG_BUF1 0x0d
|
||||
// ... //
|
||||
#define AMS_REG_PRODUCT_TYPE 0x1c
|
||||
#define AMS_REG_PRODUCT_SUBTYPE 0x1d
|
||||
#define AMS_REG_VERSION_MAJOR 0x1e
|
||||
#define AMS_REG_VERSION_MINOR 0x1f
|
||||
|
||||
#define AMS_CONFIG_UID_ADDR 0x00
|
||||
#define AMS_CONFIG_BLOCK0_ADDR 0x7e
|
||||
#define AMS_CONFIG_BLOCK1_ADDR 0x7f
|
||||
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_1V9 (0x00<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V0 (0x01<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V1 (0x02<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V2 (0x03<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V3 (0x04<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V4 (0x05<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V5 (0x06<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V6 (0x07<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V7 (0x08<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V8 (0x09<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_2V9 (0x0a<<2)
|
||||
#define AMS_CFG1_VOLTAGE_LEVEL_3V0 (0x0b<<2)
|
||||
|
||||
#define AMS_CFG1_OUTPUT_RESISTANCE_ZZ 0x00
|
||||
#define AMS_CFG1_OUTPUT_RESISTANCE_100 0x01
|
||||
#define AMS_CFG1_OUTPUT_RESISTANCE_50 0x02
|
||||
#define AMS_CFG1_OUTPUT_RESISTANCE_25 0x03
|
||||
|
||||
#define AMS_CFG2_RFCFG_EN (1<<7)
|
||||
#define AMS_CFG2_TUN_MOD (1<<6)
|
||||
|
||||
#define AMS_CMD_DEFAULT 0x02
|
||||
#define AMS_CMD_CLEAR_BUFFER 0x04
|
||||
#define AMS_CMD_RESTART_TRANSCEIVER 0x06
|
||||
#define AMS_CMD_DIS_EN_TRANSCEIVER 0x07
|
||||
#define AMS_CMD_TRANSMIT_BUFFER 0x08
|
||||
#define AMS_CMD_TRANSMIT_ACK 0x09
|
||||
#define AMS_CMD_TRANSMIT_NACK0 0x0A
|
||||
#define AMS_CMD_TRANSMIT_NACK1 0x0B
|
||||
#define AMS_CMD_TRANSMIT_NACK4 0x0D
|
||||
#define AMS_CMD_TRANSMIT_NACK5 0x0C
|
||||
#define AMS_CMD_SLEEP 0x10
|
||||
#define AMS_CMD_SENSE 0x11
|
||||
#define AMS_CMD_SENSE_SLEEP 0x12
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
@ -30,7 +30,6 @@
|
||||
// #define DISABLE_CTAPHID_WINK
|
||||
// #define DISABLE_CTAPHID_CBOR
|
||||
|
||||
#define ENABLE_SERIAL_PRINTING
|
||||
|
||||
#if defined(SOLO_HACKER)
|
||||
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION
|
||||
@ -39,7 +38,7 @@
|
||||
#endif
|
||||
|
||||
void printing_init();
|
||||
void hw_init(int lf);
|
||||
void hw_init(void);
|
||||
|
||||
//#define TEST
|
||||
//#define TEST_POWER
|
||||
@ -64,12 +63,6 @@ void hw_init(int lf);
|
||||
#define SOLO_BUTTON_PORT GPIOA
|
||||
#define SOLO_BUTTON_PIN LL_GPIO_PIN_0
|
||||
|
||||
#define SOLO_AMS_CS_PORT GPIOB
|
||||
#define SOLO_AMS_CS_PIN LL_GPIO_PIN_0
|
||||
|
||||
#define SOLO_AMS_IRQ_PORT GPIOC
|
||||
#define SOLO_AMS_IRQ_PIN LL_GPIO_PIN_15
|
||||
|
||||
#define SKIP_BUTTON_CHECK_WITH_DELAY 0
|
||||
#define SKIP_BUTTON_CHECK_FAST 0
|
||||
|
||||
|
@ -24,9 +24,6 @@
|
||||
#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"
|
||||
@ -51,7 +48,6 @@ typedef enum
|
||||
|
||||
|
||||
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;
|
||||
@ -66,9 +62,6 @@ void crypto_sha256_init()
|
||||
sha256_init(&sha256_ctx);
|
||||
}
|
||||
|
||||
void crypto_sha512_init() {
|
||||
cf_sha512_init(&sha512_ctx);
|
||||
}
|
||||
|
||||
void crypto_load_master_secret(uint8_t * key)
|
||||
{
|
||||
@ -93,10 +86,6 @@ 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);
|
||||
@ -107,11 +96,6 @@ 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];
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "stm32l4xx_ll_gpio.h"
|
||||
#include "stm32l4xx_ll_tim.h"
|
||||
#include "stm32l4xx_ll_usart.h"
|
||||
#include "stm32l4xx_ll_pwr.h"
|
||||
#include "usbd_hid.h"
|
||||
|
||||
#include APP_CONFIG
|
||||
@ -27,11 +26,6 @@
|
||||
#include "memory_layout.h"
|
||||
#include "stm32l4xx_ll_iwdg.h"
|
||||
#include "usbd_cdc_if.h"
|
||||
#include "nfc.h"
|
||||
#include "init.h"
|
||||
|
||||
#define LOW_FREQUENCY 1
|
||||
#define HIGH_FREQUENCY 0
|
||||
|
||||
void wait_for_usb_tether();
|
||||
|
||||
@ -40,8 +34,6 @@ uint32_t __90_ms = 0;
|
||||
uint32_t __device_status = 0;
|
||||
uint32_t __last_update = 0;
|
||||
extern PCD_HandleTypeDef hpcd;
|
||||
static bool haveNFC = 0;
|
||||
static bool isLowFreq = 0;
|
||||
|
||||
#define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
|
||||
|
||||
@ -58,13 +50,6 @@ void TIM6_DAC_IRQHandler()
|
||||
ctaphid_update_status(__device_status);
|
||||
}
|
||||
}
|
||||
#ifndef IS_BOOTLOADER
|
||||
// NFC sending WTX if needs
|
||||
if (device_is_nfc())
|
||||
{
|
||||
WTX_timer_exec();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Global USB interrupt handler
|
||||
@ -106,42 +91,32 @@ void device_reboot()
|
||||
{
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void device_init()
|
||||
{
|
||||
hw_init();
|
||||
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
|
||||
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
|
||||
|
||||
hw_init(LOW_FREQUENCY);
|
||||
isLowFreq = 0;
|
||||
|
||||
haveNFC = nfc_init();
|
||||
|
||||
if (haveNFC)
|
||||
{
|
||||
printf1(TAG_NFC, "Have NFC\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_NFC, "Have NO NFC\r\n");
|
||||
hw_init(HIGH_FREQUENCY);
|
||||
isLowFreq = 0;
|
||||
}
|
||||
|
||||
usbhid_init();
|
||||
ctaphid_init();
|
||||
ctap_init();
|
||||
|
||||
#ifndef IS_BOOTLOADER
|
||||
#if BOOT_TO_DFU
|
||||
flash_option_bytes_init(1);
|
||||
#else
|
||||
flash_option_bytes_init(0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
printf1(TAG_GEN,"hello solo\r\n");
|
||||
}
|
||||
|
||||
bool device_is_nfc()
|
||||
void usb_init(void);
|
||||
void usbhid_init()
|
||||
{
|
||||
return haveNFC;
|
||||
usb_init();
|
||||
|
||||
#if DEBUG_LEVEL>1
|
||||
wait_for_usb_tether();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void wait_for_usb_tether()
|
||||
@ -155,26 +130,6 @@ void wait_for_usb_tether()
|
||||
;
|
||||
}
|
||||
|
||||
void usbhid_init()
|
||||
{
|
||||
if (!isLowFreq)
|
||||
{
|
||||
init_usb();
|
||||
|
||||
#if DEBUG_LEVEL>1
|
||||
wait_for_usb_tether();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int usbhid_recv(uint8_t * msg)
|
||||
{
|
||||
if (fifo_hidmsg_size())
|
||||
@ -411,7 +366,6 @@ uint32_t ctap_atomic_count(int sel)
|
||||
}
|
||||
|
||||
|
||||
|
||||
void device_manage()
|
||||
{
|
||||
#if NON_BLOCK_PRINTING
|
||||
@ -432,10 +386,6 @@ void device_manage()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifndef IS_BOOTLOADER
|
||||
// if(device_is_nfc())
|
||||
nfc_loop();
|
||||
#endif
|
||||
}
|
||||
|
||||
static int handle_packets()
|
||||
@ -593,7 +543,7 @@ void ctap_overwrite_rk(int index,CTAP_residentKey * rk)
|
||||
|
||||
memmove(tmppage + (sizeof(CTAP_residentKey) * index) % PAGE_SIZE, rk, sizeof(CTAP_residentKey));
|
||||
flash_erase_page(page);
|
||||
flash_write(flash_addr(page), tmppage, PAGE_SIZE);
|
||||
flash_write(flash_addr(page), tmppage, ((sizeof(CTAP_residentKey) * (index + 1)) % PAGE_SIZE) );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -18,7 +18,6 @@
|
||||
#include "stm32l4xx_ll_bus.h"
|
||||
#include "stm32l4xx_ll_tim.h"
|
||||
#include "stm32l4xx_ll_rng.h"
|
||||
#include "stm32l4xx_ll_spi.h"
|
||||
#include "stm32l4xx_ll_usb.h"
|
||||
#include "stm32l4xx_hal_pcd.h"
|
||||
#include "stm32l4xx_hal.h"
|
||||
@ -30,86 +29,57 @@
|
||||
#include "usbd_composite.h"
|
||||
#include "usbd_cdc_if.h"
|
||||
#include "device.h"
|
||||
#include "init.h"
|
||||
#include APP_CONFIG
|
||||
|
||||
// KHz
|
||||
#define MAX_CLOCK_RATE 24000
|
||||
/* USER CODE BEGIN Includes */
|
||||
|
||||
#define SET_CLOCK_RATE2() SystemClock_Config()
|
||||
/* USER CODE END Includes */
|
||||
|
||||
#if MAX_CLOCK_RATE == 48000
|
||||
#define SET_CLOCK_RATE0() SystemClock_Config_LF32()
|
||||
#define SET_CLOCK_RATE1() SystemClock_Config_LF48()
|
||||
#elif MAX_CLOCK_RATE == 32000
|
||||
#define SET_CLOCK_RATE0() SystemClock_Config_LF24()
|
||||
#define SET_CLOCK_RATE1() SystemClock_Config_LF32()
|
||||
#elif MAX_CLOCK_RATE == 28000
|
||||
#define SET_CLOCK_RATE0() SystemClock_Config_LF24()
|
||||
#define SET_CLOCK_RATE1() SystemClock_Config_LF28()
|
||||
#elif MAX_CLOCK_RATE == 24000
|
||||
#define SET_CLOCK_RATE0() SystemClock_Config_LF16()
|
||||
#define SET_CLOCK_RATE1() SystemClock_Config_LF24()
|
||||
#elif MAX_CLOCK_RATE == 20000
|
||||
#define SET_CLOCK_RATE0() SystemClock_Config_LF16()
|
||||
#define SET_CLOCK_RATE1() SystemClock_Config_LF20()
|
||||
#elif MAX_CLOCK_RATE == 16000
|
||||
#define SET_CLOCK_RATE0() SystemClock_Config_LF8()
|
||||
#define SET_CLOCK_RATE1() SystemClock_Config_LF16()
|
||||
#else
|
||||
#error "Invalid clock rate selected"
|
||||
#endif
|
||||
/* Private variables ---------------------------------------------------------*/
|
||||
|
||||
USBD_HandleTypeDef Solo_USBD_Device;
|
||||
|
||||
/* Private function prototypes -----------------------------------------------*/
|
||||
static void LL_Init(void);
|
||||
void SystemClock_Config(void);
|
||||
static void MX_GPIO_Init(void);
|
||||
#if DEBUG_LEVEL > 0
|
||||
static void MX_USART1_UART_Init(void);
|
||||
#endif
|
||||
static void MX_TIM2_Init(void);
|
||||
static void MX_TIM6_Init(void);
|
||||
static void MX_RNG_Init(void);
|
||||
|
||||
#define Error_Handler() _Error_Handler(__FILE__,__LINE__)
|
||||
void _Error_Handler(char *file, int line);
|
||||
|
||||
void SystemClock_Config(void);
|
||||
void SystemClock_Config_LF16(void);
|
||||
void SystemClock_Config_LF20(void);
|
||||
void SystemClock_Config_LF24(void);
|
||||
void SystemClock_Config_LF28(void);
|
||||
void SystemClock_Config_LF48(void);
|
||||
|
||||
void hw_init(int lowfreq)
|
||||
void hw_init(void)
|
||||
{
|
||||
#ifdef IS_BOOTLOADER
|
||||
SCB->VTOR = FLASH_BASE;
|
||||
#else
|
||||
#endif
|
||||
LL_Init();
|
||||
init_gpio();
|
||||
|
||||
if (lowfreq)
|
||||
{
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2); // Under voltage
|
||||
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2);
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemClock_Config();
|
||||
}
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
SystemClock_Config(); // TODO bootloader should not change clk freq.
|
||||
|
||||
MX_GPIO_Init();
|
||||
MX_TIM2_Init(); // PWM for LEDs
|
||||
|
||||
if (!lowfreq)
|
||||
{
|
||||
init_pwm();
|
||||
}
|
||||
|
||||
init_millisecond_timer(lowfreq);
|
||||
MX_TIM6_Init(); // ~1 ms timer
|
||||
|
||||
#if DEBUG_LEVEL > 0
|
||||
init_debug_uart();
|
||||
MX_USART1_UART_Init();// debug uart
|
||||
#endif
|
||||
|
||||
init_rng();
|
||||
init_spi();
|
||||
MX_RNG_Init();
|
||||
|
||||
TIM6->SR = 0;
|
||||
__enable_irq();
|
||||
NVIC_EnableIRQ(TIM6_IRQn);
|
||||
}
|
||||
|
||||
static void LL_Init(void)
|
||||
@ -137,29 +107,12 @@ static void LL_Init(void)
|
||||
|
||||
}
|
||||
|
||||
void device_set_clock_rate(DEVICE_CLOCK_RATE param)
|
||||
{
|
||||
switch(param)
|
||||
{
|
||||
case DEVICE_LOW_POWER_IDLE:
|
||||
SET_CLOCK_RATE0();
|
||||
break;
|
||||
case DEVICE_LOW_POWER_FAST:
|
||||
SET_CLOCK_RATE1();
|
||||
break;
|
||||
case DEVICE_FAST:
|
||||
SET_CLOCK_RATE2();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief System Clock Configuration
|
||||
* @retval None
|
||||
*/
|
||||
void SystemClock_Config(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
|
||||
|
||||
@ -175,16 +128,9 @@ void SystemClock_Config(void)
|
||||
while(LL_RCC_HSI48_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
@ -241,463 +187,7 @@ void SystemClock_Config(void)
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
}
|
||||
|
||||
void SystemClock_Config_LF4(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
|
||||
|
||||
LL_Init1msTick(4000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(4000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
// 8MHz
|
||||
void SystemClock_Config_LF8(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_7);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
|
||||
|
||||
LL_Init1msTick(8000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(8000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
// 16MHz
|
||||
void SystemClock_Config_LF16(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_8);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_8);
|
||||
|
||||
LL_Init1msTick(16000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(16000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_0)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 24 MHz
|
||||
void SystemClock_Config_LF24(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE2);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_9);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_8);
|
||||
|
||||
LL_Init1msTick(24000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(24000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
|
||||
}
|
||||
|
||||
// 32 MHz
|
||||
void SystemClock_Config_LF32(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_10);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_16);
|
||||
|
||||
LL_Init1msTick(32000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(32000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
|
||||
}
|
||||
|
||||
// 28 MHz
|
||||
void SystemClock_Config_LF28(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_1);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_1)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
|
||||
|
||||
LL_RCC_HSI_Enable();
|
||||
|
||||
/* Wait till HSI is ready */
|
||||
while(LL_RCC_HSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_HSI_SetCalibTrimming(16);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_6);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLLM_DIV_2, 28, LL_RCC_PLLR_DIV_8);
|
||||
|
||||
LL_RCC_PLL_EnableDomain_SYS();
|
||||
|
||||
LL_RCC_PLL_Enable();
|
||||
|
||||
/* Wait till PLL is ready */
|
||||
while(LL_RCC_PLL_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_8);
|
||||
|
||||
LL_Init1msTick(28000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(28000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
}
|
||||
|
||||
// 48 MHz
|
||||
void SystemClock_Config_LF48(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
|
||||
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_2);
|
||||
|
||||
if(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_2)
|
||||
{
|
||||
Error_Handler();
|
||||
}
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
|
||||
|
||||
LL_RCC_LSI_Enable();
|
||||
|
||||
/* Wait till LSI is ready */
|
||||
while(LL_RCC_LSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_Enable();
|
||||
|
||||
/* Wait till MSI is ready */
|
||||
while(LL_RCC_MSI_IsReady() != 1)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_MSI_EnableRangeSelection();
|
||||
|
||||
LL_RCC_MSI_SetRange(LL_RCC_MSIRANGE_11);
|
||||
|
||||
LL_RCC_MSI_SetCalibTrimming(0);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_MSI);
|
||||
|
||||
/* Wait till System clock is ready */
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_MSI)
|
||||
{
|
||||
|
||||
}
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_16);
|
||||
|
||||
LL_Init1msTick(48000000);
|
||||
|
||||
LL_SYSTICK_SetClkSource(LL_SYSTICK_CLKSOURCE_HCLK);
|
||||
|
||||
LL_SetSystemCoreClock(48000000);
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_MSI);
|
||||
|
||||
/* SysTick_IRQn interrupt configuration */
|
||||
NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 20 MHz
|
||||
void SystemClock_Config_LF20(void)
|
||||
{
|
||||
SET_BIT(RCC->APB1ENR1, RCC_APB1ENR1_PWREN);
|
||||
}
|
||||
|
||||
void init_usb()
|
||||
void usb_init()
|
||||
{
|
||||
// enable USB power
|
||||
SET_BIT(PWR->CR2, PWR_CR2_USV);
|
||||
@ -727,7 +217,8 @@ void init_usb()
|
||||
USBD_Start(&Solo_USBD_Device);
|
||||
}
|
||||
|
||||
void init_pwm(void)
|
||||
/* TIM2 init function */
|
||||
static void MX_TIM2_Init(void)
|
||||
{
|
||||
|
||||
LL_TIM_InitTypeDef TIM_InitStruct;
|
||||
@ -798,7 +289,9 @@ void init_pwm(void)
|
||||
|
||||
}
|
||||
|
||||
void init_debug_uart(void)
|
||||
#if DEBUG_LEVEL > 0
|
||||
/* USART1 init function */
|
||||
static void MX_USART1_UART_Init(void)
|
||||
{
|
||||
|
||||
LL_USART_InitTypeDef USART_InitStruct;
|
||||
@ -808,8 +301,6 @@ void init_debug_uart(void)
|
||||
/* Peripheral clock enable */
|
||||
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
|
||||
|
||||
|
||||
LL_USART_DeInit(USART1);
|
||||
/**USART1 GPIO Configuration
|
||||
PB6 ------> USART1_TX
|
||||
PB7 ------> USART1_RX
|
||||
@ -836,37 +327,22 @@ void init_debug_uart(void)
|
||||
LL_USART_Enable(USART1);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void init_gpio(void)
|
||||
/** Pinout Configuration
|
||||
*/
|
||||
static void MX_GPIO_Init(void)
|
||||
{
|
||||
|
||||
/* GPIO Ports Clock Enable */
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
|
||||
|
||||
|
||||
|
||||
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);
|
||||
|
||||
#ifdef SOLO_AMS_IRQ_PORT
|
||||
// SAVE POWER
|
||||
// LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
|
||||
// /**/
|
||||
// LL_GPIO_InitTypeDef GPIO_InitStruct;
|
||||
// GPIO_InitStruct.Pin = SOLO_AMS_IRQ_PIN;
|
||||
// GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
|
||||
// GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||
// LL_GPIO_Init(SOLO_AMS_IRQ_PORT, &GPIO_InitStruct);
|
||||
//
|
||||
//
|
||||
// LL_GPIO_SetPinMode(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_MODE_INPUT);
|
||||
// LL_GPIO_SetPinPull(SOLO_AMS_IRQ_PORT,SOLO_AMS_IRQ_PIN,LL_GPIO_PULL_UP);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void init_millisecond_timer(int lf)
|
||||
|
||||
/* TIM6 init function */
|
||||
static void MX_TIM6_Init(void)
|
||||
{
|
||||
|
||||
LL_TIM_InitTypeDef TIM_InitStruct;
|
||||
@ -876,11 +352,7 @@ void init_millisecond_timer(int lf)
|
||||
|
||||
// 48 MHz sys clock --> 6 MHz timer clock
|
||||
// 48 MHz / 48000 == 1000 Hz
|
||||
if (!lf)
|
||||
TIM_InitStruct.Prescaler = 48000;
|
||||
else
|
||||
TIM_InitStruct.Prescaler = MAX_CLOCK_RATE;
|
||||
|
||||
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||
TIM_InitStruct.Autoreload = 90;
|
||||
LL_TIM_Init(TIM6, &TIM_InitStruct);
|
||||
@ -896,14 +368,39 @@ void init_millisecond_timer(int lf)
|
||||
|
||||
// Start immediately
|
||||
LL_TIM_EnableCounter(TIM6);
|
||||
|
||||
TIM6->SR = 0;
|
||||
__enable_irq();
|
||||
NVIC_EnableIRQ(TIM6_IRQn);
|
||||
}
|
||||
|
||||
/* TIM7 init function */
|
||||
// static void MX_TIM7_Init(void)
|
||||
// {
|
||||
//
|
||||
// LL_TIM_InitTypeDef TIM_InitStruct;
|
||||
//
|
||||
// /* Peripheral clock enable */
|
||||
// LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM7);
|
||||
//
|
||||
// // 48 MHz sys clock --> 6 MHz timer clock
|
||||
// // 6 MHz / 6000 == 1000 Hz
|
||||
// TIM_InitStruct.Prescaler = 48000;
|
||||
// TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||
// TIM_InitStruct.Autoreload = 0xffff;
|
||||
// LL_TIM_Init(TIM6, &TIM_InitStruct);
|
||||
//
|
||||
// LL_TIM_DisableARRPreload(TIM7);
|
||||
//
|
||||
// LL_TIM_SetTriggerOutput(TIM7, LL_TIM_TRGO_RESET);
|
||||
//
|
||||
// LL_TIM_DisableMasterSlaveMode(TIM7);
|
||||
//
|
||||
// // enable interrupt
|
||||
// TIM7->DIER |= 1;
|
||||
//
|
||||
// // Start immediately
|
||||
// LL_TIM_EnableCounter(TIM7);
|
||||
// }
|
||||
|
||||
void init_rng(void)
|
||||
/* RNG init function */
|
||||
static void MX_RNG_Init(void)
|
||||
{
|
||||
|
||||
/* Peripheral clock enable */
|
||||
@ -912,45 +409,3 @@ void init_rng(void)
|
||||
LL_RNG_Enable(RNG);
|
||||
|
||||
}
|
||||
|
||||
/* SPI1 init function */
|
||||
void init_spi(void)
|
||||
{
|
||||
|
||||
LL_SPI_InitTypeDef SPI_InitStruct;
|
||||
|
||||
LL_GPIO_InitTypeDef GPIO_InitStruct;
|
||||
|
||||
/* Peripheral clock enable */
|
||||
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SPI1);
|
||||
|
||||
/**SPI1 GPIO Configuration
|
||||
PA5 ------> SPI1_SCK
|
||||
PA6 ------> SPI1_MISO
|
||||
PA7 ------> SPI1_MOSI
|
||||
*/
|
||||
GPIO_InitStruct.Pin = LL_GPIO_PIN_5|LL_GPIO_PIN_6|LL_GPIO_PIN_7;
|
||||
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
|
||||
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||
GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
|
||||
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
/* SPI1 parameter configuration*/
|
||||
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
|
||||
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
|
||||
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
|
||||
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_LOW;
|
||||
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
|
||||
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
|
||||
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8;
|
||||
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
|
||||
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
|
||||
SPI_InitStruct.CRCPoly = 7;
|
||||
LL_SPI_Init(SPI1, &SPI_InitStruct);
|
||||
|
||||
LL_SPI_SetStandard(SPI1, LL_SPI_PROTOCOL_MOTOROLA);
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 SoloKeys, Inc. <https://solokeys.com/>
|
||||
*
|
||||
* This file is part of Solo.
|
||||
*
|
||||
* Solo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Solo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Solo. If not, see <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This code is available under licenses for commercial use.
|
||||
* Please contact SoloKeys for more information.
|
||||
*/
|
||||
#ifndef _INIT_H_
|
||||
#define _INIT_H_
|
||||
|
||||
void init_usb();
|
||||
void init_gpio(void);
|
||||
void init_debug_uart(void);
|
||||
void init_pwm(void);
|
||||
void init_millisecond_timer(int lf);
|
||||
void init_rng(void);
|
||||
void init_spi(void);
|
||||
|
||||
|
||||
#endif
|
@ -1,799 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "stm32l4xx.h"
|
||||
|
||||
#include "nfc.h"
|
||||
#include "ams.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "device.h"
|
||||
#include "u2f.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#include "ctap_errors.h"
|
||||
|
||||
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
|
||||
|
||||
// Capability container
|
||||
const CAPABILITY_CONTAINER NFC_CC = {
|
||||
.cclen_hi = 0x00, .cclen_lo = 0x0f,
|
||||
.version = 0x20,
|
||||
.MLe_hi = 0x00, .MLe_lo = 0x7f,
|
||||
.MLc_hi = 0x00, .MLc_lo = 0x7f,
|
||||
.tlv = { 0x04,0x06,
|
||||
0xe1,0x04,
|
||||
0x00,0x7f,
|
||||
0x00,0x00 }
|
||||
};
|
||||
|
||||
// 13 chars
|
||||
uint8_t NDEF_SAMPLE[] = "\x00\x14\xd1\x01\x0eU\x04solokeys.com/";
|
||||
|
||||
// Poor way to get some info while in passive operation
|
||||
#include <stdarg.h>
|
||||
void nprintf(const char *format, ...)
|
||||
{
|
||||
memmove((char*)NDEF_SAMPLE + sizeof(NDEF_SAMPLE) - 1 - 13," ", 13);
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
vsnprintf ((char*)NDEF_SAMPLE + sizeof(NDEF_SAMPLE) - 1 - 13, 13, format, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
uint8_t max_frame_size;
|
||||
uint8_t cid;
|
||||
uint8_t block_num;
|
||||
uint8_t selected_applet;
|
||||
} NFC_STATE;
|
||||
|
||||
void nfc_state_init()
|
||||
{
|
||||
memset(&NFC_STATE,0,sizeof(NFC_STATE));
|
||||
NFC_STATE.max_frame_size = 32;
|
||||
NFC_STATE.block_num = 1;
|
||||
}
|
||||
|
||||
bool nfc_init()
|
||||
{
|
||||
uint32_t t1;
|
||||
nfc_state_init();
|
||||
ams_init();
|
||||
|
||||
// Detect if we are powered by NFC field by listening for a message for
|
||||
// first 10 ms.
|
||||
t1 = millis();
|
||||
while ((millis() - t1) < 10)
|
||||
{
|
||||
if (nfc_loop() > 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Under USB power. Configure AMS chip.
|
||||
ams_configure();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void process_int0(uint8_t int0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool ams_wait_for_tx(uint32_t timeout_ms)
|
||||
{
|
||||
uint32_t tstart = millis();
|
||||
while (tstart + timeout_ms > millis())
|
||||
{
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
if (int0) process_int0(int0);
|
||||
if (int0 & AMS_INT_TXE)
|
||||
return true;
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, int *dlen)
|
||||
{
|
||||
uint8_t buf[32];
|
||||
*dlen = 0;
|
||||
|
||||
uint32_t tstart = millis();
|
||||
while (tstart + timeout_ms > millis())
|
||||
{
|
||||
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
|
||||
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
|
||||
|
||||
if (buffer_status2 && (int0 & AMS_INT_RXE))
|
||||
{
|
||||
if (buffer_status2 & AMS_BUF_INVALID)
|
||||
{
|
||||
printf1(TAG_NFC,"Buffer being updated!\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t len = buffer_status2 & AMS_BUF_LEN_MASK;
|
||||
ams_read_buffer(buf, len);
|
||||
printf1(TAG_NFC_APDU, ">> ");
|
||||
dump_hex1(TAG_NFC_APDU, buf, len);
|
||||
|
||||
*dlen = MIN(32, MIN(maxlen, len));
|
||||
memcpy(data, buf, *dlen);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
delay(1);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void nfc_write_frame(uint8_t * data, uint8_t len)
|
||||
{
|
||||
if (len > 32)
|
||||
{
|
||||
len = 32;
|
||||
}
|
||||
ams_write_command(AMS_CMD_CLEAR_BUFFER);
|
||||
ams_write_buffer(data,len);
|
||||
ams_write_command(AMS_CMD_TRANSMIT_BUFFER);
|
||||
|
||||
printf1(TAG_NFC_APDU, "<< ");
|
||||
dump_hex1(TAG_NFC_APDU, data, len);
|
||||
}
|
||||
|
||||
bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t resp)
|
||||
{
|
||||
uint8_t res[32];
|
||||
|
||||
if (len > 32 - 3)
|
||||
return false;
|
||||
|
||||
res[0] = NFC_CMD_IBLOCK | (req0 & 3);
|
||||
|
||||
if (len && data)
|
||||
memcpy(&res[1], data, len);
|
||||
|
||||
res[len + 1] = resp >> 8;
|
||||
res[len + 2] = resp & 0xff;
|
||||
nfc_write_frame(res, 3 + len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool nfc_write_response(uint8_t req0, uint16_t resp)
|
||||
{
|
||||
return nfc_write_response_ex(req0, NULL, 0, resp);
|
||||
}
|
||||
|
||||
void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
|
||||
{
|
||||
uint8_t res[32 + 2];
|
||||
int sendlen = 0;
|
||||
uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3);
|
||||
|
||||
if (len <= 31)
|
||||
{
|
||||
uint8_t res[32] = {0};
|
||||
res[0] = iBlock;
|
||||
if (len && data)
|
||||
memcpy(&res[1], data, len);
|
||||
nfc_write_frame(res, len + 1);
|
||||
} else {
|
||||
do {
|
||||
// transmit I block
|
||||
int vlen = MIN(31, len - sendlen);
|
||||
res[0] = iBlock;
|
||||
memcpy(&res[1], &data[sendlen], vlen);
|
||||
|
||||
// if not a last block
|
||||
if (vlen + sendlen < len)
|
||||
{
|
||||
res[0] |= 0x10;
|
||||
}
|
||||
|
||||
// send data
|
||||
nfc_write_frame(res, vlen + 1);
|
||||
sendlen += vlen;
|
||||
|
||||
// wait for transmit (32 bytes aprox 2,5ms)
|
||||
// if (!ams_wait_for_tx(10))
|
||||
// {
|
||||
// printf1(TAG_NFC, "TX timeout. slen: %d \r\n", sendlen);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if needs to receive R block (not a last block)
|
||||
if (res[0] & 0x10)
|
||||
{
|
||||
uint8_t recbuf[32] = {0};
|
||||
int reclen;
|
||||
if (!ams_receive_with_timeout(100, recbuf, sizeof(recbuf), &reclen))
|
||||
{
|
||||
printf1(TAG_NFC, "R block RX timeout %d/%d.\r\n",sendlen,len);
|
||||
break;
|
||||
}
|
||||
|
||||
if (reclen != 1)
|
||||
{
|
||||
printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len);
|
||||
dump_hex1(TAG_NFC, recbuf, reclen);
|
||||
break;
|
||||
}
|
||||
|
||||
if (((recbuf[0] & 0x01) == (res[0] & 1)) && ((recbuf[0] & 0xf6) == 0xa2))
|
||||
{
|
||||
printf1(TAG_NFC, "R block error. txdata: %02x rxdata: %02x \r\n", res[0], recbuf[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
iBlock ^= 0x01;
|
||||
} while (sendlen < len);
|
||||
}
|
||||
}
|
||||
|
||||
// WTX on/off:
|
||||
// sends/receives WTX frame to reader every `WTX_time` time in ms
|
||||
// works via timer interrupts
|
||||
// WTX: f2 01 91 40 === f2(S-block + WTX, frame without CID) 01(from iso - multiply WTX from ATS by 1) <2b crc16>
|
||||
static bool WTX_sent;
|
||||
static bool WTX_fail;
|
||||
static uint32_t WTX_timer;
|
||||
|
||||
bool WTX_process(int read_timeout);
|
||||
|
||||
void WTX_clear()
|
||||
{
|
||||
WTX_sent = false;
|
||||
WTX_fail = false;
|
||||
WTX_timer = 0;
|
||||
}
|
||||
|
||||
bool WTX_on(int WTX_time)
|
||||
{
|
||||
WTX_clear();
|
||||
WTX_timer = millis();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WTX_off()
|
||||
{
|
||||
WTX_timer = 0;
|
||||
|
||||
// read data if we sent WTX
|
||||
if (WTX_sent)
|
||||
{
|
||||
if (!WTX_process(100))
|
||||
{
|
||||
printf1(TAG_NFC, "WTX-off get last WTX error\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (WTX_fail)
|
||||
{
|
||||
printf1(TAG_NFC, "WTX-off fail\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
WTX_clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void WTX_timer_exec()
|
||||
{
|
||||
// condition: (timer on) or (not expired[300ms])
|
||||
if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
|
||||
return;
|
||||
|
||||
WTX_process(10);
|
||||
WTX_timer = millis();
|
||||
}
|
||||
|
||||
// executes twice a period. 1st for send WTX, 2nd for check the result
|
||||
// read timeout must be 10 ms to call from interrupt
|
||||
bool WTX_process(int read_timeout)
|
||||
{
|
||||
uint8_t wtx[] = {0xf2, 0x01};
|
||||
if (WTX_fail)
|
||||
return false;
|
||||
|
||||
if (!WTX_sent)
|
||||
{
|
||||
nfc_write_frame(wtx, sizeof(wtx));
|
||||
WTX_sent = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t data[32];
|
||||
int len;
|
||||
if (!ams_receive_with_timeout(read_timeout, data, sizeof(data), &len))
|
||||
{
|
||||
WTX_fail = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len != 2 || data[0] != 0xf2 || data[1] != 0x01)
|
||||
{
|
||||
WTX_fail = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
WTX_sent = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int answer_rats(uint8_t parameter)
|
||||
{
|
||||
|
||||
uint8_t fsdi = (parameter & 0xf0) >> 4;
|
||||
uint8_t cid = (parameter & 0x0f);
|
||||
|
||||
NFC_STATE.cid = cid;
|
||||
|
||||
if (fsdi == 0)
|
||||
NFC_STATE.max_frame_size = 16;
|
||||
else if (fsdi == 1)
|
||||
NFC_STATE.max_frame_size = 24;
|
||||
else
|
||||
NFC_STATE.max_frame_size = 32;
|
||||
|
||||
uint8_t res[3 + 11];
|
||||
res[0] = sizeof(res);
|
||||
res[1] = 2 | (1<<5); // 2 FSCI == 32 byte frame size, TB is enabled
|
||||
|
||||
// frame wait time = (256 * 16 / 13.56MHz) * 2^FWI
|
||||
// FWI=0, FMT=0.3ms (min)
|
||||
// FWI=4, FMT=4.8ms (default)
|
||||
// FWI=10, FMT=309ms
|
||||
// FWI=12, FMT=1237ms
|
||||
// FWI=14, FMT=4949ms (max)
|
||||
res[2] = (12<<4) | (0); // TB (FWI << 4) | (SGTI)
|
||||
|
||||
// historical bytes
|
||||
memcpy(&res[3], (uint8_t *)"SoloKey tap", 11);
|
||||
|
||||
|
||||
nfc_write_frame(res, sizeof(res));
|
||||
ams_wait_for_tx(10);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rblock_acknowledge()
|
||||
{
|
||||
uint8_t buf[32];
|
||||
NFC_STATE.block_num = !NFC_STATE.block_num;
|
||||
buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
|
||||
nfc_write_frame(buf,1);
|
||||
}
|
||||
|
||||
// Selects application. Returns 1 if success, 0 otherwise
|
||||
int select_applet(uint8_t * aid, int len)
|
||||
{
|
||||
if (memcmp(aid,AID_FIDO,sizeof(AID_FIDO)) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_FIDO;
|
||||
return APP_FIDO;
|
||||
}
|
||||
else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_NDEF_TYPE_4;
|
||||
return APP_NDEF_TYPE_4;
|
||||
}
|
||||
else if (memcmp(aid,AID_CAPABILITY_CONTAINER,sizeof(AID_CAPABILITY_CONTAINER)) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
|
||||
return APP_CAPABILITY_CONTAINER;
|
||||
}
|
||||
else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0)
|
||||
{
|
||||
NFC_STATE.selected_applet = APP_NDEF_TAG;
|
||||
return APP_NDEF_TAG;
|
||||
}
|
||||
return APP_NOTHING;
|
||||
}
|
||||
|
||||
void nfc_process_iblock(uint8_t * buf, int len)
|
||||
{
|
||||
APDU_HEADER * apdu = (APDU_HEADER *)(buf + 1);
|
||||
uint8_t * payload = buf + 1 + 5;
|
||||
uint8_t plen = apdu->lc;
|
||||
int selected;
|
||||
CTAP_RESPONSE ctap_resp;
|
||||
int status;
|
||||
|
||||
printf1(TAG_NFC,"Iblock: ");
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
|
||||
// TODO this needs to be organized better
|
||||
switch(apdu->ins)
|
||||
{
|
||||
case APDU_INS_SELECT:
|
||||
if (plen > len - 6)
|
||||
{
|
||||
printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
|
||||
plen = len-6;
|
||||
}
|
||||
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
|
||||
// {
|
||||
// printf1(TAG_NFC,"Select NDEF\r\n");
|
||||
//
|
||||
// NFC_STATE.selected_applet = APP_NDEF_TAG;
|
||||
// // Select NDEF file!
|
||||
// res[0] = NFC_CMD_IBLOCK | (buf[0] & 1);
|
||||
// res[1] = SW_SUCCESS>>8;
|
||||
// res[2] = SW_SUCCESS & 0xff;
|
||||
// nfc_write_frame(res, 3);
|
||||
// printf1(TAG_NFC,"<< "); dump_hex1(TAG_NFC,res, 3);
|
||||
// }
|
||||
// else
|
||||
{
|
||||
selected = select_applet(payload, plen);
|
||||
if (selected == APP_FIDO)
|
||||
{
|
||||
// block = buf[0] & 1;
|
||||
// block = NFC_STATE.block_num;
|
||||
// block = !block;
|
||||
// NFC_STATE.block_num = block;
|
||||
// NFC_STATE.block_num = block;
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
printf1(TAG_NFC, "FIDO applet selected.\r\n");
|
||||
}
|
||||
else if (selected != APP_NOTHING)
|
||||
{
|
||||
nfc_write_response(buf[0], SW_SUCCESS);
|
||||
printf1(TAG_NFC, "SELECTED %d\r\n", selected);
|
||||
}
|
||||
else
|
||||
{
|
||||
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
|
||||
printf1(TAG_NFC, "NOT selected\r\n"); dump_hex1(TAG_NFC,payload, plen);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_VERSION:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F GetVersion command.\r\n");
|
||||
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_REGISTER:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F Register command.\r\n");
|
||||
|
||||
if (plen != 64)
|
||||
{
|
||||
printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen);
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp();
|
||||
|
||||
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
// SystemClock_Config_LF32();
|
||||
// delay(300);
|
||||
device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
|
||||
u2f_request_nfc(&buf[1], len, &ctap_resp);
|
||||
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
|
||||
// printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
|
||||
|
||||
|
||||
|
||||
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
|
||||
break;
|
||||
|
||||
case APDU_FIDO_U2F_AUTHENTICATE:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
|
||||
|
||||
if (plen != 64 + 1 + buf[6 + 64])
|
||||
{
|
||||
delay(5);
|
||||
printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", plen, buf[6 + 64]);
|
||||
nfc_write_response(buf[0], SW_WRONG_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
timestamp();
|
||||
// WTX_on(WTX_TIME_DEFAULT);
|
||||
u2f_request_nfc(&buf[1], len, &ctap_resp);
|
||||
// if (!WTX_off())
|
||||
// return;
|
||||
|
||||
printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
|
||||
printf1(TAG_NFC,"U2F Authenticate processing %d (took %d)\r\n", millis(), timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
printf1(TAG_NFC,"U2F Authenticate answered %d (took %d)\r\n", millis(), timestamp);
|
||||
break;
|
||||
|
||||
case APDU_FIDO_NFCCTAP_MSG:
|
||||
if (NFC_STATE.selected_applet != APP_FIDO) {
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
|
||||
|
||||
WTX_on(WTX_TIME_DEFAULT);
|
||||
ctap_response_init(&ctap_resp);
|
||||
status = ctap_request(payload, plen, &ctap_resp);
|
||||
if (!WTX_off())
|
||||
return;
|
||||
|
||||
printf1(TAG_NFC, "CTAP resp: 0x%02<30> len: %d\r\n", status, ctap_resp.length);
|
||||
|
||||
if (status == CTAP1_ERR_SUCCESS)
|
||||
{
|
||||
memmove(&ctap_resp.data[1], &ctap_resp.data[0], ctap_resp.length);
|
||||
ctap_resp.length += 3;
|
||||
} else {
|
||||
ctap_resp.length = 3;
|
||||
}
|
||||
ctap_resp.data[0] = status;
|
||||
ctap_resp.data[ctap_resp.length - 2] = SW_SUCCESS >> 8;
|
||||
ctap_resp.data[ctap_resp.length - 1] = SW_SUCCESS & 0xff;
|
||||
|
||||
printf1(TAG_NFC,"CTAP processing %d (took %d)\r\n", millis(), timestamp());
|
||||
nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
|
||||
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
|
||||
break;
|
||||
|
||||
case APDU_INS_READ_BINARY:
|
||||
|
||||
|
||||
switch(NFC_STATE.selected_applet)
|
||||
{
|
||||
case APP_CAPABILITY_CONTAINER:
|
||||
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
|
||||
if (plen > 15)
|
||||
{
|
||||
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
|
||||
plen = 15;
|
||||
}
|
||||
nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, plen, SW_SUCCESS);
|
||||
ams_wait_for_tx(10);
|
||||
break;
|
||||
case APP_NDEF_TAG:
|
||||
printf1(TAG_NFC,"APP_NDEF_TAG\r\n");
|
||||
if (plen > (sizeof(NDEF_SAMPLE) - 1))
|
||||
{
|
||||
printf1(TAG_ERR, "Truncating requested CC length %d\r\n", apdu->lc);
|
||||
plen = sizeof(NDEF_SAMPLE) - 1;
|
||||
}
|
||||
nfc_write_response_ex(buf[0], NDEF_SAMPLE, plen, SW_SUCCESS);
|
||||
ams_wait_for_tx(10);
|
||||
break;
|
||||
default:
|
||||
printf1(TAG_ERR, "No binary applet selected!\r\n");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
|
||||
nfc_write_response(buf[0], SW_INS_INVALID);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
static uint8_t ibuf[1024];
|
||||
static int ibuflen = 0;
|
||||
|
||||
void clear_ibuf()
|
||||
{
|
||||
ibuflen = 0;
|
||||
memset(ibuf, 0, sizeof(ibuf));
|
||||
}
|
||||
|
||||
void nfc_process_block(uint8_t * buf, unsigned int len)
|
||||
{
|
||||
|
||||
if (!len)
|
||||
return;
|
||||
|
||||
if (IS_PPSS_CMD(buf[0]))
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
|
||||
}
|
||||
else if (IS_IBLOCK(buf[0]))
|
||||
{
|
||||
if (buf[0] & 0x10)
|
||||
{
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
|
||||
if (ibuflen + len > sizeof(ibuf))
|
||||
{
|
||||
printf1(TAG_NFC, "I block memory error! must have %d but have only %d\r\n", ibuflen + len, sizeof(ibuf));
|
||||
nfc_write_response(buf[0], SW_INTERNAL_EXCEPTION);
|
||||
return;
|
||||
}
|
||||
|
||||
printf1(TAG_NFC_APDU,"i> ");
|
||||
dump_hex1(TAG_NFC_APDU, buf, len);
|
||||
|
||||
if (len)
|
||||
{
|
||||
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
|
||||
ibuflen += len - 1;
|
||||
}
|
||||
|
||||
// send R block
|
||||
uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3);
|
||||
nfc_write_frame(&rb, 1);
|
||||
} else {
|
||||
if (ibuflen)
|
||||
{
|
||||
if (len)
|
||||
{
|
||||
memcpy(&ibuf[ibuflen], &buf[1], len - 1);
|
||||
ibuflen += len - 1;
|
||||
}
|
||||
|
||||
memmove(&ibuf[1], ibuf, ibuflen);
|
||||
ibuf[0] = buf[0];
|
||||
ibuflen++;
|
||||
|
||||
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
|
||||
|
||||
printf1(TAG_NFC_APDU,"i> ");
|
||||
dump_hex1(TAG_NFC_APDU, buf, len);
|
||||
|
||||
nfc_process_iblock(ibuf, ibuflen);
|
||||
} else {
|
||||
// printf1(TAG_NFC, "NFC_CMD_IBLOCK\r\n");
|
||||
nfc_process_iblock(buf, len);
|
||||
}
|
||||
clear_ibuf();
|
||||
}
|
||||
}
|
||||
else if (IS_RBLOCK(buf[0]))
|
||||
{
|
||||
rblock_acknowledge();
|
||||
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
|
||||
}
|
||||
else if (IS_SBLOCK(buf[0]))
|
||||
{
|
||||
|
||||
if ((buf[0] & NFC_SBLOCK_DESELECT) == 0)
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_SBLOCK, DESELECTED\r\n");
|
||||
nfc_write_frame(buf, 1);
|
||||
ams_wait_for_tx(2);
|
||||
ams_write_command(AMS_CMD_SLEEP);
|
||||
nfc_state_init();
|
||||
clear_ibuf();
|
||||
WTX_clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
|
||||
}
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf1(TAG_NFC, "unknown NFC request\r\n len[%d]:", len);
|
||||
dump_hex1(TAG_NFC, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
int nfc_loop()
|
||||
{
|
||||
uint8_t buf[32];
|
||||
AMS_DEVICE ams;
|
||||
int len = 0;
|
||||
|
||||
|
||||
read_reg_block(&ams);
|
||||
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
|
||||
|
||||
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
|
||||
{
|
||||
// delay(1); // sleep ?
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ams.regs.rfid_status)
|
||||
{
|
||||
// if (state != AMS_STATE_SENSE)
|
||||
// printf1(TAG_NFC," %s x%02x\r\n", ams_get_state_string(ams.regs.rfid_status), state);
|
||||
}
|
||||
if (ams.regs.int0 & AMS_INT_INIT)
|
||||
{
|
||||
nfc_state_init();
|
||||
}
|
||||
if (ams.regs.int1)
|
||||
{
|
||||
// ams_print_int1(ams.regs.int1);
|
||||
}
|
||||
|
||||
if ((ams.regs.int0 & AMS_INT_RXE))
|
||||
{
|
||||
if (ams.regs.buffer_status2)
|
||||
{
|
||||
if (ams.regs.buffer_status2 & AMS_BUF_INVALID)
|
||||
{
|
||||
printf1(TAG_NFC,"Buffer being updated!\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
len = ams.regs.buffer_status2 & AMS_BUF_LEN_MASK;
|
||||
ams_read_buffer(buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len)
|
||||
{
|
||||
|
||||
// ISO 14443-3
|
||||
switch(buf[0])
|
||||
{
|
||||
case NFC_CMD_REQA:
|
||||
printf1(TAG_NFC, "NFC_CMD_REQA\r\n");
|
||||
break;
|
||||
case NFC_CMD_WUPA:
|
||||
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
|
||||
break;
|
||||
case NFC_CMD_HLTA:
|
||||
printf1(TAG_NFC, "HLTA/Halt\r\n");
|
||||
break;
|
||||
case NFC_CMD_RATS:
|
||||
|
||||
answer_rats(buf[1]);
|
||||
|
||||
NFC_STATE.block_num = 1;
|
||||
clear_ibuf();
|
||||
WTX_clear();
|
||||
break;
|
||||
default:
|
||||
|
||||
// ISO 14443-4
|
||||
nfc_process_block(buf,len);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return len;
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
#ifndef _NFC_H_
|
||||
#define _NFC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "apdu.h"
|
||||
|
||||
// Return number of bytes read if any.
|
||||
int nfc_loop();
|
||||
|
||||
bool nfc_init();
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t cclen_hi;
|
||||
uint8_t cclen_lo;
|
||||
uint8_t version;
|
||||
uint8_t MLe_hi;
|
||||
uint8_t MLe_lo;
|
||||
uint8_t MLc_hi;
|
||||
uint8_t MLc_lo;
|
||||
uint8_t tlv[8];
|
||||
} __attribute__((packed)) CAPABILITY_CONTAINER;
|
||||
|
||||
// WTX time in ms
|
||||
#define WTX_TIME_DEFAULT 300
|
||||
|
||||
#define NFC_CMD_REQA 0x26
|
||||
#define NFC_CMD_WUPA 0x52
|
||||
#define NFC_CMD_HLTA 0x50
|
||||
#define NFC_CMD_RATS 0xe0
|
||||
|
||||
#define NFC_CMD_PPSS 0xd0
|
||||
#define IS_PPSS_CMD(x) (((x) & 0xf0) == NFC_CMD_PPSS)
|
||||
#define NFC_CMD_IBLOCK 0x00
|
||||
#define IS_IBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_IBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
#define NFC_CMD_RBLOCK 0x80
|
||||
#define NFC_CMD_RBLOCK_ACK 0x20
|
||||
#define IS_RBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_RBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
#define NFC_CMD_SBLOCK 0xc0
|
||||
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
|
||||
|
||||
#define NFC_SBLOCK_DESELECT 0x30
|
||||
#define NFC_SBLOCK_WTX 0x30
|
||||
|
||||
#define AID_NDEF_TYPE_4 "\xD2\x76\x00\x00\x85\x01\x01"
|
||||
#define AID_NDEF_MIFARE_TYPE_4 "\xD2\x76\x00\x00\x85\x01\x00"
|
||||
#define AID_CAPABILITY_CONTAINER "\xE1\x03"
|
||||
#define AID_NDEF_TAG "\xE1\x04"
|
||||
#define AID_FIDO "\xa0\x00\x00\x06\x47\x2f\x00\x01"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
APP_NOTHING = 0,
|
||||
APP_NDEF_TYPE_4 = 1,
|
||||
APP_MIFARE_TYPE_4,
|
||||
APP_CAPABILITY_CONTAINER,
|
||||
APP_NDEF_TAG,
|
||||
APP_FIDO,
|
||||
} APPLETS;
|
||||
|
||||
void WTX_timer_exec();
|
||||
|
||||
#endif
|
@ -24,33 +24,19 @@ void _putchar(char c)
|
||||
}
|
||||
|
||||
|
||||
int _write (int fd, const void *buf, unsigned long int len)
|
||||
|
||||
int _write (int fd, const void *buf, long int len)
|
||||
{
|
||||
uint8_t * data = (uint8_t *) buf;
|
||||
#if DEBUG_LEVEL>1
|
||||
// static uint8_t logbuf[1000] = {0};
|
||||
// static int logbuflen = 0;
|
||||
// if (logbuflen + len > sizeof(logbuf)) {
|
||||
// int mlen = logbuflen + len - sizeof(logbuf);
|
||||
// memmove(logbuf, &logbuf[mlen], sizeof(logbuf) - mlen);
|
||||
// logbuflen -= mlen;
|
||||
// }
|
||||
// memcpy(&logbuf[logbuflen], data, len);
|
||||
// logbuflen += len;
|
||||
|
||||
// Send out USB serial
|
||||
CDC_Transmit_FS(data, len);
|
||||
// if (res == USBD_OK)
|
||||
// logbuflen = 0;
|
||||
#endif
|
||||
#ifdef ENABLE_SERIAL_PRINTING
|
||||
|
||||
// Send out UART serial
|
||||
while(len--)
|
||||
{
|
||||
_putchar(*data++);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@ int __errno = 0;
|
||||
|
||||
void rng_get_bytes(uint8_t * dst, size_t sz)
|
||||
{
|
||||
uint8_t r[4];
|
||||
uint8_t r[8];
|
||||
unsigned int i,j;
|
||||
for (i = 0; i < sz; i += 4)
|
||||
{
|
||||
@ -33,7 +33,7 @@ void rng_get_bytes(uint8_t * dst, size_t sz)
|
||||
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
if ((i + j) >= sz)
|
||||
if ((i + j) > sz)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
183
targets/stm32l432/src/script.ld
Normal file
183
targets/stm32l432/src/script.ld
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
*****************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Abstract : Linker script for STM32L432KCUx Device with
|
||||
** 256KByte FLASH, 64KByte RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
**
|
||||
** Distribution: The file is distributed as is, without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
** (c)Copyright Ac6.
|
||||
** You may use this file as-is or modify it according to the needs of your
|
||||
** project. Distribution of this file (unmodified or modified) is not
|
||||
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||
** rights to distribute the assembled, compiled & linked contents of this
|
||||
** file as part of an application binary file, provided that it is built
|
||||
** using the System Workbench for MCU toolchain.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
_estack = 0x20010000; /* end of RAM */
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
|
||||
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(8);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.ARM : {
|
||||
. = ALIGN(8);
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
.init_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
. = ALIGN(8);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(8);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM
|
||||
|
||||
|
||||
|
||||
/* Remove information from the standard libraries */
|
||||
/DISCARD/ :
|
||||
{
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
||||
|
@ -79,8 +79,6 @@ Reset_Handler:
|
||||
ldr sp, =_estack /* Atollic update: set stack pointer */
|
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */
|
||||
/* Call the clock system intitialization function.*/
|
||||
bl SystemInit
|
||||
movs r1, #0
|
||||
b LoopCopyDataInit
|
||||
|
||||
@ -108,7 +106,8 @@ LoopFillZerobss:
|
||||
cmp r2, r3
|
||||
bcc FillZerobss
|
||||
|
||||
|
||||
/* Call the clock system intitialization function.*/
|
||||
bl SystemInit
|
||||
/* Call static constructors */
|
||||
bl __libc_init_array
|
||||
/* Call the application's entry point.*/
|
||||
|
@ -106,8 +106,6 @@
|
||||
*/
|
||||
|
||||
#include "stm32l4xx.h"
|
||||
#include "device.h"
|
||||
#include "init.h"
|
||||
|
||||
#if !defined (HSE_VALUE)
|
||||
#define HSE_VALUE 8000000U /*!< Value of the External oscillator in Hz */
|
||||
@ -221,9 +219,6 @@ void SystemInit(void)
|
||||
/* Disable all interrupts */
|
||||
RCC->CIER = 0x00000000U;
|
||||
|
||||
// TODO this is causing boot issues for old bootloader
|
||||
device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
838
tools/ctap_test.py
Executable file
838
tools/ctap_test.py
Executable file
@ -0,0 +1,838 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2019 SoloKeys Developers
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
# http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
# copied, modified, or distributed except according to those terms.
|
||||
#
|
||||
|
||||
# Script for testing correctness of CTAP2/CTAP1 security token
|
||||
|
||||
from __future__ import print_function, absolute_import, unicode_literals
|
||||
|
||||
from fido2.hid import CtapHidDevice, CTAPHID
|
||||
from fido2.client import Fido2Client, ClientError
|
||||
from fido2.ctap import CtapError
|
||||
from fido2.ctap1 import CTAP1
|
||||
from fido2.ctap2 import *
|
||||
from fido2.cose import *
|
||||
from fido2.utils import Timeout, sha256
|
||||
import sys, os, time
|
||||
from random import randint
|
||||
from binascii import hexlify
|
||||
import array, struct, socket
|
||||
|
||||
# Set up a FIDO 2 client using the origin https://example.com
|
||||
|
||||
|
||||
def ForceU2F(client, device):
|
||||
client.ctap = CTAP1(device)
|
||||
client.pin_protocol = None
|
||||
client._do_make_credential = client._ctap1_make_credential
|
||||
client._do_get_assertion = client._ctap1_get_assertion
|
||||
|
||||
|
||||
class Packet(object):
|
||||
def __init__(self, data):
|
||||
l = len(data)
|
||||
self.data = data
|
||||
|
||||
def ToWireFormat(self,):
|
||||
return self.data
|
||||
|
||||
@staticmethod
|
||||
def FromWireFormat(pkt_size, data):
|
||||
return Packet(data)
|
||||
|
||||
|
||||
class Tester:
|
||||
def __init__(self,):
|
||||
self.origin = "https://examplo.org"
|
||||
self.host = "examplo.org"
|
||||
|
||||
def find_device(self,):
|
||||
print(list(CtapHidDevice.list_devices()))
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
if not dev:
|
||||
raise RuntimeError("No FIDO device found")
|
||||
self.dev = dev
|
||||
self.client = Fido2Client(dev, self.origin)
|
||||
self.ctap = self.client.ctap2
|
||||
self.ctap1 = CTAP1(dev)
|
||||
|
||||
# consume timeout error
|
||||
# cmd,resp = self.recv_raw()
|
||||
|
||||
def send_data(self, cmd, data):
|
||||
if type(data) != type(b""):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
return self.dev.call(cmd, data, event)
|
||||
|
||||
def send_raw(self, data, cid=None):
|
||||
if cid is None:
|
||||
cid = self.dev._dev.cid
|
||||
elif type(cid) != type(b""):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
if type(data) != type(b""):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
data = cid + data
|
||||
l = len(data)
|
||||
if l != 64:
|
||||
pad = "\x00" * (64 - l)
|
||||
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
|
||||
data = data + pad
|
||||
data = list(data)
|
||||
assert len(data) == 64
|
||||
self.dev._dev.InternalSendPacket(Packet(data))
|
||||
|
||||
def cid(self,):
|
||||
return self.dev._dev.cid
|
||||
|
||||
def set_cid(self, cid):
|
||||
if type(cid) not in [type(b""), type(bytearray())]:
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
self.dev._dev.cid = cid
|
||||
|
||||
def recv_raw(self,):
|
||||
with Timeout(1.0) as t:
|
||||
cmd, payload = self.dev._dev.InternalRecv()
|
||||
return cmd, payload
|
||||
|
||||
def check_error(self, data, err=None):
|
||||
assert len(data) == 1
|
||||
if err is None:
|
||||
if data[0] != 0:
|
||||
raise CtapError(data[0])
|
||||
elif data[0] != err:
|
||||
raise ValueError("Unexpected error: %02x" % data[0])
|
||||
|
||||
def test_long_ping(self):
|
||||
amt = 1000
|
||||
pingdata = os.urandom(amt)
|
||||
try:
|
||||
t1 = time.time() * 1000
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
t2 = time.time() * 1000
|
||||
delt = t2 - t1
|
||||
# if (delt < 140 ):
|
||||
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
|
||||
if delt > 555 * (amt / 1000):
|
||||
raise RuntimeError("Fob is too slow (%d ms)" % delt)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
print("1000 byte ping time: %s ms" % delt)
|
||||
except CtapError as e:
|
||||
print("7609 byte Ping failed:", e)
|
||||
raise RuntimeError("ping failed")
|
||||
print("PASS: 7609 byte ping")
|
||||
# sys.flush(sys.sto)
|
||||
sys.stdout.flush()
|
||||
|
||||
def test_hid(self, check_timeouts=False):
|
||||
if check_timeouts:
|
||||
print("Test idle")
|
||||
try:
|
||||
cmd, resp = self.recv_raw()
|
||||
except socket.timeout:
|
||||
print("Pass: Idle")
|
||||
|
||||
print("Test init")
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
|
||||
|
||||
pingdata = os.urandom(100)
|
||||
try:
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError as e:
|
||||
print("100 byte Ping failed:", e)
|
||||
raise RuntimeError("ping failed")
|
||||
print("PASS: 100 byte ping")
|
||||
|
||||
self.test_long_ping()
|
||||
|
||||
try:
|
||||
r = self.send_data(CTAPHID.WINK, "")
|
||||
print(hexlify(r))
|
||||
# assert(len(r) == 0)
|
||||
except CtapError as e:
|
||||
print("wink failed:", e)
|
||||
raise RuntimeError("wink failed")
|
||||
print("PASS: wink")
|
||||
|
||||
# try:
|
||||
# r = self.send_data(CTAPHID.WINK, 'we9gofrei8g')
|
||||
# raise RuntimeError('Wink is not supposed to have payload')
|
||||
# except CtapError as e:
|
||||
# assert(e.code == CtapError.ERR.INVALID_LENGTH)
|
||||
# print('PASS: malformed wink')
|
||||
|
||||
try:
|
||||
r = self.send_data(CTAPHID.CBOR, "")
|
||||
if len(r) > 1 or r[0] == 0:
|
||||
raise RuntimeError("Cbor is supposed to have payload")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||
print("PASS: no data cbor")
|
||||
|
||||
try:
|
||||
r = self.send_data(CTAPHID.MSG, "")
|
||||
print(hexlify(r))
|
||||
if len(r) > 2:
|
||||
raise RuntimeError("MSG is supposed to have payload")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||
print("PASS: no data msg")
|
||||
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
print("PASS: resync")
|
||||
|
||||
try:
|
||||
r = self.send_data(0x66, "")
|
||||
raise RuntimeError("Invalid command did not return error")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_COMMAND
|
||||
print("PASS: invalid HID command")
|
||||
|
||||
print("Sending packet with too large of a length.")
|
||||
self.send_raw("\x81\x1d\xba\x00")
|
||||
cmd, resp = self.recv_raw()
|
||||
self.check_error(resp, CtapError.ERR.INVALID_LENGTH)
|
||||
print("PASS: invalid length")
|
||||
|
||||
r = self.send_data(CTAPHID.PING, "\x44" * 200)
|
||||
print("Sending packets that skip a sequence number.")
|
||||
self.send_raw("\x81\x04\x90")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
# skip 2
|
||||
self.send_raw("\x03")
|
||||
cmd, resp = self.recv_raw()
|
||||
self.check_error(resp, CtapError.ERR.INVALID_SEQ)
|
||||
if check_timeouts:
|
||||
cmd, resp = self.recv_raw()
|
||||
assert cmd == 0xBF # timeout
|
||||
print("PASS: invalid sequence")
|
||||
|
||||
print("Resync and send ping")
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
pingdata = os.urandom(100)
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
print("PASS: resync and ping")
|
||||
|
||||
print("Send ping and abort it")
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
print("PASS: interrupt ping with resync")
|
||||
|
||||
print("Send ping and abort it with different cid, expect timeout")
|
||||
oldcid = self.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
self.send_raw("\x81\x10\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
self.set_cid(newcid)
|
||||
self.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
|
||||
) # init from different cid
|
||||
print("wait for init response")
|
||||
cmd, r = self.recv_raw() # init response
|
||||
assert cmd == 0x86
|
||||
self.set_cid(oldcid)
|
||||
if check_timeouts:
|
||||
# print('wait for timeout')
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
|
||||
print("PASS: resync and timeout")
|
||||
|
||||
print("Test timeout")
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
t1 = time.time() * 1000
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
t2 = time.time() * 1000
|
||||
delt = t2 - t1
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
assert delt < 1000 and delt > 400
|
||||
print("Pass timeout")
|
||||
|
||||
print("Test not cont")
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
self.send_raw("\x81\x10\x00") # init packet
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_SEQ
|
||||
print("PASS: Test not cont")
|
||||
|
||||
if check_timeouts:
|
||||
print("Check random cont ignored")
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x01\x10\x00")
|
||||
try:
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
except socket.timeout:
|
||||
pass
|
||||
print("PASS: random cont")
|
||||
|
||||
print("Check busy")
|
||||
t1 = time.time() * 1000
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
oldcid = self.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.set_cid(newcid)
|
||||
self.send_raw("\x81\x04\x00")
|
||||
cmd, r = self.recv_raw() # busy response
|
||||
t2 = time.time() * 1000
|
||||
assert t2 - t1 < 100
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
self.set_cid(oldcid)
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
print("PASS: busy")
|
||||
|
||||
print("Check busy interleaved")
|
||||
cid1 = "\x11\x22\x33\x44"
|
||||
cid2 = "\x01\x22\x33\x44"
|
||||
self.set_cid(cid2)
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.set_cid(cid1)
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
|
||||
|
||||
self.set_cid(cid2) # send ping on 2nd channel
|
||||
self.send_raw("\x81\x00\x63")
|
||||
self.send_raw("\x00")
|
||||
|
||||
cmd, r = self.recv_raw() # busy response
|
||||
|
||||
self.set_cid(cid1) # finish 1st channel ping
|
||||
self.send_raw("\x00")
|
||||
|
||||
self.set_cid(cid2)
|
||||
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
self.set_cid(cid1)
|
||||
cmd, r = self.recv_raw() # ping response
|
||||
assert cmd == 0x81
|
||||
assert len(r) == 0x63
|
||||
|
||||
if check_timeouts:
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
print("PASS: busy interleaved")
|
||||
|
||||
if check_timeouts:
|
||||
print("Test idle, wait for timeout")
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
cmd, resp = self.recv_raw()
|
||||
except socket.timeout:
|
||||
print("Pass: Idle")
|
||||
|
||||
print("Test cid 0 is invalid")
|
||||
self.set_cid("\x00\x00\x00\x00")
|
||||
self.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
|
||||
)
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
print("Pass: cid 0")
|
||||
|
||||
print("Test invalid broadcast cid use")
|
||||
self.set_cid("\xff\xff\xff\xff")
|
||||
self.send_raw(
|
||||
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
|
||||
)
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
print("Pass: cid broadcast")
|
||||
|
||||
def test_u2f(self,):
|
||||
chal = sha256(b"AAA")
|
||||
appid = sha256(b"BBB")
|
||||
lastc = 0
|
||||
for i in range(0, 5):
|
||||
reg = self.ctap1.register(chal, appid)
|
||||
reg.verify(appid, chal)
|
||||
auth = self.ctap1.authenticate(chal, appid, reg.key_handle)
|
||||
# check endianness
|
||||
if lastc:
|
||||
assert (auth.counter - lastc) < 10
|
||||
lastc = auth.counter
|
||||
print(hex(lastc))
|
||||
print("U2F reg + auth pass %d/5" % (i + 1))
|
||||
|
||||
def test_fido2_simple(self, pin_token=None):
|
||||
creds = []
|
||||
exclude_list = []
|
||||
rp = {"id": self.host, "name": "ExaRP"}
|
||||
user = {"id": b"usee_od", "name": "AB User"}
|
||||
challenge = "Y2hhbGxlbmdl"
|
||||
PIN = pin_token
|
||||
|
||||
fake_id1 = array.array("B", [randint(0, 255) for i in range(0, 150)]).tobytes()
|
||||
fake_id2 = array.array("B", [randint(0, 255) for i in range(0, 73)]).tobytes()
|
||||
|
||||
exclude_list.append({"id": fake_id1, "type": "public-key"})
|
||||
exclude_list.append({"id": fake_id2, "type": "public-key"})
|
||||
|
||||
print("MC")
|
||||
t1 = time.time() * 1000
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user, challenge, pin=PIN, exclude_list=[]
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
attest.verify(data.hash)
|
||||
print("Register valid (%d ms)" % (t2 - t1))
|
||||
|
||||
cred = attest.auth_data.credential_data
|
||||
creds.append(cred)
|
||||
|
||||
allow_list = [{"id": creds[0].credential_id, "type": "public-key"}]
|
||||
t1 = time.time() * 1000
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, allow_list, pin=PIN
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
assertions[0].verify(client_data.hash, creds[0].public_key)
|
||||
|
||||
print("Assertion valid (%d ms)" % (t2 - t1))
|
||||
|
||||
def test_fido2_brute_force(self):
|
||||
creds = []
|
||||
exclude_list = []
|
||||
rp = {"id": self.host, "name": "ExaRP"}
|
||||
user = {"id": b"usee_od", "name": "AB User"}
|
||||
PIN = None
|
||||
abc = "abcdefghijklnmopqrstuvwxyz"
|
||||
abc += abc.upper()
|
||||
|
||||
self.ctap.reset()
|
||||
|
||||
for i in range(0, 2048 ** 2):
|
||||
creds = []
|
||||
|
||||
challenge = "".join([abc[randint(0, len(abc) - 1)] for x in range(0, 32)])
|
||||
|
||||
fake_id1 = array.array(
|
||||
"B", [randint(0, 255) for i in range(0, 150)]
|
||||
).tostring()
|
||||
fake_id2 = array.array(
|
||||
"B", [randint(0, 255) for i in range(0, 73)]
|
||||
).tostring()
|
||||
|
||||
exclude_list.append({"id": fake_id1, "type": "public-key"})
|
||||
exclude_list.append({"id": fake_id2, "type": "public-key"})
|
||||
|
||||
# for i in range(0,2048**2):
|
||||
for i in range(0, 1):
|
||||
t1 = time.time() * 1000
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user, challenge, pin=PIN, exclude_list=[]
|
||||
)
|
||||
print(attest.auth_data.counter)
|
||||
t2 = time.time() * 1000
|
||||
attest.verify(data.hash)
|
||||
print("Register valid (%d ms)" % (t2 - t1))
|
||||
sys.stdout.flush()
|
||||
|
||||
cred = attest.auth_data.credential_data
|
||||
creds.append(cred)
|
||||
|
||||
# for i in range(0,2048**2):
|
||||
for i in range(0, 1):
|
||||
allow_list = [{"id": creds[0].credential_id, "type": "public-key"}]
|
||||
t1 = time.time() * 1000
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, allow_list, pin=PIN
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
assertions[0].verify(client_data.hash, creds[0].public_key)
|
||||
print(assertions[0].auth_data.counter)
|
||||
|
||||
print("Assertion valid (%d ms)" % (t2 - t1))
|
||||
sys.stdout.flush()
|
||||
|
||||
def test_fido2(self):
|
||||
def test(self, pincode=None):
|
||||
creds = []
|
||||
exclude_list = []
|
||||
rp = {"id": self.host, "name": "ExaRP"}
|
||||
user = {"id": b"usee_od", "name": "AB User"}
|
||||
challenge = "Y2hhbGxlbmdl"
|
||||
PIN = pincode
|
||||
|
||||
fake_id1 = array.array(
|
||||
"B", [randint(0, 255) for i in range(0, 150)]
|
||||
).tostring()
|
||||
fake_id2 = array.array(
|
||||
"B", [randint(0, 255) for i in range(0, 73)]
|
||||
).tostring()
|
||||
|
||||
exclude_list.append({"id": fake_id1, "type": "public-key"})
|
||||
exclude_list.append({"id": fake_id2, "type": "public-key"})
|
||||
|
||||
# test make credential
|
||||
print("make 3 credentials")
|
||||
for i in range(0, 3):
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user, challenge, pin=PIN, exclude_list=[]
|
||||
)
|
||||
attest.verify(data.hash)
|
||||
# verify endian-ness is correct
|
||||
assert attest.auth_data.counter < 0x10000
|
||||
cred = attest.auth_data.credential_data
|
||||
creds.append(cred)
|
||||
print(cred)
|
||||
print("PASS")
|
||||
|
||||
if PIN is not None:
|
||||
print("make credential with wrong pin code")
|
||||
try:
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user, challenge, pin=PIN + " ", exclude_list=[]
|
||||
)
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.PIN_INVALID
|
||||
except ClientError as e:
|
||||
assert e.cause.code == CtapError.ERR.PIN_INVALID
|
||||
print("PASS")
|
||||
|
||||
print("make credential with exclude list")
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user, challenge, pin=PIN, exclude_list=exclude_list
|
||||
)
|
||||
attest.verify(data.hash)
|
||||
cred = attest.auth_data.credential_data
|
||||
creds.append(cred)
|
||||
print("PASS")
|
||||
|
||||
print("make credential with exclude list including real credential")
|
||||
real_excl = [{"id": cred.credential_id, "type": "public-key"}]
|
||||
try:
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user, challenge, pin=PIN, exclude_list=exclude_list + real_excl
|
||||
)
|
||||
raise RuntimeError("Exclude list did not return expected error")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.CREDENTIAL_EXCLUDED
|
||||
except ClientError as e:
|
||||
assert e.cause.code == CtapError.ERR.CREDENTIAL_EXCLUDED
|
||||
print("PASS")
|
||||
|
||||
for i, x in enumerate(creds):
|
||||
print("get assertion %d" % i)
|
||||
allow_list = [{"id": x.credential_id, "type": "public-key"}]
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, allow_list, pin=PIN
|
||||
)
|
||||
assertions[0].verify(client_data.hash, x.public_key)
|
||||
print("PASS")
|
||||
|
||||
if PIN is not None:
|
||||
print("get assertion with wrong pin code")
|
||||
try:
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, allow_list, pin=PIN + " "
|
||||
)
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.PIN_INVALID
|
||||
except ClientError as e:
|
||||
assert e.cause.code == CtapError.ERR.PIN_INVALID
|
||||
print("PASS")
|
||||
|
||||
print("get multiple assertions")
|
||||
allow_list = [{"id": x.credential_id, "type": "public-key"} for x in creds]
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, allow_list, pin=PIN
|
||||
)
|
||||
|
||||
for ass, cred in zip(assertions, creds):
|
||||
i += 1
|
||||
|
||||
ass.verify(client_data.hash, cred.public_key)
|
||||
print("%d verified" % i)
|
||||
print("PASS")
|
||||
|
||||
print("Reset device")
|
||||
try:
|
||||
self.ctap.reset()
|
||||
except CtapError as e:
|
||||
print("Warning, reset failed: ", e)
|
||||
pass
|
||||
print("PASS")
|
||||
|
||||
test(self, None)
|
||||
|
||||
print("Set a pin code")
|
||||
PIN = "1122aabbwfg0h9g !@#=="
|
||||
self.client.pin_protocol.set_pin(PIN)
|
||||
print("PASS")
|
||||
|
||||
print("Illegally set pin code again")
|
||||
try:
|
||||
self.client.pin_protocol.set_pin(PIN)
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.NOT_ALLOWED
|
||||
print("PASS")
|
||||
|
||||
print("Change pin code")
|
||||
PIN2 = PIN + "_pin2"
|
||||
self.client.pin_protocol.change_pin(PIN, PIN2)
|
||||
PIN = PIN2
|
||||
print("PASS")
|
||||
|
||||
print("Change pin code using wrong pin")
|
||||
try:
|
||||
self.client.pin_protocol.change_pin(PIN.replace("a", "b"), "1234")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.PIN_INVALID
|
||||
print("PASS")
|
||||
|
||||
print("MC using wrong pin")
|
||||
try:
|
||||
self.test_fido2_simple("abcd3")
|
||||
except ClientError as e:
|
||||
assert e.cause.code == CtapError.ERR.PIN_INVALID
|
||||
print("PASS")
|
||||
|
||||
print("get info")
|
||||
inf = self.ctap.get_info()
|
||||
print("PASS")
|
||||
|
||||
self.test_fido2_simple(PIN)
|
||||
|
||||
print("Re-run make_credential and get_assertion tests with pin code")
|
||||
test(self, PIN)
|
||||
|
||||
print("Reset device")
|
||||
try:
|
||||
self.ctap.reset()
|
||||
except CtapError as e:
|
||||
print("Warning, reset failed: ", e)
|
||||
print("PASS")
|
||||
|
||||
def test_rk(self,):
|
||||
creds = []
|
||||
rp = {"id": self.host, "name": "ExaRP"}
|
||||
user0 = {"id": b"first one", "name": "single User"}
|
||||
|
||||
users = [
|
||||
{"id": b"user" + os.urandom(16), "name": "AB User"} for i in range(0, 2)
|
||||
]
|
||||
challenge = "Y2hhbGxlbmdl"
|
||||
PIN = None
|
||||
print("reset")
|
||||
self.ctap.reset()
|
||||
# if PIN: self.client.pin_protocol.set_pin(PIN)
|
||||
|
||||
print("registering 1 user with RK")
|
||||
t1 = time.time() * 1000
|
||||
attest, data = self.client.make_credential(
|
||||
rp, user0, challenge, pin=PIN, exclude_list=[], rk=True
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
attest.verify(data.hash)
|
||||
creds.append(attest.auth_data.credential_data)
|
||||
print("Register valid (%d ms)" % (t2 - t1))
|
||||
|
||||
print("1 assertion")
|
||||
t1 = time.time() * 1000
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, pin=PIN
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
assertions[0].verify(client_data.hash, creds[0].public_key)
|
||||
print("Assertion valid (%d ms)" % (t2 - t1))
|
||||
|
||||
print(assertions[0], client_data)
|
||||
|
||||
print("registering %d users with RK" % len(users))
|
||||
for i in range(0, len(users)):
|
||||
t1 = time.time() * 1000
|
||||
attest, data = self.client.make_credential(
|
||||
rp, users[i], challenge, pin=PIN, exclude_list=[], rk=True
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
attest.verify(data.hash)
|
||||
print("Register valid (%d ms)" % (t2 - t1))
|
||||
|
||||
creds.append(attest.auth_data.credential_data)
|
||||
|
||||
t1 = time.time() * 1000
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, pin=PIN
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
|
||||
for x, y in zip(assertions, creds):
|
||||
x.verify(client_data.hash, y.public_key)
|
||||
|
||||
print("Assertion(s) valid (%d ms)" % (t2 - t1))
|
||||
|
||||
print("registering a duplicate user ")
|
||||
|
||||
t1 = time.time() * 1000
|
||||
attest, data = self.client.make_credential(
|
||||
rp, users[1], challenge, pin=PIN, exclude_list=[], rk=True
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
attest.verify(data.hash)
|
||||
creds = creds[:2] + creds[3:] + [attest.auth_data.credential_data]
|
||||
print("Register valid (%d ms)" % (t2 - t1))
|
||||
|
||||
t1 = time.time() * 1000
|
||||
assertions, client_data = self.client.get_assertion(
|
||||
rp["id"], challenge, pin=PIN
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
assert len(assertions) == len(users) + 1
|
||||
for x, y in zip(assertions, creds):
|
||||
x.verify(client_data.hash, y.public_key)
|
||||
|
||||
print("Assertion(s) valid (%d ms)" % (t2 - t1))
|
||||
|
||||
def test_responses(self,):
|
||||
PIN = "1234"
|
||||
RPID = self.host
|
||||
for dev in CtapHidDevice.list_devices():
|
||||
print("dev", dev)
|
||||
client = Fido2Client(dev, RPID)
|
||||
ctap = client.ctap2
|
||||
# ctap.reset()
|
||||
try:
|
||||
if PIN:
|
||||
client.pin_protocol.set_pin(PIN)
|
||||
except:
|
||||
pass
|
||||
|
||||
inf = ctap.get_info()
|
||||
# print (inf)
|
||||
print("versions: ", inf.versions)
|
||||
print("aaguid: ", inf.aaguid)
|
||||
print("rk: ", inf.options["rk"])
|
||||
print("clientPin: ", inf.options["clientPin"])
|
||||
print("max_message_size: ", inf.max_msg_size)
|
||||
|
||||
# rp = {'id': 'SelectDevice', 'name': 'SelectDevice'}
|
||||
rp = {"id": RPID, "name": "ExaRP"}
|
||||
user = {"id": os.urandom(10), "name": "SelectDevice"}
|
||||
user = {"id": b"21first one", "name": "single User"}
|
||||
challenge = "Y2hhbGxlbmdl"
|
||||
|
||||
if 1:
|
||||
attest, data = client.make_credential(
|
||||
rp, user, challenge, exclude_list=[], pin=PIN, rk=True
|
||||
)
|
||||
|
||||
cred = attest.auth_data.credential_data
|
||||
creds = [cred]
|
||||
|
||||
allow_list = [{"id": creds[0].credential_id, "type": "public-key"}]
|
||||
allow_list = []
|
||||
assertions, client_data = client.get_assertion(
|
||||
rp["id"], challenge, pin=PIN
|
||||
)
|
||||
assertions[0].verify(client_data.hash, creds[0].public_key)
|
||||
|
||||
if 0:
|
||||
print("registering 1 user with RK")
|
||||
t1 = time.time() * 1000
|
||||
attest, data = client.make_credential(
|
||||
rp, user, challenge, pin=PIN, exclude_list=[], rk=True
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
attest.verify(data.hash)
|
||||
creds = [attest.auth_data.credential_data]
|
||||
print("Register valid (%d ms)" % (t2 - t1))
|
||||
|
||||
print("1 assertion")
|
||||
t1 = time.time() * 1000
|
||||
assertions, client_data = client.get_assertion(
|
||||
rp["id"], challenge, pin=PIN
|
||||
)
|
||||
t2 = time.time() * 1000
|
||||
assertions[0].verify(client_data.hash, creds[0].public_key)
|
||||
print("Assertion valid (%d ms)" % (t2 - t1))
|
||||
|
||||
# print('fmt:',attest.fmt)
|
||||
# print('rp_id_hash',attest.auth_data.rp_id_hash)
|
||||
# print('flags:', hex(attest.auth_data.flags))
|
||||
# print('count:', hex(attest.auth_data.counter))
|
||||
print("flags MC:", attest.auth_data)
|
||||
print("flags GA:", assertions[0].auth_data)
|
||||
# print('cred_id:',attest.auth_data.credential_data.credential_id)
|
||||
# print('pubkey:',attest.auth_data.credential_data.public_key)
|
||||
# print('aaguid:',attest.auth_data.credential_data.aaguid)
|
||||
# print('cred data:',attest.auth_data.credential_data)
|
||||
# print('auth_data:',attest.auth_data)
|
||||
# print('auth_data:',attest.auth_data)
|
||||
# print('alg:',attest.att_statement['alg'])
|
||||
# print('sig:',attest.att_statement['sig'])
|
||||
# print('x5c:',attest.att_statement['x5c'])
|
||||
# print('data:',data)
|
||||
|
||||
print("assertion:", assertions[0])
|
||||
print("clientData:", client_data)
|
||||
|
||||
print()
|
||||
# break
|
||||
|
||||
|
||||
def test_find_brute_force():
|
||||
i = 0
|
||||
while 1:
|
||||
t1 = time.time() * 1000
|
||||
t = Tester()
|
||||
t.find_device()
|
||||
t2 = time.time() * 1000
|
||||
print("connected %d (%d ms)" % (i, t2 - t1))
|
||||
i += 1
|
||||
time.sleep(0.01)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
t = Tester()
|
||||
t.find_device()
|
||||
# t.test_hid()
|
||||
# t.test_long_ping()
|
||||
t.test_fido2()
|
||||
t.test_u2f()
|
||||
# t.test_rk()
|
||||
# t.test_responses()
|
||||
# test_find_brute_force()
|
||||
# t.test_fido2_simple()
|
||||
# t.test_fido2_brute_force()
|
@ -2,6 +2,5 @@ ecdsa
|
||||
fido2
|
||||
intelhex
|
||||
pyserial
|
||||
solo-python
|
||||
pyusb
|
||||
wheel
|
||||
|
1085
tools/solotool.py
Executable file
1085
tools/solotool.py
Executable file
File diff suppressed because it is too large
Load Diff
@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
./main
|
||||
|
||||
while [ $? == 100 ] ; do
|
||||
echo "Restarting software authentictor."
|
||||
./main
|
||||
done
|
@ -1,58 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright 2019 SoloKeys Developers
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
# http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
# http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
# copied, modified, or distributed except according to those terms.
|
||||
#
|
||||
|
||||
# Script for testing correctness of CTAP2/CTAP1 security token
|
||||
|
||||
import sys
|
||||
|
||||
from solo.fido2 import force_udp_backend
|
||||
from tests import Tester, FIDO2Tests, U2FTests, HIDTests, SoloTests
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: %s [sim] <[u2f]|[fido2]|[rk]|[hid]|[ping]>")
|
||||
sys.exit(0)
|
||||
|
||||
t = Tester()
|
||||
t.set_user_count(3)
|
||||
|
||||
if "sim" in sys.argv:
|
||||
print("Using UDP backend.")
|
||||
force_udp_backend()
|
||||
t.set_sim(True)
|
||||
t.set_user_count(10)
|
||||
|
||||
t.find_device()
|
||||
|
||||
if "solo" in sys.argv:
|
||||
SoloTests(t).run()
|
||||
|
||||
if "u2f" in sys.argv:
|
||||
U2FTests(t).run()
|
||||
|
||||
if "fido2" in sys.argv:
|
||||
# t.test_fido2()
|
||||
FIDO2Tests(t).run()
|
||||
|
||||
# hid tests are a bit invasive and should be done last
|
||||
if "hid" in sys.argv:
|
||||
HIDTests(t).run()
|
||||
|
||||
if "bootloader" in sys.argv:
|
||||
if t.is_sim:
|
||||
raise RuntimeError("Cannot test bootloader in simulation yet.")
|
||||
# print("Put device in bootloader mode and then hit enter")
|
||||
# input()
|
||||
# t.test_bootloader()
|
||||
|
||||
# t.test_responses()
|
||||
# t.test_fido2_brute_force()
|
@ -1,11 +0,0 @@
|
||||
from . import fido2
|
||||
from . import hid
|
||||
from . import solo
|
||||
from . import u2f
|
||||
from . import tester
|
||||
|
||||
FIDO2Tests = fido2.FIDO2Tests
|
||||
HIDTests = hid.HIDTests
|
||||
U2FTests = u2f.U2FTests
|
||||
SoloTests = solo.SoloTests
|
||||
Tester = tester.Tester
|
File diff suppressed because it is too large
Load Diff
@ -1,252 +0,0 @@
|
||||
import sys, os, time
|
||||
from binascii import hexlify
|
||||
|
||||
from fido2.hid import CTAPHID
|
||||
from fido2.ctap import CtapError
|
||||
|
||||
from .tester import Tester, Test
|
||||
|
||||
|
||||
class HIDTests(Tester):
|
||||
def __init__(self, tester=None):
|
||||
super().__init__(tester)
|
||||
self.check_timeouts = False
|
||||
|
||||
def set_check_timeouts(self, en):
|
||||
self.check_timeouts = en
|
||||
|
||||
def run(self,):
|
||||
self.test_long_ping()
|
||||
self.test_hid(self.check_timeouts)
|
||||
|
||||
def test_long_ping(self):
|
||||
amt = 1000
|
||||
pingdata = os.urandom(amt)
|
||||
with Test("Send %d byte ping" % amt):
|
||||
try:
|
||||
t1 = time.time() * 1000
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
t2 = time.time() * 1000
|
||||
delt = t2 - t1
|
||||
# if (delt < 140 ):
|
||||
# raise RuntimeError('Fob is too fast (%d ms)' % delt)
|
||||
if delt > 555 * (amt / 1000):
|
||||
raise RuntimeError("Fob is too slow (%d ms)" % delt)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError:
|
||||
raise RuntimeError("ping failed")
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
def test_hid(self, check_timeouts=False):
|
||||
if check_timeouts:
|
||||
with Test("idle"):
|
||||
try:
|
||||
cmd, resp = self.recv_raw()
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
with Test("init"):
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x11\x11\x11\x11\x11\x11\x11")
|
||||
|
||||
with Test("100 byte ping"):
|
||||
pingdata = os.urandom(100)
|
||||
try:
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError as e:
|
||||
print("100 byte Ping failed:", e)
|
||||
raise RuntimeError("ping failed")
|
||||
|
||||
self.test_long_ping()
|
||||
|
||||
with Test("Wink"):
|
||||
r = self.send_data(CTAPHID.WINK, "")
|
||||
|
||||
with Test("CBOR msg with no data"):
|
||||
try:
|
||||
r = self.send_data(CTAPHID.CBOR, "")
|
||||
if len(r) > 1 or r[0] == 0:
|
||||
raise RuntimeError("Cbor is supposed to have payload")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||
|
||||
with Test("No data in U2F msg"):
|
||||
try:
|
||||
r = self.send_data(CTAPHID.MSG, "")
|
||||
print(hexlify(r))
|
||||
if len(r) > 2:
|
||||
raise RuntimeError("MSG is supposed to have payload")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_LENGTH
|
||||
|
||||
with Test("Use init command to resync"):
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
|
||||
with Test("Invalid HID command"):
|
||||
try:
|
||||
r = self.send_data(0x66, "")
|
||||
raise RuntimeError("Invalid command did not return error")
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.INVALID_COMMAND
|
||||
|
||||
with Test("Sending packet with too large of a length."):
|
||||
self.send_raw("\x81\x1d\xba\x00")
|
||||
cmd, resp = self.recv_raw()
|
||||
Tester.check_error(resp, CtapError.ERR.INVALID_LENGTH)
|
||||
|
||||
r = self.send_data(CTAPHID.PING, "\x44" * 200)
|
||||
with Test("Sending packets that skip a sequence number."):
|
||||
self.send_raw("\x81\x04\x90")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
# skip 2
|
||||
self.send_raw("\x03")
|
||||
cmd, resp = self.recv_raw()
|
||||
Tester.check_error(resp, CtapError.ERR.INVALID_SEQ)
|
||||
|
||||
with Test("Resync and send ping"):
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
pingdata = os.urandom(100)
|
||||
r = self.send_data(CTAPHID.PING, pingdata)
|
||||
if r != pingdata:
|
||||
raise ValueError("Ping data not echo'd")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
|
||||
with Test("Send ping and abort it"):
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
try:
|
||||
r = self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
except CtapError as e:
|
||||
raise RuntimeError("resync fail: ", e)
|
||||
|
||||
with Test("Send ping and abort it with different cid, expect timeout"):
|
||||
oldcid = self.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
self.send_raw("\x81\x10\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
self.set_cid(newcid)
|
||||
self.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88"
|
||||
) # init from different cid
|
||||
print("wait for init response")
|
||||
cmd, r = self.recv_raw() # init response
|
||||
assert cmd == 0x86
|
||||
self.set_cid(oldcid)
|
||||
if check_timeouts:
|
||||
# print('wait for timeout')
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
|
||||
with Test("Test timeout"):
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
t1 = time.time() * 1000
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
t2 = time.time() * 1000
|
||||
delt = t2 - t1
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
assert delt < 1000 and delt > 400
|
||||
|
||||
with Test("Test not cont"):
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.send_raw("\x00")
|
||||
self.send_raw("\x01")
|
||||
self.send_raw("\x81\x10\x00") # init packet
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_SEQ
|
||||
|
||||
if check_timeouts:
|
||||
with Test("Check random cont ignored"):
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x01\x10\x00")
|
||||
try:
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
with Test("Check busy"):
|
||||
t1 = time.time() * 1000
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
oldcid = self.cid()
|
||||
newcid = "\x11\x22\x33\x44"
|
||||
self.send_raw("\x81\x04\x00")
|
||||
self.set_cid(newcid)
|
||||
self.send_raw("\x81\x04\x00")
|
||||
cmd, r = self.recv_raw() # busy response
|
||||
t2 = time.time() * 1000
|
||||
assert t2 - t1 < 100
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
self.set_cid(oldcid)
|
||||
cmd, r = self.recv_raw() # timeout response
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.TIMEOUT
|
||||
|
||||
with Test("Check busy interleaved"):
|
||||
cid1 = "\x11\x22\x33\x44"
|
||||
cid2 = "\x01\x22\x33\x44"
|
||||
self.set_cid(cid2)
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.set_cid(cid1)
|
||||
self.send_data(CTAPHID.INIT, "\x11\x22\x33\x44\x55\x66\x77\x88")
|
||||
self.send_raw("\x81\x00\x63") # echo 99 bytes first channel
|
||||
|
||||
self.set_cid(cid2) # send ping on 2nd channel
|
||||
self.send_raw("\x81\x00\x63")
|
||||
Tester.delay(0.1)
|
||||
self.send_raw("\x00")
|
||||
|
||||
cmd, r = self.recv_raw() # busy response
|
||||
|
||||
self.set_cid(cid1) # finish 1st channel ping
|
||||
self.send_raw("\x00")
|
||||
|
||||
self.set_cid(cid2)
|
||||
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.CHANNEL_BUSY
|
||||
|
||||
self.set_cid(cid1)
|
||||
cmd, r = self.recv_raw() # ping response
|
||||
assert cmd == 0x81
|
||||
assert len(r) == 0x63
|
||||
|
||||
if check_timeouts:
|
||||
with Test("Test idle, wait for timeout"):
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
cmd, resp = self.recv_raw()
|
||||
except socket.timeout:
|
||||
pass
|
||||
|
||||
with Test("Test cid 0 is invalid"):
|
||||
self.set_cid("\x00\x00\x00\x00")
|
||||
self.send_raw(
|
||||
"\x86\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\x00\x00\x00\x00"
|
||||
)
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
||||
|
||||
with Test("Test invalid broadcast cid use"):
|
||||
self.set_cid("\xff\xff\xff\xff")
|
||||
self.send_raw(
|
||||
"\x81\x00\x08\x11\x22\x33\x44\x55\x66\x77\x88", cid="\xff\xff\xff\xff"
|
||||
)
|
||||
cmd, r = self.recv_raw() # timeout
|
||||
assert cmd == 0xBF
|
||||
assert r[0] == CtapError.ERR.INVALID_CHANNEL
|
@ -1,70 +0,0 @@
|
||||
from solo.client import SoloClient
|
||||
|
||||
from fido2.ctap1 import ApduError
|
||||
|
||||
from .util import shannon_entropy
|
||||
from .tester import Tester, Test
|
||||
|
||||
|
||||
class SoloTests(Tester):
|
||||
def __init__(self, tester=None):
|
||||
super().__init__(tester)
|
||||
|
||||
def run(self,):
|
||||
self.test_solo()
|
||||
|
||||
def test_solo(self,):
|
||||
"""
|
||||
Solo specific tests
|
||||
"""
|
||||
# RNG command
|
||||
sc = SoloClient()
|
||||
sc.find_device(self.dev)
|
||||
sc.use_u2f()
|
||||
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
|
||||
|
||||
total = 1024 * 16
|
||||
with Test("Gathering %d random bytes..." % total):
|
||||
entropy = b""
|
||||
while len(entropy) < total:
|
||||
entropy += sc.get_rng()
|
||||
|
||||
with Test("Test entropy is close to perfect"):
|
||||
s = shannon_entropy(entropy)
|
||||
assert s > 7.98
|
||||
print("Entropy is %.5f bits per byte." % s)
|
||||
|
||||
with Test("Test Solo version command"):
|
||||
assert len(sc.solo_version()) == 3
|
||||
|
||||
with Test("Test bootloader is not active"):
|
||||
try:
|
||||
sc.write_flash(memmap[0], b"1234")
|
||||
except ApduError:
|
||||
pass
|
||||
|
||||
sc.exchange = sc.exchange_fido2
|
||||
with Test("Test Solo version and random commands with fido2 layer"):
|
||||
assert len(sc.solo_version()) == 3
|
||||
sc.get_rng()
|
||||
|
||||
def test_bootloader(self,):
|
||||
sc = SoloClient()
|
||||
sc.find_device(self.dev)
|
||||
sc.use_u2f()
|
||||
|
||||
memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
|
||||
data = b"A" * 64
|
||||
|
||||
with Test("Test version command"):
|
||||
assert len(sc.bootloader_version()) == 3
|
||||
|
||||
with Test("Test write command"):
|
||||
sc.write_flash(memmap[0], data)
|
||||
|
||||
for addr in (memmap[0] - 8, memmap[0] - 4, memmap[1], memmap[1] - 8):
|
||||
with Test("Test out of bounds write command at 0x%04x" % addr):
|
||||
try:
|
||||
sc.write_flash(addr, data)
|
||||
except CtapError as e:
|
||||
assert e.code == CtapError.ERR.NOT_ALLOWED
|
@ -1,197 +0,0 @@
|
||||
import time, struct
|
||||
|
||||
from fido2.hid import CtapHidDevice
|
||||
from fido2.client import Fido2Client
|
||||
from fido2.ctap1 import CTAP1
|
||||
from fido2.utils import Timeout
|
||||
|
||||
from fido2.ctap import CtapError
|
||||
|
||||
|
||||
def ForceU2F(client, device):
|
||||
client.ctap = CTAP1(device)
|
||||
client.pin_protocol = None
|
||||
client._do_make_credential = client._ctap1_make_credential
|
||||
client._do_get_assertion = client._ctap1_get_assertion
|
||||
|
||||
|
||||
class Packet(object):
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
|
||||
def ToWireFormat(self,):
|
||||
return self.data
|
||||
|
||||
@staticmethod
|
||||
def FromWireFormat(pkt_size, data):
|
||||
return Packet(data)
|
||||
|
||||
|
||||
class Test:
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __enter__(self,):
|
||||
print(self.msg)
|
||||
|
||||
def __exit__(self, a, b, c):
|
||||
print("Pass")
|
||||
|
||||
|
||||
class Tester:
|
||||
def __init__(self, tester=None):
|
||||
self.origin = "https://examplo.org"
|
||||
self.host = "examplo.org"
|
||||
self.user_count = 10
|
||||
self.is_sim = False
|
||||
if tester:
|
||||
self.initFromTester(tester)
|
||||
|
||||
def initFromTester(self, tester):
|
||||
self.user_count = tester.user_count
|
||||
self.is_sim = tester.is_sim
|
||||
self.dev = tester.dev
|
||||
self.ctap = tester.ctap
|
||||
self.ctap1 = tester.ctap1
|
||||
self.client = tester.client
|
||||
|
||||
def find_device(self,):
|
||||
print(list(CtapHidDevice.list_devices()))
|
||||
dev = next(CtapHidDevice.list_devices(), None)
|
||||
if not dev:
|
||||
raise RuntimeError("No FIDO device found")
|
||||
self.dev = dev
|
||||
self.client = Fido2Client(dev, self.origin)
|
||||
self.ctap = self.client.ctap2
|
||||
self.ctap1 = CTAP1(dev)
|
||||
|
||||
# consume timeout error
|
||||
# cmd,resp = self.recv_raw()
|
||||
|
||||
def set_user_count(self, count):
|
||||
self.user_count = count
|
||||
|
||||
def set_sim(self, b):
|
||||
self.is_sim = b
|
||||
|
||||
def reboot(self,):
|
||||
if self.is_sim:
|
||||
print("Sending restart command...")
|
||||
self.send_magic_reboot()
|
||||
Tester.delay(0.25)
|
||||
else:
|
||||
print("Please reboot authentictor and hit enter")
|
||||
input()
|
||||
self.find_device()
|
||||
|
||||
def send_data(self, cmd, data):
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
with Timeout(1.0) as event:
|
||||
return self.dev.call(cmd, data, event)
|
||||
|
||||
def send_raw(self, data, cid=None):
|
||||
if cid is None:
|
||||
cid = self.dev._dev.cid
|
||||
elif not isinstance(cid, bytes):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
if not isinstance(data, bytes):
|
||||
data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
|
||||
data = cid + data
|
||||
l = len(data)
|
||||
if l != 64:
|
||||
pad = "\x00" * (64 - l)
|
||||
pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
|
||||
data = data + pad
|
||||
data = list(data)
|
||||
assert len(data) == 64
|
||||
self.dev._dev.InternalSendPacket(Packet(data))
|
||||
|
||||
def send_magic_reboot(self,):
|
||||
"""
|
||||
For use in simulation and testing. Random bytes that authentictor should detect
|
||||
and then restart itself.
|
||||
"""
|
||||
magic_cmd = (
|
||||
b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
|
||||
+ b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
|
||||
+ b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
|
||||
+ b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
|
||||
+ b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
|
||||
+ b"\x9a\x72\x50\xdc"
|
||||
)
|
||||
self.dev._dev.InternalSendPacket(Packet(magic_cmd))
|
||||
|
||||
def cid(self,):
|
||||
return self.dev._dev.cid
|
||||
|
||||
def set_cid(self, cid):
|
||||
if not isinstance(cid, (bytes, bytearray)):
|
||||
cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
|
||||
self.dev._dev.cid = cid
|
||||
|
||||
def recv_raw(self,):
|
||||
with Timeout(1.0):
|
||||
cmd, payload = self.dev._dev.InternalRecv()
|
||||
return cmd, payload
|
||||
|
||||
def check_error(data, err=None):
|
||||
assert len(data) == 1
|
||||
if err is None:
|
||||
if data[0] != 0:
|
||||
raise CtapError(data[0])
|
||||
elif data[0] != err:
|
||||
raise ValueError("Unexpected error: %02x" % data[0])
|
||||
|
||||
def testFunc(self, func, test, *args, **kwargs):
|
||||
with Test(test):
|
||||
res = None
|
||||
expectedError = kwargs.get("expectedError", None)
|
||||
otherArgs = kwargs.get("other", {})
|
||||
try:
|
||||
res = func(*args, **otherArgs)
|
||||
if expectedError != CtapError.ERR.SUCCESS:
|
||||
raise RuntimeError("Expected error to occur for test: %s" % test)
|
||||
except CtapError as e:
|
||||
if expectedError is not None:
|
||||
cond = e.code != expectedError
|
||||
if isinstance(expectedError, list):
|
||||
cond = e.code not in expectedError
|
||||
else:
|
||||
expectedError = [expectedError]
|
||||
if cond:
|
||||
raise RuntimeError(
|
||||
f"Got error code {hex(e.code)}, expected {[hex(x) for x in expectedError]}"
|
||||
)
|
||||
else:
|
||||
print(e)
|
||||
return res
|
||||
|
||||
def testReset(self,):
|
||||
print("Resetting Authenticator...")
|
||||
try:
|
||||
self.ctap.reset()
|
||||
except CtapError:
|
||||
# Some authenticators need a power cycle
|
||||
print("You must power cycle authentictor. Hit enter when done.")
|
||||
input()
|
||||
time.sleep(0.2)
|
||||
self.find_device()
|
||||
self.ctap.reset()
|
||||
|
||||
def testMC(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
|
||||
|
||||
def testGA(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
|
||||
|
||||
def testCP(self, test, *args, **kwargs):
|
||||
return self.testFunc(self.ctap.client_pin, test, *args, **kwargs)
|
||||
|
||||
def testPP(self, test, *args, **kwargs):
|
||||
return self.testFunc(
|
||||
self.client.pin_protocol.get_pin_token, test, *args, **kwargs
|
||||
)
|
||||
|
||||
def delay(secs):
|
||||
time.sleep(secs)
|
@ -1,121 +0,0 @@
|
||||
from fido2.ctap1 import CTAP1, ApduError, APDU
|
||||
from fido2.utils import sha256
|
||||
from fido2.client import _call_polling
|
||||
|
||||
from .tester import Tester, Test
|
||||
|
||||
|
||||
class U2FTests(Tester):
|
||||
def __init__(self, tester=None):
|
||||
super().__init__(tester)
|
||||
|
||||
def run(self,):
|
||||
self.test_u2f()
|
||||
|
||||
def register(self, chal, appid):
|
||||
reg_data = _call_polling(0.25, None, None, self.ctap1.register, chal, appid)
|
||||
return reg_data
|
||||
|
||||
def authenticate(self, chal, appid, key_handle, check_only=False):
|
||||
auth_data = _call_polling(
|
||||
0.25,
|
||||
None,
|
||||
None,
|
||||
self.ctap1.authenticate,
|
||||
chal,
|
||||
appid,
|
||||
key_handle,
|
||||
check_only=check_only,
|
||||
)
|
||||
return auth_data
|
||||
|
||||
def test_u2f(self,):
|
||||
chal = sha256(b"AAA")
|
||||
appid = sha256(b"BBB")
|
||||
lastc = 0
|
||||
|
||||
regs = []
|
||||
|
||||
with Test("Check version"):
|
||||
assert self.ctap1.get_version() == "U2F_V2"
|
||||
|
||||
with Test("Check bad INS"):
|
||||
try:
|
||||
self.ctap1.send_apdu(0, 0, 0, 0, b"")
|
||||
except ApduError as e:
|
||||
assert e.code == 0x6D00
|
||||
|
||||
with Test("Check bad CLA"):
|
||||
try:
|
||||
self.ctap1.send_apdu(1, CTAP1.INS.VERSION, 0, 0, b"abc")
|
||||
except ApduError as e:
|
||||
assert e.code == 0x6E00
|
||||
|
||||
for i in range(0, self.user_count):
|
||||
with Test(
|
||||
"U2F reg + auth %d/%d (count: %02x)" % (i + 1, self.user_count, lastc)
|
||||
):
|
||||
reg = self.register(chal, appid)
|
||||
reg.verify(appid, chal)
|
||||
auth = self.authenticate(chal, appid, reg.key_handle)
|
||||
auth.verify(appid, chal, reg.public_key)
|
||||
|
||||
regs.append(reg)
|
||||
# check endianness
|
||||
if lastc:
|
||||
assert (auth.counter - lastc) < 10
|
||||
lastc = auth.counter
|
||||
if lastc > 0x80000000:
|
||||
print("WARNING: counter is unusually high: %04x" % lastc)
|
||||
assert 0
|
||||
|
||||
for i in range(0, self.user_count):
|
||||
with Test(
|
||||
"Checking previous registration %d/%d" % (i + 1, self.user_count)
|
||||
):
|
||||
auth = self.authenticate(chal, appid, regs[i].key_handle)
|
||||
auth.verify(appid, chal, regs[i].public_key)
|
||||
|
||||
print("Check that all previous credentials are registered...")
|
||||
for i in range(0, self.user_count):
|
||||
with Test("Check that previous credential %d is registered" % i):
|
||||
try:
|
||||
auth = self.ctap1.authenticate(
|
||||
chal, appid, regs[i].key_handle, check_only=True
|
||||
)
|
||||
except ApduError as e:
|
||||
# Indicates that key handle is registered
|
||||
assert e.code == APDU.USE_NOT_SATISFIED
|
||||
|
||||
with Test("Check an incorrect key handle is not registered"):
|
||||
kh = bytearray(regs[0].key_handle)
|
||||
kh[0] = kh[0] ^ (0x40)
|
||||
try:
|
||||
self.ctap1.authenticate(chal, appid, kh, check_only=True)
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
||||
|
||||
with Test("Try to sign with incorrect key handle"):
|
||||
try:
|
||||
self.ctap1.authenticate(chal, appid, kh)
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
||||
|
||||
with Test("Try to sign using an incorrect keyhandle length"):
|
||||
try:
|
||||
kh = regs[0].key_handle
|
||||
self.ctap1.authenticate(chal, appid, kh[: len(kh) // 2])
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
||||
|
||||
with Test("Try to sign using an incorrect appid"):
|
||||
badid = bytearray(appid)
|
||||
badid[0] = badid[0] ^ (0x40)
|
||||
try:
|
||||
auth = self.ctap1.authenticate(chal, badid, regs[0].key_handle)
|
||||
assert 0
|
||||
except ApduError as e:
|
||||
assert e.code == APDU.WRONG_DATA
|
@ -1,12 +0,0 @@
|
||||
import math
|
||||
|
||||
|
||||
def shannon_entropy(data):
|
||||
s = 0.0
|
||||
total = len(data)
|
||||
for x in range(0, 256):
|
||||
freq = data.count(x)
|
||||
p = freq / total
|
||||
if p > 0:
|
||||
s -= p * math.log2(p)
|
||||
return s
|
@ -1,19 +0,0 @@
|
||||
# Notify ModemManager this device should be ignored
|
||||
ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
|
||||
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
|
||||
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
|
||||
|
||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
|
||||
LABEL="mm_usb_device_blacklist_end"
|
||||
|
||||
|
||||
# Solo bootloader + firmware access
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess"
|
||||
|
||||
# ST DFU access
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", TAG+="uaccess"
|
||||
|
||||
# U2F Zero
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", TAG+="uaccess"
|
@ -1,19 +0,0 @@
|
||||
# Notify ModemManager this device should be ignored
|
||||
ACTION!="add|change|move", GOTO="mm_usb_device_blacklist_end"
|
||||
SUBSYSTEM!="usb", GOTO="mm_usb_device_blacklist_end"
|
||||
ENV{DEVTYPE}!="usb_device", GOTO="mm_usb_device_blacklist_end"
|
||||
|
||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ENV{ID_MM_DEVICE_IGNORE}="1"
|
||||
|
||||
LABEL="mm_usb_device_blacklist_end"
|
||||
|
||||
|
||||
# Solo bootloader + firmware access
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="0660", GROUP="plugdev"
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", MODE="0660", GROUP="plugdev"
|
||||
|
||||
# ST DFU access
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0660", GROUP="plugdev"
|
||||
|
||||
# U2F Zero
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", MODE="0660", GROUP="plugdev"
|
@ -1,17 +0,0 @@
|
||||
# TODO: would like to lookup ID_SERIAL_SHORT from `usb` SUBSYSTEM
|
||||
# but link on `hidraw` subsystem level
|
||||
# and end up with symlinks `/dev/solo[hacker|secure]-<serial>`
|
||||
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", SYMLINK+="solo-$env{ID_SERIAL_SHORT}-%n"
|
||||
## Solo Secure symlinks
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo [1-9]*", SYMLINK+="solosecure-$env{ID_SERIAL_SHORT}-%n"
|
||||
## Solo Hacker symlinks
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", ATTRS{product}=="Solo Hacker [1-9]*", SYMLINK+="solohacker-$env{ID_SERIAL_SHORT}-%n"
|
||||
## Solo Serial access + symlink
|
||||
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", SYMLINK+="soloserial"
|
||||
|
||||
# Non-unique rules (breakdown if multiple Solos are plugged in)
|
||||
## Solo
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", SYMLINK+="solo"
|
||||
## U2F Zero
|
||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="8acf", SYMLINK+="u2fzero"
|
@ -1,30 +0,0 @@
|
||||
# On modern systems, udev has a TAG uaccess, which is used in 73-seat-late.rules
|
||||
# On older systems, we use GROUP plugdev with MODE
|
||||
# --> Try `make setup` first, if it doesn't work, try `make legacy-setup`.
|
||||
#
|
||||
# The symlinks are optional, install with `make symlinks`.
|
||||
#
|
||||
# We keep 99-solo.rules in the parent directory but deprecate it,
|
||||
# remove when documentation is updated.
|
||||
|
||||
|
||||
setup: install activate
|
||||
legacy-setup: install-legacy activate
|
||||
|
||||
# Symlinks can be setup, we don't officially supply any
|
||||
# symlinks: install-symlinks activate
|
||||
|
||||
RULES_PATH=/etc/udev/rules.d
|
||||
|
||||
activate:
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
|
||||
install:
|
||||
sudo cp $(PWD)/70-solokeys-access.rules ${RULES_PATH}/70-solokeys-access.rules
|
||||
|
||||
install-legacy:
|
||||
sudo cp $(PWD)/70-solokeys-legacy-access.rules ${RULES_PATH}/70-solokeys-access.rules
|
||||
|
||||
# install-symlinks:
|
||||
# sudo cp $(PWD)/71-solokeys-symlinks.rules ${RULES_PATH}/71-solokeys-symlinks.rules
|
@ -1,14 +0,0 @@
|
||||
This is for Linux systems only.
|
||||
|
||||
To install the official SoloKeys udev rules, allowing access to your key, run
|
||||
|
||||
```
|
||||
make install
|
||||
```
|
||||
|
||||
This should work assuming your system is reasonably up-to-date. If not, try
|
||||
|
||||
```
|
||||
make install-legacy
|
||||
```
|
||||
|
Reference in New Issue
Block a user