fips205 prep

This commit is contained in:
eschorn1 2024-02-09 17:43:59 -06:00
parent ddfabeb6ac
commit b0213416c8
15 changed files with 562 additions and 357 deletions

113
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,113 @@
name: test
on: [ push ]
env:
CARGO_INCREMENTAL: 0
RUSTFLAGS: "-Dwarnings"
RUSTDOCFLAGS: "-Dwarnings"
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- 1.70.0 # MSRV
- stable
target:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
- s390x-unknown-linux-gnu
- powerpc64-unknown-linux-gnu
- riscv64gc-unknown-none-elf
- x86_64-pc-windows-gnu
- x86_64-apple-darwin
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- run: cargo build --target ${{ matrix.target }} --release --no-default-features --features "slh_dsa_sha2_128s slh_dsa_shake_128s slh_dsa_sha2_128f slh_dsa_shake_128f slh_dsa_sha2_192s slh_dsa_shake_192s slh_dsa_sha2_192f slh_dsa_shake_192f slh_dsa_sha2_256s slh_dsa_shake_256s slh_dsa_sha2_256f slh_dsa_shake_256f"
test:
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit Linux
- target: i686-unknown-linux-gnu
rust: 1.70.0 # MSRV
deps: sudo apt update && sudo apt install gcc-multilib
- target: i686-unknown-linux-gnu
rust: stable
deps: sudo apt update && sudo apt install gcc-multilib
# 64-bit Linux
- target: x86_64-unknown-linux-gnu
rust: 1.70.0 # MSRV
- target: x86_64-unknown-linux-gnu
rust: stable
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- run: ${{ matrix.deps }}
- run: cargo check --target ${{ matrix.target }} --all-features
- run: cargo test --release --target ${{ matrix.target }}
cross:
strategy:
matrix:
include:
# ARM32
- target: armv7-unknown-linux-gnueabihf
rust: 1.70.0 # MSRV (cross)
- target: armv7-unknown-linux-gnueabihf
rust: stable
# ARM64
- target: aarch64-unknown-linux-gnu
rust: 1.70.0 # MSRV (cross)
- target: aarch64-unknown-linux-gnu
rust: stable
# PPC32
- target: powerpc-unknown-linux-gnu
rust: 1.70.0 # MSRV (cross)
- target: powerpc-unknown-linux-gnu
rust: stable
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ${{ matrix.deps }}
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
targets: ${{ matrix.target }}
- uses: RustCrypto/actions/cross-install@master
- run: cross test --release --target ${{ matrix.target }} --all-features
doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: RustCrypto/actions/cargo-cache@master
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- run: cargo doc --all-features
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Clippy
run: cargo clippy --all-targets --all-features

6
.gitignore vendored
View file

@ -1,3 +1,7 @@
/target
/Cargo.lock
/.idea
/.idea
**/artifacts
**/corpus
/fuzz/.gitignore
**/Cargo.lock

View file

@ -5,6 +5,6 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## 0.1.0 (2023-10-15)
## 0.1.1 (2024-02-14)
- Initial API release skeleton
- Initial release

View file

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT OR Apache-2.0"
description = "FIPS 205 (draft): Stateless Hash-Based Digital Signature Standard"
repository = "https://github.com/integritychain/fips205"
rust-version = "1.73"
rust-version = "1.70"
[dependencies]

85
README.md Normal file
View file

