2 Commits
develop ... 2.1

Author SHA1 Message Date
kaczmarczyck
893faa5113 Ask contributors to go to develop (#677)
* Ask contributors to go to develop

* Enables CI on other branches
2024-01-15 14:57:58 +01:00
kaczmarczyck
5bfd198278 Corrects the README for the 2.1 branch. (#664) 2023-11-09 17:32:14 +01:00
36 changed files with 682 additions and 765 deletions

View File

@@ -8,10 +8,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'google/OpenSK' if: github.repository == 'google/OpenSK'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: "true" submodules: "true"
- uses: actions/setup-python@v5 - uses: actions/setup-python@v1
with: with:
python-version: "3.10" python-version: "3.10"
- name: Set up OpenSK - name: Set up OpenSK

View File

@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
# Setup # Setup
- uses: actions/setup-python@v5 - uses: actions/setup-python@v1
with: with:
python-version: "3.10" python-version: "3.10"
- uses: actions-rs/cargo@v1 - uses: actions-rs/cargo@v1
@@ -15,7 +15,7 @@ jobs:
args: cargo-bloat args: cargo-bloat
# First run: PR # First run: PR
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: true submodules: true
- name: Set up OpenSK - 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 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 # Second run: PR
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: true submodules: true
ref: ${{ github.base_ref }} ref: ${{ github.base_ref }}

View File

@@ -5,8 +5,7 @@ on:
branches: branches:
- develop - develop
pull_request: pull_request:
branches: types: [opened, synchronize, reopened]
- develop
schedule: schedule:
- cron: 30 1 * * 2 # every Tuesday at 1:30 UTC - cron: 30 1 * * 2 # every Tuesday at 1:30 UTC
@@ -23,8 +22,8 @@ jobs:
permissions: permissions:
contents: read contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- uses: actions/setup-python@v5 - uses: actions/setup-python@v1
with: with:
python-version: "3.10" python-version: "3.10"
- run: ./setup.sh - run: ./setup.sh

View File

@@ -21,7 +21,7 @@ jobs:
fuzz-seconds: 600 fuzz-seconds: 600
dry-run: false dry-run: false
- name: Upload Crash - name: Upload Crash
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v1
if: failure() && steps.build.outcome == 'success' if: failure() && steps.build.outcome == 'success'
with: with:
name: artifacts name: artifacts

View File

@@ -13,12 +13,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: "true" submodules: "true"
- name: Install Rust toolchain - name: Install Rust toolchain
run: rustup show run: rustup show
- uses: actions/setup-python@v5 - uses: actions/setup-python@v1
with: with:
python-version: "3.10" python-version: "3.10"
- name: Set up OpenSK - name: Set up OpenSK

View File

@@ -12,10 +12,10 @@ jobs:
mdlint: mdlint:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
- name: markdownlint-cli - name: markdownlint-cli
uses: nosborn/github-action-markdown-cli@v3 uses: nosborn/github-action-markdown-cli@v1.1.1
with: with:
files: '**/*.md' files: '**/*.md'
config_file: '.markdownlint.json' ignore_files: "third_party/*"
ignore_files: 'third_party/*' config_file: ".markdownlint.json"

View File

@@ -13,12 +13,12 @@ jobs:
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v2
with: with:
submodules: "true" submodules: "true"
- name: Install Rust toolchain - name: Install Rust toolchain
run: rustup show run: rustup show
- uses: actions/setup-python@v5 - uses: actions/setup-python@v1
with: with:
python-version: "3.10" python-version: "3.10"
- name: Set up OpenSK - name: Set up OpenSK
@@ -30,7 +30,7 @@ jobs:
run: ./maintainers/reproduce_hashes.sh run: ./maintainers/reproduce_hashes.sh
- name: Upload reproduced binaries - name: Upload reproduced binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v1
with: with:
name: reproduced-${{ matrix.os }} name: reproduced-${{ matrix.os }}
path: reproducible/reproduced.tar path: reproducible/reproduced.tar

View File

@@ -1,22 +1,35 @@
{ {
"default": true, "default": true,
"MD003": { "heading-style": {
"style": "atx" "style": "atx"
}, },
"MD007": { "no-trailing-spaces": {
"indent": 4
},
"MD009": {
"br_spaces": 0, "br_spaces": 0,
"strict": true "strict": true
}, },
"MD013": { "ul-indent": {
"indent": 4
},
"line-length": {
"line_length": 80, "line_length": 80,
"code_blocks": false "code_blocks": false
}, },
"MD033": { "list-marker-space": {
"ol_single": 2,
"ol_multi": 2,
"ul_single": 3,
"ul_multi": 3
},
"no-inline-html": {
"allowed_elements": [ "allowed_elements": [
"img" "img"
] ]
},
"fenced-code-language": true,
"code-block-style": {
"style": "fenced"
},
"code-fence-style": {
"style": "backtick"
} }
} }

View File

@@ -1,10 +1,7 @@
# <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px"> # <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px">
![markdownlint](https://github.com/google/OpenSK/workflows/markdownlint/badge.svg?branch=develop) ![markdownlint](https://github.com/google/OpenSK/workflows/markdownlint/badge.svg?branch=2.1)
![pylint](https://github.com/google/OpenSK/workflows/pylint/badge.svg?branch=develop) [![Coverage Status](https://coveralls.io/repos/github/google/OpenSK/badge.svg?branch=2.1)](https://coveralls.io/github/google/OpenSK?branch=2.1)
![Cargo check](https://github.com/google/OpenSK/workflows/Cargo%20check/badge.svg?branch=develop)
![Cargo format](https://github.com/google/OpenSK/workflows/Cargo%20format/badge.svg?branch=develop)
[![Coverage Status](https://coveralls.io/repos/github/google/OpenSK/badge.svg?branch=develop)](https://coveralls.io/github/google/OpenSK?branch=develop)
*News:* *News:*
@@ -27,15 +24,16 @@ enclosure!
You can run OpenSK as a [Tock OS](https://tockos.org) application, or use the You can run OpenSK as a [Tock OS](https://tockos.org) application, or use the
library to bring OpenSK to your own hardware. library to bring OpenSK to your own hardware.
You are viewing the branch for developers. New features are developed here. You are viewing the CTAP 2.1 version. This branch fixes bugs, but doesn't
Go to the default branch for a more stable version of OpenSK. implement new features. If you want to contribute, go to the
[develop branch](https://github.com/google/OpenSK/tree/develop).
### FIDO2 ### FIDO2
OpenSK's version that implemented CTAP 2.0 was certified by the FIDO Alliance. OpenSK's version that implemented CTAP 2.0 was certified by the FIDO Alliance.
The develop branch tracks the latest release version of the This branch implements version 2.1 of the
[CTAP specification](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html). [CTAP specification](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-errata-20220621.html).
This branch is not FIDO certified. This branch is not FIDO certified.
OpenSK supports U2F, and non-discoverable credentials created with either OpenSK supports U2F, and non-discoverable credentials created with either
protocol are compatible with the other. protocol are compatible with the other.

View File

@@ -23,7 +23,6 @@ import argparse
import collections import collections
import copy import copy
import os import os
from serial.tools import list_ports
import shutil import shutil
import subprocess import subprocess
import sys import sys
@@ -203,12 +202,6 @@ def assert_python_library(module: str):
f"Try to run: pip3 install {module}")) 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): class RemoveConstAction(argparse.Action):
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
@@ -710,10 +703,10 @@ class OpenSKInstaller:
fatal("This board doesn't seem to support flashing through pyocd.") fatal("This board doesn't seem to support flashing through pyocd.")
if self.args.programmer == "nordicdfu": if self.args.programmer == "nordicdfu":
assert_python_library("intelhex")
assert_mandatory_binary("nrfutil") assert_mandatory_binary("nrfutil")
nrfutil_version = self.checked_command_output(["nrfutil", "version"]) assert_python_library("intelhex")
nrfutil_version = nrfutil_version.removeprefix("nrfutil version ") assert_python_library("nordicsemi.lister")
nrfutil_version = __import__("nordicsemi.version").version.NRFUTIL_VERSION
if not nrfutil_version.startswith("6."): if not nrfutil_version.startswith("6."):
fatal(("You need to install nrfutil python3 package v6.0 or above. " fatal(("You need to install nrfutil python3 package v6.0 or above. "
f"Found: v{nrfutil_version}. If you use Python >= 3.11, please " f"Found: v{nrfutil_version}. If you use Python >= 3.11, please "
@@ -819,17 +812,22 @@ class OpenSKInstaller:
info("Press [ENTER] when ready.") info("Press [ENTER] when ready.")
_ = input() _ = input()
# Search for the DFU devices # Search for the DFU devices
serial_numbers = list_serials(0x1915, 0x521F) serial_number = []
if not serial_numbers: # 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:
fatal("Couldn't find any DFU device on your system.") fatal("Couldn't find any DFU device on your system.")
if len(serial_numbers) > 1: if len(serial_number) > 1:
fatal("Multiple DFU devices are detected. Please only connect one.") fatal("Multiple DFU devices are detected. Please only connect one.")
# Run the command without capturing stdout so that we show progress # Run the command without capturing stdout so that we show progress
info("Flashing device using DFU...") info("Flashing device using DFU...")
dfu_return_code = subprocess.run( dfu_return_code = subprocess.run(
[ [
"nrfutil", "dfu", "usb-serial", f"--package={dfu_pkg_file}", "nrfutil", "dfu", "usb-serial", f"--package={dfu_pkg_file}",
f"--serial-number={serial_numbers[0]}" f"--serial-number={serial_number[0]}"
], ],
check=False, check=False,
timeout=None, timeout=None,

View File

@@ -19,9 +19,6 @@ customize it.
### Flashing using DFU (preferred method) ### Flashing using DFU (preferred method)
You need `nrfutil` version 6. The [install manual](../install.md) has
setup instructions.
To flash the firmware, run: To flash the firmware, run:
```shell ```shell

View File

@@ -1,7 +1,8 @@
# How to Contribute # How to Contribute
We'd love to accept your patches and contributions to this project. There are We'd love to accept your patches and contributions to this project.
just a few small guidelines you need to follow. Please base your pull requests on the `develop` branch.
There are just a few small guidelines you need to follow.
## Contributor License Agreement ## Contributor License Agreement

View File

@@ -7,18 +7,18 @@
All the generated certificates and private keys are stored in the directory All the generated certificates and private keys are stored in the directory
`crypto_data/`. The expected content after running our `setup.sh` script is: `crypto_data/`. The expected content after running our `setup.sh` script is:
| File | Purpose | File | Purpose
| ------------------------ | ----------------------------------------------- | ------------------------ | --------------------------------------------------------
| `aaguid.txt` | Text file containaing the AAGUID value | `aaguid.txt` | Text file containaing the AAGUID value
| `opensk_ca.csr` | Certificate sign request for the Root CA | `opensk_ca.csr` | Certificate sign request for the Root CA
| `opensk_ca.key` | ECC secp256r1 private key used 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.pem` | PEM encoded certificate of the Root CA
| `opensk_ca.srl` | File generated by OpenSSL | `opensk_ca.srl` | File generated by OpenSSL
| `opensk_cert.csr` | CSR for attestation certificate | `opensk_cert.csr` | Certificate sign request for the attestation certificate
| `opensk_cert.pem` | PEM encoded certificate for the authenticator | `opensk_cert.pem` | PEM encoded certificate used for the authenticator
| `opensk.key` | ECC secp256r1 private key for the autenticator | `opensk.key` | ECC secp256r1 private key used for the autenticator
| `opensk_upgrade.key` | Private key for signing upgrades through CTAP | `opensk_upgrade.key` | Private key for signing upgrades through CTAP
| `opensk_upgrade_pub.pem` | Public key for verifying upgrades | `opensk_upgrade_pub.pem` | Public key added to the firmware for verifying upgrades
If you want to use your own attestation certificate and private key, If you want to use your own attestation certificate and private key,
replace the `opensk_cert.pem` and `opensk.key` files. The script at replace the `opensk_cert.pem` and `opensk.key` files. The script at

View File

@@ -107,3 +107,31 @@ alloc[256, 1] = 0x2002401c (2 ptrs, 384 bytes)
# After this operation, 1 pointers are allocated, totalling 512 bytes. # After this operation, 1 pointers are allocated, totalling 512 bytes.
dealloc[64, 1] = 0x2002410c (1 ptrs, 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
```

View File

@@ -25,8 +25,8 @@ following:
* python3 and pip (can be installed with the `python3-pip` package on Debian) * 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 * the OpenSSL command line tool (can be installed and configured with the
`libssl-dev` and `pkg-config` packages on Debian) `libssl-dev` and `pkg-config` packages on Debian)
* `nrfutil` (pip package of the same name), if you want to flash * `nrfutil` (can be installed using `pip3 install nrfutil`) if you want to flash
a device with DFU. Read the disclaimer below. a device with DFU
* `uuid-runtime` if you are missing the `uuidgen` command. * `uuid-runtime` if you are missing the `uuidgen` command.
* `llvm` and `gcc-arm-none-eabi` if you want to use the upgradability feature. * `llvm` and `gcc-arm-none-eabi` if you want to use the upgradability feature.
@@ -37,17 +37,16 @@ instructions to appropriate binaries for your system.
The scripts provided in this project have been tested under Linux and OS X. We The scripts provided in this project have been tested under Linux and OS X. We
haven't tested them on Windows and other platforms. haven't tested them on Windows and other platforms.
You need `nrfutil` version 6, if you want to flash over DFU. If you use Python newer than 3.10, then nrfutil for flashing over DFU is
The tool doesn't support Python newer than 3.10. Therefore, we don't officially currently not supported. Please use Python 3.10, or play around with [Nordic's
support DFU for other versions. If you want to try regardless, new tool](https://www.nordicsemi.com/Products/Development-tools/nrf-util)
[Nordic's new tool](https://www.nordicsemi.com/Products/Development-tools/nrf-util) instead.
might work for you.
### Compiling the firmware ### Compiling the firmware
If this is your first time installing OpenSK, please skip directly to If this is your first time installing OpenSK, please skip directly to
[Initial setup](#initial-setup). Else, see [Initial setup](#Initial-setup). Else, see
[Updating your setup](#updating-your-setup) below. [Updating your setup](#Updating-your-setup) below.
#### Updating your setup #### Updating your setup
@@ -62,23 +61,23 @@ following steps:
./setup.sh ./setup.sh
``` ```
* Flash your board according to the [instructions below](#flashing-a-firmware). * 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.
If you come from an OpenSK version before the 2.0 certified one, your credential This reset also clears the certificate. For a privacy discussion, see the
storage is not backwards compatible and you have to reset it. :warning: You will [certificate section in Customization](customization.md#Certificate-considerations).
lose logins to all websites that you registered with OpenSK. To erase your If you want to reinstall it, you also need to rerun:
persistent storage, run the deploy script twice: Once with the application
parameter `--erase_storage`, and once with `--opensk` as usual.
This reset also clears the certificate. For a privacy discussion, see the ```shell
[certificate section in Customization](customization.md#Certificate-considerations). ./tools/configure.py \
If you want to reinstall it, you also need to rerun:
```shell
./tools/configure.py \
--certificate=crypto_data/opensk_cert.pem \ --certificate=crypto_data/opensk_cert.pem \
--private-key=crypto_data/opensk.key --private-key=crypto_data/opensk.key
``` ```
#### Initial setup #### Initial setup
@@ -140,7 +139,7 @@ From here on, please follow the instructions for your hardware:
### Advanced installation ### Advanced installation
We recommend that you flash your development board with JTAG and dongles with 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: above. However, we support other programmers:
* OpenOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk * OpenOCD: `./deploy.py --board=nrf52840_dongle_opensk --opensk

View File

@@ -113,14 +113,3 @@ 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);
}
}

View File

@@ -14,7 +14,7 @@
use core::convert::TryFrom; use core::convert::TryFrom;
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum UsbEndpoint { pub enum UsbEndpoint {
MainHid = 1, MainHid = 1,
#[cfg(feature = "vendor_hid")] #[cfg(feature = "vendor_hid")]
@@ -40,7 +40,6 @@ pub enum SendOrRecvStatus {
Received(UsbEndpoint), Received(UsbEndpoint),
} }
#[derive(Debug, PartialEq, Eq)]
pub struct SendOrRecvError; pub struct SendOrRecvError;
pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>; pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;
@@ -48,16 +47,3 @@ pub type SendOrRecvResult = Result<SendOrRecvStatus, SendOrRecvError>;
pub trait HidConnection { pub trait HidConnection {
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult; 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));
}
}

View File

@@ -457,47 +457,4 @@ mod test {
fn test_invariants() { fn test_invariants() {
assert!(is_valid(&DEFAULT_CUSTOMIZATION)); 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);
}
} }

View File

@@ -140,12 +140,6 @@ 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. /// Gets a reference to the PIN protocol of the given version.
fn get_pin_protocol(&self, pin_uv_auth_protocol: PinUvAuthProtocol) -> &PinProtocol<E> { fn get_pin_protocol(&self, pin_uv_auth_protocol: PinUvAuthProtocol) -> &PinProtocol<E> {
match pin_uv_auth_protocol { match pin_uv_auth_protocol {
@@ -1513,11 +1507,9 @@ mod test {
let mut env = TestEnv::default(); let mut env = TestEnv::default();
let mut client_pin = ClientPin::<TestEnv>::new(&mut env); let mut client_pin = ClientPin::<TestEnv>::new(&mut env);
let message = [0xAA]; let message = [0xAA];
assert!(!client_pin.has_token(&mut env));
client_pin client_pin
.pin_uv_auth_token_state .pin_uv_auth_token_state
.begin_using_pin_uv_auth_token(&mut env); .begin_using_pin_uv_auth_token(&mut env);
assert!(client_pin.has_token(&mut env));
let pin_uv_auth_token_v1 = client_pin let pin_uv_auth_token_v1 = client_pin
.get_pin_protocol(PinUvAuthProtocol::V1) .get_pin_protocol(PinUvAuthProtocol::V1)
@@ -1663,7 +1655,6 @@ mod test {
.has_permissions_rp_id("example.com"), .has_permissions_rp_id("example.com"),
Ok(()) Ok(())
); );
assert!(client_pin.has_token(&mut env));
env.clock().advance(30001); env.clock().advance(30001);
client_pin.update_timeouts(&mut env); client_pin.update_timeouts(&mut env);
@@ -1681,7 +1672,6 @@ mod test {
.has_permissions_rp_id("example.com"), .has_permissions_rp_id("example.com"),
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID) Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
); );
assert!(!client_pin.has_token(&mut env));
} }
#[test] #[test]

View File

@@ -23,8 +23,6 @@ use super::response::{AuthenticatorCredentialManagementResponse, ResponseData};
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use super::{Channel, StatefulCommand, StatefulPermission}; use super::{Channel, StatefulCommand, StatefulPermission};
use crate::api::crypto::sha256::Sha256; use crate::api::crypto::sha256::Sha256;
use crate::api::customization::Customization;
use crate::ctap::data_formats::CredentialProtectionPolicy;
use crate::ctap::storage; use crate::ctap::storage;
use crate::env::{Env, Sha}; use crate::env::{Env, Sha};
use alloc::collections::BTreeSet; use alloc::collections::BTreeSet;
@@ -64,7 +62,6 @@ fn enumerate_rps_response<E: Env>(
/// Generates the response for subcommands enumerating credentials. /// Generates the response for subcommands enumerating credentials.
fn enumerate_credentials_response<E: Env>( fn enumerate_credentials_response<E: Env>(
env: &mut E,
credential: PublicKeyCredentialSource, credential: PublicKeyCredentialSource,
total_credentials: Option<u64>, total_credentials: Option<u64>,
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> { ) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
@@ -94,15 +91,12 @@ fn enumerate_credentials_response<E: Env>(
transports: None, // You can set USB as a hint here. transports: None, // You can set USB as a hint here.
}; };
let public_key = private_key.get_pub_key::<E>()?; 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 { Ok(AuthenticatorCredentialManagementResponse {
user: Some(user), user: Some(user),
credential_id: Some(credential_id), credential_id: Some(credential_id),
public_key: Some(public_key), public_key: Some(public_key),
total_credentials, total_credentials,
cred_protect, cred_protect: cred_protect_policy,
large_blob_key, large_blob_key,
..Default::default() ..Default::default()
}) })
@@ -207,7 +201,7 @@ fn process_enumerate_credentials_begin<E: Env>(
channel, channel,
); );
} }
enumerate_credentials_response(env, credential, Some(total_credentials as u64)) enumerate_credentials_response::<E>(credential, Some(total_credentials as u64))
} }
/// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement. /// Processes the subcommand enumerateCredentialsGetNextCredential for CredentialManagement.
@@ -217,7 +211,7 @@ fn process_enumerate_credentials_get_next_credential<E: Env>(
) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> { ) -> Result<AuthenticatorCredentialManagementResponse, Ctap2StatusCode> {
let credential_key = stateful_command_permission.next_enumerate_credential(env)?; let credential_key = stateful_command_permission.next_enumerate_credential(env)?;
let credential = storage::get_credential(env, credential_key)?; let credential = storage::get_credential(env, credential_key)?;
enumerate_credentials_response(env, credential, None) enumerate_credentials_response::<E>(credential, None)
} }
/// Processes the subcommand deleteCredential for CredentialManagement. /// Processes the subcommand deleteCredential for CredentialManagement.

View File

@@ -410,26 +410,11 @@ pub struct StatefulPermission<E: Env> {
channel: Option<Channel>, 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> { impl<E: Env> StatefulPermission<E> {
/// Creates the command state at device startup. /// Creates the command state at device startup.
/// ///
/// Resets are only possible after a power cycle. Therefore, there is no way to grant the Reset /// Resets are only possible after a power cycle. Therefore, initialization
/// permission outside of this function. If you initialize the app without a power cycle /// means allowing Reset, and Reset cannot be granted later.
/// (potentially after waking up from sleep), call `default` instead.
pub fn new_reset(env: &mut E) -> StatefulPermission<E> { pub fn new_reset(env: &mut E) -> StatefulPermission<E> {
StatefulPermission { StatefulPermission {
permission: env.clock().make_timer(RESET_TIMEOUT_DURATION_MS), permission: env.clock().make_timer(RESET_TIMEOUT_DURATION_MS),
@@ -558,16 +543,11 @@ impl<E: Env> CtapState<E> {
pub fn new(env: &mut E) -> Self { pub fn new(env: &mut E) -> Self {
storage::init(env).ok().unwrap(); storage::init(env).ok().unwrap();
let client_pin = ClientPin::new(env); let client_pin = ClientPin::new(env);
let stateful_command_permission = if env.boots_after_soft_reset() {
StatefulPermission::default()
} else {
StatefulPermission::new_reset(env)
};
CtapState { CtapState {
client_pin, client_pin,
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
u2f_up_state: U2fUserPresenceState::new(), u2f_up_state: U2fUserPresenceState::new(),
stateful_command_permission, stateful_command_permission: StatefulPermission::new_reset(env),
large_blobs: LargeBlobs::new(), large_blobs: LargeBlobs::new(),
} }
} }
@@ -602,12 +582,6 @@ impl<E: Env> CtapState<E> {
self.stateful_command_permission.clear_old_channels(channel); 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( pub fn process_command(
&mut self, &mut self,
env: &mut E, env: &mut E,
@@ -1308,19 +1282,15 @@ impl<E: Env> CtapState<E> {
options.append(&mut vec![ options.append(&mut vec![
(String::from("rk"), true), (String::from("rk"), true),
(String::from("up"), true), (String::from("up"), true),
(String::from("alwaysUv"), has_always_uv),
(String::from("credMgmt"), true), (String::from("credMgmt"), true),
#[cfg(feature = "config_command")]
(String::from("authnrCfg"), true), (String::from("authnrCfg"), true),
(String::from("clientPin"), storage::pin_hash(env)?.is_some()), (String::from("clientPin"), storage::pin_hash(env)?.is_some()),
(String::from("largeBlobs"), true), (String::from("largeBlobs"), true),
(String::from("pinUvAuthToken"), true), (String::from("pinUvAuthToken"), true),
#[cfg(feature = "config_command")]
(String::from("setMinPINLength"), true), (String::from("setMinPINLength"), true),
(String::from("makeCredUvNotRqd"), !has_always_uv), (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]; let mut pin_protocols = vec![PinUvAuthProtocol::V2 as u64];
if env.customization().allows_pin_protocol_v1() { if env.customization().allows_pin_protocol_v1() {
pin_protocols.push(PinUvAuthProtocol::V1 as u64); pin_protocols.push(PinUvAuthProtocol::V1 as u64);
@@ -1529,15 +1499,12 @@ mod test {
"ep" => env.customization().enterprise_attestation_mode().map(|_| false), "ep" => env.customization().enterprise_attestation_mode().map(|_| false),
"rk" => true, "rk" => true,
"up" => true, "up" => true,
#[cfg(feature = "config_command")]
"alwaysUv" => false, "alwaysUv" => false,
"credMgmt" => true, "credMgmt" => true,
#[cfg(feature = "config_command")]
"authnrCfg" => true, "authnrCfg" => true,
"clientPin" => false, "clientPin" => false,
"largeBlobs" => true, "largeBlobs" => true,
"pinUvAuthToken" => true, "pinUvAuthToken" => true,
#[cfg(feature = "config_command")]
"setMinPINLength" => true, "setMinPINLength" => true,
"makeCredUvNotRqd" => true, "makeCredUvNotRqd" => true,
}, },

View File

@@ -76,9 +76,6 @@ pub trait Env {
#[cfg(feature = "vendor_hid")] #[cfg(feature = "vendor_hid")]
fn vendor_hid_connection(&mut self) -> &mut Self::HidConnection; 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. /// Option to return a firmware version that is shown as device info.
fn firmware_version(&self) -> Option<u64> { fn firmware_version(&self) -> Option<u64> {
None None

View File

@@ -34,7 +34,6 @@ pub struct TestEnv {
store: Store<BufferStorage>, store: Store<BufferStorage>,
customization: TestCustomization, customization: TestCustomization,
clock: TestClock, clock: TestClock,
soft_reset: bool,
} }
pub type TestRng = StdRng; pub type TestRng = StdRng;
@@ -128,7 +127,6 @@ impl Default for TestEnv {
store, store,
customization, customization,
clock, clock,
soft_reset: false,
} }
} }
} }
@@ -141,10 +139,6 @@ impl TestEnv {
pub fn seed_rng_from_u64(&mut self, seed: u64) { pub fn seed_rng_from_u64(&mut self, seed: u64) {
self.rng = StdRng::seed_from_u64(seed); self.rng = StdRng::seed_from_u64(seed);
} }
pub fn set_boots_after_soft_reset(&mut self, value: bool) {
self.soft_reset = value;
}
} }
impl TestUserPresence { impl TestUserPresence {
@@ -233,10 +227,6 @@ impl Env for TestEnv {
self self
} }
fn boots_after_soft_reset(&self) -> bool {
self.soft_reset
}
fn firmware_version(&self) -> Option<u64> { fn firmware_version(&self) -> Option<u64> {
Some(0) Some(0)
} }
@@ -257,14 +247,4 @@ mod test {
clock.advance(1); clock.advance(1);
assert!(clock.is_elapsed(&timer)); 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());
}
} }

View File

@@ -81,6 +81,10 @@ impl<E: Env> Ctap<E> {
&mut self.state &mut self.state
} }
pub fn hid(&mut self) -> &mut MainHid<E> {
&mut self.hid
}
pub fn env(&mut self) -> &mut E { pub fn env(&mut self) -> &mut E {
&mut self.env &mut self.env
} }
@@ -116,10 +120,6 @@ impl<E: Env> Ctap<E> {
self.hid.should_wink(&mut self.env) 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")] #[cfg(feature = "with_ctap1")]
pub fn u2f_grant_user_presence(&mut self) { pub fn u2f_grant_user_presence(&mut self) {
self.state.u2f_grant_user_presence(&mut self.env) self.state.u2f_grant_user_presence(&mut self.env)
@@ -134,7 +134,6 @@ impl<E: Env> Ctap<E> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::ctap::status_code::Ctap2StatusCode;
use crate::env::test::TestEnv; use crate::env::test::TestEnv;
/// Assembles a packet for a payload that fits into one packet. /// Assembles a packet for a payload that fits into one packet.
@@ -201,58 +200,6 @@ mod test {
assert_eq!(response_packet[4], 0xBF); 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] #[test]
#[cfg(feature = "vendor_hid")] #[cfg(feature = "vendor_hid")]
fn test_locked_transport() { fn test_locked_transport() {
@@ -275,14 +222,4 @@ mod test {
let response_packet = init_response.next().unwrap(); let response_packet = init_response.next().unwrap();
assert_eq!(response_packet[4], 0xBF); 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());
}
} }

View File

@@ -35,6 +35,7 @@ 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
cargo check --release --target=thumbv7em-none-eabi --examples --features with_nfc 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 --target=thumbv7em-none-eabi --manifest-path bootloader/Cargo.toml
cargo check --release --manifest-path tools/heapviz/Cargo.toml
echo "Checking Rust formatting..." echo "Checking Rust formatting..."
cargo fmt -- --check cargo fmt -- --check
@@ -45,6 +46,7 @@ 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/Cargo.toml -- --check
cargo fmt --manifest-path libraries/persistent_store/fuzz/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 libraries/crypto/Cargo.toml -- --check
cargo fmt --manifest-path tools/heapviz/Cargo.toml -- --check
cargo fmt --manifest-path bootloader/Cargo.toml -- --check cargo fmt --manifest-path bootloader/Cargo.toml -- --check
echo "Checking Python formatting..." echo "Checking Python formatting..."
@@ -88,6 +90,7 @@ cargo test --manifest-path libraries/cbor/Cargo.toml
cargo test --manifest-path libraries/persistent_store/Cargo.toml --features std cargo test --manifest-path libraries/persistent_store/Cargo.toml --features std
# Running release mode to speed up. This library is legacy anyway. # 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 libraries/crypto/Cargo.toml --features std --release
cargo test --manifest-path tools/heapviz/Cargo.toml
echo "Checking that boards build properly..." echo "Checking that boards build properly..."
make -C third_party/tock/boards/nordic/nrf52840dk_opensk make -C third_party/tock/boards/nordic/nrf52840dk_opensk

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Copyright 2019-2023 Google LLC # Copyright 2019 Google LLC
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -40,11 +40,7 @@ check_command "$PIP"
# Ensure we have certificates, keys, etc. so that the tests can run # Ensure we have certificates, keys, etc. so that the tests can run
source tools/gen_key_materials.sh source tools/gen_key_materials.sh
generate_pki N generate_crypto_materials N
if [ ! -f "crypto_data/opensk.key" -o ! -f "crypto_data/opensk_cert.pem" ]
then
generate_new_batch
fi
rustup show rustup show
"$PIP" install --upgrade -r requirements.txt "$PIP" install --upgrade -r requirements.txt

4
src/env/tock/mod.rs vendored
View File

@@ -452,10 +452,6 @@ impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> E
commands::process_vendor_command(self, bytes, channel) commands::process_vendor_command(self, bytes, channel)
} }
fn boots_after_soft_reset(&self) -> bool {
false
}
fn firmware_version(&self) -> Option<u64> { fn firmware_version(&self) -> Option<u64> {
self.upgrade_storage self.upgrade_storage
.as_ref() .as_ref()

View File

@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Copyright 2019-2023 Google LLC # Copyright 2019 Google LLC
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -13,19 +13,20 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
generate_pki () { generate_crypto_materials () {
# Curve parameters # OpenSSL ext file location
local ecparams_file=crypto_data/ecparams.pem local openssl_ext_file=tools/openssl.ext
# OpenSK AAGUID # OpenSK AAGUID
local aaguid_file=crypto_data/aaguid.txt local aaguid_file=crypto_data/aaguid.txt
# Root CA key pair and certificate # Root CA key pair and certificate
local ca_priv_key=crypto_data/ca/root-ca/private/root-ca.key local ca_priv_key=crypto_data/opensk_ca.key
local ca_cert_name=crypto_data/ca/root-ca local ca_cert_name=crypto_data/opensk_ca
# Signing CA key pair and certificate # Attestation key pair and certificate that will be embedded into the
local signing_ca_priv_key=crypto_data/ca/signing-ca/private/signing-ca.key # firmware. The certificate will be signed by the Root CA.
local signing_ca_cert_name=crypto_data/ca/signing-ca local opensk_key=crypto_data/opensk.key
local opensk_cert_name=crypto_data/opensk_cert
# The upgrade private key is used for signing, the corresponding public key # The upgrade private key is used for signing, the corresponding public key
# will be COSE encoded and embedded into the firmware. # will be COSE encoded and embedded into the firmware.
@@ -35,9 +36,6 @@ generate_pki () {
# Allow invoker to override the command with a full path. # Allow invoker to override the command with a full path.
local openssl=${OPENSSL:-$(which openssl)} local openssl=${OPENSSL:-$(which openssl)}
# Print version for debug purposes
${openssl} version
# We need openssl command to continue # We need openssl command to continue
if [ ! -x "${openssl}" ] if [ ! -x "${openssl}" ]
then then
@@ -49,109 +47,55 @@ generate_pki () {
set -e set -e
force_generate="$1" force_generate="$1"
ask_for_password="$2" mkdir -p crypto_data
if [ ! -f "${ca_priv_key}" ]
if [ "${force_generate}" = "Y" ]
then then
# Remove old OpenSK certs and CRL "${openssl}" ecparam -genkey -name prime256v1 -out "${ca_priv_key}"
rm -rf crypto_data/crl crypto_data/certs
fi fi
openssl_keypwd="-nodes" if [ ! -f "${ca_cert_name}.pem" ]
openssl_batch="-batch"
if [ "${ask_for_password}" = "Y" ]
then 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 \ "${openssl}" req \
-new \ -new \
-config tools/openssl/root-ca.conf \ -key "${ca_priv_key}" \
-out "${ca_cert_name}.csr" \ -out "${ca_cert_name}.csr" \
-keyout "${ca_priv_key}" \ -subj "/CN=OpenSK CA"
"${openssl_keypwd}" \ "${openssl}" x509 \
"${openssl_batch}" \ -trustout \
-newkey "ec:${ecparams_file}" -req \
-days 7305 \
# Make root CA certificate
"${openssl}" ca \
-selfsign \
-config tools/openssl/root-ca.conf \
"${openssl_batch}" \
-in "${ca_cert_name}.csr" \ -in "${ca_cert_name}.csr" \
-signkey "${ca_priv_key}" \
-outform pem \
-out "${ca_cert_name}.pem" \ -out "${ca_cert_name}.pem" \
-extensions root_ca_ext -sha256
fi fi
if [ "${force_generate}" = "Y" -o ! -f "${signing_ca_cert_name}.pem" ] 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" ]
then then
# Create signing CA request
"${openssl}" req \ "${openssl}" req \
-new \ -new \
-config tools/openssl/signing-ca.conf \ -key "${opensk_key}" \
-out "${signing_ca_cert_name}.csr" \ -out "${opensk_cert_name}.csr" \
-keyout "${signing_ca_priv_key}" \ -subj "/C=US/O=OpenSK/OU=Authenticator Attestation/CN=OpenSK Hacker Edition"
"${openssl_keypwd}" \ "${openssl}" x509 \
"${openssl_batch}" \ -req \
-newkey "ec:${ecparams_file}" -days 3652 \
-in "${opensk_cert_name}.csr" \
# Make signing CA certificate -CA "${ca_cert_name}.pem" \
"${openssl}" ca \ -extfile "${openssl_ext_file}" \
-config tools/openssl/root-ca.conf \ -CAkey "${ca_priv_key}" \
"${openssl_batch}" \ -CAcreateserial \
-in "${signing_ca_cert_name}.csr" \ -outform pem \
-out "${signing_ca_cert_name}.pem" \ -out "${opensk_cert_name}.pem" \
-extensions signing_ca_ext -sha256
fi fi
# Create firmware update key pair
if [ "${force_generate}" = "Y" -o ! -f "${opensk_upgrade}" ] if [ "${force_generate}" = "Y" -o ! -f "${opensk_upgrade}" ]
then then
"${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_upgrade}" "${openssl}" ecparam -genkey -name prime256v1 -out "${opensk_upgrade}"
@@ -162,88 +106,11 @@ generate_pki () {
then then
"${openssl}" ec -in "${opensk_upgrade}" -pubout -out "${opensk_upgrade_pub}" "${openssl}" ec -in "${opensk_upgrade}" -pubout -out "${opensk_upgrade_pub}"
fi fi
if [ "${force_generate}" = "Y" -o ! -f "${aaguid_file}" ]
then
uuidgen > "${aaguid_file}"
fi
} }
generate_new_batch () { generate_crypto_materials "$1"
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 Normal file
View File

@@ -0,0 +1,194 @@
# 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"

14
tools/heapviz/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[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"

217
tools/heapviz/src/main.rs Normal file
View File

@@ -0,0 +1,217 @@
// 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
tools/openssl.ext Normal file
View File

@@ -0,0 +1 @@
basicConstraints=CA:FALSE

View File

@@ -1,26 +0,0 @@
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}

View File

@@ -1,84 +0,0 @@
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

View File

@@ -1,91 +0,0 @@
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