Compare commits
18 Commits
multi-pin
...
hybrid-pqc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93357524d9 | ||
|
|
44cafb9566 | ||
|
|
3c28ff49ee | ||
|
|
6610a29a67 | ||
|
|
f2fac83124 | ||
|
|
d6994e3bc3 | ||
|
|
8288bb0860 | ||
|
|
771ce7635b | ||
|
|
1b360662ee | ||
|
|
598c21071e | ||
|
|
932924ea85 | ||
|
|
01cc8333e5 | ||
|
|
2dc44984ed | ||
|
|
a44d961e7e | ||
|
|
5509e3f072 | ||
|
|
d2037a4bbe | ||
|
|
6bb12252f8 | ||
|
|
e52adf04c7 |
6
.github/workflows/cargo_check.yml
vendored
6
.github/workflows/cargo_check.yml
vendored
@@ -94,9 +94,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
command: check
|
command: check
|
||||||
args: --target thumbv7em-none-eabi --release --examples
|
args: --target thumbv7em-none-eabi --release --examples
|
||||||
|
|
||||||
- name: Check bootloader
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: check
|
|
||||||
args: --manifest-path bootloader/Cargo.toml --target thumbv7em-none-eabi --release
|
|
||||||
|
|||||||
@@ -24,12 +24,13 @@ embedded-time = "0.12.1"
|
|||||||
arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
|
arbitrary = { version = "0.4.7", features = ["derive"], optional = true }
|
||||||
rand = { version = "0.8.4", optional = true }
|
rand = { version = "0.8.4", optional = true }
|
||||||
ed25519-compact = { version = "1", default-features = false, optional = true }
|
ed25519-compact = { version = "1", default-features = false, optional = true }
|
||||||
|
dilithium = { path = "third_party/dilithium" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
debug_allocations = ["lang_items/debug_allocations"]
|
debug_allocations = ["lang_items/debug_allocations"]
|
||||||
debug_ctap = ["libtock_drivers/debug_ctap"]
|
debug_ctap = ["libtock_drivers/debug_ctap"]
|
||||||
panic_console = ["lang_items/panic_console"]
|
panic_console = ["lang_items/panic_console"]
|
||||||
std = ["crypto/std", "lang_items/std", "persistent_store/std", "rng256/std", "rand"]
|
std = ["crypto/std", "dilithium/std", "lang_items/std", "persistent_store/std", "rng256/std", "rand"]
|
||||||
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
|
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
|
||||||
with_ctap1 = ["crypto/with_ctap1"]
|
with_ctap1 = ["crypto/with_ctap1"]
|
||||||
with_nfc = ["libtock_drivers/with_nfc"]
|
with_nfc = ["libtock_drivers/with_nfc"]
|
||||||
|
|||||||
162
README.md
162
README.md
@@ -1,84 +1,114 @@
|
|||||||
# <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px">
|
# <img alt="OpenSK logo" src="docs/img/OpenSK.svg" width="200px">
|
||||||
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
[](https://coveralls.io/github/google/OpenSK?branch=develop)
|
|
||||||
|
|
||||||
## OpenSK
|
## OpenSK
|
||||||
|
|
||||||
This repository contains a Rust implementation of a
|
This is an OpenSK fork that allows signing with a PQC Hybrid scheme. If you are looking for the original documentation, please check the
|
||||||
[FIDO2](https://fidoalliance.org/fido2/) authenticator.
|
[develop branch of its GitHub page](https://github.com/google/OpenSK/tree/develop).
|
||||||
We developed OpenSK as a [Tock OS](https://tockos.org) application.
|
|
||||||
|
|
||||||
We intend to bring a full open source experience to security keys, from
|
|
||||||
application to operating system. You can even 3D print your own open source
|
|
||||||
enclosure!
|
|
||||||
You can see OpenSK in action in this
|
|
||||||
[video on YouTube](https://www.youtube.com/watch?v=klEozvpw0xg)!
|
|
||||||
|
|
||||||
You are viewing the branch for developers. New features are developed here
|
|
||||||
before they are stabilized. If you instead want to use the FIDO certified
|
|
||||||
firmware, please go back to the
|
|
||||||
[stable branch](https://github.com/google/OpenSK).
|
|
||||||
|
|
||||||
### FIDO2
|
|
||||||
|
|
||||||
The develop branch implements the
|
|
||||||
[CTAP2.1 specification](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html).
|
|
||||||
This branch is not FIDO certified. The implementation is backwards compatible
|
|
||||||
to CTAP2.0. Additionally, OpenSK supports U2F, and non-discoverable credentials
|
|
||||||
created with either protocol are compatible with the other.
|
|
||||||
|
|
||||||
### :warning: Disclaimer
|
|
||||||
|
|
||||||
This project is **proof-of-concept and a research platform**. It is **NOT**
|
|
||||||
meant for a daily usage. It comes with a few limitations:
|
|
||||||
|
|
||||||
* This branch is under development, and therefore less rigorously tested than the stable branch.
|
|
||||||
* The cryptography implementations are not resistent against side-channel attacks.
|
|
||||||
|
|
||||||
We're still in the process of integrating the
|
|
||||||
[ARM® CryptoCell-310](https://developer.arm.com/ip-products/security-ip/cryptocell-300-family)
|
|
||||||
embedded in the
|
|
||||||
[Nordic nRF52840 chip](https://infocenter.nordicsemi.com/index.jsp?topic=%2Fps_nrf52840%2Fcryptocell.html)
|
|
||||||
to enable hardware-accelerated cryptography. Our placeholder implementations of required
|
|
||||||
cryptography algorithms (ECDSA, ECC secp256r1, HMAC-SHA256 and AES256) in Rust are research-quality
|
|
||||||
code. They haven't been reviewed and don't provide constant-time guarantees.
|
|
||||||
|
|
||||||
## Hardware
|
## Hardware
|
||||||
|
|
||||||
You will need one the following supported boards:
|
You will need a
|
||||||
|
[Nordic nRF52840-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||||
* [Nordic nRF52840-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
development kit.
|
||||||
development kit. This board is more convenient for development and debug
|
|
||||||
scenarios as the JTAG probe is already on the board.
|
|
||||||
* [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/).
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
To install OpenSK,
|
To install OpenSK,
|
||||||
|
|
||||||
1. follow the [general setup steps](docs/install.md),
|
1. follow the [general setup steps](docs/install.md),
|
||||||
1. then continue with the instructions for your specific hardware:
|
1. then continue with the instructions for your specific hardware:
|
||||||
* [Nordic nRF52840-DK](docs/boards/nrf52840dk.md)
|
[Nordic nRF52840-DK](docs/boards/nrf52840dk.md)
|
||||||
* [Nordic nRF52840 Dongle](docs/boards/nrf52840_dongle.md)
|
|
||||||
* [Makerdiary nRF52840-MDK USB dongle](docs/boards/nrf52840_mdk.md)
|
|
||||||
* [Feitian OpenSK dongle](docs/boards/nrf52840_feitian.md)
|
|
||||||
|
|
||||||
To test whether the installation was successful, visit a
|
## PQC Experiments
|
||||||
[demo website](https://webauthn.io/) and try to register and login.
|
|
||||||
Please check our [Troubleshooting and Debugging](docs/debugging.md) section if you
|
|
||||||
have problems with the installation process or during development. To find out what
|
|
||||||
else you can do with your OpenSK, see [Customization](docs/customization.md).
|
|
||||||
|
|
||||||
## Contributing
|
### Modes
|
||||||
|
|
||||||
See [Contributing.md](docs/contributing.md).
|
The Dilithium mode is set at compile time. If you want to perform experiments for different modes,
|
||||||
|
you will need to recompile. The mode is a feature, defined in
|
||||||
|
`third_party/dilithium/Cargo.toml`. By default, it is set to
|
||||||
|
`default = [ "dilithium5", "optimize_stack" ]`. You can change the default mode by either changing
|
||||||
|
the number 5 to 2 or 3. Or you remove the feature for stack optimizations, e.g.
|
||||||
|
`default = [ "dilithium2" ]`.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
Note that some benchmarks will not run in all modes without stack optimizations. You can try to
|
||||||
|
play with the stack size in these cases. As an example, stack painting for speed mode Dilithium2
|
||||||
|
works if you apply the following changes:
|
||||||
|
|
||||||
|
* `APP_HEAP_SIZE = 16384` in `deploy.py`
|
||||||
|
* `libtock_core::stack_size! {0x1A000}` in `examples/measure_stack.rs`
|
||||||
|
* `STACK_SIZE = 106496;` in `nrf52840_layout.ld`
|
||||||
|
* Change the app break from `70 * 1024` to `104 * 1024` in `patches/tock/07-app-break-fix.patch`.
|
||||||
|
|
||||||
|
For your convenience, you can also simply try:
|
||||||
|
|
||||||
|
```
|
||||||
|
git apply increase_stack.patch
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiler flags
|
||||||
|
|
||||||
|
To trade binary size for speed, you can play with `[profile.release]` in `Cargo.toml`.
|
||||||
|
For example, try a different compiler optimization level:
|
||||||
|
|
||||||
|
```
|
||||||
|
opt-level = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug output
|
||||||
|
|
||||||
|
Only the CTAP commands tests are measured end to end on the host. All other experiments are
|
||||||
|
measured on the embedded device itself and output over RTT. You can either use a client to print
|
||||||
|
results by running the following commands in different terminals:
|
||||||
|
|
||||||
|
```
|
||||||
|
JLinkExe -device nrf52 -if swd -speed 1000 -autoconnect 1
|
||||||
|
JLinkRTTClient
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you directly output all messages to a file:
|
||||||
|
|
||||||
|
```
|
||||||
|
JLinkRTTLogger -device NRF52840_XXAA -if swd -speed 1000 -RTTchannel 0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Perform Experiments
|
||||||
|
|
||||||
|
The paper contains the following experiments:
|
||||||
|
|
||||||
|
#### Crypto benchmarks
|
||||||
|
|
||||||
|
Deploy the `crypto_bench` example and read the debug output with one of the methods above:
|
||||||
|
|
||||||
|
```
|
||||||
|
./deploy.py --board=nrf52840dk_opensk --crypto_bench
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CTAP benchmarks
|
||||||
|
|
||||||
|
To measure the speed of FIDO commands, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
python benchmarks.py --runs=2000
|
||||||
|
```
|
||||||
|
|
||||||
|
Aggregate results will be printed, and the raw data is written to `make_durations.txt` and
|
||||||
|
`get_durations.txt`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Stack painting
|
||||||
|
|
||||||
|
Deploy the `measure_stack` example and read the debug output with one of the methods above:
|
||||||
|
|
||||||
|
```
|
||||||
|
./deploy.py --board=nrf52840dk_opensk --measure_stack
|
||||||
|
```
|
||||||
|
|
||||||
|
#### x86 benchmarks
|
||||||
|
|
||||||
|
You don't need your embedded hardware for those, run:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd third_party/dilithium/
|
||||||
|
cargo bench --features std
|
||||||
|
```
|
||||||
|
|
||||||
See [SECURITY.md](SECURITY.md).
|
|
||||||
|
|||||||
167
benchmarks.py
Normal file
167
benchmarks.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright 2022 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.
|
||||||
|
# Lint as: python3
|
||||||
|
"""Script to benchmark CTAP commands using Dilithium Hybrid signatures."""
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
from subprocess import DEVNULL, STDOUT, check_call
|
||||||
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
from typing import Any
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import colorama
|
||||||
|
from tqdm.auto import tqdm
|
||||||
|
|
||||||
|
from fido2 import ctap
|
||||||
|
from fido2.webauthn import PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, PublicKeyCredentialParameters
|
||||||
|
from fido2 import hid
|
||||||
|
from tools.configure import fatal, info, get_opensk_devices
|
||||||
|
|
||||||
|
ES256_ALGORITHM = PublicKeyCredentialParameters("public-key", -7)
|
||||||
|
HYBRID_ALGORITHM = PublicKeyCredentialParameters("public-key", -65537)
|
||||||
|
|
||||||
|
|
||||||
|
def error(message: str):
|
||||||
|
tqdm.write(message)
|
||||||
|
|
||||||
|
|
||||||
|
def check_info(authenticator: Any):
|
||||||
|
"""Checks if the assumed upgrade info matches the authenticator's."""
|
||||||
|
try:
|
||||||
|
info("Reading info...")
|
||||||
|
if HYBRID_ALGORITHM not in authenticator.info.algorithms:
|
||||||
|
fatal("The device does not support hybrid signatures.")
|
||||||
|
except ctap.CtapError as ex:
|
||||||
|
error(f"Failed to read OpenSK info (error: {ex}")
|
||||||
|
|
||||||
|
|
||||||
|
def f_args(*params):
|
||||||
|
"""Constructs a dict from a list of arguments for sending a CBOR command.
|
||||||
|
None elements will be omitted.
|
||||||
|
:param params: Arguments, in order, to add to the command.
|
||||||
|
:return: The input parameters as a dict.
|
||||||
|
"""
|
||||||
|
return dict((i, v) for i, v in enumerate(params, 1) if v is not None)
|
||||||
|
|
||||||
|
|
||||||
|
def compute_stats(elapsed):
|
||||||
|
n = len(elapsed)
|
||||||
|
mean = sum(elapsed) / n
|
||||||
|
variance = sum((x - mean)**2 for x in elapsed) / n
|
||||||
|
std_dev = variance**0.5
|
||||||
|
return (mean, std_dev)
|
||||||
|
|
||||||
|
|
||||||
|
def get_authenticator():
|
||||||
|
devices = None
|
||||||
|
while not devices:
|
||||||
|
try:
|
||||||
|
devices = get_opensk_devices(False)
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
error(str(e))
|
||||||
|
check_call(["nrfjprog", "--reset", "--family", "NRF52"],
|
||||||
|
stdout=DEVNULL,
|
||||||
|
stderr=STDOUT)
|
||||||
|
sleep(0.1)
|
||||||
|
return devices[0]
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
colorama.init()
|
||||||
|
|
||||||
|
authenticator = get_authenticator()
|
||||||
|
# If the device supports it, wink to show which device we use.
|
||||||
|
if authenticator.device.capabilities & hid.CAPABILITY.WINK:
|
||||||
|
authenticator.device.wink()
|
||||||
|
aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid)
|
||||||
|
check_info(authenticator)
|
||||||
|
info(f"Testing OpenSK device AAGUID {aaguid} ({authenticator.device}).")
|
||||||
|
|
||||||
|
make_durations = []
|
||||||
|
get_durations = []
|
||||||
|
|
||||||
|
for _ in tqdm(range(args.runs), file=sys.stdout):
|
||||||
|
authenticator = get_authenticator()
|
||||||
|
try:
|
||||||
|
start = datetime.datetime.now()
|
||||||
|
result = authenticator.make_credential(
|
||||||
|
client_data_hash=bytes(32),
|
||||||
|
rp=PublicKeyCredentialRpEntity(id="example.com", name="Example"),
|
||||||
|
user=PublicKeyCredentialUserEntity(id=b"diana", name="Diana"),
|
||||||
|
key_params=[HYBRID_ALGORITHM],
|
||||||
|
)
|
||||||
|
end = datetime.datetime.now()
|
||||||
|
make_delta = (end - start).total_seconds() * 1000.0
|
||||||
|
make_durations.append(make_delta)
|
||||||
|
|
||||||
|
credential_data = result.auth_data.credential_data
|
||||||
|
credential_id_length = 256 * credential_data[16] + credential_data[17]
|
||||||
|
credential_id = credential_data[18:18 + credential_id_length]
|
||||||
|
allow_list = [{"type": "public-key", "id": credential_id}]
|
||||||
|
|
||||||
|
start = datetime.datetime.now()
|
||||||
|
_ = authenticator.get_assertion(
|
||||||
|
rp_id="example.com",
|
||||||
|
client_data_hash=bytes(32),
|
||||||
|
allow_list=allow_list,
|
||||||
|
)
|
||||||
|
end = datetime.datetime.now()
|
||||||
|
get_delta = (end - start).total_seconds() * 1000.0
|
||||||
|
get_durations.append(get_delta)
|
||||||
|
|
||||||
|
with open("make_durations.txt", "a", encoding="utf-8") as file_make:
|
||||||
|
file_make.write(str(make_delta) + ",\n")
|
||||||
|
with open("get_durations.txt", "a", encoding="utf-8") as file_get:
|
||||||
|
file_get.write(str(get_delta) + ",\n")
|
||||||
|
|
||||||
|
except ctap.CtapError as ex:
|
||||||
|
message = "Failed to make a hybrid signature with OpenSK"
|
||||||
|
if ex.code.value == ctap.CtapError.ERR.INVALID_COMMAND:
|
||||||
|
error(f"{message} (unsupported command).")
|
||||||
|
elif ex.code.value == ctap.CtapError.ERR.INVALID_PARAMETER:
|
||||||
|
error(f"{message} (invalid parameter, maybe a wrong byte array size?).")
|
||||||
|
elif ex.code.value == 0xF2: # VENDOR_INTERNAL_ERROR
|
||||||
|
error(f"{message} (internal conditions not met).")
|
||||||
|
elif ex.code.value == 0xF3: # VENDOR_HARDWARE_FAILURE
|
||||||
|
error(f"{message} (internal hardware error).")
|
||||||
|
else:
|
||||||
|
error(f"{message} (unexpected error: {ex})")
|
||||||
|
except Exception as e: # pylint: disable=broad-except
|
||||||
|
error(str(e))
|
||||||
|
|
||||||
|
info(f"Successful operations: {len(make_durations)} and {len(get_durations)}")
|
||||||
|
info("\nMake Credential benchmark:")
|
||||||
|
(mean, std_dev) = compute_stats(make_durations)
|
||||||
|
info(f"Average: {mean} ms/iter (standard deviation: {std_dev} ms/iter)")
|
||||||
|
info("\nGet Assertion benchmark:")
|
||||||
|
(mean, std_dev) = compute_stats(get_durations)
|
||||||
|
info(f"Average: {mean} ms/iter (standard deviation: {std_dev} ms/iter)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument(
|
||||||
|
"--runs",
|
||||||
|
type=int,
|
||||||
|
default=1000,
|
||||||
|
help=("How many iterations to use."),
|
||||||
|
)
|
||||||
|
main(parser.parse_args())
|
||||||
@@ -69,12 +69,12 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
|||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xC0000,
|
address: 0xC0000,
|
||||||
size: 0x10000, // 16 pages
|
size: 0x10000, // 16 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xD0000,
|
address: 0xD0000,
|
||||||
size: 0x4000, // 4 pages
|
size: 0x4000, // 4 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -63,12 +63,12 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
|||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xC0000,
|
address: 0xC0000,
|
||||||
size: 0x10000, // 16 pages
|
size: 0x10000, // 16 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xD0000,
|
address: 0xD0000,
|
||||||
size: 0x4000, // 4 pages
|
size: 0x4000, // 4 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
|||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xC0000,
|
address: 0xC0000,
|
||||||
size: 0x10000, // 16 pages
|
size: 0x10000, // 16 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xD0000,
|
address: 0xD0000,
|
||||||
size: 0x4000, // 4 pages
|
size: 0x4000, // 4 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -16,28 +16,28 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [
|
|||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xC0000,
|
address: 0xC0000,
|
||||||
size: 0x10000, // 16 pages
|
size: 0x10000, // 16 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xD0000,
|
address: 0xD0000,
|
||||||
size: 0x4000, // 4 pages
|
size: 0x4000, // 4 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
// Partitions can also be split to maximize MPU happiness.
|
// Partitions can also be split to maximize MPU happiness.
|
||||||
|
kernel::StorageLocation {
|
||||||
|
address: 0x4000,
|
||||||
|
size: 0x2000,
|
||||||
|
storage_type: kernel::StorageType::Partition,
|
||||||
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0x60000,
|
address: 0x60000,
|
||||||
size: 0x20000,
|
size: 0x20000,
|
||||||
storage_type: kernel::StorageType::PARTITION,
|
storage_type: kernel::StorageType::Partition,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0x80000,
|
address: 0x80000,
|
||||||
size: 0x20000,
|
size: 0x20000,
|
||||||
storage_type: kernel::StorageType::PARTITION,
|
storage_type: kernel::StorageType::Partition,
|
||||||
},
|
|
||||||
kernel::StorageLocation {
|
|
||||||
address: 0x5000,
|
|
||||||
size: 0x1000,
|
|
||||||
storage_type: kernel::StorageType::METADATA,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -16,28 +16,28 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 5] = [
|
|||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xC0000,
|
address: 0xC0000,
|
||||||
size: 0x10000, // 16 pages
|
size: 0x10000, // 16 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0xD0000,
|
address: 0xD0000,
|
||||||
size: 0x4000, // 4 pages
|
size: 0x4000, // 4 pages
|
||||||
storage_type: kernel::StorageType::STORE,
|
storage_type: kernel::StorageType::Store,
|
||||||
},
|
},
|
||||||
// Partitions can also be split to maximize MPU happiness.
|
// Partitions can also be split to maximize MPU happiness.
|
||||||
|
kernel::StorageLocation {
|
||||||
|
address: 0x4000,
|
||||||
|
size: 0x2000,
|
||||||
|
storage_type: kernel::StorageType::Partition,
|
||||||
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0x20000,
|
address: 0x20000,
|
||||||
size: 0x20000,
|
size: 0x20000,
|
||||||
storage_type: kernel::StorageType::PARTITION,
|
storage_type: kernel::StorageType::Partition,
|
||||||
},
|
},
|
||||||
kernel::StorageLocation {
|
kernel::StorageLocation {
|
||||||
address: 0x40000,
|
address: 0x40000,
|
||||||
size: 0x20000,
|
size: 0x20000,
|
||||||
storage_type: kernel::StorageType::PARTITION,
|
storage_type: kernel::StorageType::Partition,
|
||||||
},
|
|
||||||
kernel::StorageLocation {
|
|
||||||
address: 0x4000,
|
|
||||||
size: 0x1000,
|
|
||||||
storage_type: kernel::StorageType::METADATA,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ use rtt_target::{rprintln, rtt_init_print};
|
|||||||
|
|
||||||
/// Size of a flash page in bytes.
|
/// Size of a flash page in bytes.
|
||||||
const PAGE_SIZE: usize = 0x1000;
|
const PAGE_SIZE: usize = 0x1000;
|
||||||
|
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||||
|
|
||||||
/// A flash page.
|
/// A flash page.
|
||||||
type Page = [u8; PAGE_SIZE];
|
type Page = [u8; PAGE_SIZE];
|
||||||
@@ -48,21 +49,19 @@ unsafe fn read_page(address: usize) -> Page {
|
|||||||
/// Parsed metadata for a firmware partition.
|
/// Parsed metadata for a firmware partition.
|
||||||
struct Metadata {
|
struct Metadata {
|
||||||
checksum: [u8; 32],
|
checksum: [u8; 32],
|
||||||
timestamp: u32,
|
_signature: [u8; 64],
|
||||||
|
version: u64,
|
||||||
address: u32,
|
address: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Metadata {
|
|
||||||
pub const DATA_LEN: usize = 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the metadata from a flash page.
|
/// Reads the metadata from a flash page.
|
||||||
impl From<Page> for Metadata {
|
impl From<Page> for Metadata {
|
||||||
fn from(page: Page) -> Self {
|
fn from(page: Page) -> Self {
|
||||||
Metadata {
|
Metadata {
|
||||||
checksum: page[0..32].try_into().unwrap(),
|
checksum: page[0..32].try_into().unwrap(),
|
||||||
timestamp: LittleEndian::read_u32(&page[32..36]),
|
_signature: page[32..96].try_into().unwrap(),
|
||||||
address: LittleEndian::read_u32(&page[36..Metadata::DATA_LEN]),
|
version: LittleEndian::read_u64(&page[METADATA_SIGN_OFFSET..][..8]),
|
||||||
|
address: LittleEndian::read_u32(&page[METADATA_SIGN_OFFSET + 8..][..4]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,15 +75,15 @@ struct BootPartition {
|
|||||||
impl BootPartition {
|
impl BootPartition {
|
||||||
const FIRMWARE_LENGTH: usize = 0x00040000;
|
const FIRMWARE_LENGTH: usize = 0x00040000;
|
||||||
|
|
||||||
/// Reads the metadata, returns the timestamp if all checks pass.
|
/// Reads the metadata, returns the firmware version if all checks pass.
|
||||||
pub fn read_timestamp(&self) -> Result<u32, ()> {
|
pub fn read_version(&self) -> Result<u64, ()> {
|
||||||
let metadata_page = unsafe { read_page(self.metadata_address) };
|
let metadata_page = unsafe { read_page(self.metadata_address) };
|
||||||
let hash_value = self.compute_upgrade_hash(&metadata_page);
|
let hash_value = self.compute_upgrade_hash(&metadata_page);
|
||||||
let metadata = Metadata::from(metadata_page);
|
let metadata = Metadata::from(metadata_page);
|
||||||
if self.firmware_address != metadata.address as usize {
|
if self.firmware_address != metadata.address as usize {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
rprintln!(
|
rprintln!(
|
||||||
"Firmware address mismatch: expected 0x{:08X}, metadata 0x{:08X}",
|
"Partition address mismatch: expected 0x{:08X}, metadata 0x{:08X}",
|
||||||
self.firmware_address,
|
self.firmware_address,
|
||||||
metadata.address as usize
|
metadata.address as usize
|
||||||
);
|
);
|
||||||
@@ -95,7 +94,7 @@ impl BootPartition {
|
|||||||
rprintln!("Hash mismatch");
|
rprintln!("Hash mismatch");
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
Ok(metadata.timestamp)
|
Ok(metadata.version)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the SHA256 of metadata information and partition data.
|
/// Computes the SHA256 of metadata information and partition data.
|
||||||
@@ -107,11 +106,14 @@ impl BootPartition {
|
|||||||
debug_assert!(self.firmware_address % PAGE_SIZE == 0);
|
debug_assert!(self.firmware_address % PAGE_SIZE == 0);
|
||||||
debug_assert!(BootPartition::FIRMWARE_LENGTH % PAGE_SIZE == 0);
|
debug_assert!(BootPartition::FIRMWARE_LENGTH % PAGE_SIZE == 0);
|
||||||
let cc310 = crypto_cell::CryptoCell310::new();
|
let cc310 = crypto_cell::CryptoCell310::new();
|
||||||
|
cc310.update(&metadata_page[METADATA_SIGN_OFFSET..], false);
|
||||||
for page_offset in (0..BootPartition::FIRMWARE_LENGTH).step_by(PAGE_SIZE) {
|
for page_offset in (0..BootPartition::FIRMWARE_LENGTH).step_by(PAGE_SIZE) {
|
||||||
let page = unsafe { read_page(self.firmware_address + page_offset) };
|
let page = unsafe { read_page(self.firmware_address + page_offset) };
|
||||||
cc310.update(&page, false);
|
cc310.update(
|
||||||
|
&page,
|
||||||
|
page_offset + PAGE_SIZE == BootPartition::FIRMWARE_LENGTH,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
cc310.update(&metadata_page[32..Metadata::DATA_LEN], true);
|
|
||||||
cc310.finalize_and_clear()
|
cc310.finalize_and_clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,12 +158,12 @@ fn main() -> ! {
|
|||||||
};
|
};
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
rprintln!("Reading partition A");
|
rprintln!("Reading partition A");
|
||||||
let timestamp_a = partition_a.read_timestamp();
|
let version_a = partition_a.read_version();
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
rprintln!("Reading partition B");
|
rprintln!("Reading partition B");
|
||||||
let timestamp_b = partition_b.read_timestamp();
|
let version_b = partition_b.read_version();
|
||||||
|
|
||||||
match (timestamp_a, timestamp_b) {
|
match (version_a, version_b) {
|
||||||
(Ok(t1), Ok(t2)) => {
|
(Ok(t1), Ok(t2)) => {
|
||||||
if t1 >= t2 {
|
if t1 >= t2 {
|
||||||
partition_a.boot()
|
partition_a.boot()
|
||||||
|
|||||||
23
build.rs
23
build.rs
@@ -15,7 +15,6 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use openssl::{bn, ec, nid};
|
use openssl::{bn, ec, nid};
|
||||||
use sk_cbor::cbor_map;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -52,25 +51,7 @@ fn main() {
|
|||||||
.public_key()
|
.public_key()
|
||||||
.to_bytes(&group, conversion_form, &mut ctx)
|
.to_bytes(&group, conversion_form, &mut ctx)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
const POINT_LEN: usize = 32;
|
let upgrade_pubkey_path = Path::new(&out_dir).join("opensk_upgrade_pubkey.bin");
|
||||||
assert_eq!(raw_bytes.len(), 1 + 2 * POINT_LEN);
|
|
||||||
assert_eq!(raw_bytes[0], 0x04);
|
|
||||||
let x_bytes = &raw_bytes[1..][..POINT_LEN];
|
|
||||||
let y_bytes = &raw_bytes[1 + POINT_LEN..][..POINT_LEN];
|
|
||||||
|
|
||||||
const EC2_KEY_TYPE: i64 = 2;
|
|
||||||
const P_256_CURVE: i64 = 1;
|
|
||||||
const ES256_ALGORITHM: i64 = -7;
|
|
||||||
let pub_key_cbor = sk_cbor::cbor_map! {
|
|
||||||
1 => EC2_KEY_TYPE,
|
|
||||||
3 => ES256_ALGORITHM,
|
|
||||||
-1 => P_256_CURVE,
|
|
||||||
-2 => x_bytes,
|
|
||||||
-3 => y_bytes,
|
|
||||||
};
|
|
||||||
let mut cbor_bytes = vec![];
|
|
||||||
sk_cbor::writer::write(pub_key_cbor, &mut cbor_bytes).unwrap();
|
|
||||||
let upgrade_pubkey_path = Path::new(&out_dir).join("opensk_upgrade_pubkey_cbor.bin");
|
|
||||||
let mut upgrade_pub_bin_file = File::create(&upgrade_pubkey_path).unwrap();
|
let mut upgrade_pub_bin_file = File::create(&upgrade_pubkey_path).unwrap();
|
||||||
upgrade_pub_bin_file.write_all(&cbor_bytes).unwrap();
|
upgrade_pub_bin_file.write_all(&raw_bytes).unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
32
deploy.py
32
deploy.py
@@ -38,7 +38,7 @@ from tockloader import tockloader as loader
|
|||||||
from tockloader.exceptions import TockLoaderException
|
from tockloader.exceptions import TockLoaderException
|
||||||
|
|
||||||
import tools.configure
|
import tools.configure
|
||||||
from tools.deploy_partition import create_metadata, pad_to
|
from tools.deploy_partition import create_metadata, load_priv_key, pad_to
|
||||||
|
|
||||||
PROGRAMMERS = frozenset(("jlink", "openocd", "pyocd", "nordicdfu", "none"))
|
PROGRAMMERS = frozenset(("jlink", "openocd", "pyocd", "nordicdfu", "none"))
|
||||||
|
|
||||||
@@ -156,9 +156,7 @@ SUPPORTED_BOARDS = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
# The following value must match the one used in the file
|
APP_HEAP_SIZE = 32768
|
||||||
# `src/entry_point.rs`
|
|
||||||
APP_HEAP_SIZE = 90000
|
|
||||||
|
|
||||||
|
|
||||||
def get_supported_boards() -> Tuple[str]:
|
def get_supported_boards() -> Tuple[str]:
|
||||||
@@ -622,7 +620,9 @@ class OpenSKInstaller:
|
|||||||
# The kernel is already padded when read.
|
# The kernel is already padded when read.
|
||||||
firmware_image = kernel + pad_to(app, app_size)
|
firmware_image = kernel + pad_to(app, app_size)
|
||||||
|
|
||||||
metadata = create_metadata(firmware_image, board_props.kernel_address)
|
priv_key = load_priv_key(self.args.upgrade_priv_key)
|
||||||
|
metadata = create_metadata(firmware_image, board_props.kernel_address,
|
||||||
|
self.args.version, priv_key)
|
||||||
if self.args.verbose_build:
|
if self.args.verbose_build:
|
||||||
info(f"Metadata bytes: {metadata}")
|
info(f"Metadata bytes: {metadata}")
|
||||||
|
|
||||||
@@ -1131,6 +1131,22 @@ if __name__ == "__main__":
|
|||||||
help=("Don't check that patches are in sync with their submodules."),
|
help=("Don't check that patches are in sync with their submodules."),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
main_parser.add_argument(
|
||||||
|
"--private-key",
|
||||||
|
type=str,
|
||||||
|
default="crypto_data/opensk_upgrade.key",
|
||||||
|
dest="upgrade_priv_key",
|
||||||
|
help=("PEM file for signing the firmware."),
|
||||||
|
)
|
||||||
|
|
||||||
|
main_parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
type=int,
|
||||||
|
default=-1,
|
||||||
|
dest="version",
|
||||||
|
help=("Firmware version that is built."),
|
||||||
|
)
|
||||||
|
|
||||||
main_parser.set_defaults(features=["with_ctap1"])
|
main_parser.set_defaults(features=["with_ctap1"])
|
||||||
|
|
||||||
# Start parsing to know if we're going to list things or not.
|
# Start parsing to know if we're going to list things or not.
|
||||||
@@ -1159,6 +1175,12 @@ if __name__ == "__main__":
|
|||||||
const="crypto_bench",
|
const="crypto_bench",
|
||||||
help=("Compiles and installs the crypto_bench example that benchmarks "
|
help=("Compiles and installs the crypto_bench example that benchmarks "
|
||||||
"the performance of the cryptographic algorithms on the board."))
|
"the performance of the cryptographic algorithms on the board."))
|
||||||
|
apps_group.add_argument(
|
||||||
|
"--measure_stack",
|
||||||
|
dest="application",
|
||||||
|
action="store_const",
|
||||||
|
const="measure_stack",
|
||||||
|
help=("Measures stack usage of Dilithium."))
|
||||||
apps_group.add_argument(
|
apps_group.add_argument(
|
||||||
"--store_latency",
|
"--store_latency",
|
||||||
dest="application",
|
dest="application",
|
||||||
|
|||||||
@@ -55,15 +55,15 @@ There are variants of the board that introduce A/B partitions for upgrading the
|
|||||||
firmware. You can bootstrap an upgradable board using one of the two commands:
|
firmware. You can bootstrap an upgradable board using one of the two commands:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./deploy.py --board=nrf52840dk_opensk_a --opensk
|
./deploy.py --board=nrf52840dk_opensk_a --opensk --version=0
|
||||||
./deploy.py --board=nrf52840dk_opensk_b --opensk
|
./deploy.py --board=nrf52840dk_opensk_b --opensk --version=0
|
||||||
```
|
```
|
||||||
|
|
||||||
Afterwards, you can upgrade the other partition with
|
Afterwards, you can upgrade the other partition with
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./tools/perform_upgrade.sh nrf52840dk_opensk_b
|
./tools/perform_upgrade.sh nrf52840dk_opensk_b --version=1
|
||||||
./tools/perform_upgrade.sh nrf52840dk_opensk_a
|
./tools/perform_upgrade.sh nrf52840dk_opensk_a --version=1
|
||||||
```
|
```
|
||||||
|
|
||||||
respectively. You can only upgrade the partition that is not currently running,
|
respectively. You can only upgrade the partition that is not currently running,
|
||||||
@@ -75,6 +75,6 @@ If you deploy with `--vendor-hid`, also add this flag to `perform_upgrade.sh`,
|
|||||||
for example:
|
for example:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
./deploy.py --board=nrf52840dk_opensk_a --opensk --vendor-hid
|
./deploy.py --board=nrf52840dk_opensk_a --opensk --version=0 --vendor-hid
|
||||||
./tools/perform_upgrade.sh nrf52840dk_opensk_b --vendor-hid
|
./tools/perform_upgrade.sh nrf52840dk_opensk_b --version=1 --vendor-hid
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -17,27 +17,42 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate lang_items;
|
extern crate lang_items;
|
||||||
|
|
||||||
use alloc::format;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use crypto::{aes256, cbc, ecdsa, sha256, Hash256};
|
use crypto::sha256::Sha256;
|
||||||
|
use crypto::{ecdsa, hybrid};
|
||||||
use libtock_drivers::console::Console;
|
use libtock_drivers::console::Console;
|
||||||
use libtock_drivers::result::FlexUnwrap;
|
use libtock_drivers::result::FlexUnwrap;
|
||||||
use libtock_drivers::timer;
|
use libtock_drivers::timer;
|
||||||
use libtock_drivers::timer::{Timer, Timestamp};
|
use libtock_drivers::timer::{Timer, Timestamp};
|
||||||
use rng256::TockRng256;
|
use rng256::Rng256;
|
||||||
|
// use ctap2::env::tock::{take_storage, TockStorage};
|
||||||
|
// use persistent_store::Store;
|
||||||
|
|
||||||
libtock_core::stack_size! {0x800}
|
libtock_core::stack_size! {0x11800}
|
||||||
|
|
||||||
|
/*fn boot_store(mut storage: TockStorage, erase: bool) -> Store<TockStorage> {
|
||||||
|
use persistent_store::Storage;
|
||||||
|
let num_pages = storage.num_pages();
|
||||||
|
if erase {
|
||||||
|
for page in 0..num_pages {
|
||||||
|
storage.erase_page(page).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Store::new(storage).ok().unwrap()
|
||||||
|
}*/
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
// Fix to be faster.
|
||||||
|
//let storage = take_storage().unwrap();
|
||||||
|
//let mut _store = boot_store(storage, true);
|
||||||
|
|
||||||
let mut console = Console::new();
|
let mut console = Console::new();
|
||||||
|
let mut rng = rng256::TockRng256 {};
|
||||||
// Setup the timer with a dummy callback (we only care about reading the current time, but the
|
// Setup the timer with a dummy callback (we only care about reading the current time, but the
|
||||||
// API forces us to set an alarm callback too).
|
// API forces us to set an alarm callback too).
|
||||||
let mut with_callback = timer::with_callback(|_, _| {});
|
let mut with_callback = timer::with_callback(|_, _| {});
|
||||||
let timer = with_callback.init().flex_unwrap();
|
let timer = with_callback.init().flex_unwrap();
|
||||||
|
|
||||||
let mut rng = TockRng256 {};
|
|
||||||
|
|
||||||
writeln!(console, "****************************************").unwrap();
|
writeln!(console, "****************************************").unwrap();
|
||||||
writeln!(
|
writeln!(
|
||||||
console,
|
console,
|
||||||
@@ -46,136 +61,133 @@ fn main() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// AES
|
custom_bench(
|
||||||
bench(&mut console, &timer, "aes256::EncryptionKey::new", || {
|
|
||||||
aes256::EncryptionKey::new(&[0; 32]);
|
|
||||||
});
|
|
||||||
let ek = aes256::EncryptionKey::new(&[0; 32]);
|
|
||||||
bench(&mut console, &timer, "aes256::DecryptionKey::new", || {
|
|
||||||
aes256::DecryptionKey::new(&ek);
|
|
||||||
});
|
|
||||||
let dk = aes256::DecryptionKey::new(&ek);
|
|
||||||
|
|
||||||
bench(
|
|
||||||
&mut console,
|
&mut console,
|
||||||
&timer,
|
&timer,
|
||||||
"aes256::EncryptionKey::encrypt_block",
|
"ECDSA keygen",
|
||||||
|| {
|
1000,
|
||||||
ek.encrypt_block(&mut [0; 16]);
|
|| {},
|
||||||
},
|
|()| {
|
||||||
);
|
let k = ecdsa::SecKey::gensk(&mut rng);
|
||||||
bench(
|
k.genpk();
|
||||||
&mut console,
|
|
||||||
&timer,
|
|
||||||
"aes256::DecryptionKey::decrypt_block",
|
|
||||||
|| {
|
|
||||||
dk.decrypt_block(&mut [0; 16]);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// CBC
|
custom_bench(
|
||||||
let mut blocks = Vec::new();
|
|
||||||
for i in 0..8 {
|
|
||||||
blocks.resize(1 << (i + 4), 0);
|
|
||||||
bench(
|
|
||||||
&mut console,
|
|
||||||
&timer,
|
|
||||||
&format!("cbc::cbc_encrypt({} bytes)", blocks.len()),
|
|
||||||
|| {
|
|
||||||
cbc::cbc_encrypt(&ek, [0; 16], &mut blocks);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
drop(blocks);
|
|
||||||
|
|
||||||
let mut blocks = Vec::new();
|
|
||||||
for i in 0..8 {
|
|
||||||
blocks.resize(1 << (i + 4), 0);
|
|
||||||
bench(
|
|
||||||
&mut console,
|
|
||||||
&timer,
|
|
||||||
&format!("cbc::cbc_decrypt({} bytes)", blocks.len()),
|
|
||||||
|| {
|
|
||||||
cbc::cbc_decrypt(&dk, [0; 16], &mut blocks);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
drop(blocks);
|
|
||||||
|
|
||||||
// SHA-256
|
|
||||||
let mut contents = Vec::new();
|
|
||||||
for i in 0..8 {
|
|
||||||
contents.resize(16 << i, 0);
|
|
||||||
bench(
|
|
||||||
&mut console,
|
|
||||||
&timer,
|
|
||||||
&format!("sha256::Sha256::update({} bytes)", contents.len()),
|
|
||||||
|| {
|
|
||||||
let mut sha = sha256::Sha256::new();
|
|
||||||
sha.update(&contents);
|
|
||||||
sha.finalize();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
drop(contents);
|
|
||||||
|
|
||||||
// ECDSA
|
|
||||||
bench(&mut console, &timer, "ecdsa::SecKey::gensk", || {
|
|
||||||
ecdsa::SecKey::gensk(&mut rng);
|
|
||||||
});
|
|
||||||
let k = ecdsa::SecKey::gensk(&mut rng);
|
|
||||||
bench(&mut console, &timer, "ecdsa::SecKey::genpk", || {
|
|
||||||
k.genpk();
|
|
||||||
});
|
|
||||||
bench(
|
|
||||||
&mut console,
|
&mut console,
|
||||||
&timer,
|
&timer,
|
||||||
"ecdsa::SecKey::sign_rng::<sha256::Sha256, _>",
|
"ECDSA sign",
|
||||||
|
1000,
|
||||||
|| {
|
|| {
|
||||||
k.sign_rng::<sha256::Sha256, _>(&[], &mut rng);
|
let k = ecdsa::SecKey::gensk(&mut rng);
|
||||||
|
let mut m = [0; 64];
|
||||||
|
rng.fill_bytes(&mut m);
|
||||||
|
(k, m)
|
||||||
},
|
},
|
||||||
);
|
|(k, m)| {
|
||||||
bench(
|
k.sign_rfc6979::<Sha256>(&m);
|
||||||
&mut console,
|
|
||||||
&timer,
|
|
||||||
"ecdsa::SecKey::sign_rfc6979::<sha256::Sha256>",
|
|
||||||
|| {
|
|
||||||
k.sign_rfc6979::<sha256::Sha256>(&[]);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
writeln!(console, "****************************************").unwrap();
|
custom_bench(
|
||||||
writeln!(console, "All the benchmarks are done.\nHave a nice day!").unwrap();
|
&mut console,
|
||||||
writeln!(console, "****************************************").unwrap();
|
&timer,
|
||||||
|
"dilithium::SecKey::gensk_with_pk",
|
||||||
|
1000,
|
||||||
|
|| {},
|
||||||
|
|()| {
|
||||||
|
dilithium::sign::SecKey::gensk_with_pk(&mut rng);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
custom_bench(
|
||||||
|
&mut console,
|
||||||
|
&timer,
|
||||||
|
"dilithium::SecKey::sign",
|
||||||
|
1000,
|
||||||
|
|| {
|
||||||
|
let sk = dilithium::sign::SecKey::gensk(&mut rng);
|
||||||
|
let mut m = [0; 64];
|
||||||
|
rng.fill_bytes(&mut m);
|
||||||
|
(sk, m)
|
||||||
|
},
|
||||||
|
|(sk, m)| {
|
||||||
|
sk.sign(&m);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
custom_bench(
|
||||||
|
&mut console,
|
||||||
|
&timer,
|
||||||
|
"hybrid::SecKey::gensk_with_pk",
|
||||||
|
1000,
|
||||||
|
|| {},
|
||||||
|
|()| {
|
||||||
|
hybrid::SecKey::gensk_with_pk(&mut rng);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
custom_bench(
|
||||||
|
&mut console,
|
||||||
|
&timer,
|
||||||
|
"hybrid::SecKey::sign",
|
||||||
|
1000,
|
||||||
|
|| {
|
||||||
|
let sk = hybrid::SecKey::gensk(&mut rng);
|
||||||
|
let mut m = [0; 64];
|
||||||
|
rng.fill_bytes(&mut m);
|
||||||
|
(sk, m)
|
||||||
|
},
|
||||||
|
|(sk, m)| {
|
||||||
|
sk.sign_rfc6979::<Sha256>(&m).to_asn1_der();
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench<F>(console: &mut Console, timer: &Timer, title: &str, mut f: F)
|
fn custom_bench<I, O, F, S>(
|
||||||
where
|
console: &mut Console,
|
||||||
F: FnMut(),
|
timer: &Timer,
|
||||||
|
title: &str,
|
||||||
|
iter_count: usize,
|
||||||
|
mut setup: S,
|
||||||
|
mut f: F,
|
||||||
|
) where
|
||||||
|
S: FnMut() -> I,
|
||||||
|
F: FnMut(I) -> O,
|
||||||
{
|
{
|
||||||
writeln!(console, "****************************************").unwrap();
|
writeln!(console, "****************************************").unwrap();
|
||||||
writeln!(console, "Benchmarking: {}", title).unwrap();
|
writeln!(console, "Benchmarking: {}", title).unwrap();
|
||||||
writeln!(console, "----------------------------------------").unwrap();
|
writeln!(console, "----------------------------------------").unwrap();
|
||||||
let mut count = 1;
|
|
||||||
for _ in 0..30 {
|
let mut elapsed = 0.0;
|
||||||
|
|
||||||
|
for _ in 1..(iter_count + 1) {
|
||||||
|
let inputs = setup();
|
||||||
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||||
for _ in 0..count {
|
f(inputs);
|
||||||
f();
|
|
||||||
}
|
|
||||||
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||||
let elapsed = (end - start).ms();
|
|
||||||
writeln!(
|
let mut run_duration = (end - start).ms();
|
||||||
console,
|
|
||||||
"{} ms elapsed for {} iterations ({} ms/iter)",
|
// After 512 seconds, we get a negative difference between the start
|
||||||
elapsed,
|
// time and the end time.
|
||||||
count,
|
if run_duration < 0.0 {
|
||||||
elapsed / (count as f64)
|
run_duration += 512.0 * 1000.0;
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
console.flush();
|
|
||||||
if elapsed > 1000.0 {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
count <<= 1;
|
|
||||||
|
elapsed += run_duration;
|
||||||
|
|
||||||
|
writeln!(console, "{},", run_duration).unwrap();
|
||||||
|
console.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
console,
|
||||||
|
"Total: {} ms elapsed for {} iterations ({} ms/iter)",
|
||||||
|
elapsed,
|
||||||
|
iter_count,
|
||||||
|
elapsed / (iter_count as f64)
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
console.flush();
|
||||||
}
|
}
|
||||||
|
|||||||
184
examples/measure_stack.rs
Normal file
184
examples/measure_stack.rs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
// Copyright 2022 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]
|
||||||
|
#![feature(asm)]
|
||||||
|
#![feature(llvm_asm)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
extern crate lang_items;
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
use core::ptr;
|
||||||
|
use crypto::sha256::Sha256;
|
||||||
|
use crypto::{ecdsa, hybrid, sha256};
|
||||||
|
use libtock_drivers::console::Console;
|
||||||
|
|
||||||
|
libtock_core::stack_size! {0x11800}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn read_stack_pointer() -> u32 {
|
||||||
|
let x = 1u32;
|
||||||
|
let address = &x as *const u32;
|
||||||
|
address as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn print_stack_pointer(console: &mut Console) {
|
||||||
|
let x = 1u32;
|
||||||
|
writeln!(console, "Stack pointer: {:?}", &x as *const u32).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes a byte pattern to a memory range.
|
||||||
|
///
|
||||||
|
/// Since the stack grows to lower addresses, end < start.
|
||||||
|
/// Addresses after start must be unused, i.e. start must be at least the current stack pointer.
|
||||||
|
/// Addresses until end should be within the stack area.
|
||||||
|
unsafe fn paint_memory(start: u32, end: u32) {
|
||||||
|
for address in (end..start).step_by(4) {
|
||||||
|
let p = address as *const u32;
|
||||||
|
ptr::write(p as *mut u32, 0xCDCDCDCD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the lowest address that does not have the 0xCD pattern.
|
||||||
|
unsafe fn find_border(start: u32, end: u32) -> u32 {
|
||||||
|
for address in (end..start).step_by(4) {
|
||||||
|
let p = address as *const u32;
|
||||||
|
if ptr::read(p) != 0xCDCDCDCD {
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
pub fn black_box<T>(dummy: T) -> T {
|
||||||
|
unsafe { llvm_asm!("" : : "r"(&dummy)) }
|
||||||
|
dummy
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn keygen_ecdsa(rng: &mut rng256::TockRng256) {
|
||||||
|
let sk = ecdsa::SecKey::gensk(rng);
|
||||||
|
black_box(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn keygen_dilithium(rng: &mut rng256::TockRng256) {
|
||||||
|
let sk = dilithium::sign::SecKey::gensk(rng);
|
||||||
|
black_box(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn keygen_hybrid(rng: &mut rng256::TockRng256) {
|
||||||
|
let sk = hybrid::SecKey::gensk_with_pk(rng);
|
||||||
|
black_box(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn sign_ecdsa(rng: &mut rng256::TockRng256, sk: &ecdsa::SecKey) {
|
||||||
|
let sig = sk.sign_rng::<sha256::Sha256, _>(&[], rng);
|
||||||
|
black_box(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sign_dilithium(sk: &dilithium::sign::SecKey) {
|
||||||
|
let sig = sk.sign(&[]);
|
||||||
|
black_box(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn sign_hybrid(sk: &hybrid::SecKey) {
|
||||||
|
let sig = sk.sign_rfc6979::<Sha256>(&[]);
|
||||||
|
black_box(sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure the stack usage of the method itself, plus a u32.
|
||||||
|
#[inline(never)]
|
||||||
|
fn dummy_test() {
|
||||||
|
let x = 1u32;
|
||||||
|
black_box(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests whether input parameters are correctly ignored in the measurement.
|
||||||
|
#[inline(never)]
|
||||||
|
fn param_test(big_param: &mut [u8; 0x1000]) {
|
||||||
|
let x = 0x01;
|
||||||
|
big_param[0] = x;
|
||||||
|
black_box(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_result(console: &mut Console, text: &str, size: u32) {
|
||||||
|
writeln!(console, "{} size: 0x{:08X}", text, size).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut console = Console::new();
|
||||||
|
|
||||||
|
let x = 1u32;
|
||||||
|
let sp = &x as *const u32;
|
||||||
|
// Should be safe to write from here.
|
||||||
|
let start = sp as u32 - 0x100u32;
|
||||||
|
writeln!(console, "Search start address: 0x{:08X}", start).unwrap();
|
||||||
|
print_stack_pointer(&mut console);
|
||||||
|
|
||||||
|
let mut rng = rng256::TockRng256 {};
|
||||||
|
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
keygen_ecdsa(&mut rng);
|
||||||
|
let min_address1 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
keygen_dilithium(&mut rng);
|
||||||
|
let min_address2 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
keygen_hybrid(&mut rng);
|
||||||
|
let min_address3 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
let sk = ecdsa::SecKey::gensk(&mut rng);
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
sign_ecdsa(&mut rng, &sk);
|
||||||
|
let min_address4 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
let sk = dilithium::sign::SecKey::gensk(&mut rng);
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
sign_dilithium(&sk);
|
||||||
|
let min_address5 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
let sk = hybrid::SecKey::gensk(&mut rng);
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
sign_hybrid(&sk);
|
||||||
|
let min_address6 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
let mut param = [0; 0x1000];
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
param_test(&mut param);
|
||||||
|
let min_address7 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
unsafe { paint_memory(start, 0x20020000) };
|
||||||
|
dummy_test();
|
||||||
|
let min_address8 = unsafe { find_border(start, 0x20020000) };
|
||||||
|
|
||||||
|
let main_end = read_stack_pointer();
|
||||||
|
write_result(&mut console, " keygen_ecdsa", main_end - min_address1);
|
||||||
|
write_result(&mut console, "keygen_dilithium", main_end - min_address2);
|
||||||
|
write_result(&mut console, " keygen_hybrid", main_end - min_address3);
|
||||||
|
write_result(&mut console, " sign_ecdsa", main_end - min_address4);
|
||||||
|
write_result(&mut console, " sign_dilithium", main_end - min_address5);
|
||||||
|
write_result(&mut console, " sign_hybrid", main_end - min_address6);
|
||||||
|
write_result(&mut console, " test dummy", main_end - min_address7);
|
||||||
|
write_result(&mut console, " test input", main_end - min_address8);
|
||||||
|
}
|
||||||
@@ -132,49 +132,49 @@ mod example {
|
|||||||
match buf[0] {
|
match buf[0] {
|
||||||
0xe0 /* RATS */=> {
|
0xe0 /* RATS */=> {
|
||||||
let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
|
let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
|
return_code = bench_transmit(&mut console, timer, "TX: ATS", &mut answer_to_select);
|
||||||
}
|
}
|
||||||
0xc2 /* DESELECT */ => {
|
0xc2 /* DESELECT */ => {
|
||||||
// Ignore the request
|
// Ignore the request
|
||||||
let mut command_error = [0x6A, 0x81];
|
let mut command_error = [0x6A, 0x81];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
|
return_code = bench_transmit(&mut console, timer, "TX: DESELECT", &mut command_error);
|
||||||
}
|
}
|
||||||
0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
|
0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
|
||||||
// If the received packet is applet selection command (FIDO 2)
|
// If the received packet is applet selection command (FIDO 2)
|
||||||
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
|
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
|
||||||
// Vesion: "FIDO_2_0"
|
// Vesion: "FIDO_2_0"
|
||||||
let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
|
let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: Version Str", &mut reply);
|
||||||
} else if (buf[6] == 0xd2 && buf[7] == 0x76) || (buf[6] == 0xe1 && (buf[7] == 0x03 || buf[7] == 0x04)){
|
} else if (buf[6] == 0xd2 && buf[7] == 0x76) || (buf[6] == 0xe1 && (buf[7] == 0x03 || buf[7] == 0x04)){
|
||||||
let mut reply = [buf[0], 0x90, 0x00];
|
let mut reply = [buf[0], 0x90, 0x00];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: 0x9000", &mut reply);
|
||||||
} else /* Unknown file */ {
|
} else /* Unknown file */ {
|
||||||
let mut reply = [buf[0], 0x6a, 0x82];
|
let mut reply = [buf[0], 0x6a, 0x82];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x6A82", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: 0x6A82", &mut reply);
|
||||||
}
|
}
|
||||||
0xb0 /* READ */ => match buf[5] {
|
0xb0 /* READ */ => match buf[5] {
|
||||||
0x02 => {
|
0x02 => {
|
||||||
let mut reply = [buf[0], 0x12, 0x90, 0x00,];
|
let mut reply = [buf[0], 0x12, 0x90, 0x00,];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: File Size", &mut reply);
|
||||||
}
|
}
|
||||||
0x12 => {
|
0x12 => {
|
||||||
let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
|
let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
|
||||||
0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
|
0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: NDEF", &mut reply);
|
||||||
}
|
}
|
||||||
0x0f => {
|
0x0f => {
|
||||||
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
|
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
|
||||||
0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
|
0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: CC", &mut reply);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut reply = [buf[0], 0x90, 0x00];
|
let mut reply = [buf[0], 0x90, 0x00];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: 0x9000", &mut reply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut reply = [buf[0], 0x90, 0x00];
|
let mut reply = [buf[0], 0x90, 0x00];
|
||||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
return_code = bench_transmit(&mut console, timer, "TX: 0x9000", &mut reply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
|
0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
|
||||||
|
|||||||
52
increase_stack.patch
Normal file
52
increase_stack.patch
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
diff --git a/deploy.py b/deploy.py
|
||||||
|
index 7f91a2b..f7b1e9a 100755
|
||||||
|
--- a/deploy.py
|
||||||
|
+++ b/deploy.py
|
||||||
|
@@ -156,7 +156,7 @@ SUPPORTED_BOARDS = {
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
-APP_HEAP_SIZE = 32768
|
||||||
|
+APP_HEAP_SIZE = 16384
|
||||||
|
|
||||||
|
|
||||||
|
def get_supported_boards() -> Tuple[str]:
|
||||||
|
diff --git a/examples/measure_stack.rs b/examples/measure_stack.rs
|
||||||
|
index 88f9ebc..d285a80 100644
|
||||||
|
--- a/examples/measure_stack.rs
|
||||||
|
+++ b/examples/measure_stack.rs
|
||||||
|
@@ -26,7 +26,7 @@ use crypto::{ecdsa, hybrid, sha256};
|
||||||
|
use crypto::sha256::Sha256;
|
||||||
|
use libtock_drivers::console::Console;
|
||||||
|
|
||||||
|
-libtock_core::stack_size! {0x11800}
|
||||||
|
+libtock_core::stack_size! {0x1A000}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn read_stack_pointer() -> u32 {
|
||||||
|
diff --git a/nrf52840_layout.ld b/nrf52840_layout.ld
|
||||||
|
index 538a2a8..c7dd5e7 100644
|
||||||
|
--- a/nrf52840_layout.ld
|
||||||
|
+++ b/nrf52840_layout.ld
|
||||||
|
@@ -14,7 +14,7 @@ MEMORY {
|
||||||
|
* Any change to STACK_SIZE should be accompanied by a corresponding change to
|
||||||
|
* `elf2tab`'s `--stack` option
|
||||||
|
*/
|
||||||
|
-STACK_SIZE = 71680;
|
||||||
|
+STACK_SIZE = 106496;
|
||||||
|
|
||||||
|
MPU_MIN_ALIGN = 8K;
|
||||||
|
|
||||||
|
diff --git a/patches/tock/07-app-break-fix.patch b/patches/tock/07-app-break-fix.patch
|
||||||
|
index fcf46fd..4048b59 100644
|
||||||
|
--- a/patches/tock/07-app-break-fix.patch
|
||||||
|
+++ b/patches/tock/07-app-break-fix.patch
|
||||||
|
@@ -7,7 +7,7 @@ index c78b1c9fb..2769d0138 100644
|
||||||
|
// The 1.x Tock kernel allocates at least 3 kB to processes, and we need
|
||||||
|
// to ensure that happens as userspace may expect it.
|
||||||
|
- 3 * 1024
|
||||||
|
-+ 70 * 1024
|
||||||
|
++ 104 * 1024
|
||||||
|
|
||||||
|
// TOCK 2.0
|
||||||
|
//
|
||||||
@@ -13,6 +13,7 @@ edition = "2018"
|
|||||||
rng256 = { path = "../rng256" }
|
rng256 = { path = "../rng256" }
|
||||||
arrayref = "0.3.6"
|
arrayref = "0.3.6"
|
||||||
subtle = { version = "2.2.3", default-features = false, features = ["nightly"] }
|
subtle = { version = "2.2.3", default-features = false, features = ["nightly"] }
|
||||||
|
dilithium = { path = "../../third_party/dilithium" }
|
||||||
byteorder = { version = "1", default-features = false }
|
byteorder = { version = "1", default-features = false }
|
||||||
hex = { version = "0.3.2", default-features = false, optional = true }
|
hex = { version = "0.3.2", default-features = false, optional = true }
|
||||||
ring = { version = "0.16.11", optional = true }
|
ring = { version = "0.16.11", optional = true }
|
||||||
|
|||||||
@@ -308,6 +308,11 @@ pub mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rng256 for StressTestingRng {
|
impl Rng256 for StressTestingRng {
|
||||||
|
// This function is unused, as we redefine gen_uniform_u32x8.
|
||||||
|
fn fill_bytes(&mut self, _buf: &mut [u8]) {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
// This function is unused, as we redefine gen_uniform_u32x8.
|
// This function is unused, as we redefine gen_uniform_u32x8.
|
||||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ use super::exponent256::ExponentP256;
|
|||||||
use super::gfp256::GFP256;
|
use super::gfp256::GFP256;
|
||||||
use super::int256::Int256;
|
use super::int256::Int256;
|
||||||
use super::montgomery::Montgomery;
|
use super::montgomery::Montgomery;
|
||||||
#[cfg(test)]
|
|
||||||
use arrayref::array_mut_ref;
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
|
use arrayref::array_mut_ref;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::ops::Add;
|
use core::ops::Add;
|
||||||
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||||
@@ -26,7 +25,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
|||||||
// A point on the elliptic curve is represented by two field elements.
|
// A point on the elliptic curve is represented by two field elements.
|
||||||
// The "direct" representation with GFP256 (integer modulo p) is used for serialization of public
|
// The "direct" representation with GFP256 (integer modulo p) is used for serialization of public
|
||||||
// keys.
|
// keys.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct PointP256 {
|
pub struct PointP256 {
|
||||||
x: GFP256,
|
x: GFP256,
|
||||||
y: GFP256,
|
y: GFP256,
|
||||||
@@ -45,7 +44,6 @@ impl PointP256 {
|
|||||||
/** Serialization **/
|
/** Serialization **/
|
||||||
// This uses uncompressed point format from "SEC 1: Elliptic Curve Cryptography" ("Standards for
|
// This uses uncompressed point format from "SEC 1: Elliptic Curve Cryptography" ("Standards for
|
||||||
// Efficient Cryptography").
|
// Efficient Cryptography").
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn from_bytes_uncompressed_vartime(bytes: &[u8]) -> Option<PointP256> {
|
pub fn from_bytes_uncompressed_vartime(bytes: &[u8]) -> Option<PointP256> {
|
||||||
if bytes.len() != 65 || bytes[0] != 0x04 {
|
if bytes.len() != 65 || bytes[0] != 0x04 {
|
||||||
None
|
None
|
||||||
@@ -57,7 +55,7 @@ impl PointP256 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(feature = "std")]
|
||||||
pub fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
|
pub fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
|
||||||
bytes[0] = 0x04;
|
bytes[0] = 0x04;
|
||||||
self.x.to_int().to_bin(array_mut_ref![bytes, 1, 32]);
|
self.x.to_int().to_bin(array_mut_ref![bytes, 1, 32]);
|
||||||
@@ -550,12 +548,6 @@ impl core::fmt::Debug for PointP256 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for PointP256 {
|
|
||||||
fn eq(&self, other: &PointP256) -> bool {
|
|
||||||
self.x == other.x && self.y == other.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -33,12 +33,13 @@ pub struct SecKey {
|
|||||||
k: NonZeroExponentP256,
|
k: NonZeroExponentP256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Signature {
|
pub struct Signature {
|
||||||
r: NonZeroExponentP256,
|
r: NonZeroExponentP256,
|
||||||
s: NonZeroExponentP256,
|
s: NonZeroExponentP256,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct PubKey {
|
pub struct PubKey {
|
||||||
p: PointP256,
|
p: PointP256,
|
||||||
}
|
}
|
||||||
@@ -231,13 +232,12 @@ impl PubKey {
|
|||||||
.map(|p| PubKey { p })
|
.map(|p| PubKey { p })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub fn from_bytes_uncompressed(bytes: &[u8]) -> Option<PubKey> {
|
pub fn from_bytes_uncompressed(bytes: &[u8]) -> Option<PubKey> {
|
||||||
PointP256::from_bytes_uncompressed_vartime(bytes).map(|p| PubKey { p })
|
PointP256::from_bytes_uncompressed_vartime(bytes).map(|p| PubKey { p })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(feature = "std")]
|
||||||
fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
|
pub fn to_bytes_uncompressed(&self, bytes: &mut [u8; 65]) {
|
||||||
self.p.to_bytes_uncompressed(bytes);
|
self.p.to_bytes_uncompressed(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
246
libraries/crypto/src/hybrid.rs
Normal file
246
libraries/crypto/src/hybrid.rs
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
// Copyright 2021-2022 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 super::ecdsa;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
// A label generated uniformly at random from the output space of SHA256.
|
||||||
|
const LABEL: [u8; 32] = [
|
||||||
|
43, 253, 32, 250, 19, 51, 24, 237, 138, 49, 47, 182, 4, 194, 133, 183, 177, 218, 115, 58, 92,
|
||||||
|
117, 45, 172, 156, 5, 214, 176, 248, 103, 55, 216,
|
||||||
|
];
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct SecKey {
|
||||||
|
dilithium_seed: [u8; dilithium::params::SEEDBYTES],
|
||||||
|
ecdsa_sk: ecdsa::SecKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct PubKey {
|
||||||
|
pub dilithium_pk: dilithium::sign::PubKey,
|
||||||
|
pub ecdsa_pk: ecdsa::PubKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct Signature {
|
||||||
|
pub dilithium_sign: Vec<u8>,
|
||||||
|
pub ecdsa_sign: ecdsa::Signature,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ecdsa_input(msg: &[u8]) -> Vec<u8> {
|
||||||
|
let mut input = LABEL.to_vec();
|
||||||
|
input.extend(msg);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dilithium_input(msg: &[u8], ecdsa_sign: &ecdsa::Signature) -> Vec<u8> {
|
||||||
|
let mut input = LABEL.to_vec();
|
||||||
|
input.extend(msg);
|
||||||
|
input.extend(ecdsa_sign.to_asn1_der());
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SecKey {
|
||||||
|
pub const BYTES_LENGTH: usize = 32 + dilithium::params::SEEDBYTES;
|
||||||
|
pub fn gensk<R>(rng: &mut R) -> SecKey
|
||||||
|
where
|
||||||
|
R: rng256::Rng256,
|
||||||
|
{
|
||||||
|
let mut seed = [0u8; dilithium::params::SEEDBYTES];
|
||||||
|
rng.fill_bytes(&mut seed);
|
||||||
|
SecKey {
|
||||||
|
dilithium_seed: seed,
|
||||||
|
ecdsa_sk: ecdsa::SecKey::gensk(rng),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gensk_with_pk<R>(rng: &mut R) -> (SecKey, PubKey)
|
||||||
|
where
|
||||||
|
R: rng256::Rng256,
|
||||||
|
{
|
||||||
|
let mut seed = [0u8; dilithium::params::SEEDBYTES];
|
||||||
|
rng.fill_bytes(&mut seed);
|
||||||
|
let (_, dilithium_pk) = dilithium::sign::SecKey::gensk_with_pk_from_seed(&seed);
|
||||||
|
let ecdsa_sk = ecdsa::SecKey::gensk(rng);
|
||||||
|
let ecdsa_pk = ecdsa_sk.genpk();
|
||||||
|
let sk = SecKey {
|
||||||
|
dilithium_seed: seed,
|
||||||
|
ecdsa_sk,
|
||||||
|
};
|
||||||
|
let pk = PubKey {
|
||||||
|
dilithium_pk,
|
||||||
|
ecdsa_pk,
|
||||||
|
};
|
||||||
|
(sk, pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn genpk(&self) -> PubKey {
|
||||||
|
let (_, dilithium_pk) =
|
||||||
|
dilithium::sign::SecKey::gensk_with_pk_from_seed(&self.dilithium_seed);
|
||||||
|
PubKey {
|
||||||
|
dilithium_pk,
|
||||||
|
ecdsa_pk: self.ecdsa_sk.genpk(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sign_rfc6979<H>(&self, msg: &[u8]) -> Signature
|
||||||
|
where
|
||||||
|
H: super::Hash256 + super::HashBlockSize64Bytes,
|
||||||
|
{
|
||||||
|
let ecdsa_sign = self.ecdsa_sk.sign_rfc6979::<H>(&ecdsa_input(&msg));
|
||||||
|
let dilithium_sk = dilithium::sign::SecKey::gensk_from_seed(&self.dilithium_seed);
|
||||||
|
// This wastes some stack, we could revert the Dilithium API to take a &mut [u8].
|
||||||
|
let dilithium_sign = dilithium_sk
|
||||||
|
.sign(&dilithium_input(&msg, &ecdsa_sign))
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
return Signature {
|
||||||
|
ecdsa_sign,
|
||||||
|
dilithium_sign,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(bytes: &[u8; SecKey::BYTES_LENGTH]) -> Option<SecKey> {
|
||||||
|
let ecdsa_bytes = array_ref!(bytes, 0, 32);
|
||||||
|
let ecdsa_sk = ecdsa::SecKey::from_bytes(&ecdsa_bytes)?;
|
||||||
|
|
||||||
|
let dilithium_seed = array_ref!(bytes, 32, dilithium::params::SEEDBYTES).clone();
|
||||||
|
|
||||||
|
return Some(SecKey {
|
||||||
|
ecdsa_sk,
|
||||||
|
dilithium_seed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self, bytes: &mut [u8; SecKey::BYTES_LENGTH]) {
|
||||||
|
let mut ecdsa_bytes = array_mut_ref!(bytes, 0, 32);
|
||||||
|
self.ecdsa_sk.to_bytes(&mut ecdsa_bytes);
|
||||||
|
let dilithium_bytes = array_mut_ref!(bytes, 32, dilithium::params::SEEDBYTES);
|
||||||
|
dilithium_bytes.copy_from_slice(&self.dilithium_seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PubKey {
|
||||||
|
pub const BYTES_LENGTH: usize = 2 * ecdsa::NBYTES + dilithium::params::PK_SIZE_PACKED;
|
||||||
|
|
||||||
|
pub fn from_bytes(bytes: &[u8; PubKey::BYTES_LENGTH]) -> Option<PubKey> {
|
||||||
|
let ecdsa_x_bytes = array_ref!(bytes, 0, ecdsa::NBYTES);
|
||||||
|
let ecdsa_y_bytes = array_ref!(bytes, ecdsa::NBYTES, ecdsa::NBYTES);
|
||||||
|
|
||||||
|
let ecdsa_pk = ecdsa::PubKey::from_coordinates(&ecdsa_x_bytes, &ecdsa_y_bytes)?;
|
||||||
|
|
||||||
|
let dilithium_bytes = array_ref!(
|
||||||
|
bytes,
|
||||||
|
ecdsa::NBYTES + ecdsa::NBYTES,
|
||||||
|
dilithium::params::PK_SIZE_PACKED
|
||||||
|
)
|
||||||
|
.clone();
|
||||||
|
let dilithium_pk = dilithium::sign::PubKey::from_bytes(&dilithium_bytes);
|
||||||
|
|
||||||
|
Some(PubKey {
|
||||||
|
ecdsa_pk,
|
||||||
|
dilithium_pk,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self, bytes: &mut [u8; PubKey::BYTES_LENGTH]) {
|
||||||
|
let mut ecdsa_x_bytes = [0; ecdsa::NBYTES];
|
||||||
|
let mut ecdsa_y_bytes = [0; ecdsa::NBYTES];
|
||||||
|
self.ecdsa_pk
|
||||||
|
.to_coordinates(&mut ecdsa_x_bytes, &mut ecdsa_y_bytes);
|
||||||
|
array_mut_ref!(bytes, 0, ecdsa::NBYTES).clone_from(&ecdsa_x_bytes);
|
||||||
|
array_mut_ref!(bytes, ecdsa::NBYTES, ecdsa::NBYTES).clone_from(&ecdsa_y_bytes);
|
||||||
|
let mut dilithium_bytes = array_mut_ref!(
|
||||||
|
bytes,
|
||||||
|
ecdsa::NBYTES + ecdsa::NBYTES,
|
||||||
|
dilithium::params::PK_SIZE_PACKED
|
||||||
|
);
|
||||||
|
self.dilithium_pk.to_bytes(&mut dilithium_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify_vartime<H>(&self, msg: &[u8], sign: &Signature) -> bool
|
||||||
|
where
|
||||||
|
H: super::Hash256,
|
||||||
|
{
|
||||||
|
return self
|
||||||
|
.ecdsa_pk
|
||||||
|
.verify_hash_vartime(&H::hash(&ecdsa_input(&msg)), &sign.ecdsa_sign)
|
||||||
|
&& self.dilithium_pk.verify(
|
||||||
|
&dilithium_input(&msg, &sign.ecdsa_sign),
|
||||||
|
array_ref!(sign.dilithium_sign, 0, dilithium::params::SIG_SIZE_PACKED),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signature {
|
||||||
|
pub const BYTES_LENGTH: usize = 64 + dilithium::params::SIG_SIZE_PACKED;
|
||||||
|
|
||||||
|
/// Converts a signature into the CBOR required byte array representation.
|
||||||
|
///
|
||||||
|
/// This operation consumes the signature to efficiently use memory.
|
||||||
|
pub fn to_asn1_der(self) -> Vec<u8> {
|
||||||
|
let mut bytes = self.ecdsa_sign.to_asn1_der();
|
||||||
|
bytes.reserve_exact(dilithium::params::SIG_SIZE_PACKED);
|
||||||
|
bytes.extend(self.dilithium_sign.into_iter());
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
extern crate rng256;
|
||||||
|
use super::super::sha256::Sha256;
|
||||||
|
use super::*;
|
||||||
|
use rng256::Rng256;
|
||||||
|
|
||||||
|
pub const ITERATIONS: u32 = 500;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hybrid_seckey_to_bytes_from_bytes() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let mut bytes = [0; SecKey::BYTES_LENGTH];
|
||||||
|
sk.to_bytes(&mut bytes);
|
||||||
|
let decoded_sk = SecKey::from_bytes(&bytes);
|
||||||
|
assert_eq!(decoded_sk, Some(sk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hybrid_pubkey_to_bytes_from_bytes() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let pk = sk.genpk();
|
||||||
|
let mut bytes = [0; PubKey::BYTES_LENGTH];
|
||||||
|
pk.to_bytes(&mut bytes);
|
||||||
|
let decoded_pk = PubKey::from_bytes(&bytes);
|
||||||
|
assert_eq!(decoded_pk, Some(pk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hybrid_sign_rfc6979_verify_vartime() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let msg = rng.gen_uniform_u8x32();
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let pk = sk.genpk();
|
||||||
|
let sign = sk.sign_rfc6979::<Sha256>(&msg);
|
||||||
|
assert!(pk.verify_vartime::<Sha256>(&msg, &sign));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2019 Google LLC
|
// Copyright 2019-2022 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.
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
#![feature(wrapping_int_impl)]
|
#![feature(wrapping_int_impl)]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate arrayref;
|
||||||
|
|
||||||
pub mod aes256;
|
pub mod aes256;
|
||||||
pub mod cbc;
|
pub mod cbc;
|
||||||
@@ -24,6 +26,7 @@ pub mod ecdh;
|
|||||||
pub mod ecdsa;
|
pub mod ecdsa;
|
||||||
pub mod hkdf;
|
pub mod hkdf;
|
||||||
pub mod hmac;
|
pub mod hmac;
|
||||||
|
pub mod hybrid;
|
||||||
pub mod sha256;
|
pub mod sha256;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ use rand::Rng;
|
|||||||
|
|
||||||
// Lightweight RNG trait to generate uniformly distributed 256 bits.
|
// Lightweight RNG trait to generate uniformly distributed 256 bits.
|
||||||
pub trait Rng256 {
|
pub trait Rng256 {
|
||||||
|
fn fill_bytes(&mut self, buf: &mut [u8]);
|
||||||
|
|
||||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32];
|
fn gen_uniform_u8x32(&mut self) -> [u8; 32];
|
||||||
|
|
||||||
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
|
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
|
||||||
@@ -45,6 +47,10 @@ fn bytes_to_u32(bytes: [u8; 32]) -> [u32; 8] {
|
|||||||
pub struct TockRng256 {}
|
pub struct TockRng256 {}
|
||||||
|
|
||||||
impl Rng256 for TockRng256 {
|
impl Rng256 for TockRng256 {
|
||||||
|
fn fill_bytes(&mut self, buf: &mut [u8]) {
|
||||||
|
libtock_drivers::rng::fill_buffer(buf);
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||||
let mut buf: [u8; 32] = [Default::default(); 32];
|
let mut buf: [u8; 32] = [Default::default(); 32];
|
||||||
rng::fill_buffer(&mut buf);
|
rng::fill_buffer(&mut buf);
|
||||||
@@ -58,6 +64,11 @@ pub struct ThreadRng256 {}
|
|||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Rng256 for ThreadRng256 {
|
impl Rng256 for ThreadRng256 {
|
||||||
|
fn fill_bytes(&mut self, buf: &mut [u8]) {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
rng.fill(buf);
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut result = [Default::default(); 32];
|
let mut result = [Default::default(); 32];
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ MEMORY {
|
|||||||
* Any change to STACK_SIZE should be accompanied by a corresponding change to
|
* Any change to STACK_SIZE should be accompanied by a corresponding change to
|
||||||
* `elf2tab`'s `--stack` option
|
* `elf2tab`'s `--stack` option
|
||||||
*/
|
*/
|
||||||
STACK_SIZE = 16384;
|
STACK_SIZE = 71680;
|
||||||
|
|
||||||
MPU_MIN_ALIGN = 8K;
|
MPU_MIN_ALIGN = 8K;
|
||||||
|
|
||||||
|
|||||||
13
patches/libtock-rs/02-compiler-version.patch
Normal file
13
patches/libtock-rs/02-compiler-version.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/rust-toolchain b/rust-toolchain
|
||||||
|
index 1674405..2ba073e 100644
|
||||||
|
--- a/rust-toolchain
|
||||||
|
+++ b/rust-toolchain
|
||||||
|
@@ -1,7 +1,7 @@
|
||||||
|
[toolchain]
|
||||||
|
# See https://rust-lang.github.io/rustup-components-history/ for a list of
|
||||||
|
# recently nightlies and what components are available for them.
|
||||||
|
-channel = "nightly-2021-03-25"
|
||||||
|
+channel = "nightly-2021-06-25"
|
||||||
|
components = ["clippy", "miri", "rustfmt"]
|
||||||
|
targets = ["thumbv7em-none-eabi",
|
||||||
|
"riscv32imac-unknown-none-elf",
|
||||||
@@ -31,19 +31,18 @@ index 5465c95f4..e596648f7 100644
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
|
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
|
||||||
index 8844bc6c3..692bad2d3 100644
|
index 8844bc6c3..00c13a7c6 100644
|
||||||
--- a/kernel/src/sched.rs
|
--- a/kernel/src/sched.rs
|
||||||
+++ b/kernel/src/sched.rs
|
+++ b/kernel/src/sched.rs
|
||||||
@@ -118,10 +118,19 @@ pub enum SchedulingDecision {
|
@@ -118,10 +118,18 @@ pub enum SchedulingDecision {
|
||||||
TrySleep,
|
TrySleep,
|
||||||
}
|
}
|
||||||
|
|
||||||
+/// Represents the type of a storage slice.
|
+/// Represents the type of a storage slice.
|
||||||
+#[derive(Copy, Clone)]
|
+#[derive(Copy, Clone)]
|
||||||
+pub enum StorageType {
|
+pub enum StorageType {
|
||||||
+ STORE = 1,
|
+ Store = 1,
|
||||||
+ PARTITION = 2,
|
+ Partition = 2,
|
||||||
+ METADATA = 3,
|
|
||||||
+}
|
+}
|
||||||
+
|
+
|
||||||
/// Represents a storage location in flash.
|
/// Represents a storage location in flash.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ index c78b1c9fb..2769d0138 100644
|
|||||||
// The 1.x Tock kernel allocates at least 3 kB to processes, and we need
|
// The 1.x Tock kernel allocates at least 3 kB to processes, and we need
|
||||||
// to ensure that happens as userspace may expect it.
|
// to ensure that happens as userspace may expect it.
|
||||||
- 3 * 1024
|
- 3 * 1024
|
||||||
+ 16 * 1024
|
+ 70 * 1024
|
||||||
|
|
||||||
// TOCK 2.0
|
// TOCK 2.0
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ index f7899d8c5..6956523c6 100644
|
|||||||
hil::usb::CtrlSetupResult::ErrGeneric
|
hil::usb::CtrlSetupResult::ErrGeneric
|
||||||
}
|
}
|
||||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
index 642039120..adb7fde14 100644
|
index 642039120..abf224f97 100644
|
||||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
@@ -44,21 +44,59 @@ static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
@@ -44,21 +44,59 @@ static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ index da3d16d85..e8f1a87a4 100644
|
|||||||
if !app.waiting {
|
if !app.waiting {
|
||||||
// The call to receive_packet() collected a pending packet.
|
// The call to receive_packet() collected a pending packet.
|
||||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
index adb7fde14..f6762b4b9 100644
|
index abf224f97..d47e5f644 100644
|
||||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
@@ -11,6 +11,7 @@ use super::descriptors::HIDSubordinateDescriptor;
|
@@ -11,6 +11,7 @@ use super::descriptors::HIDSubordinateDescriptor;
|
||||||
|
|||||||
13
patches/tock/10-kernel-stack.patch
Normal file
13
patches/tock/10-kernel-stack.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/boards/nordic/nrf52840dk_opensk/src/main.rs b/boards/nordic/nrf52840dk_opensk/src/main.rs
|
||||||
|
index 83fd0bbab..53d623b46 100644
|
||||||
|
--- a/boards/nordic/nrf52840dk_opensk/src/main.rs
|
||||||
|
+++ b/boards/nordic/nrf52840dk_opensk/src/main.rs
|
||||||
|
@@ -147,7 +147,7 @@ static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripheral
|
||||||
|
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||||
|
#[no_mangle]
|
||||||
|
#[link_section = ".stack_buffer"]
|
||||||
|
-pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
|
||||||
|
+pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
|
||||||
|
|
||||||
|
/// Supported drivers by the platform
|
||||||
|
pub struct Platform {
|
||||||
@@ -108,7 +108,7 @@ index e8f1a87a4..2c91c0968 100644
|
|||||||
} else {
|
} else {
|
||||||
// Cannot cancel now because the transaction is already in process.
|
// Cannot cancel now because the transaction is already in process.
|
||||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
index f6762b4b9..16b80cb10 100644
|
index d47e5f644..76f6af73b 100644
|
||||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||||
@@ -18,13 +18,27 @@ use core::cell::Cell;
|
@@ -18,13 +18,27 @@ use core::cell::Cell;
|
||||||
|
|||||||
68
plot_data.py
Normal file
68
plot_data.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright 2022 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.
|
||||||
|
# Lint as: python3
|
||||||
|
"""Hacky script to read RTT output from crypto_bench and plot it."""
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def read_file(filename):
|
||||||
|
with open(filename, "r", encoding="utf-8") as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
return [float(l[:-2]) for l in lines]
|
||||||
|
|
||||||
|
|
||||||
|
def below_threshold(data, threshold=10.):
|
||||||
|
a = np.array(data)
|
||||||
|
return np.mean(a / 1000. < threshold)
|
||||||
|
|
||||||
|
|
||||||
|
def percentiles(data):
|
||||||
|
s = sorted(data)
|
||||||
|
l = len(s)
|
||||||
|
for i in range(10):
|
||||||
|
print(f"{i * 10}th percentile: {s[(i * l) // 10]}")
|
||||||
|
|
||||||
|
|
||||||
|
def show_plot(data, title):
|
||||||
|
threshold_ratio = below_threshold(data)
|
||||||
|
if threshold_ratio < 0.9999:
|
||||||
|
max_range = min(max(data), 2 * 10. * 1000)
|
||||||
|
hist_range = (0, max_range)
|
||||||
|
else:
|
||||||
|
hist_range = None
|
||||||
|
plt.hist(data, bins=50, range=hist_range, label="Timing distribution")
|
||||||
|
mean = np.mean(data)
|
||||||
|
plt.axvline(x=mean, color="g", label="Mean")
|
||||||
|
if threshold_ratio < 0.9999:
|
||||||
|
plt.axvline(x=10. * 1000, color="r", label="CTAP threshold")
|
||||||
|
|
||||||
|
plt.title(title)
|
||||||
|
plt.legend()
|
||||||
|
plt.savefig(title.replace(" ", "") + "_plot.png")
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
|
||||||
|
def run(filename, title):
|
||||||
|
data = read_file(filename)
|
||||||
|
print(title, "below 10s:", below_threshold(data))
|
||||||
|
print("Mean:", np.mean(data))
|
||||||
|
percentiles(data)
|
||||||
|
show_plot(data, title)
|
||||||
|
|
||||||
|
|
||||||
|
run("make_durations.txt", "MakeCredential")
|
||||||
|
run("get_durations.txt", "GetAssertion")
|
||||||
@@ -26,6 +26,12 @@ pub trait Customization {
|
|||||||
// Constants for adjusting privacy and protection levels.
|
// Constants for adjusting privacy and protection levels.
|
||||||
// ###########################################################################
|
// ###########################################################################
|
||||||
|
|
||||||
|
/// Removes support for PIN protocol v1.
|
||||||
|
///
|
||||||
|
/// We support PIN protocol v2, "intended to aid FIPS certification".
|
||||||
|
/// To certify, you might want to remove support for v1 using this customization.
|
||||||
|
fn allows_pin_protocol_v1(&self) -> bool;
|
||||||
|
|
||||||
/// Changes the default level for the credProtect extension.
|
/// Changes the default level for the credProtect extension.
|
||||||
///
|
///
|
||||||
/// You can change this value to one of the following for more privacy:
|
/// You can change this value to one of the following for more privacy:
|
||||||
@@ -241,25 +247,11 @@ pub trait Customization {
|
|||||||
/// With P=20 and K=150, we have I=2M which is enough for 500 increments per day
|
/// With P=20 and K=150, we have I=2M which is enough for 500 increments per day
|
||||||
/// for 10 years.
|
/// for 10 years.
|
||||||
fn max_supported_resident_keys(&self) -> usize;
|
fn max_supported_resident_keys(&self) -> usize;
|
||||||
|
|
||||||
/// Sets the slot count of the multi-PIN feature.
|
|
||||||
///
|
|
||||||
/// # Invariant
|
|
||||||
///
|
|
||||||
/// - The slot count may not:
|
|
||||||
/// - make the storage entries that concatenate data of each slots
|
|
||||||
/// become larger than the storage page size,
|
|
||||||
/// - go over u8, as we only reserve 1 byte for the array index for
|
|
||||||
/// concatenated entries, or
|
|
||||||
/// - exceed the number of keys we reserve for the storage entries
|
|
||||||
/// that use unique keys for each slot.
|
|
||||||
///
|
|
||||||
/// The upper bound of this is currently 8.
|
|
||||||
fn slot_count(&self) -> usize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CustomizationImpl {
|
pub struct CustomizationImpl {
|
||||||
|
pub allows_pin_protocol_v1: bool,
|
||||||
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
pub default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
pub default_min_pin_length: u8,
|
pub default_min_pin_length: u8,
|
||||||
pub default_min_pin_length_rp_ids: &'static [&'static str],
|
pub default_min_pin_length_rp_ids: &'static [&'static str],
|
||||||
@@ -275,10 +267,10 @@ pub struct CustomizationImpl {
|
|||||||
pub max_large_blob_array_size: usize,
|
pub max_large_blob_array_size: usize,
|
||||||
pub max_rp_ids_length: usize,
|
pub max_rp_ids_length: usize,
|
||||||
pub max_supported_resident_keys: usize,
|
pub max_supported_resident_keys: usize,
|
||||||
pub slot_count: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
||||||
|
allows_pin_protocol_v1: true,
|
||||||
default_cred_protect: None,
|
default_cred_protect: None,
|
||||||
default_min_pin_length: 4,
|
default_min_pin_length: 4,
|
||||||
default_min_pin_length_rp_ids: &[],
|
default_min_pin_length_rp_ids: &[],
|
||||||
@@ -294,10 +286,13 @@ pub const DEFAULT_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
|||||||
max_large_blob_array_size: 2048,
|
max_large_blob_array_size: 2048,
|
||||||
max_rp_ids_length: 8,
|
max_rp_ids_length: 8,
|
||||||
max_supported_resident_keys: 150,
|
max_supported_resident_keys: 150,
|
||||||
slot_count: 8,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl Customization for CustomizationImpl {
|
impl Customization for CustomizationImpl {
|
||||||
|
fn allows_pin_protocol_v1(&self) -> bool {
|
||||||
|
self.allows_pin_protocol_v1
|
||||||
|
}
|
||||||
|
|
||||||
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy> {
|
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy> {
|
||||||
self.default_cred_protect
|
self.default_cred_protect
|
||||||
}
|
}
|
||||||
@@ -368,10 +363,6 @@ impl Customization for CustomizationImpl {
|
|||||||
fn max_supported_resident_keys(&self) -> usize {
|
fn max_supported_resident_keys(&self) -> usize {
|
||||||
self.max_supported_resident_keys
|
self.max_supported_resident_keys
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slot_count(&self) -> usize {
|
|
||||||
self.slot_count
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@@ -444,11 +435,6 @@ pub fn is_valid(customization: &impl Customization) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slot count should be at most 8.
|
|
||||||
if customization.slot_count() > 8 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
// For compiling with std outside of tests.
|
// For compiling with std outside of tests.
|
||||||
#![cfg_attr(feature = "std", allow(dead_code))]
|
#![cfg_attr(feature = "std", allow(dead_code))]
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::iter::Iterator;
|
use core::iter::Iterator;
|
||||||
use persistent_store::{StorageError, StorageResult};
|
use persistent_store::{StorageError, StorageResult};
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ pub fn is_aligned(block_size: usize, address: usize) -> bool {
|
|||||||
///
|
///
|
||||||
/// The range is treated as the interval `[start, start + length)`.
|
/// The range is treated as the interval `[start, start + length)`.
|
||||||
/// All objects with length of 0, regardless of the start value, are considered empty.
|
/// All objects with length of 0, regardless of the start value, are considered empty.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct ModRange {
|
pub struct ModRange {
|
||||||
start: usize,
|
start: usize,
|
||||||
length: usize,
|
length: usize,
|
||||||
@@ -86,25 +88,45 @@ impl ModRange {
|
|||||||
self.length == 0
|
self.length == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the disjoint union with the other range, if is consecutive.
|
/// Returns the disjoint union with the other range, if consecutive.
|
||||||
///
|
///
|
||||||
/// Appending empty ranges is not possible.
|
/// Appending empty ranges is not possible.
|
||||||
/// Appending to the empty range returns the other range.
|
/// Appending to the empty range returns the other range.
|
||||||
pub fn append(&self, other: ModRange) -> Option<ModRange> {
|
///
|
||||||
|
/// Returns true if successful.
|
||||||
|
pub fn append(&mut self, other: &ModRange) -> bool {
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
return Some(other);
|
self.start = other.start;
|
||||||
|
self.length = other.length;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
if other.is_empty() {
|
if other.is_empty() {
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
if self.start >= other.start {
|
if self.start >= other.start {
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
if self.length != other.start - self.start {
|
if self.length != other.start - self.start {
|
||||||
return None;
|
return false;
|
||||||
}
|
}
|
||||||
let new_length = self.length.checked_add(other.length);
|
if let Some(new_length) = self.length.checked_add(other.length) {
|
||||||
new_length.map(|l| ModRange::new(self.start, l))
|
self.length = new_length;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to check whether a range starts within another.
|
||||||
|
fn starts_inside(&self, range: &ModRange) -> bool {
|
||||||
|
!range.is_empty() && self.start >= range.start && self.start - range.start < range.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the given range has intersects.
|
||||||
|
///
|
||||||
|
/// Mathematically, we calculate whether: `self ∩ range ≠ ∅`.
|
||||||
|
pub fn intersects_range(&self, range: &ModRange) -> bool {
|
||||||
|
self.starts_inside(range) || range.starts_inside(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the given range is fully contained.
|
/// Returns whether the given range is fully contained.
|
||||||
@@ -128,6 +150,73 @@ impl ModRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Partition {
|
||||||
|
ranges: Vec<ModRange>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Partition {
|
||||||
|
pub fn new() -> Partition {
|
||||||
|
Partition { ranges: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Total length of all ranges.
|
||||||
|
pub fn length(&self) -> usize {
|
||||||
|
self.ranges.iter().map(|r| r.length()).sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Appends the given range.
|
||||||
|
///
|
||||||
|
/// Ranges should be appending with ascending start addresses.
|
||||||
|
pub fn append(&mut self, range: ModRange) -> bool {
|
||||||
|
if let Some(last_range) = self.ranges.last_mut() {
|
||||||
|
if range.start() <= last_range.start()
|
||||||
|
|| range.start() - last_range.start() < last_range.length()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !last_range.append(&range) {
|
||||||
|
self.ranges.push(range);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.ranges.push(range);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the start address that corresponds to the given offset.
|
||||||
|
///
|
||||||
|
/// If the offset bigger than the accumulated length or the requested slice doesn't fit a
|
||||||
|
/// connected component, return `None`.
|
||||||
|
pub fn find_address(&self, mut offset: usize, length: usize) -> Option<usize> {
|
||||||
|
for range in &self.ranges {
|
||||||
|
if offset < range.length() {
|
||||||
|
return if range.length() - offset >= length {
|
||||||
|
Some(range.start() + offset)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
offset -= range.length()
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ranges_from(&self, start_address: usize) -> Vec<ModRange> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for range in &self.ranges {
|
||||||
|
match start_address.checked_sub(range.start()) {
|
||||||
|
None | Some(0) => result.push(range.clone()),
|
||||||
|
Some(offset) => {
|
||||||
|
if range.length() > offset {
|
||||||
|
result.push(ModRange::new(start_address, range.length() - offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -186,18 +275,17 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mod_range_append() {
|
fn mod_range_append() {
|
||||||
let range = ModRange::new(200, 100);
|
let mut range = ModRange::new(200, 100);
|
||||||
let new_range = range.append(ModRange::new(300, 400)).unwrap();
|
assert!(range.append(&ModRange::new(300, 400)));
|
||||||
assert!(new_range.start() == 200);
|
assert!(range.start() == 200);
|
||||||
assert!(new_range.length() == 500);
|
assert!(range.length() == 500);
|
||||||
assert!(range.append(ModRange::new(299, 400)).is_none());
|
assert!(!range.append(&ModRange::new(499, 400)));
|
||||||
assert!(range.append(ModRange::new(301, 400)).is_none());
|
assert!(!range.append(&ModRange::new(501, 400)));
|
||||||
assert!(range.append(ModRange::new(200, 400)).is_none());
|
assert!(!range.append(&ModRange::new(300, 400)));
|
||||||
let empty_append = ModRange::new_empty()
|
let mut range = ModRange::new_empty();
|
||||||
.append(ModRange::new(200, 100))
|
assert!(range.append(&ModRange::new(200, 100)));
|
||||||
.unwrap();
|
assert!(range.start() == 200);
|
||||||
assert!(empty_append.start() == 200);
|
assert!(range.length() == 100);
|
||||||
assert!(empty_append.length() == 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -216,6 +304,20 @@ mod tests {
|
|||||||
assert!(ModRange::new(usize::MAX, 2).contains_range(&ModRange::new(usize::MAX, 2)));
|
assert!(ModRange::new(usize::MAX, 2).contains_range(&ModRange::new(usize::MAX, 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mod_range_intersects_range() {
|
||||||
|
let range = ModRange::new(200, 100);
|
||||||
|
assert!(range.intersects_range(&ModRange::new(200, 1)));
|
||||||
|
assert!(range.intersects_range(&ModRange::new(299, 1)));
|
||||||
|
assert!(!range.intersects_range(&ModRange::new(199, 1)));
|
||||||
|
assert!(!range.intersects_range(&ModRange::new(300, 1)));
|
||||||
|
assert!(!ModRange::new_empty().intersects_range(&ModRange::new_empty()));
|
||||||
|
assert!(!ModRange::new_empty().intersects_range(&ModRange::new(200, 100)));
|
||||||
|
assert!(!ModRange::new(200, 100).intersects_range(&ModRange::new_empty()));
|
||||||
|
assert!(ModRange::new(usize::MAX, 1).intersects_range(&ModRange::new(usize::MAX, 1)));
|
||||||
|
assert!(ModRange::new(usize::MAX, 2).intersects_range(&ModRange::new(usize::MAX, 2)));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mod_range_aligned_iter() {
|
fn mod_range_aligned_iter() {
|
||||||
let mut iter = ModRange::new(200, 100).aligned_iter(100);
|
let mut iter = ModRange::new(200, 100).aligned_iter(100);
|
||||||
@@ -234,4 +336,49 @@ mod tests {
|
|||||||
assert_eq!(iter.next(), Some(0xffff_ffff_ffff_fff0));
|
assert_eq!(iter.next(), Some(0xffff_ffff_ffff_fff0));
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partition_append() {
|
||||||
|
let mut partition = Partition::new();
|
||||||
|
partition.append(ModRange::new(0x4000, 0x1000));
|
||||||
|
partition.append(ModRange::new(0x20000, 0x20000));
|
||||||
|
partition.append(ModRange::new(0x40000, 0x20000));
|
||||||
|
assert_eq!(partition.find_address(0, 1), Some(0x4000));
|
||||||
|
assert_eq!(partition.length(), 0x41000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partition_find_address() {
|
||||||
|
let mut partition = Partition::new();
|
||||||
|
partition.append(ModRange::new(0x4000, 0x1000));
|
||||||
|
partition.append(ModRange::new(0x20000, 0x20000));
|
||||||
|
partition.append(ModRange::new(0x40000, 0x20000));
|
||||||
|
assert_eq!(partition.find_address(0, 0x1000), Some(0x4000));
|
||||||
|
assert_eq!(partition.find_address(0x1000, 0x1000), Some(0x20000));
|
||||||
|
assert_eq!(partition.find_address(0x20000, 0x1000), Some(0x3F000));
|
||||||
|
assert_eq!(partition.find_address(0x21000, 0x1000), Some(0x40000));
|
||||||
|
assert_eq!(partition.find_address(0x40000, 0x1000), Some(0x5F000));
|
||||||
|
assert_eq!(partition.find_address(0x41000, 0x1000), None);
|
||||||
|
assert_eq!(partition.find_address(0x40000, 0x2000), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn partition_ranges_from() {
|
||||||
|
let mut partition = Partition::new();
|
||||||
|
partition.append(ModRange::new(0x4000, 0x1000));
|
||||||
|
partition.append(ModRange::new(0x20000, 0x20000));
|
||||||
|
partition.append(ModRange::new(0x40000, 0x20000));
|
||||||
|
let all_ranges = partition.ranges_from(0);
|
||||||
|
let from_start_ranges = partition.ranges_from(0x4000);
|
||||||
|
assert_eq!(&all_ranges, &from_start_ranges);
|
||||||
|
assert_eq!(all_ranges.len(), 2);
|
||||||
|
assert_eq!(all_ranges[0], ModRange::new(0x4000, 0x1000));
|
||||||
|
assert_eq!(all_ranges[1], ModRange::new(0x20000, 0x40000));
|
||||||
|
let second_range = partition.ranges_from(0x20000);
|
||||||
|
let same_second_range = partition.ranges_from(0x1F000);
|
||||||
|
assert_eq!(&second_range, &same_second_range);
|
||||||
|
assert_eq!(&second_range, &all_ranges[1..]);
|
||||||
|
let partial_range = partition.ranges_from(0x30000);
|
||||||
|
assert_eq!(partial_range[0], ModRange::new(0x30000, 0x30000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2021 Google LLC
|
// Copyright 2021-2022 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.
|
||||||
@@ -12,45 +12,28 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
use persistent_store::StorageResult;
|
use persistent_store::StorageResult;
|
||||||
|
|
||||||
pub(crate) mod helper;
|
pub(crate) mod helper;
|
||||||
|
|
||||||
/// Accessors to storage locations used for upgrading from a CTAP command.
|
/// Accessors to storage locations used for upgrading from a CTAP command.
|
||||||
pub trait UpgradeStorage {
|
pub trait UpgradeStorage {
|
||||||
/// Reads a slice of the partition, if within bounds.
|
/// Processes the given data as part of an upgrade.
|
||||||
///
|
///
|
||||||
/// The offset is relative to the start of the partition.
|
/// The offset indicates the data location inside the bundle.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns [`StorageError::OutOfBounds`] if the requested slice is not inside the partition.
|
/// - Returns [`StorageError::OutOfBounds`] if the data does not fit.
|
||||||
fn read_partition(&self, offset: usize, length: usize) -> StorageResult<&[u8]>;
|
/// - Returns [`StorageError::CustomError`] if any Metadata or other check fails.
|
||||||
|
fn write_bundle(&mut self, offset: usize, data: Vec<u8>) -> StorageResult<()>;
|
||||||
|
|
||||||
/// Writes the given data to the given offset address, if within bounds of the partition.
|
/// Returns an identifier for the requested bundle.
|
||||||
///
|
///
|
||||||
/// The offset is relative to the start of the partition.
|
/// Use this to determine whether you are writing to A or B.
|
||||||
///
|
fn bundle_identifier(&self) -> u32;
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns [`StorageError::OutOfBounds`] if the data does not fit the partition.
|
|
||||||
fn write_partition(&mut self, offset: usize, data: &[u8]) -> StorageResult<()>;
|
|
||||||
|
|
||||||
/// Returns the address of the partition.
|
/// Returns the currently running firmware version.
|
||||||
fn partition_address(&self) -> usize;
|
fn running_firmware_version(&self) -> u64;
|
||||||
|
|
||||||
/// Returns the length of the partition.
|
|
||||||
fn partition_length(&self) -> usize;
|
|
||||||
|
|
||||||
/// Reads the metadata location.
|
|
||||||
fn read_metadata(&self) -> StorageResult<&[u8]>;
|
|
||||||
|
|
||||||
/// Writes the given data into the metadata location.
|
|
||||||
///
|
|
||||||
/// The passed in data is appended with 0xFF bytes if shorter than the metadata storage.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns [`StorageError::OutOfBounds`] if the data is too long to fit the metadata storage.
|
|
||||||
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ use crate::api::customization::Customization;
|
|||||||
use crate::ctap::storage;
|
use crate::ctap::storage;
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
use alloc::str;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use alloc::{str, vec};
|
|
||||||
use crypto::hmac::hmac_256;
|
use crypto::hmac::hmac_256;
|
||||||
use crypto::sha256::Sha256;
|
use crypto::sha256::Sha256;
|
||||||
use crypto::Hash256;
|
use crypto::Hash256;
|
||||||
@@ -79,7 +79,6 @@ fn decrypt_pin(
|
|||||||
/// truncated for persistent storage.
|
/// truncated for persistent storage.
|
||||||
fn check_and_store_new_pin(
|
fn check_and_store_new_pin(
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
slot_id: usize,
|
|
||||||
shared_secret: &dyn SharedSecret,
|
shared_secret: &dyn SharedSecret,
|
||||||
new_pin_enc: Vec<u8>,
|
new_pin_enc: Vec<u8>,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
@@ -92,7 +91,7 @@ fn check_and_store_new_pin(
|
|||||||
let mut pin_hash = [0u8; PIN_AUTH_LENGTH];
|
let mut pin_hash = [0u8; PIN_AUTH_LENGTH];
|
||||||
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..PIN_AUTH_LENGTH]);
|
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..PIN_AUTH_LENGTH]);
|
||||||
// The PIN length is always < PIN_PADDED_LENGTH < 256.
|
// The PIN length is always < PIN_PADDED_LENGTH < 256.
|
||||||
storage::set_pin(env, slot_id, &pin_hash, pin_length as u8)?;
|
storage::set_pin(env, &pin_hash, pin_length as u8)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,16 +109,16 @@ pub enum PinPermission {
|
|||||||
pub struct ClientPin {
|
pub struct ClientPin {
|
||||||
pin_protocol_v1: PinProtocol,
|
pin_protocol_v1: PinProtocol,
|
||||||
pin_protocol_v2: PinProtocol,
|
pin_protocol_v2: PinProtocol,
|
||||||
consecutive_pin_mismatches: Vec<u8>,
|
consecutive_pin_mismatches: u8,
|
||||||
pin_uv_auth_token_state: PinUvAuthTokenState,
|
pin_uv_auth_token_state: PinUvAuthTokenState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientPin {
|
impl ClientPin {
|
||||||
pub fn new(env: &mut impl Env) -> ClientPin {
|
pub fn new(rng: &mut impl Rng256) -> ClientPin {
|
||||||
ClientPin {
|
ClientPin {
|
||||||
pin_protocol_v1: PinProtocol::new(env.rng()),
|
pin_protocol_v1: PinProtocol::new(rng),
|
||||||
pin_protocol_v2: PinProtocol::new(env.rng()),
|
pin_protocol_v2: PinProtocol::new(rng),
|
||||||
consecutive_pin_mismatches: vec![0; env.customization().slot_count()],
|
consecutive_pin_mismatches: 0,
|
||||||
pin_uv_auth_token_state: PinUvAuthTokenState::new(),
|
pin_uv_auth_token_state: PinUvAuthTokenState::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,21 +160,16 @@ impl ClientPin {
|
|||||||
fn verify_pin_hash_enc(
|
fn verify_pin_hash_enc(
|
||||||
&mut self,
|
&mut self,
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
slot_id: usize,
|
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
shared_secret: &dyn SharedSecret,
|
shared_secret: &dyn SharedSecret,
|
||||||
pin_hash_enc: Vec<u8>,
|
pin_hash_enc: Vec<u8>,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
// To prevent out of bounds access in code below.
|
match storage::pin_hash(env)? {
|
||||||
if slot_id >= self.consecutive_pin_mismatches.len() {
|
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
match storage::pin_hash(env, slot_id)? {
|
|
||||||
Some(pin_hash) => {
|
Some(pin_hash) => {
|
||||||
if self.consecutive_pin_mismatches[slot_id] >= 3 {
|
if self.consecutive_pin_mismatches >= 3 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||||
}
|
}
|
||||||
storage::decr_pin_retries(env, slot_id)?;
|
storage::decr_pin_retries(env)?;
|
||||||
let pin_hash_dec = shared_secret
|
let pin_hash_dec = shared_secret
|
||||||
.decrypt(&pin_hash_enc)
|
.decrypt(&pin_hash_enc)
|
||||||
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)?;
|
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)?;
|
||||||
@@ -183,11 +177,11 @@ impl ClientPin {
|
|||||||
if !bool::from(pin_hash.ct_eq(&pin_hash_dec)) {
|
if !bool::from(pin_hash.ct_eq(&pin_hash_dec)) {
|
||||||
self.get_mut_pin_protocol(pin_uv_auth_protocol)
|
self.get_mut_pin_protocol(pin_uv_auth_protocol)
|
||||||
.regenerate(env.rng());
|
.regenerate(env.rng());
|
||||||
if storage::pin_retries(env, slot_id)? == 0 {
|
if storage::pin_retries(env)? == 0 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||||
}
|
}
|
||||||
self.consecutive_pin_mismatches[slot_id] += 1;
|
self.consecutive_pin_mismatches += 1;
|
||||||
if self.consecutive_pin_mismatches[slot_id] >= 3 {
|
if self.consecutive_pin_mismatches >= 3 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED);
|
||||||
}
|
}
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
||||||
@@ -196,26 +190,20 @@ impl ClientPin {
|
|||||||
// This status code is not explicitly mentioned in the specification.
|
// This status code is not explicitly mentioned in the specification.
|
||||||
None => return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED),
|
None => return Err(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED),
|
||||||
}
|
}
|
||||||
storage::reset_pin_retries(env, slot_id)?;
|
storage::reset_pin_retries(env)?;
|
||||||
self.consecutive_pin_mismatches[slot_id] = 0;
|
self.consecutive_pin_mismatches = 0;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_get_pin_retries(
|
fn process_get_pin_retries(
|
||||||
&self,
|
&self,
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
_client_pin_params: AuthenticatorClientPinParameters,
|
|
||||||
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
) -> Result<AuthenticatorClientPinResponse, Ctap2StatusCode> {
|
||||||
// TODO: Parse slot_id from params if multi-PIN feature is enabled.
|
|
||||||
let slot_id = 0;
|
|
||||||
if slot_id >= self.consecutive_pin_mismatches.len() {
|
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
Ok(AuthenticatorClientPinResponse {
|
Ok(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
pin_uv_auth_token: None,
|
pin_uv_auth_token: None,
|
||||||
retries: Some(storage::pin_retries(env, slot_id)? as u64),
|
retries: Some(storage::pin_retries(env)? as u64),
|
||||||
power_cycle_state: Some(self.consecutive_pin_mismatches[slot_id] >= 3),
|
power_cycle_state: Some(self.consecutive_pin_mismatches >= 3),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,20 +235,18 @@ impl ClientPin {
|
|||||||
new_pin_enc,
|
new_pin_enc,
|
||||||
..
|
..
|
||||||
} = client_pin_params;
|
} = client_pin_params;
|
||||||
// TODO: Parse slot_id from params if multi-PIN feature is enabled.
|
|
||||||
let slot_id = 0;
|
|
||||||
let key_agreement = ok_or_missing(key_agreement)?;
|
let key_agreement = ok_or_missing(key_agreement)?;
|
||||||
let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?;
|
let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?;
|
||||||
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
||||||
|
|
||||||
if storage::pin_hash(env, slot_id)?.is_some() {
|
if storage::pin_hash(env)?.is_some() {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID);
|
||||||
}
|
}
|
||||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||||
shared_secret.verify(&new_pin_enc, &pin_uv_auth_param)?;
|
shared_secret.verify(&new_pin_enc, &pin_uv_auth_param)?;
|
||||||
|
|
||||||
check_and_store_new_pin(env, slot_id, shared_secret.as_ref(), new_pin_enc)?;
|
check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?;
|
||||||
storage::reset_pin_retries(env, slot_id)?;
|
storage::reset_pin_retries(env)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,14 +263,12 @@ impl ClientPin {
|
|||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
..
|
..
|
||||||
} = client_pin_params;
|
} = client_pin_params;
|
||||||
// TODO: Parse slot_id from params if multi-PIN feature is enabled.
|
|
||||||
let slot_id = 0;
|
|
||||||
let key_agreement = ok_or_missing(key_agreement)?;
|
let key_agreement = ok_or_missing(key_agreement)?;
|
||||||
let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?;
|
let pin_uv_auth_param = ok_or_missing(pin_uv_auth_param)?;
|
||||||
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
let new_pin_enc = ok_or_missing(new_pin_enc)?;
|
||||||
let pin_hash_enc = ok_or_missing(pin_hash_enc)?;
|
let pin_hash_enc = ok_or_missing(pin_hash_enc)?;
|
||||||
|
|
||||||
if storage::pin_retries(env, slot_id)? == 0 {
|
if storage::pin_retries(env)? == 0 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||||
}
|
}
|
||||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||||
@@ -293,13 +277,12 @@ impl ClientPin {
|
|||||||
shared_secret.verify(&auth_param_data, &pin_uv_auth_param)?;
|
shared_secret.verify(&auth_param_data, &pin_uv_auth_param)?;
|
||||||
self.verify_pin_hash_enc(
|
self.verify_pin_hash_enc(
|
||||||
env,
|
env,
|
||||||
slot_id,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
check_and_store_new_pin(env, slot_id, shared_secret.as_ref(), new_pin_enc)?;
|
check_and_store_new_pin(env, shared_secret.as_ref(), new_pin_enc)?;
|
||||||
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -319,33 +302,30 @@ impl ClientPin {
|
|||||||
permissions_rp_id,
|
permissions_rp_id,
|
||||||
..
|
..
|
||||||
} = client_pin_params;
|
} = client_pin_params;
|
||||||
// TODO: Parse slot_id from params if multi-PIN feature is enabled.
|
|
||||||
let slot_id = 0;
|
|
||||||
let key_agreement = ok_or_missing(key_agreement)?;
|
let key_agreement = ok_or_missing(key_agreement)?;
|
||||||
let pin_hash_enc = ok_or_missing(pin_hash_enc)?;
|
let pin_hash_enc = ok_or_missing(pin_hash_enc)?;
|
||||||
if permissions.is_some() || permissions_rp_id.is_some() {
|
if permissions.is_some() || permissions_rp_id.is_some() {
|
||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if storage::pin_retries(env, slot_id)? == 0 {
|
if storage::pin_retries(env)? == 0 {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_BLOCKED);
|
||||||
}
|
}
|
||||||
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
let shared_secret = self.get_shared_secret(pin_uv_auth_protocol, key_agreement)?;
|
||||||
self.verify_pin_hash_enc(
|
self.verify_pin_hash_enc(
|
||||||
env,
|
env,
|
||||||
slot_id,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc,
|
pin_hash_enc,
|
||||||
)?;
|
)?;
|
||||||
if storage::has_force_pin_change(env, slot_id)? {
|
if storage::has_force_pin_change(env)? {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
self.pin_protocol_v1.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
self.pin_protocol_v2.reset_pin_uv_auth_token(env.rng());
|
||||||
self.pin_uv_auth_token_state
|
self.pin_uv_auth_token_state
|
||||||
.begin_using_pin_uv_auth_token(now, slot_id);
|
.begin_using_pin_uv_auth_token(now);
|
||||||
self.pin_uv_auth_token_state.set_default_permissions();
|
self.pin_uv_auth_token_state.set_default_permissions();
|
||||||
let pin_uv_auth_token = shared_secret.encrypt(
|
let pin_uv_auth_token = shared_secret.encrypt(
|
||||||
env.rng(),
|
env.rng(),
|
||||||
@@ -411,10 +391,13 @@ impl ClientPin {
|
|||||||
client_pin_params: AuthenticatorClientPinParameters,
|
client_pin_params: AuthenticatorClientPinParameters,
|
||||||
now: CtapInstant,
|
now: CtapInstant,
|
||||||
) -> Result<ResponseData, Ctap2StatusCode> {
|
) -> Result<ResponseData, Ctap2StatusCode> {
|
||||||
|
if !env.customization().allows_pin_protocol_v1()
|
||||||
|
&& client_pin_params.pin_uv_auth_protocol == PinUvAuthProtocol::V1
|
||||||
|
{
|
||||||
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
|
}
|
||||||
let response = match client_pin_params.sub_command {
|
let response = match client_pin_params.sub_command {
|
||||||
ClientPinSubCommand::GetPinRetries => {
|
ClientPinSubCommand::GetPinRetries => Some(self.process_get_pin_retries(env)?),
|
||||||
Some(self.process_get_pin_retries(env, client_pin_params)?)
|
|
||||||
}
|
|
||||||
ClientPinSubCommand::GetKeyAgreement => {
|
ClientPinSubCommand::GetKeyAgreement => {
|
||||||
Some(self.process_get_key_agreement(client_pin_params)?)
|
Some(self.process_get_key_agreement(client_pin_params)?)
|
||||||
}
|
}
|
||||||
@@ -469,9 +452,7 @@ impl ClientPin {
|
|||||||
self.pin_protocol_v1.reset_pin_uv_auth_token(rng);
|
self.pin_protocol_v1.reset_pin_uv_auth_token(rng);
|
||||||
self.pin_protocol_v2.regenerate(rng);
|
self.pin_protocol_v2.regenerate(rng);
|
||||||
self.pin_protocol_v2.reset_pin_uv_auth_token(rng);
|
self.pin_protocol_v2.reset_pin_uv_auth_token(rng);
|
||||||
for v in &mut self.consecutive_pin_mismatches {
|
self.consecutive_pin_mismatches = 0;
|
||||||
*v = 0;
|
|
||||||
}
|
|
||||||
self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token();
|
self.pin_uv_auth_token_state.stop_using_pin_uv_auth_token();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,38 +561,24 @@ impl ClientPin {
|
|||||||
self.pin_uv_auth_token_state.has_permissions_rp_id(rp_id)
|
self.pin_uv_auth_token_state.has_permissions_rp_id(rp_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the slot_id_in_use of the current pin_uv_auth_token_state if multi-PIN
|
|
||||||
/// feature is enabled. Otherwise return the default slot (0).
|
|
||||||
pub fn get_slot_id_in_use_or_default(
|
|
||||||
&self,
|
|
||||||
env: &mut impl Env,
|
|
||||||
) -> Result<Option<usize>, Ctap2StatusCode> {
|
|
||||||
if storage::has_multi_pin(env)? {
|
|
||||||
Ok(self.pin_uv_auth_token_state.slot_id_in_use())
|
|
||||||
} else {
|
|
||||||
Ok(Some(0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn new_test(
|
pub fn new_test(
|
||||||
env: &mut impl Env,
|
|
||||||
slot_id: usize,
|
|
||||||
key_agreement_key: crypto::ecdh::SecKey,
|
key_agreement_key: crypto::ecdh::SecKey,
|
||||||
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
pin_uv_auth_token: [u8; PIN_TOKEN_LENGTH],
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
) -> ClientPin {
|
) -> ClientPin {
|
||||||
|
let mut env = crate::env::test::TestEnv::new();
|
||||||
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
let (key_agreement_key_v1, key_agreement_key_v2) = match pin_uv_auth_protocol {
|
||||||
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
|
PinUvAuthProtocol::V1 => (key_agreement_key, crypto::ecdh::SecKey::gensk(env.rng())),
|
||||||
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
|
PinUvAuthProtocol::V2 => (crypto::ecdh::SecKey::gensk(env.rng()), key_agreement_key),
|
||||||
};
|
};
|
||||||
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
let mut pin_uv_auth_token_state = PinUvAuthTokenState::new();
|
||||||
pin_uv_auth_token_state.set_permissions(0xFF);
|
pin_uv_auth_token_state.set_permissions(0xFF);
|
||||||
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0), slot_id);
|
pin_uv_auth_token_state.begin_using_pin_uv_auth_token(CtapInstant::new(0));
|
||||||
ClientPin {
|
ClientPin {
|
||||||
pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token),
|
pin_protocol_v1: PinProtocol::new_test(key_agreement_key_v1, pin_uv_auth_token),
|
||||||
pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token),
|
pin_protocol_v2: PinProtocol::new_test(key_agreement_key_v2, pin_uv_auth_token),
|
||||||
consecutive_pin_mismatches: vec![0; env.customization().slot_count()],
|
consecutive_pin_mismatches: 0,
|
||||||
pin_uv_auth_token_state,
|
pin_uv_auth_token_state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -631,7 +598,7 @@ mod test {
|
|||||||
pin[..4].copy_from_slice(b"1234");
|
pin[..4].copy_from_slice(b"1234");
|
||||||
let mut pin_hash = [0u8; 16];
|
let mut pin_hash = [0u8; 16];
|
||||||
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..16]);
|
pin_hash.copy_from_slice(&Sha256::hash(&pin[..])[..16]);
|
||||||
storage::set_pin(env, 0, &pin_hash, 4).unwrap();
|
storage::set_pin(env, &pin_hash, 4).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fails on PINs bigger than 64 bytes.
|
/// Fails on PINs bigger than 64 bytes.
|
||||||
@@ -650,7 +617,6 @@ mod test {
|
|||||||
/// tests using the wrong combination of PIN protocol and shared secret
|
/// tests using the wrong combination of PIN protocol and shared secret
|
||||||
/// should fail.
|
/// should fail.
|
||||||
fn create_client_pin_and_shared_secret(
|
fn create_client_pin_and_shared_secret(
|
||||||
slot_id: usize,
|
|
||||||
pin_uv_auth_protocol: PinUvAuthProtocol,
|
pin_uv_auth_protocol: PinUvAuthProtocol,
|
||||||
) -> (ClientPin, Box<dyn SharedSecret>) {
|
) -> (ClientPin, Box<dyn SharedSecret>) {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
@@ -658,13 +624,8 @@ mod test {
|
|||||||
let pk = key_agreement_key.genpk();
|
let pk = key_agreement_key.genpk();
|
||||||
let key_agreement = CoseKey::from(pk);
|
let key_agreement = CoseKey::from(pk);
|
||||||
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
let pin_uv_auth_token = [0x91; PIN_TOKEN_LENGTH];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||||
slot_id,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
pin_uv_auth_protocol,
|
|
||||||
);
|
|
||||||
let shared_secret = client_pin
|
let shared_secret = client_pin
|
||||||
.get_pin_protocol(pin_uv_auth_protocol)
|
.get_pin_protocol(pin_uv_auth_protocol)
|
||||||
.decapsulate(key_agreement, pin_uv_auth_protocol)
|
.decapsulate(key_agreement, pin_uv_auth_protocol)
|
||||||
@@ -680,9 +641,7 @@ mod test {
|
|||||||
sub_command: ClientPinSubCommand,
|
sub_command: ClientPinSubCommand,
|
||||||
) -> (ClientPin, AuthenticatorClientPinParameters) {
|
) -> (ClientPin, AuthenticatorClientPinParameters) {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
// TODO: Make slot_id a passed parameter once we include it in AuthenticatorClientPinParameters.
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
let (client_pin, shared_secret) =
|
|
||||||
create_client_pin_and_shared_secret(0, pin_uv_auth_protocol);
|
|
||||||
|
|
||||||
let pin = b"1234";
|
let pin = b"1234";
|
||||||
let mut padded_pin = [0u8; 64];
|
let mut padded_pin = [0u8; 64];
|
||||||
@@ -724,7 +683,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_mix_pin_protocols() {
|
fn test_mix_pin_protocols() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let client_pin = ClientPin::new(&mut env);
|
let client_pin = ClientPin::new(env.rng());
|
||||||
let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1);
|
let pin_protocol_v1 = client_pin.get_pin_protocol(PinUvAuthProtocol::V1);
|
||||||
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
let pin_protocol_v2 = client_pin.get_pin_protocol(PinUvAuthProtocol::V2);
|
||||||
let message = vec![0xAA; 16];
|
let message = vec![0xAA; 16];
|
||||||
@@ -765,7 +724,7 @@ mod test {
|
|||||||
|
|
||||||
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_verify_pin_hash_enc(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol);
|
let pin_protocol = client_pin.get_pin_protocol(pin_uv_auth_protocol);
|
||||||
let shared_secret = pin_protocol
|
let shared_secret = pin_protocol
|
||||||
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
.decapsulate(pin_protocol.get_public_key(), pin_uv_auth_protocol)
|
||||||
@@ -775,7 +734,7 @@ mod test {
|
|||||||
0x01, 0xD9, 0x88, 0x40, 0x50, 0xBB, 0xD0, 0x7A, 0x23, 0x1A, 0xEB, 0x69, 0xD8, 0x36,
|
0x01, 0xD9, 0x88, 0x40, 0x50, 0xBB, 0xD0, 0x7A, 0x23, 0x1A, 0xEB, 0x69, 0xD8, 0x36,
|
||||||
0xC4, 0x12,
|
0xC4, 0x12,
|
||||||
];
|
];
|
||||||
storage::set_pin(&mut env, 0, &pin_hash, 4).unwrap();
|
storage::set_pin(&mut env, &pin_hash, 4).unwrap();
|
||||||
|
|
||||||
let pin_hash_enc = shared_secret
|
let pin_hash_enc = shared_secret
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@@ -784,7 +743,6 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut env,
|
&mut env,
|
||||||
0,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -796,7 +754,6 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut env,
|
&mut env,
|
||||||
0,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -808,24 +765,22 @@ mod test {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.encrypt(env.rng(), &pin_hash)
|
.encrypt(env.rng(), &pin_hash)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
client_pin.consecutive_pin_mismatches[0] = 3;
|
client_pin.consecutive_pin_mismatches = 3;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut env,
|
&mut env,
|
||||||
0,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
),
|
),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_BLOCKED)
|
||||||
);
|
);
|
||||||
client_pin.consecutive_pin_mismatches[0] = 0;
|
client_pin.consecutive_pin_mismatches = 0;
|
||||||
|
|
||||||
let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH - 1];
|
let pin_hash_enc = vec![0x77; PIN_AUTH_LENGTH - 1];
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut env,
|
&mut env,
|
||||||
0,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -837,7 +792,6 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_hash_enc(
|
client_pin.verify_pin_hash_enc(
|
||||||
&mut env,
|
&mut env,
|
||||||
0,
|
|
||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
shared_secret.as_ref(),
|
shared_secret.as_ref(),
|
||||||
pin_hash_enc
|
pin_hash_enc
|
||||||
@@ -865,7 +819,7 @@ mod test {
|
|||||||
let expected_response = Some(AuthenticatorClientPinResponse {
|
let expected_response = Some(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
pin_uv_auth_token: None,
|
pin_uv_auth_token: None,
|
||||||
retries: Some(storage::pin_retries(&mut env, 0).unwrap() as u64),
|
retries: Some(storage::pin_retries(&mut env).unwrap() as u64),
|
||||||
power_cycle_state: Some(false),
|
power_cycle_state: Some(false),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -873,11 +827,11 @@ mod test {
|
|||||||
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
Ok(ResponseData::AuthenticatorClientPin(expected_response))
|
||||||
);
|
);
|
||||||
|
|
||||||
client_pin.consecutive_pin_mismatches[0] = 3;
|
client_pin.consecutive_pin_mismatches = 3;
|
||||||
let expected_response = Some(AuthenticatorClientPinResponse {
|
let expected_response = Some(AuthenticatorClientPinResponse {
|
||||||
key_agreement: None,
|
key_agreement: None,
|
||||||
pin_uv_auth_token: None,
|
pin_uv_auth_token: None,
|
||||||
retries: Some(storage::pin_retries(&mut env, 0).unwrap() as u64),
|
retries: Some(storage::pin_retries(&mut env).unwrap() as u64),
|
||||||
power_cycle_state: Some(true),
|
power_cycle_state: Some(true),
|
||||||
});
|
});
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -924,6 +878,20 @@ mod test {
|
|||||||
test_helper_process_get_key_agreement(PinUvAuthProtocol::V2);
|
test_helper_process_get_key_agreement(PinUvAuthProtocol::V2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_process_get_key_agreement_v1_not_allowed() {
|
||||||
|
let (mut client_pin, params) = create_client_pin_and_parameters(
|
||||||
|
PinUvAuthProtocol::V1,
|
||||||
|
ClientPinSubCommand::GetKeyAgreement,
|
||||||
|
);
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
env.customization_mut().set_allows_pin_protocol_v1(false);
|
||||||
|
assert_eq!(
|
||||||
|
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||||
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn test_helper_process_set_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_process_set_pin(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let (mut client_pin, params) =
|
let (mut client_pin, params) =
|
||||||
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
|
create_client_pin_and_parameters(pin_uv_auth_protocol, ClientPinSubCommand::SetPin);
|
||||||
@@ -973,8 +941,8 @@ mod test {
|
|||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_AUTH_INVALID)
|
||||||
);
|
);
|
||||||
|
|
||||||
while storage::pin_retries(&mut env, 0).unwrap() > 0 {
|
while storage::pin_retries(&mut env).unwrap() > 0 {
|
||||||
storage::decr_pin_retries(&mut env, 0).unwrap();
|
storage::decr_pin_retries(&mut env).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||||
@@ -1067,7 +1035,7 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
set_standard_pin(&mut env);
|
set_standard_pin(&mut env);
|
||||||
|
|
||||||
assert_eq!(storage::force_pin_change(&mut env, 0), Ok(()));
|
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID),
|
||||||
@@ -1177,7 +1145,7 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
set_standard_pin(&mut env);
|
set_standard_pin(&mut env);
|
||||||
|
|
||||||
assert_eq!(storage::force_pin_change(&mut env, 0), Ok(()));
|
assert_eq!(storage::force_pin_change(&mut env), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
client_pin.process_command(&mut env, params, CtapInstant::new(0)),
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
Err(Ctap2StatusCode::CTAP2_ERR_PIN_INVALID)
|
||||||
@@ -1269,17 +1237,17 @@ mod test {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
for (pin, result) in test_cases {
|
for (pin, result) in test_cases {
|
||||||
let old_pin_hash = storage::pin_hash(&mut env, 0).unwrap();
|
let old_pin_hash = storage::pin_hash(&mut env).unwrap();
|
||||||
let new_pin_enc = encrypt_pin(shared_secret.as_ref(), pin);
|
let new_pin_enc = encrypt_pin(shared_secret.as_ref(), pin);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
check_and_store_new_pin(&mut env, 0, shared_secret.as_ref(), new_pin_enc),
|
check_and_store_new_pin(&mut env, shared_secret.as_ref(), new_pin_enc),
|
||||||
result
|
result
|
||||||
);
|
);
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
assert_ne!(old_pin_hash, storage::pin_hash(&mut env, 0).unwrap());
|
assert_ne!(old_pin_hash, storage::pin_hash(&mut env).unwrap());
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(old_pin_hash, storage::pin_hash(&mut env, 0).unwrap());
|
assert_eq!(old_pin_hash, storage::pin_hash(&mut env).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1301,8 +1269,7 @@ mod test {
|
|||||||
salt: Vec<u8>,
|
salt: Vec<u8>,
|
||||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let (client_pin, shared_secret) =
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
create_client_pin_and_shared_secret(0, pin_uv_auth_protocol);
|
|
||||||
|
|
||||||
let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap();
|
let salt_enc = shared_secret.as_ref().encrypt(env.rng(), &salt).unwrap();
|
||||||
let salt_auth = shared_secret.authenticate(&salt_enc);
|
let salt_auth = shared_secret.authenticate(&salt_enc);
|
||||||
@@ -1320,8 +1287,7 @@ mod test {
|
|||||||
|
|
||||||
fn test_helper_process_hmac_secret_bad_salt_auth(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
fn test_helper_process_hmac_secret_bad_salt_auth(pin_uv_auth_protocol: PinUvAuthProtocol) {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let (client_pin, shared_secret) =
|
let (client_pin, shared_secret) = create_client_pin_and_shared_secret(pin_uv_auth_protocol);
|
||||||
create_client_pin_and_shared_secret(0, pin_uv_auth_protocol);
|
|
||||||
let cred_random = [0xC9; 32];
|
let cred_random = [0xC9; 32];
|
||||||
|
|
||||||
let salt_enc = vec![0x01; 32];
|
let salt_enc = vec![0x01; 32];
|
||||||
@@ -1437,7 +1403,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_permission() {
|
fn test_has_permission() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
client_pin.pin_uv_auth_token_state.set_permissions(0x7F);
|
client_pin.pin_uv_auth_token_state.set_permissions(0x7F);
|
||||||
for permission in PinPermission::into_enum_iter() {
|
for permission in PinPermission::into_enum_iter() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -1461,7 +1427,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_rp_id_permission() {
|
fn test_has_no_rp_id_permission() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
assert_eq!(client_pin.has_no_rp_id_permission(), Ok(()));
|
assert_eq!(client_pin.has_no_rp_id_permission(), Ok(()));
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1475,7 +1441,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_or_rp_id_permission() {
|
fn test_has_no_or_rp_id_permission() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(()));
|
assert_eq!(client_pin.has_no_or_rp_id_permission("example.com"), Ok(()));
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
@@ -1490,7 +1456,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_has_no_or_rp_id_hash_permission() {
|
fn test_has_no_or_rp_id_hash_permission() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let rp_id_hash = Sha256::hash(b"example.com");
|
let rp_id_hash = Sha256::hash(b"example.com");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash),
|
||||||
@@ -1512,7 +1478,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ensure_rp_id_permission() {
|
fn test_ensure_rp_id_permission() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(()));
|
assert_eq!(client_pin.ensure_rp_id_permission("example.com"), Ok(()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin
|
client_pin
|
||||||
@@ -1530,11 +1496,11 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_verify_pin_uv_auth_token() {
|
fn test_verify_pin_uv_auth_token() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let message = [0xAA];
|
let message = [0xAA];
|
||||||
client_pin
|
client_pin
|
||||||
.pin_uv_auth_token_state
|
.pin_uv_auth_token_state
|
||||||
.begin_using_pin_uv_auth_token(CtapInstant::new(0), 0);
|
.begin_using_pin_uv_auth_token(CtapInstant::new(0));
|
||||||
|
|
||||||
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)
|
||||||
@@ -1551,7 +1517,6 @@ mod test {
|
|||||||
let pin_uv_auth_param_v2_from_v1_token =
|
let pin_uv_auth_param_v2_from_v1_token =
|
||||||
authenticate_pin_uv_auth_token(pin_uv_auth_token_v1, &message, PinUvAuthProtocol::V2);
|
authenticate_pin_uv_auth_token(pin_uv_auth_token_v1, &message, PinUvAuthProtocol::V2);
|
||||||
|
|
||||||
assert_eq!(client_pin.pin_uv_auth_token_state.slot_id_in_use(), Some(0));
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
client_pin.verify_pin_uv_auth_token(
|
client_pin.verify_pin_uv_auth_token(
|
||||||
&message,
|
&message,
|
||||||
@@ -1605,7 +1570,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_verify_pin_uv_auth_token_not_in_use() {
|
fn test_verify_pin_uv_auth_token_not_in_use() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let client_pin = ClientPin::new(&mut env);
|
let client_pin = ClientPin::new(env.rng());
|
||||||
let message = [0xAA];
|
let message = [0xAA];
|
||||||
|
|
||||||
let pin_uv_auth_token_v1 = client_pin
|
let pin_uv_auth_token_v1 = client_pin
|
||||||
@@ -1627,7 +1592,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_reset() {
|
fn test_reset() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut client_pin = ClientPin::new(&mut env);
|
let mut client_pin = ClientPin::new(env.rng());
|
||||||
let public_key_v1 = client_pin.pin_protocol_v1.get_public_key();
|
let public_key_v1 = client_pin.pin_protocol_v1.get_public_key();
|
||||||
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
let public_key_v2 = client_pin.pin_protocol_v2.get_public_key();
|
||||||
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
let token_v1 = *client_pin.pin_protocol_v1.get_pin_uv_auth_token();
|
||||||
|
|||||||
@@ -15,11 +15,10 @@
|
|||||||
use super::data_formats::{
|
use super::data_formats::{
|
||||||
extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string,
|
extract_array, extract_bool, extract_byte_string, extract_map, extract_text_string,
|
||||||
extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams,
|
extract_unsigned, ok_or_missing, ClientPinSubCommand, ConfigSubCommand, ConfigSubCommandParams,
|
||||||
CoseKey, CoseSignature, CredentialManagementSubCommand,
|
CoseKey, CredentialManagementSubCommand, CredentialManagementSubCommandParameters,
|
||||||
CredentialManagementSubCommandParameters, GetAssertionExtensions, GetAssertionOptions,
|
GetAssertionExtensions, GetAssertionOptions, MakeCredentialExtensions, MakeCredentialOptions,
|
||||||
MakeCredentialExtensions, MakeCredentialOptions, PinUvAuthProtocol,
|
PinUvAuthProtocol, PublicKeyCredentialDescriptor, PublicKeyCredentialParameter,
|
||||||
PublicKeyCredentialDescriptor, PublicKeyCredentialParameter, PublicKeyCredentialRpEntity,
|
PublicKeyCredentialRpEntity, PublicKeyCredentialUserEntity, SetMinPinLengthParams,
|
||||||
PublicKeyCredentialUserEntity, SetMinPinLengthParams,
|
|
||||||
};
|
};
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::{cbor_read, key_material};
|
use super::{cbor_read, key_material};
|
||||||
@@ -593,10 +592,9 @@ impl TryFrom<cbor::Value> for AuthenticatorVendorConfigureParameters {
|
|||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct AuthenticatorVendorUpgradeParameters {
|
pub struct AuthenticatorVendorUpgradeParameters {
|
||||||
pub address: Option<usize>,
|
pub offset: usize,
|
||||||
pub data: Vec<u8>,
|
pub data: Vec<u8>,
|
||||||
pub hash: Vec<u8>,
|
pub hash: [u8; 32],
|
||||||
pub signature: Option<CoseSignature>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
||||||
@@ -605,25 +603,16 @@ impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
|||||||
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
||||||
destructure_cbor_map! {
|
destructure_cbor_map! {
|
||||||
let {
|
let {
|
||||||
0x01 => address,
|
0x01 => offset,
|
||||||
0x02 => data,
|
0x02 => data,
|
||||||
0x03 => hash,
|
0x03 => hash,
|
||||||
0x04 => signature,
|
|
||||||
} = extract_map(cbor_value)?;
|
} = extract_map(cbor_value)?;
|
||||||
}
|
}
|
||||||
let address = address
|
let offset = extract_unsigned(ok_or_missing(offset)?)? as usize;
|
||||||
.map(extract_unsigned)
|
|
||||||
.transpose()?
|
|
||||||
.map(|u| u as usize);
|
|
||||||
let data = extract_byte_string(ok_or_missing(data)?)?;
|
let data = extract_byte_string(ok_or_missing(data)?)?;
|
||||||
let hash = extract_byte_string(ok_or_missing(hash)?)?;
|
let hash = <[u8; 32]>::try_from(extract_byte_string(ok_or_missing(hash)?)?)
|
||||||
let signature = signature.map(CoseSignature::try_from).transpose()?;
|
.map_err(|_| Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)?;
|
||||||
Ok(AuthenticatorVendorUpgradeParameters {
|
Ok(AuthenticatorVendorUpgradeParameters { offset, data, hash })
|
||||||
address,
|
|
||||||
data,
|
|
||||||
hash,
|
|
||||||
signature,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,7 +620,7 @@ impl TryFrom<cbor::Value> for AuthenticatorVendorUpgradeParameters {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::super::data_formats::{
|
use super::super::data_formats::{
|
||||||
AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
|
AuthenticatorTransport, PublicKeyCredentialRpEntity, PublicKeyCredentialType,
|
||||||
PublicKeyCredentialUserEntity, SignatureAlgorithm,
|
PublicKeyCredentialUserEntity,
|
||||||
};
|
};
|
||||||
use super::super::ES256_CRED_PARAM;
|
use super::super::ES256_CRED_PARAM;
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -1072,6 +1061,16 @@ mod test {
|
|||||||
let command = Command::deserialize(&cbor_bytes);
|
let command = Command::deserialize(&cbor_bytes);
|
||||||
assert_eq!(command, Err(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR));
|
assert_eq!(command, Err(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR));
|
||||||
|
|
||||||
|
// Missing offset
|
||||||
|
let cbor_value = cbor_map! {
|
||||||
|
0x02 => [0xFF; 0x100],
|
||||||
|
0x03 => [0x44; 32],
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
AuthenticatorVendorUpgradeParameters::try_from(cbor_value),
|
||||||
|
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
||||||
|
);
|
||||||
|
|
||||||
// Missing data
|
// Missing data
|
||||||
let cbor_value = cbor_map! {
|
let cbor_value = cbor_map! {
|
||||||
0x01 => 0x1000,
|
0x01 => 0x1000,
|
||||||
@@ -1082,6 +1081,17 @@ mod test {
|
|||||||
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Invalid hash size
|
||||||
|
let cbor_value = cbor_map! {
|
||||||
|
0x01 => 0x1000,
|
||||||
|
0x02 => [0xFF; 0x100],
|
||||||
|
0x03 => [0x44; 33],
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
AuthenticatorVendorUpgradeParameters::try_from(cbor_value),
|
||||||
|
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
|
);
|
||||||
|
|
||||||
// Missing hash
|
// Missing hash
|
||||||
let cbor_value = cbor_map! {
|
let cbor_value = cbor_map! {
|
||||||
0x01 => 0x1000,
|
0x01 => 0x1000,
|
||||||
@@ -1092,29 +1102,7 @@ mod test {
|
|||||||
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
Err(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Valid without address
|
// Valid
|
||||||
let cbor_value = cbor_map! {
|
|
||||||
0x02 => [0xFF; 0x100],
|
|
||||||
0x03 => [0x44; 32],
|
|
||||||
0x04 => cbor_map! {
|
|
||||||
"alg" => -7,
|
|
||||||
"signature" => [0x55; 64],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
AuthenticatorVendorUpgradeParameters::try_from(cbor_value),
|
|
||||||
Ok(AuthenticatorVendorUpgradeParameters {
|
|
||||||
address: None,
|
|
||||||
data: vec![0xFF; 0x100],
|
|
||||||
hash: vec![0x44; 32],
|
|
||||||
signature: Some(CoseSignature {
|
|
||||||
algorithm: SignatureAlgorithm::Es256,
|
|
||||||
bytes: [0x55; 64],
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Valid without signature
|
|
||||||
let cbor_value = cbor_map! {
|
let cbor_value = cbor_map! {
|
||||||
0x01 => 0x1000,
|
0x01 => 0x1000,
|
||||||
0x02 => [0xFF; 0x100],
|
0x02 => [0xFF; 0x100],
|
||||||
@@ -1123,10 +1111,9 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
AuthenticatorVendorUpgradeParameters::try_from(cbor_value),
|
AuthenticatorVendorUpgradeParameters::try_from(cbor_value),
|
||||||
Ok(AuthenticatorVendorUpgradeParameters {
|
Ok(AuthenticatorVendorUpgradeParameters {
|
||||||
address: Some(0x1000),
|
offset: 0x1000,
|
||||||
data: vec![0xFF; 0x100],
|
data: vec![0xFF; 0x100],
|
||||||
hash: vec![0x44; 32],
|
hash: [0x44; 32],
|
||||||
signature: None,
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,21 +55,15 @@ fn process_set_min_pin_length(
|
|||||||
if new_min_pin_length < store_min_pin_length {
|
if new_min_pin_length < store_min_pin_length {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_POLICY_VIOLATION);
|
||||||
}
|
}
|
||||||
let force_change_pin = force_change_pin.unwrap_or(false);
|
let mut force_change_pin = force_change_pin.unwrap_or(false);
|
||||||
if force_change_pin && !storage::has_pin(env)? {
|
if force_change_pin && storage::pin_hash(env)?.is_none() {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET);
|
return Err(Ctap2StatusCode::CTAP2_ERR_PIN_NOT_SET);
|
||||||
}
|
}
|
||||||
for slot_id in 0..env.customization().slot_count() {
|
if let Some(old_length) = storage::pin_code_point_length(env)? {
|
||||||
if storage::pin_hash(env, slot_id)?.is_none() {
|
force_change_pin |= new_min_pin_length > old_length;
|
||||||
continue;
|
}
|
||||||
}
|
if force_change_pin {
|
||||||
let mut force_change_pin = force_change_pin;
|
storage::force_pin_change(env)?;
|
||||||
if let Some(old_length) = storage::pin_code_point_length(env, slot_id)? {
|
|
||||||
force_change_pin |= new_min_pin_length > old_length;
|
|
||||||
}
|
|
||||||
if force_change_pin {
|
|
||||||
storage::force_pin_change(env, slot_id)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
storage::set_min_pin_length(env, new_min_pin_length)?;
|
storage::set_min_pin_length(env, new_min_pin_length)?;
|
||||||
if let Some(min_pin_length_rp_ids) = min_pin_length_rp_ids {
|
if let Some(min_pin_length_rp_ids) = min_pin_length_rp_ids {
|
||||||
@@ -91,13 +85,9 @@ pub fn process_config(
|
|||||||
pin_uv_auth_protocol,
|
pin_uv_auth_protocol,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
let slot_id = client_pin.get_slot_id_in_use_or_default(env)?;
|
|
||||||
let enforce_uv =
|
let enforce_uv =
|
||||||
!matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) && storage::has_always_uv(env)?;
|
!matches!(sub_command, ConfigSubCommand::ToggleAlwaysUv) && storage::has_always_uv(env)?;
|
||||||
// If multi-PIN feature is enabled, no PIN is in use, and the command is to turn off alwaysUv,
|
if storage::pin_hash(env)?.is_some() || enforce_uv {
|
||||||
// the PIN check will be skipped here but an OPERATION_DENIED will still be returned later,
|
|
||||||
// which is correct behavior.
|
|
||||||
if (slot_id.is_some() && storage::pin_hash(env, slot_id.unwrap())?.is_some()) || enforce_uv {
|
|
||||||
let pin_uv_auth_param =
|
let pin_uv_auth_param =
|
||||||
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
||||||
let pin_uv_auth_protocol =
|
let pin_uv_auth_protocol =
|
||||||
@@ -143,13 +133,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
sub_command: ConfigSubCommand::EnableEnterpriseAttestation,
|
||||||
@@ -175,13 +160,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
sub_command: ConfigSubCommand::ToggleAlwaysUv,
|
||||||
@@ -215,14 +195,9 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||||
0,
|
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
pin_uv_auth_protocol,
|
|
||||||
);
|
|
||||||
storage::set_pin(&mut env, 0, &[0x88; 16], 4).unwrap();
|
|
||||||
|
|
||||||
let mut config_data = vec![0xFF; 32];
|
let mut config_data = vec![0xFF; 32];
|
||||||
config_data.extend(&[0x0D, ConfigSubCommand::ToggleAlwaysUv as u8]);
|
config_data.extend(&[0x0D, ConfigSubCommand::ToggleAlwaysUv as u8]);
|
||||||
@@ -290,13 +265,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
// First, increase minimum PIN length from 4 to 6 without PIN auth.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
@@ -307,7 +277,7 @@ mod test {
|
|||||||
|
|
||||||
// Second, increase minimum PIN length from 6 to 8 with PIN auth.
|
// Second, increase minimum PIN length from 6 to 8 with PIN auth.
|
||||||
// The stored PIN or its length don't matter since we control the token.
|
// The stored PIN or its length don't matter since we control the token.
|
||||||
storage::set_pin(&mut env, 0, &[0x88; 16], 8).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 8).unwrap();
|
||||||
let min_pin_length = 8;
|
let min_pin_length = 8;
|
||||||
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
||||||
let pin_uv_auth_param = vec![
|
let pin_uv_auth_param = vec![
|
||||||
@@ -339,13 +309,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// First, set RP IDs without PIN auth.
|
// First, set RP IDs without PIN auth.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
@@ -364,7 +329,7 @@ mod test {
|
|||||||
let min_pin_length = 8;
|
let min_pin_length = 8;
|
||||||
let min_pin_length_rp_ids = vec!["another.example.com".to_string()];
|
let min_pin_length_rp_ids = vec!["another.example.com".to_string()];
|
||||||
// The stored PIN or its length don't matter since we control the token.
|
// The stored PIN or its length don't matter since we control the token.
|
||||||
storage::set_pin(&mut env, 0, &[0x88; 16], 8).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 8).unwrap();
|
||||||
let mut config_params =
|
let mut config_params =
|
||||||
create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone()));
|
create_min_pin_config_params(min_pin_length, Some(min_pin_length_rp_ids.clone()));
|
||||||
let pin_uv_auth_param = vec![
|
let pin_uv_auth_param = vec![
|
||||||
@@ -420,15 +385,10 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0x88; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||||
// Increase min PIN, force PIN change.
|
// Increase min PIN, force PIN change.
|
||||||
let min_pin_length = 6;
|
let min_pin_length = 6;
|
||||||
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
let mut config_params = create_min_pin_config_params(min_pin_length, None);
|
||||||
@@ -440,7 +400,7 @@ mod test {
|
|||||||
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
||||||
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
||||||
assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length));
|
assert_eq!(storage::min_pin_length(&mut env), Ok(min_pin_length));
|
||||||
assert_eq!(storage::has_force_pin_change(&mut env, 0), Ok(true));
|
assert_eq!(storage::has_force_pin_change(&mut env), Ok(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -448,15 +408,10 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0x88; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0x88; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xE3, 0x74, 0xF4, 0x27, 0xBE, 0x7D, 0x40, 0xB5, 0x71, 0xB6, 0xB4, 0x1A, 0xD2, 0xC1,
|
0xE3, 0x74, 0xF4, 0x27, 0xBE, 0x7D, 0x40, 0xB5, 0x71, 0xB6, 0xB4, 0x1A, 0xD2, 0xC1,
|
||||||
0x53, 0xD7,
|
0x53, 0xD7,
|
||||||
@@ -476,7 +431,7 @@ mod test {
|
|||||||
};
|
};
|
||||||
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
let config_response = process_config(&mut env, &mut client_pin, config_params);
|
||||||
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
assert_eq!(config_response, Ok(ResponseData::AuthenticatorConfig));
|
||||||
assert_eq!(storage::has_force_pin_change(&mut env, 0), Ok(true));
|
assert_eq!(storage::has_force_pin_change(&mut env), Ok(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -484,13 +439,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let config_params = AuthenticatorConfigParameters {
|
let config_params = AuthenticatorConfigParameters {
|
||||||
sub_command: ConfigSubCommand::VendorPrototype,
|
sub_command: ConfigSubCommand::VendorPrototype,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use super::data_formats::{
|
|||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use super::{cbor_read, cbor_write};
|
use super::{cbor_read, cbor_write};
|
||||||
use crate::api::key_store::KeyStore;
|
use crate::api::key_store::KeyStore;
|
||||||
use crate::ctap::data_formats::{extract_byte_string, extract_map, extract_unsigned};
|
use crate::ctap::data_formats::{extract_byte_string, extract_map};
|
||||||
use crate::env::Env;
|
use crate::env::Env;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -48,7 +48,6 @@ struct CredentialSource {
|
|||||||
rp_id_hash: [u8; 32],
|
rp_id_hash: [u8; 32],
|
||||||
cred_protect_policy: Option<CredentialProtectionPolicy>,
|
cred_protect_policy: Option<CredentialProtectionPolicy>,
|
||||||
cred_blob: Option<Vec<u8>>,
|
cred_blob: Option<Vec<u8>>,
|
||||||
slot_id: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The data fields contained in the credential ID are serialized using CBOR maps.
|
// The data fields contained in the credential ID are serialized using CBOR maps.
|
||||||
@@ -58,7 +57,6 @@ enum CredentialSourceField {
|
|||||||
RpIdHash = 1,
|
RpIdHash = 1,
|
||||||
CredProtectPolicy = 2,
|
CredProtectPolicy = 2,
|
||||||
CredBlob = 3,
|
CredBlob = 3,
|
||||||
SlotId = 4,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CredentialSourceField> for sk_cbor::Value {
|
impl From<CredentialSourceField> for sk_cbor::Value {
|
||||||
@@ -86,7 +84,6 @@ fn decrypt_legacy_credential_id(
|
|||||||
rp_id_hash: plaintext[32..64].try_into().unwrap(),
|
rp_id_hash: plaintext[32..64].try_into().unwrap(),
|
||||||
cred_protect_policy: None,
|
cred_protect_policy: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
slot_id: None,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +102,6 @@ fn decrypt_cbor_credential_id(
|
|||||||
CredentialSourceField::RpIdHash=> rp_id_hash,
|
CredentialSourceField::RpIdHash=> rp_id_hash,
|
||||||
CredentialSourceField::CredProtectPolicy => cred_protect_policy,
|
CredentialSourceField::CredProtectPolicy => cred_protect_policy,
|
||||||
CredentialSourceField::CredBlob => cred_blob,
|
CredentialSourceField::CredBlob => cred_blob,
|
||||||
CredentialSourceField::SlotId => slot_id,
|
|
||||||
} = extract_map(cbor_credential_source)?;
|
} = extract_map(cbor_credential_source)?;
|
||||||
}
|
}
|
||||||
Ok(match (private_key, rp_id_hash) {
|
Ok(match (private_key, rp_id_hash) {
|
||||||
@@ -119,19 +115,11 @@ fn decrypt_cbor_credential_id(
|
|||||||
.map(CredentialProtectionPolicy::try_from)
|
.map(CredentialProtectionPolicy::try_from)
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
let cred_blob = cred_blob.map(extract_byte_string).transpose()?;
|
let cred_blob = cred_blob.map(extract_byte_string).transpose()?;
|
||||||
let slot_id = match slot_id.map(extract_unsigned).transpose()? {
|
|
||||||
Some(x) => Some(
|
|
||||||
usize::try_from(x)
|
|
||||||
.map_err(|_| Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?,
|
|
||||||
),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
Some(CredentialSource {
|
Some(CredentialSource {
|
||||||
private_key,
|
private_key,
|
||||||
rp_id_hash: rp_id_hash.try_into().unwrap(),
|
rp_id_hash: rp_id_hash.try_into().unwrap(),
|
||||||
cred_protect_policy,
|
cred_protect_policy,
|
||||||
cred_blob,
|
cred_blob,
|
||||||
slot_id,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
@@ -179,15 +167,13 @@ pub fn encrypt_to_credential_id(
|
|||||||
rp_id_hash: &[u8; 32],
|
rp_id_hash: &[u8; 32],
|
||||||
cred_protect_policy: Option<CredentialProtectionPolicy>,
|
cred_protect_policy: Option<CredentialProtectionPolicy>,
|
||||||
cred_blob: Option<Vec<u8>>,
|
cred_blob: Option<Vec<u8>>,
|
||||||
slot_id: usize,
|
|
||||||
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
) -> Result<Vec<u8>, Ctap2StatusCode> {
|
||||||
let mut payload = Vec::new();
|
let mut payload = Vec::new();
|
||||||
let cbor = cbor_map_options! {
|
let cbor = cbor_map_options! {
|
||||||
CredentialSourceField::PrivateKey => private_key,
|
CredentialSourceField::PrivateKey => private_key,
|
||||||
CredentialSourceField::RpIdHash=> rp_id_hash,
|
CredentialSourceField::RpIdHash => rp_id_hash,
|
||||||
CredentialSourceField::CredProtectPolicy => cred_protect_policy,
|
CredentialSourceField::CredProtectPolicy => cred_protect_policy,
|
||||||
CredentialSourceField::CredBlob => cred_blob,
|
CredentialSourceField::CredBlob => cred_blob,
|
||||||
CredentialSourceField::SlotId => slot_id as u64,
|
|
||||||
};
|
};
|
||||||
cbor_write(cbor, &mut payload)?;
|
cbor_write(cbor, &mut payload)?;
|
||||||
add_padding(&mut payload)?;
|
add_padding(&mut payload)?;
|
||||||
@@ -276,7 +262,6 @@ pub fn decrypt_credential_id(
|
|||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: credential_source.cred_blob,
|
cred_blob: credential_source.cred_blob,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: credential_source.slot_id,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,7 +282,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
let encrypted_id =
|
let encrypted_id =
|
||||||
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None, 0).unwrap();
|
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None).unwrap();
|
||||||
let decrypted_source = decrypt_credential_id(&mut env, encrypted_id, &rp_id_hash)
|
let decrypted_source = decrypt_credential_id(&mut env, encrypted_id, &rp_id_hash)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -323,7 +308,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
let mut encrypted_id =
|
let mut encrypted_id =
|
||||||
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None, 0).unwrap();
|
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None).unwrap();
|
||||||
encrypted_id[0] = UNSUPPORTED_CREDENTIAL_ID_VERSION;
|
encrypted_id[0] = UNSUPPORTED_CREDENTIAL_ID_VERSION;
|
||||||
// Override the HMAC to pass the check.
|
// Override the HMAC to pass the check.
|
||||||
encrypted_id.truncate(&encrypted_id.len() - 32);
|
encrypted_id.truncate(&encrypted_id.len() - 32);
|
||||||
@@ -343,7 +328,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
let encrypted_id =
|
let encrypted_id =
|
||||||
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None, 0).unwrap();
|
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None).unwrap();
|
||||||
for i in 0..encrypted_id.len() {
|
for i in 0..encrypted_id.len() {
|
||||||
let mut modified_id = encrypted_id.clone();
|
let mut modified_id = encrypted_id.clone();
|
||||||
modified_id[i] ^= 0x01;
|
modified_id[i] ^= 0x01;
|
||||||
@@ -371,7 +356,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
let encrypted_id =
|
let encrypted_id =
|
||||||
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None, 0).unwrap();
|
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None).unwrap();
|
||||||
|
|
||||||
for length in (1..CBOR_CREDENTIAL_ID_SIZE).step_by(16) {
|
for length in (1..CBOR_CREDENTIAL_ID_SIZE).step_by(16) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -438,7 +423,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
let encrypted_id =
|
let encrypted_id =
|
||||||
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None, 0).unwrap();
|
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None).unwrap();
|
||||||
assert_eq!(encrypted_id.len(), CBOR_CREDENTIAL_ID_SIZE);
|
assert_eq!(encrypted_id.len(), CBOR_CREDENTIAL_ID_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -459,7 +444,6 @@ mod test {
|
|||||||
&rp_id_hash,
|
&rp_id_hash,
|
||||||
cred_protect_policy,
|
cred_protect_policy,
|
||||||
cred_blob,
|
cred_blob,
|
||||||
0,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(encrypted_id.is_ok());
|
assert!(encrypted_id.is_ok());
|
||||||
@@ -477,7 +461,6 @@ mod test {
|
|||||||
&rp_id_hash,
|
&rp_id_hash,
|
||||||
Some(CredentialProtectionPolicy::UserVerificationRequired),
|
Some(CredentialProtectionPolicy::UserVerificationRequired),
|
||||||
None,
|
None,
|
||||||
0,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -498,15 +481,9 @@ mod test {
|
|||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
let rp_id_hash = [0x55; 32];
|
||||||
let cred_blob = Some(vec![0x55; env.customization().max_cred_blob_length()]);
|
let cred_blob = Some(vec![0x55; env.customization().max_cred_blob_length()]);
|
||||||
let encrypted_id = encrypt_to_credential_id(
|
let encrypted_id =
|
||||||
&mut env,
|
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, cred_blob.clone())
|
||||||
&private_key,
|
.unwrap();
|
||||||
&rp_id_hash,
|
|
||||||
None,
|
|
||||||
cred_blob.clone(),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let decrypted_source = decrypt_credential_id(&mut env, encrypted_id, &rp_id_hash)
|
let decrypted_source = decrypt_credential_id(&mut env, encrypted_id, &rp_id_hash)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -514,22 +491,4 @@ mod test {
|
|||||||
assert_eq!(decrypted_source.private_key, private_key);
|
assert_eq!(decrypted_source.private_key, private_key);
|
||||||
assert_eq!(decrypted_source.cred_blob, cred_blob);
|
assert_eq!(decrypted_source.cred_blob, cred_blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_slot_id_persisted() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
let private_key = PrivateKey::new(&mut env, SignatureAlgorithm::Es256);
|
|
||||||
|
|
||||||
let rp_id_hash = [0x55; 32];
|
|
||||||
let slot_id = 1;
|
|
||||||
let encrypted_id =
|
|
||||||
encrypt_to_credential_id(&mut env, &private_key, &rp_id_hash, None, None, slot_id)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let decrypted_source = decrypt_credential_id(&mut env, encrypted_id, &rp_id_hash)
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(decrypted_source.private_key, private_key);
|
|
||||||
assert_eq!(decrypted_source.slot_id, Some(slot_id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ fn enumerate_credentials_response(
|
|||||||
user_icon,
|
user_icon,
|
||||||
cred_blob: _,
|
cred_blob: _,
|
||||||
large_blob_key,
|
large_blob_key,
|
||||||
slot_id: _,
|
|
||||||
} = credential;
|
} = credential;
|
||||||
let user = PublicKeyCredentialUserEntity {
|
let user = PublicKeyCredentialUserEntity {
|
||||||
user_id: user_handle,
|
user_id: user_handle,
|
||||||
@@ -184,17 +183,12 @@ fn process_enumerate_credentials_begin(
|
|||||||
.rp_id_hash
|
.rp_id_hash
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?;
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_MISSING_PARAMETER)?;
|
||||||
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash[..])?;
|
client_pin.has_no_or_rp_id_hash_permission(&rp_id_hash[..])?;
|
||||||
// enumerateCredentials needs UV, so slot_id must not be None.
|
|
||||||
let slot_id = client_pin
|
|
||||||
.get_slot_id_in_use_or_default(env)?
|
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?;
|
|
||||||
let mut iter_result = Ok(());
|
let mut iter_result = Ok(());
|
||||||
let iter = storage::iter_credentials(env, &mut iter_result)?;
|
let iter = storage::iter_credentials(env, &mut iter_result)?;
|
||||||
let mut rp_credentials: Vec<usize> = iter
|
let mut rp_credentials: Vec<usize> = iter
|
||||||
.filter_map(|(key, credential)| {
|
.filter_map(|(key, credential)| {
|
||||||
let cred_rp_id_hash = Sha256::hash(credential.rp_id.as_bytes());
|
let cred_rp_id_hash = Sha256::hash(credential.rp_id.as_bytes());
|
||||||
let slot_id_matches = credential.slot_id.unwrap_or(0) == slot_id;
|
if cred_rp_id_hash == rp_id_hash.as_slice() {
|
||||||
if cred_rp_id_hash == rp_id_hash.as_slice() && slot_id_matches {
|
|
||||||
Some(key)
|
Some(key)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -391,7 +385,6 @@ mod test {
|
|||||||
user_icon: Some("icon".to_string()),
|
user_icon: Some("icon".to_string()),
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,19 +392,14 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
pin_uv_auth_protocol,
|
|
||||||
);
|
|
||||||
let credential_source = create_credential_source(&mut env);
|
let credential_source = create_credential_source(&mut env);
|
||||||
|
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
ctap_state.client_pin = client_pin;
|
ctap_state.client_pin = client_pin;
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let management_data = vec![CredentialManagementSubCommand::GetCredsMetadata as u8];
|
let management_data = vec![CredentialManagementSubCommand::GetCredsMetadata as u8];
|
||||||
let pin_uv_auth_param = authenticate_pin_uv_auth_token(
|
let pin_uv_auth_param = authenticate_pin_uv_auth_token(
|
||||||
&pin_uv_auth_token,
|
&pin_uv_auth_token,
|
||||||
@@ -486,13 +474,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let credential_source1 = create_credential_source(&mut env);
|
let credential_source1 = create_credential_source(&mut env);
|
||||||
let mut credential_source2 = create_credential_source(&mut env);
|
let mut credential_source2 = create_credential_source(&mut env);
|
||||||
credential_source2.rp_id = "another.example.com".to_string();
|
credential_source2.rp_id = "another.example.com".to_string();
|
||||||
@@ -503,7 +486,7 @@ mod test {
|
|||||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||||
storage::store_credential(&mut env, credential_source2).unwrap();
|
storage::store_credential(&mut env, credential_source2).unwrap();
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
||||||
0xD0, 0xD1,
|
0xD0, 0xD1,
|
||||||
@@ -585,13 +568,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let credential_source = create_credential_source(&mut env);
|
let credential_source = create_credential_source(&mut env);
|
||||||
|
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
@@ -604,7 +582,7 @@ mod test {
|
|||||||
storage::store_credential(&mut env, credential).unwrap();
|
storage::store_credential(&mut env, credential).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
0x1A, 0xA4, 0x96, 0xDA, 0x62, 0x80, 0x28, 0x13, 0xEB, 0x32, 0xB9, 0xF1, 0xD2, 0xA9,
|
||||||
0xD0, 0xD1,
|
0xD0, 0xD1,
|
||||||
@@ -671,13 +649,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let credential_source1 = create_credential_source(&mut env);
|
let credential_source1 = create_credential_source(&mut env);
|
||||||
let mut credential_source2 = create_credential_source(&mut env);
|
let mut credential_source2 = create_credential_source(&mut env);
|
||||||
credential_source2.user_handle = vec![0x02];
|
credential_source2.user_handle = vec![0x02];
|
||||||
@@ -691,7 +664,7 @@ mod test {
|
|||||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
storage::store_credential(&mut env, credential_source1).unwrap();
|
||||||
storage::store_credential(&mut env, credential_source2).unwrap();
|
storage::store_credential(&mut env, credential_source2).unwrap();
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xF8, 0xB0, 0x3C, 0xC1, 0xD5, 0x58, 0x9C, 0xB7, 0x4D, 0x42, 0xA1, 0x64, 0x14, 0x28,
|
0xF8, 0xB0, 0x3C, 0xC1, 0xD5, 0x58, 0x9C, 0xB7, 0x4D, 0x42, 0xA1, 0x64, 0x14, 0x28,
|
||||||
0x2B, 0x68,
|
0x2B, 0x68,
|
||||||
@@ -773,132 +746,13 @@ mod test {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_process_enumerate_credentials_multi_pin() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
storage::_enable_multi_pin_for_test(&mut env).unwrap();
|
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
|
||||||
let client_pin = ClientPin::new_test(
|
|
||||||
&mut env,
|
|
||||||
1,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// credential_source1 has no slot_id, so should be treated as slot 0. Only credential_source 2 and 4
|
|
||||||
// should be discovered.
|
|
||||||
let credential_source1 = create_credential_source(&mut env);
|
|
||||||
let mut credential_source2 = create_credential_source(&mut env);
|
|
||||||
credential_source2.user_handle = vec![0x02];
|
|
||||||
credential_source2.slot_id = Some(1);
|
|
||||||
let mut credential_source3 = create_credential_source(&mut env);
|
|
||||||
credential_source3.user_handle = vec![0x03];
|
|
||||||
credential_source3.slot_id = Some(2);
|
|
||||||
let mut credential_source4 = create_credential_source(&mut env);
|
|
||||||
credential_source4.user_handle = vec![0x04];
|
|
||||||
credential_source4.slot_id = Some(1);
|
|
||||||
|
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
|
||||||
ctap_state.client_pin = client_pin;
|
|
||||||
|
|
||||||
storage::store_credential(&mut env, credential_source1).unwrap();
|
|
||||||
storage::store_credential(&mut env, credential_source2).unwrap();
|
|
||||||
storage::store_credential(&mut env, credential_source3).unwrap();
|
|
||||||
storage::store_credential(&mut env, credential_source4).unwrap();
|
|
||||||
|
|
||||||
storage::set_pin(&mut env, 1, &[0u8; 16], 4).unwrap();
|
|
||||||
let pin_uv_auth_param = Some(vec![
|
|
||||||
0xF8, 0xB0, 0x3C, 0xC1, 0xD5, 0x58, 0x9C, 0xB7, 0x4D, 0x42, 0xA1, 0x64, 0x14, 0x28,
|
|
||||||
0x2B, 0x68,
|
|
||||||
]);
|
|
||||||
|
|
||||||
let sub_command_params = CredentialManagementSubCommandParameters {
|
|
||||||
rp_id_hash: Some(Sha256::hash(b"example.com").to_vec()),
|
|
||||||
credential_id: None,
|
|
||||||
user: None,
|
|
||||||
};
|
|
||||||
// RP ID hash:
|
|
||||||
// A379A6F6EEAFB9A55E378C118034E2751E682FAB9F2D30AB13D2125586CE1947
|
|
||||||
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
|
||||||
sub_command: CredentialManagementSubCommand::EnumerateCredentialsBegin,
|
|
||||||
sub_command_params: Some(sub_command_params),
|
|
||||||
pin_uv_auth_protocol: Some(PinUvAuthProtocol::V1),
|
|
||||||
pin_uv_auth_param,
|
|
||||||
};
|
|
||||||
let cred_management_response = process_credential_management(
|
|
||||||
&mut env,
|
|
||||||
&mut ctap_state.stateful_command_permission,
|
|
||||||
&mut ctap_state.client_pin,
|
|
||||||
cred_management_params,
|
|
||||||
DUMMY_CHANNEL,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
|
||||||
match cred_management_response.unwrap() {
|
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
|
||||||
assert!(response.user.is_some());
|
|
||||||
assert!(response.public_key.is_some());
|
|
||||||
assert_eq!(response.total_credentials, Some(2));
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid response type"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
|
||||||
sub_command: CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential,
|
|
||||||
sub_command_params: None,
|
|
||||||
pin_uv_auth_protocol: None,
|
|
||||||
pin_uv_auth_param: None,
|
|
||||||
};
|
|
||||||
let cred_management_response = process_credential_management(
|
|
||||||
&mut env,
|
|
||||||
&mut ctap_state.stateful_command_permission,
|
|
||||||
&mut ctap_state.client_pin,
|
|
||||||
cred_management_params,
|
|
||||||
DUMMY_CHANNEL,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
|
||||||
match cred_management_response.unwrap() {
|
|
||||||
ResponseData::AuthenticatorCredentialManagement(Some(response)) => {
|
|
||||||
assert!(response.user.is_some());
|
|
||||||
assert!(response.public_key.is_some());
|
|
||||||
assert_eq!(response.total_credentials, None);
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid response type"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
|
||||||
sub_command: CredentialManagementSubCommand::EnumerateCredentialsGetNextCredential,
|
|
||||||
sub_command_params: None,
|
|
||||||
pin_uv_auth_protocol: None,
|
|
||||||
pin_uv_auth_param: None,
|
|
||||||
};
|
|
||||||
let cred_management_response = process_credential_management(
|
|
||||||
&mut env,
|
|
||||||
&mut ctap_state.stateful_command_permission,
|
|
||||||
&mut ctap_state.client_pin,
|
|
||||||
cred_management_params,
|
|
||||||
DUMMY_CHANNEL,
|
|
||||||
CtapInstant::new(0),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
cred_management_response,
|
|
||||||
Err(Ctap2StatusCode::CTAP2_ERR_NOT_ALLOWED)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_process_delete_credential() {
|
fn test_process_delete_credential() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut credential_source = create_credential_source(&mut env);
|
let mut credential_source = create_credential_source(&mut env);
|
||||||
credential_source.credential_id = vec![0x1D; 32];
|
credential_source.credential_id = vec![0x1D; 32];
|
||||||
|
|
||||||
@@ -907,7 +761,7 @@ mod test {
|
|||||||
|
|
||||||
storage::store_credential(&mut env, credential_source).unwrap();
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xBD, 0xE3, 0xEF, 0x8A, 0x77, 0x01, 0xB1, 0x69, 0x19, 0xE6, 0x62, 0xB9, 0x9B, 0x89,
|
0xBD, 0xE3, 0xEF, 0x8A, 0x77, 0x01, 0xB1, 0x69, 0x19, 0xE6, 0x62, 0xB9, 0x9B, 0x89,
|
||||||
0x9C, 0x64,
|
0x9C, 0x64,
|
||||||
@@ -967,13 +821,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let client_pin = ClientPin::new_test(
|
let client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut credential_source = create_credential_source(&mut env);
|
let mut credential_source = create_credential_source(&mut env);
|
||||||
credential_source.credential_id = vec![0x1D; 32];
|
credential_source.credential_id = vec![0x1D; 32];
|
||||||
|
|
||||||
@@ -982,7 +831,7 @@ mod test {
|
|||||||
|
|
||||||
storage::store_credential(&mut env, credential_source).unwrap();
|
storage::store_credential(&mut env, credential_source).unwrap();
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let pin_uv_auth_param = Some(vec![
|
let pin_uv_auth_param = Some(vec![
|
||||||
0xA5, 0x55, 0x8F, 0x03, 0xC3, 0xD3, 0x73, 0x1C, 0x07, 0xDA, 0x1F, 0x8C, 0xC7, 0xBD,
|
0xA5, 0x55, 0x8F, 0x03, 0xC3, 0xD3, 0x73, 0x1C, 0x07, 0xDA, 0x1F, 0x8C, 0xC7, 0xBD,
|
||||||
0x9D, 0xB7,
|
0x9D, 0xB7,
|
||||||
@@ -1040,7 +889,7 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
let mut ctap_state = CtapState::new(&mut env, CtapInstant::new(0));
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
|
|
||||||
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
let cred_management_params = AuthenticatorCredentialManagementParameters {
|
||||||
sub_command: CredentialManagementSubCommand::GetCredsMetadata,
|
sub_command: CredentialManagementSubCommand::GetCredsMetadata,
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ use alloc::vec;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
|
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
|
||||||
use crypto::ecdsa;
|
|
||||||
use crypto::sha256::Sha256;
|
use crypto::sha256::Sha256;
|
||||||
|
use crypto::{ecdsa, hybrid};
|
||||||
use rng256::Rng256;
|
use rng256::Rng256;
|
||||||
use sk_cbor as cbor;
|
use sk_cbor as cbor;
|
||||||
use sk_cbor::{cbor_array, cbor_bytes, cbor_int};
|
use sk_cbor::{cbor_array, cbor_bytes, cbor_int};
|
||||||
@@ -82,6 +82,7 @@ pub enum PrivateKey {
|
|||||||
Ecdsa([u8; 32]),
|
Ecdsa([u8; 32]),
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
Ed25519(ed25519_compact::SecretKey),
|
Ed25519(ed25519_compact::SecretKey),
|
||||||
|
Hybrid(hybrid::SecKey),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivateKey {
|
impl PrivateKey {
|
||||||
@@ -100,6 +101,34 @@ impl PrivateKey {
|
|||||||
let bytes = env.rng().gen_uniform_u8x32();
|
let bytes = env.rng().gen_uniform_u8x32();
|
||||||
Self::new_ed25519_from_bytes(&bytes).unwrap()
|
Self::new_ed25519_from_bytes(&bytes).unwrap()
|
||||||
}
|
}
|
||||||
|
SignatureAlgorithm::Hybrid => PrivateKey::Hybrid(hybrid::SecKey::gensk(env.rng())),
|
||||||
|
SignatureAlgorithm::Unknown => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new private / public key pair for the given algorithm.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the algorithm is [`SignatureAlgorithm::Unknown`].
|
||||||
|
pub fn new_with_pub_key(env: &mut impl Env, alg: SignatureAlgorithm) -> (Self, CoseKey) {
|
||||||
|
match alg {
|
||||||
|
SignatureAlgorithm::Es256 => {
|
||||||
|
let private_key = PrivateKey::Ecdsa(env.key_store().generate_ecdsa_seed().unwrap());
|
||||||
|
let pub_key = private_key.get_pub_key(env).unwrap();
|
||||||
|
(private_key, pub_key)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "ed25519")]
|
||||||
|
SignatureAlgorithm::Eddsa => {
|
||||||
|
let bytes = env.rng().gen_uniform_u8x32();
|
||||||
|
let private_key = Self::new_ed25519_from_bytes(&bytes).unwrap();
|
||||||
|
let pub_key = private_key.get_pub_key(env).unwrap();
|
||||||
|
(private_key, pub_key)
|
||||||
|
}
|
||||||
|
SignatureAlgorithm::Hybrid => {
|
||||||
|
let (hybrid_key, pub_key) = hybrid::SecKey::gensk_with_pk(env.rng());
|
||||||
|
(PrivateKey::Hybrid(hybrid_key), CoseKey::from(pub_key))
|
||||||
|
}
|
||||||
SignatureAlgorithm::Unknown => unreachable!(),
|
SignatureAlgorithm::Unknown => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,6 +166,15 @@ impl PrivateKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper function that creates a private key of type Hybrid.
|
||||||
|
fn new_hybrid_from_bytes(bytes: &[u8]) -> Option<Self> {
|
||||||
|
if bytes.len() != hybrid::SecKey::BYTES_LENGTH {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
hybrid::SecKey::from_bytes(array_ref!(bytes, 0, hybrid::SecKey::BYTES_LENGTH))
|
||||||
|
.map(PrivateKey::from)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the corresponding public key.
|
/// Returns the corresponding public key.
|
||||||
pub fn get_pub_key(&self, env: &mut impl Env) -> Result<CoseKey, Ctap2StatusCode> {
|
pub fn get_pub_key(&self, env: &mut impl Env) -> Result<CoseKey, Ctap2StatusCode> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
@@ -145,6 +183,7 @@ impl PrivateKey {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
PrivateKey::Ed25519(ed25519_key) => CoseKey::from(ed25519_key.public_key()),
|
PrivateKey::Ed25519(ed25519_key) => CoseKey::from(ed25519_key.public_key()),
|
||||||
|
PrivateKey::Hybrid(hybrid_key) => CoseKey::from(hybrid_key.genpk()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +199,9 @@ impl PrivateKey {
|
|||||||
.to_asn1_der(),
|
.to_asn1_der(),
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
PrivateKey::Ed25519(ed25519_key) => ed25519_key.sign(message, None).to_vec(),
|
PrivateKey::Ed25519(ed25519_key) => ed25519_key.sign(message, None).to_vec(),
|
||||||
|
PrivateKey::Hybrid(hybrid_key) => {
|
||||||
|
hybrid_key.sign_rfc6979::<Sha256>(message).to_asn1_der()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +211,7 @@ impl PrivateKey {
|
|||||||
PrivateKey::Ecdsa(_) => SignatureAlgorithm::Es256,
|
PrivateKey::Ecdsa(_) => SignatureAlgorithm::Es256,
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
PrivateKey::Ed25519(_) => SignatureAlgorithm::Eddsa,
|
PrivateKey::Ed25519(_) => SignatureAlgorithm::Eddsa,
|
||||||
|
PrivateKey::Hybrid(_) => SignatureAlgorithm::Hybrid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +221,11 @@ impl PrivateKey {
|
|||||||
PrivateKey::Ecdsa(ecdsa_seed) => ecdsa_seed.to_vec(),
|
PrivateKey::Ecdsa(ecdsa_seed) => ecdsa_seed.to_vec(),
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
PrivateKey::Ed25519(ed25519_key) => ed25519_key.seed().to_vec(),
|
PrivateKey::Ed25519(ed25519_key) => ed25519_key.seed().to_vec(),
|
||||||
|
PrivateKey::Hybrid(hybrid_key) => {
|
||||||
|
let mut key_bytes = vec![0u8; hybrid::SecKey::BYTES_LENGTH];
|
||||||
|
hybrid_key.to_bytes(array_mut_ref!(key_bytes, 0, hybrid::SecKey::BYTES_LENGTH));
|
||||||
|
key_bytes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -214,11 +262,19 @@ impl TryFrom<cbor::Value> for PrivateKey {
|
|||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
SignatureAlgorithm::Eddsa => PrivateKey::new_ed25519_from_bytes(&key_bytes)
|
SignatureAlgorithm::Eddsa => PrivateKey::new_ed25519_from_bytes(&key_bytes)
|
||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR),
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR),
|
||||||
|
SignatureAlgorithm::Hybrid => PrivateKey::new_hybrid_from_bytes(&key_bytes)
|
||||||
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_INVALID_CBOR),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<hybrid::SecKey> for PrivateKey {
|
||||||
|
fn from(hybrid_key: hybrid::SecKey) -> Self {
|
||||||
|
PrivateKey::Hybrid(hybrid_key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ impl Ctap1Command {
|
|||||||
.ecdsa_key(env)
|
.ecdsa_key(env)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
||||||
let pk = sk.genpk();
|
let pk = sk.genpk();
|
||||||
let key_handle = encrypt_to_credential_id(env, &private_key, &application, None, None, 0)
|
let key_handle = encrypt_to_credential_id(env, &private_key, &application, None, None)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
.map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
|
||||||
if key_handle.len() > 0xFF {
|
if key_handle.len() > 0xFF {
|
||||||
// This is just being defensive with unreachable code.
|
// This is just being defensive with unreachable code.
|
||||||
@@ -320,12 +320,11 @@ impl Ctap1Command {
|
|||||||
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
|
||||||
}
|
}
|
||||||
ctap_state
|
ctap_state
|
||||||
.increment_global_signature_counter(env, 0)
|
.increment_global_signature_counter(env)
|
||||||
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
|
||||||
let mut signature_data = ctap_state
|
let mut signature_data = ctap_state
|
||||||
.generate_auth_data(
|
.generate_auth_data(
|
||||||
env,
|
env,
|
||||||
0,
|
|
||||||
&application,
|
&application,
|
||||||
Ctap1Command::USER_PRESENCE_INDICATOR_BYTE,
|
Ctap1Command::USER_PRESENCE_INDICATOR_BYTE,
|
||||||
)
|
)
|
||||||
@@ -499,8 +498,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
|
|
||||||
let response =
|
let response =
|
||||||
@@ -518,8 +516,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let application = [0x55; 32];
|
let application = [0x55; 32];
|
||||||
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
|
|
||||||
@@ -538,8 +535,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let mut message = create_authenticate_message(
|
let mut message = create_authenticate_message(
|
||||||
&application,
|
&application,
|
||||||
Ctap1Flags::DontEnforceUpAndSign,
|
Ctap1Flags::DontEnforceUpAndSign,
|
||||||
@@ -577,8 +573,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let mut message =
|
let mut message =
|
||||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
message[0] = 0xEE;
|
message[0] = 0xEE;
|
||||||
@@ -598,8 +593,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let mut message =
|
let mut message =
|
||||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
message[1] = 0xEE;
|
message[1] = 0xEE;
|
||||||
@@ -619,8 +613,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let mut message =
|
let mut message =
|
||||||
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
|
||||||
message[2] = 0xEE;
|
message[2] = 0xEE;
|
||||||
@@ -648,8 +641,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let message =
|
let message =
|
||||||
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
|
create_authenticate_message(&application, Ctap1Flags::EnforceUpAndSign, &key_handle);
|
||||||
|
|
||||||
@@ -659,7 +651,7 @@ mod test {
|
|||||||
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
|
Ctap1Command::process_command(&mut env, &message, &mut ctap_state, CtapInstant::new(0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
let global_signature_counter = storage::global_signature_counter(&mut env, 0).unwrap();
|
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
&mut env,
|
&mut env,
|
||||||
array_ref!(response, 1, 4),
|
array_ref!(response, 1, 4),
|
||||||
@@ -677,8 +669,7 @@ mod test {
|
|||||||
|
|
||||||
let rp_id = "example.com";
|
let rp_id = "example.com";
|
||||||
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
|
||||||
let key_handle =
|
let key_handle = encrypt_to_credential_id(&mut env, &sk, &application, None, None).unwrap();
|
||||||
encrypt_to_credential_id(&mut env, &sk, &application, None, None, 0).unwrap();
|
|
||||||
let message = create_authenticate_message(
|
let message = create_authenticate_message(
|
||||||
&application,
|
&application,
|
||||||
Ctap1Flags::DontEnforceUpAndSign,
|
Ctap1Flags::DontEnforceUpAndSign,
|
||||||
@@ -693,7 +684,7 @@ mod test {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(response[0], 0x01);
|
assert_eq!(response[0], 0x01);
|
||||||
let global_signature_counter = storage::global_signature_counter(&mut env, 0).unwrap();
|
let global_signature_counter = storage::global_signature_counter(&mut env).unwrap();
|
||||||
check_signature_counter(
|
check_signature_counter(
|
||||||
&mut env,
|
&mut env,
|
||||||
array_ref!(response, 1, 4),
|
array_ref!(response, 1, 4),
|
||||||
|
|||||||
@@ -15,21 +15,26 @@
|
|||||||
use super::crypto_wrapper::PrivateKey;
|
use super::crypto_wrapper::PrivateKey;
|
||||||
use super::status_code::Ctap2StatusCode;
|
use super::status_code::Ctap2StatusCode;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
#[cfg(feature = "fuzz")]
|
#[cfg(feature = "fuzz")]
|
||||||
use arbitrary::Arbitrary;
|
use arbitrary::Arbitrary;
|
||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::convert::TryFrom;
|
use core::convert::TryFrom;
|
||||||
use crypto::{ecdh, ecdsa};
|
use crypto::{ecdh, ecdsa, hybrid};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use enum_iterator::IntoEnumIterator;
|
use enum_iterator::IntoEnumIterator;
|
||||||
use sk_cbor as cbor;
|
use sk_cbor::{cbor_array_vec, cbor_bytes, cbor_map_options, destructure_cbor_map};
|
||||||
use sk_cbor::{cbor_array_vec, cbor_map, cbor_map_options, destructure_cbor_map};
|
use {dilithium, sk_cbor as cbor};
|
||||||
|
|
||||||
// Used as the identifier for ECDSA in assertion signatures and COSE.
|
// Used as the identifier for ECDSA in assertion signatures and COSE.
|
||||||
pub const ES256_ALGORITHM: i64 = -7;
|
pub const ES256_ALGORITHM: i64 = -7;
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
pub const EDDSA_ALGORITHM: i64 = -8;
|
pub const EDDSA_ALGORITHM: i64 = -8;
|
||||||
|
// Used as the identifier for Hybrid in assertion signatures.
|
||||||
|
// (numbers less than -65536 are reserved for private use)
|
||||||
|
// TODO: Update this number later.
|
||||||
|
pub const HYBRID_ALGORITHM: i64 = -65537;
|
||||||
|
|
||||||
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
// https://www.w3.org/TR/webauthn/#dictdef-publickeycredentialrpentity
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@@ -510,6 +515,7 @@ pub enum SignatureAlgorithm {
|
|||||||
Es256 = ES256_ALGORITHM as isize,
|
Es256 = ES256_ALGORITHM as isize,
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
Eddsa = EDDSA_ALGORITHM as isize,
|
Eddsa = EDDSA_ALGORITHM as isize,
|
||||||
|
Hybrid = HYBRID_ALGORITHM as isize,
|
||||||
// This is the default for all numbers not covered above.
|
// This is the default for all numbers not covered above.
|
||||||
// Unknown types should be ignored, instead of returning errors.
|
// Unknown types should be ignored, instead of returning errors.
|
||||||
Unknown = 0,
|
Unknown = 0,
|
||||||
@@ -527,6 +533,7 @@ impl From<i64> for SignatureAlgorithm {
|
|||||||
ES256_ALGORITHM => SignatureAlgorithm::Es256,
|
ES256_ALGORITHM => SignatureAlgorithm::Es256,
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
EDDSA_ALGORITHM => SignatureAlgorithm::Eddsa,
|
EDDSA_ALGORITHM => SignatureAlgorithm::Eddsa,
|
||||||
|
HYBRID_ALGORITHM => SignatureAlgorithm::Hybrid,
|
||||||
_ => SignatureAlgorithm::Unknown,
|
_ => SignatureAlgorithm::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -595,7 +602,6 @@ pub struct PublicKeyCredentialSource {
|
|||||||
pub user_icon: Option<String>,
|
pub user_icon: Option<String>,
|
||||||
pub cred_blob: Option<Vec<u8>>,
|
pub cred_blob: Option<Vec<u8>>,
|
||||||
pub large_blob_key: Option<Vec<u8>>,
|
pub large_blob_key: Option<Vec<u8>>,
|
||||||
pub slot_id: Option<usize>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We serialize credentials for the persistent storage using CBOR maps. Each field of a credential
|
// We serialize credentials for the persistent storage using CBOR maps. Each field of a credential
|
||||||
@@ -614,7 +620,6 @@ enum PublicKeyCredentialSourceField {
|
|||||||
CredBlob = 10,
|
CredBlob = 10,
|
||||||
LargeBlobKey = 11,
|
LargeBlobKey = 11,
|
||||||
PrivateKey = 12,
|
PrivateKey = 12,
|
||||||
SlotId = 13,
|
|
||||||
// When a field is removed, its tag should be reserved and not used for new fields. We document
|
// When a field is removed, its tag should be reserved and not used for new fields. We document
|
||||||
// those reserved tags below.
|
// those reserved tags below.
|
||||||
// Reserved tags:
|
// Reserved tags:
|
||||||
@@ -641,7 +646,6 @@ impl From<PublicKeyCredentialSource> for cbor::Value {
|
|||||||
PublicKeyCredentialSourceField::CredBlob => credential.cred_blob,
|
PublicKeyCredentialSourceField::CredBlob => credential.cred_blob,
|
||||||
PublicKeyCredentialSourceField::LargeBlobKey => credential.large_blob_key,
|
PublicKeyCredentialSourceField::LargeBlobKey => credential.large_blob_key,
|
||||||
PublicKeyCredentialSourceField::PrivateKey => credential.private_key,
|
PublicKeyCredentialSourceField::PrivateKey => credential.private_key,
|
||||||
PublicKeyCredentialSourceField::SlotId => credential.slot_id.map(|x| x as u64),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,7 +668,6 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
|||||||
PublicKeyCredentialSourceField::CredBlob => cred_blob,
|
PublicKeyCredentialSourceField::CredBlob => cred_blob,
|
||||||
PublicKeyCredentialSourceField::LargeBlobKey => large_blob_key,
|
PublicKeyCredentialSourceField::LargeBlobKey => large_blob_key,
|
||||||
PublicKeyCredentialSourceField::PrivateKey => private_key,
|
PublicKeyCredentialSourceField::PrivateKey => private_key,
|
||||||
PublicKeyCredentialSourceField::SlotId => slot_id,
|
|
||||||
} = extract_map(cbor_value)?;
|
} = extract_map(cbor_value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -691,12 +694,6 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
|||||||
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?,
|
.ok_or(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?,
|
||||||
(None, Some(k)) => k,
|
(None, Some(k)) => k,
|
||||||
};
|
};
|
||||||
let slot_id = match slot_id.map(extract_unsigned).transpose()? {
|
|
||||||
Some(x) => Some(
|
|
||||||
usize::try_from(x).map_err(|_| Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)?,
|
|
||||||
),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We don't return whether there were unknown fields in the CBOR value. This means that
|
// We don't return whether there were unknown fields in the CBOR value. This means that
|
||||||
// deserialization is not injective. In particular deserialization is only an inverse of
|
// deserialization is not injective. In particular deserialization is only an inverse of
|
||||||
@@ -721,7 +718,6 @@ impl TryFrom<cbor::Value> for PublicKeyCredentialSource {
|
|||||||
user_icon,
|
user_icon,
|
||||||
cred_blob,
|
cred_blob,
|
||||||
large_blob_key,
|
large_blob_key,
|
||||||
slot_id,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -744,6 +740,7 @@ pub struct CoseKey {
|
|||||||
algorithm: i64,
|
algorithm: i64,
|
||||||
key_type: i64,
|
key_type: i64,
|
||||||
curve: i64,
|
curve: i64,
|
||||||
|
dilithium_bytes: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoseKey {
|
impl CoseKey {
|
||||||
@@ -755,6 +752,8 @@ impl CoseKey {
|
|||||||
const EC2_KEY_TYPE: i64 = 2;
|
const EC2_KEY_TYPE: i64 = 2;
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
const OKP_KEY_TYPE: i64 = 1;
|
const OKP_KEY_TYPE: i64 = 1;
|
||||||
|
// The key type changes for hybrid. The value is made up.
|
||||||
|
const HYBRID_KEY_TYPE: i64 = -65537;
|
||||||
// The parameter behind map key -1.
|
// The parameter behind map key -1.
|
||||||
const P_256_CURVE: i64 = 1;
|
const P_256_CURVE: i64 = 1;
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
@@ -774,6 +773,7 @@ impl TryFrom<cbor::Value> for CoseKey {
|
|||||||
-1 => curve,
|
-1 => curve,
|
||||||
-2 => x_bytes,
|
-2 => x_bytes,
|
||||||
-3 => y_bytes,
|
-3 => y_bytes,
|
||||||
|
-4 => dilithium_bytes,
|
||||||
} = extract_map(cbor_value)?;
|
} = extract_map(cbor_value)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -796,16 +796,30 @@ impl TryFrom<cbor::Value> for CoseKey {
|
|||||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
}
|
}
|
||||||
let key_type = extract_integer(ok_or_missing(key_type)?)?;
|
let key_type = extract_integer(ok_or_missing(key_type)?)?;
|
||||||
if key_type != CoseKey::EC2_KEY_TYPE {
|
if key_type != CoseKey::EC2_KEY_TYPE && key_type != CoseKey::HYBRID_KEY_TYPE {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parsed_dilithium_bytes = if key_type == CoseKey::EC2_KEY_TYPE {
|
||||||
|
if dilithium_bytes.is_some() {
|
||||||
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let dilithium_bytes = extract_byte_string(ok_or_missing(dilithium_bytes)?)?;
|
||||||
|
if dilithium_bytes.len() != dilithium::params::PK_SIZE_PACKED {
|
||||||
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
|
}
|
||||||
|
Some(dilithium_bytes)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(CoseKey {
|
Ok(CoseKey {
|
||||||
x_bytes: *array_ref![x_bytes.as_slice(), 0, ecdh::NBYTES],
|
x_bytes: *array_ref![x_bytes.as_slice(), 0, ecdh::NBYTES],
|
||||||
y_bytes: *array_ref![y_bytes.as_slice(), 0, ecdh::NBYTES],
|
y_bytes: *array_ref![y_bytes.as_slice(), 0, ecdh::NBYTES],
|
||||||
algorithm,
|
algorithm,
|
||||||
key_type,
|
key_type,
|
||||||
curve,
|
curve,
|
||||||
|
dilithium_bytes: parsed_dilithium_bytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -818,14 +832,16 @@ impl From<CoseKey> for cbor::Value {
|
|||||||
algorithm,
|
algorithm,
|
||||||
key_type,
|
key_type,
|
||||||
curve,
|
curve,
|
||||||
|
dilithium_bytes,
|
||||||
} = cose_key;
|
} = cose_key;
|
||||||
|
|
||||||
cbor_map! {
|
cbor_map_options! {
|
||||||
1 => key_type,
|
1 => Some(key_type),
|
||||||
3 => algorithm,
|
3 => Some(algorithm),
|
||||||
-1 => curve,
|
-1 => Some(curve),
|
||||||
-2 => x_bytes,
|
-2 => Some(cbor_bytes!(x_bytes.to_vec())),
|
||||||
-3 => y_bytes,
|
-3 => Some(cbor_bytes!(y_bytes.to_vec())),
|
||||||
|
-4 => dilithium_bytes.map(|b| cbor_bytes!(b)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -841,14 +857,15 @@ impl From<ecdh::PubKey> for CoseKey {
|
|||||||
algorithm: CoseKey::ECDH_ALGORITHM,
|
algorithm: CoseKey::ECDH_ALGORITHM,
|
||||||
key_type: CoseKey::EC2_KEY_TYPE,
|
key_type: CoseKey::EC2_KEY_TYPE,
|
||||||
curve: CoseKey::P_256_CURVE,
|
curve: CoseKey::P_256_CURVE,
|
||||||
|
dilithium_bytes: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ecdsa::PubKey> for CoseKey {
|
impl From<ecdsa::PubKey> for CoseKey {
|
||||||
fn from(pk: ecdsa::PubKey) -> Self {
|
fn from(pk: ecdsa::PubKey) -> Self {
|
||||||
let mut x_bytes = [0; ecdh::NBYTES];
|
let mut x_bytes = [0; ecdsa::NBYTES];
|
||||||
let mut y_bytes = [0; ecdh::NBYTES];
|
let mut y_bytes = [0; ecdsa::NBYTES];
|
||||||
pk.to_coordinates(&mut x_bytes, &mut y_bytes);
|
pk.to_coordinates(&mut x_bytes, &mut y_bytes);
|
||||||
CoseKey {
|
CoseKey {
|
||||||
x_bytes,
|
x_bytes,
|
||||||
@@ -856,6 +873,7 @@ impl From<ecdsa::PubKey> for CoseKey {
|
|||||||
algorithm: ES256_ALGORITHM,
|
algorithm: ES256_ALGORITHM,
|
||||||
key_type: CoseKey::EC2_KEY_TYPE,
|
key_type: CoseKey::EC2_KEY_TYPE,
|
||||||
curve: CoseKey::P_256_CURVE,
|
curve: CoseKey::P_256_CURVE,
|
||||||
|
dilithium_bytes: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -869,6 +887,27 @@ impl From<ed25519_compact::PublicKey> for CoseKey {
|
|||||||
key_type: CoseKey::OKP_KEY_TYPE,
|
key_type: CoseKey::OKP_KEY_TYPE,
|
||||||
curve: CoseKey::ED25519_CURVE,
|
curve: CoseKey::ED25519_CURVE,
|
||||||
algorithm: EDDSA_ALGORITHM,
|
algorithm: EDDSA_ALGORITHM,
|
||||||
|
dilithium_bytes: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<hybrid::PubKey> for CoseKey {
|
||||||
|
fn from(pk: hybrid::PubKey) -> Self {
|
||||||
|
let mut ecdsa_x_bytes = [0; ecdsa::NBYTES];
|
||||||
|
let mut ecdsa_y_bytes = [0; ecdsa::NBYTES];
|
||||||
|
pk.ecdsa_pk
|
||||||
|
.to_coordinates(&mut ecdsa_x_bytes, &mut ecdsa_y_bytes);
|
||||||
|
let mut dilithium_bytes = vec![0; dilithium::params::PK_SIZE_PACKED];
|
||||||
|
let bytes_ref = array_mut_ref!(dilithium_bytes, 0, dilithium::params::PK_SIZE_PACKED);
|
||||||
|
pk.dilithium_pk.to_bytes(bytes_ref);
|
||||||
|
CoseKey {
|
||||||
|
x_bytes: ecdsa_x_bytes,
|
||||||
|
y_bytes: ecdsa_y_bytes,
|
||||||
|
key_type: CoseKey::EC2_KEY_TYPE,
|
||||||
|
curve: CoseKey::P_256_CURVE,
|
||||||
|
algorithm: ES256_ALGORITHM,
|
||||||
|
dilithium_bytes: Some(dilithium_bytes),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -883,6 +922,7 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
|||||||
algorithm,
|
algorithm,
|
||||||
key_type,
|
key_type,
|
||||||
curve,
|
curve,
|
||||||
|
dilithium_bytes,
|
||||||
} = cose_key;
|
} = cose_key;
|
||||||
|
|
||||||
// Since algorithm can be used for different COSE key types, we check
|
// Since algorithm can be used for different COSE key types, we check
|
||||||
@@ -895,6 +935,9 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
|||||||
if key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
|
if key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
}
|
}
|
||||||
|
if dilithium_bytes.is_some() {
|
||||||
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
|
}
|
||||||
ecdh::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
ecdh::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
||||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
}
|
}
|
||||||
@@ -910,6 +953,7 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
|||||||
algorithm,
|
algorithm,
|
||||||
key_type,
|
key_type,
|
||||||
curve,
|
curve,
|
||||||
|
dilithium_bytes,
|
||||||
} = cose_key;
|
} = cose_key;
|
||||||
|
|
||||||
if algorithm != ES256_ALGORITHM
|
if algorithm != ES256_ALGORITHM
|
||||||
@@ -918,55 +962,11 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
|||||||
{
|
{
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||||
}
|
}
|
||||||
ecdsa::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
if dilithium_bytes.is_some() {
|
||||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data structure for receiving a signature.
|
|
||||||
///
|
|
||||||
/// See https://datatracker.ietf.org/doc/html/rfc8152#appendix-C.1.1 for reference.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct CoseSignature {
|
|
||||||
pub algorithm: SignatureAlgorithm,
|
|
||||||
pub bytes: [u8; ecdsa::Signature::BYTES_LENGTH],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<cbor::Value> for CoseSignature {
|
|
||||||
type Error = Ctap2StatusCode;
|
|
||||||
|
|
||||||
fn try_from(cbor_value: cbor::Value) -> Result<Self, Ctap2StatusCode> {
|
|
||||||
destructure_cbor_map! {
|
|
||||||
let {
|
|
||||||
"alg" => algorithm,
|
|
||||||
"signature" => bytes,
|
|
||||||
} = extract_map(cbor_value)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let algorithm = SignatureAlgorithm::try_from(ok_or_missing(algorithm)?)?;
|
|
||||||
let bytes = extract_byte_string(ok_or_missing(bytes)?)?;
|
|
||||||
if bytes.len() != ecdsa::Signature::BYTES_LENGTH {
|
|
||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||||
}
|
}
|
||||||
|
ecdsa::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
||||||
Ok(CoseSignature {
|
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||||
algorithm,
|
|
||||||
bytes: *array_ref![bytes.as_slice(), 0, ecdsa::Signature::BYTES_LENGTH],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<CoseSignature> for ecdsa::Signature {
|
|
||||||
type Error = Ctap2StatusCode;
|
|
||||||
|
|
||||||
fn try_from(cose_signature: CoseSignature) -> Result<Self, Ctap2StatusCode> {
|
|
||||||
match cose_signature.algorithm {
|
|
||||||
SignatureAlgorithm::Es256 => ecdsa::Signature::from_bytes(&cose_signature.bytes)
|
|
||||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER),
|
|
||||||
#[cfg(feature = "ed25519")]
|
|
||||||
SignatureAlgorithm::Eddsa => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
|
||||||
SignatureAlgorithm::Unknown => Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1300,10 +1300,9 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
use cbor::{
|
use cbor::{
|
||||||
cbor_array, cbor_bool, cbor_bytes, cbor_bytes_lit, cbor_false, cbor_int, cbor_null,
|
cbor_array, cbor_bool, cbor_bytes_lit, cbor_false, cbor_int, cbor_map, cbor_null,
|
||||||
cbor_text, cbor_unsigned,
|
cbor_text, cbor_unsigned,
|
||||||
};
|
};
|
||||||
use crypto::sha256::Sha256;
|
|
||||||
use rng256::Rng256;
|
use rng256::Rng256;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -2014,64 +2013,6 @@ mod test {
|
|||||||
assert_eq!(cose_key.algorithm, ES256_ALGORITHM);
|
assert_eq!(cose_key.algorithm, ES256_ALGORITHM);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_from_into_cose_signature() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
|
|
||||||
let dummy_signature = sk.sign_rfc6979::<Sha256>(&[]);
|
|
||||||
let mut bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
|
||||||
dummy_signature.to_bytes(&mut bytes);
|
|
||||||
let cbor_value = cbor_map! {
|
|
||||||
"alg" => ES256_ALGORITHM,
|
|
||||||
"signature" => bytes,
|
|
||||||
};
|
|
||||||
let cose_signature = CoseSignature::try_from(cbor_value).unwrap();
|
|
||||||
let created_signature = crypto::ecdsa::Signature::try_from(cose_signature).unwrap();
|
|
||||||
let mut created_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
|
||||||
created_signature.to_bytes(&mut created_bytes);
|
|
||||||
assert_eq!(bytes[..], created_bytes[..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cose_signature_wrong_algorithm() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
let sk = crypto::ecdsa::SecKey::gensk(env.rng());
|
|
||||||
let dummy_signature = sk.sign_rfc6979::<Sha256>(&[]);
|
|
||||||
let mut bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
|
||||||
dummy_signature.to_bytes(&mut bytes);
|
|
||||||
let cbor_value = cbor_map! {
|
|
||||||
"alg" => -1, // unused algorithm
|
|
||||||
"signature" => bytes,
|
|
||||||
};
|
|
||||||
let cose_signature = CoseSignature::try_from(cbor_value).unwrap();
|
|
||||||
let created_signature = crypto::ecdsa::Signature::try_from(cose_signature);
|
|
||||||
// Can not compare directly, since ecdsa::Signature does not implement Debug.
|
|
||||||
assert_eq!(
|
|
||||||
created_signature.err(),
|
|
||||||
Some(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cose_signature_wrong_signature_length() {
|
|
||||||
let cbor_value = cbor_map! {
|
|
||||||
"alg" => ES256_ALGORITHM,
|
|
||||||
"signature" => [0; ecdsa::Signature::BYTES_LENGTH - 1],
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
CoseSignature::try_from(cbor_value),
|
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
|
||||||
);
|
|
||||||
let cbor_value = cbor_map! {
|
|
||||||
"alg" => ES256_ALGORITHM,
|
|
||||||
"signature" => [0; ecdsa::Signature::BYTES_LENGTH + 1],
|
|
||||||
};
|
|
||||||
assert_eq!(
|
|
||||||
CoseSignature::try_from(cbor_value),
|
|
||||||
Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_from_pin_uv_auth_protocol() {
|
fn test_from_pin_uv_auth_protocol() {
|
||||||
let cbor_protocol: cbor::Value = cbor_int!(0x01);
|
let cbor_protocol: cbor::Value = cbor_int!(0x01);
|
||||||
@@ -2240,7 +2181,6 @@ mod test {
|
|||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -2303,16 +2243,6 @@ mod test {
|
|||||||
..credential
|
..credential
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
|
|
||||||
Ok(credential.clone())
|
|
||||||
);
|
|
||||||
|
|
||||||
let credential = PublicKeyCredentialSource {
|
|
||||||
slot_id: Some(1),
|
|
||||||
..credential
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
|
PublicKeyCredentialSource::try_from(cbor::Value::from(credential.clone())),
|
||||||
Ok(credential)
|
Ok(credential)
|
||||||
@@ -2337,7 +2267,6 @@ mod test {
|
|||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let source_cbor = cbor_map! {
|
let source_cbor = cbor_map! {
|
||||||
@@ -2370,7 +2299,6 @@ mod test {
|
|||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let source_cbor = cbor_map! {
|
let source_cbor = cbor_map! {
|
||||||
|
|||||||
@@ -14,9 +14,6 @@
|
|||||||
|
|
||||||
pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
pub const ATTESTATION_PRIVATE_KEY_LENGTH: usize = 32;
|
||||||
pub const AAGUID_LENGTH: usize = 16;
|
pub const AAGUID_LENGTH: usize = 16;
|
||||||
pub const UPGRADE_PUBLIC_KEY_LENGTH: usize = 77;
|
|
||||||
|
|
||||||
pub const AAGUID: &[u8; AAGUID_LENGTH] =
|
pub const AAGUID: &[u8; AAGUID_LENGTH] =
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
|
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
|
||||||
pub const UPGRADE_PUBLIC_KEY: &[u8; UPGRADE_PUBLIC_KEY_LENGTH] =
|
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey_cbor.bin"));
|
|
||||||
|
|||||||
@@ -87,9 +87,7 @@ impl LargeBlobs {
|
|||||||
if offset != self.expected_next_offset {
|
if offset != self.expected_next_offset {
|
||||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ);
|
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_SEQ);
|
||||||
}
|
}
|
||||||
// We only check whether slot 0 has a PIN here because if multi-PIN is enabled,
|
if storage::pin_hash(env)?.is_some() || storage::has_always_uv(env)? {
|
||||||
// has_always_uv will always be true and the condition will always pass.
|
|
||||||
if storage::pin_hash(env, 0)?.is_some() || storage::has_always_uv(env)? {
|
|
||||||
let pin_uv_auth_param =
|
let pin_uv_auth_param =
|
||||||
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
pin_uv_auth_param.ok_or(Ctap2StatusCode::CTAP2_ERR_PUAT_REQUIRED)?;
|
||||||
let pin_uv_auth_protocol =
|
let pin_uv_auth_protocol =
|
||||||
@@ -149,13 +147,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
let large_blob = vec![
|
let large_blob = vec![
|
||||||
@@ -185,13 +178,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 200;
|
const BLOB_LEN: usize = 200;
|
||||||
@@ -252,13 +240,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 200;
|
const BLOB_LEN: usize = 200;
|
||||||
@@ -303,13 +286,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 200;
|
const BLOB_LEN: usize = 200;
|
||||||
@@ -354,13 +332,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
let large_blobs_params = AuthenticatorLargeBlobsParameters {
|
||||||
@@ -382,13 +355,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, PinUvAuthProtocol::V1);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
PinUvAuthProtocol::V1,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 20;
|
const BLOB_LEN: usize = 20;
|
||||||
@@ -415,13 +383,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
let key_agreement_key = crypto::ecdh::SecKey::gensk(env.rng());
|
||||||
let pin_uv_auth_token = [0x55; 32];
|
let pin_uv_auth_token = [0x55; 32];
|
||||||
let mut client_pin = ClientPin::new_test(
|
let mut client_pin =
|
||||||
&mut env,
|
ClientPin::new_test(key_agreement_key, pin_uv_auth_token, pin_uv_auth_protocol);
|
||||||
0,
|
|
||||||
key_agreement_key,
|
|
||||||
pin_uv_auth_token,
|
|
||||||
pin_uv_auth_protocol,
|
|
||||||
);
|
|
||||||
let mut large_blobs = LargeBlobs::new();
|
let mut large_blobs = LargeBlobs::new();
|
||||||
|
|
||||||
const BLOB_LEN: usize = 20;
|
const BLOB_LEN: usize = 20;
|
||||||
@@ -429,7 +392,7 @@ mod test {
|
|||||||
let mut large_blob = vec![0x1B; DATA_LEN];
|
let mut large_blob = vec![0x1B; DATA_LEN];
|
||||||
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
large_blob.extend_from_slice(&Sha256::hash(&large_blob[..])[..TRUNCATED_HASH_LEN]);
|
||||||
|
|
||||||
storage::set_pin(&mut env, 0, &[0u8; 16], 4).unwrap();
|
storage::set_pin(&mut env, &[0u8; 16], 4).unwrap();
|
||||||
let mut large_blob_data = vec![0xFF; 32];
|
let mut large_blob_data = vec![0xFF; 32];
|
||||||
// Command constant and offset bytes.
|
// Command constant and offset bytes.
|
||||||
large_blob_data.extend(&[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
large_blob_data.extend(&[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||||
|
|||||||
1173
src/ctap/mod.rs
1173
src/ctap/mod.rs
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ use alloc::vec::Vec;
|
|||||||
use arrayref::array_ref;
|
use arrayref::array_ref;
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use persistent_store::{concat, fragment};
|
use persistent_store::{fragment, StoreUpdate};
|
||||||
use rng256::Rng256;
|
use rng256::Rng256;
|
||||||
use sk_cbor::cbor_array_vec;
|
use sk_cbor::cbor_array_vec;
|
||||||
|
|
||||||
@@ -237,34 +237,9 @@ pub fn new_creation_order(env: &mut impl Env) -> Result<u64, Ctap2StatusCode> {
|
|||||||
Ok(max.unwrap_or(0).wrapping_add(1))
|
Ok(max.unwrap_or(0).wrapping_add(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_and_get_key_for_slot(
|
|
||||||
env: &mut impl Env,
|
|
||||||
slot_id: usize,
|
|
||||||
first_key: usize,
|
|
||||||
key_array: core::ops::Range<usize>,
|
|
||||||
) -> Result<usize, Ctap2StatusCode> {
|
|
||||||
if slot_id >= env.customization().slot_count() {
|
|
||||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
|
||||||
}
|
|
||||||
Ok(if slot_id == 0 {
|
|
||||||
first_key
|
|
||||||
} else {
|
|
||||||
key_array.start + slot_id - 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the global signature counter.
|
/// Returns the global signature counter.
|
||||||
pub fn global_signature_counter(
|
pub fn global_signature_counter(env: &mut impl Env) -> Result<u32, Ctap2StatusCode> {
|
||||||
env: &mut impl Env,
|
match env.store().find(key::GLOBAL_SIGNATURE_COUNTER)? {
|
||||||
slot_id: usize,
|
|
||||||
) -> Result<u32, Ctap2StatusCode> {
|
|
||||||
let key = check_and_get_key_for_slot(
|
|
||||||
env,
|
|
||||||
slot_id,
|
|
||||||
key::FIRST_GLOBAL_SIGNATURE_COUNTER,
|
|
||||||
key::GLOBAL_SIGNATURE_COUNTER,
|
|
||||||
)?;
|
|
||||||
match env.store().find(key)? {
|
|
||||||
None => Ok(INITIAL_SIGNATURE_COUNTER),
|
None => Ok(INITIAL_SIGNATURE_COUNTER),
|
||||||
Some(value) if value.len() == 4 => Ok(u32::from_ne_bytes(*array_ref!(&value, 0, 4))),
|
Some(value) if value.len() == 4 => Ok(u32::from_ne_bytes(*array_ref!(&value, 0, 4))),
|
||||||
Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
Some(_) => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||||
@@ -274,19 +249,13 @@ pub fn global_signature_counter(
|
|||||||
/// Increments the global signature counter.
|
/// Increments the global signature counter.
|
||||||
pub fn incr_global_signature_counter(
|
pub fn incr_global_signature_counter(
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
slot_id: usize,
|
|
||||||
increment: u32,
|
increment: u32,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
let key = check_and_get_key_for_slot(
|
let old_value = global_signature_counter(env)?;
|
||||||
env,
|
|
||||||
slot_id,
|
|
||||||
key::FIRST_GLOBAL_SIGNATURE_COUNTER,
|
|
||||||
key::GLOBAL_SIGNATURE_COUNTER,
|
|
||||||
)?;
|
|
||||||
let old_value = global_signature_counter(env, slot_id)?;
|
|
||||||
// In hopes that servers handle the wrapping gracefully.
|
// In hopes that servers handle the wrapping gracefully.
|
||||||
let new_value = old_value.wrapping_add(increment);
|
let new_value = old_value.wrapping_add(increment);
|
||||||
env.store().insert(key, &new_value.to_ne_bytes())?;
|
env.store()
|
||||||
|
.insert(key::GLOBAL_SIGNATURE_COUNTER, &new_value.to_ne_bytes())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,22 +273,9 @@ pub fn cred_random_secret(env: &mut impl Env, has_uv: bool) -> Result<[u8; 32],
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reads the PIN properties and wraps them into PinProperties.
|
/// Reads the PIN properties and wraps them into PinProperties.
|
||||||
fn pin_properties(
|
fn pin_properties(env: &mut impl Env) -> Result<Option<PinProperties>, Ctap2StatusCode> {
|
||||||
env: &mut impl Env,
|
let pin_properties = match env.store().find(key::PIN_PROPERTIES)? {
|
||||||
slot_id: usize,
|
None => return Ok(None),
|
||||||
) -> Result<Option<PinProperties>, Ctap2StatusCode> {
|
|
||||||
let pin_properties = match concat::read(env.store(), key::PIN_PROPERTIES, slot_id as u8)? {
|
|
||||||
None => {
|
|
||||||
// Backward compatibility: old implementation where there is only 1 PIN slot
|
|
||||||
// uses the entry with key `FIRST_PIN_PROPERTIES`.
|
|
||||||
if slot_id != 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
match env.store().find(key::FIRST_PIN_PROPERTIES)? {
|
|
||||||
None => return Ok(None),
|
|
||||||
Some(pin_properties) => pin_properties,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(pin_properties) => pin_properties,
|
Some(pin_properties) => pin_properties,
|
||||||
};
|
};
|
||||||
const PROPERTIES_LENGTH: usize = PIN_AUTH_LENGTH + 1;
|
const PROPERTIES_LENGTH: usize = PIN_AUTH_LENGTH + 1;
|
||||||
@@ -332,30 +288,14 @@ fn pin_properties(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if PIN is set for at least one slot.
|
|
||||||
pub fn has_pin(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
|
||||||
for slot_id in 0..env.customization().slot_count() {
|
|
||||||
if pin_hash(env, slot_id)?.is_some() {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the PIN hash if defined.
|
/// Returns the PIN hash if defined.
|
||||||
pub fn pin_hash(
|
pub fn pin_hash(env: &mut impl Env) -> Result<Option<[u8; PIN_AUTH_LENGTH]>, Ctap2StatusCode> {
|
||||||
env: &mut impl Env,
|
Ok(pin_properties(env)?.map(|p| p.hash))
|
||||||
slot_id: usize,
|
|
||||||
) -> Result<Option<[u8; PIN_AUTH_LENGTH]>, Ctap2StatusCode> {
|
|
||||||
Ok(pin_properties(env, slot_id)?.map(|p| p.hash))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the length of the currently set PIN if defined.
|
/// Returns the length of the currently set PIN if defined.
|
||||||
pub fn pin_code_point_length(
|
pub fn pin_code_point_length(env: &mut impl Env) -> Result<Option<u8>, Ctap2StatusCode> {
|
||||||
env: &mut impl Env,
|
Ok(pin_properties(env)?.map(|p| p.code_point_length))
|
||||||
slot_id: usize,
|
|
||||||
) -> Result<Option<u8>, Ctap2StatusCode> {
|
|
||||||
Ok(pin_properties(env, slot_id)?.map(|p| p.code_point_length))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the PIN hash and length.
|
/// Sets the PIN hash and length.
|
||||||
@@ -363,32 +303,26 @@ pub fn pin_code_point_length(
|
|||||||
/// If it was already defined, it is updated.
|
/// If it was already defined, it is updated.
|
||||||
pub fn set_pin(
|
pub fn set_pin(
|
||||||
env: &mut impl Env,
|
env: &mut impl Env,
|
||||||
slot_id: usize,
|
|
||||||
pin_hash: &[u8; PIN_AUTH_LENGTH],
|
pin_hash: &[u8; PIN_AUTH_LENGTH],
|
||||||
pin_code_point_length: u8,
|
pin_code_point_length: u8,
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
) -> Result<(), Ctap2StatusCode> {
|
||||||
concat::delete(env.store(), key::FORCE_PIN_CHANGE, slot_id as u8)?;
|
|
||||||
if slot_id == 0 {
|
|
||||||
// Backward compatibility: data might be stored in these entries for slot 0 as well.
|
|
||||||
env.store().remove(key::FIRST_FORCE_PIN_CHANGE)?;
|
|
||||||
env.store().remove(key::FIRST_PIN_PROPERTIES)?;
|
|
||||||
}
|
|
||||||
let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH];
|
let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH];
|
||||||
pin_properties[0] = pin_code_point_length;
|
pin_properties[0] = pin_code_point_length;
|
||||||
pin_properties[1..].clone_from_slice(pin_hash);
|
pin_properties[1..].clone_from_slice(pin_hash);
|
||||||
concat::write(
|
Ok(env.store().transaction(&[
|
||||||
env.store(),
|
StoreUpdate::Insert {
|
||||||
key::PIN_PROPERTIES,
|
key: key::PIN_PROPERTIES,
|
||||||
slot_id as u8,
|
value: &pin_properties[..],
|
||||||
&pin_properties[..],
|
},
|
||||||
)?;
|
StoreUpdate::Remove {
|
||||||
Ok(())
|
key: key::FORCE_PIN_CHANGE,
|
||||||
|
},
|
||||||
|
])?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of remaining PIN retries.
|
/// Returns the number of remaining PIN retries.
|
||||||
pub fn pin_retries(env: &mut impl Env, slot_id: usize) -> Result<u8, Ctap2StatusCode> {
|
pub fn pin_retries(env: &mut impl Env) -> Result<u8, Ctap2StatusCode> {
|
||||||
let key = check_and_get_key_for_slot(env, slot_id, key::FIRST_PIN_RETRIES, key::PIN_RETRIES)?;
|
match env.store().find(key::PIN_RETRIES)? {
|
||||||
match env.store().find(key)? {
|
|
||||||
None => Ok(env.customization().max_pin_retries()),
|
None => Ok(env.customization().max_pin_retries()),
|
||||||
Some(value) if value.len() == 1 => Ok(value[0]),
|
Some(value) if value.len() == 1 => Ok(value[0]),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||||
@@ -396,20 +330,18 @@ pub fn pin_retries(env: &mut impl Env, slot_id: usize) -> Result<u8, Ctap2Status
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decrements the number of remaining PIN retries.
|
/// Decrements the number of remaining PIN retries.
|
||||||
pub fn decr_pin_retries(env: &mut impl Env, slot_id: usize) -> Result<(), Ctap2StatusCode> {
|
pub fn decr_pin_retries(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
||||||
let key = check_and_get_key_for_slot(env, slot_id, key::FIRST_PIN_RETRIES, key::PIN_RETRIES)?;
|
let old_value = pin_retries(env)?;
|
||||||
let old_value = pin_retries(env, slot_id)?;
|
|
||||||
let new_value = old_value.saturating_sub(1);
|
let new_value = old_value.saturating_sub(1);
|
||||||
if new_value != old_value {
|
if new_value != old_value {
|
||||||
env.store().insert(key, &[new_value])?;
|
env.store().insert(key::PIN_RETRIES, &[new_value])?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets the number of remaining PIN retries.
|
/// Resets the number of remaining PIN retries.
|
||||||
pub fn reset_pin_retries(env: &mut impl Env, slot_id: usize) -> Result<(), Ctap2StatusCode> {
|
pub fn reset_pin_retries(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
||||||
let key = check_and_get_key_for_slot(env, slot_id, key::FIRST_PIN_RETRIES, key::PIN_RETRIES)?;
|
Ok(env.store().remove(key::PIN_RETRIES)?)
|
||||||
Ok(env.store().remove(key)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the minimum PIN length.
|
/// Returns the minimum PIN length.
|
||||||
@@ -535,34 +467,17 @@ pub fn reset(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the PIN needs to be changed before its next usage.
|
/// Returns whether the PIN needs to be changed before its next usage.
|
||||||
pub fn has_force_pin_change(env: &mut impl Env, slot_id: usize) -> Result<bool, Ctap2StatusCode> {
|
pub fn has_force_pin_change(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
||||||
match concat::read(env.store(), key::FORCE_PIN_CHANGE, slot_id as u8)? {
|
match env.store().find(key::FORCE_PIN_CHANGE)? {
|
||||||
None => {
|
None => Ok(false),
|
||||||
if slot_id != 0 {
|
|
||||||
Ok(false)
|
|
||||||
} else {
|
|
||||||
// Backward compatibility: old implementation where there is only 1 PIN slot
|
|
||||||
// uses the entry with key `FIRST_FORCE_PIN_CHANGE`.
|
|
||||||
match env.store().find(key::FIRST_FORCE_PIN_CHANGE)? {
|
|
||||||
None => Ok(false),
|
|
||||||
Some(value) if value.is_empty() => Ok(true),
|
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(value) if value.is_empty() => Ok(true),
|
Some(value) if value.is_empty() => Ok(true),
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marks the PIN as outdated with respect to the new PIN policy.
|
/// Marks the PIN as outdated with respect to the new PIN policy.
|
||||||
pub fn force_pin_change(env: &mut impl Env, slot_id: usize) -> Result<(), Ctap2StatusCode> {
|
pub fn force_pin_change(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
||||||
Ok(concat::write(
|
Ok(env.store().insert(key::FORCE_PIN_CHANGE, &[])?)
|
||||||
env.store(),
|
|
||||||
key::FORCE_PIN_CHANGE,
|
|
||||||
slot_id as u8,
|
|
||||||
&[],
|
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether enterprise attestation is enabled.
|
/// Returns whether enterprise attestation is enabled.
|
||||||
@@ -613,25 +528,6 @@ pub fn toggle_always_uv(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether multi-PIN is enabled.
|
|
||||||
pub fn has_multi_pin(env: &mut impl Env) -> Result<bool, Ctap2StatusCode> {
|
|
||||||
match env.store().find(key::MULTI_PIN)? {
|
|
||||||
None => Ok(false),
|
|
||||||
Some(value) if value.is_empty() => Ok(true),
|
|
||||||
_ => Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Call this in config_commands after the whole multi-PIN feature is ready.
|
|
||||||
// Before that, this function only be used for testing purpose.
|
|
||||||
/// Enables multi-PIN, when disabled.
|
|
||||||
pub fn _enable_multi_pin_for_test(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
|
||||||
if !has_multi_pin(env)? {
|
|
||||||
env.store().insert(key::MULTI_PIN, &[])?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<persistent_store::StoreError> for Ctap2StatusCode {
|
impl From<persistent_store::StoreError> for Ctap2StatusCode {
|
||||||
fn from(error: persistent_store::StoreError) -> Ctap2StatusCode {
|
fn from(error: persistent_store::StoreError) -> Ctap2StatusCode {
|
||||||
use persistent_store::StoreError;
|
use persistent_store::StoreError;
|
||||||
@@ -755,7 +651,6 @@ mod test {
|
|||||||
CredentialProtectionPolicy, PublicKeyCredentialSource, PublicKeyCredentialType,
|
CredentialProtectionPolicy, PublicKeyCredentialSource, PublicKeyCredentialType,
|
||||||
};
|
};
|
||||||
use crate::env::test::TestEnv;
|
use crate::env::test::TestEnv;
|
||||||
use persistent_store::StoreUpdate;
|
|
||||||
use rng256::Rng256;
|
use rng256::Rng256;
|
||||||
|
|
||||||
fn create_credential_source(
|
fn create_credential_source(
|
||||||
@@ -777,7 +672,6 @@ mod test {
|
|||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -974,7 +868,6 @@ mod test {
|
|||||||
user_icon: None,
|
user_icon: None,
|
||||||
cred_blob: None,
|
cred_blob: None,
|
||||||
large_blob_key: None,
|
large_blob_key: None,
|
||||||
slot_id: None,
|
|
||||||
};
|
};
|
||||||
assert_eq!(found_credential, Some(expected_credential));
|
assert_eq!(found_credential, Some(expected_credential));
|
||||||
}
|
}
|
||||||
@@ -1006,8 +899,8 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|
||||||
// Pin hash is initially not set.
|
// Pin hash is initially not set.
|
||||||
assert!(pin_hash(&mut env, 0).unwrap().is_none());
|
assert!(pin_hash(&mut env).unwrap().is_none());
|
||||||
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
|
assert!(pin_code_point_length(&mut env).unwrap().is_none());
|
||||||
|
|
||||||
// Setting the pin sets the pin hash.
|
// Setting the pin sets the pin hash.
|
||||||
let random_data = env.rng().gen_uniform_u8x32();
|
let random_data = env.rng().gen_uniform_u8x32();
|
||||||
@@ -1016,131 +909,17 @@ mod test {
|
|||||||
let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH);
|
let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH);
|
||||||
let pin_length_1 = 4;
|
let pin_length_1 = 4;
|
||||||
let pin_length_2 = 63;
|
let pin_length_2 = 63;
|
||||||
set_pin(&mut env, 0, &pin_hash_1, pin_length_1).unwrap();
|
set_pin(&mut env, &pin_hash_1, pin_length_1).unwrap();
|
||||||
assert_eq!(pin_hash(&mut env, 0).unwrap(), Some(pin_hash_1));
|
assert_eq!(pin_hash(&mut env).unwrap(), Some(pin_hash_1));
|
||||||
assert_eq!(
|
assert_eq!(pin_code_point_length(&mut env).unwrap(), Some(pin_length_1));
|
||||||
pin_code_point_length(&mut env, 0).unwrap(),
|
set_pin(&mut env, &pin_hash_2, pin_length_2).unwrap();
|
||||||
Some(pin_length_1)
|
assert_eq!(pin_hash(&mut env).unwrap(), Some(pin_hash_2));
|
||||||
);
|
assert_eq!(pin_code_point_length(&mut env).unwrap(), Some(pin_length_2));
|
||||||
set_pin(&mut env, 0, &pin_hash_2, pin_length_2).unwrap();
|
|
||||||
assert_eq!(pin_hash(&mut env, 0).unwrap(), Some(pin_hash_2));
|
|
||||||
assert_eq!(
|
|
||||||
pin_code_point_length(&mut env, 0).unwrap(),
|
|
||||||
Some(pin_length_2)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Resetting the storage resets the pin hash.
|
// Resetting the storage resets the pin hash.
|
||||||
reset(&mut env).unwrap();
|
reset(&mut env).unwrap();
|
||||||
assert!(pin_hash(&mut env, 0).unwrap().is_none());
|
assert!(pin_hash(&mut env).unwrap().is_none());
|
||||||
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
|
assert!(pin_code_point_length(&mut env).unwrap().is_none());
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pin_hash_and_length_multi_pin() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
for i in 0..env.customization().slot_count() {
|
|
||||||
// Pin hash is initially not set.
|
|
||||||
assert!(pin_hash(&mut env, i).unwrap().is_none());
|
|
||||||
assert!(pin_code_point_length(&mut env, i).unwrap().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setting the pin sets the pin hash.
|
|
||||||
let random_data = env.rng().gen_uniform_u8x32();
|
|
||||||
assert_eq!(random_data.len(), 2 * PIN_AUTH_LENGTH);
|
|
||||||
let pin_hash_1 = *array_ref!(random_data, 0, PIN_AUTH_LENGTH);
|
|
||||||
let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH);
|
|
||||||
let pin_length_1 = 4;
|
|
||||||
let pin_length_2 = 63;
|
|
||||||
set_pin(&mut env, 1, &pin_hash_1, pin_length_1).unwrap();
|
|
||||||
assert_eq!(pin_hash(&mut env, 1).unwrap(), Some(pin_hash_1));
|
|
||||||
assert_eq!(
|
|
||||||
pin_code_point_length(&mut env, 1).unwrap(),
|
|
||||||
Some(pin_length_1)
|
|
||||||
);
|
|
||||||
// Other slots aren't affected.
|
|
||||||
assert!(pin_hash(&mut env, 0).unwrap().is_none());
|
|
||||||
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
|
|
||||||
|
|
||||||
set_pin(&mut env, 1, &pin_hash_2, pin_length_2).unwrap();
|
|
||||||
assert_eq!(pin_hash(&mut env, 1).unwrap(), Some(pin_hash_2));
|
|
||||||
assert_eq!(
|
|
||||||
pin_code_point_length(&mut env, 1).unwrap(),
|
|
||||||
Some(pin_length_2)
|
|
||||||
);
|
|
||||||
// Other slots aren't affected.
|
|
||||||
assert!(pin_hash(&mut env, 0).unwrap().is_none());
|
|
||||||
assert!(pin_code_point_length(&mut env, 0).unwrap().is_none());
|
|
||||||
|
|
||||||
// Set PIN for multiple slots is supported.
|
|
||||||
set_pin(&mut env, 2, &pin_hash_1, pin_length_1).unwrap();
|
|
||||||
assert_eq!(pin_hash(&mut env, 2).unwrap(), Some(pin_hash_1));
|
|
||||||
assert_eq!(
|
|
||||||
pin_code_point_length(&mut env, 2).unwrap(),
|
|
||||||
Some(pin_length_1)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Resetting the storage resets the pin hash.
|
|
||||||
reset(&mut env).unwrap();
|
|
||||||
for i in 0..env.customization().slot_count() {
|
|
||||||
assert!(pin_hash(&mut env, i).unwrap().is_none());
|
|
||||||
assert!(pin_code_point_length(&mut env, i).unwrap().is_none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_pin_legacy(
|
|
||||||
env: &mut impl Env,
|
|
||||||
pin_hash: &[u8; PIN_AUTH_LENGTH],
|
|
||||||
pin_code_point_length: u8,
|
|
||||||
) -> Result<(), Ctap2StatusCode> {
|
|
||||||
let mut pin_properties = [0; 1 + PIN_AUTH_LENGTH];
|
|
||||||
pin_properties[0] = pin_code_point_length;
|
|
||||||
pin_properties[1..].clone_from_slice(pin_hash);
|
|
||||||
Ok(env.store().transaction(&[
|
|
||||||
StoreUpdate::Insert {
|
|
||||||
key: key::FIRST_PIN_PROPERTIES,
|
|
||||||
value: &pin_properties[..],
|
|
||||||
},
|
|
||||||
StoreUpdate::Remove {
|
|
||||||
key: key::FIRST_FORCE_PIN_CHANGE,
|
|
||||||
},
|
|
||||||
])?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pin_hash_and_length_backward_compat() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
// Setting the pin sets the pin hash.
|
|
||||||
let random_data = env.rng().gen_uniform_u8x32();
|
|
||||||
assert_eq!(random_data.len(), 2 * PIN_AUTH_LENGTH);
|
|
||||||
|
|
||||||
let pin_hash_1 = *array_ref!(random_data, 0, PIN_AUTH_LENGTH);
|
|
||||||
let pin_hash_2 = *array_ref!(random_data, PIN_AUTH_LENGTH, PIN_AUTH_LENGTH);
|
|
||||||
let pin_length_1 = 4;
|
|
||||||
let pin_length_2 = 63;
|
|
||||||
|
|
||||||
assert!(set_pin_legacy(&mut env, &pin_hash_1, pin_length_1).is_ok());
|
|
||||||
|
|
||||||
// Should fallback to read from legacy storage location successfully.
|
|
||||||
assert_eq!(pin_hash(&mut env, 0).unwrap(), Some(pin_hash_1));
|
|
||||||
assert_eq!(
|
|
||||||
pin_code_point_length(&mut env, 0).unwrap(),
|
|
||||||
Some(pin_length_1)
|
|
||||||
);
|
|
||||||
// Fallback logic should only apply to slot 0.
|
|
||||||
assert!(pin_hash(&mut env, 1).unwrap().is_none());
|
|
||||||
assert!(pin_code_point_length(&mut env, 1).unwrap().is_none());
|
|
||||||
|
|
||||||
// Setting PIN again should use the new storage location, and erase the old one.
|
|
||||||
set_pin(&mut env, 0, &pin_hash_2, pin_length_2).unwrap();
|
|
||||||
assert_eq!(pin_hash(&mut env, 0).unwrap(), Some(pin_hash_2));
|
|
||||||
assert_eq!(
|
|
||||||
pin_code_point_length(&mut env, 0).unwrap(),
|
|
||||||
Some(pin_length_2)
|
|
||||||
);
|
|
||||||
assert_eq!(env.store().find(key::FIRST_PIN_PROPERTIES), Ok(None));
|
|
||||||
assert_eq!(env.store().find(key::FIRST_FORCE_PIN_CHANGE), Ok(None));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1149,76 +928,24 @@ mod test {
|
|||||||
|
|
||||||
// The pin retries is initially at the maximum.
|
// The pin retries is initially at the maximum.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pin_retries(&mut env, 0),
|
pin_retries(&mut env),
|
||||||
Ok(env.customization().max_pin_retries())
|
Ok(env.customization().max_pin_retries())
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decrementing the pin retries decrements the pin retries.
|
// Decrementing the pin retries decrements the pin retries.
|
||||||
for retries in (0..env.customization().max_pin_retries()).rev() {
|
for retries in (0..env.customization().max_pin_retries()).rev() {
|
||||||
decr_pin_retries(&mut env, 0).unwrap();
|
decr_pin_retries(&mut env).unwrap();
|
||||||
assert_eq!(pin_retries(&mut env, 0), Ok(retries));
|
assert_eq!(pin_retries(&mut env), Ok(retries));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrementing the pin retries after zero does not modify the pin retries.
|
// Decrementing the pin retries after zero does not modify the pin retries.
|
||||||
decr_pin_retries(&mut env, 0).unwrap();
|
decr_pin_retries(&mut env).unwrap();
|
||||||
assert_eq!(pin_retries(&mut env, 0), Ok(0));
|
assert_eq!(pin_retries(&mut env), Ok(0));
|
||||||
|
|
||||||
// Resetting the pin retries resets the pin retries.
|
// Resetting the pin retries resets the pin retries.
|
||||||
reset_pin_retries(&mut env, 0).unwrap();
|
reset_pin_retries(&mut env).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
pin_retries(&mut env, 0),
|
pin_retries(&mut env),
|
||||||
Ok(env.customization().max_pin_retries())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_pin_retries_multi_pin() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
for i in 0..env.customization().slot_count() {
|
|
||||||
// The pin retries is initially at the maximum.
|
|
||||||
assert_eq!(
|
|
||||||
pin_retries(&mut env, i),
|
|
||||||
Ok(env.customization().max_pin_retries())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrementing the pin retries decrements the pin retries.
|
|
||||||
for retries in (0..env.customization().max_pin_retries()).rev() {
|
|
||||||
decr_pin_retries(&mut env, 1).unwrap();
|
|
||||||
assert_eq!(pin_retries(&mut env, 1), Ok(retries));
|
|
||||||
// Other slots shouldn't be affected.
|
|
||||||
assert_eq!(
|
|
||||||
pin_retries(&mut env, 2),
|
|
||||||
Ok(env.customization().max_pin_retries())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrementing the pin retries after zero does not modify the pin retries.
|
|
||||||
decr_pin_retries(&mut env, 1).unwrap();
|
|
||||||
assert_eq!(pin_retries(&mut env, 1), Ok(0));
|
|
||||||
|
|
||||||
// Operating on multiple slots is supported.
|
|
||||||
decr_pin_retries(&mut env, 2).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
pin_retries(&mut env, 2),
|
|
||||||
Ok(env.customization().max_pin_retries() - 1)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Resetting the pin retries resets the pin retries.
|
|
||||||
reset_pin_retries(&mut env, 1).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
pin_retries(&mut env, 1),
|
|
||||||
Ok(env.customization().max_pin_retries())
|
|
||||||
);
|
|
||||||
|
|
||||||
// Other slots shouldn't be affected.
|
|
||||||
assert_eq!(
|
|
||||||
pin_retries(&mut env, 2),
|
|
||||||
Ok(env.customization().max_pin_retries() - 1)
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
pin_retries(&mut env, 3),
|
|
||||||
Ok(env.customization().max_pin_retries())
|
Ok(env.customization().max_pin_retries())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1361,56 +1088,11 @@ mod test {
|
|||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|
||||||
let mut counter_value = 1;
|
let mut counter_value = 1;
|
||||||
assert_eq!(
|
assert_eq!(global_signature_counter(&mut env).unwrap(), counter_value);
|
||||||
global_signature_counter(&mut env, 0).unwrap(),
|
|
||||||
counter_value
|
|
||||||
);
|
|
||||||
for increment in 1..10 {
|
for increment in 1..10 {
|
||||||
assert!(incr_global_signature_counter(&mut env, 0, increment).is_ok());
|
assert!(incr_global_signature_counter(&mut env, increment).is_ok());
|
||||||
counter_value += increment;
|
counter_value += increment;
|
||||||
assert_eq!(
|
assert_eq!(global_signature_counter(&mut env).unwrap(), counter_value);
|
||||||
global_signature_counter(&mut env, 0).unwrap(),
|
|
||||||
counter_value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_global_signature_counter_multi_pin() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
let mut counter_value = 1;
|
|
||||||
for i in 0..env.customization().slot_count() {
|
|
||||||
assert_eq!(
|
|
||||||
global_signature_counter(&mut env, i).unwrap(),
|
|
||||||
counter_value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
for increment in 1..10 {
|
|
||||||
assert!(incr_global_signature_counter(&mut env, 1, increment).is_ok());
|
|
||||||
counter_value += increment;
|
|
||||||
assert_eq!(
|
|
||||||
global_signature_counter(&mut env, 1).unwrap(),
|
|
||||||
counter_value
|
|
||||||
);
|
|
||||||
// Other slots aren't affected.
|
|
||||||
assert_eq!(global_signature_counter(&mut env, 2).unwrap(), 1);
|
|
||||||
}
|
|
||||||
let counter_value_for_slot_1 = counter_value;
|
|
||||||
counter_value = 1;
|
|
||||||
|
|
||||||
for increment in 1..10 {
|
|
||||||
assert!(incr_global_signature_counter(&mut env, 2, increment).is_ok());
|
|
||||||
counter_value += increment;
|
|
||||||
assert_eq!(
|
|
||||||
global_signature_counter(&mut env, 2).unwrap(),
|
|
||||||
counter_value
|
|
||||||
);
|
|
||||||
// Other slots aren't affected.
|
|
||||||
assert_eq!(
|
|
||||||
global_signature_counter(&mut env, 1).unwrap(),
|
|
||||||
counter_value_for_slot_1
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1418,60 +1100,11 @@ mod test {
|
|||||||
fn test_force_pin_change() {
|
fn test_force_pin_change() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
|
|
||||||
assert!(!has_force_pin_change(&mut env, 0).unwrap());
|
assert!(!has_force_pin_change(&mut env).unwrap());
|
||||||
assert_eq!(force_pin_change(&mut env, 0), Ok(()));
|
assert_eq!(force_pin_change(&mut env), Ok(()));
|
||||||
assert!(has_force_pin_change(&mut env, 0).unwrap());
|
assert!(has_force_pin_change(&mut env).unwrap());
|
||||||
assert_eq!(set_pin(&mut env, 0, &[0x88; 16], 8), Ok(()));
|
assert_eq!(set_pin(&mut env, &[0x88; 16], 8), Ok(()));
|
||||||
assert!(!has_force_pin_change(&mut env, 0).unwrap());
|
assert!(!has_force_pin_change(&mut env).unwrap());
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_force_pin_change_multi_pin() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
for i in 0..env.customization().slot_count() {
|
|
||||||
assert!(!has_force_pin_change(&mut env, i).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(force_pin_change(&mut env, 1), Ok(()));
|
|
||||||
assert!(has_force_pin_change(&mut env, 1).unwrap());
|
|
||||||
// Other slots shouldn't be affected.
|
|
||||||
assert!(!has_force_pin_change(&mut env, 2).unwrap());
|
|
||||||
|
|
||||||
assert_eq!(set_pin(&mut env, 1, &[0x88; 16], 8), Ok(()));
|
|
||||||
assert!(!has_force_pin_change(&mut env, 1).unwrap());
|
|
||||||
// Other slots shouldn't be affected.
|
|
||||||
assert!(!has_force_pin_change(&mut env, 2).unwrap());
|
|
||||||
|
|
||||||
// Operating on multiple slots is supported.
|
|
||||||
assert_eq!(force_pin_change(&mut env, 2), Ok(()));
|
|
||||||
assert!(has_force_pin_change(&mut env, 2).unwrap());
|
|
||||||
assert!(!has_force_pin_change(&mut env, 1).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn force_pin_change_legacy(env: &mut impl Env) -> Result<(), Ctap2StatusCode> {
|
|
||||||
Ok(env.store().insert(key::FIRST_FORCE_PIN_CHANGE, &[])?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_force_pin_change_backward_compat() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
assert!(!has_force_pin_change(&mut env, 0).unwrap());
|
|
||||||
assert!(!has_force_pin_change(&mut env, 1).unwrap());
|
|
||||||
assert_eq!(force_pin_change_legacy(&mut env), Ok(()));
|
|
||||||
// Should fallback to read from legacy storage location successfully.
|
|
||||||
assert!(has_force_pin_change(&mut env, 0).unwrap());
|
|
||||||
// Fallback logic should only apply to slot 0.
|
|
||||||
assert!(!has_force_pin_change(&mut env, 1).unwrap());
|
|
||||||
|
|
||||||
assert_eq!(set_pin(&mut env, 0, &[0x88; 16], 8), Ok(()));
|
|
||||||
assert!(!has_force_pin_change(&mut env, 0).unwrap());
|
|
||||||
// Old storage location should be cleared.
|
|
||||||
assert_eq!(env.store().find(key::FIRST_FORCE_PIN_CHANGE), Ok(None));
|
|
||||||
|
|
||||||
assert_eq!(force_pin_change(&mut env, 0), Ok(()));
|
|
||||||
assert!(has_force_pin_change(&mut env, 0).unwrap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -1512,15 +1145,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_multi_pin() {
|
|
||||||
let mut env = TestEnv::new();
|
|
||||||
|
|
||||||
assert!(!has_multi_pin(&mut env).unwrap());
|
|
||||||
assert_eq!(_enable_multi_pin_for_test(&mut env), Ok(()));
|
|
||||||
assert!(has_multi_pin(&mut env).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_deserialize_credential() {
|
fn test_serialize_deserialize_credential() {
|
||||||
let mut env = TestEnv::new();
|
let mut env = TestEnv::new();
|
||||||
@@ -1538,7 +1162,6 @@ mod test {
|
|||||||
user_icon: Some(String::from("icon")),
|
user_icon: Some(String::from("icon")),
|
||||||
cred_blob: Some(vec![0xCB]),
|
cred_blob: Some(vec![0xCB]),
|
||||||
large_blob_key: Some(vec![0x1B]),
|
large_blob_key: Some(vec![0x1B]),
|
||||||
slot_id: Some(1),
|
|
||||||
};
|
};
|
||||||
let serialized = serialize_credential(credential.clone()).unwrap();
|
let serialized = serialize_credential(credential.clone()).unwrap();
|
||||||
let reconstructed = deserialize_credential(&serialized).unwrap();
|
let reconstructed = deserialize_credential(&serialized).unwrap();
|
||||||
|
|||||||
@@ -73,37 +73,6 @@ make_partition! {
|
|||||||
// - When adding a (non-persistent) key below this message, make sure its value is bigger or
|
// - When adding a (non-persistent) key below this message, make sure its value is bigger or
|
||||||
// equal than NUM_PERSISTENT_KEYS.
|
// equal than NUM_PERSISTENT_KEYS.
|
||||||
|
|
||||||
/// Whether multi-PIN is enabled.
|
|
||||||
///
|
|
||||||
/// The value must be empty. Only presence of the value matters.
|
|
||||||
MULTI_PIN = 983;
|
|
||||||
|
|
||||||
// Start of key arrays for multi-PIN feature: these fields are separated for each slots, so
|
|
||||||
// a unique key is needed for each slot. However, we reuse the existing fields and rename them
|
|
||||||
// to `FIRST_{KEY_NAME}` so the upgrade is backward compatible.
|
|
||||||
// Depending on `Customization::slot_count()`, only a prefix of those keys is used.
|
|
||||||
|
|
||||||
/// Whether the PIN needs to be changed each slot.
|
|
||||||
///
|
|
||||||
/// The PIN needs to be changed if the slot exists and its data is empty.
|
|
||||||
FORCE_PIN_CHANGE = 984;
|
|
||||||
|
|
||||||
/// The number of PIN retries for each slot, except the first.
|
|
||||||
PIN_RETRIES = 985..992;
|
|
||||||
|
|
||||||
/// The PIN hash and length for each slot.
|
|
||||||
///
|
|
||||||
/// If a slot is absent, there is no PIN set for that slot. The first byte represents
|
|
||||||
/// the length, the following are an array with the hash.
|
|
||||||
PIN_PROPERTIES = 992;
|
|
||||||
|
|
||||||
/// The global signature counters for each slot, except the first.
|
|
||||||
///
|
|
||||||
/// If the entry is absent, the counter is 0.
|
|
||||||
GLOBAL_SIGNATURE_COUNTER = 993..1000;
|
|
||||||
|
|
||||||
// End of key arrays for multi-PIN feature.
|
|
||||||
|
|
||||||
/// Reserved for future credential-related objects.
|
/// Reserved for future credential-related objects.
|
||||||
///
|
///
|
||||||
/// In particular, additional credentials could be added there by reducing the lower bound of
|
/// In particular, additional credentials could be added there by reducing the lower bound of
|
||||||
@@ -128,10 +97,8 @@ make_partition! {
|
|||||||
/// If this entry exists and is empty, enterprise attestation is enabled.
|
/// If this entry exists and is empty, enterprise attestation is enabled.
|
||||||
ENTERPRISE_ATTESTATION = 2039;
|
ENTERPRISE_ATTESTATION = 2039;
|
||||||
|
|
||||||
/// Whether the PIN needs to be changed for the first slot.
|
/// If this entry exists and is empty, the PIN needs to be changed.
|
||||||
///
|
FORCE_PIN_CHANGE = 2040;
|
||||||
/// The PIN needs to be changed if this entry exists and is empty.
|
|
||||||
FIRST_FORCE_PIN_CHANGE = 2040;
|
|
||||||
|
|
||||||
/// The secret of the CredRandom feature.
|
/// The secret of the CredRandom feature.
|
||||||
CRED_RANDOM_SECRET = 2041;
|
CRED_RANDOM_SECRET = 2041;
|
||||||
@@ -144,22 +111,24 @@ make_partition! {
|
|||||||
/// If the entry is absent, the minimum PIN length is `Customization::default_min_pin_length()`.
|
/// If the entry is absent, the minimum PIN length is `Customization::default_min_pin_length()`.
|
||||||
MIN_PIN_LENGTH = 2043;
|
MIN_PIN_LENGTH = 2043;
|
||||||
|
|
||||||
/// The number of PIN retries for the first slot.
|
/// The number of PIN retries.
|
||||||
///
|
///
|
||||||
/// If the entry is absent, the number of PIN retries is `Customization::max_pin_retries()`.
|
/// If the entry is absent, the number of PIN retries is `Customization::max_pin_retries()`.
|
||||||
FIRST_PIN_RETRIES = 2044;
|
PIN_RETRIES = 2044;
|
||||||
|
|
||||||
/// The PIN hash and length for the first slot.
|
/// The PIN hash and length.
|
||||||
///
|
///
|
||||||
/// If the entry is absent, there is no PIN set. The first byte represents
|
/// If the entry is absent, there is no PIN set. The first byte represents
|
||||||
/// the length, the following are an array with the hash.
|
/// the length, the following are an array with the hash.
|
||||||
FIRST_PIN_PROPERTIES = 2045;
|
PIN_PROPERTIES = 2045;
|
||||||
|
|
||||||
/// Reserved for the key store implementation of the environment.
|
/// Reserved for the key store implementation of the environment.
|
||||||
_RESERVED_KEY_STORE = 2046;
|
_RESERVED_KEY_STORE = 2046;
|
||||||
|
|
||||||
/// The global signature counter for the first slot.
|
/// The global signature counter.
|
||||||
FIRST_GLOBAL_SIGNATURE_COUNTER = 2047;
|
///
|
||||||
|
/// If the entry is absent, the counter is 0.
|
||||||
|
GLOBAL_SIGNATURE_COUNTER = 2047;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ pub struct PinUvAuthTokenState {
|
|||||||
permissions_rp_id: Option<String>,
|
permissions_rp_id: Option<String>,
|
||||||
usage_timer: TimedPermission,
|
usage_timer: TimedPermission,
|
||||||
user_verified: bool,
|
user_verified: bool,
|
||||||
slot_id_in_use: Option<usize>,
|
in_use: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PinUvAuthTokenState {
|
impl PinUvAuthTokenState {
|
||||||
@@ -52,18 +52,13 @@ impl PinUvAuthTokenState {
|
|||||||
permissions_rp_id: None,
|
permissions_rp_id: None,
|
||||||
usage_timer: TimedPermission::waiting(),
|
usage_timer: TimedPermission::waiting(),
|
||||||
user_verified: false,
|
user_verified: false,
|
||||||
slot_id_in_use: None,
|
in_use: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the pinUvAuthToken is active.
|
/// Returns whether the pinUvAuthToken is active.
|
||||||
pub fn is_in_use(&self) -> bool {
|
pub fn is_in_use(&self) -> bool {
|
||||||
self.slot_id_in_use.is_some()
|
self.in_use
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the slot id in use.
|
|
||||||
pub fn slot_id_in_use(&self) -> Option<usize> {
|
|
||||||
self.slot_id_in_use
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the permission is granted.
|
/// Checks if the permission is granted.
|
||||||
@@ -118,15 +113,15 @@ impl PinUvAuthTokenState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Starts the timer for pinUvAuthToken usage.
|
/// Starts the timer for pinUvAuthToken usage.
|
||||||
pub fn begin_using_pin_uv_auth_token(&mut self, now: CtapInstant, slot_id: usize) {
|
pub fn begin_using_pin_uv_auth_token(&mut self, now: CtapInstant) {
|
||||||
self.user_verified = true;
|
self.user_verified = true;
|
||||||
self.usage_timer = TimedPermission::granted(now, INITIAL_USAGE_TIME_LIMIT);
|
self.usage_timer = TimedPermission::granted(now, INITIAL_USAGE_TIME_LIMIT);
|
||||||
self.slot_id_in_use = Some(slot_id);
|
self.in_use = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the usage timer, and disables the pinUvAuthToken on timeout.
|
/// Updates the usage timer, and disables the pinUvAuthToken on timeout.
|
||||||
pub fn pin_uv_auth_token_usage_timer_observer(&mut self, now: CtapInstant) {
|
pub fn pin_uv_auth_token_usage_timer_observer(&mut self, now: CtapInstant) {
|
||||||
if !self.is_in_use() {
|
if !self.in_use {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.usage_timer = self.usage_timer.check_expiration(now);
|
self.usage_timer = self.usage_timer.check_expiration(now);
|
||||||
@@ -137,7 +132,7 @@ impl PinUvAuthTokenState {
|
|||||||
|
|
||||||
/// Returns whether the user is verified.
|
/// Returns whether the user is verified.
|
||||||
pub fn get_user_verified_flag_value(&self) -> bool {
|
pub fn get_user_verified_flag_value(&self) -> bool {
|
||||||
self.is_in_use() && self.user_verified
|
self.in_use && self.user_verified
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consumes the user verification.
|
/// Consumes the user verification.
|
||||||
@@ -156,7 +151,7 @@ impl PinUvAuthTokenState {
|
|||||||
self.permissions_set = 0;
|
self.permissions_set = 0;
|
||||||
self.usage_timer = TimedPermission::waiting();
|
self.usage_timer = TimedPermission::waiting();
|
||||||
self.user_verified = false;
|
self.user_verified = false;
|
||||||
self.slot_id_in_use = None;
|
self.in_use = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,28 +164,24 @@ mod test {
|
|||||||
fn test_observer() {
|
fn test_observer() {
|
||||||
let mut token_state = PinUvAuthTokenState::new();
|
let mut token_state = PinUvAuthTokenState::new();
|
||||||
let mut now: CtapInstant = CtapInstant::new(0);
|
let mut now: CtapInstant = CtapInstant::new(0);
|
||||||
token_state.begin_using_pin_uv_auth_token(now, 0);
|
token_state.begin_using_pin_uv_auth_token(now);
|
||||||
assert!(token_state.is_in_use());
|
assert!(token_state.is_in_use());
|
||||||
assert_eq!(token_state.slot_id_in_use(), Some(0));
|
|
||||||
now = now + Milliseconds(100_u32);
|
now = now + Milliseconds(100_u32);
|
||||||
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
||||||
assert!(token_state.is_in_use());
|
assert!(token_state.is_in_use());
|
||||||
now = now + INITIAL_USAGE_TIME_LIMIT;
|
now = now + INITIAL_USAGE_TIME_LIMIT;
|
||||||
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
token_state.pin_uv_auth_token_usage_timer_observer(now);
|
||||||
assert!(!token_state.is_in_use());
|
assert!(!token_state.is_in_use());
|
||||||
assert!(token_state.slot_id_in_use().is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stop() {
|
fn test_stop() {
|
||||||
let mut token_state = PinUvAuthTokenState::new();
|
let mut token_state = PinUvAuthTokenState::new();
|
||||||
let now: CtapInstant = CtapInstant::new(0);
|
let now: CtapInstant = CtapInstant::new(0);
|
||||||
token_state.begin_using_pin_uv_auth_token(now, 0);
|
token_state.begin_using_pin_uv_auth_token(now);
|
||||||
assert!(token_state.is_in_use());
|
assert!(token_state.is_in_use());
|
||||||
assert_eq!(token_state.slot_id_in_use(), Some(0));
|
|
||||||
token_state.stop_using_pin_uv_auth_token();
|
token_state.stop_using_pin_uv_auth_token();
|
||||||
assert!(!token_state.is_in_use());
|
assert!(!token_state.is_in_use());
|
||||||
assert!(token_state.slot_id_in_use().is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -274,11 +265,11 @@ mod test {
|
|||||||
let mut token_state = PinUvAuthTokenState::new();
|
let mut token_state = PinUvAuthTokenState::new();
|
||||||
assert!(!token_state.get_user_verified_flag_value());
|
assert!(!token_state.get_user_verified_flag_value());
|
||||||
let now: CtapInstant = CtapInstant::new(0);
|
let now: CtapInstant = CtapInstant::new(0);
|
||||||
token_state.begin_using_pin_uv_auth_token(now, 0);
|
token_state.begin_using_pin_uv_auth_token(now);
|
||||||
assert!(token_state.get_user_verified_flag_value());
|
assert!(token_state.get_user_verified_flag_value());
|
||||||
token_state.clear_user_verified_flag();
|
token_state.clear_user_verified_flag();
|
||||||
assert!(!token_state.get_user_verified_flag_value());
|
assert!(!token_state.get_user_verified_flag_value());
|
||||||
token_state.begin_using_pin_uv_auth_token(now, 0);
|
token_state.begin_using_pin_uv_auth_token(now);
|
||||||
assert!(token_state.get_user_verified_flag_value());
|
assert!(token_state.get_user_verified_flag_value());
|
||||||
token_state.stop_using_pin_uv_auth_token();
|
token_state.stop_using_pin_uv_auth_token();
|
||||||
assert!(!token_state.get_user_verified_flag_value());
|
assert!(!token_state.get_user_verified_flag_value());
|
||||||
|
|||||||
18
src/env/test/customization.rs
vendored
18
src/env/test/customization.rs
vendored
@@ -18,6 +18,7 @@ use alloc::string::String;
|
|||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
pub struct TestCustomization {
|
pub struct TestCustomization {
|
||||||
|
allows_pin_protocol_v1: bool,
|
||||||
default_cred_protect: Option<CredentialProtectionPolicy>,
|
default_cred_protect: Option<CredentialProtectionPolicy>,
|
||||||
default_min_pin_length: u8,
|
default_min_pin_length: u8,
|
||||||
default_min_pin_length_rp_ids: Vec<String>,
|
default_min_pin_length_rp_ids: Vec<String>,
|
||||||
@@ -33,10 +34,13 @@ pub struct TestCustomization {
|
|||||||
max_large_blob_array_size: usize,
|
max_large_blob_array_size: usize,
|
||||||
max_rp_ids_length: usize,
|
max_rp_ids_length: usize,
|
||||||
max_supported_resident_keys: usize,
|
max_supported_resident_keys: usize,
|
||||||
slot_count: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestCustomization {
|
impl TestCustomization {
|
||||||
|
pub fn set_allows_pin_protocol_v1(&mut self, is_allowed: bool) {
|
||||||
|
self.allows_pin_protocol_v1 = is_allowed;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn setup_enterprise_attestation(
|
pub fn setup_enterprise_attestation(
|
||||||
&mut self,
|
&mut self,
|
||||||
mode: Option<EnterpriseAttestationMode>,
|
mode: Option<EnterpriseAttestationMode>,
|
||||||
@@ -50,6 +54,10 @@ impl TestCustomization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Customization for TestCustomization {
|
impl Customization for TestCustomization {
|
||||||
|
fn allows_pin_protocol_v1(&self) -> bool {
|
||||||
|
self.allows_pin_protocol_v1
|
||||||
|
}
|
||||||
|
|
||||||
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy> {
|
fn default_cred_protect(&self) -> Option<CredentialProtectionPolicy> {
|
||||||
self.default_cred_protect
|
self.default_cred_protect
|
||||||
}
|
}
|
||||||
@@ -113,15 +121,12 @@ impl Customization for TestCustomization {
|
|||||||
fn max_supported_resident_keys(&self) -> usize {
|
fn max_supported_resident_keys(&self) -> usize {
|
||||||
self.max_supported_resident_keys
|
self.max_supported_resident_keys
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slot_count(&self) -> usize {
|
|
||||||
self.slot_count
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CustomizationImpl> for TestCustomization {
|
impl From<CustomizationImpl> for TestCustomization {
|
||||||
fn from(c: CustomizationImpl) -> Self {
|
fn from(c: CustomizationImpl) -> Self {
|
||||||
let CustomizationImpl {
|
let CustomizationImpl {
|
||||||
|
allows_pin_protocol_v1,
|
||||||
default_cred_protect,
|
default_cred_protect,
|
||||||
default_min_pin_length,
|
default_min_pin_length,
|
||||||
default_min_pin_length_rp_ids,
|
default_min_pin_length_rp_ids,
|
||||||
@@ -137,7 +142,6 @@ impl From<CustomizationImpl> for TestCustomization {
|
|||||||
max_large_blob_array_size,
|
max_large_blob_array_size,
|
||||||
max_rp_ids_length,
|
max_rp_ids_length,
|
||||||
max_supported_resident_keys,
|
max_supported_resident_keys,
|
||||||
slot_count,
|
|
||||||
} = c;
|
} = c;
|
||||||
|
|
||||||
let default_min_pin_length_rp_ids = default_min_pin_length_rp_ids
|
let default_min_pin_length_rp_ids = default_min_pin_length_rp_ids
|
||||||
@@ -151,6 +155,7 @@ impl From<CustomizationImpl> for TestCustomization {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
allows_pin_protocol_v1,
|
||||||
default_cred_protect,
|
default_cred_protect,
|
||||||
default_min_pin_length,
|
default_min_pin_length,
|
||||||
default_min_pin_length_rp_ids,
|
default_min_pin_length_rp_ids,
|
||||||
@@ -166,7 +171,6 @@ impl From<CustomizationImpl> for TestCustomization {
|
|||||||
max_large_blob_array_size,
|
max_large_blob_array_size,
|
||||||
max_rp_ids_length,
|
max_rp_ids_length,
|
||||||
max_supported_resident_keys,
|
max_supported_resident_keys,
|
||||||
slot_count,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
src/env/test/mod.rs
vendored
4
src/env/test/mod.rs
vendored
@@ -50,6 +50,10 @@ impl TestRng256 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Rng256 for TestRng256 {
|
impl Rng256 for TestRng256 {
|
||||||
|
fn fill_bytes(&mut self, buf: &mut [u8]) {
|
||||||
|
self.rng.fill(buf)
|
||||||
|
}
|
||||||
|
|
||||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||||
let mut result = [Default::default(); 32];
|
let mut result = [Default::default(); 32];
|
||||||
self.rng.fill(&mut result);
|
self.rng.fill(&mut result);
|
||||||
|
|||||||
67
src/env/test/upgrade_storage.rs
vendored
67
src/env/test/upgrade_storage.rs
vendored
@@ -17,27 +17,22 @@ use crate::api::upgrade_storage::UpgradeStorage;
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use persistent_store::{StorageError, StorageResult};
|
use persistent_store::{StorageError, StorageResult};
|
||||||
|
|
||||||
const PARTITION_LENGTH: usize = 0x40000;
|
const PARTITION_LENGTH: usize = 0x41000;
|
||||||
const METADATA_LENGTH: usize = 0x1000;
|
const METADATA_LENGTH: usize = 0x1000;
|
||||||
|
|
||||||
pub struct BufferUpgradeStorage {
|
pub struct BufferUpgradeStorage {
|
||||||
/// Content of the partition storage.
|
/// Content of the partition storage.
|
||||||
partition: Box<[u8]>,
|
partition: Box<[u8]>,
|
||||||
|
|
||||||
/// Content of the metadata storage.
|
|
||||||
metadata: Box<[u8]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferUpgradeStorage {
|
impl BufferUpgradeStorage {
|
||||||
pub fn new() -> StorageResult<BufferUpgradeStorage> {
|
pub fn new() -> StorageResult<BufferUpgradeStorage> {
|
||||||
Ok(BufferUpgradeStorage {
|
Ok(BufferUpgradeStorage {
|
||||||
partition: vec![0xff; PARTITION_LENGTH].into_boxed_slice(),
|
partition: vec![0xff; PARTITION_LENGTH].into_boxed_slice(),
|
||||||
metadata: vec![0xff; METADATA_LENGTH].into_boxed_slice(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl UpgradeStorage for BufferUpgradeStorage {
|
#[cfg(test)]
|
||||||
fn read_partition(&self, offset: usize, length: usize) -> StorageResult<&[u8]> {
|
fn read_partition(&self, offset: usize, length: usize) -> StorageResult<&[u8]> {
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
return Err(StorageError::OutOfBounds);
|
return Err(StorageError::OutOfBounds);
|
||||||
@@ -49,40 +44,31 @@ impl UpgradeStorage for BufferUpgradeStorage {
|
|||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn write_partition(&mut self, offset: usize, data: &[u8]) -> StorageResult<()> {
|
impl UpgradeStorage for BufferUpgradeStorage {
|
||||||
|
fn write_bundle(&mut self, offset: usize, data: Vec<u8>) -> StorageResult<()> {
|
||||||
|
if offset == 0 && data.len() != METADATA_LENGTH {
|
||||||
|
return Err(StorageError::OutOfBounds);
|
||||||
|
}
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
return Err(StorageError::OutOfBounds);
|
return Err(StorageError::OutOfBounds);
|
||||||
}
|
}
|
||||||
let partition_range = ModRange::new(0, self.partition.len());
|
let partition_range = ModRange::new(0, self.partition.len());
|
||||||
if partition_range.contains_range(&ModRange::new(offset, data.len())) {
|
if partition_range.contains_range(&ModRange::new(offset, data.len())) {
|
||||||
self.partition[offset..][..data.len()].copy_from_slice(data);
|
self.partition[offset..][..data.len()].copy_from_slice(&data);
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partition_address(&self) -> usize {
|
fn bundle_identifier(&self) -> u32 {
|
||||||
0x60000
|
0x60000
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partition_length(&self) -> usize {
|
fn running_firmware_version(&self) -> u64 {
|
||||||
PARTITION_LENGTH
|
0
|
||||||
}
|
|
||||||
|
|
||||||
fn read_metadata(&self) -> StorageResult<&[u8]> {
|
|
||||||
Ok(&self.metadata[..])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()> {
|
|
||||||
if data.len() <= METADATA_LENGTH {
|
|
||||||
self.metadata.copy_from_slice(&[0xff; METADATA_LENGTH]);
|
|
||||||
self.metadata[..data.len()].copy_from_slice(data);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(StorageError::OutOfBounds)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,13 +77,13 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_write_partition() {
|
fn read_write_bundle() {
|
||||||
let mut storage = BufferUpgradeStorage::new().unwrap();
|
let mut storage = BufferUpgradeStorage::new().unwrap();
|
||||||
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0xFF]);
|
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0xFF]);
|
||||||
assert!(storage.write_partition(1, &[0x88, 0x88]).is_ok());
|
assert!(storage.write_bundle(1, vec![0x88, 0x88]).is_ok());
|
||||||
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0x88]);
|
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0x88]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
storage.write_partition(PARTITION_LENGTH - 1, &[0x88, 0x88]),
|
storage.write_bundle(PARTITION_LENGTH - 1, vec![0x88, 0x88],),
|
||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@@ -109,11 +95,11 @@ mod tests {
|
|||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
storage.write_partition(4, &[]),
|
storage.write_bundle(4, vec![]),
|
||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
storage.write_partition(PARTITION_LENGTH + 4, &[]),
|
storage.write_bundle(PARTITION_LENGTH + 4, vec![]),
|
||||||
Err(StorageError::OutOfBounds)
|
Err(StorageError::OutOfBounds)
|
||||||
);
|
);
|
||||||
assert_eq!(storage.read_partition(4, 0), Err(StorageError::OutOfBounds));
|
assert_eq!(storage.read_partition(4, 0), Err(StorageError::OutOfBounds));
|
||||||
@@ -126,23 +112,6 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn partition_slice() {
|
fn partition_slice() {
|
||||||
let storage = BufferUpgradeStorage::new().unwrap();
|
let storage = BufferUpgradeStorage::new().unwrap();
|
||||||
assert_eq!(storage.partition_address(), 0x60000);
|
assert_eq!(storage.bundle_identifier(), 0x60000);
|
||||||
assert_eq!(storage.partition_length(), PARTITION_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn read_write_metadata() {
|
|
||||||
let mut storage = BufferUpgradeStorage::new().unwrap();
|
|
||||||
assert_eq!(storage.read_metadata().unwrap(), &[0xFF; METADATA_LENGTH]);
|
|
||||||
assert!(storage.write_metadata(&[0x88, 0x88]).is_ok());
|
|
||||||
assert_eq!(
|
|
||||||
storage.write_metadata(&[0x88; METADATA_LENGTH + 1]),
|
|
||||||
Err(StorageError::OutOfBounds)
|
|
||||||
);
|
|
||||||
let new_metadata = storage.read_metadata().unwrap();
|
|
||||||
assert_eq!(&new_metadata[0..2], &[0x88, 0x88]);
|
|
||||||
assert_eq!(&new_metadata[2..], &[0xFF; METADATA_LENGTH - 2]);
|
|
||||||
assert!(storage.write_metadata(&[]).is_ok());
|
|
||||||
assert_eq!(storage.read_metadata().unwrap(), &[0xFF; METADATA_LENGTH]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/env/tock/mod.rs
vendored
2
src/env/tock/mod.rs
vendored
@@ -117,7 +117,7 @@ impl UserPresence for TockEnv {
|
|||||||
}
|
}
|
||||||
fn wait_with_timeout(&mut self, timeout: Milliseconds<ClockInt>) -> UserPresenceResult {
|
fn wait_with_timeout(&mut self, timeout: Milliseconds<ClockInt>) -> UserPresenceResult {
|
||||||
if timeout.integer() == 0 {
|
if timeout.integer() == 0 {
|
||||||
return Err(UserPresenceError::Timeout);
|
return Ok(());
|
||||||
}
|
}
|
||||||
blink_leds(self.blink_pattern);
|
blink_leds(self.blink_pattern);
|
||||||
self.blink_pattern += 1;
|
self.blink_pattern += 1;
|
||||||
|
|||||||
372
src/env/tock/storage.rs
vendored
372
src/env/tock/storage.rs
vendored
@@ -12,15 +12,23 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
use crate::api::upgrade_storage::helper::{find_slice, is_aligned, ModRange};
|
use crate::api::upgrade_storage::helper::{find_slice, is_aligned, ModRange, Partition};
|
||||||
use crate::api::upgrade_storage::UpgradeStorage;
|
use crate::api::upgrade_storage::UpgradeStorage;
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
use arrayref::array_ref;
|
||||||
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
|
use crypto::sha256::Sha256;
|
||||||
|
use crypto::{ecdsa, Hash256};
|
||||||
use libtock_core::{callback, syscalls};
|
use libtock_core::{callback, syscalls};
|
||||||
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
|
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
|
||||||
|
|
||||||
const DRIVER_NUMBER: usize = 0x50003;
|
const DRIVER_NUMBER: usize = 0x50003;
|
||||||
|
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||||
|
|
||||||
|
const UPGRADE_PUBLIC_KEY: &[u8; 65] =
|
||||||
|
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey.bin"));
|
||||||
|
|
||||||
mod subscribe_nr {
|
mod subscribe_nr {
|
||||||
pub const DONE: usize = 0;
|
pub const DONE: usize = 0;
|
||||||
@@ -52,7 +60,6 @@ mod memop_nr {
|
|||||||
mod storage_type {
|
mod storage_type {
|
||||||
pub const STORE: usize = 1;
|
pub const STORE: usize = 1;
|
||||||
pub const PARTITION: usize = 2;
|
pub const PARTITION: usize = 2;
|
||||||
pub const METADATA: usize = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_info(nr: usize, arg: usize) -> StorageResult<usize> {
|
fn get_info(nr: usize, arg: usize) -> StorageResult<usize> {
|
||||||
@@ -94,6 +101,10 @@ fn block_command(driver: usize, cmd: usize, arg1: usize, arg2: usize) -> Storage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn read_slice(address: usize, length: usize) -> &'static [u8] {
|
||||||
|
core::slice::from_raw_parts(address as *const u8, length)
|
||||||
|
}
|
||||||
|
|
||||||
fn write_slice(ptr: usize, value: &[u8]) -> StorageResult<()> {
|
fn write_slice(ptr: usize, value: &[u8]) -> StorageResult<()> {
|
||||||
let code = unsafe {
|
let code = unsafe {
|
||||||
syscalls::raw::allow(
|
syscalls::raw::allow(
|
||||||
@@ -220,11 +231,19 @@ impl Storage for TockStorage {
|
|||||||
|
|
||||||
pub struct TockUpgradeStorage {
|
pub struct TockUpgradeStorage {
|
||||||
page_size: usize,
|
page_size: usize,
|
||||||
partition: ModRange,
|
partition: Partition,
|
||||||
metadata: ModRange,
|
metadata: ModRange,
|
||||||
|
running_metadata: ModRange,
|
||||||
|
identifier: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TockUpgradeStorage {
|
impl TockUpgradeStorage {
|
||||||
|
// Ideally, the kernel should tell us metadata and partitions directly.
|
||||||
|
// This code only works for one layout, refactor this into the storage driver to support more.
|
||||||
|
const METADATA_ADDRESS: usize = 0x4000;
|
||||||
|
const PARTITION_ADDRESS_A: usize = 0x20000;
|
||||||
|
const PARTITION_ADDRESS_B: usize = 0x60000;
|
||||||
|
|
||||||
/// Provides access to the other upgrade partition and metadata if available.
|
/// Provides access to the other upgrade partition and metadata if available.
|
||||||
///
|
///
|
||||||
/// The implementation assumes that storage locations returned by the kernel through
|
/// The implementation assumes that storage locations returned by the kernel through
|
||||||
@@ -235,24 +254,26 @@ impl TockUpgradeStorage {
|
|||||||
/// Returns `CustomError` if any of the following conditions do not hold:
|
/// Returns `CustomError` if any of the following conditions do not hold:
|
||||||
/// - The page size is a power of two.
|
/// - The page size is a power of two.
|
||||||
/// - The storage slices are page-aligned.
|
/// - The storage slices are page-aligned.
|
||||||
/// - There are not partition or metadata slices.
|
/// - There are no partition or no metadata slices.
|
||||||
/// Returns a `NotAligned` error if partitions or metadata ranges are
|
/// Returns a `NotAligned` error if partitions or metadata ranges are
|
||||||
/// - not exclusive or,
|
/// - not exclusive or,
|
||||||
/// - not consecutive.
|
/// - not consecutive.
|
||||||
pub fn new() -> StorageResult<TockUpgradeStorage> {
|
pub fn new() -> StorageResult<TockUpgradeStorage> {
|
||||||
let mut locations = TockUpgradeStorage {
|
let mut locations = TockUpgradeStorage {
|
||||||
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
||||||
partition: ModRange::new_empty(),
|
partition: Partition::new(),
|
||||||
metadata: ModRange::new_empty(),
|
metadata: ModRange::new_empty(),
|
||||||
|
running_metadata: ModRange::new_empty(),
|
||||||
|
identifier: Self::PARTITION_ADDRESS_A as u32,
|
||||||
};
|
};
|
||||||
if !locations.page_size.is_power_of_two() {
|
if !locations.page_size.is_power_of_two() {
|
||||||
return Err(StorageError::CustomError);
|
return Err(StorageError::CustomError);
|
||||||
}
|
}
|
||||||
|
let mut firmware_range = ModRange::new_empty();
|
||||||
for i in 0..memop(memop_nr::STORAGE_CNT, 0)? {
|
for i in 0..memop(memop_nr::STORAGE_CNT, 0)? {
|
||||||
let storage_type = memop(memop_nr::STORAGE_TYPE, i)?;
|
let storage_type = memop(memop_nr::STORAGE_TYPE, i)?;
|
||||||
match storage_type {
|
if !matches!(storage_type, storage_type::PARTITION) {
|
||||||
storage_type::PARTITION | storage_type::METADATA => (),
|
continue;
|
||||||
_ => continue,
|
|
||||||
};
|
};
|
||||||
let storage_ptr = memop(memop_nr::STORAGE_PTR, i)?;
|
let storage_ptr = memop(memop_nr::STORAGE_PTR, i)?;
|
||||||
let storage_len = memop(memop_nr::STORAGE_LEN, i)?;
|
let storage_len = memop(memop_nr::STORAGE_LEN, i)?;
|
||||||
@@ -260,91 +281,292 @@ impl TockUpgradeStorage {
|
|||||||
return Err(StorageError::CustomError);
|
return Err(StorageError::CustomError);
|
||||||
}
|
}
|
||||||
let range = ModRange::new(storage_ptr, storage_len);
|
let range = ModRange::new(storage_ptr, storage_len);
|
||||||
match storage_type {
|
match range.start() {
|
||||||
storage_type::PARTITION => {
|
Self::METADATA_ADDRESS => {
|
||||||
locations.partition = locations
|
// Will be swapped if we are on B.
|
||||||
.partition
|
locations.metadata = ModRange::new(range.start(), locations.page_size);
|
||||||
.append(range)
|
locations.running_metadata =
|
||||||
.ok_or(StorageError::NotAligned)?
|
ModRange::new(range.start() + locations.page_size, locations.page_size);
|
||||||
}
|
}
|
||||||
storage_type::METADATA => {
|
_ => {
|
||||||
locations.metadata = locations
|
if !firmware_range.append(&range) {
|
||||||
.metadata
|
return Err(StorageError::NotAligned);
|
||||||
.append(range)
|
}
|
||||||
.ok_or(StorageError::NotAligned)?
|
|
||||||
}
|
}
|
||||||
_ => (),
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if locations.partition.is_empty() || locations.metadata.is_empty() {
|
if firmware_range.is_empty()
|
||||||
Err(StorageError::CustomError)
|
|| locations.metadata.is_empty()
|
||||||
} else {
|
|| locations.running_metadata.is_empty()
|
||||||
Ok(locations)
|
{
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
}
|
}
|
||||||
|
if firmware_range.start() == Self::PARTITION_ADDRESS_B {
|
||||||
|
core::mem::swap(&mut locations.metadata, &mut locations.running_metadata);
|
||||||
|
locations.identifier = Self::PARTITION_ADDRESS_B as u32;
|
||||||
|
}
|
||||||
|
if !locations.partition.append(locations.metadata.clone()) {
|
||||||
|
return Err(StorageError::NotAligned);
|
||||||
|
}
|
||||||
|
if !locations.partition.append(firmware_range) {
|
||||||
|
return Err(StorageError::NotAligned);
|
||||||
|
}
|
||||||
|
Ok(locations)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_page_aligned(&self, x: usize) -> bool {
|
fn is_page_aligned(&self, x: usize) -> bool {
|
||||||
is_aligned(self.page_size, x)
|
is_aligned(self.page_size, x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns whether the metadata is contained in this range or not.
|
||||||
|
///
|
||||||
|
/// Assumes that metadata is written in one call per range. If the metadata is only partially
|
||||||
|
/// contained, returns an error.
|
||||||
|
fn contains_metadata(&self, checked_range: &ModRange) -> StorageResult<bool> {
|
||||||
|
if checked_range.intersects_range(&self.metadata) {
|
||||||
|
if checked_range.contains_range(&self.metadata) {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Err(StorageError::NotAligned)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the metadata's hash matches the partition's content.
|
||||||
|
fn check_partition_hash(&self, metadata: &[u8]) -> StorageResult<()> {
|
||||||
|
let start_address = self.metadata.start() + METADATA_SIGN_OFFSET;
|
||||||
|
let mut hasher = Sha256::new();
|
||||||
|
for range in self.partition.ranges_from(start_address) {
|
||||||
|
let partition_slice = unsafe { read_slice(range.start(), range.length()) };
|
||||||
|
// The hash implementation handles this in chunks, so no memory issues.
|
||||||
|
hasher.update(partition_slice);
|
||||||
|
}
|
||||||
|
let computed_hash = hasher.finalize();
|
||||||
|
if &computed_hash != parse_metadata_hash(metadata) {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UpgradeStorage for TockUpgradeStorage {
|
impl UpgradeStorage for TockUpgradeStorage {
|
||||||
fn read_partition(&self, offset: usize, length: usize) -> StorageResult<&[u8]> {
|
fn write_bundle(&mut self, offset: usize, data: Vec<u8>) -> StorageResult<()> {
|
||||||
if length == 0 {
|
|
||||||
return Err(StorageError::OutOfBounds);
|
|
||||||
}
|
|
||||||
let address = self.partition.start() + offset;
|
|
||||||
if self
|
|
||||||
.partition
|
|
||||||
.contains_range(&ModRange::new(address, length))
|
|
||||||
{
|
|
||||||
Ok(unsafe { core::slice::from_raw_parts(address as *const u8, length) })
|
|
||||||
} else {
|
|
||||||
Err(StorageError::OutOfBounds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_partition(&mut self, offset: usize, data: &[u8]) -> StorageResult<()> {
|
|
||||||
if data.is_empty() {
|
if data.is_empty() {
|
||||||
return Err(StorageError::OutOfBounds);
|
return Err(StorageError::OutOfBounds);
|
||||||
}
|
}
|
||||||
let address = self.partition.start() + offset;
|
let address = self
|
||||||
|
.partition
|
||||||
|
.find_address(offset, data.len())
|
||||||
|
.ok_or(StorageError::OutOfBounds)?;
|
||||||
let write_range = ModRange::new(address, data.len());
|
let write_range = ModRange::new(address, data.len());
|
||||||
if self.partition.contains_range(&write_range) {
|
if self.contains_metadata(&write_range)? {
|
||||||
// Erases all pages that have their first byte in the write range.
|
let new_metadata = &data[self.metadata.start() - address..][..self.metadata.length()];
|
||||||
// Since we expect calls in order, we don't want to erase half-written pages.
|
check_metadata(self, UPGRADE_PUBLIC_KEY, new_metadata)?;
|
||||||
for address in write_range.aligned_iter(self.page_size) {
|
|
||||||
erase_page(address, self.page_size)?;
|
|
||||||
}
|
|
||||||
write_slice(address, data)
|
|
||||||
} else {
|
|
||||||
Err(StorageError::OutOfBounds)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn partition_address(&self) -> usize {
|
// Erases all pages that have their first byte in the write range.
|
||||||
self.partition.start()
|
// Since we expect calls in order, we don't want to erase half-written pages.
|
||||||
}
|
for address in write_range.aligned_iter(self.page_size) {
|
||||||
|
erase_page(address, self.page_size)?;
|
||||||
fn partition_length(&self) -> usize {
|
|
||||||
self.partition.length()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_metadata(&self) -> StorageResult<&[u8]> {
|
|
||||||
Ok(unsafe {
|
|
||||||
core::slice::from_raw_parts(self.metadata.start() as *const u8, self.metadata.length())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()> {
|
|
||||||
// If less data is passed in than is reserved, assume the rest is 0xFF.
|
|
||||||
if data.len() <= self.metadata.length() {
|
|
||||||
for address in self.metadata.aligned_iter(self.page_size) {
|
|
||||||
erase_page(address, self.page_size)?;
|
|
||||||
}
|
|
||||||
write_slice(self.metadata.start(), data)
|
|
||||||
} else {
|
|
||||||
Err(StorageError::OutOfBounds)
|
|
||||||
}
|
}
|
||||||
|
write_slice(address, &data)?;
|
||||||
|
let written_slice = unsafe { read_slice(address, data.len()) };
|
||||||
|
if written_slice != data {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
// Case: Last slice is written.
|
||||||
|
if data.len() == self.partition.length() - offset {
|
||||||
|
let metadata = unsafe { read_slice(self.metadata.start(), self.metadata.length()) };
|
||||||
|
self.check_partition_hash(metadata)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bundle_identifier(&self) -> u32 {
|
||||||
|
self.identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
fn running_firmware_version(&self) -> u64 {
|
||||||
|
let running_metadata = unsafe {
|
||||||
|
read_slice(
|
||||||
|
self.running_metadata.start(),
|
||||||
|
self.running_metadata.length(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
parse_metadata_version(running_metadata)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the metadata of an upgrade, and checks its correctness.
|
||||||
|
///
|
||||||
|
/// The metadata is a page starting with:
|
||||||
|
/// - 32 B upgrade hash (SHA256)
|
||||||
|
/// - 64 B signature,
|
||||||
|
/// that are not signed over. The second part is included in the signature with
|
||||||
|
/// - 8 B version and
|
||||||
|
/// - 4 B partition address in little endian encoding
|
||||||
|
/// written at METADATA_SIGN_OFFSET.
|
||||||
|
///
|
||||||
|
/// Checks signature correctness against the hash, and whether the partition offset matches.
|
||||||
|
/// Whether the hash matches the partition content is not tested here!
|
||||||
|
fn check_metadata(
|
||||||
|
upgrade_locations: &impl UpgradeStorage,
|
||||||
|
public_key_bytes: &[u8],
|
||||||
|
metadata: &[u8],
|
||||||
|
) -> StorageResult<()> {
|
||||||
|
const METADATA_LEN: usize = 0x1000;
|
||||||
|
if metadata.len() != METADATA_LEN {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = parse_metadata_version(metadata);
|
||||||
|
if version < upgrade_locations.running_firmware_version() {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
|
||||||
|
let metadata_address = LittleEndian::read_u32(&metadata[METADATA_SIGN_OFFSET + 8..][..4]);
|
||||||
|
if metadata_address != upgrade_locations.bundle_identifier() {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
|
||||||
|
verify_signature(
|
||||||
|
array_ref!(metadata, 32, 64),
|
||||||
|
public_key_bytes,
|
||||||
|
parse_metadata_hash(metadata),
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the metadata, returns the hash.
|
||||||
|
fn parse_metadata_hash(data: &[u8]) -> &[u8; 32] {
|
||||||
|
array_ref!(data, 0, 32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parses the metadata, returns the firmware version.
|
||||||
|
fn parse_metadata_version(data: &[u8]) -> u64 {
|
||||||
|
LittleEndian::read_u64(&data[METADATA_SIGN_OFFSET..][..8])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies the signature over the given hash.
|
||||||
|
///
|
||||||
|
/// The public key is COSE encoded, and the hash is a SHA256.
|
||||||
|
fn verify_signature(
|
||||||
|
signature_bytes: &[u8; 64],
|
||||||
|
public_key_bytes: &[u8],
|
||||||
|
signed_hash: &[u8; 32],
|
||||||
|
) -> StorageResult<()> {
|
||||||
|
let signature =
|
||||||
|
ecdsa::Signature::from_bytes(signature_bytes).ok_or(StorageError::CustomError)?;
|
||||||
|
let public_key = ecdsa::PubKey::from_bytes_uncompressed(public_key_bytes)
|
||||||
|
.ok_or(StorageError::CustomError)?;
|
||||||
|
if !public_key.verify_hash_vartime(signed_hash, &signature) {
|
||||||
|
return Err(StorageError::CustomError);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::env::test::TestEnv;
|
||||||
|
use crate::env::Env;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_check_metadata() {
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||||
|
let upgrade_locations = env.upgrade_storage().unwrap();
|
||||||
|
|
||||||
|
const METADATA_LEN: usize = 0x1000;
|
||||||
|
const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||||
|
let mut metadata = vec![0xFF; METADATA_LEN];
|
||||||
|
LittleEndian::write_u32(&mut metadata[METADATA_SIGN_OFFSET + 8..][..4], 0x60000);
|
||||||
|
|
||||||
|
let mut signed_over_data = metadata[METADATA_SIGN_OFFSET..].to_vec();
|
||||||
|
signed_over_data.extend(&[0xFF; 0x20000]);
|
||||||
|
let signed_hash = Sha256::hash(&signed_over_data);
|
||||||
|
|
||||||
|
metadata[..32].copy_from_slice(&signed_hash);
|
||||||
|
let signature = private_key.sign_rfc6979::<Sha256>(&signed_over_data);
|
||||||
|
let mut signature_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||||
|
signature.to_bytes(&mut signature_bytes);
|
||||||
|
metadata[32..96].copy_from_slice(&signature_bytes);
|
||||||
|
|
||||||
|
let public_key = private_key.genpk();
|
||||||
|
let mut public_key_bytes = [0; 65];
|
||||||
|
public_key.to_bytes_uncompressed(&mut public_key_bytes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
check_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Manipulating the partition address fails.
|
||||||
|
metadata[METADATA_SIGN_OFFSET + 8] = 0x88;
|
||||||
|
assert_eq!(
|
||||||
|
check_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
|
metadata[METADATA_SIGN_OFFSET + 8] = 0x00;
|
||||||
|
// Wrong metadata length fails.
|
||||||
|
assert_eq!(
|
||||||
|
check_metadata(
|
||||||
|
upgrade_locations,
|
||||||
|
&public_key_bytes,
|
||||||
|
&metadata[..METADATA_LEN - 1]
|
||||||
|
),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
|
// Manipulating the hash fails.
|
||||||
|
metadata[0] ^= 0x01;
|
||||||
|
assert_eq!(
|
||||||
|
check_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
|
metadata[0] ^= 0x01;
|
||||||
|
// Manipulating the signature fails.
|
||||||
|
metadata[32] ^= 0x01;
|
||||||
|
assert_eq!(
|
||||||
|
check_metadata(upgrade_locations, &public_key_bytes, &metadata),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_verify_signature() {
|
||||||
|
let mut env = TestEnv::new();
|
||||||
|
let private_key = crypto::ecdsa::SecKey::gensk(env.rng());
|
||||||
|
let message = [0x44; 64];
|
||||||
|
let signed_hash = Sha256::hash(&message);
|
||||||
|
let signature = private_key.sign_rfc6979::<Sha256>(&message);
|
||||||
|
|
||||||
|
let mut signature_bytes = [0; ecdsa::Signature::BYTES_LENGTH];
|
||||||
|
signature.to_bytes(&mut signature_bytes);
|
||||||
|
|
||||||
|
let public_key = private_key.genpk();
|
||||||
|
let mut public_key_bytes = [0; 65];
|
||||||
|
public_key.to_bytes_uncompressed(&mut public_key_bytes);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
verify_signature(&signature_bytes, &public_key_bytes, &signed_hash),
|
||||||
|
Ok(())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
verify_signature(&signature_bytes, &public_key_bytes, &[0x55; 32]),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
|
public_key_bytes[0] ^= 0x01;
|
||||||
|
assert_eq!(
|
||||||
|
verify_signature(&signature_bytes, &public_key_bytes, &signed_hash),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
|
public_key_bytes[0] ^= 0x01;
|
||||||
|
signature_bytes[0] ^= 0x01;
|
||||||
|
assert_eq!(
|
||||||
|
verify_signature(&signature_bytes, &public_key_bytes, &signed_hash),
|
||||||
|
Err(StorageError::CustomError)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ use libtock_drivers::timer::Duration;
|
|||||||
use libtock_drivers::usb_ctap_hid;
|
use libtock_drivers::usb_ctap_hid;
|
||||||
use usb_ctap_hid::UsbEndpoint;
|
use usb_ctap_hid::UsbEndpoint;
|
||||||
|
|
||||||
libtock_core::stack_size! {0x4000}
|
libtock_core::stack_size! {0x11800}
|
||||||
|
|
||||||
const SEND_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(1000);
|
const SEND_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(1000);
|
||||||
const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
||||||
|
|||||||
957
third_party/dilithium/Cargo.lock
generated
vendored
Normal file
957
third_party/dilithium/Cargo.lock
generated
vendored
Normal file
@@ -0,0 +1,957 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayref"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||||
|
|
||||||
|
[[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 = "autocfg"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"byte-tools",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstr"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byte-tools"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cast"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[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 = "cloudabi"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"cast",
|
||||||
|
"clap",
|
||||||
|
"criterion-plot",
|
||||||
|
"csv",
|
||||||
|
"itertools 0.10.5",
|
||||||
|
"lazy_static",
|
||||||
|
"num-traits",
|
||||||
|
"oorandom",
|
||||||
|
"plotters",
|
||||||
|
"rayon",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_cbor",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"tinytemplate",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "criterion-plot"
|
||||||
|
version = "0.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
|
||||||
|
dependencies = [
|
||||||
|
"cast",
|
||||||
|
"itertools 0.10.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f916dfc5d356b0ed9dae65f1db9fc9770aa2851d2662b988ccf4fe3516e86348"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.1.0",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"memoffset",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edbafec5fa1f196ca66527c1b12c2ec4745ca14b50f1ad8f9f6f720b55d11fac"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csv"
|
||||||
|
version = "1.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
|
||||||
|
dependencies = [
|
||||||
|
"bstr",
|
||||||
|
"csv-core",
|
||||||
|
"itoa 0.4.8",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csv-core"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dilithium"
|
||||||
|
version = "0.2.0-alpha.3"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"byteorder",
|
||||||
|
"criterion",
|
||||||
|
"digest",
|
||||||
|
"hex",
|
||||||
|
"itertools 0.7.11",
|
||||||
|
"once_cell",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"rng256",
|
||||||
|
"sha3",
|
||||||
|
"structopt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d00328cedcac5e81c683e5620ca6a30756fc23027ebf9bff405c0e8da1fbb7e"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.7.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keccak"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838"
|
||||||
|
|
||||||
|
[[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.135"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libtock_codegen"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libtock_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libtock_codegen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libtock_drivers"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libtock_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oorandom"
|
||||||
|
version = "11.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
"plotters-backend",
|
||||||
|
"plotters-svg",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-backend"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plotters-svg"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
|
||||||
|
dependencies = [
|
||||||
|
"plotters-backend",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.46"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94e2ef8dbfc347b10c094890f778ee2e36ca9bb4262e86dc99cd217e35f3470b"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.8",
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
"rand_hc",
|
||||||
|
"rand_isaac",
|
||||||
|
"rand_jitter",
|
||||||
|
"rand_os",
|
||||||
|
"rand_pcg",
|
||||||
|
"rand_xorshift",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.8",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_isaac"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_jitter"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_os"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||||
|
dependencies = [
|
||||||
|
"cloudabi",
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_pcg"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.8",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.1.0",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"num_cpus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
|
||||||
|
dependencies = [
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rng256"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arrayref",
|
||||||
|
"libtock_drivers",
|
||||||
|
"rand",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "same-file"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.145"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_cbor"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
|
||||||
|
dependencies = [
|
||||||
|
"half",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.145"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41feea4228a6f1cd09ec7a3593a682276702cd67b5273544757dae23c096f074"
|
||||||
|
dependencies = [
|
||||||
|
"itoa 1.0.4",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha3"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b64dcef59ed4290b9fb562b53df07f564690d6539e8ecdd4728cf392477530bc"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"byte-tools",
|
||||||
|
"digest",
|
||||||
|
"keccak",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt"
|
||||||
|
version = "0.3.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"lazy_static",
|
||||||
|
"structopt-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "structopt-derive"
|
||||||
|
version = "0.4.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.102"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3fcd952facd492f9be3ef0d0b7032a6e442ee9b361d4acc2b1d0c4aaa5f613a1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "textwrap"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinytemplate"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vec_map"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "2.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||||
|
dependencies = [
|
||||||
|
"same-file",
|
||||||
|
"winapi",
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.83"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web-sys"
|
||||||
|
version = "0.3.60"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
34
third_party/dilithium/Cargo.toml
vendored
Normal file
34
third_party/dilithium/Cargo.toml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[package]
|
||||||
|
name = "dilithium"
|
||||||
|
version = "0.2.0-alpha.3"
|
||||||
|
authors = ["quininer <quininer@live.com>"]
|
||||||
|
description = "Digital Signatures from Module Lattices"
|
||||||
|
repository = "https://github.com/quininer/dilithium"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rng256 = { path = "../../libraries/rng256" }
|
||||||
|
rand_core = { version = "0.6", default-features = false }
|
||||||
|
arrayref = {version = "0.3", default-features = false}
|
||||||
|
itertools = { version = "0.7", default-features = false }
|
||||||
|
byteorder = { version = "1", default-features = false }
|
||||||
|
sha3 = { version = "0.7.3", default-features = false }
|
||||||
|
digest = { version = "0.7", default-features = false }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
hex = "0.3"
|
||||||
|
structopt = "0.3.25"
|
||||||
|
criterion = "0.3"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = [ "rng256/std" ]
|
||||||
|
default = [ "dilithium5", "optimize_stack" ]
|
||||||
|
dilithium2 = []
|
||||||
|
dilithium3 = []
|
||||||
|
dilithium5 = []
|
||||||
|
optimize_stack = []
|
||||||
|
derive_debug = []
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "sign_bench"
|
||||||
|
harness = false
|
||||||
8
third_party/dilithium/LICENSE
vendored
Normal file
8
third_party/dilithium/LICENSE
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
MIT License
|
||||||
|
Copyright (c) 2017 quininer@live.com
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
7
third_party/dilithium/README.md
vendored
Normal file
7
third_party/dilithium/README.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Dilithium
|
||||||
|
---------
|
||||||
|
|
||||||
|
Digital Signatures from Module Lattices
|
||||||
|
|
||||||
|
* [CRYSTALS – Dilithium: Digital Signatures from Module Lattices](https://eprint.iacr.org/2017/633.pdf)
|
||||||
|
* [ref dilithium implemention](https://github.com/pq-crystals/dilithium)
|
||||||
70
third_party/dilithium/benches/sign_bench.rs
vendored
Normal file
70
third_party/dilithium/benches/sign_bench.rs
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
// Benchmarks for key generation and signing with Dilithium.
|
||||||
|
// cargo criterion --features std
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
|
extern crate criterion;
|
||||||
|
extern crate dilithium;
|
||||||
|
extern crate rng256;
|
||||||
|
|
||||||
|
use core::time::Duration;
|
||||||
|
use criterion::*;
|
||||||
|
use dilithium::sign::SecKey;
|
||||||
|
use rng256::Rng256;
|
||||||
|
|
||||||
|
const SAMPLE_SIZE: usize = 1000;
|
||||||
|
const MEASUREMENT_TIME: Duration = Duration::from_secs(10);
|
||||||
|
|
||||||
|
fn bench_sk(c: &mut Criterion) {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
c.bench_function("gensk", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| {},
|
||||||
|
|_| {
|
||||||
|
SecKey::gensk(&mut rng);
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_pk(c: &mut Criterion) {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
c.bench_function("genpk", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| SecKey::gensk(&mut rng),
|
||||||
|
|sk| {
|
||||||
|
sk.genpk();
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_sign(c: &mut Criterion) {
|
||||||
|
const MESSAGE_LENGTH: usize = 64;
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
c.bench_function("sign", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| {
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let mut message = [0; MESSAGE_LENGTH];
|
||||||
|
rng.fill_bytes(&mut message);
|
||||||
|
(sk, message)
|
||||||
|
},
|
||||||
|
|(sk, message)| {
|
||||||
|
sk.sign(&message);
|
||||||
|
},
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group! {
|
||||||
|
name = benches;
|
||||||
|
config = Criterion::default().sample_size(SAMPLE_SIZE).measurement_time(MEASUREMENT_TIME);
|
||||||
|
targets = bench_sk, bench_pk, bench_sign
|
||||||
|
}
|
||||||
|
criterion_main!(benches);
|
||||||
41
third_party/dilithium/examples/sign.rs
vendored
Normal file
41
third_party/dilithium/examples/sign.rs
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Command for changing the stack size:
|
||||||
|
// cargo run --example sign --features std -- --stack-size-kb (new value in KB)
|
||||||
|
|
||||||
|
extern crate dilithium;
|
||||||
|
extern crate rng256;
|
||||||
|
extern crate structopt;
|
||||||
|
|
||||||
|
use dilithium::sign::SecKey;
|
||||||
|
use rng256::Rng256;
|
||||||
|
use std::thread;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
const DEFAULT_STACK_SIZE_KB: &str = "81";
|
||||||
|
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
struct Opts {
|
||||||
|
#[structopt(long, default_value=DEFAULT_STACK_SIZE_KB)]
|
||||||
|
stack_size_kb: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let mut message = [0; 59];
|
||||||
|
rng.fill_bytes(&mut message);
|
||||||
|
sk.sign(&message);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let stack_size_kb = Opts::from_args().stack_size_kb;
|
||||||
|
|
||||||
|
// We bound the stack size for generating keys and signing in Dilithium.
|
||||||
|
let child = thread::Builder::new()
|
||||||
|
.stack_size(stack_size_kb * 1024)
|
||||||
|
.spawn(run)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Wait for thread to join
|
||||||
|
child.join().unwrap();
|
||||||
|
}
|
||||||
25
third_party/dilithium/src/lib.rs
vendored
Normal file
25
third_party/dilithium/src/lib.rs
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate arrayref;
|
||||||
|
extern crate byteorder;
|
||||||
|
extern crate digest;
|
||||||
|
extern crate itertools;
|
||||||
|
extern crate sha3;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod utils;
|
||||||
|
mod ntt;
|
||||||
|
mod packing;
|
||||||
|
pub mod params;
|
||||||
|
mod poly;
|
||||||
|
mod polyvec;
|
||||||
|
mod reduce;
|
||||||
|
mod rounding;
|
||||||
|
pub mod sign;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_mul;
|
||||||
94
third_party/dilithium/src/ntt.rs
vendored
Normal file
94
third_party/dilithium/src/ntt.rs
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use params::N;
|
||||||
|
use reduce::montgomery_reduce;
|
||||||
|
|
||||||
|
const ZETAS: [i32; N] = [
|
||||||
|
0, 25847, -2608894, -518909, 237124, -777960, -876248, 466468, 1826347, 2353451, -359251,
|
||||||
|
-2091905, 3119733, -2884855, 3111497, 2680103, 2725464, 1024112, -1079900, 3585928, -549488,
|
||||||
|
-1119584, 2619752, -2108549, -2118186, -3859737, -1399561, -3277672, 1757237, -19422, 4010497,
|
||||||
|
280005, 2706023, 95776, 3077325, 3530437, -1661693, -3592148, -2537516, 3915439, -3861115,
|
||||||
|
-3043716, 3574422, -2867647, 3539968, -300467, 2348700, -539299, -1699267, -1643818, 3505694,
|
||||||
|
-3821735, 3507263, -2140649, -1600420, 3699596, 811944, 531354, 954230, 3881043, 3900724,
|
||||||
|
-2556880, 2071892, -2797779, -3930395, -1528703, -3677745, -3041255, -1452451, 3475950,
|
||||||
|
2176455, -1585221, -1257611, 1939314, -4083598, -1000202, -3190144, -3157330, -3632928, 126922,
|
||||||
|
3412210, -983419, 2147896, 2715295, -2967645, -3693493, -411027, -2477047, -671102, -1228525,
|
||||||
|
-22981, -1308169, -381987, 1349076, 1852771, -1430430, -3343383, 264944, 508951, 3097992,
|
||||||
|
44288, -1100098, 904516, 3958618, -3724342, -8578, 1653064, -3249728, 2389356, -210977, 759969,
|
||||||
|
-1316856, 189548, -3553272, 3159746, -1851402, -2409325, -177440, 1315589, 1341330, 1285669,
|
||||||
|
-1584928, -812732, -1439742, -3019102, -3881060, -3628969, 3839961, 2091667, 3407706, 2316500,
|
||||||
|
3817976, -3342478, 2244091, -2446433, -3562462, 266997, 2434439, -1235728, 3513181, -3520352,
|
||||||
|
-3759364, -1197226, -3193378, 900702, 1859098, 909542, 819034, 495491, -1613174, -43260,
|
||||||
|
-522500, -655327, -3122442, 2031748, 3207046, -3556995, -525098, -768622, -3595838, 342297,
|
||||||
|
286988, -2437823, 4108315, 3437287, -3342277, 1735879, 203044, 2842341, 2691481, -2590150,
|
||||||
|
1265009, 4055324, 1247620, 2486353, 1595974, -3767016, 1250494, 2635921, -3548272, -2994039,
|
||||||
|
1869119, 1903435, -1050970, -1333058, 1237275, -3318210, -1430225, -451100, 1312455, 3306115,
|
||||||
|
-1962642, -1279661, 1917081, -2546312, -1374803, 1500165, 777191, 2235880, 3406031, -542412,
|
||||||
|
-2831860, -1671176, -1846953, -2584293, -3724270, 594136, -3776993, -2013608, 2432395, 2454455,
|
||||||
|
-164721, 1957272, 3369112, 185531, -1207385, -3183426, 162844, 1616392, 3014001, 810149,
|
||||||
|
1652634, -3694233, -1799107, -3038916, 3523897, 3866901, 269760, 2213111, -975884, 1717735,
|
||||||
|
472078, -426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893, -2939036,
|
||||||
|
-2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687, -554416, 3919660, -48306,
|
||||||
|
-1362209, 3937738, 1400424, -846154, 1976782,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// Implements forward NTT, in-place.
|
||||||
|
///
|
||||||
|
/// No modular reduction is performed after additions or substractions.
|
||||||
|
/// The output vector is in bitreversed order.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `p` - a polynomial in standard representation.
|
||||||
|
pub fn ntt(p: &mut [i32; N]) {
|
||||||
|
let mut k = 1;
|
||||||
|
let mut len = 128;
|
||||||
|
while len > 0 {
|
||||||
|
for start in Itertools::step(0..N, 2 * len) {
|
||||||
|
let zeta = i64::from(ZETAS[k]);
|
||||||
|
k += 1;
|
||||||
|
for j in start..(start + len) {
|
||||||
|
let t = montgomery_reduce(zeta * i64::from(p[j + len]));
|
||||||
|
p[j + len] = p[j] - t;
|
||||||
|
p[j] += t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements inverse NTT and multiplication by Montgomery factor 2^32.
|
||||||
|
///
|
||||||
|
/// The implementation is in-place.
|
||||||
|
/// No modular reduction is performed after additions or substractions.
|
||||||
|
/// Input coefficients must be smaller than Q in absolute value.
|
||||||
|
/// The output coefficients are smaller than Q in absolute value.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `p` - a polynomial in NTT representation.
|
||||||
|
pub fn invntt_frominvmont(p: &mut [i32; N]) {
|
||||||
|
let mut k = 255;
|
||||||
|
let mut len = 1;
|
||||||
|
|
||||||
|
while len < N {
|
||||||
|
for start in Itertools::step(0..N, 2 * len) {
|
||||||
|
let zeta = (-1) * i64::from(ZETAS[k]);
|
||||||
|
k -= 1;
|
||||||
|
|
||||||
|
for j in start..(start + len) {
|
||||||
|
let t = p[j];
|
||||||
|
p[j] += p[j + len];
|
||||||
|
p[j + len] = t - p[j + len];
|
||||||
|
p[j + len] = montgomery_reduce(zeta * i64::from(p[j + len]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// F = MONT^2 / 256 mod Q, where MONT = 2^32 mod Q.
|
||||||
|
const F: i64 = 41978;
|
||||||
|
for j in 0..N {
|
||||||
|
p[j] = montgomery_reduce(F * i64::from(p[j]));
|
||||||
|
}
|
||||||
|
}
|
||||||
164
third_party/dilithium/src/packing.rs
vendored
Normal file
164
third_party/dilithium/src/packing.rs
vendored
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
use params::{
|
||||||
|
K, L, N, OMEGA, PK_SIZE_PACKED, POLT1_SIZE_PACKED, POLZ_SIZE_PACKED, SEEDBYTES, SIG_SIZE_PACKED,
|
||||||
|
};
|
||||||
|
use poly::{self, Poly};
|
||||||
|
use polyvec::{PolyVecK, PolyVecL};
|
||||||
|
|
||||||
|
pub mod pk {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Decodes a public key with the shape: `pk || rho || encodings of t1`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `pk` - the encoded public key
|
||||||
|
/// * `rho` - output array for the randomness seed `rho`
|
||||||
|
/// * `t1` - output PolyVecK for the vector of polynomials `t1`
|
||||||
|
pub fn unpack(pk: &[u8; PK_SIZE_PACKED], rho: &mut [u8; SEEDBYTES], t1: &mut PolyVecK) {
|
||||||
|
let (rho_bytes, t1s_bytes) = array_refs!(pk, SEEDBYTES, POLT1_SIZE_PACKED * K);
|
||||||
|
|
||||||
|
rho.clone_from(rho_bytes);
|
||||||
|
for i in 0..K {
|
||||||
|
let t1_bytes = array_ref!(t1s_bytes, i * POLT1_SIZE_PACKED, POLT1_SIZE_PACKED);
|
||||||
|
poly::t1_unpack(&mut t1[i], t1_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encodes and Decodes a signature with the shape:
|
||||||
|
// c_seed || encodings of z || encodings of h
|
||||||
|
pub mod sign {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Encodes an array used to obtain the challenge `c`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sig` - the output array representing the encoded signature
|
||||||
|
/// * `c_seed` - array to be encoded
|
||||||
|
pub fn pack_c(sign: &mut [u8; SIG_SIZE_PACKED], c_seed: &[u8; SEEDBYTES]) {
|
||||||
|
let c_bytes = array_mut_ref!(sign, 0, SEEDBYTES);
|
||||||
|
for i in 0..SEEDBYTES {
|
||||||
|
c_bytes[i] = c_seed[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes `z[i]`, where z is a vector of `L` polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sig` - output array representing the encoded signature
|
||||||
|
/// * `z_component` - polynomial representing `z[i]`
|
||||||
|
/// * `i` - the index of the component to be encoded
|
||||||
|
pub fn pack_z_component(sign: &mut [u8; SIG_SIZE_PACKED], z_component: &Poly, i: usize) {
|
||||||
|
let z_bytes = array_mut_ref!(sign, SEEDBYTES + i * POLZ_SIZE_PACKED, POLZ_SIZE_PACKED);
|
||||||
|
poly::z_pack(z_bytes, &z_component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes `z`, where `z` is a vector of `L` polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sig` - output array representing the encoded signature
|
||||||
|
/// * `z` - vector of `L` polynomials`
|
||||||
|
#[cfg(not(feature = "optimize_stack"))]
|
||||||
|
pub fn pack_z(sign: &mut [u8; SIG_SIZE_PACKED], z: &PolyVecL) {
|
||||||
|
for i in 0..L {
|
||||||
|
pack_z_component(sign, &z[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes `h[i]`, where `h` is a vector of `K` polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sig` - output array representing the encoded signature
|
||||||
|
/// * `h_component` - polynomial representing `h[i]`
|
||||||
|
/// * `i` - the index of the component to be encoded
|
||||||
|
/// * `non_zero_coeff_index` - the index returned when encoding
|
||||||
|
/// `h[i - 1]` (0 if `i` = 0)
|
||||||
|
pub fn pack_h_component(
|
||||||
|
sign: &mut [u8; SIG_SIZE_PACKED],
|
||||||
|
h_component: &Poly,
|
||||||
|
i: usize,
|
||||||
|
non_zero_coeff_index: &mut usize,
|
||||||
|
) {
|
||||||
|
let h_bytes = array_mut_ref!(sign, SEEDBYTES + POLZ_SIZE_PACKED * L, OMEGA + K);
|
||||||
|
|
||||||
|
for j in 0..N {
|
||||||
|
if h_component[j] != 0 {
|
||||||
|
h_bytes[*non_zero_coeff_index] = j as u8;
|
||||||
|
*non_zero_coeff_index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h_bytes[OMEGA + i] = *non_zero_coeff_index as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encodes `h`, where `h` is a vector of `K` polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sig` - output array representing the encoded signature
|
||||||
|
/// * `h` - vector of `K` polynomials.
|
||||||
|
#[cfg(not(feature = "optimize_stack"))]
|
||||||
|
pub fn pack_h(sign: &mut [u8; SIG_SIZE_PACKED], h: &PolyVecK) {
|
||||||
|
let mut non_zero_coeff_index = 0;
|
||||||
|
for i in 0..K {
|
||||||
|
pack_h_component(sign, &h[i], i, &mut non_zero_coeff_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decodes the components of the signature.
|
||||||
|
///
|
||||||
|
/// The values are written into the output arguments `c_seed`,
|
||||||
|
/// `z`, and `h` from `sig`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `sig` - the encoded signature
|
||||||
|
/// * `c_seed` - output array for the seed used to compute the challenge
|
||||||
|
/// * `z` - output PolyVecL for the vector of polynomials `z`
|
||||||
|
/// * `h` - output PolyVecK for the vector of polynomials `h`
|
||||||
|
pub fn unpack(
|
||||||
|
sign: &[u8; SIG_SIZE_PACKED],
|
||||||
|
c_seed: &mut [u8; SEEDBYTES],
|
||||||
|
z: &mut PolyVecL,
|
||||||
|
h: &mut PolyVecK,
|
||||||
|
) -> bool {
|
||||||
|
let (c_bytes, z_bytes, h_bytes) =
|
||||||
|
array_refs!(sign, SEEDBYTES, POLZ_SIZE_PACKED * L, OMEGA + K);
|
||||||
|
|
||||||
|
for i in 0..SEEDBYTES {
|
||||||
|
c_seed[i] = c_bytes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..L {
|
||||||
|
let z_bytes = array_ref!(z_bytes, i * POLZ_SIZE_PACKED, POLZ_SIZE_PACKED);
|
||||||
|
poly::z_unpack(&mut z[i], z_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode h
|
||||||
|
let mut k = 0;
|
||||||
|
for i in 0..K {
|
||||||
|
if (h_bytes[OMEGA + i] as usize) < k || (h_bytes[OMEGA + i] as usize) > OMEGA {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for j in k..(h_bytes[OMEGA + i] as usize) {
|
||||||
|
// Coefficients are ordered for strong unforgeability
|
||||||
|
if j > k && h_bytes[j] <= h_bytes[j - 1] {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
h[i][h_bytes[j] as usize] = 1;
|
||||||
|
}
|
||||||
|
k = h_bytes[OMEGA + i] as usize;
|
||||||
|
}
|
||||||
|
// Extra indices are zero for strong unforgeability
|
||||||
|
if h_bytes[k..OMEGA].iter().any(|&v| v != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
78
third_party/dilithium/src/params.rs
vendored
Normal file
78
third_party/dilithium/src/params.rs
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#![cfg_attr(feature = "cargo-clippy", allow(unreadable_literal))]
|
||||||
|
|
||||||
|
pub const SEEDBYTES: usize = 32;
|
||||||
|
pub const CRHBYTES: usize = 64;
|
||||||
|
pub const N: usize = 256;
|
||||||
|
pub const Q: i32 = 8380417;
|
||||||
|
pub const D: usize = 13;
|
||||||
|
pub const ROOT_OF_UNITY: usize = 1753;
|
||||||
|
|
||||||
|
#[cfg(feature = "dilithium2")]
|
||||||
|
mod mode {
|
||||||
|
use super::Q;
|
||||||
|
pub const K: usize = 4;
|
||||||
|
pub const L: usize = 4;
|
||||||
|
pub const ETA: i32 = 2;
|
||||||
|
pub const TAU: usize = 39;
|
||||||
|
pub const BETA: i32 = 78;
|
||||||
|
pub const GAMMA1: i32 = 1 << 17;
|
||||||
|
pub const GAMMA2: i32 = (Q - 1) / 88;
|
||||||
|
pub const OMEGA: usize = 80;
|
||||||
|
|
||||||
|
pub const POLZ_SIZE_PACKED: usize = 576;
|
||||||
|
pub const POLW1_SIZE_PACKED: usize = 192;
|
||||||
|
pub const POLETA_SIZE_PACKED: usize = 96;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dilithium3")]
|
||||||
|
mod mode {
|
||||||
|
use super::Q;
|
||||||
|
pub const K: usize = 6;
|
||||||
|
pub const L: usize = 5;
|
||||||
|
pub const ETA: i32 = 4;
|
||||||
|
pub const TAU: usize = 49;
|
||||||
|
pub const BETA: i32 = 196;
|
||||||
|
pub const GAMMA1: i32 = 1 << 19;
|
||||||
|
pub const GAMMA2: i32 = (Q - 1) / 32;
|
||||||
|
pub const OMEGA: usize = 55;
|
||||||
|
|
||||||
|
pub const POLZ_SIZE_PACKED: usize = 640;
|
||||||
|
pub const POLW1_SIZE_PACKED: usize = 128;
|
||||||
|
pub const POLETA_SIZE_PACKED: usize = 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "dilithium5")]
|
||||||
|
mod mode {
|
||||||
|
use super::Q;
|
||||||
|
pub const K: usize = 8;
|
||||||
|
pub const L: usize = 7;
|
||||||
|
pub const ETA: i32 = 2;
|
||||||
|
pub const TAU: usize = 60;
|
||||||
|
pub const BETA: i32 = 120;
|
||||||
|
pub const GAMMA1: i32 = 1 << 19;
|
||||||
|
pub const GAMMA2: i32 = (Q - 1) / 32;
|
||||||
|
pub const OMEGA: usize = 75;
|
||||||
|
|
||||||
|
pub const POLZ_SIZE_PACKED: usize = 640;
|
||||||
|
pub const POLW1_SIZE_PACKED: usize = 128;
|
||||||
|
pub const POLETA_SIZE_PACKED: usize = 96;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::mode::*;
|
||||||
|
|
||||||
|
pub const POLT1_SIZE_PACKED: usize = 320;
|
||||||
|
pub const POLT0_SIZE_PACKED: usize = 416;
|
||||||
|
|
||||||
|
pub const PK_SIZE_PACKED: usize = SEEDBYTES + K * POLT1_SIZE_PACKED;
|
||||||
|
pub const SK_SIZE_PACKED: usize = 3 * SEEDBYTES + (L + K) * POLETA_SIZE_PACKED;
|
||||||
|
pub const SK_SIZE_PACKED_ORIGINAL: usize =
|
||||||
|
3 * SEEDBYTES + (L + K) * POLETA_SIZE_PACKED + K * POLT0_SIZE_PACKED;
|
||||||
|
pub const SIG_SIZE_PACKED: usize = L * POLZ_SIZE_PACKED + (OMEGA + K) + SEEDBYTES;
|
||||||
|
|
||||||
|
pub const PUBLICKEYBYTES: usize = PK_SIZE_PACKED;
|
||||||
|
pub const SECRETKEYBYTES: usize = SK_SIZE_PACKED;
|
||||||
|
pub const BYTES: usize = SIG_SIZE_PACKED;
|
||||||
|
|
||||||
|
/// `MONT = 2^32 mod Q`
|
||||||
|
pub const MONT: i64 = -4186625;
|
||||||
|
pub const QINV: isize = 58728449;
|
||||||
775
third_party/dilithium/src/poly.rs
vendored
Normal file
775
third_party/dilithium/src/poly.rs
vendored
Normal file
@@ -0,0 +1,775 @@
|
|||||||
|
use byteorder::{ByteOrder, LittleEndian};
|
||||||
|
pub use ntt::{invntt_frominvmont as invntt_montgomery, ntt};
|
||||||
|
use params::{
|
||||||
|
CRHBYTES, D, ETA, GAMMA1, GAMMA2, N, POLETA_SIZE_PACKED, POLT1_SIZE_PACKED, POLW1_SIZE_PACKED,
|
||||||
|
POLZ_SIZE_PACKED, Q, SEEDBYTES, TAU,
|
||||||
|
};
|
||||||
|
use reduce::{caddq as xcaddq, freeze as xfreeze, montgomery_reduce, reduce32};
|
||||||
|
use rounding;
|
||||||
|
|
||||||
|
pub type Poly = [i32; N];
|
||||||
|
|
||||||
|
/// Reduces the coefficients of the polynomial `a` to [-6283009,6283007].
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
pub fn reduce(a: &mut Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
a[i] = reduce32(a[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds `Q` to every negative coefficient in `a`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
pub fn caddq(a: &mut Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
a[i] = xcaddq(a[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// For every coefficient `x` in `a`, computes `x mod Q`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
pub fn freeze(a: &mut Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
a[i] = xfreeze(a[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes `c = a + b`, where `c`, `a`, and `b` are polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
pub fn add(c: &mut Poly, a: &Poly, b: &Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
c[i] = a[i] + b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes `c = c + a`, where `c`, and `a` are polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
pub fn add_assign(c: &mut Poly, a: &Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
c[i] += a[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes `c = a - b`, where `c`, `a` and `b` are polynomials.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
pub fn sub(c: &mut Poly, a: &Poly, b: &Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
c[i] = a[i] - b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiplies the polynomial `a` by `2^D` without modular reduction.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial with coefficients smaller than than 2^{31-D}
|
||||||
|
/// in absolute value
|
||||||
|
pub fn shift_left(a: &mut Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
a[i] <<= D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes `c = a * b` in NTT domain representation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `c` - the output polynomial, in NTT domain representation
|
||||||
|
/// * `a` - a polynomial in NTT domain representation
|
||||||
|
/// * `b` - a polynomial in NTT domain representation
|
||||||
|
pub fn pointwise_invmontgomery(c: &mut Poly, a: &Poly, b: &Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
c[i] = montgomery_reduce((a[i] as i64) * (b[i] as i64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `c = a * b` in standard representation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in NTT domain representation
|
||||||
|
/// * `b` - a polynomial in NTT domain representation
|
||||||
|
pub fn multiply(a: &Poly, b: &Poly) -> Poly {
|
||||||
|
let mut c = [0; N];
|
||||||
|
pointwise_invmontgomery(&mut c, a, b);
|
||||||
|
invntt_montgomery(&mut c);
|
||||||
|
reduce(&mut c);
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decomposes a into the quotient and remainder of its division with `2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// For every coefficient `c` of the polynomial `a`, computes `c0`, `c1`
|
||||||
|
/// such that `c mod Q = c1 * 2^D + c0`, with `-2^{D-1} < c0 <= 2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in standard representation (not NTT)
|
||||||
|
/// * `a0` - output polynomial representing the remainder (coefficients `c0`)
|
||||||
|
/// * `a1` - output polynomial representing the quotient (coefficients `c1`)
|
||||||
|
pub fn power2round(a: &Poly, a0: &mut Poly, a1: &mut Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
let (x, y) = rounding::power2round(a[i]);
|
||||||
|
a0[i] = x;
|
||||||
|
a1[i] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains the remainder of dividing `a` with `2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// For every coefficient `c` of the polynomial a, computes `c0`, `c1`
|
||||||
|
/// such that `c mod Q = c1 * 2^D + c0`, with `-2^{D-1} < c0 <= 2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in standard representation (not NTT)
|
||||||
|
/// * `a0` - output polynomial representing the remainder (coefficients `c0`)
|
||||||
|
pub fn power2round_remainder(a: &Poly) -> Poly {
|
||||||
|
let mut remainder = [0; N];
|
||||||
|
for i in 0..N {
|
||||||
|
let (x, _) = rounding::power2round(a[i]);
|
||||||
|
remainder[i] = x;
|
||||||
|
}
|
||||||
|
remainder
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains the quotient of dividing `a` with `2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// For every coefficient `c` of the polynomial `a`, computes `c0, c1`
|
||||||
|
/// such that `c mod Q = c1 * 2^D + c0`, with `-2^{D-1} < c0 <= 2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in standard representation (not NTT)
|
||||||
|
/// * `a1` - output polynomial representing the quotient (coefficients `c1`)
|
||||||
|
pub fn power2round_quotient(a: &Poly) -> Poly {
|
||||||
|
let mut quotient = [0; N];
|
||||||
|
for i in 0..N {
|
||||||
|
let (_, y) = rounding::power2round(a[i]);
|
||||||
|
quotient[i] = y;
|
||||||
|
}
|
||||||
|
quotient
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains the high bits and the low bits of `a`.
|
||||||
|
///
|
||||||
|
/// For every coefficient `c` of the input polynomial `a`, computes its
|
||||||
|
/// high bits `c1` and low bits `c0` such that `c mod Q = c1*ALPHA + c0`,
|
||||||
|
/// where -ALPHA/2 < c0 <= ALPHA/2.
|
||||||
|
/// Exception: if `c1 = (Q-1)/ALPHA`, `c1` is set to 0 and `c0 = c mod Q - Q`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in standard representation (not NTT)
|
||||||
|
/// * `a0` - output polynomial representing `a`'s low bits (coefficients `c0`)
|
||||||
|
/// * `a1` - output polynomial representing `a`'s high bits (coefficients `c1`)
|
||||||
|
pub fn decompose(a: &Poly, a0: &mut Poly, a1: &mut Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
let (x, y) = rounding::decompose(a[i]);
|
||||||
|
a0[i] = x; // low bits
|
||||||
|
a1[i] = y; // high bits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a polynomial whose coefficients are the high bits of `a`.
|
||||||
|
///
|
||||||
|
/// For every coefficient `c` of the input polynomial a, computes its
|
||||||
|
/// high bits `c1` and low bits `c0` such that `c mod Q = c1*ALPHA + c0`,
|
||||||
|
/// where `-ALPHA/2 < c0 <= ALPHA/2`.
|
||||||
|
/// Exception: if `c1 = (Q-1)/ALPHA`, `c1` is set to 0 and `c0 = c mod Q - Q`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in standard representation (not NTT)
|
||||||
|
#[cfg(feature = "optimize_stack")]
|
||||||
|
pub fn high_bits(a: &Poly) -> Poly {
|
||||||
|
let mut high_bits: Poly = [0; N];
|
||||||
|
for i in 0..N {
|
||||||
|
let (_x, y) = rounding::decompose(a[i]);
|
||||||
|
high_bits[i] = y;
|
||||||
|
}
|
||||||
|
return high_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a polynomial whose coefficients are the low bits of `a`.
|
||||||
|
///
|
||||||
|
/// For every coefficient `c` of the input polynomial `a`, computes its
|
||||||
|
/// high bits `c1` and low bits `c0` such that `c mod Q = c1*ALPHA + c0`,
|
||||||
|
/// where `-ALPHA/2 < c0 <= ALPHA/2`.
|
||||||
|
/// Exception: if `c1 = (Q-1)/ALPHA`, `c1` is set to 0 and `c0 = c mod Q - Q`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial in standard representation (not NTT)
|
||||||
|
#[cfg(feature = "optimize_stack")]
|
||||||
|
pub fn low_bits(a: &Poly) -> Poly {
|
||||||
|
let mut low_bits: Poly = [0; N];
|
||||||
|
for i in 0..N {
|
||||||
|
let (x, _y) = rounding::decompose(a[i]);
|
||||||
|
low_bits[i] = x;
|
||||||
|
}
|
||||||
|
return low_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Makes the hint used to obtain `a` from an approximate result `b`.
|
||||||
|
///
|
||||||
|
/// Given a polynomial of low bits `a`, and a polynomial of high bits `b`,
|
||||||
|
/// computes the hint polynomial `h`. The coefficient of `h` indicate
|
||||||
|
/// whether the low bits of the corresponding coefficient of the input
|
||||||
|
/// polynomial `a` overflow into the high bits (`b`).
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
/// * `b` - a polynomial
|
||||||
|
/// * `h` - the output polynomial
|
||||||
|
pub fn make_hint(a: &Poly, b: &Poly, h: &mut Poly) -> usize {
|
||||||
|
let mut s = 0;
|
||||||
|
|
||||||
|
for i in 0..N {
|
||||||
|
h[i] = rounding::make_hint(a[i], b[i]) as i32;
|
||||||
|
s += h[i] as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses a hint polynomial `h` to correct the high bits of a polynomial `b`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - the output corrected polynomial
|
||||||
|
/// * `b` - a polynomial
|
||||||
|
/// * `h` - the hint polynomial: containing values 0 or 1
|
||||||
|
pub fn use_hint(a: &mut Poly, b: &Poly, h: &Poly) {
|
||||||
|
for i in 0..N {
|
||||||
|
a[i] = rounding::use_hint(b[i], h[i] as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the infinity norm of a polynomial `a` against a given bound `b`.
|
||||||
|
///
|
||||||
|
/// The input coefficients must be reduced by `reduce32()`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a polynomial
|
||||||
|
/// * `b` - the bound.
|
||||||
|
pub fn chknorm(a: &Poly, b: i32) -> bool {
|
||||||
|
if b > (Q - 1) / 8 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is ok to leak which coefficient violates the bound since
|
||||||
|
// the probability for each coefficient is independent of secret
|
||||||
|
// data but we must not leak the sign of the centralized representative.
|
||||||
|
for i in 0..N {
|
||||||
|
let mut t: i32 = a[i] >> 31;
|
||||||
|
t = a[i] - (t & 2 * a[i]);
|
||||||
|
|
||||||
|
if t >= b {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Samples a polynomial with random coefficients in `[0, Q - 1]`.
|
||||||
|
///
|
||||||
|
/// The sampling is done by performing rejection sampling on the output stream
|
||||||
|
/// of `SHAKE256(seed|nonce)`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - the output polynomial
|
||||||
|
/// * `seed` - an array of random bytes
|
||||||
|
/// * `nonce` - a number.
|
||||||
|
pub fn uniform(a: &mut Poly, seed: &[u8; SEEDBYTES], nonce: u16) {
|
||||||
|
use digest::{ExtendableOutput, Input, XofReader};
|
||||||
|
use sha3::Shake128;
|
||||||
|
|
||||||
|
fn rej_uniform(a: &mut [i32], i_start: usize, buf: &[u8], buf_len: usize) -> usize {
|
||||||
|
let mut ctr = 0usize;
|
||||||
|
let mut pos = 0usize;
|
||||||
|
let mut t: u32;
|
||||||
|
|
||||||
|
let len = a.len() - i_start;
|
||||||
|
|
||||||
|
while ctr < len && pos + 3 <= buf_len {
|
||||||
|
t = buf[pos] as u32;
|
||||||
|
pos += 1;
|
||||||
|
t |= (buf[pos] as u32) << 8;
|
||||||
|
pos += 1;
|
||||||
|
t |= (buf[pos] as u32) << 16;
|
||||||
|
pos += 1;
|
||||||
|
t &= 0x7FFFFF;
|
||||||
|
if t < (Q as u32) {
|
||||||
|
a[i_start + ctr] = t as i32;
|
||||||
|
ctr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut hasher = Shake128::default();
|
||||||
|
hasher.process(seed);
|
||||||
|
|
||||||
|
let nonce0 = (nonce & ((1 << 8) - 1)) as u8;
|
||||||
|
let nonce1 = (nonce >> 8) as u8;
|
||||||
|
hasher.process(&[nonce0, nonce1]);
|
||||||
|
|
||||||
|
const STREAM128_BLOCKBYTES: usize = 168;
|
||||||
|
const POLY_UNIFORM_NBLOCKS: usize = (768 + STREAM128_BLOCKBYTES - 1) / STREAM128_BLOCKBYTES;
|
||||||
|
let mut buf_len = POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES;
|
||||||
|
let mut buf = [0u8; POLY_UNIFORM_NBLOCKS * STREAM128_BLOCKBYTES + 2];
|
||||||
|
|
||||||
|
let mut xof = hasher.xof_result();
|
||||||
|
xof.read(&mut buf[..buf_len]);
|
||||||
|
|
||||||
|
let mut ctr = rej_uniform(a, 0, &buf, buf_len);
|
||||||
|
|
||||||
|
while ctr < N {
|
||||||
|
let off = buf_len % 3;
|
||||||
|
for i in 0..off {
|
||||||
|
buf[i] = buf[buf_len - off + i];
|
||||||
|
}
|
||||||
|
for i in 0..STREAM128_BLOCKBYTES {
|
||||||
|
buf[off + i] = 0;
|
||||||
|
}
|
||||||
|
xof.read(&mut buf[off..off + STREAM128_BLOCKBYTES]);
|
||||||
|
|
||||||
|
buf_len = STREAM128_BLOCKBYTES + off;
|
||||||
|
|
||||||
|
ctr += rej_uniform(a, ctr, &buf, buf_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Samples a polynomial with random coefficients in `[-ETA, ETA]`.
|
||||||
|
///
|
||||||
|
/// The sampling is done by performing rejection sampling on the output stream
|
||||||
|
/// of `SHAKE256(seed|nonce)`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - the output polynomial
|
||||||
|
/// * `seed` - an array of random bytes
|
||||||
|
/// * `nonce` - a number.
|
||||||
|
pub fn uniform_eta(a: &mut Poly, seed: &[u8; CRHBYTES], nonce: u16) {
|
||||||
|
use digest::{ExtendableOutput, Input, XofReader};
|
||||||
|
use sha3::Shake256;
|
||||||
|
|
||||||
|
const STREAM256_BLOCKBYTES: usize = 136;
|
||||||
|
|
||||||
|
const POLY_UNIFORM_ETA_NBLOCKS: usize = match ETA {
|
||||||
|
2 => (136 + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES,
|
||||||
|
_ => (227 + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn rej_eta(a: &mut [i32], a_start: usize, buf: &[u8], buf_len: usize) -> usize {
|
||||||
|
let mut ctr = 0;
|
||||||
|
let mut pos = 0;
|
||||||
|
|
||||||
|
while a_start + ctr < a.len() && pos < buf_len {
|
||||||
|
let mut t0 = (buf[pos] as u32) & 0x0F;
|
||||||
|
let mut t1 = (buf[pos] as u32) >> 4;
|
||||||
|
pos += 1;
|
||||||
|
|
||||||
|
if ETA == 2 {
|
||||||
|
if t0 < 15 {
|
||||||
|
t0 = t0 - (205 * t0 >> 10) * 5;
|
||||||
|
a[a_start + ctr] = 2 - (t0 as i32);
|
||||||
|
ctr += 1;
|
||||||
|
}
|
||||||
|
if t1 < 15 && a_start + ctr < a.len() {
|
||||||
|
t1 = t1 - (205 * t1 >> 10) * 5;
|
||||||
|
a[a_start + ctr] = 2 - (t1 as i32);
|
||||||
|
ctr += 1;
|
||||||
|
}
|
||||||
|
} else if ETA == 4 {
|
||||||
|
if t0 < 9 {
|
||||||
|
a[a_start + ctr] = 4 - (t0 as i32);
|
||||||
|
ctr += 1;
|
||||||
|
}
|
||||||
|
if t1 < 9 && a_start + ctr < a.len() {
|
||||||
|
a[a_start + ctr] = 4 - (t1 as i32);
|
||||||
|
ctr += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf_len = POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES;
|
||||||
|
let mut buf = [0u8; POLY_UNIFORM_ETA_NBLOCKS * STREAM256_BLOCKBYTES];
|
||||||
|
|
||||||
|
let mut hasher = Shake256::default();
|
||||||
|
hasher.process(seed);
|
||||||
|
let nonce0 = (nonce & ((1 << 8) - 1)) as u8;
|
||||||
|
let nonce1 = (nonce >> 8) as u8;
|
||||||
|
hasher.process(&[nonce0, nonce1]);
|
||||||
|
let mut xof = hasher.xof_result();
|
||||||
|
xof.read(&mut buf[..buf_len]);
|
||||||
|
|
||||||
|
let mut ctr = rej_eta(a, 0, &buf, buf_len);
|
||||||
|
|
||||||
|
while ctr < N {
|
||||||
|
xof.read(&mut buf[..STREAM256_BLOCKBYTES]);
|
||||||
|
ctr += rej_eta(a, ctr, &buf, STREAM256_BLOCKBYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Samples a polynomial with random coefficients in `[-(GAMMA1 - 1), GAMMA1]`.
|
||||||
|
///
|
||||||
|
/// The sampling is done by unpacking the first `POLZ_SIZE_PACKED` bytes in the
|
||||||
|
/// output stream of `SHAKE256(seed|nonce)`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - the output polynomial
|
||||||
|
/// * `seed` - an array of random bytes
|
||||||
|
/// * `nonce` - a number.
|
||||||
|
pub fn uniform_gamma1m1(a: &mut Poly, seed: &[u8; CRHBYTES], nonce: u16) {
|
||||||
|
use digest::{ExtendableOutput, Input, XofReader};
|
||||||
|
use sha3::Shake256;
|
||||||
|
const SHAKE256_RATE: usize = 136;
|
||||||
|
|
||||||
|
let mut outbuf = [0; 5 * SHAKE256_RATE];
|
||||||
|
let mut nonce_bytes = [0; 2];
|
||||||
|
LittleEndian::write_u16(&mut nonce_bytes, nonce);
|
||||||
|
|
||||||
|
let mut hasher = Shake256::default();
|
||||||
|
hasher.process(seed);
|
||||||
|
hasher.process(&nonce_bytes);
|
||||||
|
|
||||||
|
let mut xof = hasher.xof_result();
|
||||||
|
xof.read(&mut outbuf);
|
||||||
|
|
||||||
|
z_unpack(a, array_ref!(&outbuf, 0, POLZ_SIZE_PACKED));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a polynomial with coefficients in {0, -1, 1}.
|
||||||
|
///
|
||||||
|
/// Returns a polynomial sampled with `TAU` nonzero coefficients in
|
||||||
|
/// {-1, 1} and `N - TAU` zero coefficients using the output stream
|
||||||
|
/// of `SHAKE256(seed)`.
|
||||||
|
/// More details can be found in the paper, in section 2.3.
|
||||||
|
/// <https://eprint.iacr.org/2017/633.pdf>
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `seed` - an array of bytes
|
||||||
|
pub fn build_challenge_from_seed(seed: &[u8; SEEDBYTES]) -> Poly {
|
||||||
|
use digest::{ExtendableOutput, Input, XofReader};
|
||||||
|
use sha3::Shake256;
|
||||||
|
const SHAKE256_RATE: usize = 136;
|
||||||
|
|
||||||
|
let mut outbuf = [0u8; SHAKE256_RATE];
|
||||||
|
|
||||||
|
let mut hasher = Shake256::default();
|
||||||
|
hasher.process(seed);
|
||||||
|
let mut xof = hasher.xof_result();
|
||||||
|
xof.read(&mut outbuf);
|
||||||
|
|
||||||
|
let mut signs: u64 = 0;
|
||||||
|
for i in 0..8 {
|
||||||
|
signs |= (outbuf[i] as u64) << 8 * i;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pos = 8;
|
||||||
|
let mut c = [0i32; N];
|
||||||
|
for i in (N - TAU)..N {
|
||||||
|
let b = loop {
|
||||||
|
if pos >= SHAKE256_RATE {
|
||||||
|
xof.read(&mut outbuf);
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let b = outbuf[pos] as usize;
|
||||||
|
pos += 1;
|
||||||
|
if b <= i {
|
||||||
|
break b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
c[i] = c[b];
|
||||||
|
c[b] = 1i32 - (2 * (signs & 1) as i32);
|
||||||
|
signs >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bit-packs a polynomial with coefficients in `[-ETA, ETA]`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output array, which will contain the polynomial's encoding
|
||||||
|
/// * `a` - the polynomial to encode
|
||||||
|
#[inline]
|
||||||
|
pub fn eta_pack(r: &mut [u8; POLETA_SIZE_PACKED], a: &Poly) {
|
||||||
|
if ETA == 2 {
|
||||||
|
let mut t = [0u8; 8];
|
||||||
|
for i in 0..(N / 8) {
|
||||||
|
for j in 0..8 {
|
||||||
|
t[j] = (ETA - a[8 * i + j]) as u8;
|
||||||
|
}
|
||||||
|
|
||||||
|
r[3 * i + 0] = (t[0] >> 0) | (t[1] << 3) | (t[2] << 6);
|
||||||
|
r[3 * i + 1] = (t[2] >> 2) | (t[3] << 1) | (t[4] << 4) | (t[5] << 7);
|
||||||
|
r[3 * i + 2] = (t[5] >> 1) | (t[6] << 2) | (t[7] << 5);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut t = [0u8; 2];
|
||||||
|
for i in 0..(N / 2) {
|
||||||
|
t[0] = (ETA - a[2 * i + 0]) as u8;
|
||||||
|
t[1] = (ETA - a[2 * i + 1]) as u8;
|
||||||
|
r[i] = t[0] | (t[1] << 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpacks a polynomial with coefficients in `[-ETA, ETA]`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output decoded polynomial
|
||||||
|
/// * `a` - the polynomial's encoding
|
||||||
|
#[inline]
|
||||||
|
pub fn eta_unpack(r: &mut Poly, a: &[u8; POLETA_SIZE_PACKED]) {
|
||||||
|
if ETA == 2 {
|
||||||
|
for i in 0..(N / 8) {
|
||||||
|
r[8 * i + 0] = ((a[3 * i + 0] as i32) >> 0) & 7;
|
||||||
|
r[8 * i + 1] = ((a[3 * i + 0] as i32) >> 3) & 7;
|
||||||
|
r[8 * i + 2] = (((a[3 * i + 0] as i32) >> 6) | ((a[3 * i + 1] as i32) << 2)) & 7;
|
||||||
|
r[8 * i + 3] = ((a[3 * i + 1] as i32) >> 1) & 7;
|
||||||
|
r[8 * i + 4] = ((a[3 * i + 1] as i32) >> 4) & 7;
|
||||||
|
r[8 * i + 5] = (((a[3 * i + 1] as i32) >> 7) | ((a[3 * i + 2] as i32) << 1)) & 7;
|
||||||
|
r[8 * i + 6] = ((a[3 * i + 2] as i32) >> 2) & 7;
|
||||||
|
r[8 * i + 7] = ((a[3 * i + 2] as i32) >> 5) & 7;
|
||||||
|
|
||||||
|
for j in 0..8 {
|
||||||
|
r[8 * i + j] = ETA - r[8 * i + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i in 0..(N / 2) {
|
||||||
|
r[2 * i + 0] = (a[i] as i32) & 0x0F;
|
||||||
|
r[2 * i + 1] = (a[i] as i32) >> 4;
|
||||||
|
r[2 * i + 0] = ETA - r[2 * i + 0];
|
||||||
|
r[2 * i + 1] = ETA - r[2 * i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bit-packs a polynomial with coefficients fitting in 10 bits.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output array, which will contain the polynomial's encoding
|
||||||
|
/// * `a` - the polynomial to encode
|
||||||
|
#[inline]
|
||||||
|
pub fn t1_pack(r: &mut [u8; POLT1_SIZE_PACKED], a: &Poly) {
|
||||||
|
for i in 0..(N / 4) {
|
||||||
|
r[5 * i + 0] = (a[4 * i + 0] >> 0) as u8;
|
||||||
|
r[5 * i + 1] = ((a[4 * i + 0] >> 8) | (a[4 * i + 1] << 2)) as u8;
|
||||||
|
r[5 * i + 2] = ((a[4 * i + 1] >> 6) | (a[4 * i + 2] << 4)) as u8;
|
||||||
|
r[5 * i + 3] = ((a[4 * i + 2] >> 4) | (a[4 * i + 3] << 6)) as u8;
|
||||||
|
r[5 * i + 4] = (a[4 * i + 3] >> 2) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpacks a polynomial with coefficients fitting in 10 bits.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output decoded polynomial
|
||||||
|
/// * `a` - the polynomial's encoding
|
||||||
|
#[inline]
|
||||||
|
pub fn t1_unpack(r: &mut Poly, a: &[u8; POLT1_SIZE_PACKED]) {
|
||||||
|
for i in 0..(N / 4) {
|
||||||
|
r[4 * i + 0] =
|
||||||
|
((((a[5 * i + 0] >> 0) as u32) | ((a[5 * i + 1] as u32) << 8)) & 0x3FF) as i32;
|
||||||
|
r[4 * i + 1] =
|
||||||
|
((((a[5 * i + 1] >> 2) as u32) | ((a[5 * i + 2] as u32) << 6)) & 0x3FF) as i32;
|
||||||
|
r[4 * i + 2] =
|
||||||
|
((((a[5 * i + 2] >> 4) as u32) | ((a[5 * i + 3] as u32) << 4)) & 0x3FF) as i32;
|
||||||
|
r[4 * i + 3] =
|
||||||
|
((((a[5 * i + 3] >> 6) as u32) | ((a[5 * i + 4] as u32) << 2)) & 0x3FF) as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Packs a polynomial with coefficients in `[-(GAMMA1 - 1), GAMMA1]`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output array, which will contain the polynomial's encoding
|
||||||
|
/// * `a` - the polynomial to encode
|
||||||
|
#[inline]
|
||||||
|
pub fn z_pack(r: &mut [u8; POLZ_SIZE_PACKED], a: &Poly) {
|
||||||
|
let mut t = [0u32; 4];
|
||||||
|
|
||||||
|
if GAMMA1 == (1 << 17) {
|
||||||
|
for i in 0..(N / 4) {
|
||||||
|
for j in 0..4 {
|
||||||
|
t[j] = (GAMMA1 - a[4 * i + j]) as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
r[9 * i + 0] = t[0] as u8;
|
||||||
|
r[9 * i + 1] = (t[0] >> 8) as u8;
|
||||||
|
r[9 * i + 2] = (t[0] >> 16) as u8;
|
||||||
|
r[9 * i + 2] |= (t[1] << 2) as u8;
|
||||||
|
r[9 * i + 3] = (t[1] >> 6) as u8;
|
||||||
|
r[9 * i + 4] = (t[1] >> 14) as u8;
|
||||||
|
r[9 * i + 4] |= (t[2] << 4) as u8;
|
||||||
|
r[9 * i + 5] = (t[2] >> 4) as u8;
|
||||||
|
r[9 * i + 6] = (t[2] >> 12) as u8;
|
||||||
|
r[9 * i + 6] |= (t[3] << 6) as u8;
|
||||||
|
r[9 * i + 7] = (t[3] >> 2) as u8;
|
||||||
|
r[9 * i + 8] = (t[3] >> 10) as u8;
|
||||||
|
}
|
||||||
|
} else if GAMMA1 == (1 << 19) {
|
||||||
|
for i in 0..(N / 2) {
|
||||||
|
t[0] = (GAMMA1 - a[2 * i + 0]) as u32;
|
||||||
|
t[1] = (GAMMA1 - a[2 * i + 1]) as u32;
|
||||||
|
|
||||||
|
r[5 * i + 0] = t[0] as u8;
|
||||||
|
r[5 * i + 1] = (t[0] >> 8) as u8;
|
||||||
|
r[5 * i + 2] = (t[0] >> 16) as u8;
|
||||||
|
r[5 * i + 2] |= (t[1] << 4) as u8;
|
||||||
|
r[5 * i + 3] = (t[1] >> 4) as u8;
|
||||||
|
r[5 * i + 4] = (t[1] >> 12) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unpacks a polynomial with coefficients in `[-(GAMMA1 - 1), GAMMA1]`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output decoded polynomial
|
||||||
|
/// * `a` - the polynomial's encoding
|
||||||
|
#[inline]
|
||||||
|
pub fn z_unpack(r: &mut Poly, a: &[u8; POLZ_SIZE_PACKED]) {
|
||||||
|
if GAMMA1 == (1 << 17) {
|
||||||
|
for i in 0..(N / 4) {
|
||||||
|
r[4 * i + 0] = a[9 * i + 0] as i32;
|
||||||
|
r[4 * i + 0] |= (a[9 * i + 1] as i32) << 8;
|
||||||
|
r[4 * i + 0] |= (a[9 * i + 2] as i32) << 16;
|
||||||
|
r[4 * i + 0] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r[4 * i + 1] = (a[9 * i + 2] >> 2) as i32;
|
||||||
|
r[4 * i + 1] |= (a[9 * i + 3] as i32) << 6;
|
||||||
|
r[4 * i + 1] |= (a[9 * i + 4] as i32) << 14;
|
||||||
|
r[4 * i + 1] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r[4 * i + 2] = (a[9 * i + 4] >> 4) as i32;
|
||||||
|
r[4 * i + 2] |= (a[9 * i + 5] as i32) << 4;
|
||||||
|
r[4 * i + 2] |= (a[9 * i + 6] as i32) << 12;
|
||||||
|
r[4 * i + 2] &= 0x3FFFF;
|
||||||
|
|
||||||
|
r[4 * i + 3] = (a[9 * i + 6] >> 6) as i32;
|
||||||
|
r[4 * i + 3] |= (a[9 * i + 7] as i32) << 2;
|
||||||
|
r[4 * i + 3] |= (a[9 * i + 8] as i32) << 10;
|
||||||
|
r[4 * i + 3] &= 0x3FFFF;
|
||||||
|
|
||||||
|
for j in 0..4 {
|
||||||
|
r[4 * i + j] = GAMMA1 - r[4 * i + j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if GAMMA1 == (1 << 19) {
|
||||||
|
for i in 0..(N / 2) {
|
||||||
|
r[2 * i + 0] = a[5 * i + 0] as i32;
|
||||||
|
r[2 * i + 0] |= (a[5 * i + 1] as i32) << 8;
|
||||||
|
r[2 * i + 0] |= (a[5 * i + 2] as i32) << 16;
|
||||||
|
r[2 * i + 0] &= 0xFFFFF;
|
||||||
|
|
||||||
|
r[2 * i + 1] = (a[5 * i + 2] >> 4) as i32;
|
||||||
|
r[2 * i + 1] |= (a[5 * i + 3] as i32) << 4;
|
||||||
|
r[2 * i + 1] |= (a[5 * i + 4] as i32) << 12;
|
||||||
|
r[2 * i + 0] &= 0xFFFFF;
|
||||||
|
|
||||||
|
r[2 * i + 0] = GAMMA1 - r[2 * i + 0];
|
||||||
|
r[2 * i + 1] = GAMMA1 - r[2 * i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bit-packs a polynomial with coefficients in `[0,15]` or `[0,43]`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output array, which will contain the polynomial's encoding
|
||||||
|
/// * `a` - the polynomial to encode
|
||||||
|
#[inline]
|
||||||
|
pub fn w1_pack(r: &mut [u8; POLW1_SIZE_PACKED], a: &Poly) {
|
||||||
|
if GAMMA2 == (Q - 1) / 88 {
|
||||||
|
for i in 0..(N / 4) {
|
||||||
|
r[3 * i + 0] = a[4 * i + 0] as u8;
|
||||||
|
r[3 * i + 0] |= (a[4 * i + 1] << 6) as u8;
|
||||||
|
r[3 * i + 1] = (a[4 * i + 1] >> 2) as u8;
|
||||||
|
r[3 * i + 1] |= (a[4 * i + 2] << 4) as u8;
|
||||||
|
r[3 * i + 2] = (a[4 * i + 2] >> 4) as u8;
|
||||||
|
r[3 * i + 2] |= (a[4 * i + 3] << 2) as u8;
|
||||||
|
}
|
||||||
|
} else if GAMMA2 == (Q - 1) / 32 {
|
||||||
|
for i in 0..(N / 2) {
|
||||||
|
r[i] = (a[2 * i + 0] | (a[2 * i + 1] << 4)) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bit-packs a polynomial `t0` with coefficients in `[-2^{D-1}, 2^{D-1}]`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `r` - the output array, which will contain the polynomial's encoding
|
||||||
|
/// * `a` - the polynomial to encode
|
||||||
|
#[inline]
|
||||||
|
pub fn t0_pack(r: &mut [u8], a: &Poly) {
|
||||||
|
let mut t = [0u32; 8];
|
||||||
|
for i in 0..(N / 8) {
|
||||||
|
for j in 0..8 {
|
||||||
|
t[j] = ((1 << (D - 1) as u32) - a[8 * i + j]) as u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
r[13 * i + 0] = (t[0]) as u8;
|
||||||
|
r[13 * i + 1] = (t[0] >> 8) as u8;
|
||||||
|
r[13 * i + 1] |= (t[1] << 5) as u8;
|
||||||
|
r[13 * i + 2] = (t[1] >> 3) as u8;
|
||||||
|
r[13 * i + 3] = (t[1] >> 11) as u8;
|
||||||
|
r[13 * i + 3] |= (t[2] << 2) as u8;
|
||||||
|
r[13 * i + 4] = (t[2] >> 6) as u8;
|
||||||
|
r[13 * i + 4] |= (t[3] << 7) as u8;
|
||||||
|
r[13 * i + 5] = (t[3] >> 1) as u8;
|
||||||
|
r[13 * i + 6] = (t[3] >> 9) as u8;
|
||||||
|
r[13 * i + 6] |= (t[4] << 4) as u8;
|
||||||
|
r[13 * i + 7] = (t[4] >> 4) as u8;
|
||||||
|
r[13 * i + 8] = (t[4] >> 12) as u8;
|
||||||
|
r[13 * i + 8] |= (t[5] << 1) as u8;
|
||||||
|
r[13 * i + 9] = (t[5] >> 7) as u8;
|
||||||
|
r[13 * i + 9] |= (t[6] << 6) as u8;
|
||||||
|
r[13 * i + 10] = (t[6] >> 2) as u8;
|
||||||
|
r[13 * i + 11] = (t[6] >> 10) as u8;
|
||||||
|
r[13 * i + 11] |= (t[7] << 3) as u8;
|
||||||
|
r[13 * i + 12] = (t[7] >> 5) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
167
third_party/dilithium/src/polyvec.rs
vendored
Normal file
167
third_party/dilithium/src/polyvec.rs
vendored
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use params::{K, L, N};
|
||||||
|
use poly::{self, Poly};
|
||||||
|
|
||||||
|
macro_rules! polyvec {
|
||||||
|
( $polyvec:ident, $len:expr ) => {
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct $polyvec(pub [Poly; $len]);
|
||||||
|
|
||||||
|
impl $polyvec {
|
||||||
|
pub fn reduce(&mut self) {
|
||||||
|
self.0.iter_mut().for_each(poly::reduce)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn caddq(&mut self) {
|
||||||
|
self.0.iter_mut().for_each(poly::caddq)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn freeze(&mut self) {
|
||||||
|
self.0.iter_mut().for_each(poly::freeze)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_add(&mut self, u: &Self, v: &Self) {
|
||||||
|
for i in 0..$len {
|
||||||
|
poly::add(&mut self[i], &u[i], &v[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_assign(&mut self, u: &Self) {
|
||||||
|
for i in 0..$len {
|
||||||
|
poly::add_assign(&mut self[i], &u[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_sub(&mut self, u: &Self, v: &Self) {
|
||||||
|
for i in 0..$len {
|
||||||
|
poly::sub(&mut self[i], &u[i], &v[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shift_left(&mut self) {
|
||||||
|
self.0.iter_mut().for_each(|p| poly::shift_left(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ntt(&mut self) {
|
||||||
|
self.0.iter_mut().for_each(poly::ntt);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invntt_montgomery(&mut self) {
|
||||||
|
self.0.iter_mut().for_each(poly::invntt_montgomery)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn chknorm(&self, bound: i32) -> bool {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.map(|p| poly::chknorm(p, bound))
|
||||||
|
.fold(false, |x, y| x | y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::core::ops::Index<usize> for $polyvec {
|
||||||
|
type Output = Poly;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn index(&self, i: usize) -> &Self::Output {
|
||||||
|
self.0.index(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::core::ops::IndexMut<usize> for $polyvec {
|
||||||
|
#[inline(always)]
|
||||||
|
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
|
||||||
|
self.0.index_mut(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::core::cmp::PartialEq for $polyvec {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.zip(&other.0)
|
||||||
|
.flat_map(|(x, y)| x.iter().zip(y.iter()))
|
||||||
|
.all(|(x, y)| x == y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for $polyvec {}
|
||||||
|
|
||||||
|
impl Default for $polyvec {
|
||||||
|
fn default() -> Self {
|
||||||
|
$polyvec([[0; N]; $len])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
polyvec!(PolyVecL, L);
|
||||||
|
polyvec!(PolyVecK, K);
|
||||||
|
|
||||||
|
pub fn pointwise_acc_invmontgomery(w: &mut Poly, u: &PolyVecL, v: &PolyVecL) {
|
||||||
|
let mut t = [0; N];
|
||||||
|
|
||||||
|
poly::pointwise_invmontgomery(w, &u[0], &v[0]);
|
||||||
|
|
||||||
|
for i in 1..L {
|
||||||
|
poly::pointwise_invmontgomery(&mut t, &u[i], &v[i]);
|
||||||
|
poly::add_assign(w, &t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes a partial result of the dot product `w = u * v`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `w` - the output polynomial, which will contain the partial result
|
||||||
|
/// * `u_component` - the polynomial `u[i]`
|
||||||
|
/// * `v_component` - the polynomial `v[i]`
|
||||||
|
/// * `i` - the index
|
||||||
|
pub fn pointwise_acc_invmontgomery_componentwise(
|
||||||
|
w: &mut Poly,
|
||||||
|
u_component: &Poly,
|
||||||
|
v_component: &Poly,
|
||||||
|
i: usize,
|
||||||
|
) {
|
||||||
|
if i == 0 {
|
||||||
|
poly::pointwise_invmontgomery(w, &u_component, &v_component);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut t = [0; N];
|
||||||
|
poly::pointwise_invmontgomery(&mut t, &u_component, &v_component);
|
||||||
|
poly::add_assign(w, &t);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolyVecK {
|
||||||
|
pub fn power2round(&self, v0: &mut Self, v1: &mut Self) {
|
||||||
|
for i in 0..K {
|
||||||
|
poly::power2round(&self[i], &mut v0[i], &mut v1[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn power2round_remainder(&self, v0: &mut Self) {
|
||||||
|
for i in 0..K {
|
||||||
|
v0[i] = poly::power2round_remainder(&self[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decompose(&self, v0: &mut Self, v1: &mut Self) {
|
||||||
|
for i in 0..K {
|
||||||
|
poly::decompose(&self[i], &mut v0[i], &mut v1[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_hint(u: &PolyVecK, v: &PolyVecK, h: &mut PolyVecK) -> usize {
|
||||||
|
let mut s = 0;
|
||||||
|
for i in 0..K {
|
||||||
|
s += poly::make_hint(&u[i], &v[i], &mut h[i]);
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn use_hint(w: &mut PolyVecK, u: &PolyVecK, h: &PolyVecK) {
|
||||||
|
for i in 0..K {
|
||||||
|
poly::use_hint(&mut w[i], &u[i], &h[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
52
third_party/dilithium/src/reduce.rs
vendored
Normal file
52
third_party/dilithium/src/reduce.rs
vendored
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
use params::{Q, QINV};
|
||||||
|
|
||||||
|
/// Returns a value between `-Q` and `Q` that is equivalent to `a`.
|
||||||
|
///
|
||||||
|
/// For a finite field element `a` with `-2^{31}*Q <= a <= Q*2^31`,
|
||||||
|
/// computes `r` equivalent to `a*2^{-32} (mod Q)` such that `-Q < r < Q`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a number between `2^{31}*Q` and `Q*2^31`.
|
||||||
|
pub fn montgomery_reduce(a: i64) -> i32 {
|
||||||
|
let mut t: i32 = (((a as i32) as i64) * (QINV as i64)) as i32;
|
||||||
|
t = ((a - (t as i64) * (Q as i64)) >> 32) as i32;
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a value between `-6283009` and `6283007` that is equivalent to `a`.
|
||||||
|
///
|
||||||
|
/// For a finite field element `a` with `a <= 2^{31} - 2^{22} - 1`,
|
||||||
|
/// computes `r` equivalent to `a (mod Q)` such that
|
||||||
|
/// `-6283009 <= r <= 6283007`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a number between `2^{31}*Q` and `Q*2^31`.
|
||||||
|
pub fn reduce32(a: i32) -> i32 {
|
||||||
|
let mut t: i32 = (a + (1 << 22)) >> 23;
|
||||||
|
t = a - t * Q;
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds `Q` if the input finite field element is negative.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a number.
|
||||||
|
pub fn caddq(a: i32) -> i32 {
|
||||||
|
let mut t = a;
|
||||||
|
t += (a >> 31) & Q;
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the standard representative `r = a mod Q`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a number.
|
||||||
|
pub fn freeze(a: i32) -> i32 {
|
||||||
|
let a = reduce32(a);
|
||||||
|
let a = caddq(a);
|
||||||
|
a
|
||||||
|
}
|
||||||
93
third_party/dilithium/src/rounding.rs
vendored
Normal file
93
third_party/dilithium/src/rounding.rs
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
use params::{D, GAMMA2, Q};
|
||||||
|
|
||||||
|
/// Returns the remainder and the quotient of `a` divided by `2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// For a finite field element `a`, computes `a0` and `a1` such that
|
||||||
|
/// `a mod Q = a1*2^D + a0` with `-2^{D-1} < a0 <= 2^{D-1}`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a number assumed to be a standard representative modulo `Q`.
|
||||||
|
pub fn power2round(a: i32) -> (i32, i32) {
|
||||||
|
let a1: i32 = (a + (1 << (D - 1)) - 1) >> D;
|
||||||
|
let a0: i32 = a - (a1 << D);
|
||||||
|
|
||||||
|
(a0, a1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the high bits and low bits of `a`.
|
||||||
|
///
|
||||||
|
/// For a finite field element `a`, computes the high and the low bits `a1`
|
||||||
|
/// and respectively `a0`, such that `a mod Q = a1*ALPHA + a0`
|
||||||
|
/// with `-ALPHA/2 < a0 <= ALPHA/2`.
|
||||||
|
/// Exception: If `a1 = (Q-1)/ALPHA`, `a0` is set to 0.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - a number assumed to be a standard representative modulo `Q`.
|
||||||
|
pub fn decompose(a: i32) -> (i32, i32) {
|
||||||
|
let mut a1: i32 = (a + 127) >> 7;
|
||||||
|
if GAMMA2 == (Q - 1) / 32 {
|
||||||
|
a1 = (a1 * 1025 + (1 << 21)) >> 22;
|
||||||
|
a1 &= 15;
|
||||||
|
} else if GAMMA2 == (Q - 1) / 88 {
|
||||||
|
a1 = (a1 * 11275 + (1 << 23)) >> 24;
|
||||||
|
a1 ^= ((43 - a1) >> 31) & a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut a0: i32 = a - a1 * 2 * GAMMA2;
|
||||||
|
a0 -= (((Q - 1) / 2 - a0) >> 31) & Q;
|
||||||
|
|
||||||
|
(a0, a1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes the hint bit.
|
||||||
|
///
|
||||||
|
/// The hint bit indicates whether the low bits `a0` overflow into the
|
||||||
|
/// the high bits `a1`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a0` - a number representing the low bits of some element `a`
|
||||||
|
/// * `a1` - a number representing the high bits of the same element `a`
|
||||||
|
pub fn make_hint(a0: i32, a1: i32) -> u32 {
|
||||||
|
if a0 > GAMMA2 || a0 < -GAMMA2 || (a0 == -GAMMA2 && a1 != 0) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses the given hint to correct the high bits of a.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `a` - the number to be corrected
|
||||||
|
/// * `hint` - a value 0 or 1
|
||||||
|
pub fn use_hint(a: i32, hint: u32) -> i32 {
|
||||||
|
let (a0, a1) = decompose(a);
|
||||||
|
|
||||||
|
if hint == 0 {
|
||||||
|
a1
|
||||||
|
} else if GAMMA2 == (Q - 1) / 32 {
|
||||||
|
if a0 > 0 {
|
||||||
|
(a1 + 1) & 15
|
||||||
|
} else {
|
||||||
|
(a1 - 1) & 15
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if a0 > 0 {
|
||||||
|
if a1 == 43 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
a1 + 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if a1 == 0 {
|
||||||
|
43
|
||||||
|
} else {
|
||||||
|
a1 - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1220
third_party/dilithium/src/sign.rs
vendored
Normal file
1220
third_party/dilithium/src/sign.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
64
third_party/dilithium/src/test_mul.rs
vendored
Normal file
64
third_party/dilithium/src/test_mul.rs
vendored
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
extern crate rng256;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use params::{N, Q};
|
||||||
|
use poly::Poly;
|
||||||
|
|
||||||
|
const NTESTS: usize = 10000;
|
||||||
|
|
||||||
|
fn poly_naivemul(c: &mut Poly, a: &Poly, b: &Poly) {
|
||||||
|
let mut r = [0; 2 * N];
|
||||||
|
|
||||||
|
for i in 0..N {
|
||||||
|
for j in 0..N {
|
||||||
|
r[i + j] += (((a[i] as i64) * (b[j] as i64)) % (Q as i64)) as i32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in N..(2 * N) {
|
||||||
|
r[i - N] = (r[i - N] - r[i]) % Q;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.copy_from_slice(&r[..N]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mul() {
|
||||||
|
use self::rng256::Rng256;
|
||||||
|
|
||||||
|
let mut rndbuf = [0; 32];
|
||||||
|
let mut c = [0; N];
|
||||||
|
let (mut c1, mut c2) = ([0; N], [0; N]);
|
||||||
|
let (mut a, mut b) = ([0; N], [0; N]);
|
||||||
|
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
for _ in 0..NTESTS {
|
||||||
|
rng.fill_bytes(&mut rndbuf);
|
||||||
|
poly::uniform(&mut a, &rndbuf, 0);
|
||||||
|
rng.fill_bytes(&mut rndbuf);
|
||||||
|
poly::uniform(&mut b, &rndbuf, 0);
|
||||||
|
|
||||||
|
c.copy_from_slice(&a[..N]);
|
||||||
|
poly::ntt(&mut c);
|
||||||
|
for j in 0..N {
|
||||||
|
c[j] = ((c[j] as i64) * -114592 % (Q as i64)) as i32;
|
||||||
|
}
|
||||||
|
poly::invntt_montgomery(&mut c);
|
||||||
|
|
||||||
|
for j in 0..N {
|
||||||
|
assert_eq!((c[j] - a[j]) % Q, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
poly_naivemul(&mut c1, &a, &b);
|
||||||
|
|
||||||
|
poly::ntt(&mut a);
|
||||||
|
poly::ntt(&mut b);
|
||||||
|
poly::pointwise_invmontgomery(&mut c2, &a, &b);
|
||||||
|
poly::invntt_montgomery(&mut c2);
|
||||||
|
|
||||||
|
for j in 0..N {
|
||||||
|
assert_eq!((c1[j] - c2[j]) % Q, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
third_party/dilithium/src/utils.rs
vendored
Normal file
10
third_party/dilithium/src/utils.rs
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
macro_rules! shake256 {
|
||||||
|
( $output:expr ; $( $input:expr ),* ) => {
|
||||||
|
let mut hasher = ::sha3::Shake256::default();
|
||||||
|
$(
|
||||||
|
::digest::Input::process(&mut hasher, $input);
|
||||||
|
)*
|
||||||
|
let mut reader = ::digest::ExtendableOutput::xof_result(hasher);
|
||||||
|
::digest::XofReader::read(&mut reader, $output);
|
||||||
|
}
|
||||||
|
}
|
||||||
3548
third_party/dilithium/tests/dilithium_c.rs
vendored
Normal file
3548
third_party/dilithium/tests/dilithium_c.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
65
third_party/dilithium/tests/sign.rs
vendored
Normal file
65
third_party/dilithium/tests/sign.rs
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
extern crate dilithium;
|
||||||
|
extern crate rng256;
|
||||||
|
|
||||||
|
use dilithium::sign::{PubKey, SecKey};
|
||||||
|
use rng256::Rng256;
|
||||||
|
|
||||||
|
const ITERATIONS: u32 = 500;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sk_with_pk() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let (sk, pk) = SecKey::gensk_with_pk(&mut rng);
|
||||||
|
let pk_from_sk = sk.genpk();
|
||||||
|
assert_eq!(pk, pk_from_sk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sign() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
|
||||||
|
let mut message = [0; 59];
|
||||||
|
rng.fill_bytes(&mut message);
|
||||||
|
let sig = sk.sign(&message);
|
||||||
|
|
||||||
|
let pk = sk.genpk();
|
||||||
|
|
||||||
|
let mut bytes = [0; dilithium::params::PK_SIZE_PACKED];
|
||||||
|
pk.to_bytes(&mut bytes);
|
||||||
|
assert!(pk.verify(&message, &sig));
|
||||||
|
|
||||||
|
message[2] ^= 42;
|
||||||
|
assert!(!pk.verify(&message, &sig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_seckey_to_bytes_from_bytes() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let mut bytes = [0; dilithium::params::SK_SIZE_PACKED];
|
||||||
|
sk.to_bytes(&mut bytes);
|
||||||
|
let decoded_sk = SecKey::from_bytes(&bytes);
|
||||||
|
assert_eq!(decoded_sk, sk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_pubkey_to_bytes_from_bytes() {
|
||||||
|
let mut rng = rng256::ThreadRng256 {};
|
||||||
|
|
||||||
|
for _ in 0..ITERATIONS {
|
||||||
|
let sk = SecKey::gensk(&mut rng);
|
||||||
|
let pk = sk.genpk();
|
||||||
|
let mut bytes = [0; dilithium::params::PK_SIZE_PACKED];
|
||||||
|
pk.to_bytes(&mut bytes);
|
||||||
|
let decoded_pk = PubKey::from_bytes(&bytes);
|
||||||
|
assert_eq!(decoded_pk, pk);
|
||||||
|
}
|
||||||
|
}
|
||||||
70
third_party/dilithium/tests/testvectors.txt
vendored
Normal file
70
third_party/dilithium/tests/testvectors.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -169,7 +169,7 @@ def main(args):
|
|||||||
("Failed to configure OpenSK (device is partially programmed but "
|
("Failed to configure OpenSK (device is partially programmed but "
|
||||||
"the given cert/key don't match the ones currently programmed)."))
|
"the given cert/key don't match the ones currently programmed)."))
|
||||||
else:
|
else:
|
||||||
error(f"Failed to configure OpenSK (unknown error: {ex}")
|
error(f"Failed to configure OpenSK (unknown error: {ex})")
|
||||||
return responses
|
return responses
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ from __future__ import division
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import datetime
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import struct
|
import struct
|
||||||
@@ -44,6 +43,7 @@ OPENSK_VID_PID = (0x1915, 0x521F)
|
|||||||
OPENSK_VENDOR_UPGRADE = 0x42
|
OPENSK_VENDOR_UPGRADE = 0x42
|
||||||
OPENSK_VENDOR_UPGRADE_INFO = 0x43
|
OPENSK_VENDOR_UPGRADE_INFO = 0x43
|
||||||
PAGE_SIZE = 0x1000
|
PAGE_SIZE = 0x1000
|
||||||
|
METADATA_SIGN_OFFSET = 0x800
|
||||||
KERNEL_SIZE = 0x20000
|
KERNEL_SIZE = 0x20000
|
||||||
APP_SIZE = 0x20000
|
APP_SIZE = 0x20000
|
||||||
PARTITION_ADDRESS = {
|
PARTITION_ADDRESS = {
|
||||||
@@ -54,7 +54,15 @@ ES256_ALGORITHM = -7
|
|||||||
ARCH = "thumbv7em-none-eabi"
|
ARCH = "thumbv7em-none-eabi"
|
||||||
|
|
||||||
|
|
||||||
def create_metadata(firmware_image: bytes, partition_address: int) -> bytes:
|
def hash_message(message: bytes) -> bytes:
|
||||||
|
"""Uses SHA256 to hash a message."""
|
||||||
|
sha256_hash = hashlib.sha256()
|
||||||
|
sha256_hash.update(message)
|
||||||
|
return sha256_hash.digest()
|
||||||
|
|
||||||
|
|
||||||
|
def create_metadata(firmware_image: bytes, partition_address: int, version: int,
|
||||||
|
priv_key: Any) -> bytes:
|
||||||
"""Creates the matching metadata for the given firmware.
|
"""Creates the matching metadata for the given firmware.
|
||||||
|
|
||||||
The metadata consists of a timestamp, the expected address and a hash of
|
The metadata consists of a timestamp, the expected address and a hash of
|
||||||
@@ -65,25 +73,26 @@ def create_metadata(firmware_image: bytes, partition_address: int) -> bytes:
|
|||||||
partition_address: The address to be written as a metadata property.
|
partition_address: The address to be written as a metadata property.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
A byte array consisting of 32B hash, 4B timestamp and 4B partition address
|
A byte array of page size, consisting of
|
||||||
in little endian encoding.
|
- 32 B hash,
|
||||||
|
- 64 B signature,
|
||||||
|
at the beginning and
|
||||||
|
- 8 B version and
|
||||||
|
- 4 B partition address in little endian encoding
|
||||||
|
after METADATA_SIGN_OFFSET. All other bytes are 0xFF.
|
||||||
"""
|
"""
|
||||||
t = datetime.datetime.utcnow().timestamp()
|
if version < 0 or version >= 2**63:
|
||||||
timestamp = struct.pack("<I", int(t))
|
fatal("The version must fit into an unsigned integer with 63 bit.\n"
|
||||||
|
"Please pass it using --version")
|
||||||
|
version_bytes = struct.pack("<Q", version)
|
||||||
partition_start = struct.pack("<I", partition_address)
|
partition_start = struct.pack("<I", partition_address)
|
||||||
sha256_hash = hashlib.sha256()
|
# Prefix sizes that are a multiple of 64 suit our bootloader's SHA.
|
||||||
sha256_hash.update(firmware_image)
|
signed_metadata = pad_to(version_bytes + partition_start,
|
||||||
sha256_hash.update(timestamp)
|
PAGE_SIZE - METADATA_SIGN_OFFSET)
|
||||||
sha256_hash.update(partition_start)
|
signed_data = signed_metadata + firmware_image
|
||||||
checksum = sha256_hash.digest()
|
checksum = hash_message(signed_data)
|
||||||
return checksum + timestamp + partition_start
|
signature = sign_firmware(signed_data, priv_key)
|
||||||
|
return pad_to(checksum + signature, METADATA_SIGN_OFFSET) + signed_metadata
|
||||||
|
|
||||||
def hash_message(message: bytes) -> bytes:
|
|
||||||
"""Uses SHA256 to hash a message."""
|
|
||||||
sha256_hash = hashlib.sha256()
|
|
||||||
sha256_hash.update(message)
|
|
||||||
return sha256_hash.digest()
|
|
||||||
|
|
||||||
|
|
||||||
def check_info(partition_address: int, authenticator: Any):
|
def check_info(partition_address: int, authenticator: Any):
|
||||||
@@ -95,9 +104,10 @@ def check_info(partition_address: int, authenticator: Any):
|
|||||||
data={},
|
data={},
|
||||||
)
|
)
|
||||||
if result[0x01] != partition_address:
|
if result[0x01] != partition_address:
|
||||||
fatal("Identifiers do not match.")
|
fatal(f"Identifiers do not match, received 0x{result[0x01]:0x}, "
|
||||||
|
f"expected 0x{partition_address:0x}.")
|
||||||
except ctap.CtapError as ex:
|
except ctap.CtapError as ex:
|
||||||
error(f"Failed to read OpenSK upgrade info (error: {ex}")
|
fatal(f"Failed to read OpenSK upgrade info (error: {ex})")
|
||||||
|
|
||||||
|
|
||||||
def get_kernel(board: str) -> bytes:
|
def get_kernel(board: str) -> bytes:
|
||||||
@@ -137,17 +147,21 @@ def generate_firmware_image(board: str) -> bytes:
|
|||||||
return pad_to(kernel, KERNEL_SIZE) + pad_to(app, APP_SIZE)
|
return pad_to(kernel, KERNEL_SIZE) + pad_to(app, APP_SIZE)
|
||||||
|
|
||||||
|
|
||||||
def load_priv_key(priv_key_file: argparse.FileType) -> Any:
|
def load_priv_key(priv_key_filename: str) -> Any:
|
||||||
"""Loads the ECDSA private key from the specified file."""
|
"""Loads the ECDSA private key from the specified file."""
|
||||||
priv_key = get_private_key(priv_key_file.read())
|
try:
|
||||||
if not isinstance(priv_key, ec.EllipticCurvePrivateKey):
|
with open(priv_key_filename, "rb") as priv_key_file:
|
||||||
fatal("Private key must be an Elliptic Curve one.")
|
priv_key = get_private_key(priv_key_file.read())
|
||||||
if not isinstance(priv_key.curve, ec.SECP256R1):
|
if not isinstance(priv_key, ec.EllipticCurvePrivateKey):
|
||||||
fatal("Private key must use Secp256r1 curve.")
|
fatal("Private key must be an Elliptic Curve one.")
|
||||||
if priv_key.key_size != 256:
|
if not isinstance(priv_key.curve, ec.SECP256R1):
|
||||||
fatal("Private key must be 256 bits long.")
|
fatal("Private key must use Secp256r1 curve.")
|
||||||
info("Private key is valid.")
|
if priv_key.key_size != 256:
|
||||||
return priv_key
|
fatal("Private key must be 256 bits long.")
|
||||||
|
info("Private key is valid.")
|
||||||
|
return priv_key
|
||||||
|
except IOError as e:
|
||||||
|
fatal(f"Unable to open file: {priv_key_filename}\n{e}")
|
||||||
|
|
||||||
|
|
||||||
def sign_firmware(data: bytes, priv_key: Any) -> bytes:
|
def sign_firmware(data: bytes, priv_key: Any) -> bytes:
|
||||||
@@ -159,19 +173,15 @@ def sign_firmware(data: bytes, priv_key: Any) -> bytes:
|
|||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
colorama.init()
|
colorama.init()
|
||||||
|
if not args.priv_key:
|
||||||
|
fatal("Please pass in a private key file using --private-key.")
|
||||||
|
|
||||||
firmware_image = generate_firmware_image(args.board)
|
firmware_image = generate_firmware_image(args.board)
|
||||||
partition_address = PARTITION_ADDRESS[args.board]
|
partition_address = PARTITION_ADDRESS[args.board]
|
||||||
metadata = create_metadata(firmware_image, partition_address)
|
|
||||||
|
|
||||||
if not args.priv_key:
|
|
||||||
fatal("Please pass in a private key file using --private-key.")
|
|
||||||
priv_key = load_priv_key(args.priv_key)
|
priv_key = load_priv_key(args.priv_key)
|
||||||
signed_data = firmware_image + metadata[32:40]
|
metadata = create_metadata(firmware_image, partition_address, args.version,
|
||||||
signature = {
|
priv_key)
|
||||||
"alg": ES256_ALGORITHM,
|
partition = metadata + firmware_image
|
||||||
"signature": sign_firmware(signed_data, priv_key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.use_vendor_hid:
|
if args.use_vendor_hid:
|
||||||
patcher = patch.object(hid.base, "FIDO_USAGE_PAGE", 0xFF00)
|
patcher = patch.object(hid.base, "FIDO_USAGE_PAGE", 0xFF00)
|
||||||
@@ -189,11 +199,18 @@ def main(args):
|
|||||||
aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid)
|
aaguid = uuid.UUID(bytes=authenticator.get_info().aaguid)
|
||||||
info(f"Upgrading OpenSK device AAGUID {aaguid} ({authenticator.device}).")
|
info(f"Upgrading OpenSK device AAGUID {aaguid} ({authenticator.device}).")
|
||||||
|
|
||||||
|
running_version = authenticator.get_info().firmware_version
|
||||||
|
if args.version < running_version:
|
||||||
|
fatal(f"Can not write version {args.version} when version "
|
||||||
|
f"{running_version} is running.")
|
||||||
|
else:
|
||||||
|
info(f"Running version: {running_version}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_info(partition_address, authenticator)
|
check_info(partition_address, authenticator)
|
||||||
offset = 0
|
offset = 0
|
||||||
for offset in range(0, len(firmware_image), PAGE_SIZE):
|
for offset in range(0, len(partition), PAGE_SIZE):
|
||||||
page = firmware_image[offset:][:PAGE_SIZE]
|
page = partition[offset:][:PAGE_SIZE]
|
||||||
info(f"Writing at offset 0x{offset:08X}...")
|
info(f"Writing at offset 0x{offset:08X}...")
|
||||||
cbor_data = {1: offset, 2: page, 3: hash_message(page)}
|
cbor_data = {1: offset, 2: page, 3: hash_message(page)}
|
||||||
authenticator.send_cbor(
|
authenticator.send_cbor(
|
||||||
@@ -201,26 +218,20 @@ def main(args):
|
|||||||
data=cbor_data,
|
data=cbor_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
info("Writing metadata...")
|
|
||||||
cbor_data = {2: metadata, 3: hash_message(metadata), 4: signature}
|
|
||||||
authenticator.send_cbor(
|
|
||||||
OPENSK_VENDOR_UPGRADE,
|
|
||||||
data=cbor_data,
|
|
||||||
)
|
|
||||||
except ctap.CtapError as ex:
|
except ctap.CtapError as ex:
|
||||||
message = "Failed to upgrade OpenSK"
|
message = "Failed to upgrade OpenSK"
|
||||||
if ex.code.value == ctap.CtapError.ERR.INVALID_COMMAND:
|
if ex.code.value == ctap.CtapError.ERR.INVALID_COMMAND:
|
||||||
error(f"{message} (unsupported command).")
|
error(f"{message} (unsupported command).")
|
||||||
elif ex.code.value == ctap.CtapError.ERR.INVALID_PARAMETER:
|
elif ex.code.value == ctap.CtapError.ERR.INVALID_PARAMETER:
|
||||||
error(f"{message} (invalid parameter, maybe a wrong byte array size?).")
|
error(f"{message} (invalid parameter).")
|
||||||
elif ex.code.value == ctap.CtapError.ERR_INTEGRITY_FAILURE:
|
elif ex.code.value == ctap.CtapError.ERR.INTEGRITY_FAILURE:
|
||||||
error(f"{message} (hashes or signature don't match).")
|
error(f"{message} (data hash does not match slice).")
|
||||||
elif ex.code.value == 0xF2: # VENDOR_INTERNAL_ERROR
|
elif ex.code.value == 0xF2: # VENDOR_INTERNAL_ERROR
|
||||||
error(f"{message} (internal conditions not met).")
|
error(f"{message} (internal conditions not met).")
|
||||||
elif ex.code.value == 0xF3: # VENDOR_HARDWARE_FAILURE
|
elif ex.code.value == 0xF3: # VENDOR_HARDWARE_FAILURE
|
||||||
error(f"{message} (internal hardware error).")
|
error(f"{message} (internal hardware error).")
|
||||||
else:
|
else:
|
||||||
error(f"{message} (unexpected error: {ex}")
|
error(f"{message} (unexpected error: {ex})")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
@@ -247,7 +258,7 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--private-key",
|
"--private-key",
|
||||||
type=argparse.FileType("rb"),
|
type=str,
|
||||||
default="crypto_data/opensk_upgrade.key",
|
default="crypto_data/opensk_upgrade.key",
|
||||||
dest="priv_key",
|
dest="priv_key",
|
||||||
help=("PEM file for signing the firmware."),
|
help=("PEM file for signing the firmware."),
|
||||||
@@ -259,4 +270,10 @@ if __name__ == "__main__":
|
|||||||
dest="use_vendor_hid",
|
dest="use_vendor_hid",
|
||||||
help=("Whether to upgrade the device using the Vendor HID interface."),
|
help=("Whether to upgrade the device using the Vendor HID interface."),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--version",
|
||||||
|
type=int,
|
||||||
|
dest="version",
|
||||||
|
help=("Firmware version that is built."),
|
||||||
|
)
|
||||||
main(parser.parse_args())
|
main(parser.parse_args())
|
||||||
|
|||||||
Reference in New Issue
Block a user