@ -0,0 +1,85 @@
# [IntegrityChain]: FIPS 205 Stateless Hash-Based Digital Signature Standard
[![crate][crate-image]][crate-link]
[![Docs][docs-image]][docs-link]
[![Build Status][build-image]][build-link]
![Apache2/MIT licensed][license-image]
![Rust Version][rustc-image]
[FIPS 205] (Initial Public Draft) Stateless Hash-Based Digital Signature Standard written in pure Rust for server,
desktop, browser and embedded applications.
This crate implements the FIPS 205 **draft** standard in pure Rust with minimal and mainstream dependencies. All
twelve (!!) security parameter sets are fully functional. The implementation does not require the standard library,
e.g. `#[no_std]`, has no heap allocations, e.g. no `alloc` needed, and exposes the `RNG` so it is suitable for the
full range of applications down to the bare-metal. The API is stabilized and the code is heavily biased towards safety
and correctness; further performance optimizations will be implemented as the standard matures. This crate will
quickly follow any changes to FIPS 205 as they become available.
See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.205.ipd.pdf> for a full description of the target functionality.
The functionality is extremely simple to use, as demonstrated by the following example.
~~~rust
use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets.
use fips205::traits::{SerDes, Signer, Verifier};
# use std::error::Error;
#
# fn main() -> Result<(), Box<dyn Error>> {
let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
// Generate key pair and signature
let (pk1, sk) = slh_dsa_shake_128s::try_keygen_vt()?; // Generate both public and secret keys
let sig_bytes = sk.try_sign_ct(&msg_bytes, true)?; // Use the secret key to generate a msg signature
// Serialize the public key, and send with message and signature bytes
let (pk_send, msg_send, sig_send) = (pk1.into_bytes(), msg_bytes, sig_bytes);
let (pk_recv, msg_recv, sig_recv) = (pk_send, msg_send, sig_send);
// Deserialize the public key, then use it to verify the msg signature
let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
let v = pk2.try_verify_vt(&msg_recv, &sig_recv)?;
assert!(v);
# Ok(())
# }
~~~
The Rust [Documentation][docs-link] lives under each **Module** corresponding to the desired
[security parameter](#modules) below.
## Notes
* This crate is fully functional and corresponds to the first initial public draft of FIPS 205.
* Constant-time assurances target the source-code level only, and are a work in progress.
* Note that FIPS 205 places specific requirements on randomness per section 3.1, hence the exposed `RNG`.
* Requires Rust **1.70** or higher. The minimum supported Rust version may be changed in the future, but
it will be done with a minor version bump.
* All on-by-default features of this library are covered by SemVer.
* This software is experimental and still under active development -- USE AT YOUR OWN RISK!
## License
Contents are licensed under either the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
or [MIT license](http://opensource.org/licenses/MIT) at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as
defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
[//]: # (badges)
[crate-image]: https://buildstats.info/crate/fips205
[crate-link]: https://crates.io/crates/fips205
[docs-image]: https://docs.rs/fips205/badge.svg
[docs-link]: https://docs.rs/fips205/
[build-image]: https://github.com/integritychain/fips205/workflows/test/badge.svg
[build-link]: https://github.com/integritychain/fips205/actions?query=workflow%3Atest
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
[rustc-image]: https://img.shields.io/badge/rustc-1.70+-blue.svg
[//]: # (general links)
[IntegrityChain]: https://github.com/integritychain/
[FIPS 205]: https://csrc.nist.gov/pubs/fips/205/ipd

17
SECURITY.md Normal file
View file

@ -0,0 +1,17 @@
# Security Policy
## Supported Versions
Security updates are applied only to the most recent release.
## Reporting a Vulnerability
If you have discovered a security vulnerability in this project, please report
it privately. **Do not disclose it as a public issue.** This gives us time to
work with you to fix the issue before public exposure, reducing the chance that
the exploit will be used before a patch is released.
Please disclose it at [security advisory](https://github.com/integritychain/fips205/security/advisories/new).
This project is maintained by a team of volunteers on a reasonable-effort basis.
As such, please give us at least 90 days to work on a fix before public exposure.

View file

@ -1,5 +1,5 @@
use generic_array::{ArrayLength, GenericArray};
use crate::types::Adrs;
use generic_array::{ArrayLength, GenericArray};
// Holds hasher function references; constructed by each wrapper
@ -26,14 +26,14 @@ pub(crate) struct Hashers<K: ArrayLength, LEN: ArrayLength, M: ArrayLength, N: A
feature = "slh_dsa_shake_256s"
))]
pub(crate) mod shake {
use crate::types::Adrs;
use generic_array::{ArrayLength, GenericArray};
use sha3::digest::{ExtendableOutput, Update, XofReader};
use sha3::Shake256;
use crate::types::Adrs;
#[allow(clippy::module_name_repetitions)]
pub fn shake256(input: &[&[u8]], out: &mut [u8]) {
fn shake256(input: &[&[u8]], out: &mut [u8]) {
let mut hasher = Shake256::default();
input.iter().for_each(|item| hasher.update(item));
let mut reader = hasher.finalize_xof();
@ -103,13 +103,13 @@ pub(crate) mod shake {
#[cfg(any(feature = "slh_dsa_sha2_128f", feature = "slh_dsa_sha2_128s"))]
pub(crate) mod sha2_cat_1 {
use crate::types::Adrs;
use core::cmp::min;
use generic_array::{ArrayLength, GenericArray};
use sha2::{Digest, Sha256};
use crate::types::Adrs;
pub fn sha2_256(input: &[&[u8]], out: &mut [u8]) {
fn sha2_256(input: &[&[u8]], out: &mut [u8]) {
debug_assert!(out.len() <= 32);
let mut hasher = Sha256::new();
input.iter().for_each(|item| hasher.update(item));
@ -157,7 +157,7 @@ pub(crate) mod sha2_cat_1 {
}
pub fn hmac_sha_256(key: &[u8], a0: &[u8], b1: &[u8]) -> [u8; 32] {
fn hmac_sha_256(key: &[u8], a0: &[u8], b1: &[u8]) -> [u8; 32] {
let k2 = key;
let mut padded = [0x36; 64];
for (p, &k) in padded.iter_mut().zip(k2.iter()) {
@ -248,13 +248,13 @@ pub(crate) mod sha2_cat_1 {
feature = "slh_dsa_sha2_256s"
))]
pub(crate) mod sha2_cat_3_5 {
use crate::types::Adrs;
use core::cmp::min;
use generic_array::{ArrayLength, GenericArray};
use sha2::{Digest, Sha256, Sha512};
use crate::types::Adrs;
pub fn sha2_256(input: &[&[u8]], out: &mut [u8]) {
fn sha2_256(input: &[&[u8]], out: &mut [u8]) {
debug_assert!(out.len() <= 32);
let mut hasher = Sha256::new();
input.iter().for_each(|item| hasher.update(item));
@ -263,7 +263,7 @@ pub(crate) mod sha2_cat_3_5 {
}
pub fn sha2_512(input: &[&[u8]], out: &mut [u8]) {
fn sha2_512(input: &[&[u8]], out: &mut [u8]) {
debug_assert!(out.len() <= 64);
let mut hasher = Sha512::new();
input.iter().for_each(|item| hasher.update(item));
@ -311,7 +311,7 @@ pub(crate) mod sha2_cat_3_5 {
}
pub fn hmac_sha_512(key: &[u8], a0: &[u8], b1: &[u8]) -> [u8; 64] {
fn hmac_sha_512(key: &[u8], a0: &[u8], b1: &[u8]) -> [u8; 64] {
let k2 = key;
let mut padded = [0x36; 128];
for (p, &k) in padded.iter_mut().zip(k2.iter()) {

View file

@ -1,5 +1,5 @@
use generic_array::ArrayLength;
use crate::types::{Adrs, SlhDsaSig};
use generic_array::ArrayLength;
/// Algorithm 1: `toInt(X, n)` on page 14.
@ -120,7 +120,7 @@ impl<
N: ArrayLength,
> SlhDsaSig<A, D, HP, K, LEN, N>
{
pub fn deserialize<const SIG_LEN: usize>(self) -> [u8; SIG_LEN] {
pub(crate) fn deserialize<const SIG_LEN: usize>(self) -> [u8; SIG_LEN] {
let mut out = [0u8; SIG_LEN];
debug_assert_eq!(
out.len(),
@ -156,8 +156,7 @@ impl<
out
}
pub fn serialize(bytes: &[u8]) -> Self {
pub(crate) fn serialize(bytes: &[u8]) -> Self {
debug_assert_eq!(
bytes.len(),
N::to_usize() + // randomness

View file

@ -1,7 +1,7 @@
use generic_array::{ArrayLength, GenericArray};
use crate::hashers::Hashers;
use crate::types::{Adrs, HtSig};
use crate::xmss;
use generic_array::{ArrayLength, GenericArray};
/// Algorithm 11: `ht_sign(M, SK.seed, PK.seed, idx_tree, idx_leaf)` on page 27.

View file

@ -2,18 +2,23 @@
#![deny(clippy::pedantic)]
#![deny(warnings)]
//#![deny(missing_docs)]
#![doc = include_str!("../README.md")]
/// Implements FIPS 205 draft Stateless Hash-Based Digital Signature Standard.
/// See <https://csrc.nist.gov/pubs/fips/205/ipd>
/// TKTK crate doc
// TODO
// 1. General clean-up
// 7. Doc, of course!
mod fors;
mod hashers;
mod helpers;
mod hypertree;
mod slh;
mod test;
mod traits;
pub mod traits;
mod types;
mod wots;
mod xmss;

View file

@ -1,9 +1,9 @@
use generic_array::{ArrayLength, GenericArray};
use rand_core::CryptoRngCore;
use crate::hashers::Hashers;
use crate::types::FORS_TREE;
use crate::types::{Adrs, SlhDsaSig, SlhPrivateKey, SlhPublicKey};
use crate::{fors, helpers, hypertree, xmss};
use generic_array::{ArrayLength, GenericArray};
use rand_core::CryptoRngCore;
/// Algorithm 17: `slh_keygen()` on page 34.

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
use generic_array::{ArrayLength, GenericArray};
use crate::hashers::Hashers;
use crate::helpers;
use crate::types::{Adrs, WotsPk, WotsSig, WOTS_PK, WOTS_PRF};
use generic_array::{ArrayLength, GenericArray};
/// Algorithm 4: `chain(X, i, s, PK.seed, ADRS)` on page 17.

View file

@ -1,7 +1,7 @@
use generic_array::{ArrayLength, GenericArray};
use crate::hashers::Hashers;
use crate::types::{Adrs, XmssSig, TREE, WOTS_HASH};
use crate::wots;
use generic_array::{ArrayLength, GenericArray};
/// Algorithm 8: `xmss_node(SK.seed, i, z, PK.seed, ADRS)` on page 22.

314
tests/py_vectors.rs Normal file

File diff suppressed because one or more lines are too long