Merge branch 'develop' into usize_32_or_std

This commit is contained in:
Julien Cretin
2021-04-26 13:12:55 +02:00
92 changed files with 13884 additions and 6932 deletions
+2 -20
View File
@@ -42,12 +42,6 @@ jobs:
command: check
args: --target thumbv7em-none-eabi --release --features with_ctap1
- name: Check OpenSK with_ctap2_1
uses: actions-rs/cargo@v1
with:
command: check
args: --target thumbv7em-none-eabi --release --features with_ctap2_1
- name: Check OpenSK debug_ctap
uses: actions-rs/cargo@v1
with:
@@ -66,12 +60,6 @@ jobs:
command: check
args: --target thumbv7em-none-eabi --release --features debug_allocations
- name: Check OpenSK ram_storage
uses: actions-rs/cargo@v1
with:
command: check
args: --target thumbv7em-none-eabi --release --features ram_storage
- name: Check OpenSK verbose
uses: actions-rs/cargo@v1
with:
@@ -84,17 +72,11 @@ jobs:
command: check
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1
- name: Check OpenSK debug_ctap,with_ctap2_1
- name: Check OpenSK debug_ctap,with_ctap1,panic_console,debug_allocations,verbose
uses: actions-rs/cargo@v1
with:
command: check
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap2_1
- name: Check OpenSK debug_ctap,with_ctap1,with_ctap2_1,panic_console,debug_allocations,verbose
uses: actions-rs/cargo@v1
with:
command: check
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1,with_ctap2_1,panic_console,debug_allocations,verbose
args: --target thumbv7em-none-eabi --release --features debug_ctap,with_ctap1,,panic_console,debug_allocations,verbose
- name: Check examples
uses: actions-rs/cargo@v1
+2 -2
View File
@@ -33,10 +33,10 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path libraries/crypto/Cargo.toml --release --features std,derive_debug
args: --manifest-path libraries/crypto/Cargo.toml --release --features std
- name: Unit testing of crypto library (debug mode)
uses: actions-rs/cargo@v1
with:
command: test
args: --manifest-path libraries/crypto/Cargo.toml --features std,derive_debug
args: --manifest-path libraries/crypto/Cargo.toml --features std
-24
View File
@@ -51,27 +51,3 @@ jobs:
command: test
args: --features std,with_ctap1
- name: Unit testing of CTAP2 (release mode + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --release --features std,with_ctap2_1
- name: Unit testing of CTAP2 (debug mode + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --features std,with_ctap2_1
- name: Unit testing of CTAP2 (release mode + CTAP1 + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --release --features std,with_ctap1,with_ctap2_1
- name: Unit testing of CTAP2 (debug mode + CTAP1 + CTAP2.1)
uses: actions-rs/cargo@v1
with:
command: test
args: --features std,with_ctap1,with_ctap2_1
@@ -13,6 +13,11 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Unit testing of Persistent store library (release mode)
uses: actions-rs/cargo@v1
with:
+1
View File
@@ -11,3 +11,4 @@ Cargo.lock
/reproducible/binaries.sha256sum
/reproducible/elf2tab.txt
/reproducible/reproduced.tar
__pycache__
+2
View File
@@ -1,6 +1,8 @@
[submodule "third_party/libtock-rs"]
path = third_party/libtock-rs
url = https://github.com/tock/libtock-rs
ignore = dirty
[submodule "third_party/tock"]
path = third_party/tock
url = https://github.com/tock/tock
ignore = dirty
-7
View File
@@ -1,7 +0,0 @@
{
"recommendations": [
"davidanson.vscode-markdownlint",
"rust-lang.rust",
"ms-python.python"
]
}
-27
View File
@@ -1,27 +0,0 @@
{
"clang-format.fallbackStyle": "google",
"editor.detectIndentation": true,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.insertSpaces": true,
"editor.tabSize": 4,
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
"rust-client.channel": "nightly",
// The toolchain is updated from time to time so let's make sure that RLS is updated too
"rust-client.updateOnStartup": true,
"rust.clippy_preference": "on",
// Try to make VSCode formating as close as possible to the Google style.
"python.formatting.provider": "yapf",
"python.formatting.yapfArgs": [
"--style=yapf"
],
"python.linting.enabled": true,
"python.linting.lintOnSave": true,
"python.linting.pylintEnabled": true,
"python.linting.pylintPath": "pylint",
"[python]": {
"editor.tabSize": 2
},
}
+5 -6
View File
@@ -1,6 +1,6 @@
[package]
name = "ctap2"
version = "0.1.0"
version = "1.0.0"
authors = [
"Fabian Kaczmarczyck <kaczmarczyck@google.com>",
"Guillaume Endignoux <guillaumee@google.com>",
@@ -15,19 +15,18 @@ libtock_drivers = { path = "third_party/libtock-drivers" }
lang_items = { path = "third_party/lang-items" }
cbor = { path = "libraries/cbor" }
crypto = { path = "libraries/crypto" }
persistent_store = { path = "libraries/persistent_store" }
byteorder = { version = "1", default-features = false }
arrayref = "0.3.6"
subtle = { version = "2.2", default-features = false, features = ["nightly"] }
[features]
debug_allocations = ["lang_items/debug_allocations"]
debug_ctap = ["crypto/derive_debug", "libtock_drivers/debug_ctap"]
debug_ctap = ["libtock_drivers/debug_ctap"]
panic_console = ["lang_items/panic_console"]
std = ["cbor/std", "crypto/std", "crypto/derive_debug", "lang_items/std"]
ram_storage = []
std = ["cbor/std", "crypto/std", "lang_items/std", "persistent_store/std"]
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
with_ctap1 = ["crypto/with_ctap1"]
with_ctap2_1 = []
with_nfc = ["libtock_drivers/with_nfc"]
[dev-dependencies]
@@ -35,7 +34,6 @@ elf2tab = "0.6.0"
enum-iterator = "0.6.0"
[build-dependencies]
openssl = "0.10"
uuid = { version = "0.8", features = ["v4"] }
[profile.dev]
@@ -45,3 +43,4 @@ lto = true # Link Time Optimization usually reduces size of binaries and static
[profile.release]
panic = "abort"
lto = true # Link Time Optimization usually reduces size of binaries and static libraries
opt-level = "z"
+57
View File
@@ -0,0 +1,57 @@
{
"folders": [
{
"name": "OpenSK",
"path": "."
},
{
"name": "tock",
"path": "third_party/tock"
},
{
"name": "libtock-rs",
"path": "third_party/libtock-rs"
},
{
"name": "libtock-drivers",
"path": "third_party/libtock-drivers"
}
],
"settings": {
"clang-format.fallbackStyle": "google",
"editor.detectIndentation": true,
"editor.formatOnPaste": false,
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.insertSpaces": true,
"editor.tabSize": 4,
"files.insertFinalNewline": true,
"files.trimTrailingWhitespace": true,
// Ensure we use the toolchain we set in rust-toolchain file
"rust-client.channel": "default",
// The toolchain is updated from time to time so let's make sure that RLS is updated too
"rust-client.updateOnStartup": true,
"rust.clippy_preference": "on",
"rust.target": "thumbv7em-none-eabi",
"rust.all_targets": false,
// Try to make VSCode formating as close as possible to the Google style.
"python.formatting.provider": "yapf",
"python.formatting.yapfArgs": [
"--style=yapf"
],
"python.linting.enabled": true,
"python.linting.lintOnSave": true,
"python.linting.pylintEnabled": true,
"python.linting.pylintPath": "pylint",
"[python]": {
"editor.tabSize": 2
},
},
"extensions": {
"recommendations": [
"davidanson.vscode-markdownlint",
"rust-lang.rust",
"ms-python.python"
]
}
}
+48 -50
View File
@@ -1,6 +1,5 @@
# <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px">
[![Build Status](https://travis-ci.org/google/OpenSK.svg?branch=master)](https://travis-ci.org/google/OpenSK)
![markdownlint](https://github.com/google/OpenSK/workflows/markdownlint/badge.svg?branch=master)
![pylint](https://github.com/google/OpenSK/workflows/pylint/badge.svg?branch=master)
![Cargo check](https://github.com/google/OpenSK/workflows/Cargo%20check/badge.svg?branch=master)
@@ -25,14 +24,16 @@ few limitations:
### FIDO2
Although we tested and implemented our firmware based on the published
[CTAP2.0 specifications](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html),
our implementation was not reviewed nor officially tested and doesn't claim to
be FIDO Certified.
We started adding features of the upcoming next version of the
[CTAP2.1 specifications](https://fidoalliance.org/specs/fido2/fido-client-to-authenticator-protocol-v2.1-rd-20191217.html).
The development is currently between 2.0 and 2.1, with updates hidden behind a feature flag.
Please add the flag `--ctap2.1` to the deploy command to include them.
The stable branch implements the published
[CTAP2.0 specifications](https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html)
and is FIDO certified.
<img alt="FIDO2 certified L1" src="docs/img/FIDO2_Certified_L1.png" width="200px">
It already contains some preview features of 2.1, that you can try by adding the
flag `--ctap2.1` to the deploy command. The full
[CTAP2.1 specification](https://fidoalliance.org/specs/fido-v2.1-rd-20201208/fido-client-to-authenticator-protocol-v2.1-rd-20201208.html)
is work in progress in the develop branch and is tested less thoroughly.
### Cryptography
@@ -58,8 +59,8 @@ For a more detailed guide, please refer to our
./setup.sh
```
2. Next step is to install Tock OS as well as the OpenSK application on your
board (**Warning**: it will erase the locally stored credentials). Run:
1. Next step is to install Tock OS as well as the OpenSK application on your
board. Run:
```shell
# Nordic nRF52840-DK board
@@ -68,7 +69,17 @@ For a more detailed guide, please refer to our
./deploy.py --board=nrf52840_dongle --opensk
```
3. On Linux, you may want to avoid the need for `root` privileges to interact
1. Finally you need to inject the cryptographic material if you enabled
batch attestation or CTAP1/U2F compatibility (which is the case by
default):
```shell
./tools/configure.py \
--certificate=crypto_data/opensk_cert.pem \
--private-key=crypto_data/opensk.key
```
1. On Linux, you may want to avoid the need for `root` privileges to interact
with the key. For that purpose we provide a udev rule file that can be
installed with the following command:
@@ -83,33 +94,19 @@ 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 `main.rs`.
2. Decide whether you want to use batch attestation. There is a boolean flag in
`ctap/mod.rs`. It is mandatory for U2F, and you can create your own
self-signed certificate. The flag is used for FIDO2 and has some privacy
implications. Please check
[WebAuthn](https://www.w3.org/TR/webauthn/#attestation) for more
information.
3. Decide whether you want to use signature counters. Currently, only global
signature counters are implemented, as they are the default option for U2F.
The flag in `ctap/mod.rs` only turns them off for FIDO2. The most privacy
preserving solution is individual or no signature counters. Again, please
check [WebAuthn](https://www.w3.org/TR/webauthn/#signature-counter) for
documentation.
4. Depending on your available flash storage, choose an appropriate maximum
number of supported residential keys and number of pages in
`ctap/storage.rs`.
5. Change the default level for the credProtect extension in `ctap/mod.rs`.
When changing the default, resident credentials become undiscoverable without
user verification. This helps privacy, but can make usage less comfortable
for credentials that need less protection.
6. Increase the default minimum length for PINs in `ctap/storage.rs`.
The current minimum is 4. Values from 4 to 63 are allowed. Requiring longer
PINs can help establish trust between users and relying parties. It makes
user verification harder to break, but less convenient.
NIST recommends at least 6-digit PINs in section 5.1.9.1:
https://pages.nist.gov/800-63-3/sp800-63b.html
You can add relying parties to the list of readers of the minimum PIN length.
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.
### 3D printed enclosure
@@ -148,7 +145,7 @@ operation.
The additional output looks like the following.
```
```text
# Allocation of 256 byte(s), aligned on 1 byte(s). The allocated address is
# 0x2002401c. After this operation, 2 pointers have been allocated, totalling
# 384 bytes (the total heap usage may be larger, due to alignment and
@@ -163,12 +160,12 @@ 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).
* 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
@@ -177,10 +174,11 @@ 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.
* `--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
+1 -1
View File
@@ -48,7 +48,7 @@ static STRINGS: &'static [&'static str] = &[
// Product
"OpenSK",
// Serial number
"v0.1",
"v1.0",
];
// State for loading and holding applications.
-59
View File
@@ -12,11 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use openssl::asn1;
use openssl::ec;
use openssl::nid::Nid;
use openssl::pkey::PKey;
use openssl::x509;
use std::env;
use std::fs::File;
use std::io::Read;
@@ -25,65 +20,11 @@ use std::path::Path;
use uuid::Uuid;
fn main() {
println!("cargo:rerun-if-changed=crypto_data/opensk.key");
println!("cargo:rerun-if-changed=crypto_data/opensk_cert.pem");
println!("cargo:rerun-if-changed=crypto_data/aaguid.txt");
let out_dir = env::var_os("OUT_DIR").unwrap();
let priv_key_bin_path = Path::new(&out_dir).join("opensk_pkey.bin");
let cert_bin_path = Path::new(&out_dir).join("opensk_cert.bin");
let aaguid_bin_path = Path::new(&out_dir).join("opensk_aaguid.bin");
// Load the OpenSSL PEM ECC key
let ecc_data = include_bytes!("crypto_data/opensk.key");
let pkey =
ec::EcKey::private_key_from_pem(ecc_data).expect("Failed to load OpenSK private key file");
// Check key validity
pkey.check_key().unwrap();
assert_eq!(pkey.group().curve_name(), Some(Nid::X9_62_PRIME256V1));
// Private keys generated by OpenSSL have variable size but we only handle
// constant size. Serialization is done in big endian so if the size is less
// than 32 bytes, we need to prepend with null bytes.
// If the size is 33 bytes, this means the serialized BigInt is negative.
// Any other size is invalid.
let priv_key_hex = pkey.private_key().to_hex_str().unwrap();
let priv_key_vec = pkey.private_key().to_vec();
let key_len = priv_key_vec.len();
assert!(
key_len <= 33,
"Invalid private key (too big): {} ({:#?})",
priv_key_hex,
priv_key_vec,
);
// Copy OpenSSL generated key to our vec, starting from the end
let mut output_vec = [0u8; 32];
let min_key_len = std::cmp::min(key_len, 32);
output_vec[32 - min_key_len..].copy_from_slice(&priv_key_vec[key_len - min_key_len..]);
// Create the raw private key out of the OpenSSL data
let mut priv_key_bin_file = File::create(&priv_key_bin_path).unwrap();
priv_key_bin_file.write_all(&output_vec).unwrap();
// Convert the PEM certificate to DER and extract the serial for AAGUID
let input_pem_cert = include_bytes!("crypto_data/opensk_cert.pem");
let cert = x509::X509::from_pem(input_pem_cert).expect("Failed to load OpenSK certificate");
// Do some sanity check on the certificate
assert!(cert
.public_key()
.unwrap()
.public_eq(&PKey::from_ec_key(pkey).unwrap()));
let now = asn1::Asn1Time::days_from_now(0).unwrap();
assert!(cert.not_after() > now);
assert!(cert.not_before() <= now);
let mut cert_bin_file = File::create(&cert_bin_path).unwrap();
cert_bin_file.write_all(&cert.to_der().unwrap()).unwrap();
let mut aaguid_bin_file = File::create(&aaguid_bin_path).unwrap();
let mut aaguid_txt_file = File::open("crypto_data/aaguid.txt").unwrap();
let mut content = String::new();
+64 -16
View File
@@ -352,6 +352,7 @@ class OpenSKInstaller:
def build_opensk(self):
info("Building OpenSK application")
self._check_invariants()
self._build_app_or_example(is_example=False)
def _build_app_or_example(self, is_example):
@@ -390,6 +391,11 @@ class OpenSKInstaller:
# Create a TAB file
self.create_tab_file({props.arch: app_path})
def _check_invariants(self):
print("Testing invariants in customization.rs...")
self.checked_command_output(
["cargo", "test", "--features=std", "--lib", "customization"])
def generate_crypto_materials(self, force_regenerate):
has_error = subprocess.call([
os.path.join("tools", "gen_key_materials.sh"),
@@ -710,6 +716,22 @@ class OpenSKInstaller:
check=False,
timeout=None,
).returncode
# Configure OpenSK through vendor specific command if needed
if any([
self.args.lock_device,
self.args.config_cert,
self.args.config_pkey,
]):
# pylint: disable=g-import-not-at-top,import-outside-toplevel
import tools.configure
tools.configure.main(
argparse.Namespace(
batch=False,
certificate=self.args.config_cert,
priv_key=self.args.config_pkey,
lock=self.args.lock_device,
))
return 0
@@ -770,6 +792,33 @@ if __name__ == "__main__":
help=("Erases the persistent storage when installing an application. "
"All stored data will be permanently lost."),
)
main_parser.add_argument(
"--lock-device",
action="store_true",
default=False,
dest="lock_device",
help=("Try to disable JTAG at the end of the operations. This "
"operation may fail if the device is already locked or if "
"the certificate/private key are not programmed."),
)
main_parser.add_argument(
"--inject-certificate",
default=None,
metavar="PEM_FILE",
type=argparse.FileType("rb"),
dest="config_cert",
help=("If this option is set, the corresponding certificate "
"will be programmed into the key as the last operation."),
)
main_parser.add_argument(
"--inject-private-key",
default=None,
metavar="PEM_FILE",
type=argparse.FileType("rb"),
dest="config_pkey",
help=("If this option is set, the corresponding private key "
"will be programmed into the key as the last operation."),
)
main_parser.add_argument(
"--programmer",
metavar="METHOD",
@@ -838,14 +887,6 @@ if __name__ == "__main__":
help=("Compiles the OpenSK application without backward compatible "
"support for U2F/CTAP1 protocol."),
)
main_parser.add_argument(
"--ctap2.1",
action="append_const",
const="with_ctap2_1",
dest="features",
help=("Compiles the OpenSK application with backward compatible "
"support for CTAP2.1 protocol."),
)
main_parser.add_argument(
"--nfc",
action="append_const",
@@ -863,14 +904,6 @@ if __name__ == "__main__":
"This is useful to allow flashing multiple OpenSK authenticators "
"in a row without them being considered clones."),
)
main_parser.add_argument(
"--no-persistent-storage",
action="append_const",
const="ram_storage",
dest="features",
help=("Compiles and installs the OpenSK application without persistent "
"storage (i.e. unplugging the key will reset the key)."),
)
main_parser.add_argument(
"--elf2tab-output",
@@ -907,6 +940,21 @@ if __name__ == "__main__":
const="crypto_bench",
help=("Compiles and installs the crypto_bench example that benchmarks "
"the performance of the cryptographic algorithms on the board."))
apps_group.add_argument(
"--store_latency",
dest="application",
action="store_const",
const="store_latency",
help=("Compiles and installs the store_latency example which prints "
"latency statistics of the persistent store library."))
apps_group.add_argument(
"--erase_storage",
dest="application",
action="store_const",
const="erase_storage",
help=("Compiles and installs the erase_storage example which erases "
"the storage. During operation the dongle red light is on. Once "
"the operation is completed the dongle green light is on."))
apps_group.add_argument(
"--panic_test",
dest="application",
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

+7 -3
View File
@@ -17,6 +17,7 @@ You will need one the following supported boards:
* [Nordic nRF52840 Dongle](https://www.nordicsemi.com/Software-and-tools/Development-Kits/nRF52840-Dongle)
to have a more practical form factor.
* [Makerdiary nRF52840-MDK USB dongle](https://wiki.makerdiary.com/nrf52840-mdk/).
* [Feitian OpenSK dongle](https://feitiantech.github.io/OpenSK_USB/).
In the case of the Nordic USB dongle, you may also need the following extra
hardware:
@@ -125,6 +126,7 @@ This is the expected content after running our `setup.sh` script:
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
@@ -136,9 +138,11 @@ File | Purpose
If you want to use your own attestation certificate and private key, simply
replace `opensk_cert.pem` and `opensk.key` files.
Our build script `build.rs` is responsible for converting `opensk_cert.pem` and
`opensk.key` files into raw data that is then used by the Rust file:
`src/ctap/key_material.rs`.
Our build script `build.rs` is responsible for converting the `aaguid.txt` file
into raw data that is then used by the Rust file `src/ctap/key_material.rs`.
Our configuration script `tools/configure.py` is responsible for configuring
an OpenSK device with the correct certificate and private key.
### Flashing a firmware
+53
View File
@@ -0,0 +1,53 @@
// 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.
#![no_std]
extern crate lang_items;
use core::fmt::Write;
use ctap2::embedded_flash::new_storage;
use libtock_drivers::console::Console;
use libtock_drivers::led;
use libtock_drivers::result::FlexUnwrap;
use persistent_store::{Storage, StorageIndex};
fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
let index = StorageIndex { page, byte: 0 };
let length = storage.page_size();
storage
.read_slice(index, length)
.unwrap()
.iter()
.all(|&x| x == 0xff)
}
fn main() {
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
const NUM_PAGES: usize = 20; // should be at least ctap::storage::NUM_PAGES
let mut storage = new_storage(NUM_PAGES);
writeln!(Console::new(), "Erase {} pages of storage:", NUM_PAGES).unwrap();
for page in 0..NUM_PAGES {
write!(Console::new(), "- Page {} ", page).unwrap();
if is_page_erased(&storage, page) {
writeln!(Console::new(), "skipped (was already erased).").unwrap();
} else {
storage.erase_page(page).unwrap();
writeln!(Console::new(), "erased.").unwrap();
}
}
writeln!(Console::new(), "Done.").unwrap();
led::get(1).flex_unwrap().off().flex_unwrap();
led::get(0).flex_unwrap().on().flex_unwrap(); // green on dongle
}
+138
View File
@@ -0,0 +1,138 @@
// Copyright 2019-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.
#![no_std]
extern crate alloc;
extern crate lang_items;
use alloc::vec;
use core::fmt::Write;
use ctap2::embedded_flash::{new_storage, Storage};
use libtock_drivers::console::Console;
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
use persistent_store::Store;
fn timestamp(timer: &Timer) -> Timestamp<f64> {
Timestamp::<f64>::from_clock_value(timer.get_current_clock().ok().unwrap())
}
fn measure<T>(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
let before = timestamp(timer);
let result = operation();
let after = timestamp(timer);
(result, after - before)
}
// Only use one store at a time.
unsafe fn boot_store(num_pages: usize, erase: bool) -> Store<Storage> {
let mut storage = new_storage(num_pages);
if erase {
for page in 0..num_pages {
use persistent_store::Storage;
storage.erase_page(page).unwrap();
}
}
Store::new(storage).ok().unwrap()
}
fn compute_latency(timer: &Timer, num_pages: usize, key_increment: usize, word_length: usize) {
let mut console = Console::new();
writeln!(
console,
"\nLatency for num_pages={} key_increment={} word_length={}.",
num_pages, key_increment, word_length
)
.unwrap();
let mut store = unsafe { boot_store(num_pages, true) };
let total_capacity = store.capacity().unwrap().total();
assert_eq!(store.capacity().unwrap().used(), 0);
assert_eq!(store.lifetime().unwrap().used(), 0);
// Burn N words to align the end of the user capacity with the virtual capacity.
store.insert(0, &vec![0; 4 * (num_pages - 1)]).unwrap();
store.remove(0).unwrap();
assert_eq!(store.capacity().unwrap().used(), 0);
assert_eq!(store.lifetime().unwrap().used(), num_pages);
// Insert entries until there is space for one more.
let count = total_capacity / (1 + word_length) - 1;
let ((), time) = measure(timer, || {
for i in 0..count {
let key = 1 + key_increment * i;
// For some reason the kernel sometimes fails.
while store.insert(key, &vec![0; 4 * word_length]).is_err() {
// We never enter this loop in practice, but we still need it for the kernel.
writeln!(console, "Retry insert.").unwrap();
}
}
});
writeln!(console, "Setup: {:.1}ms for {} entries.", time.ms(), count).unwrap();
// Measure latency of insert.
let key = 1 + key_increment * count;
let ((), time) = measure(&timer, || {
store.insert(key, &vec![0; 4 * word_length]).unwrap()
});
writeln!(console, "Insert: {:.1}ms.", time.ms()).unwrap();
assert_eq!(
store.lifetime().unwrap().used(),
num_pages + (1 + count) * (1 + word_length)
);
// Measure latency of boot.
let (mut store, time) = measure(&timer, || unsafe { boot_store(num_pages, false) });
writeln!(console, "Boot: {:.1}ms.", time.ms()).unwrap();
// Measure latency of remove.
let ((), time) = measure(&timer, || store.remove(key).unwrap());
writeln!(console, "Remove: {:.1}ms.", time.ms()).unwrap();
// Measure latency of compaction.
let length = total_capacity + num_pages - store.lifetime().unwrap().used();
if length > 0 {
// Fill the store such that compaction is needed for one word.
store.insert(0, &vec![0; 4 * (length - 1)]).unwrap();
store.remove(0).unwrap();
}
assert!(store.capacity().unwrap().remaining() > 0);
assert_eq!(store.lifetime().unwrap().used(), num_pages + total_capacity);
let ((), time) = measure(timer, || store.prepare(1).unwrap());
writeln!(console, "Compaction: {:.1}ms.", time.ms()).unwrap();
assert!(store.lifetime().unwrap().used() > total_capacity + num_pages);
}
fn main() {
let mut with_callback = timer::with_callback(|_, _| {});
let timer = with_callback.init().ok().unwrap();
writeln!(Console::new(), "\nRunning 4 tests...").unwrap();
// Those non-overwritten 50 words entries simulate credentials.
compute_latency(&timer, 3, 1, 50);
compute_latency(&timer, 20, 1, 50);
// Those overwritten 1 word entries simulate counters.
compute_latency(&timer, 3, 0, 1);
compute_latency(&timer, 20, 0, 1);
writeln!(Console::new(), "\nDone.").unwrap();
// Results on nrf52840dk:
//
// | Pages | Overwrite | Length | Boot | Compaction | Insert | Remove |
// | ----- | --------- | --------- | ------- | ---------- | ------ | ------ |
// | 3 | no | 50 words | 2.0 ms | 132.8 ms | 4.3 ms | 1.2 ms |
// | 20 | no | 50 words | 7.8 ms | 135.7 ms | 9.9 ms | 4.0 ms |
// | 3 | yes | 1 word | 19.6 ms | 90.8 ms | 4.7 ms | 2.3 ms |
// | 20 | yes | 1 word | 183.3 ms | 90.9 ms | 4.8 ms | 2.3 ms |
}
+1 -1
View File
@@ -10,5 +10,5 @@ arrayref = "0.3.6"
libtock_drivers = { path = "../../third_party/libtock-drivers" }
crypto = { path = "../../libraries/crypto", features = ['std'] }
cbor = { path = "../../libraries/cbor", features = ['std'] }
ctap2 = { path = "../..", features = ['std', 'ram_storage'] }
ctap2 = { path = "../..", features = ['std'] }
lang_items = { path = "../../third_party/lang-items", features = ['std'] }

Some files were not shown because too many files have changed in this diff Show More