diff --git a/.all-contributorsrc b/.all-contributorsrc
new file mode 100644
index 0000000..aed6148
--- /dev/null
+++ b/.all-contributorsrc
@@ -0,0 +1,178 @@
+{
+ "files": [
+ "README.md"
+ ],
+ "imageSize": 100,
+ "commit": false,
+ "contributors": [
+ {
+ "login": "szszszsz",
+ "name": "Szczepan Zalega",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/17005426?v=4",
+ "profile": "https://github.com/szszszsz",
+ "contributions": [
+ "code",
+ "doc",
+ "ideas"
+ ]
+ },
+ {
+ "login": "Wesseldr",
+ "name": "Wessel dR",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/4012809?v=4",
+ "profile": "https://github.com/Wesseldr",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "agl",
+ "name": "Adam Langley",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/21203?v=4",
+ "profile": "https://www.imperialviolet.org",
+ "contributions": [
+ "bug",
+ "code"
+ ]
+ },
+ {
+ "login": "merlokk",
+ "name": "Oleg Moiseenko",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/807634?v=4",
+ "profile": "http://www.lotteam.com",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "aseigler",
+ "name": "Alex Seigler",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/6605560?v=4",
+ "profile": "https://github.com/aseigler",
+ "contributions": [
+ "bug"
+ ]
+ },
+ {
+ "login": "dschuermann",
+ "name": "Dominik SchΓΌrmann",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/321888?v=4",
+ "profile": "https://www.cotech.de/services/",
+ "contributions": [
+ "bug"
+ ]
+ },
+ {
+ "login": "ehershey",
+ "name": "Ernie Hershey",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/286008?v=4",
+ "profile": "https://github.com/ehershey",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "YakBizzarro",
+ "name": "Andrea Corna",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/767740?v=4",
+ "profile": "https://github.com/YakBizzarro",
+ "contributions": [
+ "infra"
+ ]
+ },
+ {
+ "login": "pjz",
+ "name": "Paul Jimenez",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/11100?v=4",
+ "profile": "https://place.org/~pj/",
+ "contributions": [
+ "infra",
+ "code"
+ ]
+ },
+ {
+ "login": "yparitcher",
+ "name": "yparitcher",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/38916402?v=4",
+ "profile": "https://github.com/yparitcher",
+ "contributions": [
+ "ideas",
+ "maintenance"
+ ]
+ },
+ {
+ "login": "StoyanDimitrov",
+ "name": "StoyanDimitrov",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/10962709?v=4",
+ "profile": "https://github.com/StoyanDimitrov",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "alphathegeek",
+ "name": "alphathegeek",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/51253712?v=4",
+ "profile": "https://github.com/alphathegeek",
+ "contributions": [
+ "ideas"
+ ]
+ },
+ {
+ "login": "rgerganov",
+ "name": "Radoslav Gerganov",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/271616?v=4",
+ "profile": "https://xakcop.com",
+ "contributions": [
+ "ideas",
+ "code"
+ ]
+ },
+ {
+ "login": "manuel-domke",
+ "name": "Manuel Domke",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/10274356?v=4",
+ "profile": "http://13-37.org",
+ "contributions": [
+ "ideas",
+ "code",
+ "business"
+ ]
+ },
+ {
+ "login": "esden",
+ "name": "Piotr Esden-Tempski",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/17334?v=4",
+ "profile": "http://1bitsquared.com",
+ "contributions": [
+ "business"
+ ]
+ },
+ {
+ "login": "m3hm00d",
+ "name": "f.m3hm00d",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/42179593?v=4",
+ "profile": "https://github.com/m3hm00d",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "hughsie",
+ "name": "Richard Hughes",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/151380?v=4",
+ "profile": "http://blogs.gnome.org/hughsie/",
+ "contributions": [
+ "ideas",
+ "code",
+ "infra",
+ "tool"
+ ]
+ }
+ ],
+ "contributorsPerLine": 7,
+ "projectName": "solo",
+ "projectOwner": "solokeys",
+ "repoType": "github",
+ "repoHost": "https://github.com"
+}
diff --git a/.gitignore b/.gitignore
index e80f85c..41478aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -83,3 +83,5 @@ targets/*/docs/
main
builds/*
+tools/testing/.idea/*
+tools/testing/tests/__pycache__/*
diff --git a/README.md b/README.md
index b243ea1..ba13ea7 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,18 @@
-[](https://github.com/solokeys/solo/blob/master/LICENSE)
-[](https://travis-ci.com/solokeys/solo)
-[](https://discourse.solokeys.com)
+**NEW!** We launched a new tiny security key called Somu, it's live on Crowd Supply and you can [pre-order it now](https://solokeys.com/somu)!
+
+[
](https://solokeys.com/somu)
+
+Somu is the micro version of Solo. We were inspired to make a secure Tomu, so we took its tiny form factor, we added the secure microcontroller and firmware of Solo, et voilΓ ! Here we have Somu.
+
+[](https://update.solokeys.com/)
[](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
+[](https://travis-ci.com/solokeys/solo)
Solo is an open source security key, and you can get one at [solokeys.com](https://solokeys.com).
-Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
+[
](https://solokeys.com)
-
+Solo supports FIDO2 and U2F standards for strong two-factor authentication and password-less login, and it will protect you against phishing and other online attacks. With colored cases and multilingual guides we want to make secure login more personable and accessible to everyone around the globe.
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.
@@ -41,7 +36,7 @@ Solo for Hacker is a special version of Solo that let you customize its firmware
Check out [solokeys.com](https://solokeys.com), for options on where to buy Solo. Solo Hacker can be converted to a secure version, but normal Solo cannot be converted to a Hacker version.
-If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We only support Python3.
+If you have a Solo for Hacker, here's how you can load your own code on it. You can find more details, including how to permanently lock it, in our [documentation](https://docs.solokeys.io/solo/building/). We support Python3.
```bash
git clone --recurse-submodules https://github.com/solokeys/solo
@@ -94,10 +89,7 @@ Run the Solo application:
./main
```
-In another shell, you can run client software, for example our tests:
-```bash
-python tools/ctap_test.py sim fido2
-```
+In another shell, you can run our [test suite](https://github.com/solokeys/fido2-tests).
You can find more details in our [documentation](https://docs.solokeys.io/solo/), including how to build on the the NUCLEO-L432KC development board.
@@ -107,14 +99,46 @@ You can find more details in our [documentation](https://docs.solokeys.io/solo/)
Check out our [official documentation](https://docs.solokeys.io/solo/).
-# Contributors
+# Contributors β¨
Solo is an upgrade to [U2F Zero](https://github.com/conorpp/u2f-zero). It was born from Conor's passion for making secure hardware, and from our shared belief that security should be open to be trustworthy, in hardware like in software.
-Contributors are welcome. The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
-
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+The ultimate goal is to have a FIDO2 security key supporting USB, NFC, and BLE interfaces, that can run on a variety of MCUs.
Look at the issues to see what is currently being worked on. Feel free to add issues as well.
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+
+
+
+
+
+
+
# License
@@ -123,6 +147,8 @@ Solo is fully open source.
All software, unless otherwise noted, is dual licensed under Apache 2.0 and MIT.
You may use Solo software under the terms of either the Apache 2.0 license or MIT license.
+Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
+
All hardware, unless otherwise noted, is dual licensed under CERN and CC-BY-SA.
You may use Solo hardware under the terms of either the CERN 2.1 license or CC-BY-SA 4.0 license.
@@ -135,3 +161,20 @@ You may use Solo documentation under the terms of the CC-BY-SA 4.0 license
# Where To Buy Solo
You can buy Solo, Solo Tap, and Solo for Hackers at [solokeys.com](https://solokeys.com).
+
+
+
+
+
+[](https://github.com/solokeys/solo/blob/master/LICENSE)
+[](#contributors)
+[](https://travis-ci.com/solokeys/solo)
+[](https://discourse.solokeys.com)
+[](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)
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..2ba3dde
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,32 @@
+# Security Policy
+
+## Supported Versions
+
+We fix security issues as soon as they are found, and release firmware updates.
+Each such release is accompanied by release notes, see .
+
+The latest version can be determined using the file .
+
+To update your key:
+- either visit , or
+- use our commandline tool :
+```
+solo key update [--secure|--hacker]
+```
+
+## Reporting a Vulnerability
+
+To report vulnerabilities you have found:
+
+- preferably contact [@conor1](https://keybase.io/conor1), [@0x0ece](https://keybase.io/0x0ece) or [@nickray](https://keybase.io/nickray) via Keybase, or
+- send us e-mail using OpenPGP to [security@solokeys.com](mailto:security@solokeys.com).
+
+
+
+We do not currently run a paid bug bounty program, but are happy to provide you with a bunch of Solo keys in recognition of your findings.
+
+## Mailing List
+
+Join our release notification mailing list to be informed about each release:
+
+https://sendy.solokeys.com/subscription?f=9MLIqMDmox1Ucz89C892Kq09IqYMM7OB8UrBrkvtTkDI763QF3L5PMYlRhlVNo2AI892mO
diff --git a/STABLE_VERSION b/STABLE_VERSION
index 276cbf9..35cee72 100644
--- a/STABLE_VERSION
+++ b/STABLE_VERSION
@@ -1 +1 @@
-2.3.0
+2.4.3
diff --git a/docs/solo/bootloader-mode.md b/docs/solo/bootloader-mode.md
new file mode 100644
index 0000000..ff4358a
--- /dev/null
+++ b/docs/solo/bootloader-mode.md
@@ -0,0 +1,51 @@
+# Booting into bootloader mode
+
+You can put Solo into bootloader mode by holding down the button, and plugging in Solo. After 2 seconds, bootloader mode will activate.
+You'll see a yellowish flashing light and you can let go of the button.
+
+Now Solo is ready to [accept firmware updates](/solo/signed-updates). If the Solo is a secured model, it can only accept signed updates, typically in the `firmware-*.json` format.
+
+If Solo is running a hacker build, it can be put into bootloader mode on command. This makes it easier for development.
+
+```bash
+solo program aux enter-bootloader
+```
+
+# The boot stages of Solo
+
+Solo has 3 boot stages.
+
+## DFU
+
+The first stage is the DFU (Device Firmware Update) which is in a ROM on Solo. It is baked into the chip and is not implemented by us.
+This is what allows the entire firmware of Solo to be programmed. **It's not recommended to develop for Solo using the DFU because
+if you program broken firmware, you could brick your device**.
+
+On hacker devices, you can boot into the DFU by holding down the button for 5 seconds, when Solo is already in bootloader mode.
+
+You can also run this command when Solo is in bootloader mode to put it in DFU mode.
+
+```bash
+solo program aux enter-dfu
+```
+
+Note it will stay in DFU mode until to tell it to boot again. You can boot it again by running the following.
+
+```bash
+solo program aux leave-dfu
+```
+
+*Warning*: If you change the firmware to something broken, and you tell the DFU to boot it, you could brick your device.
+
+## Solo Bootloader
+
+The next boot stage is the "Solo bootloader". So when we say to put your Solo into bootloader mode, it is this stage.
+This bootloader is written by us and allows signed firmware updates to be written. On Solo Hackers, there is no signature checking
+and will allow any firmware updates.
+
+It is safe to develop for Solo using our Solo bootloader. If broken firmware is uploaded to the device, then the Solo
+bootloader can always be booted again by holding down the button when plugging in.
+
+## Solo application
+
+This is what contains all the important functionality of Solo. FIDO2, U2F, etc. This is what Solo will boot to by default.
diff --git a/docs/solo/building.md b/docs/solo/building.md
index eed67c8..3fbfb9b 100644
--- a/docs/solo/building.md
+++ b/docs/solo/building.md
@@ -1,22 +1,34 @@
+# Building solo
+
To build, develop and debug the firmware for the STM32L432. This will work
for Solo Hacker, the Nucleo development board, or your own homemade Solo.
There exists a development board [NUCLEO-L432KC](https://www.st.com/en/evaluation-tools/nucleo-l432kc.html) you can use; The board does contain a debugger, so all you need is a USB cable (and some [udev](/udev) [rules](https://rust-embedded.github.io/book/intro/install/linux.html#udev-rules)).
-# Prerequisites
+## Prerequisites
Install the [latest ARM compiler toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads) for your system. We recommend getting the latest compilers from ARM.
You can also install the ARM toolchain using a package manager like `apt-get` or `pacman`,
but be warned they might be out of date. Typically it will be called `gcc-arm-none-eabi binutils-arm-none-eabi`.
-To program your build, you'll need one of the following programs.
+Install `solo-python` usually with `pip3 install solo-python`. The `solo` python application may also be used for [programming](#programming).
-- [openocd](http://openocd.org)
-- [stlink](https://github.com/texane/stlink)
-- [STM32CubeProg](https://www.st.com/en/development-tools/stm32cubeprog.html)
+## Obtain source code and solo tool
-# Compilation
+Source code can be downloaded from:
+
+- [github releases list](https://github.com/solokeys/solo/releases)
+- [github repository](https://github.com/solokeys/solo)
+
+**solo** tool can be downloaded from:
+
+- from python programs [repository](https://pypi.org/project/solo-python/) `pip install solo-python`
+- from installing prerequisites `pip3 install -r tools/requirements.txt`
+- github repository: [repository](https://github.com/solokeys/solo-python)
+- installation python enviroment with command `make venv` from root directory of source code
+
+## Compilation
Enter the `stm32l4xx` target directory.
@@ -36,7 +48,7 @@ enabled, like being able to jump to the bootloader on command. It then merges b
and solo builds into the same binary. I.e. it combines `bootloader.hex` and `solo.hex`
into `all.hex`.
-If you're just planning to do development, please don't try to reprogram the bootloader,
+If you're just planning to do development, **please don't try to reprogram the bootloader**,
as this can be risky if done often. Just use `solo.hex`.
### Building with debug messages
@@ -68,6 +80,8 @@ solo monitor
### Building a Solo release
+To build Solo
+
If you want to build a release of Solo, we recommend trying a Hacker build first
just to make sure that it's working. Otherwise it may not be as easy or possible to
fix any mistakes.
@@ -78,106 +92,13 @@ If you're ready to program a full release, run this recipe to build.
make build-release-locked
```
-Programming `all.hex` will cause the device to permanently lock itself.
+This outputs bootloader.hex, solo.hex, and the combined all.hex.
+Programming `all.hex` will cause the device to permanently lock itself. This means debuggers cannot be used and signature checking
+will be enforced on all future updates.
-# Programming
+Note if you program a secured `solo.hex` file onto a Solo Hacker, it will lock the flash, but the bootloader
+will still accept unsigned firmware updates. So you can switch it back to being a hacker, but you will
+not be able to replace the unlocked bootloader anymore, since the permanently locked flash also disables the DFU.
+[Read more on Solo's boot stages](/solo/bootloader-mode).
-It's recommended to test a debug/hacker build first to make sure Solo is working as expected.
-Then you can switch to a locked down build, which cannot be reprogrammed as easily (or not at all!).
-
-We recommend using our `solo` tool to manage programming. It is cross platform. First you must
-install the prerequisites:
-
-```
-pip3 install -r tools/requirements.txt
-```
-
-If you're on Windows, you must also install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/).
-
-## Pre-programmed Solo Hacker
-
-If your Solo device is already programmed (it flashes green when powered), we recommend
-programming it using the Solo bootloader.
-
-```
-solo program aux enter-bootloader
-solo program bootloader solo.hex
-```
-
-Make sure to program `solo.hex` and not `all.hex`. Nothing bad would happen, but you'd
-see errors.
-
-If something bad happens, you can always boot the Solo bootloader by doing the following.
-
-1. Unplug device.
-2. Hold down button.
-3. Plug in device while holding down button.
-4. Wait about 2 seconds for flashing yellow light. Release button.
-
-If you hold the button for an additional 5 seconds, it will boot to the ST DFU (device firmware update).
-Don't use the ST DFU unless you know what you're doing.
-
-## ST USB DFU
-
-If your Solo has never been programmed, it will boot the ST USB DFU. The LED is turned
-off and it enumerates as "STM BOOTLOADER".
-
-You can program it by running the following.
-
-```
-solo program aux enter-bootloader
-solo program aux enter-dfu
-# powercycle key
-solo program dfu all.hex
-```
-
-Make sure to program `all.hex`, as this contains both the bootloader and the Solo application.
-
-If all goes well, you should see a slow-flashing green light.
-
-## Solo Hacker vs Solo
-
-A Solo hacker device doesn't need to be in bootloader mode to be programmed, it will automatically switch.
-
-Solo (locked) needs the button to be held down when plugged in to boot to the bootloader.
-
-A locked Solo will only accept signed updates.
-
-## Signed updates
-
-If this is not a device with a hacker build, you can only program signed updates.
-
-```
-solo program bootloader /path/to/firmware.json
-```
-
-If you've provisioned the Solo bootloader with your own secp256r1 public key, you can sign your
-firmware by running the following command.
-
-```
-solo sign /path/to/signing-key.pem /path/to/solo.hex /output-path/to/firmware.json
-```
-
-If your Solo isn't locked, you can always reprogram it using a debugger connected directly
-to the token.
-
-# Permanently locking the device
-
-If you plan to be using your Solo for real, you should lock it permanently. This prevents
-someone from connecting a debugger to your token and stealing credentials.
-
-To do this, build the locked release firmware.
-```
-make build-release-locked
-```
-
-Now when you program `all.hex`, the device will lock itself when it first boots. You can only update it
-with signed updates.
-
-If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
-
-```
-# WARNING: No more signed updates.
-solo program disable-bootloader
-```
diff --git a/docs/solo/customization.md b/docs/solo/customization.md
new file mode 100644
index 0000000..601a576
--- /dev/null
+++ b/docs/solo/customization.md
@@ -0,0 +1,141 @@
+# Customization
+
+If you are interested in customizing parts of your Solo, and you have a Solo Hacker, this page is for you.
+
+## Custom Attestation key
+
+The attestation key is used in the FIDO2 *makeCredential* or U2F *register* requests. It signs
+newly generated credentials. The certificate associated with the attestation key is output with newly created credentials.
+
+Platforms or services can use the attestation feature to enforce specific authenticators to be used.
+This is typically a use case for organizations and isn't seen in the wild for consumer use cases.
+
+Attestation keys are typically the same for at least 100K units of a particular authenticator model.
+This is so they don't contribute a significant fingerprint that platforms could use to identify the user.
+
+If you don't want to use the default attestation key that Solo builds with, you can create your own
+and program it.
+
+### Creating your attestation key pair
+
+Since we are generating keys, it's important to use a good entropy source.
+You can use the [True RNG on your Solo](/solo/solo-extras) to generate some good random numbers.
+
+```
+# Run for 1 second, then hit control-c
+solo key rng raw > seed.bin
+```
+
+First we will create a self signed key pair that acts as the root of trust. This
+won't go on the authenticator, but will sign the keypair that does.
+
+Please change the root certification information as needed. You may change the ECC curve.
+
+```
+curve=prime256v1
+
+country=US
+state=Maine
+organization=OpenSourceSecurity
+unit="Root CA"
+CN=example.com
+email=example@example.com
+
+# generate EC private key
+openssl ecparam -genkey -name "$curve" -out root_key.pem -rand seed.bin
+
+# generate a "signing request"
+openssl req -new -key root_key.pem -out root_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
+
+# self sign the request
+openssl x509 -trustout -req -days 18250 -in root_key.pem.csr -signkey root_key.pem -out root_cert.pem -sha256
+
+# convert to smaller size format DER
+openssl x509 -in root_cert.pem -outform der -out root_cert.der
+
+# print out information and verify
+openssl x509 -in root_cert.pem -text -noout
+```
+
+You need to create a extended certificate for the device certificate to work with FIDO2. You need to create this
+file, `v3.ext`, and add these options to it.
+
+```
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+```
+
+Now to generate & sign the attestation key pair that will go on your device, or maybe 100,000 devices :).
+Note you must use a prime256v1 curve for this step, and you must leave the unit/OU as "Authenticator Attestation".
+
+```
+country=US
+state=Maine
+organization=OpenSourceSecurity
+unit="Authenticator Attestation"
+CN=example.com
+email=example@example.com
+
+# generate EC private key
+openssl ecparam -genkey -name "$curve" -out device_key.pem -rand seed.bin
+
+# generate a "signing request"
+openssl req -new -key device_key.pem -out device_key.pem.csr -subj "/C=$country/ST=$state/O=$organization/OU=$unit/CN=example.com/emailAddress=$email"
+
+# sign the request
+openssl x509 -req -days 18250 -in device_key.pem.csr -extfile v3.ext -CA root_cert.pem -CAkey root_key.pem -set_serial 01 -out device_cert.pem -sha256
+
+# convert to smaller size format DER
+openssl x509 -in device_cert.pem -outform der -out device_cert.der
+
+# Verify the device certificate details
+openssl x509 -in device_cert.pem -text -noout
+```
+
+Let's verify that the attestation key and certificate are valid, and that they can be verified with the root key pair.
+
+```
+echo 'challenge $RANDOM' > chal.txt
+
+# check that they are valid key pairs
+openssl dgst -sha256 -sign device_key.pem -out sig.txt chal.txt
+openssl dgst -sha256 -verify <(openssl x509 -in device_cert.pem -pubkey -noout) -signature sig.txt chal.txt
+
+openssl dgst -sha256 -sign "root_key.pem" -out sig.txt chal.txt
+openssl dgst -sha256 -verify <(openssl x509 -in root_cert.pem -pubkey -noout) -signature sig.txt chal.txt
+
+# Check they are a chain
+openssl verify -verbose -CAfile "root_cert.pem" "device_cert.pem"
+```
+
+If the checks succeed, you are ready to program the device attestation key and certificate.
+
+### Programming an attestation key and certificate
+
+Convert the DER format of the device attestation certificate to "C" bytes using our utility script. You may first need to
+first install prerequisite python modules (pip install -r tools/requirements.txt).
+
+```
+python tools/gencert/cbytes.py device_cert.der
+```
+
+Copy the byte string portion into the [`attestation.c` source file of Solo](https://github.com/solokeys/solo/blob/master/targets/stm32l432/src/attestation.c). Overwrite the development or "default" certificate that is already there.
+
+Now [build the Solo firmware](/solo/building), either a secure or hacker build. You will need to produce a bootloader.hex file and a solo.hex file.
+
+Print your attestation key in a hex string format.
+
+```
+python tools/print_x_y.py device_key.pem
+```
+
+Merge the bootloader.hex, solo.hex, and attestion key into one firmware file.
+
+```
+solo mergehex --attestation-key bootloader.hex solo.hex all.hex
+```
+
+Now you have a newly create `all.hex` file with a custom attestation key. You can [program this all.hex file
+with Solo in DFU mode](/solo/programming#procedure).
\ No newline at end of file
diff --git a/docs/solo/images/conforms.PNG b/docs/solo/images/conforms.PNG
new file mode 100644
index 0000000..5aaec6f
Binary files /dev/null and b/docs/solo/images/conforms.PNG differ
diff --git a/docs/solo/images/solo_conforms.PNG b/docs/solo/images/solo_conforms.PNG
new file mode 100644
index 0000000..03e3bf1
Binary files /dev/null and b/docs/solo/images/solo_conforms.PNG differ
diff --git a/docs/solo/nucleo32-board.md b/docs/solo/nucleo32-board.md
new file mode 100644
index 0000000..ac579d2
--- /dev/null
+++ b/docs/solo/nucleo32-board.md
@@ -0,0 +1,258 @@
+# Nucleo32 board preparation
+
+Additional steps are required to run the firmware on the Nucleo32 board.
+
+## USB-A cable
+
+Board does not provide an USB cable / socket for the target MCU communication.
+Own provided USB plug has to be connected in the following way:
+
+| PIN / Arduino PIN | MCU leg | USB wire color | Signal |
+| ----------------- | ------- | -------------- | ------ |
+| D10 / PA11 | 21 | white | D- |
+| D2 / PA12 | 22 | green | D+ |
+| GND (near D2) | ------- | black | GND |
+| **not connected** | ------- | red | 5V |
+
+Each USB plug pin should be connected via the wire in a color defined by the standard. It might be confirmed with a
+multimeter for additional safety. USB plug description:
+
+| PIN | USB wire color | Signal |
+| --- | -------------- | ------ |
+| 4 | black | GND |
+| 3 | green | D+ |
+| 2 | white | D- |
+| 1 | red | 5V |
+
+See this [USB plug] image, and Wikipedia's [USB plug description].
+
+Plug in [USB-A_schematic.pdf] has wrong wire order, registered as [solo-hw#1].
+
+The power is taken from the debugger / board (unless the board is configured in another way).
+Make sure 5V is not connected, and is covered from contacting with the board elements.
+
+Based on [USB-A_schematic.pdf].
+
+## Firmware modification
+
+Following patch has to be applied to skip the user presence confirmation, for tests. Might be applied at a later stage.
+
+```text
+diff --git a/targets/stm32l432/src/app.h b/targets/stm32l432/src/app.h
+index c14a7ed..c89c3b5 100644
+--- a/targets/stm32l432/src/app.h
++++ b/targets/stm32l432/src/app.h
+@@ -71,6 +71,6 @@ void hw_init(void);
+ #define SOLO_BUTTON_PIN LL_GPIO_PIN_0
+
+ #define SKIP_BUTTON_CHECK_WITH_DELAY 0
+-#define SKIP_BUTTON_CHECK_FAST 0
++#define SKIP_BUTTON_CHECK_FAST 1
+
+ #endif
+```
+
+It is possible to provide a button and connect it to the MCU pins, as instructed in [USB-A_schematic.pdf]:
+
+```text
+PA0 / pin 6 --> button --> GND
+```
+
+In that case the mentioned patch would not be required.
+
+## Development environment setup
+
+Environment: Fedora 29 x64, Linux 4.19.9
+
+See for the original guide. Here details not included there will be covered.
+
+### Install ARM tools Linux
+
+1. Download current [ARM tools] package: [gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2].
+
+2. Extract the archive.
+
+3. Add full path to the `./bin` directory as first entry to the `$PATH` variable,
+ as in `~/gcc-arm/gcc-arm-none-eabi-8-2018-q4-major/bin/:$PATH`.
+
+### Install ARM tools OsX using brew package manager
+
+```bash
+brew tap ArmMbed/homebrew-formulae
+brew install arm-none-eabi-gcc
+```
+
+### Install flashing software
+
+ST provides a CLI flashing tool - `STM32_Programmer_CLI`. It can be downloaded directly from the vendor's site:
+1\. Go to [download site URL](https://www.st.com/content/st_com/en/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-programmers/stm32cubeprog.html),
+go to bottom page and from STM32CubeProg row select Download button.
+2\. Unzip contents of the archive.
+3\. Run \*Linux setup
+4\. In installation directory go to ./bin - there the ./STM32_Programmer_CLI is located
+5\. Add symlink to the STM32 CLI binary to .local/bin. Make sure the latter it is in $PATH.
+
+If you're on OsX and installed the STM32CubeProg, you need to add the following to your path:
+
+```bash
+# ~/.bash_profile
+export PATH="/Applications/STMicroelectronics/STM32Cube/STM32CubeProgrammer/STM32CubeProgrammer.app/Contents/MacOs/bin/":$PATH
+```
+
+## Building and flashing
+
+### Building
+
+Please follow , as the build way changes rapidly.
+Currently (8.1.19) to build the firmware, following lines should be executed
+
+```bash
+# while in the main project directory
+cd targets/stm32l432
+make cbor
+make build-hacker DEBUG=1
+```
+
+Note: `DEBUG=2` stops the device initialization, until a serial client will be attached to its virtual port.
+Do not use it, if you do not plan to do so.
+
+### Flashing via the Makefile command
+
+```bash
+# while in the main project directory
+# create Python virtual environment with required packages, and activate
+make venv
+. venv/bin/activate
+# Run flashing
+cd ./targets/stm32l432
+make flash
+ # which runs:
+ # flash: solo.hex bootloader.hex
+ # python merge_hex.py solo.hex bootloader.hex all.hex (intelhex library required)
+ # STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
+ # STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
+```
+
+### Manual flashing
+
+In case you already have a firmware to flash (named `all.hex`), please run the following:
+
+```bash
+STM32_Programmer_CLI -c port=SWD -halt -e all --readunprotect
+STM32_Programmer_CLI -c port=SWD -halt -d all.hex -rst
+```
+
+## Testing
+
+### Internal
+
+Project-provided tests.
+
+#### Simulated device
+
+A simulated device is provided to test the HID layer.
+
+##### Build
+
+```bash
+make clean
+cd tinycbor
+make
+cd ..
+make env2
+```
+
+##### Execution
+
+```bash
+# run simulated device (will create a network UDP server)
+./main
+# run test 1
+./env2/bin/python tools/ctap_test.py
+# run test 2 (or other files in the examples directory)
+./env2/bin/python python-fido2/examples/credential.py
+```
+
+#### Real device
+
+```bash
+# while in the main project directory
+# not passing as of 8.1.19, due to test solution issues
+make fido2-test
+```
+
+### External
+
+#### FIDO2 test sites
+
+1.
+2.
+3.
+
+#### U2F test sites
+
+1.
+2.
+
+#### FIDO2 standalone clients
+
+1.
+2.
+3.
+4.
+
+## USB serial console reading
+
+Device opens an USB-emulated serial port to output its messages. While Nucleo board offers such already,
+the Solo device provides its own.
+
+- Provided Python tool
+
+```bash
+python3 ../../tools/solotool.py monitor /dev/solokey-serial
+```
+
+- External application
+
+```bash
+sudo picocom -b 115200 /dev/solokey-serial
+```
+
+where `/dev/solokey-serial` is an udev symlink to `/dev/ttyACM1`.
+
+## Other
+
+### Dumping firmware
+
+Size is calculated using bash arithmetic.
+
+```bash
+STM32_Programmer_CLI -c port=SWD -halt -u 0x0 $((256*1024)) current.hex
+```
+
+### Software reset
+
+```bash
+STM32_Programmer_CLI -c port=SWD -rst
+```
+
+### Installing required Python packages
+
+Client script requires some Python packages, which could be easily installed locally to the project
+via the Makefile command. It is sufficient to run:
+
+```bash
+make env3
+```
+
+[solo-hw#1]: https://github.com/solokeys/solo-hw/issues/1
+
+[usb plug]: https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/USB.svg/1200px-USB.svg.png
+
+[usb plug description]: https://en.wikipedia.org/wiki/USB#Receptacle_(socket)_identification
+
+[usb-a_schematic.pdf]: https://github.com/solokeys/solo-hw/releases/download/1.2/USB-A_schematic.pdf
+
+[arm tools]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
+
+[gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2]: https://developer.arm.com/-/media/Files/downloads/gnu-rm/8-2018q4/gcc-arm-none-eabi-8-2018-q4-major-linux.tar.bz2?revision=d830f9dd-cd4f-406d-8672-cca9210dd220?product=GNU%20Arm%20Embedded%20Toolchain,64-bit,,Linux,8-2018-q4-major
diff --git a/docs/solo/programming.md b/docs/solo/programming.md
new file mode 100644
index 0000000..a63d39e
--- /dev/null
+++ b/docs/solo/programming.md
@@ -0,0 +1,113 @@
+# Programming
+
+This page documents how to update or program your Solo.
+
+## Prerequisites
+
+To program Solo, you'll likely only need to use our Solo tool.
+
+```python
+pip3 install solo-python
+```
+
+## Updating the firmware
+
+If you just want to update the firmware, you can run one of the following commands.
+Make sure your key [is in bootloader mode](/solo/bootloader-mode#solo-bootloader) first.
+
+```bash
+solo key update <--secure | --hacker>
+```
+
+You can manually install the [latest release](https://github.com/solokeys/solo/releases), or use a build that you made.
+
+```bash
+# If it's a hacker, it will automatically boot into bootloader mode.
+solo program bootloader
+```
+
+Note you won't be able to use `all.hex` or the `bundle-*.hex` builds, as these include the solo bootloader. You shouldn't
+risk changing the Solo bootloader unless you want to make it a secure device, or [make other customizations]().
+
+## Updating a Hacker to a Secure Solo
+
+Updating a hacker to be a secure build overwrites the [Solo bootloader](/solo/bootloader-mode#solo-bootloader).
+So it's important to not mess this up or you may brick your device.
+
+You can use a firmware build from the [latest release](https://github.com/solokeys/solo/releases) or use
+a build that you made yourself.
+
+You need to use a firmware file that has the combined bootloader and application (or at the very least just the bootloader).
+This means using the `bundle-*.hex` file or the `all.hex` from your build. If you overwrite the Solo flash with a missing bootloader,
+it will be bricked.
+
+We provide two types of bundled builds. The `bundle-hacker-*.hex` build is the hacker build. If you update with this,
+you will update the bootloader and application, but nothing will be secured. The `bundle-secure-non-solokeys.hex`
+is a secured build that will lock your device and it will behave just like a Secure Solo. The main difference is that
+it uses a "default" attestation key in the device, rather than the SoloKeys attestation key. There is no security
+concern with using our default attestation key, aside from a privacy implication that services can distinguish it from Solo Secure.
+
+### Procedure
+
+1. Boot into DFU mode.
+
+ # Enter Solo bootloader
+ solo program aux enter-bootloader
+
+ # Enter DFU
+ solo program aux enter-dfu
+
+ The device should be turned off.
+
+2. Program the device
+
+ solo program dfu
+
+ Double check you programmed it with bootloader + application (or just bootloader).
+ If you messed it up, simply don't do the next step and repeat this step correctly.
+
+3. Boot the device
+
+ Once Solo boots a secure build, it will lock the flash permantly from debugger access. Also the bootloader
+ will only accept signed firmware updates.
+
+ solo program aux leave-dfu
+
+If you are having problems with solo tool and DFU mode, you could alternatively try booting into DFU
+by holding down the button while Solo is in bootloader mode. Then try another programming tool that works
+with ST DFU:
+
+* STM32CubeProg
+* openocd
+* stlink
+
+Windows users need to install [libusb](https://sourceforge.net/projects/libusb-win32/files/libusb-win32-releases/1.2.6.0/)
+for solo-python to work with Solo's DFU.
+
+
+## Programming a Solo that hasn't been programmed
+
+A Solo that hasn't been programmed will boot into DFU mode. You can program
+it by following a bootloader, or combined bootloader + application.
+
+```
+solo program dfu
+```
+
+Then boot the device. Make sure it has a bootloader to boot to.
+
+```
+solo program aux leave-dfu
+```
+
+## Disable signed firmware updates
+
+If you'd like to also permanently disable signed updates, plug in your programmed Solo and run the following:
+
+```bash
+# WARNING: No more signed updates.
+solo program disable-bootloader
+```
+
+You won't be able to update to any new releases.
+
diff --git a/docs/solo/solo-extras.md b/docs/solo/solo-extras.md
new file mode 100644
index 0000000..9a72a3f
--- /dev/null
+++ b/docs/solo/solo-extras.md
@@ -0,0 +1,19 @@
+# Solo Extras
+
+## Random number generation
+
+Solo contains a True Random Number Generator (TRNG). A TRNG is a hardware based mechanism
+that leverages natural phenomenon to generate random numbers, which is can be better than a traditional
+RNG that has state and updates deterministically using cryptographic methods.
+
+You can easily access the TRNG stream on Solo using our python tool [solo-python](https://github.com/solokeys/solo-python).
+
+```
+solo key rng raw > random.bin
+```
+
+Or you can seed the state of the RNG on your kernel (/dev/random).
+
+```
+solo key rng feedkernel
+```
diff --git a/docs/solo/udev.md b/docs/solo/udev.md
index 6866f99..8a8b205 100644
--- a/docs/solo/udev.md
+++ b/docs/solo/udev.md
@@ -1,20 +1,21 @@
# Summary
-On Linux, by default USB dongles can't be accessed by users, for security reasons. To allow user access, so-called "udev rules" must be installed. (Under Fedora, your key may work without such a rule.)
+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.
-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):
+For some users, things will work automatically:
-```
-SUBSYSTEM=="hidraw", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="a2ca", TAG+="uaccess", MODE="0660", GROUP="plugdev"
-```
+ - Fedora seems to use a ["universal" udev rule for FIDO devices](https://github.com/amluto/u2f-hidraw-policy)
+ - Our udev rule made it into [libu2f-host](https://github.com/Yubico/libu2f-host/) v1.1.10
+ - Arch Linux [has this package](https://www.archlinux.org/packages/community/x86_64/libu2f-host/)
+ - [Debian sid](https://packages.debian.org/sid/libu2f-udev) and [Ubuntu Eon](https://packages.ubuntu.com/eoan/libu2f-udev) can use the `libu2f-udev` package
+ - Debian Buster and Ubuntu Disco still distribute v1.1.10, so need the manual rule
+ - FreeBSD has support in [u2f-devd](https://github.com/solokeys/solo/issues/144#issuecomment-500216020)
-Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
+There is hope that `udev` itself will adopt the Fedora approach (which is to check for HID usage page `F1D0`, and avoids manually whitelisting each U2F/FIDO2 key): .
-```
-sudo udevadm control --reload-rules && sudo udevadm trigger
-```
+Further progress is tracked in: .
-A simple way to setup both the udev rule and the udevadm reload is:
+If you still need to setup a rule, a simple way to do it is:
```
git clone git@github.com:solokeys/solo.git
@@ -22,9 +23,11 @@ cd solo/udev
make setup
```
-We are working on getting user access to Solo keys enabled automatically in common Linux distributions: .
-
-
+Or, manually, create a file like [`70-solokeys-access.rules`](https://github.com/solokeys/solo/blob/master/udev/70-solokeys-access.rules) in your `/etc/udev/rules.d` directory.
+Additionally, run the following command after you create this file (it is not necessary to do this again in the future):
+```
+sudo udevadm control --reload-rules && sudo udevadm trigger
+```
# How do udev rules work and why are they needed
diff --git a/fido2/apdu.c b/fido2/apdu.c
new file mode 100644
index 0000000..df89099
--- /dev/null
+++ b/fido2/apdu.c
@@ -0,0 +1,122 @@
+// Copyright 2019 SoloKeys Developers
+//
+// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+// iso7816:2013. 5.3.2 Decoding conventions for command bodies
+
+#include "apdu.h"
+
+int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu)
+{
+ EXT_APDU_HEADER *hapdu = (EXT_APDU_HEADER *)data;
+
+ apdu->cla = hapdu->cla;
+ apdu->ins = hapdu->ins;
+ apdu->p1 = hapdu->p1;
+ apdu->p2 = hapdu->p2;
+
+ apdu->lc = 0;
+ apdu->data = NULL;
+ apdu->le = 0;
+ apdu->extended_apdu = false;
+ apdu->case_type = 0x00;
+
+ uint8_t b0 = hapdu->lc[0];
+
+ // case 1
+ if (len == 4)
+ {
+ apdu->case_type = 0x01;
+ }
+
+ // case 2S (Le)
+ if (len == 5)
+ {
+ apdu->case_type = 0x02;
+ apdu->le = b0;
+ if (!apdu->le)
+ apdu->le = 0x100;
+ }
+
+ // case 3S (Lc + data)
+ if (len == 5U + b0 && b0 != 0)
+ {
+ apdu->case_type = 0x03;
+ apdu->lc = b0;
+ }
+
+ // case 4S (Lc + data + Le)
+ if (len == 5U + b0 + 1U && b0 != 0)
+ {
+ apdu->case_type = 0x04;
+ apdu->lc = b0;
+ apdu->le = data[len - 1];
+ if (!apdu->le)
+ apdu->le = 0x100;
+ }
+
+ // extended length apdu
+ if (len >= 7 && b0 == 0)
+ {
+ uint16_t extlen = (hapdu->lc[1] << 8) + hapdu->lc[2];
+
+ // case 2E (Le) - extended
+ if (len == 7)
+ {
+ apdu->case_type = 0x12;
+ apdu->extended_apdu = true;
+ apdu->le = extlen;
+ if (!apdu->le)
+ apdu->le = 0x10000;
+ }
+
+ // case 3E (Lc + data) - extended
+ if (len == 7U + extlen)
+ {
+ apdu->case_type = 0x13;
+ apdu->extended_apdu = true;
+ apdu->lc = extlen;
+ }
+
+ // case 4E (Lc + data + Le) - extended 2-byte Le
+ if (len == 7U + extlen + 2U)
+ {
+ apdu->case_type = 0x14;
+ apdu->extended_apdu = true;
+ apdu->lc = extlen;
+ apdu->le = (data[len - 2] << 8) + data[len - 1];
+ if (!apdu->le)
+ apdu->le = 0x10000;
+ }
+
+ // case 4E (Lc + data + Le) - extended 3-byte Le
+ if (len == 7U + extlen + 3U && data[len - 3] == 0)
+ {
+ apdu->case_type = 0x24;
+ apdu->extended_apdu = true;
+ apdu->lc = extlen;
+ apdu->le = (data[len - 2] << 8) + data[len - 1];
+ if (!apdu->le)
+ apdu->le = 0x10000;
+ }
+ }
+
+ if (!apdu->case_type)
+ return 1;
+
+ if (apdu->lc)
+ {
+ if (apdu->extended_apdu)
+ {
+ apdu->data = data + 7;
+ } else {
+ apdu->data = data + 5;
+ }
+
+ }
+
+ return 0;
+}
diff --git a/fido2/apdu.h b/fido2/apdu.h
index d9687c7..7ce8477 100644
--- a/fido2/apdu.h
+++ b/fido2/apdu.h
@@ -2,6 +2,8 @@
#define _APDU_H_
#include
+#include
+#include
typedef struct
{
@@ -12,19 +14,46 @@ typedef struct
uint8_t lc;
} __attribute__((packed)) APDU_HEADER;
+typedef struct
+{
+ uint8_t cla;
+ uint8_t ins;
+ uint8_t p1;
+ uint8_t p2;
+ uint8_t lc[3];
+} __attribute__((packed)) EXT_APDU_HEADER;
+
+typedef struct
+{
+ uint8_t cla;
+ uint8_t ins;
+ uint8_t p1;
+ uint8_t p2;
+ uint16_t lc;
+ uint8_t *data;
+ uint32_t le;
+ bool extended_apdu;
+ uint8_t case_type;
+} __attribute__((packed)) APDU_STRUCT;
+
+extern int apdu_decode(uint8_t *data, size_t len, APDU_STRUCT *apdu);
+
#define APDU_FIDO_U2F_REGISTER 0x01
#define APDU_FIDO_U2F_AUTHENTICATE 0x02
#define APDU_FIDO_U2F_VERSION 0x03
#define APDU_FIDO_NFCCTAP_MSG 0x10
#define APDU_INS_SELECT 0xA4
#define APDU_INS_READ_BINARY 0xB0
+#define APDU_GET_RESPONSE 0xC0
#define SW_SUCCESS 0x9000
#define SW_GET_RESPONSE 0x6100 // Command successfully executed; 'XX' bytes of data are available and can be requested using GET RESPONSE.
#define SW_WRONG_LENGTH 0x6700
#define SW_COND_USE_NOT_SATISFIED 0x6985
#define SW_FILE_NOT_FOUND 0x6a82
+#define SW_INCORRECT_P1P2 0x6a86
#define SW_INS_INVALID 0x6d00 // Instruction code not supported or invalid
+#define SW_CLA_INVALID 0x6e00
#define SW_INTERNAL_EXCEPTION 0x6f00
#endif //_APDU_H_
diff --git a/fido2/crypto.c b/fido2/crypto.c
index 63520c1..6aea29f 100644
--- a/fido2/crypto.c
+++ b/fido2/crypto.c
@@ -262,6 +262,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
memmove(y,pubkey+32,32);
}
+void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
+{
+ uECC_compute_public_key(privkey, pubkey, _es256_curve);
+}
+
void crypto_load_external_key(uint8_t * key, int len)
{
_signing_key = key;
diff --git a/fido2/crypto.h b/fido2/crypto.h
index 4fc54a2..6b67b02 100644
--- a/fido2/crypto.h
+++ b/fido2/crypto.h
@@ -26,6 +26,7 @@ void crypto_sha512_final(uint8_t * hash);
void crypto_ecc256_init();
void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8_t * y);
+void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey);
void crypto_ecc256_load_key(uint8_t * data, int len, uint8_t * data2, int len2);
void crypto_ecc256_load_attestation_key();
@@ -38,6 +39,7 @@ void generate_private_key(uint8_t * data, int len, uint8_t * data2, int len2, ui
void crypto_ecc256_make_key_pair(uint8_t * pubkey, uint8_t * privkey);
void crypto_ecc256_shared_secret(const uint8_t * pubkey, const uint8_t * privkey, uint8_t * shared_secret);
+#define CRYPTO_TRANSPORT_KEY2 ((uint8_t*)2)
#define CRYPTO_TRANSPORT_KEY ((uint8_t*)1)
#define CRYPTO_MASTER_KEY ((uint8_t*)0)
diff --git a/fido2/ctap.c b/fido2/ctap.c
index d46a664..8edf591 100644
--- a/fido2/ctap.c
+++ b/fido2/ctap.c
@@ -256,7 +256,9 @@ static int ctap_generate_cose_key(CborEncoder * cose_key, uint8_t * hmac_input,
switch(algtype)
{
case COSE_ALG_ES256:
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
crypto_ecc256_derive_public_key(hmac_input, len, x, y);
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
break;
default:
printf2(TAG_ERR,"Error, COSE alg %d not supported\n", algtype);
@@ -355,9 +357,9 @@ static int ctap_make_extensions(CTAP_extensions * ext, uint8_t * ext_encoder_buf
}
// Generate credRandom
- crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY, 0, credRandom);
+ crypto_sha256_hmac_init(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
crypto_sha256_update((uint8_t*)&ext->hmac_secret.credential->id, sizeof(CredentialId));
- crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY, 0, credRandom);
+ crypto_sha256_hmac_final(CRYPTO_TRANSPORT_KEY2, 0, credRandom);
// Decrypt saltEnc
crypto_aes256_init(shared_secret, NULL);
@@ -432,6 +434,12 @@ static unsigned int get_credential_id_size(CTAP_credentialDescriptor * cred)
return sizeof(CredentialId);
}
+static int ctap2_user_presence_test()
+{
+ device_set_status(CTAPHID_STATUS_UPNEEDED);
+ return ctap_user_presence_test(CTAP2_UP_DELAY_MS);
+}
+
static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * auth_data_buf, uint32_t * len, CTAP_credInfo * credInfo)
{
CborEncoder cose_key;
@@ -459,11 +467,9 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
count = auth_data_update_count(&authData->head);
- device_set_status(CTAPHID_STATUS_UPNEEDED);
-
int but;
- but = ctap_user_presence_test(CTAP2_UP_DELAY_MS);
+ but = ctap2_user_presence_test(CTAP2_UP_DELAY_MS);
if (!but)
{
@@ -473,6 +479,7 @@ static int ctap_make_auth_data(struct rpId * rp, CborEncoder * map, uint8_t * au
{
return CTAP2_ERR_KEEPALIVE_CANCEL;
}
+
device_set_status(CTAPHID_STATUS_PROCESSING);
authData->head.flags = (but << 0);
@@ -605,7 +612,6 @@ int ctap_calculate_signature(uint8_t * data, int datalen, uint8_t * clientDataHa
crypto_sha256_final(hashbuf);
crypto_ecc256_sign(hashbuf, 32, sigbuf);
-
return ctap_encode_der_sig(sigbuf,sigder);
}
@@ -701,11 +707,11 @@ uint8_t ctap_make_credential(CborEncoder * encoder, uint8_t * request, int lengt
}
if (MC.pinAuthEmpty)
{
- if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS))
+ if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
{
return CTAP2_ERR_OPERATION_DENIED;
}
- return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
+ return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if ((MC.paramsParsed & MC_requiredMask) != MC_requiredMask)
{
@@ -1056,7 +1062,7 @@ uint8_t ctap_end_get_assertion(CborEncoder * map, CTAP_credentialDescriptor * cr
else
#endif
{
- sigder_sz = ctap_calculate_signature(auth_data_buf, sizeof(CTAP_authDataHeader), clientDataHash, auth_data_buf, sigbuf, sigder);
+ sigder_sz = ctap_calculate_signature(auth_data_buf, auth_data_buf_sz, clientDataHash, auth_data_buf, sigbuf, sigder);
}
{
@@ -1137,11 +1143,11 @@ uint8_t ctap_get_assertion(CborEncoder * encoder, uint8_t * request, int length)
if (GA.pinAuthEmpty)
{
- if (!ctap_user_presence_test(CTAP2_UP_DELAY_MS))
+ if (!ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
{
return CTAP2_ERR_OPERATION_DENIED;
}
- return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_INVALID : CTAP2_ERR_PIN_NOT_SET;
+ return ctap_is_pin_set() == 1 ? CTAP2_ERR_PIN_AUTH_INVALID : CTAP2_ERR_PIN_NOT_SET;
}
if (GA.pinAuthPresent)
{
@@ -1475,6 +1481,11 @@ uint8_t ctap_client_pin(CborEncoder * encoder, uint8_t * request, int length)
ret = cbor_encode_int(&map, RESP_keyAgreement);
check_ret(ret);
+
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
+ crypto_ecc256_compute_public_key(KEY_AGREEMENT_PRIV, KEY_AGREEMENT_PUB);
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
+
ret = ctap_add_cose_key(&map, KEY_AGREEMENT_PUB, KEY_AGREEMENT_PUB+32, PUB_KEY_CRED_PUB_KEY, COSE_ALG_ECDH_ES_HKDF_256);
check_retr(ret);
@@ -1604,7 +1615,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
switch(cmd)
{
case CTAP_MAKE_CREDENTIAL:
- device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_MAKE_CREDENTIAL\n");
timestamp();
status = ctap_make_credential(&encoder, pkt_raw, length);
@@ -1615,7 +1625,6 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_GET_ASSERTION:
- device_set_status(CTAPHID_STATUS_PROCESSING);
printf1(TAG_CTAP,"CTAP_GET_ASSERTION\n");
timestamp();
status = ctap_get_assertion(&encoder, pkt_raw, length);
@@ -1647,7 +1656,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
case CTAP_RESET:
printf1(TAG_CTAP,"CTAP_RESET\n");
- if (ctap_user_presence_test(CTAP2_UP_DELAY_MS))
+ if (ctap2_user_presence_test(CTAP2_UP_DELAY_MS))
{
ctap_reset();
}
@@ -1676,7 +1685,7 @@ uint8_t ctap_request(uint8_t * pkt_raw, int length, CTAP_RESPONSE * resp)
break;
default:
status = CTAP1_ERR_INVALID_COMMAND;
- printf2(TAG_ERR,"error, invalid cmd\n");
+ printf2(TAG_ERR,"error, invalid cmd: %x\n", cmd);
}
done:
@@ -1765,10 +1774,7 @@ void ctap_init()
exit(1);
}
- if (device_is_nfc() != NFC_IS_ACTIVE)
- {
- ctap_reset_key_agreement();
- }
+ ctap_reset_key_agreement();
#ifdef BRIDGE_TO_WALLET
wallet_init();
@@ -1967,7 +1973,7 @@ int8_t ctap_load_key(uint8_t index, uint8_t * key)
static void ctap_reset_key_agreement()
{
- crypto_ecc256_make_key_pair(KEY_AGREEMENT_PUB, KEY_AGREEMENT_PRIV);
+ ctap_generate_rng(KEY_AGREEMENT_PRIV, sizeof(KEY_AGREEMENT_PRIV));
}
void ctap_reset()
@@ -1987,5 +1993,5 @@ void ctap_reset()
memset(PIN_CODE_HASH,0,sizeof(PIN_CODE_HASH));
ctap_reset_key_agreement();
- crypto_reset_master_secret();
+ crypto_load_master_secret(STATE.key_space);
}
diff --git a/fido2/device.h b/fido2/device.h
index dfb95ec..0c11fe8 100644
--- a/fido2/device.h
+++ b/fido2/device.h
@@ -105,6 +105,8 @@ void device_set_clock_rate(DEVICE_CLOCK_RATE param);
#define NFC_IS_AVAILABLE 2
int device_is_nfc();
+void request_from_nfc(bool request_active);
+
void device_init_button();
#endif
diff --git a/fido2/u2f.c b/fido2/u2f.c
index 14cb848..7999ab2 100644
--- a/fido2/u2f.c
+++ b/fido2/u2f.c
@@ -113,14 +113,14 @@ end:
printf1(TAG_U2F,"u2f resp: "); dump_hex1(TAG_U2F, _u2f_resp->data, _u2f_resp->length);
}
-void u2f_request_nfc(uint8_t * req, int len, CTAP_RESPONSE * resp)
+void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp)
{
- if (len < 5 || !req)
+ if (!header)
return;
- uint32_t alen = req[4];
-
- u2f_request_ex((APDU_HEADER *)req, &req[5], alen, resp);
+ request_from_nfc(true); // disable presence test
+ u2f_request_ex((APDU_HEADER *)header, data, datalen, resp);
+ request_from_nfc(false); // enable presence test
}
void u2f_request(struct u2f_request_apdu* req, CTAP_RESPONSE * resp)
diff --git a/fido2/u2f.h b/fido2/u2f.h
index 9055b36..ad73cc9 100644
--- a/fido2/u2f.h
+++ b/fido2/u2f.h
@@ -101,7 +101,7 @@ 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);
+void u2f_request_nfc(uint8_t * header, uint8_t * data, int datalen, CTAP_RESPONSE * resp);
int8_t u2f_authenticate_credential(struct u2f_key_handle * kh, uint8_t * appid);
diff --git a/in-docker-build.sh b/in-docker-build.sh
index 631d359..ece565c 100755
--- a/in-docker-build.sh
+++ b/in-docker-build.sh
@@ -38,6 +38,7 @@ build firmware hacker solo
build firmware hacker-debug-1 solo
build firmware hacker-debug-2 solo
build firmware secure solo
+build firmware secure-non-solokeys solo
pip install -U pip
pip install -U solo-python
@@ -49,3 +50,6 @@ 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
+bundle="bundle-secure-non-solokeys-${version}"
+/opt/conda/bin/solo mergehex bootloader-verifying-${version}.hex firmware-secure-non-solokeys-${version}.hex ${bundle}.hex
+sha256sum ${bundle}.hex > ${bundle}.sha2
diff --git a/mkdocs.yml b/mkdocs.yml
index b140e92..1b45b2f 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -11,6 +11,11 @@ nav:
- FIDO2 Implementation: solo/fido2-impl.md
- Metadata Statements: solo/metadata-statements.md
- Build instructions: solo/building.md
+ - Programming instructions: solo/programming.md
+ - Bootloader mode: solo/bootloader-mode.md
+ - Customization: solo/customization.md
+ - Solo Extras: solo/solo-extras.md
+ - Running on Nucleo32 board: solo/nucleo32-board.md
- Signed update process: solo/signed-updates.md
- Code documentation: solo/code-overview.md
- Contributing Code: solo/contributing.md
diff --git a/pc/device.c b/pc/device.c
index 2a0e166..0944254 100644
--- a/pc/device.c
+++ b/pc/device.c
@@ -628,3 +628,8 @@ int device_is_nfc()
{
return 0;
}
+
+void device_set_clock_rate(DEVICE_CLOCK_RATE param)
+{
+
+}
diff --git a/targets/stm32l432/Makefile b/targets/stm32l432/Makefile
index f325b57..e7db178 100644
--- a/targets/stm32l432/Makefile
+++ b/targets/stm32l432/Makefile
@@ -21,6 +21,9 @@ firmware-hacker-debug-1:
firmware-hacker-debug-2:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=2 EXTRA_DEFINES='-DSOLO_HACKER -DFLASH_ROP=0'
+firmware-secure-non-solokeys:
+ $(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DFLASH_ROP=2'
+
firmware-secure:
$(MAKE) -f $(APPMAKE) -j8 solo.hex PREFIX=$(PREFIX) DEBUG=0 EXTRA_DEFINES='-DUSE_SOLOKEYS_CERT -DFLASH_ROP=2'
@@ -59,7 +62,6 @@ boot-no-sig:
build-release-locked: cbor clean2 boot-sig-checking clean all-locked
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
- rm -f solo.hex bootloader.hex # don't program solo.hex ...
build-release: cbor clean2 boot-sig-checking clean all
$(VENV) $(merge_hex) solo.hex bootloader.hex all.hex
diff --git a/targets/stm32l432/build/application.mk b/targets/stm32l432/build/application.mk
index e482f87..848887f 100644
--- a/targets/stm32l432/build/application.mk
+++ b/targets/stm32l432/build/application.mk
@@ -7,7 +7,7 @@ SRC += src/startup_stm32l432xx.s src/system_stm32l4xx.c
SRC += $(DRIVER_LIBS) $(USB_LIB)
# FIDO2 lib
-SRC += ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
+SRC += ../../fido2/apdu.c ../../fido2/util.c ../../fido2/u2f.c ../../fido2/test_power.c
SRC += ../../fido2/stubs.c ../../fido2/log.c ../../fido2/ctaphid.c ../../fido2/ctap.c
SRC += ../../fido2/ctap_parse.c ../../fido2/main.c
SRC += ../../fido2/extensions/extensions.c ../../fido2/extensions/solo.c
diff --git a/targets/stm32l432/build/common.mk b/targets/stm32l432/build/common.mk
index f018bce..c6901d7 100644
--- a/targets/stm32l432/build/common.mk
+++ b/targets/stm32l432/build/common.mk
@@ -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 lib/stm32l4xx_ll_spi.c lib/stm32l4xx_ll_exti.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 \
diff --git a/targets/stm32l432/lib/stm32l4xx_ll_exti.c b/targets/stm32l432/lib/stm32l4xx_ll_exti.c
new file mode 100644
index 0000000..5c52247
--- /dev/null
+++ b/targets/stm32l432/lib/stm32l4xx_ll_exti.c
@@ -0,0 +1,290 @@
+/**
+ ******************************************************************************
+ * @file stm32l4xx_ll_exti.c
+ * @author MCD Application Team
+ * @brief EXTI LL module driver.
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2017 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+#if defined(USE_FULL_LL_DRIVER)
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32l4xx_ll_exti.h"
+#ifdef USE_FULL_ASSERT
+#include "stm32_assert.h"
+#else
+#define assert_param(expr) ((void)0U)
+#endif
+
+/** @addtogroup STM32L4xx_LL_Driver
+ * @{
+ */
+
+#if defined (EXTI)
+
+/** @defgroup EXTI_LL EXTI
+ * @{
+ */
+
+/* Private types -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private constants ---------------------------------------------------------*/
+/* Private macros ------------------------------------------------------------*/
+/** @addtogroup EXTI_LL_Private_Macros
+ * @{
+ */
+
+#define IS_LL_EXTI_LINE_0_31(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_0_31) == 0x00000000U)
+#define IS_LL_EXTI_LINE_32_63(__VALUE__) (((__VALUE__) & ~LL_EXTI_LINE_ALL_32_63) == 0x00000000U)
+
+#define IS_LL_EXTI_MODE(__VALUE__) (((__VALUE__) == LL_EXTI_MODE_IT) \
+ || ((__VALUE__) == LL_EXTI_MODE_EVENT) \
+ || ((__VALUE__) == LL_EXTI_MODE_IT_EVENT))
+
+
+#define IS_LL_EXTI_TRIGGER(__VALUE__) (((__VALUE__) == LL_EXTI_TRIGGER_NONE) \
+ || ((__VALUE__) == LL_EXTI_TRIGGER_RISING) \
+ || ((__VALUE__) == LL_EXTI_TRIGGER_FALLING) \
+ || ((__VALUE__) == LL_EXTI_TRIGGER_RISING_FALLING))
+
+/**
+ * @}
+ */
+
+/* Private function prototypes -----------------------------------------------*/
+
+/* Exported functions --------------------------------------------------------*/
+/** @addtogroup EXTI_LL_Exported_Functions
+ * @{
+ */
+
+/** @addtogroup EXTI_LL_EF_Init
+ * @{
+ */
+
+/**
+ * @brief De-initialize the EXTI registers to their default reset values.
+ * @retval An ErrorStatus enumeration value:
+ * - 0x00: EXTI registers are de-initialized
+ */
+uint32_t LL_EXTI_DeInit(void)
+{
+ /* Interrupt mask register set to default reset values */
+ LL_EXTI_WriteReg(IMR1, 0xFF820000U);
+ /* Event mask register set to default reset values */
+ LL_EXTI_WriteReg(EMR1, 0x00000000U);
+ /* Rising Trigger selection register set to default reset values */
+ LL_EXTI_WriteReg(RTSR1, 0x00000000U);
+ /* Falling Trigger selection register set to default reset values */
+ LL_EXTI_WriteReg(FTSR1, 0x00000000U);
+ /* Software interrupt event register set to default reset values */
+ LL_EXTI_WriteReg(SWIER1, 0x00000000U);
+ /* Pending register clear */
+ LL_EXTI_WriteReg(PR1, 0x007DFFFFU);
+
+ /* Interrupt mask register 2 set to default reset values */
+#if defined(LL_EXTI_LINE_40)
+ LL_EXTI_WriteReg(IMR2, 0x00000187U);
+#else
+ LL_EXTI_WriteReg(IMR2, 0x00000087U);
+#endif
+ /* Event mask register 2 set to default reset values */
+ LL_EXTI_WriteReg(EMR2, 0x00000000U);
+ /* Rising Trigger selection register 2 set to default reset values */
+ LL_EXTI_WriteReg(RTSR2, 0x00000000U);
+ /* Falling Trigger selection register 2 set to default reset values */
+ LL_EXTI_WriteReg(FTSR2, 0x00000000U);
+ /* Software interrupt event register 2 set to default reset values */
+ LL_EXTI_WriteReg(SWIER2, 0x00000000U);
+ /* Pending register 2 clear */
+ LL_EXTI_WriteReg(PR2, 0x00000078U);
+
+ return 0x00u;
+}
+
+/**
+ * @brief Initialize the EXTI registers according to the specified parameters in EXTI_InitStruct.
+ * @param EXTI_InitStruct pointer to a @ref LL_EXTI_InitTypeDef structure.
+ * @retval An ErrorStatus enumeration value:
+ * - 0x00: EXTI registers are initialized
+ * - any other calue : wrong configuration
+ */
+uint32_t LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct)
+{
+ uint32_t status = 0x00u;
+
+ /* Check the parameters */
+ assert_param(IS_LL_EXTI_LINE_0_31(EXTI_InitStruct->Line_0_31));
+ assert_param(IS_LL_EXTI_LINE_32_63(EXTI_InitStruct->Line_32_63));
+ assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->LineCommand));
+ assert_param(IS_LL_EXTI_MODE(EXTI_InitStruct->Mode));
+
+ /* ENABLE LineCommand */
+ if (EXTI_InitStruct->LineCommand != DISABLE)
+ {
+ assert_param(IS_LL_EXTI_TRIGGER(EXTI_InitStruct->Trigger));
+
+ /* Configure EXTI Lines in range from 0 to 31 */
+ if (EXTI_InitStruct->Line_0_31 != LL_EXTI_LINE_NONE)
+ {
+ switch (EXTI_InitStruct->Mode)
+ {
+ case LL_EXTI_MODE_IT:
+ /* First Disable Event on provided Lines */
+ LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
+ /* Then Enable IT on provided Lines */
+ LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
+ break;
+ case LL_EXTI_MODE_EVENT:
+ /* First Disable IT on provided Lines */
+ LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
+ /* Then Enable Event on provided Lines */
+ LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
+ break;
+ case LL_EXTI_MODE_IT_EVENT:
+ /* Directly Enable IT & Event on provided Lines */
+ LL_EXTI_EnableIT_0_31(EXTI_InitStruct->Line_0_31);
+ LL_EXTI_EnableEvent_0_31(EXTI_InitStruct->Line_0_31);
+ break;
+ default:
+ status = 0x01u;
+ break;
+ }
+ if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
+ {
+ switch (EXTI_InitStruct->Trigger)
+ {
+ case LL_EXTI_TRIGGER_RISING:
+ /* First Disable Falling Trigger on provided Lines */
+ LL_EXTI_DisableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
+ /* Then Enable Rising Trigger on provided Lines */
+ LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
+ break;
+ case LL_EXTI_TRIGGER_FALLING:
+ /* First Disable Rising Trigger on provided Lines */
+ LL_EXTI_DisableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
+ /* Then Enable Falling Trigger on provided Lines */
+ LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
+ break;
+ case LL_EXTI_TRIGGER_RISING_FALLING:
+ LL_EXTI_EnableRisingTrig_0_31(EXTI_InitStruct->Line_0_31);
+ LL_EXTI_EnableFallingTrig_0_31(EXTI_InitStruct->Line_0_31);
+ break;
+ default:
+ status |= 0x02u;
+ break;
+ }
+ }
+ }
+ /* Configure EXTI Lines in range from 32 to 63 */
+ if (EXTI_InitStruct->Line_32_63 != LL_EXTI_LINE_NONE)
+ {
+ switch (EXTI_InitStruct->Mode)
+ {
+ case LL_EXTI_MODE_IT:
+ /* First Disable Event on provided Lines */
+ LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
+ /* Then Enable IT on provided Lines */
+ LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
+ break;
+ case LL_EXTI_MODE_EVENT:
+ /* First Disable IT on provided Lines */
+ LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
+ /* Then Enable Event on provided Lines */
+ LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
+ break;
+ case LL_EXTI_MODE_IT_EVENT:
+ /* Directly Enable IT & Event on provided Lines */
+ LL_EXTI_EnableIT_32_63(EXTI_InitStruct->Line_32_63);
+ LL_EXTI_EnableEvent_32_63(EXTI_InitStruct->Line_32_63);
+ break;
+ default:
+ status |= 0x04u;
+ break;
+ }
+ if (EXTI_InitStruct->Trigger != LL_EXTI_TRIGGER_NONE)
+ {
+ switch (EXTI_InitStruct->Trigger)
+ {
+ case LL_EXTI_TRIGGER_RISING:
+ /* First Disable Falling Trigger on provided Lines */
+ LL_EXTI_DisableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
+ /* Then Enable IT on provided Lines */
+ LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
+ break;
+ case LL_EXTI_TRIGGER_FALLING:
+ /* First Disable Rising Trigger on provided Lines */
+ LL_EXTI_DisableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
+ /* Then Enable Falling Trigger on provided Lines */
+ LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
+ break;
+ case LL_EXTI_TRIGGER_RISING_FALLING:
+ LL_EXTI_EnableRisingTrig_32_63(EXTI_InitStruct->Line_32_63);
+ LL_EXTI_EnableFallingTrig_32_63(EXTI_InitStruct->Line_32_63);
+ break;
+ default:
+ status = ERROR;
+ break;
+ }
+ }
+ }
+ }
+ /* DISABLE LineCommand */
+ else
+ {
+ /* De-configure EXTI Lines in range from 0 to 31 */
+ LL_EXTI_DisableIT_0_31(EXTI_InitStruct->Line_0_31);
+ LL_EXTI_DisableEvent_0_31(EXTI_InitStruct->Line_0_31);
+ /* De-configure EXTI Lines in range from 32 to 63 */
+ LL_EXTI_DisableIT_32_63(EXTI_InitStruct->Line_32_63);
+ LL_EXTI_DisableEvent_32_63(EXTI_InitStruct->Line_32_63);
+ }
+
+ return status;
+}
+
+/**
+ * @brief Set each @ref LL_EXTI_InitTypeDef field to default value.
+ * @param EXTI_InitStruct Pointer to a @ref LL_EXTI_InitTypeDef structure.
+ * @retval None
+ */
+void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct)
+{
+ EXTI_InitStruct->Line_0_31 = LL_EXTI_LINE_NONE;
+ EXTI_InitStruct->Line_32_63 = LL_EXTI_LINE_NONE;
+ EXTI_InitStruct->LineCommand = DISABLE;
+ EXTI_InitStruct->Mode = LL_EXTI_MODE_IT;
+ EXTI_InitStruct->Trigger = LL_EXTI_TRIGGER_FALLING;
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* defined (EXTI) */
+
+/**
+ * @}
+ */
+
+#endif /* USE_FULL_LL_DRIVER */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/targets/stm32l432/lib/stm32l4xx_ll_exti.h b/targets/stm32l432/lib/stm32l4xx_ll_exti.h
new file mode 100644
index 0000000..245f7fd
--- /dev/null
+++ b/targets/stm32l432/lib/stm32l4xx_ll_exti.h
@@ -0,0 +1,1361 @@
+/**
+ ******************************************************************************
+ * @file stm32l4xx_ll_exti.h
+ * @author MCD Application Team
+ * @brief Header file of EXTI LL module.
+ ******************************************************************************
+ * @attention
+ *
+ * © Copyright (c) 2017 STMicroelectronics.
+ * All rights reserved.
+ *
+ * This software component is licensed by ST under BSD 3-Clause license,
+ * the "License"; You may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at:
+ * opensource.org/licenses/BSD-3-Clause
+ *
+ ******************************************************************************
+ */
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __STM32L4xx_LL_EXTI_H
+#define __STM32L4xx_LL_EXTI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32l4xx.h"
+
+/** @addtogroup STM32L4xx_LL_Driver
+ * @{
+ */
+
+#if defined (EXTI)
+
+/** @defgroup EXTI_LL EXTI
+ * @{
+ */
+
+/* Private types -------------------------------------------------------------*/
+/* Private variables ---------------------------------------------------------*/
+/* Private constants ---------------------------------------------------------*/
+/* Private Macros ------------------------------------------------------------*/
+#if defined(USE_FULL_LL_DRIVER)
+/** @defgroup EXTI_LL_Private_Macros EXTI Private Macros
+ * @{
+ */
+/**
+ * @}
+ */
+#endif /*USE_FULL_LL_DRIVER*/
+/* Exported types ------------------------------------------------------------*/
+#if defined(USE_FULL_LL_DRIVER)
+/** @defgroup EXTI_LL_ES_INIT EXTI Exported Init structure
+ * @{
+ */
+typedef struct
+{
+
+ uint32_t Line_0_31; /*!< Specifies the EXTI lines to be enabled or disabled for Lines in range 0 to 31
+ This parameter can be any combination of @ref EXTI_LL_EC_LINE */
+
+ uint32_t Line_32_63; /*!< Specifies the EXTI lines to be enabled or disabled for Lines in range 32 to 63
+ This parameter can be any combination of @ref EXTI_LL_EC_LINE */
+
+ FunctionalState LineCommand; /*!< Specifies the new state of the selected EXTI lines.
+ This parameter can be set either to ENABLE or DISABLE */
+
+ uint8_t Mode; /*!< Specifies the mode for the EXTI lines.
+ This parameter can be a value of @ref EXTI_LL_EC_MODE. */
+
+ uint8_t Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
+ This parameter can be a value of @ref EXTI_LL_EC_TRIGGER. */
+} LL_EXTI_InitTypeDef;
+
+/**
+ * @}
+ */
+#endif /*USE_FULL_LL_DRIVER*/
+
+/* Exported constants --------------------------------------------------------*/
+/** @defgroup EXTI_LL_Exported_Constants EXTI Exported Constants
+ * @{
+ */
+
+/** @defgroup EXTI_LL_EC_LINE LINE
+ * @{
+ */
+#define LL_EXTI_LINE_0 EXTI_IMR1_IM0 /*!< Extended line 0 */
+#define LL_EXTI_LINE_1 EXTI_IMR1_IM1 /*!< Extended line 1 */
+#define LL_EXTI_LINE_2 EXTI_IMR1_IM2 /*!< Extended line 2 */
+#define LL_EXTI_LINE_3 EXTI_IMR1_IM3 /*!< Extended line 3 */
+#define LL_EXTI_LINE_4 EXTI_IMR1_IM4 /*!< Extended line 4 */
+#define LL_EXTI_LINE_5 EXTI_IMR1_IM5 /*!< Extended line 5 */
+#define LL_EXTI_LINE_6 EXTI_IMR1_IM6 /*!< Extended line 6 */
+#define LL_EXTI_LINE_7 EXTI_IMR1_IM7 /*!< Extended line 7 */
+#define LL_EXTI_LINE_8 EXTI_IMR1_IM8 /*!< Extended line 8 */
+#define LL_EXTI_LINE_9 EXTI_IMR1_IM9 /*!< Extended line 9 */
+#define LL_EXTI_LINE_10 EXTI_IMR1_IM10 /*!< Extended line 10 */
+#define LL_EXTI_LINE_11 EXTI_IMR1_IM11 /*!< Extended line 11 */
+#define LL_EXTI_LINE_12 EXTI_IMR1_IM12 /*!< Extended line 12 */
+#define LL_EXTI_LINE_13 EXTI_IMR1_IM13 /*!< Extended line 13 */
+#define LL_EXTI_LINE_14 EXTI_IMR1_IM14 /*!< Extended line 14 */
+#define LL_EXTI_LINE_15 EXTI_IMR1_IM15 /*!< Extended line 15 */
+#if defined(EXTI_IMR1_IM16)
+#define LL_EXTI_LINE_16 EXTI_IMR1_IM16 /*!< Extended line 16 */
+#endif
+#define LL_EXTI_LINE_17 EXTI_IMR1_IM17 /*!< Extended line 17 */
+#if defined(EXTI_IMR1_IM18)
+#define LL_EXTI_LINE_18 EXTI_IMR1_IM18 /*!< Extended line 18 */
+#endif
+#define LL_EXTI_LINE_19 EXTI_IMR1_IM19 /*!< Extended line 19 */
+#if defined(EXTI_IMR1_IM20)
+#define LL_EXTI_LINE_20 EXTI_IMR1_IM20 /*!< Extended line 20 */
+#endif
+#if defined(EXTI_IMR1_IM21)
+#define LL_EXTI_LINE_21 EXTI_IMR1_IM21 /*!< Extended line 21 */
+#endif
+#if defined(EXTI_IMR1_IM22)
+#define LL_EXTI_LINE_22 EXTI_IMR1_IM22 /*!< Extended line 22 */
+#endif
+#define LL_EXTI_LINE_23 EXTI_IMR1_IM23 /*!< Extended line 23 */
+#if defined(EXTI_IMR1_IM24)
+#define LL_EXTI_LINE_24 EXTI_IMR1_IM24 /*!< Extended line 24 */
+#endif
+#if defined(EXTI_IMR1_IM25)
+#define LL_EXTI_LINE_25 EXTI_IMR1_IM25 /*!< Extended line 25 */
+#endif
+#if defined(EXTI_IMR1_IM26)
+#define LL_EXTI_LINE_26 EXTI_IMR1_IM26 /*!< Extended line 26 */
+#endif
+#if defined(EXTI_IMR1_IM27)
+#define LL_EXTI_LINE_27 EXTI_IMR1_IM27 /*!< Extended line 27 */
+#endif
+#if defined(EXTI_IMR1_IM28)
+#define LL_EXTI_LINE_28 EXTI_IMR1_IM28 /*!< Extended line 28 */
+#endif
+#if defined(EXTI_IMR1_IM29)
+#define LL_EXTI_LINE_29 EXTI_IMR1_IM29 /*!< Extended line 29 */
+#endif
+#if defined(EXTI_IMR1_IM30)
+#define LL_EXTI_LINE_30 EXTI_IMR1_IM30 /*!< Extended line 30 */
+#endif
+#if defined(EXTI_IMR1_IM31)
+#define LL_EXTI_LINE_31 EXTI_IMR1_IM31 /*!< Extended line 31 */
+#endif
+#define LL_EXTI_LINE_ALL_0_31 EXTI_IMR1_IM /*!< All Extended line not reserved*/
+
+#define LL_EXTI_LINE_32 EXTI_IMR2_IM32 /*!< Extended line 32 */
+#if defined(EXTI_IMR2_IM33)
+#define LL_EXTI_LINE_33 EXTI_IMR2_IM33 /*!< Extended line 33 */
+#endif
+#if defined(EXTI_IMR2_IM34)
+#define LL_EXTI_LINE_34 EXTI_IMR2_IM34 /*!< Extended line 34 */
+#endif
+#if defined(EXTI_IMR2_IM35)
+#define LL_EXTI_LINE_35 EXTI_IMR2_IM35 /*!< Extended line 35 */
+#endif
+#if defined(EXTI_IMR2_IM36)
+#define LL_EXTI_LINE_36 EXTI_IMR2_IM36 /*!< Extended line 36 */
+#endif
+#if defined(EXTI_IMR2_IM37)
+#define LL_EXTI_LINE_37 EXTI_IMR2_IM37 /*!< Extended line 37 */
+#endif
+#if defined(EXTI_IMR2_IM38)
+#define LL_EXTI_LINE_38 EXTI_IMR2_IM38 /*!< Extended line 38 */
+#endif
+#if defined(EXTI_IMR2_IM39)
+#define LL_EXTI_LINE_39 EXTI_IMR2_IM39 /*!< Extended line 39 */
+#endif
+#if defined(EXTI_IMR2_IM40)
+#define LL_EXTI_LINE_40 EXTI_IMR2_IM40 /*!< Extended line 40 */
+#endif
+#define LL_EXTI_LINE_ALL_32_63 EXTI_IMR2_IM /*!< All Extended line not reserved*/
+
+
+#define LL_EXTI_LINE_ALL (0xFFFFFFFFU) /*!< All Extended line */
+
+#if defined(USE_FULL_LL_DRIVER)
+#define LL_EXTI_LINE_NONE (0x00000000U) /*!< None Extended line */
+#endif /*USE_FULL_LL_DRIVER*/
+
+/**
+ * @}
+ */
+
+
+#if defined(USE_FULL_LL_DRIVER)
+
+/** @defgroup EXTI_LL_EC_MODE Mode
+ * @{
+ */
+#define LL_EXTI_MODE_IT ((uint8_t)0x00U) /*!< Interrupt Mode */
+#define LL_EXTI_MODE_EVENT ((uint8_t)0x01U) /*!< Event Mode */
+#define LL_EXTI_MODE_IT_EVENT ((uint8_t)0x02U) /*!< Interrupt & Event Mode */
+/**
+ * @}
+ */
+
+/** @defgroup EXTI_LL_EC_TRIGGER Edge Trigger
+ * @{
+ */
+#define LL_EXTI_TRIGGER_NONE ((uint8_t)0x00U) /*!< No Trigger Mode */
+#define LL_EXTI_TRIGGER_RISING ((uint8_t)0x01U) /*!< Trigger Rising Mode */
+#define LL_EXTI_TRIGGER_FALLING ((uint8_t)0x02U) /*!< Trigger Falling Mode */
+#define LL_EXTI_TRIGGER_RISING_FALLING ((uint8_t)0x03U) /*!< Trigger Rising & Falling Mode */
+
+/**
+ * @}
+ */
+
+
+#endif /*USE_FULL_LL_DRIVER*/
+
+
+/**
+ * @}
+ */
+
+/* Exported macro ------------------------------------------------------------*/
+/** @defgroup EXTI_LL_Exported_Macros EXTI Exported Macros
+ * @{
+ */
+
+/** @defgroup EXTI_LL_EM_WRITE_READ Common Write and read registers Macros
+ * @{
+ */
+
+/**
+ * @brief Write a value in EXTI register
+ * @param __REG__ Register to be written
+ * @param __VALUE__ Value to be written in the register
+ * @retval None
+ */
+#define LL_EXTI_WriteReg(__REG__, __VALUE__) WRITE_REG(EXTI->__REG__, (__VALUE__))
+
+/**
+ * @brief Read a value in EXTI register
+ * @param __REG__ Register to be read
+ * @retval Register value
+ */
+#define LL_EXTI_ReadReg(__REG__) READ_REG(EXTI->__REG__)
+/**
+ * @}
+ */
+
+
+/**
+ * @}
+ */
+
+
+
+/* Exported functions --------------------------------------------------------*/
+/** @defgroup EXTI_LL_Exported_Functions EXTI Exported Functions
+ * @{
+ */
+/** @defgroup EXTI_LL_EF_IT_Management IT_Management
+ * @{
+ */
+
+/**
+ * @brief Enable ExtiLine Interrupt request for Lines in range 0 to 31
+ * @note The reset value for the direct or internal lines (see RM)
+ * is set to 1 in order to enable the interrupt by default.
+ * Bits are set automatically at Power on.
+ * @rmtoll IMR1 IMx LL_EXTI_EnableIT_0_31
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_17
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_23
+ * @arg @ref LL_EXTI_LINE_24
+ * @arg @ref LL_EXTI_LINE_25
+ * @arg @ref LL_EXTI_LINE_26
+ * @arg @ref LL_EXTI_LINE_27
+ * @arg @ref LL_EXTI_LINE_28
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @arg @ref LL_EXTI_LINE_ALL_0_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableIT_0_31(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->IMR1, ExtiLine);
+}
+/**
+ * @brief Enable ExtiLine Interrupt request for Lines in range 32 to 63
+ * @note The reset value for the direct lines (lines from 32 to 34, line
+ * 39) is set to 1 in order to enable the interrupt by default.
+ * Bits are set automatically at Power on.
+ * @rmtoll IMR2 IMx LL_EXTI_EnableIT_32_63
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_32
+ * @arg @ref LL_EXTI_LINE_33
+ * @arg @ref LL_EXTI_LINE_34(*)
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @arg @ref LL_EXTI_LINE_39(*)
+ * @arg @ref LL_EXTI_LINE_40(*)
+ * @arg @ref LL_EXTI_LINE_ALL_32_63
+ * @note (*): Available in some devices
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableIT_32_63(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->IMR2, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Interrupt request for Lines in range 0 to 31
+ * @note The reset value for the direct or internal lines (see RM)
+ * is set to 1 in order to enable the interrupt by default.
+ * Bits are set automatically at Power on.
+ * @rmtoll IMR1 IMx LL_EXTI_DisableIT_0_31
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_17
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_23
+ * @arg @ref LL_EXTI_LINE_24
+ * @arg @ref LL_EXTI_LINE_25
+ * @arg @ref LL_EXTI_LINE_26
+ * @arg @ref LL_EXTI_LINE_27
+ * @arg @ref LL_EXTI_LINE_28
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @arg @ref LL_EXTI_LINE_ALL_0_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableIT_0_31(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->IMR1, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Interrupt request for Lines in range 32 to 63
+ * @note The reset value for the direct lines (lines from 32 to 34, line
+ * 39) is set to 1 in order to enable the interrupt by default.
+ * Bits are set automatically at Power on.
+ * @rmtoll IMR2 IMx LL_EXTI_DisableIT_32_63
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_32
+ * @arg @ref LL_EXTI_LINE_33
+ * @arg @ref LL_EXTI_LINE_34(*)
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @arg @ref LL_EXTI_LINE_39(*)
+ * @arg @ref LL_EXTI_LINE_40(*)
+ * @arg @ref LL_EXTI_LINE_ALL_32_63
+ * @note (*): Available in some devices
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableIT_32_63(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->IMR2, ExtiLine);
+}
+
+/**
+ * @brief Indicate if ExtiLine Interrupt request is enabled for Lines in range 0 to 31
+ * @note The reset value for the direct or internal lines (see RM)
+ * is set to 1 in order to enable the interrupt by default.
+ * Bits are set automatically at Power on.
+ * @rmtoll IMR1 IMx LL_EXTI_IsEnabledIT_0_31
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_17
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_23
+ * @arg @ref LL_EXTI_LINE_24
+ * @arg @ref LL_EXTI_LINE_25
+ * @arg @ref LL_EXTI_LINE_26
+ * @arg @ref LL_EXTI_LINE_27
+ * @arg @ref LL_EXTI_LINE_28
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @arg @ref LL_EXTI_LINE_ALL_0_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledIT_0_31(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->IMR1, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @brief Indicate if ExtiLine Interrupt request is enabled for Lines in range 32 to 63
+ * @note The reset value for the direct lines (lines from 32 to 34, line
+ * 39) is set to 1 in order to enable the interrupt by default.
+ * Bits are set automatically at Power on.
+ * @rmtoll IMR2 IMx LL_EXTI_IsEnabledIT_32_63
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_32
+ * @arg @ref LL_EXTI_LINE_33
+ * @arg @ref LL_EXTI_LINE_34(*)
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @arg @ref LL_EXTI_LINE_39(*)
+ * @arg @ref LL_EXTI_LINE_40(*)
+ * @arg @ref LL_EXTI_LINE_ALL_32_63
+ * @note (*): Available in some devices
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledIT_32_63(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->IMR2, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @}
+ */
+
+/** @defgroup EXTI_LL_EF_Event_Management Event_Management
+ * @{
+ */
+
+/**
+ * @brief Enable ExtiLine Event request for Lines in range 0 to 31
+ * @rmtoll EMR1 EMx LL_EXTI_EnableEvent_0_31
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_17
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_23
+ * @arg @ref LL_EXTI_LINE_24
+ * @arg @ref LL_EXTI_LINE_25
+ * @arg @ref LL_EXTI_LINE_26
+ * @arg @ref LL_EXTI_LINE_27
+ * @arg @ref LL_EXTI_LINE_28
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @arg @ref LL_EXTI_LINE_ALL_0_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableEvent_0_31(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->EMR1, ExtiLine);
+
+}
+
+/**
+ * @brief Enable ExtiLine Event request for Lines in range 32 to 63
+ * @rmtoll EMR2 EMx LL_EXTI_EnableEvent_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_32
+ * @arg @ref LL_EXTI_LINE_33
+ * @arg @ref LL_EXTI_LINE_34(*)
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @arg @ref LL_EXTI_LINE_39(*)
+ * @arg @ref LL_EXTI_LINE_40(*)
+ * @arg @ref LL_EXTI_LINE_ALL_32_63
+ * @note (*): Available in some devices
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableEvent_32_63(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->EMR2, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Event request for Lines in range 0 to 31
+ * @rmtoll EMR1 EMx LL_EXTI_DisableEvent_0_31
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_17
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_23
+ * @arg @ref LL_EXTI_LINE_24
+ * @arg @ref LL_EXTI_LINE_25
+ * @arg @ref LL_EXTI_LINE_26
+ * @arg @ref LL_EXTI_LINE_27
+ * @arg @ref LL_EXTI_LINE_28
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @arg @ref LL_EXTI_LINE_ALL_0_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableEvent_0_31(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->EMR1, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Event request for Lines in range 32 to 63
+ * @rmtoll EMR2 EMx LL_EXTI_DisableEvent_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_32
+ * @arg @ref LL_EXTI_LINE_33
+ * @arg @ref LL_EXTI_LINE_34(*)
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @arg @ref LL_EXTI_LINE_39(*)
+ * @arg @ref LL_EXTI_LINE_40(*)
+ * @arg @ref LL_EXTI_LINE_ALL_32_63
+ * @note (*): Available in some devices
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableEvent_32_63(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->EMR2, ExtiLine);
+}
+
+/**
+ * @brief Indicate if ExtiLine Event request is enabled for Lines in range 0 to 31
+ * @rmtoll EMR1 EMx LL_EXTI_IsEnabledEvent_0_31
+ * @param ExtiLine This parameter can be one of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_17
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_23
+ * @arg @ref LL_EXTI_LINE_24
+ * @arg @ref LL_EXTI_LINE_25
+ * @arg @ref LL_EXTI_LINE_26
+ * @arg @ref LL_EXTI_LINE_27
+ * @arg @ref LL_EXTI_LINE_28
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @arg @ref LL_EXTI_LINE_ALL_0_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledEvent_0_31(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->EMR1, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+
+}
+
+/**
+ * @brief Indicate if ExtiLine Event request is enabled for Lines in range 32 to 63
+ * @rmtoll EMR2 EMx LL_EXTI_IsEnabledEvent_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_32
+ * @arg @ref LL_EXTI_LINE_33
+ * @arg @ref LL_EXTI_LINE_34(*)
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @arg @ref LL_EXTI_LINE_39(*)
+ * @arg @ref LL_EXTI_LINE_40(*)
+ * @arg @ref LL_EXTI_LINE_ALL_32_63
+ * @note (*): Available in some devices
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledEvent_32_63(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->EMR2, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @}
+ */
+
+/** @defgroup EXTI_LL_EF_Rising_Trigger_Management Rising_Trigger_Management
+ * @{
+ */
+
+/**
+ * @brief Enable ExtiLine Rising Edge Trigger for Lines in range 0 to 31
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a rising edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_RTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for
+ * the same interrupt line. In this case, both generate a trigger
+ * condition.
+ * @rmtoll RTSR1 RTx LL_EXTI_EnableRisingTrig_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableRisingTrig_0_31(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->RTSR1, ExtiLine);
+
+}
+
+/**
+ * @brief Enable ExtiLine Rising Edge Trigger for Lines in range 32 to 63
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a rising edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_RTSR register, the
+ * pending bit is not set.Rising and falling edge triggers can be set for
+ * the same interrupt line. In this case, both generate a trigger
+ * condition.
+ * @rmtoll RTSR2 RTx LL_EXTI_EnableRisingTrig_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableRisingTrig_32_63(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->RTSR2, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Rising Edge Trigger for Lines in range 0 to 31
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a rising edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_RTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for
+ * the same interrupt line. In this case, both generate a trigger
+ * condition.
+ * @rmtoll RTSR1 RTx LL_EXTI_DisableRisingTrig_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableRisingTrig_0_31(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->RTSR1, ExtiLine);
+
+}
+
+/**
+ * @brief Disable ExtiLine Rising Edge Trigger for Lines in range 32 to 63
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a rising edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_RTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for
+ * the same interrupt line. In this case, both generate a trigger
+ * condition.
+ * @rmtoll RTSR2 RTx LL_EXTI_DisableRisingTrig_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableRisingTrig_32_63(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->RTSR2, ExtiLine);
+}
+
+/**
+ * @brief Check if rising edge trigger is enabled for Lines in range 0 to 31
+ * @rmtoll RTSR1 RTx LL_EXTI_IsEnabledRisingTrig_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledRisingTrig_0_31(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->RTSR1, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @brief Check if rising edge trigger is enabled for Lines in range 32 to 63
+ * @rmtoll RTSR2 RTx LL_EXTI_IsEnabledRisingTrig_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledRisingTrig_32_63(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->RTSR2, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @}
+ */
+
+/** @defgroup EXTI_LL_EF_Falling_Trigger_Management Falling_Trigger_Management
+ * @{
+ */
+
+/**
+ * @brief Enable ExtiLine Falling Edge Trigger for Lines in range 0 to 31
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a falling edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_FTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for
+ * the same interrupt line. In this case, both generate a trigger
+ * condition.
+ * @rmtoll FTSR1 FTx LL_EXTI_EnableFallingTrig_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableFallingTrig_0_31(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->FTSR1, ExtiLine);
+}
+
+/**
+ * @brief Enable ExtiLine Falling Edge Trigger for Lines in range 32 to 63
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a Falling edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_FTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for
+ * the same interrupt line. In this case, both generate a trigger
+ * condition.
+ * @rmtoll FTSR2 FTx LL_EXTI_EnableFallingTrig_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_EnableFallingTrig_32_63(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->FTSR2, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Falling Edge Trigger for Lines in range 0 to 31
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a Falling edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_FTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for the same interrupt line.
+ * In this case, both generate a trigger condition.
+ * @rmtoll FTSR1 FTx LL_EXTI_DisableFallingTrig_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableFallingTrig_0_31(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->FTSR1, ExtiLine);
+}
+
+/**
+ * @brief Disable ExtiLine Falling Edge Trigger for Lines in range 32 to 63
+ * @note The configurable wakeup lines are edge-triggered. No glitch must be
+ * generated on these lines. If a Falling edge on a configurable interrupt
+ * line occurs during a write operation in the EXTI_FTSR register, the
+ * pending bit is not set.
+ * Rising and falling edge triggers can be set for the same interrupt line.
+ * In this case, both generate a trigger condition.
+ * @rmtoll FTSR2 FTx LL_EXTI_DisableFallingTrig_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_DisableFallingTrig_32_63(uint32_t ExtiLine)
+{
+ CLEAR_BIT(EXTI->FTSR2, ExtiLine);
+}
+
+/**
+ * @brief Check if falling edge trigger is enabled for Lines in range 0 to 31
+ * @rmtoll FTSR1 FTx LL_EXTI_IsEnabledFallingTrig_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledFallingTrig_0_31(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->FTSR1, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @brief Check if falling edge trigger is enabled for Lines in range 32 to 63
+ * @rmtoll FTSR2 FTx LL_EXTI_IsEnabledFallingTrig_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsEnabledFallingTrig_32_63(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->FTSR2, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @}
+ */
+
+/** @defgroup EXTI_LL_EF_Software_Interrupt_Management Software_Interrupt_Management
+ * @{
+ */
+
+/**
+ * @brief Generate a software Interrupt Event for Lines in range 0 to 31
+ * @note If the interrupt is enabled on this line in the EXTI_IMR1, writing a 1 to
+ * this bit when it is at '0' sets the corresponding pending bit in EXTI_PR1
+ * resulting in an interrupt request generation.
+ * This bit is cleared by clearing the corresponding bit in the EXTI_PR1
+ * register (by writing a 1 into the bit)
+ * @rmtoll SWIER1 SWIx LL_EXTI_GenerateSWI_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_GenerateSWI_0_31(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->SWIER1, ExtiLine);
+}
+
+/**
+ * @brief Generate a software Interrupt Event for Lines in range 32 to 63
+ * @note If the interrupt is enabled on this line inthe EXTI_IMR2, writing a 1 to
+ * this bit when it is at '0' sets the corresponding pending bit in EXTI_PR2
+ * resulting in an interrupt request generation.
+ * This bit is cleared by clearing the corresponding bit in the EXTI_PR2
+ * register (by writing a 1 into the bit)
+ * @rmtoll SWIER2 SWIx LL_EXTI_GenerateSWI_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_GenerateSWI_32_63(uint32_t ExtiLine)
+{
+ SET_BIT(EXTI->SWIER2, ExtiLine);
+}
+
+/**
+ * @}
+ */
+
+/** @defgroup EXTI_LL_EF_Flag_Management Flag_Management
+ * @{
+ */
+
+/**
+ * @brief Check if the ExtLine Flag is set or not for Lines in range 0 to 31
+ * @note This bit is set when the selected edge event arrives on the interrupt
+ * line. This bit is cleared by writing a 1 to the bit.
+ * @rmtoll PR1 PIFx LL_EXTI_IsActiveFlag_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsActiveFlag_0_31(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->PR1, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @brief Check if the ExtLine Flag is set or not for Lines in range 32 to 63
+ * @note This bit is set when the selected edge event arrives on the interrupt
+ * line. This bit is cleared by writing a 1 to the bit.
+ * @rmtoll PR2 PIFx LL_EXTI_IsActiveFlag_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval State of bit (1 or 0).
+ */
+__STATIC_INLINE uint32_t LL_EXTI_IsActiveFlag_32_63(uint32_t ExtiLine)
+{
+ return ((READ_BIT(EXTI->PR2, ExtiLine) == (ExtiLine)) ? 1UL : 0UL);
+}
+
+/**
+ * @brief Read ExtLine Combination Flag for Lines in range 0 to 31
+ * @note This bit is set when the selected edge event arrives on the interrupt
+ * line. This bit is cleared by writing a 1 to the bit.
+ * @rmtoll PR1 PIFx LL_EXTI_ReadFlag_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval @note This bit is set when the selected edge event arrives on the interrupt
+ */
+__STATIC_INLINE uint32_t LL_EXTI_ReadFlag_0_31(uint32_t ExtiLine)
+{
+ return (uint32_t)(READ_BIT(EXTI->PR1, ExtiLine));
+}
+
+/**
+ * @brief Read ExtLine Combination Flag for Lines in range 32 to 63
+ * @note This bit is set when the selected edge event arrives on the interrupt
+ * line. This bit is cleared by writing a 1 to the bit.
+ * @rmtoll PR2 PIFx LL_EXTI_ReadFlag_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval @note This bit is set when the selected edge event arrives on the interrupt
+ */
+__STATIC_INLINE uint32_t LL_EXTI_ReadFlag_32_63(uint32_t ExtiLine)
+{
+ return (uint32_t)(READ_BIT(EXTI->PR2, ExtiLine));
+}
+
+/**
+ * @brief Clear ExtLine Flags for Lines in range 0 to 31
+ * @note This bit is set when the selected edge event arrives on the interrupt
+ * line. This bit is cleared by writing a 1 to the bit.
+ * @rmtoll PR1 PIFx LL_EXTI_ClearFlag_0_31
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_0
+ * @arg @ref LL_EXTI_LINE_1
+ * @arg @ref LL_EXTI_LINE_2
+ * @arg @ref LL_EXTI_LINE_3
+ * @arg @ref LL_EXTI_LINE_4
+ * @arg @ref LL_EXTI_LINE_5
+ * @arg @ref LL_EXTI_LINE_6
+ * @arg @ref LL_EXTI_LINE_7
+ * @arg @ref LL_EXTI_LINE_8
+ * @arg @ref LL_EXTI_LINE_9
+ * @arg @ref LL_EXTI_LINE_10
+ * @arg @ref LL_EXTI_LINE_11
+ * @arg @ref LL_EXTI_LINE_12
+ * @arg @ref LL_EXTI_LINE_13
+ * @arg @ref LL_EXTI_LINE_14
+ * @arg @ref LL_EXTI_LINE_15
+ * @arg @ref LL_EXTI_LINE_16
+ * @arg @ref LL_EXTI_LINE_18
+ * @arg @ref LL_EXTI_LINE_19
+ * @arg @ref LL_EXTI_LINE_20
+ * @arg @ref LL_EXTI_LINE_21
+ * @arg @ref LL_EXTI_LINE_22
+ * @arg @ref LL_EXTI_LINE_29
+ * @arg @ref LL_EXTI_LINE_30
+ * @arg @ref LL_EXTI_LINE_31
+ * @note Please check each device line mapping for EXTI Line availability
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_ClearFlag_0_31(uint32_t ExtiLine)
+{
+ WRITE_REG(EXTI->PR1, ExtiLine);
+}
+
+/**
+ * @brief Clear ExtLine Flags for Lines in range 32 to 63
+ * @note This bit is set when the selected edge event arrives on the interrupt
+ * line. This bit is cleared by writing a 1 to the bit.
+ * @rmtoll PR2 PIFx LL_EXTI_ClearFlag_32_63
+ * @param ExtiLine This parameter can be a combination of the following values:
+ * @arg @ref LL_EXTI_LINE_35
+ * @arg @ref LL_EXTI_LINE_36
+ * @arg @ref LL_EXTI_LINE_37
+ * @arg @ref LL_EXTI_LINE_38
+ * @retval None
+ */
+__STATIC_INLINE void LL_EXTI_ClearFlag_32_63(uint32_t ExtiLine)
+{
+ WRITE_REG(EXTI->PR2, ExtiLine);
+}
+
+
+/**
+ * @}
+ */
+
+#if defined(USE_FULL_LL_DRIVER)
+/** @defgroup EXTI_LL_EF_Init Initialization and de-initialization functions
+ * @{
+ */
+
+uint32_t LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct);
+uint32_t LL_EXTI_DeInit(void);
+void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct);
+
+
+/**
+ * @}
+ */
+#endif /* USE_FULL_LL_DRIVER */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+#endif /* EXTI */
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM32L4xx_LL_EXTI_H */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
diff --git a/targets/stm32l432/lib/usbd/usbd_composite.c b/targets/stm32l432/lib/usbd/usbd_composite.c
index a70db6a..1aa641d 100644
--- a/targets/stm32l432/lib/usbd/usbd_composite.c
+++ b/targets/stm32l432/lib/usbd/usbd_composite.c
@@ -27,229 +27,256 @@ static uint8_t *USBD_Composite_GetOtherSpeedCfgDesc (uint16_t *length);
static uint8_t *USBD_Composite_GetDeviceQualifierDescriptor (uint16_t *length);
-#define NUM_INTERFACES 3
+#define NUM_CLASSES 3
-#if NUM_INTERFACES>2
+
+#if NUM_CLASSES>2
+#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90 + 8+9 + 4 + 84)
+#define NUM_INTERFACES 4
+#elif NUM_CLASSES>1
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90 + 84)
-#elif NUM_INTERFACES>1
-#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (90)
+#define NUM_INTERFACES 2
#else
#define COMPOSITE_CDC_HID_DESCRIPTOR_SIZE (41)
+#define NUM_INTERFACES 1
#endif
-#define HID_INTF_NUM 0
-#define CDC_INTF_NUM 1
-#define CCID_INTF_NUM 2
+#define HID_INTF_NUM 0
+#define CCID_INTF_NUM 1
+#define CDC_MASTER_INTF_NUM 2
+#define CDC_SLAVE_INTF_NUM 3
__ALIGN_BEGIN uint8_t COMPOSITE_CDC_HID_DESCRIPTOR[COMPOSITE_CDC_HID_DESCRIPTOR_SIZE] __ALIGN_END =
- {
- /*Configuration Descriptor*/
- 0x09, /* bLength: Configuration Descriptor size */
- USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
- COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
- 0x00,
- NUM_INTERFACES, /* bNumInterfaces: 1 interface */
- 0x01, /* bConfigurationValue: Configuration value */
- 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
- 0x80, /* bmAttributes: self powered */
- 0x32, /* MaxPower 100 mA */
+ {
+ /*Configuration Descriptor*/
+ 0x09, /* bLength: Configuration Descriptor size */
+ USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
+ COMPOSITE_CDC_HID_DESCRIPTOR_SIZE, /* wTotalLength:no of returned bytes */
+ 0x00,
+ NUM_INTERFACES, /* bNumInterfaces */
+ 0x01, /* bConfigurationValue: Configuration value */
+ 0x00, /* iConfiguration: Index of string descriptor describing the configuration */
+ 0x80, /* bmAttributes: self powered */
+ 0x32, /* MaxPower 100 mA */
- /*---------------------------------------------------------------------------*/
+ /*---------------------------------------------------------------------------*/
- /* */
- /* HID */
- /* */
-
- /************** Descriptor of Joystick Mouse interface ****************/
- 0x09, /*bLength: Interface Descriptor size*/
- USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
- HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
- 0x00, /*bAlternateSetting: Alternate setting*/
- 0x02, /*bNumEndpoints*/
- 0x03, /*bInterfaceClass: HID*/
- 0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
- 0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
- 2, /*iInterface: Index of string descriptor*/
- /******************** Descriptor of Joystick Mouse HID ********************/
- 0x09, /*bLength: HID Descriptor size*/
- HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
- 0x11, /*bcdHID: HID Class Spec release number*/
- 0x01,
- 0x00, /*bCountryCode: Hardware target country*/
- 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
- 0x22, /*bDescriptorType*/
- HID_FIDO_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
- 0,
- /******************** Descriptor of Mouse endpoint ********************/
- 0x07, /*bLength: Endpoint Descriptor size*/
- USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
- HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
- 0x03, /*bmAttributes: Interrupt endpoint*/
- HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
- 0x00,
- HID_BINTERVAL, /*bInterval: Polling Interval */
-
- 0x07, /*bLength: Endpoint Descriptor size*/
- USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
- HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
- 0x03, /*bmAttributes: Interrupt endpoint*/
- HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
- 0x00,
- HID_BINTERVAL, /*bInterval: Polling Interval */
+ /* */
+ /* HID */
+ /* */
+ /************** Descriptor of Joystick Mouse interface ****************/
+ 0x09, /*bLength: Interface Descriptor size*/
+ USB_DESC_TYPE_INTERFACE, /*bDescriptorType: Interface descriptor type*/
+ HID_INTF_NUM, /*bInterfaceNumber: Number of Interface*/
+ 0x00, /*bAlternateSetting: Alternate setting*/
+ 0x02, /*bNumEndpoints*/
+ 0x03, /*bInterfaceClass: HID*/
+ 0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
+ 0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
+ 2, /*iInterface: Index of string descriptor*/
+ /******************** Descriptor of Joystick Mouse HID ********************/
+ 0x09, /*bLength: HID Descriptor size*/
+ HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
+ 0x11, /*bcdHID: HID Class Spec release number*/
+ 0x01,
+ 0x00, /*bCountryCode: Hardware target country*/
+ 0x01, /*bNumDescriptors: Number of HID class descriptors to follow*/
+ 0x22, /*bDescriptorType*/
+ HID_FIDO_REPORT_DESC_SIZE, /*wItemLength: Total length of Report descriptor*/
+ 0,
+ /******************** Descriptor of Mouse endpoint ********************/
+ 0x07, /*bLength: Endpoint Descriptor size*/
+ USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
+ HID_EPIN_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
+ 0x03, /*bmAttributes: Interrupt endpoint*/
+ HID_EPIN_SIZE, /*wMaxPacketSize: 4 Byte max */
+ 0x00,
+ HID_BINTERVAL, /*bInterval: Polling Interval */
+ 0x07, /*bLength: Endpoint Descriptor size*/
+ USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
+ HID_EPOUT_ADDR, /*bEndpointAddress: Endpoint Address (IN)*/
+ 0x03, /*bmAttributes: Interrupt endpoint*/
+ HID_EPOUT_SIZE, /*wMaxPacketSize: 4 Byte max */
+ 0x00,
+ HID_BINTERVAL, /*bInterval: Polling Interval */
#if NUM_INTERFACES>1
- /* */
- /* CDC */
- /* */
+ /* CCID Interface Descriptor */
+ 9, /* bLength: Interface Descriptor size */
+ USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
+ CCID_INTF_NUM, /* bInterfaceNumber: CCID Interface */
+ 0, /* Alternate setting for this interface */
+ 3, /* bNumEndpoints: Bulk-IN, Bulk-OUT, Intr-IN */
+ 0x0B, /* CCID class */
+ 0x00, /* CCID subclass */
+ 0x00, /* CCID protocol */
+ 0, /* string index for interface */
+ /* ICC Descriptor */
+ 54, /* bLength: */
+ 0x21, /* bDescriptorType: USBDESCR_ICC */
+ 0x10, 0x01, /* bcdCCID: revision 1.1 (of CCID) */
+ 0, /* bMaxSlotIndex: */
+ 1, /* bVoltageSupport: 5V-only */
+ 0x02, 0, 0, 0, /* dwProtocols: T=1 */
+ 0xa0, 0x0f, 0, 0, /* dwDefaultClock: 4000 */
+ 0xa0, 0x0f, 0, 0, /* dwMaximumClock: 4000 */
+ 0, /* bNumClockSupported: 0x00 */
+ 0x80, 0x25, 0, 0, /* dwDataRate: 9600 */
+ 0x80, 0x25, 0, 0, /* dwMaxDataRate: 9600 */
+ 0, /* bNumDataRateSupported: 0x00 */
+ 0xfe, 0, 0, 0, /* dwMaxIFSD: 254 */
+ 0, 0, 0, 0, /* dwSynchProtocols: 0 */
+ 0, 0, 0, 0, /* dwMechanical: 0 */
+ 0x7a, 0x04, 0x02, 0x00, /* dwFeatures:
+ * Short and extended APDU level: 0x40000 ----
+ * Short APDU level : 0x20000 *
+ * (ICCD?) : 0x00800 ----
+ * Automatic IFSD : 0x00400 *
+ * NAD value other than 0x00 : 0x00200
+ * Can set ICC in clock stop : 0x00100
+ * Automatic PPS CUR : 0x00080
+ * Automatic PPS PROP : 0x00040 *
+ * Auto baud rate change : 0x00020 *
+ * Auto clock change : 0x00010 *
+ * Auto voltage selection : 0x00008 *
+ * Auto activaction of ICC : 0x00004
+ * Automatic conf. based on ATR : 0x00002 *
+ */
+ 0x0f, 0x01, 0, 0, /* dwMaxCCIDMessageLength: 271 */
+ 0xff, /* bClassGetResponse: 0xff */
+ 0x00, /* bClassEnvelope: 0 */
+ 0, 0, /* wLCDLayout: 0 */
+ 0, /* bPinSupport: No PIN pad */
- /*Interface Descriptor */
- 0x09, /* bLength: Interface Descriptor size */
- USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
- /* Interface descriptor type */
-/*!*/ CDC_INTF_NUM, /* bInterfaceNumber: Number of Interface */
- 0x00, /* bAlternateSetting: Alternate setting */
- 0x03, /* bNumEndpoints: 3 endpoints used */
- 0x02, /* bInterfaceClass: Communication Interface Class */
- 0x02, /* bInterfaceSubClass: Abstract Control Model */
- 0x00, /* bInterfaceProtocol: Common AT commands */
- 0x00, /* iInterface: */
-
- /*Header Functional Descriptor*/
- 0x05, /* bLength: Endpoint Descriptor size */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x00, /* bDescriptorSubtype: Header Func Desc */
- 0x10, /* bcdCDC: spec release number */
- 0x01,
-
- /*Call Management Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x01, /* bDescriptorSubtype: Call Management Func Desc */
- 0x00, /* bmCapabilities: D0+D1 */
-/*!*/ CDC_INTF_NUM, /* bDataInterface: 0 */
-
- /*ACM Functional Descriptor*/
- 0x04, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
- 0x02, /* bmCapabilities */
-
- /*Union Functional Descriptor*/
- 0x05, /* bFunctionLength */
- 0x24, /* bDescriptorType: CS_INTERFACE */
- 0x06, /* bDescriptorSubtype: Union func desc */
-/*!*/ CDC_INTF_NUM, /* bMasterInterface: Communication class interface */
-/*!*/ CDC_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
-
- /*Endpoint 2 Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_CMD_EP, /* bEndpointAddress */
- 0x03, /* bmAttributes: Interrupt */
- LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_CMD_PACKET_SIZE),
- 0x10, /* bInterval: */
-
- /*Endpoint OUT Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_OUT_EP, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
-
- /*Endpoint IN Descriptor*/
- 0x07, /* bLength: Endpoint Descriptor size */
- USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
- CDC_IN_EP, /* bEndpointAddress */
- 0x02, /* bmAttributes: Bulk */
- LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
- HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
- 0x00, /* bInterval: ignore for Bulk transfer */
+ 1, /* bMaxCCIDBusySlots: 1 */
+ /*Endpoint IN1 Descriptor*/
+ 7, /* bLength: Endpoint Descriptor size */
+ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
+ CCID_IN_EP, /* bEndpointAddress: (IN1) */
+ 0x02, /* bmAttributes: Bulk */
+ HID_FIDO_REPORT_DESC_SIZE, 0x00, /* wMaxPacketSize: */
+ 0x00, /* bInterval */
+ /*Endpoint OUT1 Descriptor*/
+ 7, /* bLength: Endpoint Descriptor size */
+ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
+ CCID_OUT_EP, /* bEndpointAddress: (OUT1) */
+ 0x02, /* bmAttributes: Bulk */
+ HID_FIDO_REPORT_DESC_SIZE, 0x00, /* wMaxPacketSize: */
+ 0x00, /* bInterval */
+ /*Endpoint IN2 Descriptor*/
+ 7, /* bLength: Endpoint Descriptor size */
+ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
+ CCID_CMD_EP, /* bEndpointAddress: (IN2) */
+ 0x03, /* bmAttributes: Interrupt */
+ 0x04, 0x00, /* wMaxPacketSize: 4 */
+ 0xFF, /* bInterval (255ms) */
#endif
-#if NUM_INTERFACES>2
-/* CCID Interface Descriptor */
-9, /* bLength: Interface Descriptor size */
-USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
-CCID_INTF_NUM, /* bInterfaceNumber: CCID Interface */
-0, /* Alternate setting for this interface */
-3, /* bNumEndpoints: Bulk-IN, Bulk-OUT, Intr-IN */
-0x0B, /* CCID class */
-0x00, /* CCID subclass */
-0x00, /* CCID protocol */
-0, /* string index for interface */
+#if NUM_INTERFACES > 2
-/* ICC Descriptor */
-54, /* bLength: */
-0x21, /* bDescriptorType: USBDESCR_ICC */
-0x10, 0x01, /* bcdCCID: revision 1.1 (of CCID) */
-0, /* bMaxSlotIndex: */
-1, /* bVoltageSupport: 5V-only */
-0x02, 0, 0, 0, /* dwProtocols: T=1 */
-0xa0, 0x0f, 0, 0, /* dwDefaultClock: 4000 */
-0xa0, 0x0f, 0, 0, /* dwMaximumClock: 4000 */
-0, /* bNumClockSupported: 0x00 */
-0x80, 0x25, 0, 0, /* dwDataRate: 9600 */
-0x80, 0x25, 0, 0, /* dwMaxDataRate: 9600 */
-0, /* bNumDataRateSupported: 0x00 */
-0xfe, 0, 0, 0, /* dwMaxIFSD: 254 */
-0, 0, 0, 0, /* dwSynchProtocols: 0 */
-0, 0, 0, 0, /* dwMechanical: 0 */
-0x7a, 0x04, 0x02, 0x00, /* dwFeatures:
- * Short and extended APDU level: 0x40000 ----
- * Short APDU level : 0x20000 *
- * (ICCD?) : 0x00800 ----
- * Automatic IFSD : 0x00400 *
- * NAD value other than 0x00 : 0x00200
- * Can set ICC in clock stop : 0x00100
- * Automatic PPS CUR : 0x00080
- * Automatic PPS PROP : 0x00040 *
- * Auto baud rate change : 0x00020 *
- * Auto clock change : 0x00010 *
- * Auto voltage selection : 0x00008 *
- * Auto activaction of ICC : 0x00004
- * Automatic conf. based on ATR : 0x00002 *
- */
-0x0f, 0x01, 0, 0, /* dwMaxCCIDMessageLength: 271 */
-0xff, /* bClassGetResponse: 0xff */
-0x00, /* bClassEnvelope: 0 */
-0, 0, /* wLCDLayout: 0 */
-0, /* bPinSupport: No PIN pad */
+ /* */
+ /* CDC */
+ /* */
+ // This "IAD" is needed for Windows since it ignores the standard Union Functional Descriptor
+ 0x08, // bLength
+ 0x0B, // IAD type
+ CDC_MASTER_INTF_NUM, // First interface
+ CDC_SLAVE_INTF_NUM, // Next interface
+ 0x02, // bInterfaceClass of the first interface
+ 0x02, // bInterfaceSubClass of the first interface
+ 0x00, // bInterfaceProtocol of the first interface
+ 0x00, // Interface string index
-1, /* bMaxCCIDBusySlots: 1 */
-/*Endpoint IN1 Descriptor*/
-7, /* bLength: Endpoint Descriptor size */
-USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
-CCID_IN_EP, /* bEndpointAddress: (IN1) */
-0x02, /* bmAttributes: Bulk */
-HID_FIDO_REPORT_DESC_SIZE, 0x00, /* wMaxPacketSize: */
-0x00, /* bInterval */
-/*Endpoint OUT1 Descriptor*/
-7, /* bLength: Endpoint Descriptor size */
-USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
-CCID_OUT_EP, /* bEndpointAddress: (OUT1) */
-0x02, /* bmAttributes: Bulk */
-HID_FIDO_REPORT_DESC_SIZE, 0x00, /* wMaxPacketSize: */
-0x00, /* bInterval */
-/*Endpoint IN2 Descriptor*/
-7, /* bLength: Endpoint Descriptor size */
-USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
-CCID_CMD_EP, /* bEndpointAddress: (IN2) */
-0x03, /* bmAttributes: Interrupt */
-0x04, 0x00, /* wMaxPacketSize: 4 */
-0xFF, /* bInterval (255ms) */
+ /*Interface Descriptor */
+ 0x09, /* bLength: Interface Descriptor size */
+ USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */
+ /* Interface descriptor type */
+ /*!*/ CDC_MASTER_INTF_NUM, /* bInterfaceNumber: Number of Interface */
+ 0x00, /* bAlternateSetting: Alternate setting */
+ 0x01, /* bNumEndpoints: 1 endpoint used */
+ 0x02, /* bInterfaceClass: Communication Interface Class */
+ 0x02, /* bInterfaceSubClass: Abstract Control Model */
+ 0x00, /* bInterfaceProtocol: Common AT commands */
+ 0x00, /* iInterface: */
+ /*Header Functional Descriptor*/
+ 0x05, /* bLength: Endpoint Descriptor size */
+ 0x24, /* bDescriptorType: CS_INTERFACE */
+ 0x00, /* bDescriptorSubtype: Header Func Desc */
+ 0x10, /* bcdCDC: spec release number */
+ 0x01,
+
+ /*Call Management Functional Descriptor*/
+ 0x05, /* bFunctionLength */
+ 0x24, /* bDescriptorType: CS_INTERFACE */
+ 0x01, /* bDescriptorSubtype: Call Management Func Desc */
+ 0x00, /* bmCapabilities: D0+D1 */
+ /*!*/ CDC_SLAVE_INTF_NUM, /* bDataInterface: 0 */
+
+ /*ACM Functional Descriptor*/
+ 0x04, /* bFunctionLength */
+ 0x24, /* bDescriptorType: CS_INTERFACE */
+ 0x02, /* bDescriptorSubtype: Abstract Control Management desc */
+ 0x02, /* bmCapabilities */
+
+ /*Union Functional Descriptor*/
+ 0x05, /* bFunctionLength */
+ 0x24, /* bDescriptorType: CS_INTERFACE */
+ 0x06, /* bDescriptorSubtype: Union func desc */
+ /*!*/ CDC_MASTER_INTF_NUM, /* bMasterInterface: Communication class interface */
+ /*!*/ CDC_SLAVE_INTF_NUM, /* bSlaveInterface0: Data Class Interface */
+
+ /* Control Endpoint Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
+ CDC_CMD_EP, /* bEndpointAddress */
+ 0x03, /* bmAttributes: Interrupt */
+ LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */
+ HIBYTE(CDC_CMD_PACKET_SIZE),
+ 0x10, /* bInterval: */
+
+ /* Interface descriptor */
+ 0x09, /* bLength */
+ USB_DESC_TYPE_INTERFACE, /* bDescriptorType */
+ CDC_SLAVE_INTF_NUM, /* bInterfaceNumber */
+ 0x00, /* bAlternateSetting */
+ 0x02, /* bNumEndpoints */
+ 0x0A, /* bInterfaceClass: Communication class data */
+ 0x00, /* bInterfaceSubClass */
+ 0x00, /* bInterfaceProtocol */
+ 0x00,
+
+ /*Endpoint OUT Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
+ CDC_OUT_EP, /* bEndpointAddress */
+ 0x02, /* bmAttributes: Bulk */
+ LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
+ HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
+ 0x00, /* bInterval: ignore for Bulk transfer */
+
+ /*Endpoint IN Descriptor*/
+ 0x07, /* bLength: Endpoint Descriptor size */
+ USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
+ CDC_IN_EP, /* bEndpointAddress */
+ 0x02, /* bmAttributes: Bulk */
+ LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
+ HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE),
+ 0x00, /* bInterval: ignore for Bulk transfer */
+
+ 4, /* Descriptor size */
+ 3, /* Descriptor type */
+ 0x09,
+ 0x04,
#endif
+
};
-
USBD_ClassTypeDef USBD_Composite =
{
USBD_Composite_Init,
@@ -274,21 +301,31 @@ int in_endpoint_to_class[MAX_ENDPOINTS];
int out_endpoint_to_class[MAX_ENDPOINTS];
-void USBD_Composite_Set_Classes(USBD_ClassTypeDef *class0, USBD_ClassTypeDef *class1, USBD_ClassTypeDef *class2)
+void USBD_Composite_Set_Classes(USBD_ClassTypeDef *hid_class, USBD_ClassTypeDef *ccid_class, USBD_ClassTypeDef *cdc_class)) {
+ USBD_Classes[0] = hid_class;
+ USBD_Classes[1] = ccid_class;
+ USBD_Classes[2] = cdc_class;
+}
+
+static USBD_ClassTypeDef * getClass(uint8_t index)
{
- //Y
- USBD_Classes[0] = class0;
- USBD_Classes[1] = class1;
- USBD_Classes[2] = class2;
- //Y
+ switch(index)
+ {
+ case HID_INTF_NUM:
+ return USBD_Classes[0];
+ case CCID_INTF_NUM:
+ return USBD_Classes[2];
+ case CDC_MASTER_INTF_NUM:
+ case CDC_SLAVE_INTF_NUM:
+ return USBD_Classes[3];
+
+ }
+ return NULL;
}
static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i;
-
- //N
-
- for(i = 0; i < NUM_INTERFACES; i++) {
+ for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->Init(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL;
}
@@ -299,7 +336,7 @@ static uint8_t USBD_Composite_Init (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx) {
int i;
- for(i = 0; i < NUM_INTERFACES; i++) {
+ for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->DeInit(pdev, cfgidx) != USBD_OK) {
return USBD_FAIL;
}
@@ -310,11 +347,13 @@ static uint8_t USBD_Composite_DeInit (USBD_HandleTypeDef *pdev, uint8_t cfgidx)
static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req) {
int i;
- //N
+ USBD_ClassTypeDef * device_class;
+ device_class = getClass(req->wIndex);
+
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
case USB_REQ_TYPE_CLASS :
- if (req->wIndex < NUM_INTERFACES)
- return USBD_Classes[req->wIndex]->Setup(pdev, req);
+ if (device_class != NULL)
+ return device_class->Setup(pdev, req);
else
return USBD_FAIL;
@@ -323,7 +362,7 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
switch (req->bRequest) {
case USB_REQ_GET_DESCRIPTOR :
- for(i = 0; i < NUM_INTERFACES; i++) {
+ for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->Setup(pdev, req) != USBD_OK) {
return USBD_FAIL;
}
@@ -333,8 +372,8 @@ static uint8_t USBD_Composite_Setup (USBD_HandleTypeDef *pdev, USBD_SetupReqType
case USB_REQ_GET_INTERFACE :
case USB_REQ_SET_INTERFACE :
- if (req->wIndex < NUM_INTERFACES)
- return USBD_Classes[req->wIndex]->Setup(pdev, req);
+ if (device_class != NULL)
+ return device_class->Setup(pdev, req);
else
return USBD_FAIL;
}
@@ -361,7 +400,7 @@ static uint8_t USBD_Composite_DataOut (USBD_HandleTypeDef *pdev, uint8_t epnum)
static uint8_t USBD_Composite_EP0_RxReady (USBD_HandleTypeDef *pdev) {
int i;
- for(i = 0; i < NUM_INTERFACES; i++) {
+ for(i = 0; i < NUM_CLASSES; i++) {
if (USBD_Classes[i]->EP0_RxReady != NULL) {
if (USBD_Classes[i]->EP0_RxReady(pdev) != USBD_OK) {
return USBD_FAIL;
diff --git a/targets/stm32l432/lib/usbd/usbd_hid.c b/targets/stm32l432/lib/usbd/usbd_hid.c
index bf32911..c96436b 100644
--- a/targets/stm32l432/lib/usbd/usbd_hid.c
+++ b/targets/stm32l432/lib/usbd/usbd_hid.c
@@ -342,6 +342,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
uint8_t *pbuf = NULL;
uint16_t status_info = 0U;
USBD_StatusTypeDef ret = USBD_OK;
+ req->wLength = req->wLength & 0x7f;
switch (req->bmRequest & USB_REQ_TYPE_MASK)
{
@@ -386,6 +387,7 @@ static uint8_t USBD_HID_Setup (USBD_HandleTypeDef *pdev,
break;
case USB_REQ_GET_DESCRIPTOR:
+ req->wLength = req->wLength & 0x7f;
if(req->wValue >> 8 == HID_REPORT_DESC)
{
len = MIN(HID_FIDO_REPORT_DESC_SIZE , req->wLength);
diff --git a/targets/stm32l432/src/app.h b/targets/stm32l432/src/app.h
index 3bcfac2..308e5f6 100644
--- a/targets/stm32l432/src/app.h
+++ b/targets/stm32l432/src/app.h
@@ -31,7 +31,7 @@
// #define DISABLE_CTAPHID_WINK
// #define DISABLE_CTAPHID_CBOR
-#define ENABLE_SERIAL_PRINTING
+// #define ENABLE_SERIAL_PRINTING
#if defined(SOLO_HACKER)
#define SOLO_PRODUCT_NAME "Solo Hacker " SOLO_VERSION
diff --git a/targets/stm32l432/src/crypto.c b/targets/stm32l432/src/crypto.c
index 31812d4..1dde2f3 100644
--- a/targets/stm32l432/src/crypto.c
+++ b/targets/stm32l432/src/crypto.c
@@ -157,6 +157,11 @@ void crypto_sha256_hmac_final(uint8_t * key, uint32_t klen, uint8_t * hmac)
key = master_secret;
klen = sizeof(master_secret)/2;
}
+ else if (key == CRYPTO_TRANSPORT_KEY2)
+ {
+ key = transport_secret;
+ klen = 32;
+ }
if(klen > 64)
@@ -277,6 +282,11 @@ void crypto_ecc256_derive_public_key(uint8_t * data, int len, uint8_t * x, uint8
memmove(x,pubkey,32);
memmove(y,pubkey+32,32);
}
+void crypto_ecc256_compute_public_key(uint8_t * privkey, uint8_t * pubkey)
+{
+ uECC_compute_public_key(privkey, pubkey, _es256_curve);
+}
+
void crypto_load_external_key(uint8_t * key, int len)
{
diff --git a/targets/stm32l432/src/device.c b/targets/stm32l432/src/device.c
index ae441fc..49bbfe7 100644
--- a/targets/stm32l432/src/device.c
+++ b/targets/stm32l432/src/device.c
@@ -38,11 +38,14 @@ void wait_for_usb_tether();
uint32_t __90_ms = 0;
+uint32_t __last_button_press_time = 0;
+uint32_t __last_button_bounce_time = 0;
uint32_t __device_status = 0;
uint32_t __last_update = 0;
extern PCD_HandleTypeDef hpcd;
static int _NFC_status = 0;
static bool isLowFreq = 0;
+static bool _RequestComeFromNFC = false;
// #define IS_BUTTON_PRESSED() (0 == (LL_GPIO_ReadInputPort(SOLO_BUTTON_PORT) & SOLO_BUTTON_PIN))
static int is_physical_button_pressed()
@@ -57,6 +60,10 @@ static int is_touch_button_pressed()
int (*IS_BUTTON_PRESSED)() = is_physical_button_pressed;
+void request_from_nfc(bool request_active) {
+ _RequestComeFromNFC = request_active;
+}
+
// Timer6 overflow handler. happens every ~90ms.
void TIM6_DAC_IRQHandler()
{
@@ -70,6 +77,21 @@ void TIM6_DAC_IRQHandler()
ctaphid_update_status(__device_status);
}
}
+
+
+ if (is_touch_button_pressed == IS_BUTTON_PRESSED)
+ {
+ if (IS_BUTTON_PRESSED())
+ {
+ // Only allow 1 press per 25 ms.
+ if ((millis() - __last_button_bounce_time) > 25)
+ {
+ __last_button_press_time = millis();
+ }
+ __last_button_bounce_time = millis();
+ }
+ }
+
#ifndef IS_BOOTLOADER
// NFC sending WTX if needs
if (device_is_nfc() == NFC_IS_ACTIVE)
@@ -79,6 +101,21 @@ void TIM6_DAC_IRQHandler()
#endif
}
+// Interrupt on rising edge of button (button released)
+void EXTI0_IRQHandler(void)
+{
+ EXTI->PR1 = EXTI->PR1;
+ if (is_physical_button_pressed == IS_BUTTON_PRESSED)
+ {
+ // Only allow 1 press per 25 ms.
+ if ((millis() - __last_button_bounce_time) > 25)
+ {
+ __last_button_press_time = millis();
+ }
+ __last_button_bounce_time = millis();
+ }
+}
+
// Global USB interrupt handler
void USB_IRQHandler(void)
{
@@ -488,13 +525,49 @@ static int handle_packets()
return 0;
}
+static int wait_for_button_activate(uint32_t wait)
+{
+ int ret;
+ uint32_t start = millis();
+ do
+ {
+ if ((start + wait) < millis())
+ {
+ return 0;
+ }
+ delay(1);
+ ret = handle_packets();
+ if (ret)
+ return ret;
+ } while (!IS_BUTTON_PRESSED());
+ return 0;
+}
+static int wait_for_button_release(uint32_t wait)
+{
+ int ret;
+ uint32_t start = millis();
+ do
+ {
+ if ((start + wait) < millis())
+ {
+ return 0;
+ }
+ delay(1);
+ ret = handle_packets();
+ if (ret)
+ return ret;
+ } while (IS_BUTTON_PRESSED());
+ return 0;
+}
+
int ctap_user_presence_test(uint32_t up_delay)
{
int ret;
- if (device_is_nfc() == NFC_IS_ACTIVE)
+ if (device_is_nfc() == NFC_IS_ACTIVE || _RequestComeFromNFC)
{
return 1;
}
+
#if SKIP_BUTTON_CHECK_WITH_DELAY
int i=500;
while(i--)
@@ -507,53 +580,41 @@ int ctap_user_presence_test(uint32_t up_delay)
#elif SKIP_BUTTON_CHECK_FAST
delay(2);
ret = handle_packets();
- if (ret) return ret;
+ if (ret)
+ return ret;
goto done;
#endif
- uint32_t t1 = millis();
+
+ // If button was pressed within last [2] seconds, succeed.
+ if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
+ {
+ goto done;
+ }
+
+ // Set LED status and wait.
led_rgb(0xff3520);
-if (IS_BUTTON_PRESSED == is_touch_button_pressed)
-{
- // Wait for user to release touch button if it's already pressed
- while (IS_BUTTON_PRESSED())
- {
- if (t1 + up_delay < millis())
- {
- printf1(TAG_GEN,"Button not pressed\n");
- goto fail;
- }
- ret = handle_packets();
- if (ret) return ret;
- }
-}
-
-t1 = millis();
-
-do
-{
- if (t1 + up_delay < millis())
- {
- goto fail;
- }
- delay(1);
- ret = handle_packets();
+ // Block and wait for some time.
+ ret = wait_for_button_activate(up_delay);
+ if (ret) return ret;
+ ret = wait_for_button_release(up_delay);
if (ret) return ret;
-}
-while (! IS_BUTTON_PRESSED());
-led_rgb(0x001040);
-
-delay(50);
+ // If button was pressed within last [2] seconds, succeed.
+ if (__last_button_press_time && (millis() - __last_button_press_time < 2000))
+ {
+ goto done;
+ }
+
+
+ return 0;
-#if SKIP_BUTTON_CHECK_WITH_DELAY || SKIP_BUTTON_CHECK_FAST
done:
-#endif
-return 1;
+ ret = wait_for_button_release(up_delay);
+ __last_button_press_time = 0;
+ return 1;
-fail:
-return 0;
}
int ctap_generate_rng(uint8_t * dst, size_t num)
diff --git a/targets/stm32l432/src/init.c b/targets/stm32l432/src/init.c
index 93ca071..2475bdf 100644
--- a/targets/stm32l432/src/init.c
+++ b/targets/stm32l432/src/init.c
@@ -20,6 +20,7 @@
#include "stm32l4xx_ll_rng.h"
#include "stm32l4xx_ll_spi.h"
#include "stm32l4xx_ll_usb.h"
+#include "stm32l4xx_ll_exti.h"
#include "stm32l4xx_hal_pcd.h"
#include "stm32l4xx_hal.h"
@@ -851,19 +852,17 @@ void init_gpio(void)
LL_GPIO_SetPinMode(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_MODE_INPUT);
LL_GPIO_SetPinPull(SOLO_BUTTON_PORT,SOLO_BUTTON_PIN,LL_GPIO_PULL_UP);
-#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);
+#ifndef IS_BOOTLOADER
+ LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
+ LL_EXTI_InitTypeDef EXTI_InitStruct;
+ EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0; // GPIOA_0
+ EXTI_InitStruct.Line_32_63 = LL_EXTI_LINE_NONE;
+ EXTI_InitStruct.LineCommand = ENABLE;
+ EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
+ EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
+ LL_EXTI_Init(&EXTI_InitStruct);
+
+ NVIC_EnableIRQ(EXTI0_IRQn);
#endif
}
diff --git a/targets/stm32l432/src/nfc.c b/targets/stm32l432/src/nfc.c
index 51e34bb..1d935b0 100644
--- a/targets/stm32l432/src/nfc.c
+++ b/targets/stm32l432/src/nfc.c
@@ -14,6 +14,20 @@
#define IS_IRQ_ACTIVE() (1 == (LL_GPIO_ReadInputPort(SOLO_AMS_IRQ_PORT) & SOLO_AMS_IRQ_PIN))
+// chain buffer for 61XX responses
+static uint8_t resp_chain_buffer[2048] = {0};
+static size_t resp_chain_buffer_len = 0;
+
+uint8_t p14443_block_offset(uint8_t pcb) {
+ uint8_t offset = 1;
+ // NAD following
+ if (pcb & 0x04) offset++;
+ // CID following
+ if (pcb & 0x08) offset++;
+
+ return offset;
+}
+
// Capability container
const CAPABILITY_CONTAINER NFC_CC = {
.cclen_hi = 0x00, .cclen_lo = 0x0f,
@@ -82,19 +96,27 @@ int nfc_init()
return NFC_IS_NA;
}
+static uint8_t gl_int0 = 0;
void process_int0(uint8_t int0)
{
-
+ gl_int0 = int0;
}
bool ams_wait_for_tx(uint32_t timeout_ms)
{
+ if (gl_int0 & AMS_INT_TXE) {
+ uint8_t int0 = ams_read_reg(AMS_REG_INT0);
+ process_int0(int0);
+
+ return true;
+ }
+
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
uint8_t int0 = ams_read_reg(AMS_REG_INT0);
- if (int0) process_int0(int0);
- if (int0 & AMS_INT_TXE)
+ process_int0(int0);
+ if (int0 & AMS_INT_TXE || int0 & AMS_INT_RXE)
return true;
delay(1);
@@ -111,7 +133,13 @@ bool ams_receive_with_timeout(uint32_t timeout_ms, uint8_t * data, int maxlen, i
uint32_t tstart = millis();
while (tstart + timeout_ms > millis())
{
- uint8_t int0 = ams_read_reg(AMS_REG_INT0);
+ uint8_t int0 = 0;
+ if (gl_int0 & AMS_INT_RXE) {
+ int0 = gl_int0;
+ } else {
+ int0 = ams_read_reg(AMS_REG_INT0);
+ process_int0(int0);
+ }
uint8_t buffer_status2 = ams_read_reg(AMS_REG_BUF2);
if (buffer_status2 && (int0 & AMS_INT_RXE))
@@ -161,14 +189,25 @@ bool nfc_write_response_ex(uint8_t req0, uint8_t * data, uint8_t len, uint16_t r
if (len > 32 - 3)
return false;
- res[0] = NFC_CMD_IBLOCK | (req0 & 3);
+ res[0] = NFC_CMD_IBLOCK | (req0 & 0x0f);
+ res[1] = 0;
+ res[2] = 0;
+
+ uint8_t block_offset = p14443_block_offset(req0);
if (len && data)
- memcpy(&res[1], data, len);
+ memcpy(&res[block_offset], data, len);
- res[len + 1] = resp >> 8;
- res[len + 2] = resp & 0xff;
- nfc_write_frame(res, 3 + len);
+ res[len + block_offset + 0] = resp >> 8;
+ res[len + block_offset + 1] = resp & 0xff;
+
+ nfc_write_frame(res, block_offset + len + 2);
+
+ if (!ams_wait_for_tx(1))
+ {
+ printf1(TAG_NFC, "TX resp timeout. len: %d \r\n", len);
+ return false;
+ }
return true;
}
@@ -178,25 +217,28 @@ 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)
+void nfc_write_response_chaining_plain(uint8_t req0, uint8_t * data, int len)
{
uint8_t res[32 + 2];
- int sendlen = 0;
- uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 3);
+ uint8_t iBlock = NFC_CMD_IBLOCK | (req0 & 0x0f);
+ uint8_t block_offset = p14443_block_offset(req0);
if (len <= 31)
{
uint8_t res[32] = {0};
- res[0] = iBlock;
+ res[0] = iBlock;
if (len && data)
- memcpy(&res[1], data, len);
- nfc_write_frame(res, len + 1);
+ memcpy(&res[block_offset], data, len);
+ nfc_write_frame(res, len + block_offset);
} else {
+ int sendlen = 0;
do {
// transmit I block
- int vlen = MIN(31, len - sendlen);
- res[0] = iBlock;
- memcpy(&res[1], &data[sendlen], vlen);
+ int vlen = MIN(32 - block_offset, len - sendlen);
+ res[0] = iBlock;
+ res[1] = 0;
+ res[2] = 0;
+ memcpy(&res[block_offset], &data[sendlen], vlen);
// if not a last block
if (vlen + sendlen < len)
@@ -205,15 +247,15 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
}
// send data
- nfc_write_frame(res, vlen + 1);
+ nfc_write_frame(res, vlen + block_offset);
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 (!ams_wait_for_tx(5))
+ {
+ 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)
@@ -226,9 +268,10 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
break;
}
- if (reclen != 1)
+ uint8_t rblock_offset = p14443_block_offset(recbuf[0]);
+ if (reclen != rblock_offset)
{
- printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen,sendlen,len);
+ printf1(TAG_NFC, "R block length error. len: %d. %d/%d \r\n", reclen, sendlen, len);
dump_hex1(TAG_NFC, recbuf, reclen);
break;
}
@@ -245,6 +288,37 @@ void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len)
}
}
+void append_get_response(uint8_t *data, size_t rest_len)
+{
+ data[0] = 0x61;
+ data[1] = 0x00;
+ if (rest_len <= 0xff)
+ data[1] = rest_len & 0xff;
+}
+
+void nfc_write_response_chaining(uint8_t req0, uint8_t * data, int len, bool extapdu)
+{
+ resp_chain_buffer_len = 0;
+
+ // if we dont need to break data to parts that need to exchange via GET RESPONSE command (ISO 7816-4 7.1.3)
+ if (len <= 255 || extapdu)
+ {
+ nfc_write_response_chaining_plain(req0, data, len);
+ } else {
+ size_t pcklen = MIN(253, len);
+ resp_chain_buffer_len = len - pcklen;
+ printf1(TAG_NFC, "61XX chaining %d/%d.\r\n", pcklen, resp_chain_buffer_len);
+
+ memmove(resp_chain_buffer, data, pcklen);
+ append_get_response(&resp_chain_buffer[pcklen], resp_chain_buffer_len);
+
+ nfc_write_response_chaining_plain(req0, resp_chain_buffer, pcklen + 2); // 2 for 61XX
+
+ // put the rest data into chain buffer
+ memmove(resp_chain_buffer, &data[pcklen], resp_chain_buffer_len);
+ }
+}
+
// WTX on/off:
// sends/receives WTX frame to reader every `WTX_time` time in ms
// works via timer interrupts
@@ -297,7 +371,7 @@ bool WTX_off()
void WTX_timer_exec()
{
// condition: (timer on) or (not expired[300ms])
- if ((WTX_timer <= 0) || WTX_timer + 300 > millis())
+ if ((WTX_timer == 0) || WTX_timer + 300 > millis())
return;
WTX_process(10);
@@ -308,12 +382,12 @@ void WTX_timer_exec()
// read timeout must be 10 ms to call from interrupt
bool WTX_process(int read_timeout)
{
- uint8_t wtx[] = {0xf2, 0x01};
if (WTX_fail)
return false;
if (!WTX_sent)
{
+ uint8_t wtx[] = {0xf2, 0x01};
nfc_write_frame(wtx, sizeof(wtx));
WTX_sent = true;
return true;
@@ -371,39 +445,72 @@ int answer_rats(uint8_t parameter)
nfc_write_frame(res, sizeof(res));
- ams_wait_for_tx(10);
+ if (!ams_wait_for_tx(10))
+ {
+ printf1(TAG_NFC, "RATS TX timeout.\r\n");
+ ams_write_command(AMS_CMD_DEFAULT);
+ return 1;
+ }
return 0;
}
-void rblock_acknowledge()
+void rblock_acknowledge(uint8_t req0, bool ack)
{
- uint8_t buf[32];
+ uint8_t buf[32] = {0};
+
+ uint8_t block_offset = p14443_block_offset(req0);
NFC_STATE.block_num = !NFC_STATE.block_num;
- buf[0] = NFC_CMD_RBLOCK | NFC_STATE.block_num;
- nfc_write_frame(buf,1);
+
+ buf[0] = NFC_CMD_RBLOCK | (req0 & 0x0f);
+ if (ack)
+ buf[0] |= NFC_CMD_RBLOCK_ACK;
+
+ nfc_write_frame(buf, block_offset);
+}
+
+// international AID = RID:PIX
+// RID length == 5 bytes
+// usually aid length must be between 5 and 16 bytes
+int applet_cmp(uint8_t * aid, int len, uint8_t * const_aid, int const_len)
+{
+ if (len > const_len)
+ return 10;
+
+ // if international AID
+ if ((const_aid[0] & 0xf0) == 0xa0)
+ {
+ if (len < 5)
+ return 11;
+ return memcmp(aid, const_aid, MIN(len, const_len));
+ } else {
+ if (len != const_len)
+ return 11;
+
+ return memcmp(aid, const_aid, const_len);
+ }
}
// 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)
+ if (applet_cmp(aid, len, (uint8_t *)AID_FIDO, sizeof(AID_FIDO) - 1) == 0)
{
NFC_STATE.selected_applet = APP_FIDO;
return APP_FIDO;
}
- else if (memcmp(aid,AID_NDEF_TYPE_4,sizeof(AID_NDEF_TYPE_4)) == 0)
+ else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TYPE_4, sizeof(AID_NDEF_TYPE_4) - 1) == 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)
+ else if (applet_cmp(aid, len, (uint8_t *)AID_CAPABILITY_CONTAINER, sizeof(AID_CAPABILITY_CONTAINER) - 1) == 0)
{
NFC_STATE.selected_applet = APP_CAPABILITY_CONTAINER;
return APP_CAPABILITY_CONTAINER;
}
- else if (memcmp(aid,AID_NDEF_TAG,sizeof(AID_NDEF_TAG)) == 0)
+ else if (applet_cmp(aid, len, (uint8_t *)AID_NDEF_TAG, sizeof(AID_NDEF_TAG) - 1) == 0)
{
NFC_STATE.selected_applet = APP_NDEF_TAG;
return APP_NDEF_TAG;
@@ -413,25 +520,80 @@ int select_applet(uint8_t * aid, int len)
void nfc_process_iblock(uint8_t * buf, int len)
{
- APDU_HEADER * apdu = (APDU_HEADER *)(buf + 1);
- uint8_t * payload = buf + 1 + 5;
- uint8_t plen = apdu->lc;
int selected;
CTAP_RESPONSE ctap_resp;
int status;
+ uint16_t reslen;
- printf1(TAG_NFC,"Iblock: ");
- dump_hex1(TAG_NFC, buf, len);
+ uint8_t block_offset = p14443_block_offset(buf[0]);
+ APDU_STRUCT apdu;
+ if (apdu_decode(buf + block_offset, len - block_offset, &apdu)) {
+ printf1(TAG_NFC,"apdu decode error\r\n");
+ nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
+ return;
+ }
+ printf1(TAG_NFC,"apdu ok. %scase=%02x cla=%02x ins=%02x p1=%02x p2=%02x lc=%d le=%d\r\n",
+ apdu.extended_apdu ? "[e]":"", apdu.case_type, apdu.cla, apdu.ins, apdu.p1, apdu.p2, apdu.lc, apdu.le);
+
+ // check CLA
+ if (apdu.cla != 0x00 && apdu.cla != 0x80) {
+ printf1(TAG_NFC, "Unknown CLA %02x\r\n", apdu.cla);
+ nfc_write_response(buf[0], SW_CLA_INVALID);
+ return;
+ }
+
// TODO this needs to be organized better
- switch(apdu->ins)
+ switch(apdu.ins)
{
- case APDU_INS_SELECT:
- if (plen > len - 6)
+ // ISO 7816. 7.1 GET RESPONSE command
+ case APDU_GET_RESPONSE:
+ if (apdu.p1 != 0x00 || apdu.p2 != 0x00)
{
- printf1(TAG_ERR, "Truncating APDU length %d\r\n", apdu->lc);
- plen = len-6;
+ nfc_write_response(buf[0], SW_INCORRECT_P1P2);
+ printf1(TAG_NFC, "P1 or P2 error\r\n");
+ return;
}
+
+ // too many bytes needs. 0x00 and 0x100 - any length
+ if (apdu.le != 0 && apdu.le != 0x100 && apdu.le > resp_chain_buffer_len)
+ {
+ uint16_t wlresp = SW_WRONG_LENGTH; // here can be 6700, 6C00, 6FXX. but the most standard way - 67XX or 6700
+ if (resp_chain_buffer_len <= 0xff)
+ wlresp += resp_chain_buffer_len & 0xff;
+ nfc_write_response(buf[0], wlresp);
+ printf1(TAG_NFC, "buffer length less than requesteds\r\n");
+ return;
+ }
+
+ // create temporary packet
+ uint8_t pck[255] = {0};
+ size_t pcklen = 253;
+ if (apdu.le)
+ pcklen = apdu.le;
+ if (pcklen > resp_chain_buffer_len)
+ pcklen = resp_chain_buffer_len;
+
+ printf1(TAG_NFC, "GET RESPONSE. pck len: %d buffer len: %d\r\n", pcklen, resp_chain_buffer_len);
+
+ // create packet and add 61XX there if we have another portion(s) of data
+ memmove(pck, resp_chain_buffer, pcklen);
+ size_t dlen = 0;
+ if (resp_chain_buffer_len - pcklen)
+ {
+ append_get_response(&pck[pcklen], resp_chain_buffer_len - pcklen);
+ dlen = 2;
+ }
+
+ // send
+ nfc_write_response_chaining_plain(buf[0], pck, pcklen + dlen); // dlen for 61XX
+
+ // shift the buffer
+ resp_chain_buffer_len -= pcklen;
+ memmove(resp_chain_buffer, &resp_chain_buffer[pcklen], resp_chain_buffer_len);
+ break;
+
+ case APDU_INS_SELECT:
// if (apdu->p1 == 0 && apdu->p2 == 0x0c)
// {
// printf1(TAG_NFC,"Select NDEF\r\n");
@@ -446,14 +608,9 @@ void nfc_process_iblock(uint8_t * buf, int len)
// }
// else
{
- selected = select_applet(payload, plen);
+ selected = select_applet(apdu.data, apdu.lc);
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");
}
@@ -465,7 +622,7 @@ void nfc_process_iblock(uint8_t * buf, int len)
else
{
nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
- printf1(TAG_NFC, "NOT selected\r\n"); dump_hex1(TAG_NFC,payload, plen);
+ printf1(TAG_NFC, "NOT selected "); dump_hex1(TAG_NFC, apdu.data, apdu.lc);
}
}
break;
@@ -478,7 +635,8 @@ void nfc_process_iblock(uint8_t * buf, int len)
printf1(TAG_NFC, "U2F GetVersion command.\r\n");
- nfc_write_response_ex(buf[0], (uint8_t *)"U2F_V2", 6, SW_SUCCESS);
+ u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
+ nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length, apdu.extended_apdu);
break;
case APDU_FIDO_U2F_REGISTER:
@@ -489,9 +647,9 @@ void nfc_process_iblock(uint8_t * buf, int len)
printf1(TAG_NFC, "U2F Register command.\r\n");
- if (plen != 64)
+ if (apdu.lc != 64)
{
- printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", plen);
+ printf1(TAG_NFC, "U2F Register request length error. len=%d.\r\n", apdu.lc);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
@@ -502,19 +660,15 @@ void nfc_process_iblock(uint8_t * buf, int len)
// WTX_on(WTX_TIME_DEFAULT);
// SystemClock_Config_LF32();
// delay(300);
- if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_FAST);;
- u2f_request_nfc(&buf[1], len, &ctap_resp);
- if (device_is_nfc()) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);;
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_FAST);
+ u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &ctap_resp);
+ if (device_is_nfc() == NFC_IS_ACTIVE) device_set_clock_rate(DEVICE_LOW_POWER_IDLE);
// if (!WTX_off())
// return;
+ printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
printf1(TAG_NFC,"U2F Register P2 took %d\r\n", timestamp());
- nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length);
-
- // printf1(TAG_NFC, "U2F resp len: %d\r\n", ctap_resp.length);
-
-
-
+ nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length, apdu.extended_apdu);
printf1(TAG_NFC,"U2F Register answered %d (took %d)\r\n", millis(), timestamp());
break;
@@ -527,41 +681,43 @@ void nfc_process_iblock(uint8_t * buf, int len)
printf1(TAG_NFC, "U2F Authenticate command.\r\n");
- if (plen != 64 + 1 + buf[6 + 64])
+ if (apdu.lc != 64 + 1 + apdu.data[64])
{
delay(5);
- printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", plen, buf[6 + 64]);
+ printf1(TAG_NFC, "U2F Authenticate request length error. len=%d keyhlen=%d.\r\n", apdu.lc, apdu.data[64]);
nfc_write_response(buf[0], SW_WRONG_LENGTH);
return;
}
timestamp();
// WTX_on(WTX_TIME_DEFAULT);
- u2f_request_nfc(&buf[1], len, &ctap_resp);
+ u2f_request_nfc(&buf[block_offset], apdu.data, apdu.lc, &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);
+ nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length, apdu.extended_apdu);
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;
+ return;
}
printf1(TAG_NFC, "FIDO2 CTAP message. %d\r\n", timestamp());
- WTX_on(WTX_TIME_DEFAULT);
+ // WTX_on(WTX_TIME_DEFAULT);
+ request_from_nfc(true);
ctap_response_init(&ctap_resp);
- status = ctap_request(payload, plen, &ctap_resp);
- if (!WTX_off())
- return;
+ status = ctap_request(apdu.data, apdu.lc, &ctap_resp);
+ request_from_nfc(false);
+ // if (!WTX_off())
+ // return;
- printf1(TAG_NFC, "CTAP resp: 0x%02οΏ½ len: %d\r\n", status, ctap_resp.length);
+ printf1(TAG_NFC, "CTAP resp: 0x%02x len: %d\r\n", status, ctap_resp.length);
if (status == CTAP1_ERR_SUCCESS)
{
@@ -575,49 +731,45 @@ void nfc_process_iblock(uint8_t * buf, int len)
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);
+ nfc_write_response_chaining(buf[0], ctap_resp.data, ctap_resp.length, apdu.extended_apdu);
printf1(TAG_NFC,"CTAP answered %d (took %d)\r\n", millis(), timestamp());
break;
case APDU_INS_READ_BINARY:
-
-
+ // response length
+ reslen = apdu.le & 0xffff;
switch(NFC_STATE.selected_applet)
{
case APP_CAPABILITY_CONTAINER:
printf1(TAG_NFC,"APP_CAPABILITY_CONTAINER\r\n");
- if (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);
+ if (reslen == 0 || reslen > sizeof(NFC_CC))
+ reslen = sizeof(NFC_CC);
+ nfc_write_response_ex(buf[0], (uint8_t *)&NFC_CC, reslen, 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);
+ if (reslen == 0 || reslen > sizeof(NDEF_SAMPLE) - 1)
+ reslen = sizeof(NDEF_SAMPLE) - 1;
+ nfc_write_response_ex(buf[0], NDEF_SAMPLE, reslen, SW_SUCCESS);
ams_wait_for_tx(10);
break;
default:
+ nfc_write_response(buf[0], SW_FILE_NOT_FOUND);
printf1(TAG_ERR, "No binary applet selected!\r\n");
return;
break;
}
-
break;
+
default:
- printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu->ins);
+ printf1(TAG_NFC, "Unknown INS %02x\r\n", apdu.ins);
nfc_write_response(buf[0], SW_INS_INVALID);
break;
}
-
-
+
+ printf1(TAG_NFC,"prev.Iblock: ");
+ dump_hex1(TAG_NFC, buf, len);
}
static uint8_t ibuf[1024];
@@ -631,16 +783,24 @@ void clear_ibuf()
void nfc_process_block(uint8_t * buf, unsigned int len)
{
-
+ printf1(TAG_NFC, "-----\r\n");
if (!len)
return;
if (IS_PPSS_CMD(buf[0]))
{
- printf1(TAG_NFC, "NFC_CMD_PPSS\r\n");
+ printf1(TAG_NFC, "NFC_CMD_PPSS [%d] 0x%02x\r\n", len, (len > 2) ? buf[2] : 0);
+
+ if (buf[1] == 0x11 && (buf[2] & 0x0f) == 0x00) {
+ nfc_write_frame(buf, 1); // ack with correct start byte
+ } else {
+ printf1(TAG_NFC, "NFC_CMD_PPSS ERROR!!!\r\n");
+ nfc_write_frame((uint8_t*)"\x00", 1); // this should not happend. but iso14443-4 dont have NACK here, so just 0x00
+ }
}
else if (IS_IBLOCK(buf[0]))
{
+ uint8_t block_offset = p14443_block_offset(buf[0]);
if (buf[0] & 0x10)
{
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining blen=%d len=%d\r\n", ibuflen, len);
@@ -654,27 +814,27 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
printf1(TAG_NFC_APDU,"i> ");
dump_hex1(TAG_NFC_APDU, buf, len);
- if (len)
+ if (len > block_offset)
{
- memcpy(&ibuf[ibuflen], &buf[1], len - 1);
- ibuflen += len - 1;
+ memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
+ ibuflen += len - block_offset;
}
// send R block
- uint8_t rb = NFC_CMD_RBLOCK | NFC_CMD_RBLOCK_ACK | (buf[0] & 3);
- nfc_write_frame(&rb, 1);
+ rblock_acknowledge(buf[0], true);
} else {
if (ibuflen)
{
- if (len)
+ if (len > block_offset)
{
- memcpy(&ibuf[ibuflen], &buf[1], len - 1);
- ibuflen += len - 1;
+ memcpy(&ibuf[ibuflen], &buf[block_offset], len - block_offset);
+ ibuflen += len - block_offset;
}
- memmove(&ibuf[1], ibuf, ibuflen);
- ibuf[0] = buf[0];
- ibuflen++;
+ // add last chaining to top of the block
+ memmove(&ibuf[block_offset], ibuf, ibuflen);
+ memmove(ibuf, buf, block_offset);
+ ibuflen += block_offset;
printf1(TAG_NFC_APDU, "NFC_CMD_IBLOCK chaining last block. blen=%d len=%d\r\n", ibuflen, len);
@@ -683,7 +843,6 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
nfc_process_iblock(ibuf, ibuflen);
} else {
- // printf1(TAG_NFC, "NFC_CMD_IBLOCK\r\n");
nfc_process_iblock(buf, len);
}
clear_ibuf();
@@ -691,7 +850,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
}
else if (IS_RBLOCK(buf[0]))
{
- rblock_acknowledge();
+ rblock_acknowledge(buf[0], false);
printf1(TAG_NFC, "NFC_CMD_RBLOCK\r\n");
}
else if (IS_SBLOCK(buf[0]))
@@ -710,6 +869,7 @@ void nfc_process_block(uint8_t * buf, unsigned int len)
else
{
printf1(TAG_NFC, "NFC_CMD_SBLOCK, Unknown. len[%d]\r\n", len);
+ nfc_write_response(buf[0], SW_COND_USE_NOT_SATISFIED);
}
dump_hex1(TAG_NFC, buf, len);
}
@@ -728,6 +888,8 @@ int nfc_loop()
read_reg_block(&ams);
+ uint8_t old_int0 = gl_int0;
+ process_int0(ams.regs.int0);
uint8_t state = AMS_STATE_MASK & ams.regs.rfid_status;
if (state != AMS_STATE_SELECTED && state != AMS_STATE_SELECTEDX)
@@ -741,7 +903,7 @@ int nfc_loop()
// 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)
+ if (ams.regs.int0 & AMS_INT_INIT || old_int0 & AMS_INT_INIT)
{
nfc_state_init();
}
@@ -750,7 +912,7 @@ int nfc_loop()
// ams_print_int1(ams.regs.int1);
}
- if ((ams.regs.int0 & AMS_INT_RXE))
+ if (ams.regs.int0 & AMS_INT_RXE || old_int0 & AMS_INT_RXE)
{
if (ams.regs.buffer_status2)
{
@@ -779,6 +941,7 @@ int nfc_loop()
printf1(TAG_NFC, "NFC_CMD_WUPA\r\n");
break;
case NFC_CMD_HLTA:
+ ams_write_command(AMS_CMD_SLEEP);
printf1(TAG_NFC, "HLTA/Halt\r\n");
break;
case NFC_CMD_RATS:
diff --git a/targets/stm32l432/src/nfc.h b/targets/stm32l432/src/nfc.h
index 9871537..f284a8e 100644
--- a/targets/stm32l432/src/nfc.h
+++ b/targets/stm32l432/src/nfc.h
@@ -40,6 +40,8 @@ typedef struct
#define NFC_CMD_SBLOCK 0xc0
#define IS_SBLOCK(x) ( (((x) & 0xc0) == NFC_CMD_SBLOCK) && (((x) & 0x02) == 0x02) )
+extern uint8_t p14443_block_offset(uint8_t pcb);
+
#define NFC_SBLOCK_DESELECT 0x30
#define NFC_SBLOCK_WTX 0x30
diff --git a/tools/testing/main.py b/tools/testing/main.py
deleted file mode 100644
index 7e105e0..0000000
--- a/tools/testing/main.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# Copyright 2019 SoloKeys Developers
-#
-# Licensed under the Apache License, Version 2.0, or the MIT license , 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()
diff --git a/tools/testing/tests/__init__.py b/tools/testing/tests/__init__.py
deleted file mode 100644
index 23cf5a8..0000000
--- a/tools/testing/tests/__init__.py
+++ /dev/null
@@ -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
diff --git a/tools/testing/tests/fido2.py b/tools/testing/tests/fido2.py
deleted file mode 100644
index 778cfb2..0000000
--- a/tools/testing/tests/fido2.py
+++ /dev/null
@@ -1,1292 +0,0 @@
-from __future__ import print_function, absolute_import, unicode_literals
-import time
-from random import randint
-import array
-from functools import cmp_to_key
-
-from fido2 import cbor
-from fido2.ctap import CtapError
-
-from fido2.ctap2 import ES256, PinProtocolV1, AttestedCredentialData
-from fido2.utils import sha256, hmac_sha256
-from fido2.attestation import Attestation
-
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-
-from .u2f import U2FTests
-from .tester import Tester, Test
-from .util import shannon_entropy
-
-rp = {"id": "examplo.org", "name": "ExaRP"}
-rp2 = {"id": "solokeys.com", "name": "ExaRP"}
-user = {"id": b"usee_od", "name": "AB User"}
-user1 = {"id": b"1234567890", "name": "Conor Patrick"}
-user2 = {"id": b"oiewhfoi", "name": "Han Solo"}
-user3 = {"id": b"23ohfpjwo@@", "name": "John Smith"}
-challenge = "Y2hhbGxlbmdl"
-pin_protocol = 1
-key_params = [{"type": "public-key", "alg": ES256.ALGORITHM}]
-cdh = b"123456789abcdef0123456789abcdef0"
-
-
-def VerifyAttestation(attest, data):
- verifier = Attestation.for_type(attest.fmt)
- verifier().verify(attest.att_statement, attest.auth_data, data.hash)
-
-
-def cbor_key_to_representative(key):
- if isinstance(key, int):
- if key >= 0:
- return (0, key)
- return (1, -key)
- elif isinstance(key, bytes):
- return (2, key)
- elif isinstance(key, str):
- return (3, key)
- else:
- raise ValueError(key)
-
-
-def cbor_str_cmp(a, b):
- if isinstance(a, str) or isinstance(b, str):
- a = a.encode("utf8")
- b = b.encode("utf8")
-
- if len(a) == len(b):
- for x, y in zip(a, b):
- if x != y:
- return x - y
- return 0
- else:
- return len(a) - len(b)
-
-
-def cmp_cbor_keys(a, b):
- a = cbor_key_to_representative(a)
- b = cbor_key_to_representative(b)
- if a[0] != b[0]:
- return a[0] - b[0]
- if a[0] in (2, 3):
- return cbor_str_cmp(a[1], b[1])
- else:
- return (a[1] > b[1]) - (a[1] < b[1])
-
-
-def TestCborKeysSorted(cbor_obj):
- # Cbor canonical ordering of keys.
- # https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#ctap2-canonical-cbor-encoding-form
-
- if isinstance(cbor_obj, bytes):
- cbor_obj = cbor.loads(cbor_obj)[0]
-
- if isinstance(cbor_obj, dict):
- l = [x for x in cbor_obj]
- else:
- l = cbor_obj
-
- l_sorted = sorted(l[:], key=cmp_to_key(cmp_cbor_keys))
-
- for i in range(len(l)):
-
- if not isinstance(l[i], (str, int)):
- raise ValueError(f"Cbor map key {l[i]} must be int or str for CTAP2")
-
- if l[i] != l_sorted[i]:
- raise ValueError(f"Cbor map item {i}: {l[i]} is out of order")
-
- return l
-
-
-# hot patch cbor map parsing to test the order of keys in map
-_load_map_old = cbor.load_map
-
-
-def _load_map_new(ai, data):
- values, data = _load_map_old(ai, data)
- TestCborKeysSorted(values)
- return values, data
-
-
-cbor.load_map = _load_map_new
-cbor._DESERIALIZERS[5] = _load_map_new
-
-
-class FIDO2Tests(Tester):
- def __init__(self, tester=None):
- super().__init__(tester)
- self.self_test()
-
- def self_test(self,):
- cbor_key_list_sorted = [
- 0,
- 1,
- 1,
- 2,
- 3,
- -1,
- -2,
- "b",
- "c",
- "aa",
- "aaa",
- "aab",
- "baa",
- "bbb",
- ]
- with Test("Self test CBOR sorting"):
- TestCborKeysSorted(cbor_key_list_sorted)
-
- with Test("Self test CBOR sorting integers", catch=ValueError):
- TestCborKeysSorted([1, 0])
-
- with Test("Self test CBOR sorting major type", catch=ValueError):
- TestCborKeysSorted([-1, 0])
-
- with Test("Self test CBOR sorting strings", catch=ValueError):
- TestCborKeysSorted(["bb", "a"])
-
- with Test("Self test CBOR sorting same length strings", catch=ValueError):
- TestCborKeysSorted(["ab", "aa"])
-
- def run(self,):
- self.test_fido2()
-
- def test_fido2_simple(self, pin_token=None):
- creds = []
- exclude_list = []
- 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"})
-
- t1 = time.time() * 1000
- attest, data = self.client.make_credential(
- rp, user, challenge, pin=PIN, exclude_list=[]
- )
- t2 = time.time() * 1000
- VerifyAttestation(attest, data)
- print("Register time: %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 time: %d ms" % (t2 - t1))
-
- def test_extensions(self,):
-
- salt1 = b"\x5a" * 32
- salt2 = b"\x96" * 32
- salt3 = b"\x03" * 32
-
- # self.testReset()
-
- with Test("Get info has hmac-secret"):
- info = self.ctap.get_info()
- assert "hmac-secret" in info.extensions
-
- reg = self.testMC(
- "Send MC with hmac-secret ext set to true, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.SUCCESS,
- other={"extensions": {"hmac-secret": True}, "options": {"rk": True}},
- )
-
- with Test("Check 'hmac-secret' is set to true in auth_data extensions"):
- assert reg.auth_data.extensions
- assert "hmac-secret" in reg.auth_data.extensions
- assert reg.auth_data.extensions["hmac-secret"] == True
-
- reg = self.testMC(
- "Send MC with fake extension set to true, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.SUCCESS,
- other={"extensions": {"tetris": True}},
- )
-
- with Test("Get shared secret"):
- key_agreement, shared_secret = (
- self.client.pin_protocol._init_shared_secret()
- )
- cipher = Cipher(
- algorithms.AES(shared_secret),
- modes.CBC(b"\x00" * 16),
- default_backend(),
- )
-
- def get_salt_params(salts):
- enc = cipher.encryptor()
- salt_enc = b""
- for salt in salts:
- salt_enc += enc.update(salt)
- salt_enc += enc.finalize()
-
- salt_auth = hmac_sha256(shared_secret, salt_enc)[:16]
- return salt_enc, salt_auth
-
- for salt_list in ((salt1,), (salt1, salt2)):
- salt_enc, salt_auth = get_salt_params(salt_list)
-
- auth = self.testGA(
- "Send GA request with %d salts hmac-secret, expect success"
- % len(salt_list),
- rp["id"],
- cdh,
- other={
- "extensions": {
- "hmac-secret": {1: key_agreement, 2: salt_enc, 3: salt_auth}
- }
- },
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test(
- "Check that hmac-secret is in auth_data extensions and has %d bytes"
- % (len(salt_list) * 32)
- ):
- ext = auth.auth_data.extensions
- assert ext
- assert "hmac-secret" in ext
- assert isinstance(ext["hmac-secret"], bytes)
- assert len(ext["hmac-secret"]) == len(salt_list) * 32
-
- with Test("Check that shannon_entropy of hmac-secret is good"):
- ext = auth.auth_data.extensions
- dec = cipher.decryptor()
- key = dec.update(ext["hmac-secret"]) + dec.finalize()
-
- print(shannon_entropy(ext["hmac-secret"]))
- if len(salt_list) == 1:
- assert shannon_entropy(ext["hmac-secret"]) > 4.6
- assert shannon_entropy(key) > 4.6
- if len(salt_list) == 2:
- assert shannon_entropy(ext["hmac-secret"]) > 5.4
- assert shannon_entropy(key) > 5.4
-
- salt_enc, salt_auth = get_salt_params((salt3,))
-
- auth = self.testGA(
- "Send GA request with hmac-secret missing keyAgreement, expect error",
- rp["id"],
- cdh,
- other={"extensions": {"hmac-secret": {2: salt_enc, 3: salt_auth}}},
- )
- auth = self.testGA(
- "Send GA request with hmac-secret missing saltAuth, expect MISSING_PARAMETER",
- rp["id"],
- cdh,
- other={"extensions": {"hmac-secret": {1: key_agreement, 2: salt_enc}}},
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
- auth = self.testGA(
- "Send GA request with hmac-secret missing saltEnc, expect MISSING_PARAMETER",
- rp["id"],
- cdh,
- other={"extensions": {"hmac-secret": {1: key_agreement, 3: salt_auth}}},
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- bad_auth = list(salt_auth[:])
- bad_auth[len(bad_auth) // 2] = bad_auth[len(bad_auth) // 2] ^ 1
- bad_auth = bytes(bad_auth)
-
- auth = self.testGA(
- "Send GA request with hmac-secret containing bad saltAuth, expect EXTENSION_FIRST",
- rp["id"],
- cdh,
- other={
- "extensions": {
- "hmac-secret": {1: key_agreement, 2: salt_enc, 3: bad_auth}
- }
- },
- expectedError=CtapError.ERR.EXTENSION_FIRST,
- )
-
- salt4 = b"\x5a" * 16
- salt5 = b"\x96" * 64
- for salt_list in ((salt4,), (salt4, salt5)):
- salt_enc, salt_auth = get_salt_params(salt_list)
-
- salt_auth = hmac_sha256(shared_secret, salt_enc)[:16]
- auth = self.testGA(
- "Send GA request with incorrect salt length %d, expect INVALID_LENGTH"
- % len(salt_enc),
- rp["id"],
- cdh,
- other={
- "extensions": {
- "hmac-secret": {1: key_agreement, 2: salt_enc, 3: salt_auth}
- }
- },
- expectedError=CtapError.ERR.INVALID_LENGTH,
- )
-
- def test_get_info(self,):
- with Test("Get info"):
- info = self.ctap.get_info()
- print(bytes(info))
- print(cbor.loads(bytes(info)))
-
- with Test("Check FIDO2 string is in VERSIONS field"):
- assert "FIDO_2_0" in info.versions
-
- with Test("Check pin protocols field"):
- if len(info.pin_protocols):
- assert sum(info.pin_protocols) > 0
-
- with Test("Check options field"):
- for x in info.options:
- assert info.options[x] in [True, False]
-
- if "uv" in info.options:
- if info.options["uv"]:
- self.testMC(
- "Send MC request with uv set to true, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- other={"options": {"uv": True}},
- expectedError=CtapError.ERR.SUCCESS,
- )
- if "up" in info.options:
- if info.options["up"]:
- self.testMC(
- "Send MC request with up set to true, expect INVALID_OPTION",
- cdh,
- rp,
- user,
- key_params,
- other={"options": {"up": True}},
- expectedError=CtapError.ERR.INVALID_OPTION,
- )
-
- def test_make_credential(self,):
-
- prev_reg = self.testMC(
- "Send MC request, expect success",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- allow_list = [
- {
- "id": prev_reg.auth_data.credential_data.credential_id,
- "type": "public-key",
- }
- ]
- with Test("Check attestation format is correct"):
- assert prev_reg.fmt in ["packed", "tpm", "android-key", "adroid-safetynet"]
-
- with Test("Check auth_data is at least 77 bytes"):
- assert len(prev_reg.auth_data) >= 77
-
- self.testMC(
- "Send MC request with missing clientDataHash, expect error",
- None,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testMC(
- "Send MC request with integer for clientDataHash, expect error",
- 5,
- rp,
- user,
- key_params,
- )
-
- self.testMC(
- "Send MC request with missing user, expect error",
- cdh,
- rp,
- None,
- key_params,
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testMC(
- "Send MC request with bytearray user, expect error",
- cdh,
- rp,
- b"1234abcd",
- key_params,
- )
-
- self.testMC(
- "Send MC request with missing RP, expect error",
- cdh,
- None,
- user,
- key_params,
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testMC(
- "Send MC request with bytearray RP, expect error",
- cdh,
- b"1234abcd",
- user,
- key_params,
- )
-
- self.testMC(
- "Send MC request with missing pubKeyCredParams, expect error",
- cdh,
- rp,
- user,
- None,
- )
-
- self.testMC(
- "Send MC request with incorrect pubKeyCredParams, expect error",
- cdh,
- rp,
- user,
- b"2356",
- )
-
- self.testMC(
- "Send MC request with incorrect excludeList, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"exclude_list": 8},
- )
-
- self.testMC(
- "Send MC request with incorrect extensions, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"extensions": 8},
- )
-
- self.testMC(
- "Send MC request with incorrect options, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"options": 8},
- )
-
- self.testMC(
- "Send MC request with bad RP.name",
- cdh,
- {"id": self.host, "name": 8, "icon": "icon"},
- user,
- key_params,
- )
-
- self.testMC(
- "Send MC request with bad RP.id",
- cdh,
- {"id": 8, "name": "name", "icon": "icon"},
- user,
- key_params,
- )
-
- self.testMC(
- "Send MC request with bad RP.icon",
- cdh,
- {"id": self.host, "name": "name", "icon": 8},
- user,
- key_params,
- )
-
- self.testMC(
- "Send MC request with bad user.name",
- cdh,
- rp,
- {"id": b"usee_od", "name": 8},
- key_params,
- )
-
- self.testMC(
- "Send MC request with bad user.id",
- cdh,
- rp,
- {"id": "usee_od", "name": "name"},
- key_params,
- )
-
- self.testMC(
- "Send MC request with bad user.displayName",
- cdh,
- rp,
- {"id": "usee_od", "name": "name", "displayName": 8},
- key_params,
- )
-
- self.testMC(
- "Send MC request with bad user.icon",
- cdh,
- rp,
- {"id": "usee_od", "name": "name", "icon": 8},
- key_params,
- )
-
- self.testMC(
- "Send MC request with non-map pubKeyCredParams item",
- cdh,
- rp,
- user,
- ["wrong"],
- )
-
- self.testMC(
- "Send MC request with pubKeyCredParams item missing type field",
- cdh,
- rp,
- user,
- [{"alg": ES256.ALGORITHM}],
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testMC(
- "Send MC request with pubKeyCredParams item with bad type field",
- cdh,
- rp,
- user,
- [{"alg": ES256.ALGORITHM, "type": b"public-key"}],
- )
-
- self.testMC(
- "Send MC request with pubKeyCredParams item missing alg",
- cdh,
- rp,
- user,
- [{"type": "public-key"}],
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testMC(
- "Send MC request with pubKeyCredParams item with bad alg",
- cdh,
- rp,
- user,
- [{"alg": "7", "type": "public-key"}],
- )
-
- self.testMC(
- "Send MC request with pubKeyCredParams item with bogus alg, expect UNSUPPORTED_ALGORITHM",
- cdh,
- rp,
- user,
- [{"alg": 1234, "type": "public-key"}],
- expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
- )
-
- self.testMC(
- "Send MC request with pubKeyCredParams item with bogus type, expect UNSUPPORTED_ALGORITHM",
- cdh,
- rp,
- user,
- [{"alg": ES256.ALGORITHM, "type": "rot13"}],
- expectedError=CtapError.ERR.UNSUPPORTED_ALGORITHM,
- )
-
- self.testMC(
- "Send MC request with excludeList item with bogus type, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.SUCCESS,
- other={"exclude_list": [{"id": b"1234", "type": "rot13"}]},
- )
-
- self.testMC(
- "Send MC request with excludeList item with bogus type, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.SUCCESS,
- other={
- "exclude_list": [
- {"id": b"1234", "type": "mangoPapayaCoconutNotAPublicKey"}
- ]
- },
- )
-
- self.testMC(
- "Send MC request with excludeList with bad item, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"exclude_list": ["1234"]},
- )
-
- self.testMC(
- "Send MC request with excludeList with item missing type field, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"exclude_list": [{"id": b"1234"}]},
- )
-
- self.testMC(
- "Send MC request with excludeList with item missing id field, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"exclude_list": [{"type": "public-key"}]},
- )
-
- self.testMC(
- "Send MC request with excludeList with item containing bad id field, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"exclude_list": [{"type": "public-key", "id": "1234"}]},
- )
-
- self.testMC(
- "Send MC request with excludeList with item containing bad type field, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"exclude_list": [{"type": b"public-key", "id": b"1234"}]},
- )
-
- self.testMC(
- "Send MC request with excludeList containing previous registration, expect CREDENTIAL_EXCLUDED",
- cdh,
- rp,
- user,
- key_params,
- other={
- "exclude_list": [
- {
- "type": "public-key",
- "id": prev_reg.auth_data.credential_data.credential_id,
- }
- ]
- },
- expectedError=CtapError.ERR.CREDENTIAL_EXCLUDED,
- )
-
- self.testMC(
- "Send MC request with unknown option, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- other={"options": {"unknown": False}},
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- self.testReset()
-
- self.testGA(
- "Send GA request with reset auth, expect NO_CREDENTIALS",
- rp["id"],
- cdh,
- allow_list,
- expectedError=CtapError.ERR.NO_CREDENTIALS,
- )
-
- def test_get_assertion(self,):
-
- self.testReset()
-
- prev_reg = self.testMC(
- "Send MC request, expect success",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- allow_list = [
- {
- "id": prev_reg.auth_data.credential_data.credential_id,
- "type": "public-key",
- }
- ]
-
- prev_auth = self.testGA(
- "Send GA request, expect success",
- rp["id"],
- cdh,
- allow_list,
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Test auth_data is 37 bytes"):
- assert len(prev_auth.auth_data) == 37
-
- with Test("Test that auth_data.rpIdHash is correct"):
- assert sha256(rp["id"].encode()) == prev_auth.auth_data.rp_id_hash
-
- with Test("Check that AT flag is not set"):
- assert (prev_auth.auth_data.flags & 0xF8) == 0
-
- with Test("Test that user, credential and numberOfCredentials are not present"):
- assert prev_auth.user == None
- assert prev_auth.number_of_credentials == None
-
- self.testGA(
- "Send GA request with empty allow_list, expect NO_CREDENTIALS",
- rp["id"],
- cdh,
- [],
- expectedError=CtapError.ERR.NO_CREDENTIALS,
- )
-
- # apply bit flip
- badid = list(prev_reg.auth_data.credential_data.credential_id[:])
- badid[len(badid) // 2] = badid[len(badid) // 2] ^ 1
- badid = bytes(badid)
-
- self.testGA(
- "Send GA request with corrupt credId in allow_list, expect NO_CREDENTIALS",
- rp["id"],
- cdh,
- [{"id": badid, "type": "public-key"}],
- expectedError=CtapError.ERR.NO_CREDENTIALS,
- )
-
- self.testGA(
- "Send GA request with missing RPID, expect MISSING_PARAMETER",
- None,
- cdh,
- allow_list,
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testGA(
- "Send GA request with bad RPID, expect error",
- {"type": "wrong"},
- cdh,
- allow_list,
- )
-
- self.testGA(
- "Send GA request with missing clientDataHash, expect MISSING_PARAMETER",
- rp["id"],
- None,
- allow_list,
- expectedError=CtapError.ERR.MISSING_PARAMETER,
- )
-
- self.testGA(
- "Send GA request with bad clientDataHash, expect error",
- rp["id"],
- {"type": "wrong"},
- allow_list,
- )
-
- self.testGA(
- "Send GA request with bad allow_list, expect error",
- rp["id"],
- cdh,
- {"type": "wrong"},
- )
-
- self.testGA(
- "Send GA request with bad item in allow_list, expect error",
- rp["id"],
- cdh,
- allow_list + ["wrong"],
- )
-
- self.testGA(
- "Send GA request with unknown option, expect SUCCESS",
- rp["id"],
- cdh,
- allow_list,
- other={"options": {"unknown": True}},
- expectedError=CtapError.ERR.SUCCESS,
- )
- with Test("Get info"):
- info = self.ctap.get_info()
-
- if "uv" in info.options:
- if info.options["uv"]:
- res = self.testGA(
- "Send GA request with uv set to true, expect SUCCESS",
- rp["id"],
- cdh,
- allow_list,
- other={"options": {"uv": True}},
- expectedError=CtapError.ERR.SUCCESS,
- )
- with Test("Check that UV flag is set in response"):
- assert res.auth_data.flags & (1 << 2)
- if "up" in info.options:
- if info.options["up"]:
- res = self.testGA(
- "Send GA request with up set to true, expect SUCCESS",
- rp["id"],
- cdh,
- allow_list,
- other={"options": {"up": True}},
- expectedError=CtapError.ERR.SUCCESS,
- )
- with Test("Check that UP flag is set in response"):
- assert res.auth_data.flags & 1
-
- self.testGA(
- "Send GA request with bogus type item in allow_list, expect SUCCESS",
- rp["id"],
- cdh,
- allow_list + [{"type": "rot13", "id": b"1234"}],
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- self.testGA(
- "Send GA request with item missing type field in allow_list, expect error",
- rp["id"],
- cdh,
- allow_list + [{"id": b"1234"}],
- )
-
- self.testGA(
- "Send GA request with item containing bad type field in allow_list, expect error",
- rp["id"],
- cdh,
- allow_list + [{"type": b"public-key", "id": b"1234"}],
- )
-
- self.testGA(
- "Send GA request with item containing bad id in allow_list, expect error",
- rp["id"],
- cdh,
- allow_list + [{"type": b"public-key", "id": 42}],
- )
-
- self.testGA(
- "Send GA request with item missing id in allow_list, expect error",
- rp["id"],
- cdh,
- allow_list + [{"type": b"public-key"}],
- )
-
- self.testReset()
-
- appid = sha256(rp["id"].encode("utf8"))
- chal = sha256(challenge.encode("utf8"))
- with Test("Send CTAP1 register request"):
- u2f = U2FTests(self)
- reg = u2f.register(chal, appid)
- reg.verify(appid, chal)
-
- with Test("Authenticate CTAP1"):
- auth = u2f.authenticate(chal, appid, reg.key_handle)
- auth.verify(appid, chal, reg.public_key)
-
- auth = self.testGA(
- "Authenticate CTAP1 registration with CTAP2",
- rp["id"],
- cdh,
- [{"id": reg.key_handle, "type": "public-key"}],
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Check assertion is correct"):
- credential_data = AttestedCredentialData.from_ctap1(
- reg.key_handle, reg.public_key
- )
- auth.verify(cdh, credential_data.public_key)
- assert auth.credential["id"] == reg.key_handle
-
- def test_rk(self, pin_code=None):
-
- pin_auth = None
- if pin_code:
- pin_protocol = 1
- else:
- pin_protocol = None
- if pin_code:
- with Test("Set pin code"):
- self.client.pin_protocol.set_pin(pin_code)
- pin_token = self.client.pin_protocol.get_pin_token(pin_code)
- pin_auth = hmac_sha256(pin_token, cdh)[:16]
-
- self.testMC(
- "Send MC request with rk option set to true, expect SUCCESS",
- cdh,
- rp,
- user,
- key_params,
- other={
- "options": {"rk": True},
- "pin_auth": pin_auth,
- "pin_protocol": pin_protocol,
- },
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Get info"):
- info = self.ctap.get_info()
-
- options = {"rk": True}
- if "uv" in info.options and info.options["uv"]:
- options["uv"] = False
-
- for i, x in enumerate([user1, user2, user3]):
- self.testMC(
- "Send MC request with rk option set to true, expect SUCCESS %d/3"
- % (i + 1),
- cdh,
- rp2,
- x,
- key_params,
- other={
- "options": options,
- "pin_auth": pin_auth,
- "pin_protocol": pin_protocol,
- },
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- auth1 = self.testGA(
- "Send GA request with no allow_list, expect SUCCESS",
- rp2["id"],
- cdh,
- other={"pin_auth": pin_auth, "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Check that there are 3 credentials returned"):
- assert auth1.number_of_credentials == 3
-
- with Test("Get the next 2 assertions"):
- auth2 = self.ctap.get_next_assertion()
- auth3 = self.ctap.get_next_assertion()
-
- if not pin_code:
- with Test("Check only the user ID was returned"):
- assert "id" in auth1.user.keys() and len(auth1.user.keys()) == 1
- assert "id" in auth2.user.keys() and len(auth2.user.keys()) == 1
- assert "id" in auth3.user.keys() and len(auth3.user.keys()) == 1
- else:
- with Test("Check that all user info was returned"):
- for x in (auth1, auth2, auth3):
- for y in ("name", "icon", "displayName", "id"):
- if y not in x.user.keys():
- print("FAIL: %s was not in user: " % y, x.user)
-
- with Test("Send an extra getNextAssertion request, expect error"):
- try:
- self.ctap.get_next_assertion()
- assert 0
- except CtapError as e:
- print(e)
-
- def test_client_pin(self,):
- pin1 = "1234567890"
- self.test_rk(pin1)
-
- # PinProtocolV1
- res = self.testCP(
- "Test getKeyAgreement, expect SUCCESS",
- pin_protocol,
- PinProtocolV1.CMD.GET_KEY_AGREEMENT,
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Test getKeyAgreement has appropriate fields"):
- key = res[1]
- assert "Is public key" and key[1] == 2
- assert "Is P256" and key[-1] == 1
- assert "Is ALG_ECDH_ES_HKDF_256" and key[3] == -25
-
- assert "Right key" and len(key[-3]) == 32 and isinstance(key[-3], bytes)
-
- with Test("Test setting a new pin"):
- pin2 = "qwertyuiop\x11\x22\x33\x00123"
- self.client.pin_protocol.change_pin(pin1, pin2)
-
- with Test("Test getting new pin_auth"):
- pin_token = self.client.pin_protocol.get_pin_token(pin2)
- pin_auth = hmac_sha256(pin_token, cdh)[:16]
-
- res_mc = self.testMC(
- "Send MC request with new pin auth",
- cdh,
- rp,
- user,
- key_params,
- other={"pin_auth": pin_auth, "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Check UV flag is set"):
- assert res_mc.auth_data.flags & (1 << 2)
-
- res_ga = self.testGA(
- "Send GA request with pinAuth, expect SUCCESS",
- rp["id"],
- cdh,
- [
- {
- "type": "public-key",
- "id": res_mc.auth_data.credential_data.credential_id,
- }
- ],
- other={"pin_auth": pin_auth, "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Check UV flag is set"):
- assert res_ga.auth_data.flags & (1 << 2)
-
- res_ga = self.testGA(
- "Send GA request with no pinAuth, expect SUCCESS",
- rp["id"],
- cdh,
- [
- {
- "type": "public-key",
- "id": res_mc.auth_data.credential_data.credential_id,
- }
- ],
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Check UV flag is NOT set"):
- assert not (res_ga.auth_data.flags & (1 << 2))
-
- self.testReset()
-
- with Test("Test sending zero-length pin_auth, expect PIN_NOT_SET"):
- self.testMC(
- "Send MC request with new pin auth",
- cdh,
- rp,
- user,
- key_params,
- other={"pin_auth": b"", "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.PIN_NOT_SET,
- )
- self.testGA(
- "Send MC request with new pin auth",
- rp["id"],
- cdh,
- other={"pin_auth": b"", "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.PIN_NOT_SET,
- )
-
- with Test("Setting pin code, expect SUCCESS"):
- self.client.pin_protocol.set_pin(pin1)
-
- with Test("Test sending zero-length pin_auth, expect PIN_INVALID"):
- self.testMC(
- "Send MC request with new pin auth",
- cdh,
- rp,
- user,
- key_params,
- other={"pin_auth": b"", "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.PIN_INVALID,
- )
- self.testGA(
- "Send MC request with new pin auth",
- rp["id"],
- cdh,
- other={"pin_auth": b"", "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.PIN_INVALID,
- )
-
- self.testReset()
- with Test("Setting pin code >63 bytes, expect POLICY_VIOLATION "):
- try:
- self.client.pin_protocol.set_pin("A" * 64)
- assert 0
- except CtapError as e:
- assert e.code == CtapError.ERR.PIN_POLICY_VIOLATION
-
- with Test("Get pin token when no pin is set, expect PIN_NOT_SET"):
- try:
- self.client.pin_protocol.get_pin_token(pin1)
- assert 0
- except CtapError as e:
- assert e.code == CtapError.ERR.PIN_NOT_SET
-
- with Test("Get change pin when no pin is set, expect PIN_NOT_SET"):
- try:
- self.client.pin_protocol.change_pin(pin1, "1234")
- assert 0
- except CtapError as e:
- assert e.code == CtapError.ERR.PIN_NOT_SET
-
- with Test("Setting pin code and get pin_token, expect SUCCESS"):
- self.client.pin_protocol.set_pin(pin1)
- pin_token = self.client.pin_protocol.get_pin_token(pin1)
- pin_auth = hmac_sha256(pin_token, cdh)[:16]
-
- with Test("Get info and assert that clientPin is set to true"):
- info = self.ctap.get_info()
- assert info.options["clientPin"]
-
- with Test("Test setting pin again fails"):
- try:
- self.client.pin_protocol.set_pin(pin1)
- assert 0
- except CtapError as e:
- print(e)
-
- res_mc = self.testMC(
- "Send MC request with no pin_auth, expect PIN_REQUIRED",
- cdh,
- rp,
- user,
- key_params,
- expectedError=CtapError.ERR.PIN_REQUIRED,
- )
-
- res_mc = self.testGA(
- "Send GA request with no pin_auth, expect NO_CREDENTIALS",
- rp["id"],
- cdh,
- expectedError=CtapError.ERR.NO_CREDENTIALS,
- )
-
- res = self.testCP(
- "Test getRetries, expect SUCCESS",
- pin_protocol,
- PinProtocolV1.CMD.GET_RETRIES,
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Check there is 8 pin attempts left"):
- assert res[3] == 8
-
- # Flip 1 bit
- pin_wrong = list(pin1)
- c = pin1[len(pin1) // 2]
-
- pin_wrong[len(pin1) // 2] = chr(ord(c) ^ 1)
- pin_wrong = "".join(pin_wrong)
-
- for i in range(1, 3):
- self.testPP(
- "Get pin_token with wrong pin code, expect PIN_INVALID (%d/2)" % i,
- pin_wrong,
- expectedError=CtapError.ERR.PIN_INVALID,
- )
- print("Check there is %d pin attempts left" % (8 - i))
- res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES)
- assert res[3] == (8 - i)
- print("Pass")
-
- for i in range(1, 3):
- self.testPP(
- "Get pin_token with wrong pin code, expect PIN_AUTH_BLOCKED %d/2" % i,
- pin_wrong,
- expectedError=CtapError.ERR.PIN_AUTH_BLOCKED,
- )
-
- self.reboot()
-
- with Test("Get pin_token, expect SUCCESS"):
- pin_token = self.client.pin_protocol.get_pin_token(pin1)
- pin_auth = hmac_sha256(pin_token, cdh)[:16]
-
- res_mc = self.testMC(
- "Send MC request with correct pin_auth",
- cdh,
- rp,
- user,
- key_params,
- other={"pin_auth": pin_auth, "pin_protocol": pin_protocol},
- expectedError=CtapError.ERR.SUCCESS,
- )
-
- with Test("Test getRetries resets to 8"):
- res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES)
- assert res[3] == (8)
-
- for i in range(1, 10):
- err = CtapError.ERR.PIN_INVALID
- if i in (3, 6):
- err = CtapError.ERR.PIN_AUTH_BLOCKED
- elif i >= 8:
- err = [CtapError.ERR.PIN_BLOCKED, CtapError.ERR.PIN_INVALID]
- self.testPP(
- "Lock out authentictor and check correct error codes %d/9" % i,
- pin_wrong,
- expectedError=err,
- )
-
- attempts = 8 - i
- if i > 8:
- attempts = 0
-
- with Test("Check there is %d pin attempts left" % attempts):
- res = self.ctap.client_pin(pin_protocol, PinProtocolV1.CMD.GET_RETRIES)
- assert res[3] == attempts
-
- if err == CtapError.ERR.PIN_AUTH_BLOCKED:
- self.reboot()
-
- res_mc = self.testMC(
- "Send MC request with correct pin_auth, expect error",
- cdh,
- rp,
- user,
- key_params,
- other={"pin_auth": pin_auth, "pin_protocol": pin_protocol},
- )
-
- self.reboot()
-
- self.testPP(
- "Get pin_token with correct pin code, expect PIN_BLOCKED",
- pin1,
- expectedError=CtapError.ERR.PIN_BLOCKED,
- )
-
- def test_fido2(self,):
-
- self.testReset()
-
- self.test_get_info()
-
- self.test_get_assertion()
-
- self.test_make_credential()
-
- self.test_rk(None)
-
- self.test_client_pin()
-
- self.testReset()
-
- self.test_extensions()
-
- print("Done")
diff --git a/tools/testing/tests/hid.py b/tools/testing/tests/hid.py
deleted file mode 100644
index 74d9d92..0000000
--- a/tools/testing/tests/hid.py
+++ /dev/null
@@ -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
diff --git a/tools/testing/tests/solo.py b/tools/testing/tests/solo.py
deleted file mode 100644
index 8853f0d..0000000
--- a/tools/testing/tests/solo.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from solo.client import SoloClient
-from solo.commands import SoloExtension
-
-from fido2.ctap1 import ApduError
-from fido2.utils import sha256
-
-from .util import shannon_entropy
-from .tester import Tester, Test
-
-
-class SoloTests(Tester):
- def __init__(self, tester=None):
- super().__init__(tester)
-
- def run(self,):
- self.test_solo()
-
- def test_solo(self,):
- """
- Solo specific tests
- """
- # RNG command
- sc = SoloClient()
- sc.find_device(self.dev)
- sc.use_u2f()
- memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
-
- total = 1024 * 16
- with Test("Gathering %d random bytes..." % total):
- entropy = b""
- while len(entropy) < total:
- entropy += sc.get_rng()
-
- with Test("Test entropy is close to perfect"):
- s = shannon_entropy(entropy)
- assert s > 7.98
- print("Entropy is %.5f bits per byte." % s)
-
- with Test("Test Solo version command"):
- assert len(sc.solo_version()) == 3
-
- with Test("Test bootloader is not active"):
- try:
- sc.write_flash(memmap[0], b"1234")
- except ApduError:
- pass
-
- sc.exchange = sc.exchange_fido2
-
- req = SoloClient.format_request(SoloExtension.version, 0, b"A" * 16)
- a = sc.ctap2.get_assertion(
- sc.host, b"B" * 32, [{"id": req, "type": "public-key"}]
- )
-
- with Test("Test custom command returned valid assertion"):
- assert a.auth_data.rp_id_hash == sha256(sc.host.encode("utf8"))
- assert a.credential["id"] == req
- assert (a.auth_data.flags & 0x5) == 0x5
-
- with Test("Test Solo version and random commands with fido2 layer"):
- assert len(sc.solo_version()) == 3
- sc.get_rng()
-
- def test_bootloader(self,):
- sc = SoloClient()
- sc.find_device(self.dev)
- sc.use_u2f()
-
- memmap = (0x08005000, 0x08005000 + 198 * 1024 - 8)
- data = b"A" * 64
-
- with Test("Test version command"):
- assert len(sc.bootloader_version()) == 3
-
- with Test("Test write command"):
- sc.write_flash(memmap[0], data)
-
- for addr in (memmap[0] - 8, memmap[0] - 4, memmap[1], memmap[1] - 8):
- with Test("Test out of bounds write command at 0x%04x" % addr):
- try:
- sc.write_flash(addr, data)
- except CtapError as e:
- assert e.code == CtapError.ERR.NOT_ALLOWED
diff --git a/tools/testing/tests/tester.py b/tools/testing/tests/tester.py
deleted file mode 100644
index 10cd1d3..0000000
--- a/tools/testing/tests/tester.py
+++ /dev/null
@@ -1,204 +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, catch=None):
- self.msg = msg
- self.catch = catch
-
- def __enter__(self,):
- print(self.msg)
-
- def __exit__(self, a, b, c):
- if self.catch is None:
- print("Pass")
- elif isinstance(b, self.catch):
- print("Pass")
- return b
- else:
- raise RuntimeError(f"Expected exception {self.catch} did not occur.")
-
-
-class Tester:
- def __init__(self, tester=None):
- self.origin = "https://examplo.org"
- self.host = "examplo.org"
- self.user_count = 10
- self.is_sim = False
- if tester:
- self.initFromTester(tester)
-
- def initFromTester(self, tester):
- self.user_count = tester.user_count
- self.is_sim = tester.is_sim
- self.dev = tester.dev
- self.ctap = tester.ctap
- self.ctap1 = tester.ctap1
- self.client = tester.client
-
- def find_device(self,):
- print(list(CtapHidDevice.list_devices()))
- dev = next(CtapHidDevice.list_devices(), None)
- if not dev:
- raise RuntimeError("No FIDO device found")
- self.dev = dev
- self.client = Fido2Client(dev, self.origin)
- self.ctap = self.client.ctap2
- self.ctap1 = CTAP1(dev)
-
- # consume timeout error
- # cmd,resp = self.recv_raw()
-
- def set_user_count(self, count):
- self.user_count = count
-
- def set_sim(self, b):
- self.is_sim = b
-
- def reboot(self,):
- if self.is_sim:
- print("Sending restart command...")
- self.send_magic_reboot()
- Tester.delay(0.25)
- else:
- print("Please reboot authentictor and hit enter")
- input()
- self.find_device()
-
- def send_data(self, cmd, data):
- if not isinstance(data, bytes):
- data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
- with Timeout(1.0) as event:
- return self.dev.call(cmd, data, event)
-
- def send_raw(self, data, cid=None):
- if cid is None:
- cid = self.dev._dev.cid
- elif not isinstance(cid, bytes):
- cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
- if not isinstance(data, bytes):
- data = struct.pack("%dB" % len(data), *[ord(x) for x in data])
- data = cid + data
- l = len(data)
- if l != 64:
- pad = "\x00" * (64 - l)
- pad = struct.pack("%dB" % len(pad), *[ord(x) for x in pad])
- data = data + pad
- data = list(data)
- assert len(data) == 64
- self.dev._dev.InternalSendPacket(Packet(data))
-
- def send_magic_reboot(self,):
- """
- For use in simulation and testing. Random bytes that authentictor should detect
- and then restart itself.
- """
- magic_cmd = (
- b"\xac\x10\x52\xca\x95\xe5\x69\xde\x69\xe0\x2e\xbf"
- + b"\xf3\x33\x48\x5f\x13\xf9\xb2\xda\x34\xc5\xa8\xa3"
- + b"\x40\x52\x66\x97\xa9\xab\x2e\x0b\x39\x4d\x8d\x04"
- + b"\x97\x3c\x13\x40\x05\xbe\x1a\x01\x40\xbf\xf6\x04"
- + b"\x5b\xb2\x6e\xb7\x7a\x73\xea\xa4\x78\x13\xf6\xb4"
- + b"\x9a\x72\x50\xdc"
- )
- self.dev._dev.InternalSendPacket(Packet(magic_cmd))
-
- def cid(self,):
- return self.dev._dev.cid
-
- def set_cid(self, cid):
- if not isinstance(cid, (bytes, bytearray)):
- cid = struct.pack("%dB" % len(cid), *[ord(x) for x in cid])
- self.dev._dev.cid = cid
-
- def recv_raw(self,):
- with Timeout(1.0):
- cmd, payload = self.dev._dev.InternalRecv()
- return cmd, payload
-
- def check_error(data, err=None):
- assert len(data) == 1
- if err is None:
- if data[0] != 0:
- raise CtapError(data[0])
- elif data[0] != err:
- raise ValueError("Unexpected error: %02x" % data[0])
-
- def testFunc(self, func, test, *args, **kwargs):
- with Test(test):
- res = None
- expectedError = kwargs.get("expectedError", None)
- otherArgs = kwargs.get("other", {})
- try:
- res = func(*args, **otherArgs)
- if expectedError != CtapError.ERR.SUCCESS:
- raise RuntimeError("Expected error to occur for test: %s" % test)
- except CtapError as e:
- if expectedError is not None:
- cond = e.code != expectedError
- if isinstance(expectedError, list):
- cond = e.code not in expectedError
- else:
- expectedError = [expectedError]
- if cond:
- raise RuntimeError(
- f"Got error code {hex(e.code)}, expected {[hex(x) for x in expectedError]}"
- )
- else:
- print(e)
- return res
-
- def testReset(self,):
- print("Resetting Authenticator...")
- try:
- self.ctap.reset()
- except CtapError:
- # Some authenticators need a power cycle
- print("You must power cycle authentictor. Hit enter when done.")
- input()
- time.sleep(0.2)
- self.find_device()
- self.ctap.reset()
-
- def testMC(self, test, *args, **kwargs):
- return self.testFunc(self.ctap.make_credential, test, *args, **kwargs)
-
- def testGA(self, test, *args, **kwargs):
- return self.testFunc(self.ctap.get_assertion, test, *args, **kwargs)
-
- def testCP(self, test, *args, **kwargs):
- return self.testFunc(self.ctap.client_pin, test, *args, **kwargs)
-
- def testPP(self, test, *args, **kwargs):
- return self.testFunc(
- self.client.pin_protocol.get_pin_token, test, *args, **kwargs
- )
-
- def delay(secs):
- time.sleep(secs)
diff --git a/tools/testing/tests/u2f.py b/tools/testing/tests/u2f.py
deleted file mode 100644
index d1e8033..0000000
--- a/tools/testing/tests/u2f.py
+++ /dev/null
@@ -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
diff --git a/tools/testing/tests/util.py b/tools/testing/tests/util.py
deleted file mode 100644
index 94c3c45..0000000
--- a/tools/testing/tests/util.py
+++ /dev/null
@@ -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