Compare commits
1 Commits
2.1
...
hybrid-pqc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
93357524d9 |
6
.github/workflows/cargo_check.yml
vendored
6
.github/workflows/cargo_check.yml
vendored
@@ -94,9 +94,3 @@ jobs:
|
||||
with:
|
||||
command: check
|
||||
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 }
|
||||
rand = { version = "0.8.4", optional = true }
|
||||
ed25519-compact = { version = "1", default-features = false, optional = true }
|
||||
dilithium = { path = "third_party/dilithium" }
|
||||
|
||||
[features]
|
||||
debug_allocations = ["lang_items/debug_allocations"]
|
||||
debug_ctap = ["libtock_drivers/debug_ctap"]
|
||||
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"]
|
||||
with_ctap1 = ["crypto/with_ctap1"]
|
||||
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">
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
[](https://coveralls.io/github/google/OpenSK?branch=develop)
|
||||
|
||||
## OpenSK
|
||||
|
||||
This repository contains a Rust implementation of a
|
||||
[FIDO2](https://fidoalliance.org/fido2/) authenticator.
|
||||
We developed OpenSK as a [Tock OS](https://tockos.org) application.
|
||||
This is an OpenSK fork that allows signing with a PQC Hybrid scheme. If you are looking for the original documentation, please check the
|
||||
[develop branch of its GitHub page](https://github.com/google/OpenSK/tree/develop).
|
||||
|
||||
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
|
||||
|
||||
You will need one the following supported boards:
|
||||
|
||||
* [Nordic nRF52840-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
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/).
|
||||
You will need a
|
||||
[Nordic nRF52840-DK](https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK)
|
||||
development kit.
|
||||
|
||||
## Installation
|
||||
|
||||
To install OpenSK,
|
||||
|
||||
1. follow the [general setup steps](docs/install.md),
|
||||
1. then continue with the instructions for your specific hardware:
|
||||
* [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)
|
||||
[Nordic nRF52840-DK](docs/boards/nrf52840dk.md)
|
||||
|
||||
To test whether the installation was successful, visit a
|
||||
[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).
|
||||
## PQC Experiments
|
||||
|
||||
## 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())
|
||||
10
deploy.py
10
deploy.py
@@ -156,9 +156,7 @@ SUPPORTED_BOARDS = {
|
||||
),
|
||||
}
|
||||
|
||||
# The following value must match the one used in the file
|
||||
# `src/entry_point.rs`
|
||||
APP_HEAP_SIZE = 90000
|
||||
APP_HEAP_SIZE = 32768
|
||||
|
||||
|
||||
def get_supported_boards() -> Tuple[str]:
|
||||
@@ -1177,6 +1175,12 @@ if __name__ == "__main__":
|
||||
const="crypto_bench",
|
||||
help=("Compiles and installs the crypto_bench example that benchmarks "
|
||||
"the performance of the cryptographic algorithms on the board."))
|
||||
apps_group.add_argument(
|
||||
"--measure_stack",
|
||||
dest="application",
|
||||
action="store_const",
|
||||
const="measure_stack",
|
||||
help=("Measures stack usage of Dilithium."))
|
||||
apps_group.add_argument(
|
||||
"--store_latency",
|
||||
dest="application",
|
||||
|
||||
@@ -17,27 +17,42 @@
|
||||
extern crate alloc;
|
||||
extern crate lang_items;
|
||||
|
||||
use alloc::format;
|
||||
use alloc::vec::Vec;
|
||||
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::result::FlexUnwrap;
|
||||
use libtock_drivers::timer;
|
||||
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() {
|
||||
// Fix to be faster.
|
||||
//let storage = take_storage().unwrap();
|
||||
//let mut _store = boot_store(storage, true);
|
||||
|
||||
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
|
||||
// API forces us to set an alarm callback too).
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
|
||||
let mut rng = TockRng256 {};
|
||||
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(
|
||||
console,
|
||||
@@ -46,136 +61,133 @@ fn main() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// AES
|
||||
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(
|
||||
custom_bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"aes256::EncryptionKey::encrypt_block",
|
||||
|| {
|
||||
ek.encrypt_block(&mut [0; 16]);
|
||||
},
|
||||
);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"aes256::DecryptionKey::decrypt_block",
|
||||
|| {
|
||||
dk.decrypt_block(&mut [0; 16]);
|
||||
"ECDSA keygen",
|
||||
1000,
|
||||
|| {},
|
||||
|()| {
|
||||
let k = ecdsa::SecKey::gensk(&mut rng);
|
||||
k.genpk();
|
||||
},
|
||||
);
|
||||
|
||||
// CBC
|
||||
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(
|
||||
custom_bench(
|
||||
&mut console,
|
||||
&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)
|
||||
},
|
||||
);
|
||||
bench(
|
||||
&mut console,
|
||||
&timer,
|
||||
"ecdsa::SecKey::sign_rfc6979::<sha256::Sha256>",
|
||||
|| {
|
||||
k.sign_rfc6979::<sha256::Sha256>(&[]);
|
||||
|(k, m)| {
|
||||
k.sign_rfc6979::<Sha256>(&m);
|
||||
},
|
||||
);
|
||||
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(console, "All the benchmarks are done.\nHave a nice day!").unwrap();
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
custom_bench(
|
||||
&mut console,
|
||||
&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)
|
||||
where
|
||||
F: FnMut(),
|
||||
fn custom_bench<I, O, F, S>(
|
||||
console: &mut Console,
|
||||
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, "Benchmarking: {}", title).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());
|
||||
for _ in 0..count {
|
||||
f();
|
||||
}
|
||||
f(inputs);
|
||||
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let elapsed = (end - start).ms();
|
||||
writeln!(
|
||||
console,
|
||||
"{} ms elapsed for {} iterations ({} ms/iter)",
|
||||
elapsed,
|
||||
count,
|
||||
elapsed / (count as f64)
|
||||
)
|
||||
.unwrap();
|
||||
console.flush();
|
||||
if elapsed > 1000.0 {
|
||||
break;
|
||||
|
||||
let mut run_duration = (end - start).ms();
|
||||
|
||||
// After 512 seconds, we get a negative difference between the start
|
||||
// time and the end time.
|
||||
if run_duration < 0.0 {
|
||||
run_duration += 512.0 * 1000.0;
|
||||
}
|
||||
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] {
|
||||
0xe0 /* RATS */=> {
|
||||
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 */ => {
|
||||
// Ignore the request
|
||||
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] {
|
||||
// If the received packet is applet selection command (FIDO 2)
|
||||
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
|
||||
// Vesion: "FIDO_2_0"
|
||||
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)){
|
||||
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 */ {
|
||||
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] {
|
||||
0x02 => {
|
||||
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 => {
|
||||
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,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
|
||||
return_code = bench_transmit(&mut console, timer, "TX: NDEF", &mut reply);
|
||||
}
|
||||
0x0f => {
|
||||
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
|
||||
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];
|
||||
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];
|
||||
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 */ => {
|
||||
|
||||
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" }
|
||||
arrayref = "0.3.6"
|
||||
subtle = { version = "2.2.3", default-features = false, features = ["nightly"] }
|
||||
dilithium = { path = "../../third_party/dilithium" }
|
||||
byteorder = { version = "1", default-features = false }
|
||||
hex = { version = "0.3.2", default-features = false, optional = true }
|
||||
ring = { version = "0.16.11", optional = true }
|
||||
|
||||
@@ -308,6 +308,11 @@ pub mod test {
|
||||
}
|
||||
|
||||
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.
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32] {
|
||||
unreachable!()
|
||||
|
||||
@@ -25,7 +25,7 @@ use subtle::{Choice, ConditionallySelectable, ConstantTimeEq};
|
||||
// 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
|
||||
// keys.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PointP256 {
|
||||
x: GFP256,
|
||||
y: GFP256,
|
||||
@@ -548,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)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -33,12 +33,13 @@ pub struct SecKey {
|
||||
k: NonZeroExponentP256,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Signature {
|
||||
r: NonZeroExponentP256,
|
||||
s: NonZeroExponentP256,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct PubKey {
|
||||
p: PointP256,
|
||||
}
|
||||
|
||||
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");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
#![feature(wrapping_int_impl)]
|
||||
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate arrayref;
|
||||
|
||||
pub mod aes256;
|
||||
pub mod cbc;
|
||||
@@ -24,6 +26,7 @@ pub mod ecdh;
|
||||
pub mod ecdsa;
|
||||
pub mod hkdf;
|
||||
pub mod hmac;
|
||||
pub mod hybrid;
|
||||
pub mod sha256;
|
||||
pub mod util;
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ use rand::Rng;
|
||||
|
||||
// Lightweight RNG trait to generate uniformly distributed 256 bits.
|
||||
pub trait Rng256 {
|
||||
fn fill_bytes(&mut self, buf: &mut [u8]);
|
||||
|
||||
fn gen_uniform_u8x32(&mut self) -> [u8; 32];
|
||||
|
||||
fn gen_uniform_u32x8(&mut self) -> [u32; 8] {
|
||||
@@ -45,6 +47,10 @@ fn bytes_to_u32(bytes: [u8; 32]) -> [u32; 8] {
|
||||
pub struct 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] {
|
||||
let mut buf: [u8; 32] = [Default::default(); 32];
|
||||
rng::fill_buffer(&mut buf);
|
||||
@@ -58,6 +64,11 @@ pub struct ThreadRng256 {}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
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] {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut result = [Default::default(); 32];
|
||||
|
||||
@@ -14,7 +14,7 @@ MEMORY {
|
||||
* Any change to STACK_SIZE should be accompanied by a corresponding change to
|
||||
* `elf2tab`'s `--stack` option
|
||||
*/
|
||||
STACK_SIZE = 16384;
|
||||
STACK_SIZE = 71680;
|
||||
|
||||
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",
|
||||
@@ -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
|
||||
+ 16 * 1024
|
||||
+ 70 * 1024
|
||||
|
||||
// TOCK 2.0
|
||||
//
|
||||
|
||||
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 {
|
||||
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")
|
||||
@@ -171,7 +171,7 @@ pub fn encrypt_to_credential_id(
|
||||
let mut payload = Vec::new();
|
||||
let cbor = cbor_map_options! {
|
||||
CredentialSourceField::PrivateKey => private_key,
|
||||
CredentialSourceField::RpIdHash=> rp_id_hash,
|
||||
CredentialSourceField::RpIdHash => rp_id_hash,
|
||||
CredentialSourceField::CredProtectPolicy => cred_protect_policy,
|
||||
CredentialSourceField::CredBlob => cred_blob,
|
||||
};
|
||||
|
||||
@@ -20,8 +20,8 @@ use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use core::convert::TryFrom;
|
||||
use crypto::cbc::{cbc_decrypt, cbc_encrypt};
|
||||
use crypto::ecdsa;
|
||||
use crypto::sha256::Sha256;
|
||||
use crypto::{ecdsa, hybrid};
|
||||
use rng256::Rng256;
|
||||
use sk_cbor as cbor;
|
||||
use sk_cbor::{cbor_array, cbor_bytes, cbor_int};
|
||||
@@ -82,6 +82,7 @@ pub enum PrivateKey {
|
||||
Ecdsa([u8; 32]),
|
||||
#[cfg(feature = "ed25519")]
|
||||
Ed25519(ed25519_compact::SecretKey),
|
||||
Hybrid(hybrid::SecKey),
|
||||
}
|
||||
|
||||
impl PrivateKey {
|
||||
@@ -100,6 +101,34 @@ impl PrivateKey {
|
||||
let bytes = env.rng().gen_uniform_u8x32();
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
pub fn get_pub_key(&self, env: &mut impl Env) -> Result<CoseKey, Ctap2StatusCode> {
|
||||
Ok(match self {
|
||||
@@ -145,6 +183,7 @@ impl PrivateKey {
|
||||
}
|
||||
#[cfg(feature = "ed25519")]
|
||||
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(),
|
||||
#[cfg(feature = "ed25519")]
|
||||
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,
|
||||
#[cfg(feature = "ed25519")]
|
||||
PrivateKey::Ed25519(_) => SignatureAlgorithm::Eddsa,
|
||||
PrivateKey::Hybrid(_) => SignatureAlgorithm::Hybrid,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +221,11 @@ impl PrivateKey {
|
||||
PrivateKey::Ecdsa(ecdsa_seed) => ecdsa_seed.to_vec(),
|
||||
#[cfg(feature = "ed25519")]
|
||||
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")]
|
||||
SignatureAlgorithm::Eddsa => PrivateKey::new_ed25519_from_bytes(&key_bytes)
|
||||
.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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<hybrid::SecKey> for PrivateKey {
|
||||
fn from(hybrid_key: hybrid::SecKey) -> Self {
|
||||
PrivateKey::Hybrid(hybrid_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -15,21 +15,26 @@
|
||||
use super::crypto_wrapper::PrivateKey;
|
||||
use super::status_code::Ctap2StatusCode;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "fuzz")]
|
||||
use arbitrary::Arbitrary;
|
||||
use arrayref::array_ref;
|
||||
use core::convert::TryFrom;
|
||||
use crypto::{ecdh, ecdsa};
|
||||
use crypto::{ecdh, ecdsa, hybrid};
|
||||
#[cfg(test)]
|
||||
use enum_iterator::IntoEnumIterator;
|
||||
use sk_cbor as cbor;
|
||||
use sk_cbor::{cbor_array_vec, cbor_map, cbor_map_options, destructure_cbor_map};
|
||||
use sk_cbor::{cbor_array_vec, cbor_bytes, cbor_map_options, destructure_cbor_map};
|
||||
use {dilithium, sk_cbor as cbor};
|
||||
|
||||
// Used as the identifier for ECDSA in assertion signatures and COSE.
|
||||
pub const ES256_ALGORITHM: i64 = -7;
|
||||
#[cfg(feature = "ed25519")]
|
||||
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
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -510,6 +515,7 @@ pub enum SignatureAlgorithm {
|
||||
Es256 = ES256_ALGORITHM as isize,
|
||||
#[cfg(feature = "ed25519")]
|
||||
Eddsa = EDDSA_ALGORITHM as isize,
|
||||
Hybrid = HYBRID_ALGORITHM as isize,
|
||||
// This is the default for all numbers not covered above.
|
||||
// Unknown types should be ignored, instead of returning errors.
|
||||
Unknown = 0,
|
||||
@@ -527,6 +533,7 @@ impl From<i64> for SignatureAlgorithm {
|
||||
ES256_ALGORITHM => SignatureAlgorithm::Es256,
|
||||
#[cfg(feature = "ed25519")]
|
||||
EDDSA_ALGORITHM => SignatureAlgorithm::Eddsa,
|
||||
HYBRID_ALGORITHM => SignatureAlgorithm::Hybrid,
|
||||
_ => SignatureAlgorithm::Unknown,
|
||||
}
|
||||
}
|
||||
@@ -733,6 +740,7 @@ pub struct CoseKey {
|
||||
algorithm: i64,
|
||||
key_type: i64,
|
||||
curve: i64,
|
||||
dilithium_bytes: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl CoseKey {
|
||||
@@ -744,6 +752,8 @@ impl CoseKey {
|
||||
const EC2_KEY_TYPE: i64 = 2;
|
||||
#[cfg(feature = "ed25519")]
|
||||
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.
|
||||
const P_256_CURVE: i64 = 1;
|
||||
#[cfg(feature = "ed25519")]
|
||||
@@ -763,6 +773,7 @@ impl TryFrom<cbor::Value> for CoseKey {
|
||||
-1 => curve,
|
||||
-2 => x_bytes,
|
||||
-3 => y_bytes,
|
||||
-4 => dilithium_bytes,
|
||||
} = extract_map(cbor_value)?;
|
||||
}
|
||||
|
||||
@@ -785,16 +796,30 @@ impl TryFrom<cbor::Value> for CoseKey {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
x_bytes: *array_ref![x_bytes.as_slice(), 0, ecdh::NBYTES],
|
||||
y_bytes: *array_ref![y_bytes.as_slice(), 0, ecdh::NBYTES],
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
dilithium_bytes: parsed_dilithium_bytes,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -807,14 +832,16 @@ impl From<CoseKey> for cbor::Value {
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
dilithium_bytes,
|
||||
} = cose_key;
|
||||
|
||||
cbor_map! {
|
||||
1 => key_type,
|
||||
3 => algorithm,
|
||||
-1 => curve,
|
||||
-2 => x_bytes,
|
||||
-3 => y_bytes,
|
||||
cbor_map_options! {
|
||||
1 => Some(key_type),
|
||||
3 => Some(algorithm),
|
||||
-1 => Some(curve),
|
||||
-2 => Some(cbor_bytes!(x_bytes.to_vec())),
|
||||
-3 => Some(cbor_bytes!(y_bytes.to_vec())),
|
||||
-4 => dilithium_bytes.map(|b| cbor_bytes!(b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -830,6 +857,7 @@ impl From<ecdh::PubKey> for CoseKey {
|
||||
algorithm: CoseKey::ECDH_ALGORITHM,
|
||||
key_type: CoseKey::EC2_KEY_TYPE,
|
||||
curve: CoseKey::P_256_CURVE,
|
||||
dilithium_bytes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -845,6 +873,7 @@ impl From<ecdsa::PubKey> for CoseKey {
|
||||
algorithm: ES256_ALGORITHM,
|
||||
key_type: CoseKey::EC2_KEY_TYPE,
|
||||
curve: CoseKey::P_256_CURVE,
|
||||
dilithium_bytes: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -858,6 +887,27 @@ impl From<ed25519_compact::PublicKey> for CoseKey {
|
||||
key_type: CoseKey::OKP_KEY_TYPE,
|
||||
curve: CoseKey::ED25519_CURVE,
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -872,6 +922,7 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
dilithium_bytes,
|
||||
} = cose_key;
|
||||
|
||||
// Since algorithm can be used for different COSE key types, we check
|
||||
@@ -884,6 +935,9 @@ impl TryFrom<CoseKey> for ecdh::PubKey {
|
||||
if key_type != CoseKey::EC2_KEY_TYPE || curve != CoseKey::P_256_CURVE {
|
||||
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)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
}
|
||||
@@ -899,6 +953,7 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
||||
algorithm,
|
||||
key_type,
|
||||
curve,
|
||||
dilithium_bytes,
|
||||
} = cose_key;
|
||||
|
||||
if algorithm != ES256_ALGORITHM
|
||||
@@ -907,6 +962,9 @@ impl TryFrom<CoseKey> for ecdsa::PubKey {
|
||||
{
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_UNSUPPORTED_ALGORITHM);
|
||||
}
|
||||
if dilithium_bytes.is_some() {
|
||||
return Err(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER);
|
||||
}
|
||||
ecdsa::PubKey::from_coordinates(&x_bytes, &y_bytes)
|
||||
.ok_or(Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
}
|
||||
@@ -1242,7 +1300,7 @@ mod test {
|
||||
use super::*;
|
||||
use crate::env::test::TestEnv;
|
||||
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,
|
||||
};
|
||||
use rng256::Rng256;
|
||||
|
||||
@@ -114,8 +114,6 @@ pub const U2F_VERSION_STRING: &str = "U2F_V2";
|
||||
// TODO(#106) change to final string when ready
|
||||
pub const FIDO2_1_VERSION_STRING: &str = "FIDO_2_1_PRE";
|
||||
|
||||
// We currently only support one algorithm for signatures: ES256.
|
||||
// This algorithm is requested in MakeCredential and advertized in GetInfo.
|
||||
pub const ES256_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialParameter {
|
||||
cred_type: PublicKeyCredentialType::PublicKey,
|
||||
alg: SignatureAlgorithm::Es256,
|
||||
@@ -127,10 +125,16 @@ pub const EDDSA_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialPa
|
||||
alg: SignatureAlgorithm::Eddsa,
|
||||
};
|
||||
|
||||
pub const HYBRID_CRED_PARAM: PublicKeyCredentialParameter = PublicKeyCredentialParameter {
|
||||
cred_type: PublicKeyCredentialType::PublicKey,
|
||||
alg: SignatureAlgorithm::Hybrid,
|
||||
};
|
||||
|
||||
const SUPPORTED_CRED_PARAMS: &[PublicKeyCredentialParameter] = &[
|
||||
ES256_CRED_PARAM,
|
||||
#[cfg(feature = "ed25519")]
|
||||
EDDSA_CRED_PARAM,
|
||||
HYBRID_CRED_PARAM,
|
||||
];
|
||||
|
||||
fn get_preferred_cred_param(
|
||||
@@ -261,6 +265,8 @@ fn send_keepalive_up_needed(
|
||||
}
|
||||
match processed_packet {
|
||||
ProcessedPacket::InitPacket { cmd, .. } => {
|
||||
// Clippy doesn't understand the macro.
|
||||
#[allow(clippy::branches_sharing_code)]
|
||||
if cmd == CtapHidCommand::Cancel as u8 {
|
||||
// We ignore the payload, we can't answer with an error code anyway.
|
||||
debug_ctap!(env, "User presence check cancelled");
|
||||
@@ -848,7 +854,7 @@ impl CtapState {
|
||||
|
||||
// We decide on the algorithm early, but delay key creation since it takes time.
|
||||
// We rather do that later so all intermediate checks may return faster.
|
||||
let private_key = PrivateKey::new(env, algorithm);
|
||||
let (private_key, public_cose_key) = PrivateKey::new_with_pub_key(env, algorithm);
|
||||
let credential_id = if options.rk {
|
||||
let random_id = env.rng().gen_uniform_u8x32().to_vec();
|
||||
let credential_source = PublicKeyCredentialSource {
|
||||
@@ -887,13 +893,11 @@ impl CtapState {
|
||||
|
||||
let mut auth_data = self.generate_auth_data(env, &rp_id_hash, flags)?;
|
||||
auth_data.extend(&storage::aaguid(env)?);
|
||||
// The length is fixed to 0x20 or 0x80 and fits one byte.
|
||||
if credential_id.len() > 0xFF {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR);
|
||||
}
|
||||
auth_data.extend(vec![0x00, credential_id.len() as u8]);
|
||||
auth_data.extend(vec![
|
||||
(credential_id.len() >> 8) as u8,
|
||||
credential_id.len() as u8,
|
||||
]);
|
||||
auth_data.extend(&credential_id);
|
||||
let public_cose_key = private_key.get_pub_key(env)?;
|
||||
cbor_write(cbor::Value::from(public_cose_key), &mut auth_data)?;
|
||||
if has_extension_output {
|
||||
let hmac_secret_output = if extensions.hmac_secret {
|
||||
@@ -943,7 +947,20 @@ impl CtapState {
|
||||
Some(vec![certificate]),
|
||||
)
|
||||
}
|
||||
None => (private_key.sign_and_encode(env, &signature_data)?, None),
|
||||
None => {
|
||||
if matches!(algorithm, SignatureAlgorithm::Hybrid) {
|
||||
// We can't attest with Dilithium due to message size limits.
|
||||
let new_ecdsa_key = ecdsa::SecKey::gensk(env.rng());
|
||||
(
|
||||
new_ecdsa_key
|
||||
.sign_rfc6979::<Sha256>(&signature_data)
|
||||
.to_asn1_der(),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
(private_key.sign_and_encode(env, &signature_data)?, None)
|
||||
}
|
||||
}
|
||||
};
|
||||
let attestation_statement = PackedAttestationStatement {
|
||||
alg: SignatureAlgorithm::Es256 as i64,
|
||||
|
||||
4
src/env/test/mod.rs
vendored
4
src/env/test/mod.rs
vendored
@@ -50,6 +50,10 @@ impl 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] {
|
||||
let mut result = [Default::default(); 32];
|
||||
self.rng.fill(&mut result);
|
||||
|
||||
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 {
|
||||
if timeout.integer() == 0 {
|
||||
return Err(UserPresenceError::Timeout);
|
||||
return Ok(());
|
||||
}
|
||||
blink_leds(self.blink_pattern);
|
||||
self.blink_pattern += 1;
|
||||
|
||||
2
src/env/tock/storage.rs
vendored
2
src/env/tock/storage.rs
vendored
@@ -379,7 +379,7 @@ impl UpgradeStorage for TockUpgradeStorage {
|
||||
// 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)?;
|
||||
self.check_partition_hash(metadata)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ use libtock_drivers::timer::Duration;
|
||||
use libtock_drivers::usb_ctap_hid;
|
||||
use usb_ctap_hid::UsbEndpoint;
|
||||
|
||||
libtock_core::stack_size! {0x4000}
|
||||
libtock_core::stack_size! {0x11800}
|
||||
|
||||
const SEND_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(1000);
|
||||
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
Reference in New Issue
Block a user