Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4339600730 | ||
|
|
298db9ea99 | ||
|
|
ba0d717d88 | ||
|
|
0185d1e443 | ||
|
|
191d043cc4 | ||
|
|
6b8aa3aaf3 | ||
|
|
5fdc6e0739 | ||
|
|
af763450a9 | ||
|
|
eaeb927d92 |
4
.github/workflows/cargo_audit.yml
vendored
4
.github/workflows/cargo_audit.yml
vendored
@@ -8,10 +8,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'google/OpenSK'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "true"
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up OpenSK
|
||||
|
||||
6
.github/workflows/cargo_bloat.yml
vendored
6
.github/workflows/cargo_bloat.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Setup
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- uses: actions-rs/cargo@v1
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
args: cargo-bloat
|
||||
|
||||
# First run: PR
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up OpenSK
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no -C link-arg=-Tnrf52840_layout.ld" cargo bloat --release --target=thumbv7em-none-eabi --features=config_command,with_ctap1 --crates >> .github/workflows/bloat_output_new.txt
|
||||
|
||||
# Second run: PR
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
ref: ${{ github.base_ref }}
|
||||
|
||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -23,8 +23,8 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- run: ./setup.sh
|
||||
|
||||
2
.github/workflows/cifuzz.yml
vendored
2
.github/workflows/cifuzz.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
fuzz-seconds: 600
|
||||
dry-run: false
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v1
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
|
||||
4
.github/workflows/coveralls.yml
vendored
4
.github/workflows/coveralls.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "true"
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up OpenSK
|
||||
|
||||
8
.github/workflows/mdlint.yml
vendored
8
.github/workflows/mdlint.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
mdlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: markdownlint-cli
|
||||
uses: nosborn/github-action-markdown-cli@v1.1.1
|
||||
uses: nosborn/github-action-markdown-cli@v3
|
||||
with:
|
||||
files: '**/*.md'
|
||||
ignore_files: "third_party/*"
|
||||
config_file: ".markdownlint.json"
|
||||
config_file: '.markdownlint.json'
|
||||
ignore_files: 'third_party/*'
|
||||
|
||||
6
.github/workflows/reproducible.yml
vendored
6
.github/workflows/reproducible.yml
vendored
@@ -13,12 +13,12 @@ jobs:
|
||||
fail-fast: false
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: "true"
|
||||
- name: Install Rust toolchain
|
||||
run: rustup show
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up OpenSK
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
run: ./maintainers/reproduce_hashes.sh
|
||||
|
||||
- name: Upload reproduced binaries
|
||||
uses: actions/upload-artifact@v1
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: reproduced-${{ matrix.os }}
|
||||
path: reproducible/reproduced.tar
|
||||
|
||||
@@ -1,35 +1,22 @@
|
||||
{
|
||||
"default": true,
|
||||
"heading-style": {
|
||||
"MD003": {
|
||||
"style": "atx"
|
||||
},
|
||||
"no-trailing-spaces": {
|
||||
"MD007": {
|
||||
"indent": 4
|
||||
},
|
||||
"MD009": {
|
||||
"br_spaces": 0,
|
||||
"strict": true
|
||||
},
|
||||
"ul-indent": {
|
||||
"indent": 4
|
||||
},
|
||||
"line-length": {
|
||||
"MD013": {
|
||||
"line_length": 80,
|
||||
"code_blocks": false
|
||||
},
|
||||
"list-marker-space": {
|
||||
"ol_single": 2,
|
||||
"ol_multi": 2,
|
||||
"ul_single": 3,
|
||||
"ul_multi": 3
|
||||
},
|
||||
"no-inline-html": {
|
||||
"MD033": {
|
||||
"allowed_elements": [
|
||||
"img"
|
||||
]
|
||||
},
|
||||
"fenced-code-language": true,
|
||||
"code-block-style": {
|
||||
"style": "fenced"
|
||||
},
|
||||
"code-fence-style": {
|
||||
"style": "backtick"
|
||||
"img"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
This bootloader supports upgradability for OpenSK. Its functionality is to
|
||||
|
||||
- check images on A/B partitions,
|
||||
- boot the most recent valid partition.
|
||||
- check images on A/B partitions,
|
||||
- boot the most recent valid partition.
|
||||
|
||||
## How to use
|
||||
|
||||
|
||||
26
deploy.py
26
deploy.py
@@ -23,6 +23,7 @@ import argparse
|
||||
import collections
|
||||
import copy
|
||||
import os
|
||||
from serial.tools import list_ports
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -202,6 +203,12 @@ def assert_python_library(module: str):
|
||||
f"Try to run: pip3 install {module}"))
|
||||
|
||||
|
||||
def list_serials(vid: int, pid: int) -> List[str]:
|
||||
ports = list_ports.comports()
|
||||
ports = filter(lambda p: p.vid == vid and p.pid == pid, ports)
|
||||
return list(map(lambda p: p.serial_number, ports))
|
||||
|
||||
|
||||
class RemoveConstAction(argparse.Action):
|
||||
|
||||
# pylint: disable=redefined-builtin
|
||||
@@ -703,10 +710,10 @@ class OpenSKInstaller:
|
||||
fatal("This board doesn't seem to support flashing through pyocd.")
|
||||
|
||||
if self.args.programmer == "nordicdfu":
|
||||
assert_mandatory_binary("nrfutil")
|
||||
assert_python_library("intelhex")
|
||||
assert_python_library("nordicsemi.lister")
|
||||
nrfutil_version = __import__("nordicsemi.version").version.NRFUTIL_VERSION
|
||||
assert_mandatory_binary("nrfutil")
|
||||
nrfutil_version = self.checked_command_output(["nrfutil", "version"])
|
||||
nrfutil_version = nrfutil_version.removeprefix("nrfutil version ")
|
||||
if not nrfutil_version.startswith("6."):
|
||||
fatal(("You need to install nrfutil python3 package v6.0 or above. "
|
||||
f"Found: v{nrfutil_version}. If you use Python >= 3.11, please "
|
||||
@@ -812,22 +819,17 @@ class OpenSKInstaller:
|
||||
info("Press [ENTER] when ready.")
|
||||
_ = input()
|
||||
# Search for the DFU devices
|
||||
serial_number = []
|
||||
# pylint: disable=g-import-not-at-top,import-outside-toplevel
|
||||
from nordicsemi.lister import device_lister
|
||||
for device in device_lister.DeviceLister().enumerate():
|
||||
if device.vendor_id == "1915" and device.product_id == "521F":
|
||||
serial_number.append(device.serial_number)
|
||||
if not serial_number:
|
||||
serial_numbers = list_serials(0x1915, 0x521F)
|
||||
if not serial_numbers:
|
||||
fatal("Couldn't find any DFU device on your system.")
|
||||
if len(serial_number) > 1:
|
||||
if len(serial_numbers) > 1:
|
||||
fatal("Multiple DFU devices are detected. Please only connect one.")
|
||||
# Run the command without capturing stdout so that we show progress
|
||||
info("Flashing device using DFU...")
|
||||
dfu_return_code = subprocess.run(
|
||||
[
|
||||
"nrfutil", "dfu", "usb-serial", f"--package={dfu_pkg_file}",
|
||||
f"--serial-number={serial_number[0]}"
|
||||
f"--serial-number={serial_numbers[0]}"
|
||||
],
|
||||
check=False,
|
||||
timeout=None,
|
||||
|
||||
@@ -19,6 +19,9 @@ customize it.
|
||||
|
||||
### Flashing using DFU (preferred method)
|
||||
|
||||
You need `nrfutil` version 6. The [install manual](../install.md) has
|
||||
setup instructions.
|
||||
|
||||
To flash the firmware, run:
|
||||
|
||||
```shell
|
||||
|
||||
@@ -7,18 +7,18 @@
|
||||
All the generated certificates and private keys are stored in the directory
|
||||
`crypto_data/`. The expected content after running our `setup.sh` script is:
|
||||
|
||||
File | Purpose
|
||||
------------------------ | --------------------------------------------------------
|
||||
`aaguid.txt` | Text file containaing the AAGUID value
|
||||
`opensk_ca.csr` | Certificate sign request for the Root CA
|
||||
`opensk_ca.key` | ECC secp256r1 private key used for the Root CA
|
||||
`opensk_ca.pem` | PEM encoded certificate of the Root CA
|
||||
`opensk_ca.srl` | File generated by OpenSSL
|
||||
`opensk_cert.csr` | Certificate sign request for the attestation certificate
|
||||
`opensk_cert.pem` | PEM encoded certificate used for the authenticator
|
||||
`opensk.key` | ECC secp256r1 private key used for the autenticator
|
||||
`opensk_upgrade.key` | Private key for signing upgrades through CTAP
|
||||
`opensk_upgrade_pub.pem` | Public key added to the firmware for verifying upgrades
|
||||
| File | Purpose |
|
||||
| ------------------------ | ----------------------------------------------- |
|
||||
| `aaguid.txt` | Text file containaing the AAGUID value |
|
||||
| `opensk_ca.csr` | Certificate sign request for the Root CA |
|
||||
| `opensk_ca.key` | ECC secp256r1 private key used for the Root CA |
|
||||
| `opensk_ca.pem` | PEM encoded certificate of the Root CA |
|
||||
| `opensk_ca.srl` | File generated by OpenSSL |
|
||||
| `opensk_cert.csr` | CSR for attestation certificate |
|
||||
| `opensk_cert.pem` | PEM encoded certificate for the authenticator |
|
||||
| `opensk.key` | ECC secp256r1 private key for the autenticator |
|
||||
| `opensk_upgrade.key` | Private key for signing upgrades through CTAP |
|
||||
| `opensk_upgrade_pub.pem` | Public key for verifying upgrades |
|
||||
|
||||
If you want to use your own attestation certificate and private key,
|
||||
replace the `opensk_cert.pem` and `opensk.key` files. The script at
|
||||
@@ -50,20 +50,20 @@ carefully if you want to take this privacy risk.
|
||||
If you build your own security key, depending on the hardware you use, there are
|
||||
a few things you can personalize:
|
||||
|
||||
1. If you have multiple buttons, choose the buttons responsible for user
|
||||
presence in `src/main.rs`.
|
||||
1. If you have colored LEDs, like different blinking patterns and want to play
|
||||
around with the code in `src/main.rs` more, take a look at e.g. `wink_leds`.
|
||||
1. You find more options and documentation in `src/ctap/customization.rs`,
|
||||
including:
|
||||
* The default level for the credProtect extension.
|
||||
* The default minimum PIN length, and what relying parties can set it.
|
||||
* Whether you want to enforce alwaysUv.
|
||||
* Settings for enterprise attestation.
|
||||
* The maximum PIN retries.
|
||||
* Whether you want to use batch attestation.
|
||||
* Whether you want to use signature counters.
|
||||
* Various constants to adapt to different hardware.
|
||||
1. If you have multiple buttons, choose the buttons responsible for user
|
||||
presence in `src/main.rs`.
|
||||
1. If you have colored LEDs, like different blinking patterns and want to play
|
||||
around with the code in `src/main.rs` more, take a look at e.g. `wink_leds`.
|
||||
1. You find more options and documentation in `src/ctap/customization.rs`,
|
||||
including:
|
||||
* The default level for the credProtect extension.
|
||||
* The default minimum PIN length, and what relying parties can set it.
|
||||
* Whether you want to enforce alwaysUv.
|
||||
* Settings for enterprise attestation.
|
||||
* The maximum PIN retries.
|
||||
* Whether you want to use batch attestation.
|
||||
* Whether you want to use signature counters.
|
||||
* Various constants to adapt to different hardware.
|
||||
|
||||
### Testing and Fuzzing
|
||||
|
||||
|
||||
@@ -59,15 +59,15 @@ JLinkRTTClient
|
||||
You can enhance the debug output by adding flags to the deploy command (see
|
||||
below for details):
|
||||
|
||||
* `--debug`: more debug messages
|
||||
* `--panic-console`: add panic messages
|
||||
* `--debug-allocations`: print information about the used heap
|
||||
* `--debug`: more debug messages
|
||||
* `--panic-console`: add panic messages
|
||||
* `--debug-allocations`: print information about the used heap
|
||||
|
||||
Adding debugging to your firmware increases resource usage, including
|
||||
|
||||
* USB communication speed
|
||||
* RAM usage
|
||||
* binary size
|
||||
* USB communication speed
|
||||
* RAM usage
|
||||
* binary size
|
||||
|
||||
Depending on your choice of board, you may have to increase the available stack
|
||||
for kernel or app, or disable features so that the binary fits the flash. Also
|
||||
@@ -107,31 +107,3 @@ alloc[256, 1] = 0x2002401c (2 ptrs, 384 bytes)
|
||||
# After this operation, 1 pointers are allocated, totalling 512 bytes.
|
||||
dealloc[64, 1] = 0x2002410c (1 ptrs, 512 bytes)
|
||||
```
|
||||
|
||||
A tool is provided to analyze such reports, in `tools/heapviz`. This tool
|
||||
parses the console output, identifies the lines corresponding to (de)allocation
|
||||
operations, and first computes some statistics:
|
||||
|
||||
* Address range used by the heap over this run of the program,
|
||||
* Peak heap usage (how many useful bytes are allocated),
|
||||
* Peak heap consumption (how many bytes are used by the heap, including
|
||||
unavailable bytes between allocated blocks, due to alignment constraints and
|
||||
memory fragmentation),
|
||||
* Fragmentation overhead (difference between heap consumption and usage).
|
||||
|
||||
Then, the `heapviz` tool displays an animated "movie" of the allocated bytes in
|
||||
heap memory. Each frame in this "movie" shows bytes that are currently
|
||||
allocated, that were allocated but are now freed, and that have never been
|
||||
allocated. A new frame is generated for each (de)allocation operation. This tool
|
||||
uses the `ncurses` library, that you may have to install beforehand.
|
||||
|
||||
You can control the tool with the following parameters:
|
||||
|
||||
* `--logfile` (required) to provide the file which contains the console output
|
||||
to parse,
|
||||
* `--fps` (optional) to customize the number of frames per second in the movie
|
||||
animation.
|
||||
|
||||
```shell
|
||||
cargo run --manifest-path tools/heapviz/Cargo.toml -- --logfile console.log --fps 50
|
||||
```
|
||||
|
||||
131
docs/install.md
131
docs/install.md
@@ -8,27 +8,27 @@ This document lists required steps to start build your own OpenSK.
|
||||
|
||||
OpenSK supports different ways to flash your board:
|
||||
|
||||
* [Segger J-Link](https://www.segger.com/products/debug-probes/j-link/)
|
||||
(default method).
|
||||
* [OpenOCD](http://openocd.org/).
|
||||
* [pyOCD](https://pypi.org/project/pyocd/).
|
||||
* [nrfutil](https://pypi.org/project/nrfutil/) for the USB dongle boards that
|
||||
support it, which allows you to directly flash a working board over USB
|
||||
without additional hardware.
|
||||
* [Segger J-Link](https://www.segger.com/products/debug-probes/j-link/)
|
||||
(default method).
|
||||
* [OpenOCD](http://openocd.org/).
|
||||
* [pyOCD](https://pypi.org/project/pyocd/).
|
||||
* [nrfutil](https://pypi.org/project/nrfutil/) for the USB dongle boards that
|
||||
support it, which allows you to directly flash a working board over USB
|
||||
without additional hardware.
|
||||
|
||||
### Software requirements
|
||||
|
||||
In order to compile and flash a working OpenSK firmware, you will need the
|
||||
following:
|
||||
|
||||
* rustup (can be installed with [Rustup](https://rustup.rs/))
|
||||
* python3 and pip (can be installed with the `python3-pip` package on Debian)
|
||||
* the OpenSSL command line tool (can be installed and configured with the
|
||||
`libssl-dev` and `pkg-config` packages on Debian)
|
||||
* `nrfutil` (can be installed using `pip3 install nrfutil`) if you want to flash
|
||||
a device with DFU
|
||||
* `uuid-runtime` if you are missing the `uuidgen` command.
|
||||
* `llvm` and `gcc-arm-none-eabi` if you want to use the upgradability feature.
|
||||
* rustup (can be installed with [Rustup](https://rustup.rs/))
|
||||
* python3 and pip (can be installed with the `python3-pip` package on Debian)
|
||||
* the OpenSSL command line tool (can be installed and configured with the
|
||||
`libssl-dev` and `pkg-config` packages on Debian)
|
||||
* `nrfutil` (pip package of the same name), if you want to flash
|
||||
a device with DFU. Read the disclaimer below.
|
||||
* `uuid-runtime` if you are missing the `uuidgen` command.
|
||||
* `llvm` and `gcc-arm-none-eabi` if you want to use the upgradability feature.
|
||||
|
||||
The proprietary software to use the default programmer can be found on the
|
||||
[Segger website](https://www.segger.com/downloads/jlink). Please follow their
|
||||
@@ -37,47 +37,48 @@ instructions to appropriate binaries for your system.
|
||||
The scripts provided in this project have been tested under Linux and OS X. We
|
||||
haven't tested them on Windows and other platforms.
|
||||
|
||||
If you use Python newer than 3.10, then nrfutil for flashing over DFU is
|
||||
currently not supported. Please use Python 3.10, or play around with [Nordic's
|
||||
new tool](https://www.nordicsemi.com/Products/Development-tools/nrf-util)
|
||||
instead.
|
||||
You need `nrfutil` version 6, if you want to flash over DFU.
|
||||
The tool doesn't support Python newer than 3.10. Therefore, we don't officially
|
||||
support DFU for other versions. If you want to try regardless,
|
||||
[Nordic's new tool](https://www.nordicsemi.com/Products/Development-tools/nrf-util)
|
||||
might work for you.
|
||||
|
||||
### Compiling the firmware
|
||||
|
||||
If this is your first time installing OpenSK, please skip directly to
|
||||
[Initial setup](#Initial-setup). Else, see
|
||||
[Updating your setup](#Updating-your-setup) below.
|
||||
[Initial setup](#initial-setup). Else, see
|
||||
[Updating your setup](#updating-your-setup) below.
|
||||
|
||||
#### Updating your setup
|
||||
|
||||
Depending on the difference to your last state, you may need some of the
|
||||
following steps:
|
||||
|
||||
* If you are not just testing minor changes, reset and redo the setup. This
|
||||
will delete all uncommited changes.
|
||||
* If you are not just testing minor changes, reset and redo the setup. This
|
||||
will delete all uncommited changes.
|
||||
|
||||
```shell
|
||||
./reset.sh
|
||||
./setup.sh
|
||||
```
|
||||
```shell
|
||||
./reset.sh
|
||||
./setup.sh
|
||||
```
|
||||
|
||||
* Flash your board according to the
|
||||
[flashing instructions below](#Flashing-a-firmware]. If you come from an
|
||||
OpenSK version before the 2.0 certified one, your credential storage is not
|
||||
backwards compatible and you have to reset it. :warning: You will lose
|
||||
logins to all websites that you registered with OpenSK. To erase your
|
||||
persistent storage, run the deploy script twice: Once with the application
|
||||
parameter `--erase_storage`, and once with `--opensk` as usual.
|
||||
* Flash your board according to the [instructions below](#flashing-a-firmware).
|
||||
|
||||
This reset also clears the certificate. For a privacy discussion, see the
|
||||
[certificate section in Customization](customization.md#Certificate-considerations).
|
||||
If you want to reinstall it, you also need to rerun:
|
||||
If you come from an OpenSK version before the 2.0 certified one, your credential
|
||||
storage is not backwards compatible and you have to reset it. :warning: You will
|
||||
lose logins to all websites that you registered with OpenSK. To erase your
|
||||
persistent storage, run the deploy script twice: Once with the application
|
||||
parameter `--erase_storage`, and once with `--opensk` as usual.
|
||||
|
||||
```shell
|
||||
./tools/configure.py \
|
||||
--certificate=crypto_data/opensk_cert.pem \
|
||||
--private-key=crypto_data/opensk.key
|
||||
```
|
||||
This reset also clears the certificate. For a privacy discussion, see the
|
||||
[certificate section in Customization](customization.md#Certificate-considerations).
|
||||
If you want to reinstall it, you also need to rerun:
|
||||
|
||||
```shell
|
||||
./tools/configure.py \
|
||||
--certificate=crypto_data/opensk_cert.pem \
|
||||
--private-key=crypto_data/opensk.key
|
||||
```
|
||||
|
||||
#### Initial setup
|
||||
|
||||
@@ -92,17 +93,17 @@ cd OpenSK
|
||||
|
||||
The setup script performs the following steps:
|
||||
|
||||
1. Make sure that the git submodules are checked out.
|
||||
1. Make sure that the git submodules are checked out.
|
||||
|
||||
1. Apply our patches that haven't yet been merged upstream to both
|
||||
[Tock](https://github.com/tock/tock) and
|
||||
[libtock-rs](https://github.com/tock/libtock-rs).
|
||||
1. Apply our patches that haven't yet been merged upstream to both
|
||||
[Tock](https://github.com/tock/tock) and
|
||||
[libtock-rs](https://github.com/tock/libtock-rs).
|
||||
|
||||
1. Generate crypto material, see [Customization](customization.md) for details.
|
||||
1. Generate crypto material, see [Customization](customization.md) for details.
|
||||
|
||||
1. Install the correct Rust toolchain for ARM devices.
|
||||
1. Install the correct Rust toolchain for ARM devices.
|
||||
|
||||
1. Install [tockloader](https://github.com/tock/tockloader).
|
||||
1. Install [tockloader](https://github.com/tock/tockloader).
|
||||
|
||||
Additionally on Linux, you need to install a `udev` rule file to allow non-root
|
||||
users to interact with OpenSK devices. To install it, execute:
|
||||
@@ -131,24 +132,24 @@ for understand privacy tradeoffs.
|
||||
|
||||
From here on, please follow the instructions for your hardware:
|
||||
|
||||
* [Nordic nRF52840-DK](boards/nrf52840dk.md)
|
||||
* [Nordic nRF52840 Dongle](boards/nrf52840_dongle.md)
|
||||
* [Makerdiary nRF52840-MDK USB dongle](boards/nrf52840_mdk.md)
|
||||
* [Feitian OpenSK dongle](boards/nrf52840_feitian.md)
|
||||
* [Nordic nRF52840-DK](boards/nrf52840dk.md)
|
||||
* [Nordic nRF52840 Dongle](boards/nrf52840_dongle.md)
|
||||
* [Makerdiary nRF52840-MDK USB dongle](boards/nrf52840_mdk.md)
|
||||
* [Feitian OpenSK dongle](boards/nrf52840_feitian.md)
|
||||
|
||||
### Advanced installation
|
||||
|
||||
We recommend that you flash your development board with JTAG and dongles with
|
||||
DFU, as described in the [board documentation](#Flashing-a-firmware) linked
|
||||
DFU, as described in the [board documentation](#flashing-a-firmware) linked
|
||||
above. However, we support other programmers:
|
||||
|
||||
* OpenOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=openocd`
|
||||
* pyOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=pyocd`
|
||||
* Custom: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=none`. In this case, an IntelHex file will be created and how
|
||||
to program a board is left to the user.
|
||||
* OpenOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=openocd`
|
||||
* pyOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=pyocd`
|
||||
* Custom: `./deploy.py --board=nrf52840_dongle_opensk --opensk
|
||||
--programmer=none`. In this case, an IntelHex file will be created and how
|
||||
to program a board is left to the user.
|
||||
|
||||
If your board is already flashed with Tock OS, you may skip installing it:
|
||||
`./deploy.py --board=nrf52840dk_opensk --opensk --no-tockos`
|
||||
@@ -162,10 +163,10 @@ We experiment with a new CTAP command to allow upgrading your device without
|
||||
access to its debugging port. For that purpose, the flash storage is split into
|
||||
4 parts:
|
||||
|
||||
* the bootloader to decide with partition to boot
|
||||
* firmware partition A
|
||||
* firmware partition B
|
||||
* the persistent storage for credentials
|
||||
* the bootloader to decide with partition to boot
|
||||
* firmware partition A
|
||||
* firmware partition B
|
||||
* the persistent storage for credentials
|
||||
|
||||
The storage is backward compatible to non-upgradable boards. Deploying an
|
||||
upgradable board automatically installs the bootloader. Please keep in mind that
|
||||
|
||||
@@ -113,3 +113,14 @@ impl From<StoreError> for Error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_store_error() {
|
||||
assert_eq!(Error::from(StoreError::StorageError), Error::Storage);
|
||||
assert_eq!(Error::from(StoreError::InvalidStorage), Error::Internal);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use core::convert::TryFrom;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum UsbEndpoint {
|
||||
MainHid = 1,
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
@@ -40,6 +40,7 @@ pub enum SendOrRecvStatus {
|
||||
Received(UsbEndpoint),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct SendOrRecvError;
|
||||
|
||||
pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;
|
||||
@@ -47,3 +48,16 @@ pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;
|
||||
pub trait HidConnection {
|
||||
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_endpoint_num() {
|
||||
assert_eq!(UsbEndpoint::try_from(1), Ok(UsbEndpoint::MainHid));
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
assert_eq!(UsbEndpoint::try_from(2), Ok(UsbEndpoint::VendorHid));
|
||||
assert_eq!(UsbEndpoint::try_from(3), Err(SendOrRecvError));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -457,4 +457,47 @@ mod test {
|
||||
fn test_invariants() {
|
||||
assert!(is_valid(&DEFAULT_CUSTOMIZATION));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_accessors() {
|
||||
let customization = CustomizationImpl {
|
||||
aaguid: &[0; AAGUID_LENGTH],
|
||||
allows_pin_protocol_v1: true,
|
||||
default_cred_protect: None,
|
||||
default_min_pin_length: 4,
|
||||
default_min_pin_length_rp_ids: &["example.com"],
|
||||
enforce_always_uv: false,
|
||||
enterprise_attestation_mode: None,
|
||||
enterprise_rp_id_list: &[],
|
||||
max_msg_size: 7609,
|
||||
max_pin_retries: 8,
|
||||
use_batch_attestation: true,
|
||||
use_signature_counter: true,
|
||||
max_cred_blob_length: 32,
|
||||
max_credential_count_in_list: Some(3),
|
||||
max_large_blob_array_size: 2048,
|
||||
max_rp_ids_length: 8,
|
||||
max_supported_resident_keys: 150,
|
||||
};
|
||||
assert_eq!(customization.aaguid(), &[0; AAGUID_LENGTH]);
|
||||
assert!(customization.allows_pin_protocol_v1());
|
||||
assert!(customization.default_cred_protect().is_none());
|
||||
assert_eq!(customization.default_min_pin_length(), 4);
|
||||
assert_eq!(
|
||||
customization.default_min_pin_length_rp_ids(),
|
||||
vec![String::from("example.com")]
|
||||
);
|
||||
assert!(!customization.enforce_always_uv());
|
||||
assert!(customization.enterprise_attestation_mode().is_none());
|
||||
assert!(customization.enterprise_rp_id_list().is_empty());
|
||||
assert_eq!(customization.max_msg_size(), 7609);
|
||||
assert_eq!(customization.max_pin_retries(), 8);
|
||||
assert!(customization.use_batch_attestation());
|
||||
assert!(customization.use_signature_counter());
|
||||
assert_eq!(customization.max_cred_blob_length(), 32);
|
||||
assert_eq!(customization.max_credential_count_in_list(), Some(3));
|
||||
assert_eq!(customization.max_large_blob_array_size(), 2048);
|
||||
assert_eq!(customization.max_rp_ids_length(), 8);
|
||||
assert_eq!(customization.max_supported_resident_keys(), 150);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,6 +140,12 @@ impl<E: Env> ClientPin<E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if a PIN UV token is in use.
|
||||
pub fn has_token(&mut self, env: &mut E) -> bool {
|
||||
self.update_timeouts(env);
|
||||
self.pin_uv_auth_token_state.is_in_use()
|
||||
}
|
||||
|
||||
/// Gets a reference to the PIN protocol of the given version.
|
||||
fn get_pin_protocol(&self, pin_uv_auth_protocol: PinUvAuthProtocol) -> &PinProtocol<E> {
|
||||
match pin_uv_auth_protocol {
|
||||
@@ -1507,9 +1513,11 @@ mod test {
|
||||
let mut env = TestEnv::default();
|
||||
let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
|
||||
let message = [0xAA];
|
||||
assert!(!client_pin.has_token(&mut env));
|
||||
client_pin
|
||||
.pin_uv_auth_token_state
|
||||
.begin_using_pin_uv_auth_token(&mut env);
|
||||
assert!(client_pin.has_token(&mut env));
|
||||
|
||||
let pin_uv_auth_token_v1 = client_pin
|
||||
.get_pin_protocol(PinUvAuthProtocol::V1)
|
||||
@@ -1655,6 +1663,7 @@ mod test {
|
||||
.has_permissions_rp_id("example.com"),
|
||||
Ok(())
|
||||
);
|
||||
assert!(client_pin.has_token(&mut env));
|
||||
|
||||
env.clock().advance(30001);
|
||||
client_pin.update_timeouts(&mut env);
|
||||
@@ -1672,6 +1681,7 @@ mod test {
|
||||
.has_permissions_rp_id("example.com"),
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||
);
|
||||
assert!(!client_pin.has_token(&mut env));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -23,6 +23,8 @@ use super::response::{AuthenticatorCredentialManagementResponse, ResponseData};
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use super::{Channel, StatefulCommand, StatefulPermission};
|
||||
use crate::api::crypto::sha256::Sha256;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::ctap::data_formats::CredentialProtectionPolicy;
|
||||
use crate::ctap::storage;
|
||||
use crate::env::{Env, Sha};
|
||||
use alloc::collections::BTreeSet;
|
||||
@@ -62,6 +64,7 @@ fn enumerate_rps_response<E: Env>(
|
||||
|
||||
/// Generates the response for subcommands enumerating credentials.
|
||||
fn enumerate_credentials_response<E: Env>(
|
||||
env: &mut E,
|
||||
credential: PublicKeyCredentialSource,
|
||||
total_credentials: Option<u64>,
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
@@ -91,12 +94,15 @@ fn enumerate_credentials_response<E: Env>(
|
||||
transports: None, // You can set USB as a hint here.
|
||||
};
|
||||
let public_key = private_key.get_pub_key::<E>()?;
|
||||
let cred_protect = cred_protect_policy
|
||||
.or(env.customization().default_cred_protect())
|
||||
.or(Some(CredentialProtectionPolicy::UserVerificationOptional));
|
||||
Ok(AuthenticatorCredentialManagementResponse {
|
||||
user: Some(user),
|
||||
credential_id: Some(credential_id),
|
||||
public_key: Some(public_key),
|
||||
total_credentials,
|
||||
cred_protect: cred_protect_policy,
|
||||
cred_protect,
|
||||
large_blob_key,
|
||||
..Default::default()
|
||||
})
|
||||
@@ -201,7 +207,7 @@ fn process_enumerate_credentials_begin<E: Env>(
|
||||
channel,
|
||||
);
|
||||
}
|
||||
enumerate_credentials_response::<E>(credential, Some(total_credentials as u64))
|
||||
enumerate_credentials_response(env, credential, Some(total_credentials as u64))
|
||||
}
|
||||
|
||||
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
|
||||
@@ -211,7 +217,7 @@ fn process_enumerate_credentials_get_next_credential<E: Env>(
|
||||
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
|
||||
let credential_key = stateful_command_permission.next_enumerate_credential(env)?;
|
||||
let credential = storage::get_credential(env, credential_key)?;
|
||||
enumerate_credentials_response::<E>(credential, None)
|
||||
enumerate_credentials_response(env, credential, None)
|
||||
}
|
||||
|
||||
/// Processes the subcommand deleteCredential for CredentialManagement.
|
||||
|
||||
@@ -410,11 +410,26 @@ pub struct StatefulPermission<E: Env> {
|
||||
channel: Option<Channel>,
|
||||
}
|
||||
|
||||
impl<E: Env> Default for StatefulPermission<E> {
|
||||
/// Creates the command state at device startup without user action.
|
||||
///
|
||||
/// Reset is not granted after a forced reboot. The user replugging the device is a required
|
||||
/// to avoid accidental data loss.
|
||||
fn default() -> StatefulPermission<E> {
|
||||
StatefulPermission {
|
||||
permission: <E::Clock as Clock>::Timer::default(),
|
||||
command_type: None,
|
||||
channel: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Env> StatefulPermission<E> {
|
||||
/// Creates the command state at device startup.
|
||||
///
|
||||
/// Resets are only possible after a power cycle. Therefore, initialization
|
||||
/// means allowing Reset, and Reset cannot be granted later.
|
||||
/// Resets are only possible after a power cycle. Therefore, there is no way to grant the Reset
|
||||
/// permission outside of this function. If you initialize the app without a power cycle
|
||||
/// (potentially after waking up from sleep), call `default` instead.
|
||||
pub fn new_reset(env: &mut E) -> StatefulPermission<E> {
|
||||
StatefulPermission {
|
||||
permission: env.clock().make_timer(RESET_TIMEOUT_DURATION_MS),
|
||||
@@ -543,11 +558,16 @@ impl<E: Env> CtapState<E> {
|
||||
pub fn new(env: &mut E) -> Self {
|
||||
storage::init(env).ok().unwrap();
|
||||
let client_pin = ClientPin::new(env);
|
||||
let stateful_command_permission = if env.boots_after_soft_reset() {
|
||||
StatefulPermission::default()
|
||||
} else {
|
||||
StatefulPermission::new_reset(env)
|
||||
};
|
||||
CtapState {
|
||||
client_pin,
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
u2f_up_state: U2fUserPresenceState::new(),
|
||||
stateful_command_permission: StatefulPermission::new_reset(env),
|
||||
stateful_command_permission,
|
||||
large_blobs: LargeBlobs::new(),
|
||||
}
|
||||
}
|
||||
@@ -582,6 +602,12 @@ impl<E: Env> CtapState<E> {
|
||||
self.stateful_command_permission.clear_old_channels(channel);
|
||||
}
|
||||
|
||||
/// Checks if the application has any timers running.
|
||||
pub fn can_sleep(&mut self, env: &mut E) -> bool {
|
||||
!self.client_pin.has_token(env)
|
||||
&& self.stateful_command_permission.get_command(env).is_err()
|
||||
}
|
||||
|
||||
pub fn process_command(
|
||||
&mut self,
|
||||
env: &mut E,
|
||||
@@ -1282,15 +1308,19 @@ impl<E: Env> CtapState<E> {
|
||||
options.append(&mut vec![
|
||||
(String::from("rk"), true),
|
||||
(String::from("up"), true),
|
||||
(String::from("alwaysUv"), has_always_uv),
|
||||
(String::from("credMgmt"), true),
|
||||
#[cfg(feature = "config_command")]
|
||||
(String::from("authnrCfg"), true),
|
||||
(String::from("clientPin"), storage::pin_hash(env)?.is_some()),
|
||||
(String::from("largeBlobs"), true),
|
||||
(String::from("pinUvAuthToken"), true),
|
||||
#[cfg(feature = "config_command")]
|
||||
(String::from("setMinPINLength"), true),
|
||||
(String::from("makeCredUvNotRqd"), !has_always_uv),
|
||||
]);
|
||||
if cfg!(feature = "config_command") || env.customization().enforce_always_uv() {
|
||||
options.push((String::from("alwaysUv"), has_always_uv));
|
||||
}
|
||||
let mut pin_protocols = vec![PinUvAuthProtocol::V2 as u64];
|
||||
if env.customization().allows_pin_protocol_v1() {
|
||||
pin_protocols.push(PinUvAuthProtocol::V1 as u64);
|
||||
@@ -1499,12 +1529,15 @@ mod test {
|
||||
"ep" => env.customization().enterprise_attestation_mode().map(|_| false),
|
||||
"rk" => true,
|
||||
"up" => true,
|
||||
#[cfg(feature = "config_command")]
|
||||
"alwaysUv" => false,
|
||||
"credMgmt" => true,
|
||||
#[cfg(feature = "config_command")]
|
||||
"authnrCfg" => true,
|
||||
"clientPin" => false,
|
||||
"largeBlobs" => true,
|
||||
"pinUvAuthToken" => true,
|
||||
#[cfg(feature = "config_command")]
|
||||
"setMinPINLength" => true,
|
||||
"makeCredUvNotRqd" => true,
|
||||
},
|
||||
|
||||
3
libraries/opensk/src/env/mod.rs
vendored
3
libraries/opensk/src/env/mod.rs
vendored
@@ -76,6 +76,9 @@ pub trait Env {
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
fn vendor_hid_connection(&mut self) -> &mut Self::HidConnection;
|
||||
|
||||
/// Indicates that the last power cycle was not caused by user action.
|
||||
fn boots_after_soft_reset(&self) -> bool;
|
||||
|
||||
/// Option to return a firmware version that is shown as device info.
|
||||
fn firmware_version(&self) -> Option<u64> {
|
||||
None
|
||||
|
||||
20
libraries/opensk/src/env/test/mod.rs
vendored
20
libraries/opensk/src/env/test/mod.rs
vendored
@@ -34,6 +34,7 @@ pub struct TestEnv {
|
||||
store: Store<BufferStorage>,
|
||||
customization: TestCustomization,
|
||||
clock: TestClock,
|
||||
soft_reset: bool,
|
||||
}
|
||||
|
||||
pub type TestRng = StdRng;
|
||||
@@ -127,6 +128,7 @@ impl Default for TestEnv {
|
||||
store,
|
||||
customization,
|
||||
clock,
|
||||
soft_reset: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,6 +141,10 @@ impl TestEnv {
|
||||
pub fn seed_rng_from_u64(&mut self, seed: u64) {
|
||||
self.rng = StdRng::seed_from_u64(seed);
|
||||
}
|
||||
|
||||
pub fn set_boots_after_soft_reset(&mut self, value: bool) {
|
||||
self.soft_reset = value;
|
||||
}
|
||||
}
|
||||
|
||||
impl TestUserPresence {
|
||||
@@ -227,6 +233,10 @@ impl Env for TestEnv {
|
||||
self
|
||||
}
|
||||
|
||||
fn boots_after_soft_reset(&self) -> bool {
|
||||
self.soft_reset
|
||||
}
|
||||
|
||||
fn firmware_version(&self) -> Option<u64> {
|
||||
Some(0)
|
||||
}
|
||||
@@ -247,4 +257,14 @@ mod test {
|
||||
clock.advance(1);
|
||||
assert!(clock.is_elapsed(&timer));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_soft_reset() {
|
||||
let mut env = TestEnv::default();
|
||||
assert!(!env.boots_after_soft_reset());
|
||||
env.set_boots_after_soft_reset(true);
|
||||
assert!(env.boots_after_soft_reset());
|
||||
env.set_boots_after_soft_reset(false);
|
||||
assert!(!env.boots_after_soft_reset());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,10 +81,6 @@ impl<E: Env> Ctap<E> {
|
||||
&mut self.state
|
||||
}
|
||||
|
||||
pub fn hid(&mut self) -> &mut MainHid<E> {
|
||||
&mut self.hid
|
||||
}
|
||||
|
||||
pub fn env(&mut self) -> &mut E {
|
||||
&mut self.env
|
||||
}
|
||||
@@ -120,6 +116,10 @@ impl<E: Env> Ctap<E> {
|
||||
self.hid.should_wink(&mut self.env)
|
||||
}
|
||||
|
||||
pub fn can_sleep(&mut self) -> bool {
|
||||
!self.should_wink() && self.state.can_sleep(&mut self.env)
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
pub fn u2f_grant_user_presence(&mut self) {
|
||||
self.state.u2f_grant_user_presence(&mut self.env)
|
||||
@@ -134,6 +134,7 @@ impl<E: Env> Ctap<E> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::ctap::status_code::Ctap2StatusCode;
|
||||
use crate::env::test::TestEnv;
|
||||
|
||||
/// Assembles a packet for a payload that fits into one packet.
|
||||
@@ -200,6 +201,58 @@ mod test {
|
||||
assert_eq!(response_packet[4], 0xBF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hard_reset() {
|
||||
let env = TestEnv::default();
|
||||
let mut ctap = Ctap::<TestEnv>::new(env);
|
||||
assert!(!ctap.can_sleep());
|
||||
|
||||
// Send Init, receive Init response.
|
||||
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
|
||||
let response_packet = init_response.next().unwrap();
|
||||
assert_eq!(response_packet[4], 0x86);
|
||||
let cid = *array_ref!(response_packet, 15, 4);
|
||||
|
||||
// Send Reset, get Ok.
|
||||
let reset_packet = assemble_packet(&cid, 0x10, &[0x07]);
|
||||
let mut reset_response = ctap.process_hid_packet(&reset_packet, Transport::MainHid);
|
||||
let response_packet = reset_response.next().unwrap();
|
||||
let status_byte = Ctap2StatusCode::CTAP2_OK as u8;
|
||||
let expected_data = [0x90, 0x00, 0x01, status_byte];
|
||||
assert_eq!(response_packet[..4], cid);
|
||||
assert_eq!(response_packet[4..8], expected_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_soft_reset() {
|
||||
let mut env = TestEnv::default();
|
||||
env.set_boots_after_soft_reset(true);
|
||||
let mut ctap = Ctap::<TestEnv>::new(env);
|
||||
assert!(ctap.can_sleep());
|
||||
|
||||
// Send Init, receive Init response.
|
||||
let mut init_response = ctap.process_hid_packet(&init_packet(), Transport::MainHid);
|
||||
let response_packet = init_response.next().unwrap();
|
||||
assert_eq!(response_packet[4], 0x86);
|
||||
let cid = *array_ref!(response_packet, 15, 4);
|
||||
|
||||
// Send Reset, get error.
|
||||
let reset_packet = assemble_packet(&cid, 0x10, &[0x07]);
|
||||
let mut reset_response = ctap.process_hid_packet(&reset_packet, Transport::MainHid);
|
||||
let response_packet = reset_response.next().unwrap();
|
||||
let status_byte = Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED as u8;
|
||||
let expected_data = [0x90, 0x00, 0x01, status_byte];
|
||||
assert_eq!(response_packet[..4], cid);
|
||||
assert_eq!(response_packet[4..8], expected_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_api() {
|
||||
let env = TestEnv::default();
|
||||
let mut ctap = Ctap::<TestEnv>::new(env);
|
||||
assert_eq!(ctap.env().firmware_version(), Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
fn test_locked_transport() {
|
||||
@@ -222,4 +275,14 @@ mod test {
|
||||
let response_packet = init_response.next().unwrap();
|
||||
assert_eq!(response_packet[4], 0xBF);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
fn test_ctap1_initial_state() {
|
||||
let env = TestEnv::default();
|
||||
let mut ctap = Ctap::<TestEnv>::new(env);
|
||||
// Granting doesn't work until a CTAP1 request was processed.
|
||||
ctap.u2f_grant_user_presence();
|
||||
assert!(!ctap.u2f_needs_user_presence());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ cargo check --release --target=thumbv7em-none-eabi --features "$MOST_FEATURES"
|
||||
cargo check --release --target=thumbv7em-none-eabi --examples
|
||||
cargo check --release --target=thumbv7em-none-eabi --examples --features with_nfc
|
||||
cargo check --release --target=thumbv7em-none-eabi --manifest-path bootloader/Cargo.toml
|
||||
cargo check --release --manifest-path tools/heapviz/Cargo.toml
|
||||
|
||||
echo "Checking Rust formatting..."
|
||||
cargo fmt -- --check
|
||||
@@ -46,7 +45,6 @@ cargo fmt --manifest-path libraries/cbor/fuzz/Cargo.toml -- --check
|
||||
cargo fmt --manifest-path libraries/persistent_store/Cargo.toml -- --check
|
||||
cargo fmt --manifest-path libraries/persistent_store/fuzz/Cargo.toml -- --check
|
||||
cargo fmt --manifest-path libraries/crypto/Cargo.toml -- --check
|
||||
cargo fmt --manifest-path tools/heapviz/Cargo.toml -- --check
|
||||
cargo fmt --manifest-path bootloader/Cargo.toml -- --check
|
||||
|
||||
echo "Checking Python formatting..."
|
||||
@@ -90,7 +88,6 @@ cargo test --manifest-path libraries/cbor/Cargo.toml
|
||||
cargo test --manifest-path libraries/persistent_store/Cargo.toml --features std
|
||||
# Running release mode to speed up. This library is legacy anyway.
|
||||
cargo test --manifest-path libraries/crypto/Cargo.toml --features std --release
|
||||
cargo test --manifest-path tools/heapviz/Cargo.toml
|
||||
|
||||
echo "Checking that boards build properly..."
|
||||
make -C third_party/tock/boards/nordic/nrf52840dk_opensk
|
||||
|
||||
8
setup.sh
8
setup.sh
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2019 Google LLC
|
||||
# Copyright 2019-2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,11 @@ check_command "$PIP"
|
||||
|
||||
# Ensure we have certificates, keys, etc. so that the tests can run
|
||||
source tools/gen_key_materials.sh
|
||||
generate_crypto_materials N
|
||||
generate_pki N
|
||||
if [ ! -f "crypto_data/opensk.key" -o ! -f "crypto_data/opensk_cert.pem" ]
|
||||
then
|
||||
generate_new_batch
|
||||
fi
|
||||
|
||||
rustup show
|
||||
"$PIP" install --upgrade -r requirements.txt
|
||||
|
||||
4
src/env/tock/mod.rs
vendored
4
src/env/tock/mod.rs
vendored
@@ -452,6 +452,10 @@ impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> E
|
||||
commands::process_vendor_command(self, bytes, channel)
|
||||
}
|
||||
|
||||
fn boots_after_soft_reset(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn firmware_version(&self) -> Option<u64> {
|
||||
self.upgrade_storage
|
||||
.as_ref()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# Copyright 2019 Google LLC
|
||||
# Copyright 2019-2023 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -13,20 +13,19 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
generate_crypto_materials () {
|
||||
# OpenSSL ext file location
|
||||
local openssl_ext_file=tools/openssl.ext
|
||||
generate_pki () {
|
||||
# Curve parameters
|
||||
local ecparams_file=crypto_data/ecparams.pem
|
||||
# OpenSK AAGUID
|
||||
local aaguid_file=crypto_data/aaguid.txt
|
||||
|
||||
# Root CA key pair and certificate
|
||||
local ca_priv_key=crypto_data/opensk_ca.key
|
||||
local ca_cert_name=crypto_data/opensk_ca
|
||||
local ca_priv_key=crypto_data/ca/root-ca/private/root-ca.key
|
||||
local ca_cert_name=crypto_data/ca/root-ca
|
||||
|
||||
# Attestation key pair and certificate that will be embedded into the
|
||||
# firmware. The certificate will be signed by the Root CA.
|
||||
local opensk_key=crypto_data/opensk.key
|
||||
local opensk_cert_name=crypto_data/opensk_cert
|
||||
# Signing CA key pair and certificate
|
||||
local signing_ca_priv_key=crypto_data/ca/signing-ca/private/signing-ca.key
|
||||
local signing_ca_cert_name=crypto_data/ca/signing-ca
|
||||
|
||||
# The upgrade private key is used for signing, the corresponding public key
|
||||
# will be COSE encoded and embedded into the firmware.
|
||||
@@ -36,6 +35,9 @@ generate_crypto_materials () {
|
||||
# Allow invoker to override the command with a full path.
|
||||
local openssl=${OPENSSL:-$(which openssl)}
|
||||
|
||||
# Print version for debug purposes
|
||||
${openssl} version
|
||||
|
||||
# We need openssl command to continue
|
||||
if [ ! -x "${openssl}" ]
|
||||
then
|
||||
@@ -47,55 +49,109 @@ generate_crypto_materials () {
|
||||
set -e
|
||||
|
||||
force_generate="$1"
|
||||
mkdir -p crypto_data
|
||||
if [ ! -f "${ca_priv_key}" ]
|
||||
ask_for_password="$2"
|
||||
|
||||
if [ "${force_generate}" = "Y" ]
|
||||
then
|
||||
"${openssl}" ecparam -genkey -name prime256v1 -out "${ca_priv_key}"
|
||||
# Remove old OpenSK certs and CRL
|
||||
rm -rf crypto_data/crl crypto_data/certs
|
||||
fi
|
||||
|
||||
if [ ! -f "${ca_cert_name}.pem" ]
|
||||
openssl_keypwd="-nodes"
|
||||
openssl_batch="-batch"
|
||||
if [ "${ask_for_password}" = "Y" ]
|
||||
then
|
||||
openssl_keypwd=""
|
||||
openssl_batch=""
|
||||
fi
|
||||
|
||||
# Create PKI directories
|
||||
mkdir -p crypto_data/ca/root-ca/private crypto_data/ca/root-ca/db
|
||||
mkdir -p crypto_data/ca/signing-ca/private crypto_data/ca/signing-ca/db
|
||||
mkdir -p crypto_data/crl crypto_data/certs
|
||||
chmod 700 crypto_data/ca/root-ca/private crypto_data/ca/signing-ca/private
|
||||
|
||||
# Prepare PKI databases
|
||||
for fname in \
|
||||
crypto_data/ca/root-ca/db/root-ca.db \
|
||||
crypto_data/ca/root-ca/db/root-ca.db.attr \
|
||||
crypto_data/ca/signing-ca/db/signing-ca.db \
|
||||
crypto_data/ca/signing-ca/db/signing-ca.db.attr
|
||||
do
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${fname}" ]
|
||||
then
|
||||
cp /dev/null "${fname}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Initialize PKI serial numbers
|
||||
for fname in \
|
||||
crypto_data/ca/root-ca/db/root-ca.pem.srl \
|
||||
crypto_data/ca/root-ca/db/root-ca.pem.srl \
|
||||
crypto_data/ca/signing-ca/db/signing-ca.pem.srl \
|
||||
crypto_data/ca/signing-ca/db/signing-ca.pem.srl
|
||||
do
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${fname}" ]
|
||||
then
|
||||
echo 01 > "${fname}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Generate AAGUID
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${aaguid_file}" ]
|
||||
then
|
||||
uuidgen > "${aaguid_file}"
|
||||
fi
|
||||
|
||||
if [ ! -f "${ecparams_file}" ]
|
||||
then
|
||||
"${openssl}" ecparam -param_enc "named_curve" -name "prime256v1" -out "${ecparams_file}"
|
||||
fi
|
||||
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${ca_cert_name}.pem" ]
|
||||
then
|
||||
# Create root CA request and key pair
|
||||
"${openssl}" req \
|
||||
-new \
|
||||
-key "${ca_priv_key}" \
|
||||
-config tools/openssl/root-ca.conf \
|
||||
-out "${ca_cert_name}.csr" \
|
||||
-subj "/CN=OpenSK CA"
|
||||
"${openssl}" x509 \
|
||||
-trustout \
|
||||
-req \
|
||||
-days 7305 \
|
||||
-keyout "${ca_priv_key}" \
|
||||
"${openssl_keypwd}" \
|
||||
"${openssl_batch}" \
|
||||
-newkey "ec:${ecparams_file}"
|
||||
|
||||
# Make root CA certificate
|
||||
"${openssl}" ca \
|
||||
-selfsign \
|
||||
-config tools/openssl/root-ca.conf \
|
||||
"${openssl_batch}" \
|
||||
-in "${ca_cert_name}.csr" \
|
||||
-signkey "${ca_priv_key}" \
|
||||
-outform pem \
|
||||
-out "${ca_cert_name}.pem" \
|
||||
-sha256
|
||||
-extensions root_ca_ext
|
||||
fi
|
||||
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${opensk_key}" ]
|
||||
then
|
||||
"${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_key}"
|
||||
fi
|
||||
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${opensk_cert_name}.pem" ]
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${signing_ca_cert_name}.pem" ]
|
||||
then
|
||||
# Create signing CA request
|
||||
"${openssl}" req \
|
||||
-new \
|
||||
-key "${opensk_key}" \
|
||||
-out "${opensk_cert_name}.csr" \
|
||||
-subj "/C=US/O=OpenSK/OU=Authenticator Attestation/CN=OpenSK Hacker Edition"
|
||||
"${openssl}" x509 \
|
||||
-req \
|
||||
-days 3652 \
|
||||
-in "${opensk_cert_name}.csr" \
|
||||
-CA "${ca_cert_name}.pem" \
|
||||
-extfile "${openssl_ext_file}" \
|
||||
-CAkey "${ca_priv_key}" \
|
||||
-CAcreateserial \
|
||||
-outform pem \
|
||||
-out "${opensk_cert_name}.pem" \
|
||||
-sha256
|
||||
-config tools/openssl/signing-ca.conf \
|
||||
-out "${signing_ca_cert_name}.csr" \
|
||||
-keyout "${signing_ca_priv_key}" \
|
||||
"${openssl_keypwd}" \
|
||||
"${openssl_batch}" \
|
||||
-newkey "ec:${ecparams_file}"
|
||||
|
||||
# Make signing CA certificate
|
||||
"${openssl}" ca \
|
||||
-config tools/openssl/root-ca.conf \
|
||||
"${openssl_batch}" \
|
||||
-in "${signing_ca_cert_name}.csr" \
|
||||
-out "${signing_ca_cert_name}.pem" \
|
||||
-extensions signing_ca_ext
|
||||
fi
|
||||
|
||||
# Create firmware update key pair
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${opensk_upgrade}" ]
|
||||
then
|
||||
"${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_upgrade}"
|
||||
@@ -106,11 +162,88 @@ generate_crypto_materials () {
|
||||
then
|
||||
"${openssl}" ec -in "${opensk_upgrade}" -pubout -out "${opensk_upgrade_pub}"
|
||||
fi
|
||||
|
||||
if [ "${force_generate}" = "Y" -o ! -f "${aaguid_file}" ]
|
||||
then
|
||||
uuidgen > "${aaguid_file}"
|
||||
fi
|
||||
}
|
||||
|
||||
generate_crypto_materials "$1"
|
||||
generate_new_batch () {
|
||||
local openssl=${OPENSSL:-$(which openssl)}
|
||||
# Curve parameters
|
||||
local ecparams_file=crypto_data/ecparams.pem
|
||||
# OpenSK AAGUID
|
||||
local aaguid_file=crypto_data/aaguid.txt
|
||||
|
||||
set -e
|
||||
|
||||
# We need openssl command to continue
|
||||
if [ ! -x "${openssl}" ]
|
||||
then
|
||||
echo "Missing openssl command. Try to specify its full path using OPENSSL environment variable."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${ecparams_file}" ]
|
||||
then
|
||||
echo "Missing curve parameters. Has the PKI been generated?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${aaguid_file}" ]
|
||||
then
|
||||
echo "Missing AAGUID file."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
batch_id=$(uuidgen | tr -d '-')
|
||||
aaguid=$(tr -d '-' < "${aaguid_file}")
|
||||
|
||||
# Attestation key pair and certificate that will be embedded into the
|
||||
# firmware. The certificate will be signed by the Root CA.
|
||||
local opensk_key=certs/${batch_id}.key
|
||||
local opensk_cert_name=certs/${batch_id}
|
||||
|
||||
# x509v3 extension values are passed through environment variables.
|
||||
export OPENSK_AAGUID="${aaguid}"
|
||||
# Comma separated values of supported transport for the batch attestation certificate.
|
||||
# 0=BTC, 1=BLE, 2=USB, 3=NFC
|
||||
# Default to USB only
|
||||
export OPENSK_TRANSPORT="${OPENSK_TRANSPORT:-2}" # comma separated values. 1=BLE, 2=USB, 3=NFC
|
||||
|
||||
ask_for_password="$1"
|
||||
openssl_keypwd="-nodes"
|
||||
openssl_batch="-batch"
|
||||
if [ "${ask_for_password}" = "Y" ]
|
||||
then
|
||||
openssl_keypwd=""
|
||||
openssl_batch=""
|
||||
fi
|
||||
|
||||
# Generate certificate request for the current batch
|
||||
"${openssl}" req \
|
||||
-new \
|
||||
-config "tools/openssl/opensk.conf" \
|
||||
-keyout "crypto_data/${opensk_key}" \
|
||||
-out "crypto_data/${opensk_cert_name}.csr" \
|
||||
"${openssl_keypwd}" \
|
||||
"${openssl_batch}" \
|
||||
-newkey "ec:${ecparams_file}"
|
||||
# Sign it using signing-CA and injecting the AAGUID as an extension
|
||||
"${openssl}" ca \
|
||||
-config "tools/openssl/signing-ca.conf" \
|
||||
"${openssl_batch}" \
|
||||
-in "crypto_data/${opensk_cert_name}.csr" \
|
||||
-out "crypto_data/${opensk_cert_name}.pem" \
|
||||
-extensions "fido_key_ext"
|
||||
|
||||
# Force symlink to the latest batch
|
||||
ln -s -f "${opensk_cert_name}.pem" crypto_data/opensk_cert.pem
|
||||
ln -s -f "${opensk_key}" crypto_data/opensk.key
|
||||
}
|
||||
|
||||
if [ "X${1}" != "X" ]
|
||||
then
|
||||
ask_for_password=${2:-N}
|
||||
generate_pki $1 $ask_for_password
|
||||
if [ "$1" = "Y" -o ! -f "crypto_data/opensk.key" -o ! -f "crypto_data/opensk_cert.pem" ]
|
||||
then
|
||||
generate_new_batch $ask_for_password
|
||||
fi
|
||||
fi
|
||||
|
||||
194
tools/heapviz/Cargo.lock
generated
194
tools/heapviz/Cargo.lock
generated
@@ -1,194 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapviz"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"lazy_static",
|
||||
"ncurses",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.150"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
|
||||
[[package]]
|
||||
name = "ncurses"
|
||||
version = "5.101.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e2c5d34d72657dc4b638a1c25d40aae81e4f1c699062f72f467237920752032"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
@@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "heapviz"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Guillaume Endignoux <guillaumee@google.com>",
|
||||
]
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33.1"
|
||||
lazy_static = "1.4.0"
|
||||
ncurses = "5.99.0"
|
||||
regex = "1"
|
||||
@@ -1,217 +0,0 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use clap::{App, Arg};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::thread::sleep;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Configuration, built from CLI parameters.
|
||||
struct Config {
|
||||
/// Handle to the log file containing allocation operations.
|
||||
logfile: File,
|
||||
/// Number of allocation operations to show per second.
|
||||
fps: u64,
|
||||
}
|
||||
|
||||
fn parse_cli() -> Config {
|
||||
let matches = App::new("Heap visualizer")
|
||||
.version("0.1")
|
||||
.author("Guillaume Endignoux <guillaumee@google.com>")
|
||||
.about("Tool to visualize heap usage of libtock-rs applications")
|
||||
.arg(Arg::with_name("logfile")
|
||||
.short("f")
|
||||
.long("logfile")
|
||||
.value_name("FILE")
|
||||
.help("Log file containing allocation info (deploy OpenSK with --debug-allocations to obtain it)")
|
||||
.takes_value(true)
|
||||
.required(true))
|
||||
.arg(Arg::with_name("fps")
|
||||
.long("fps")
|
||||
.value_name("FPS")
|
||||
.help("Number of allocation operations to show per second")
|
||||
.takes_value(true)
|
||||
.default_value("20"))
|
||||
.get_matches();
|
||||
|
||||
let logpath = matches.value_of("logfile").unwrap();
|
||||
let fps = matches
|
||||
.value_of("fps")
|
||||
.unwrap()
|
||||
.parse::<u64>()
|
||||
.expect("The --fps parameter must be an integer");
|
||||
let logfile = File::open(logpath).expect("Couldn't open --logfile for reading");
|
||||
|
||||
Config { logfile, fps }
|
||||
}
|
||||
|
||||
/// An allocation or deallocation event.
|
||||
#[cfg_attr(test, derive(Debug, PartialEq))]
|
||||
struct Event {
|
||||
/// Whether this even is an allocation (true) or a deallocation (false).
|
||||
is_alloc: bool,
|
||||
/// The start address of the (de)allocated block, in bytes.
|
||||
start: usize,
|
||||
/// The length of the (de)allocated block, in bytes.
|
||||
len: usize,
|
||||
}
|
||||
|
||||
fn parse_event(line: &str) -> Option<Event> {
|
||||
// The following regex matches lines looking like the following from OpenSK's output. Such lines
|
||||
// are printed to the console when the `--debug-allocations` feature is enabled in the deploy
|
||||
// script.
|
||||
//
|
||||
// ```
|
||||
// alloc[256, 1] = 0x2002401c (2 ptrs, 384 bytes)
|
||||
// dealloc[64, 1] = 0x2002410c (1 ptrs, 512 bytes)
|
||||
// ```
|
||||
//
|
||||
// The two integers between square brackets after the (de)alloc keywords represent the length
|
||||
// and alignement of the allocated block, respectively. The integer serialized in hexadecimal
|
||||
// after the equal sign represents the starting address of the allocated block. The two
|
||||
// integers within parentheses represent statistics about the total number of allocated blocks
|
||||
// and the total number of allocated bytes after the (de)allocation operation, respectively.
|
||||
//
|
||||
// This regex captures three elements, in this order.
|
||||
// - The keyword to know whether this operation is an allocation or a deallocation.
|
||||
// - The length of the allocated block.
|
||||
// - The starting address of the allocated block.
|
||||
lazy_static! {
|
||||
static ref RE: Regex =
|
||||
Regex::new(r"^(alloc|dealloc)\[(\d+), \d+\] = 0x([0-9a-f]+) \(\d+ ptrs, \d+ bytes\)$")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
RE.captures(line).map(|caps| {
|
||||
let typ = caps.get(1).unwrap().as_str();
|
||||
let len = caps.get(2).unwrap().as_str().parse::<usize>().unwrap();
|
||||
let start = usize::from_str_radix(&caps.get(3).unwrap().as_str(), 16).unwrap();
|
||||
Event {
|
||||
is_alloc: typ == "alloc",
|
||||
start,
|
||||
len,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let config = parse_cli();
|
||||
|
||||
let mut events = Vec::new();
|
||||
for line in BufReader::new(config.logfile).lines() {
|
||||
if let Some(event) = parse_event(&line.unwrap()) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
let count_alloc = events.iter().filter(|e| e.is_alloc).count();
|
||||
let count_dealloc = events.len() - count_alloc;
|
||||
let start = events.iter().map(|e| e.start).min().unwrap_or(0);
|
||||
let end = events.iter().map(|e| e.start + e.len).max().unwrap_or(0);
|
||||
let mut usage = 0;
|
||||
let peak = events
|
||||
.iter()
|
||||
.map(|e| {
|
||||
if e.is_alloc {
|
||||
usage += e.len;
|
||||
} else {
|
||||
usage -= e.len;
|
||||
}
|
||||
usage
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(0);
|
||||
let len = end - start;
|
||||
println!(
|
||||
"Observed {} allocations and {} deallocations",
|
||||
count_alloc, count_dealloc
|
||||
);
|
||||
println!("Start address: {:08x}", start);
|
||||
println!("End address: {:08x}", end);
|
||||
println!("Peak usage: {0} = {0:08x} bytes", peak);
|
||||
println!("Peak consumption: {0} = {0:08x} bytes", len);
|
||||
println!("Fragmentation overhead: {0} = {0:08x} bytes", len - peak);
|
||||
|
||||
print!("\nPress ENTER to start the visualization...");
|
||||
std::io::stdout().flush().unwrap();
|
||||
// Wait for ENTER, by reading a single byte and discarding it.
|
||||
let _ = std::io::stdin().lock().read(&mut [0u8]).unwrap();
|
||||
|
||||
let window = ncurses::initscr();
|
||||
ncurses::cbreak();
|
||||
ncurses::noecho();
|
||||
ncurses::intrflush(window, false);
|
||||
ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);
|
||||
|
||||
let width = ncurses::getmaxx(window) as usize;
|
||||
|
||||
for e in events.iter() {
|
||||
let position = e.start - start;
|
||||
ncurses::wmove(window, (position / width) as i32, (position % width) as i32);
|
||||
|
||||
let mut s = Vec::with_capacity(e.len);
|
||||
if e.is_alloc {
|
||||
s.resize(e.len, b'#');
|
||||
} else {
|
||||
s.resize(e.len, b'.');
|
||||
}
|
||||
ncurses::addstr(std::str::from_utf8(s.as_slice()).unwrap());
|
||||
ncurses::refresh();
|
||||
sleep(Duration::from_nanos(1_000_000_000 / config.fps));
|
||||
}
|
||||
|
||||
ncurses::endwin();
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_event_alloc() {
|
||||
assert_eq!(
|
||||
parse_event("alloc[256, 1] = 0x2002401c (2 ptrs, 384 bytes)"),
|
||||
Some(Event {
|
||||
is_alloc: true,
|
||||
start: 0x2002401c,
|
||||
len: 256,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_event_dealloc() {
|
||||
assert_eq!(
|
||||
parse_event("dealloc[64, 1] = 0x2002410c (1 ptrs, 512 bytes)"),
|
||||
Some(Event {
|
||||
is_alloc: false,
|
||||
start: 0x2002410c,
|
||||
len: 64,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_event_none() {
|
||||
assert_eq!(
|
||||
parse_event(
|
||||
"NRF52 HW INFO: Variant: AAD0, Part: N52840, Package: QI, Ram: K256, Flash: K1024"
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
basicConstraints=CA:FALSE
|
||||
26
tools/openssl/opensk.conf
Normal file
26
tools/openssl/opensk.conf
Normal file
@@ -0,0 +1,26 @@
|
||||
oid_section = OIDS
|
||||
|
||||
[ OIDS ]
|
||||
fido_attestation = 1.3.6.1.4.1.45724.2.1.1
|
||||
fido_aaguid = 1.3.6.1.4.1.45724.1.1.4
|
||||
|
||||
[ req ]
|
||||
encrypt_key = no
|
||||
default_md = sha256
|
||||
utf8 = yes
|
||||
string_mask = utf8only
|
||||
prompt = no
|
||||
distinguished_name = fido_dn
|
||||
req_extensions = fido_reqext
|
||||
|
||||
[ fido_dn ]
|
||||
countryName = "US"
|
||||
organizationName = "OpenSK"
|
||||
organizationalUnitName = "Authenticator Attestation"
|
||||
commonName = "OpenSK Hacker Edition"
|
||||
|
||||
[ fido_reqext ]
|
||||
keyUsage = critical,digitalSignature
|
||||
subjectKeyIdentifier = hash
|
||||
fido_attestation = ASN1:FORMAT:BITLIST,BITSTRING:${ENV::OPENSK_TRANSPORT}
|
||||
fido_aaguid = ASN1:FORMAT:HEX,OCTETSTRING:${ENV::OPENSK_AAGUID}
|
||||
84
tools/openssl/root-ca.conf
Normal file
84
tools/openssl/root-ca.conf
Normal file
@@ -0,0 +1,84 @@
|
||||
oid_section = OIDS
|
||||
|
||||
[ default ]
|
||||
ca = root-ca
|
||||
dir = ./crypto_data
|
||||
|
||||
[ req ]
|
||||
encrypt_key = yes
|
||||
default_md = sha256
|
||||
utf8 = yes
|
||||
string_mask = utf8only
|
||||
prompt = no
|
||||
distinguished_name = ca_dn
|
||||
req_extensions = ca_reqext
|
||||
|
||||
[ OIDS ]
|
||||
fido_attestation = 1.3.6.1.4.1.45724.2.1.1
|
||||
fido_aaguid = 1.3.6.1.4.1.45724.1.1.4
|
||||
|
||||
[ ca_dn ]
|
||||
countryName = "US"
|
||||
organizationName = "OpenSK"
|
||||
organizationalUnitName = "Authenticator Attestation"
|
||||
commonName = "OpenSK CA"
|
||||
|
||||
[ ca_reqext ]
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
basicConstraints = critical,CA:true
|
||||
subjectKeyIdentifier = hash
|
||||
|
||||
[ ca ]
|
||||
default_ca = root_ca
|
||||
|
||||
[ root_ca ]
|
||||
certificate = $dir/ca/$ca.pem
|
||||
private_key = $dir/ca/$ca/private/$ca.key
|
||||
new_certs_dir = $dir/ca/$ca
|
||||
serial = $dir/ca/$ca/db/$ca.pem.srl
|
||||
crlnumber = $dir/ca/$ca/db/$ca.pem.srl
|
||||
database = $dir/ca/$ca/db/$ca.db
|
||||
unique_subject = no
|
||||
default_days = 36525
|
||||
default_md = sha256
|
||||
policy = match_pol
|
||||
email_in_dn = no
|
||||
preserve = no
|
||||
name_opt = ca_default
|
||||
cert_opt = ca_default
|
||||
copy_extensions = none
|
||||
x509_extensions = signing_ca_ext
|
||||
default_crl_days = 365
|
||||
crl_extensions = crl_ext
|
||||
|
||||
[ match_pol ]
|
||||
countryName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = match
|
||||
commonName = supplied
|
||||
|
||||
[ any_pol ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = optional
|
||||
emailAddress = optional
|
||||
|
||||
[ root_ca_ext ]
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
basicConstraints = critical,CA:true
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
fido_attestation = ASN1:FORMAT:HEX,BITSTRING:00
|
||||
|
||||
[ signing_ca_ext ]
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
basicConstraints = critical,CA:true,pathlen:0
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
[ crl_ext ]
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
91
tools/openssl/signing-ca.conf
Normal file
91
tools/openssl/signing-ca.conf
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
oid_section = OIDS
|
||||
|
||||
[ default ]
|
||||
ca = signing-ca
|
||||
dir = ./crypto_data
|
||||
|
||||
[ req ]
|
||||
default_bits = 4096
|
||||
encrypt_key = yes
|
||||
default_md = sha256
|
||||
utf8 = yes
|
||||
string_mask = utf8only
|
||||
prompt = no
|
||||
distinguished_name = ca_dn
|
||||
req_extensions = ca_reqext
|
||||
|
||||
[ OIDS ]
|
||||
fido_attestation = 1.3.6.1.4.1.45724.2.1.1
|
||||
fido_aaguid = 1.3.6.1.4.1.45724.1.1.4
|
||||
|
||||
[ ca_dn ]
|
||||
countryName = "US"
|
||||
organizationName = "OpenSK"
|
||||
organizationalUnitName = "Authenticator Attestation"
|
||||
commonName = "OpenSK Signing"
|
||||
|
||||
[ ca_reqext ]
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
basicConstraints = critical,CA:true,pathlen:0
|
||||
subjectKeyIdentifier = hash
|
||||
|
||||
[ ca ]
|
||||
default_ca = signing_ca
|
||||
|
||||
[ signing_ca ]
|
||||
certificate = $dir/ca/$ca.pem
|
||||
private_key = $dir/ca/$ca/private/$ca.key
|
||||
new_certs_dir = $dir/ca/$ca
|
||||
serial = $dir/ca/$ca/db/$ca.pem.srl
|
||||
crlnumber = $dir/ca/$ca/db/$ca.pem.srl
|
||||
database = $dir/ca/$ca/db/$ca.db
|
||||
unique_subject = no
|
||||
default_days = 35064
|
||||
default_md = sha256
|
||||
policy = match_pol
|
||||
email_in_dn = no
|
||||
preserve = no
|
||||
name_opt = ca_default
|
||||
cert_opt = ca_default
|
||||
copy_extensions = copy
|
||||
x509_extensions = fido_key_ext
|
||||
default_crl_days = 7
|
||||
crl_extensions = crl_ext
|
||||
|
||||
[ match_pol ]
|
||||
countryName = match
|
||||
organizationName = match
|
||||
organizationalUnitName = match
|
||||
commonName = supplied
|
||||
|
||||
[ any_pol ]
|
||||
countryName = optional
|
||||
stateOrProvinceName = optional
|
||||
localityName = optional
|
||||
organizationName = optional
|
||||
organizationalUnitName = optional
|
||||
commonName = optional
|
||||
emailAddress = optional
|
||||
|
||||
[ root_ca_ext ]
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
basicConstraints = critical,CA:true
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
[ signing_ca_ext ]
|
||||
keyUsage = critical,keyCertSign,cRLSign
|
||||
basicConstraints = critical,CA:true,pathlen:0
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
[ fido_key_ext ]
|
||||
keyUsage = critical,digitalSignature
|
||||
basicConstraints = CA:false
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
[ crl_ext ]
|
||||
authorityKeyIdentifier = keyid:always
|
||||
|
||||
Reference in New Issue
Block a user