mirror of
https://github.com/saymrwulf/fips205-source.git
synced 2026-09-03 19:53:49 +00:00
docs, bench stats
This commit is contained in:
parent
49bebd533e
commit
5f8a96c02b
8 changed files with 564 additions and 395 deletions
43
README.md
43
README.md
|
|
@ -10,11 +10,13 @@
|
||||||
desktop, browser and embedded applications. The source repository includes examples demonstrating
|
desktop, browser and embedded applications. The source repository includes examples demonstrating
|
||||||
benchmarking, constant-time statistical measurements, and WASM execution.
|
benchmarking, constant-time statistical measurements, and WASM execution.
|
||||||
|
|
||||||
This crate implements the FIPS 205 **final/released** standard in pure Rust with minimal and mainstream dependencies. All
|
This crate implements the FIPS 205 **final/released** standard in pure Rust with minimal and mainstream dependencies,
|
||||||
twelve (!!) security parameter sets are fully functional. The implementation does not require the standard library,
|
and without any unsafe code. All twelve (!!) security parameter sets are fully functional. The implementation's
|
||||||
e.g. `#[no_std]`, has no heap allocations, e.g. no `alloc` needed, and exposes the `RNG` so it is suitable for the
|
key- and signature-generation functionality operates in constant-time, does not require the standard library, e.g.
|
||||||
full range of applications from server down to the bare-metal. The API is stabilized and the code is heavily biased
|
`#[no_std]`, has no heap allocations, e.g. no `alloc` needed, and exposes the `RNG` so it is suitable for the full
|
||||||
towards safety and correctness; further performance optimizations will be implemented as the standard matures.
|
range of applications from server 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 204 standard/vectors as they become available.
|
||||||
|
|
||||||
See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.205.pdf> for a full description of the target functionality.
|
See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.205.pdf> for a full description of the target functionality.
|
||||||
|
|
||||||
|
|
@ -29,16 +31,24 @@ use fips205::traits::{SerDes, Signer, Verifier};
|
||||||
|
|
||||||
let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
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()?; // Generate both public and secret keys
|
// Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?; // Use the secret key to generate signature
|
let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
// Use the secret key to generate a signature. The second parameter is the
|
||||||
|
// context string (often just an empty &[]), and the last parameter selects
|
||||||
|
// the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
|
||||||
// Serialize the public key, and send with message and signature bytes
|
|
||||||
|
// Serialize the public key, and send with message and signature bytes. These
|
||||||
|
// statements model sending byte arrays over the wire.
|
||||||
let (pk_send, msg_send, sig_send) = (pk1.into_bytes(), msg_bytes, sig_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);
|
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
|
|
||||||
|
// Deserialize the public key. This only fails on a malformed key.
|
||||||
let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
// Use the public key to verify the msg signature
|
||||||
let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
assert!(v);
|
assert!(v);
|
||||||
# Ok(())
|
# Ok(())
|
||||||
|
|
@ -50,15 +60,16 @@ desired [security parameter](#modules) below.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
* This crate is fully functional and corresponds to the final/released FIPS 205, including
|
* This crate is fully functional and corresponds to the final/released FIPS 205 (August 13, 2024),
|
||||||
the pre-hash variants which formalize methods for signing a hash of the message instead of
|
including the pre-hash variants which formalize methods for signing a hash of the message instead
|
||||||
the message itself (along with metadata about the hasher used).
|
of the message itself (along with metadata about the hasher used).
|
||||||
* Constant-time assurances target the source-code level only, and are a work in progress.
|
* Constant-time assurances target the source-code level only, with confirmation via
|
||||||
|
manual review/inspection, the embedded target, and the `dudect` dynamic tests.
|
||||||
* Note that FIPS 205 places specific requirements on randomness per section 3.1, hence the exposed `RNG`.
|
* 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,
|
* 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.
|
but it will be done with a minor version bump (when the major version is larger than 0).
|
||||||
* All on-by-default features of this library are covered by `SemVer`.
|
* 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!
|
* The FIPS 205 standard and this software should be considered experimental -- USE AT YOUR OWN RISK!
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,50 @@
|
||||||
|
Figure-of-merit only; no particular care has been taken to disable turbo-boost etc.
|
||||||
|
Note that constant-time restrictions on the implementation do impact performance.
|
||||||
|
|
||||||
Figure-of-merit ... no particular care taken to disable turbo boost etc
|
Additional performance optimizations will follow the next update to FIPS 205.
|
||||||
|
|
||||||
~~~
|
~~~
|
||||||
// $ RUSTFLAGS="-C target-cpu=native" cargo bench
|
October 3, 2024
|
||||||
// Intel® Core™ i7-7700K CPU @ 4.20GHz × 8
|
Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 Circa 2017 w/ Rust 1.81.0
|
||||||
|
|
||||||
// Mar 10 2024
|
$ RUSTFLAGS="-C target-cpu=native" cargo bench
|
||||||
|
|
||||||
sha2_128f keygen time: [1.8046 ms 1.8049 ms 1.8053 ms]
|
sha2_128f keygen time: [1.7823 ms 1.7830 ms 1.7839 ms]
|
||||||
sha2_192f keygen time: [2.6420 ms 2.6425 ms 2.6432 ms]
|
sha2_192f keygen time: [2.6234 ms 2.6256 ms 2.6279 ms]
|
||||||
sha2_256f keygen time: [6.9202 ms 6.9300 ms 6.9481 ms]
|
sha2_256f keygen time: [6.8753 ms 6.8797 ms 6.8858 ms]
|
||||||
shake_128f keygen time: [2.9981 ms 3.0007 ms 3.0050 ms]
|
shake_128f keygen time: [2.7946 ms 2.7953 ms 2.7961 ms]
|
||||||
shake_192f keygen time: [4.2558 ms 4.2562 ms 4.2568 ms]
|
shake_192f keygen time: [4.0918 ms 4.0954 ms 4.0993 ms]
|
||||||
shake_256f keygen time: [11.221 ms 11.236 ms 11.266 ms]
|
shake_256f keygen time: [10.704 ms 10.717 ms 10.739 ms]
|
||||||
sha2_128s keygen time: [115.98 ms 116.09 ms 116.20 ms]
|
sha2_128s keygen time: [113.89 ms 113.90 ms 113.92 ms]
|
||||||
sha2_192s keygen time: [169.23 ms 169.36 ms 169.49 ms]
|
sha2_192s keygen time: [166.62 ms 166.63 ms 166.65 ms]
|
||||||
sha2_256s keygen time: [110.80 ms 110.83 ms 110.86 ms]
|
sha2_256s keygen time: [109.25 ms 109.34 ms 109.43 ms]
|
||||||
shake_128s keygen time: [186.05 ms 186.39 ms 186.98 ms]
|
shake_128s keygen time: [178.32 ms 178.41 ms 178.52 ms]
|
||||||
shake_192s keygen time: [272.52 ms 272.68 ms 272.86 ms]
|
shake_192s keygen time: [261.50 ms 261.55 ms 261.63 ms]
|
||||||
shake_256s keygen time: [178.79 ms 178.95 ms 179.17 ms]
|
shake_256s keygen time: [173.21 ms 173.22 ms 173.23 ms]
|
||||||
|
|
||||||
sha2_128f sign time: [42.183 ms 42.204 ms 42.239 ms]
|
sha2_128f sign time: [41.623 ms 41.635 ms 41.654 ms]
|
||||||
sha2_192f sign time: [69.770 ms 69.801 ms 69.859 ms]
|
sha2_192f sign time: [68.686 ms 68.886 ms 69.138 ms]
|
||||||
sha2_256f sign time: [142.39 ms 142.45 ms 142.54 ms]
|
sha2_256f sign time: [141.52 ms 141.54 ms 141.56 ms]
|
||||||
shake_128f sign time: [67.953 ms 67.966 ms 67.986 ms]
|
shake_128f sign time: [65.349 ms 65.364 ms 65.381 ms]
|
||||||
shake_192f sign time: [109.94 ms 109.95 ms 109.96 ms]
|
shake_192f sign time: [106.67 ms 106.68 ms 106.70 ms]
|
||||||
shake_256f sign time: [224.70 ms 224.71 ms 224.73 ms]
|
shake_256f sign time: [217.07 ms 217.25 ms 217.45 ms]
|
||||||
sha2_128s sign time: [878.51 ms 878.70 ms 878.91 ms]
|
sha2_128s sign time: [867.34 ms 868.15 ms 869.14 ms]
|
||||||
sha2_192s sign time: [1.5740 s 1.5750 s 1.5761 s]
|
sha2_192s sign time: [1.5404 s 1.5414 s 1.5426 s]
|
||||||
sha2_256s sign time: [1.3848 s 1.3855 s 1.3865 s]
|
sha2_256s sign time: [1.3559 s 1.3563 s 1.3568 s]
|
||||||
shake_128s sign time: [1.4197 s 1.4206 s 1.4216 s]
|
shake_128s sign time: [1.3682 s 1.3730 s 1.3788 s]
|
||||||
shake_192s sign time: [2.4545 s 2.4554 s 2.4565 s]
|
shake_192s sign time: [2.3982 s 2.4085 s 2.4198 s]
|
||||||
shake_256s sign time: [2.1544 s 2.1550 s 2.1557 s]
|
shake_256s sign time: [2.0949 s 2.1288 s 2.1678 s]
|
||||||
|
|
||||||
sha2_128f verify time: [2.5928 ms 2.5939 ms 2.5958 ms]
|
sha2_128f verify time: [2.5693 ms 2.5735 ms 2.5781 ms]
|
||||||
sha2_192f verify time: [3.7586 ms 3.7621 ms 3.7659 ms]
|
sha2_192f verify time: [3.8974 ms 3.9857 ms 4.0836 ms]
|
||||||
sha2_256f verify time: [3.8196 ms 3.8216 ms 3.8242 ms]
|
sha2_256f verify time: [3.8619 ms 3.8760 ms 3.8925 ms]
|
||||||
shake_128f verify time: [4.0462 ms 4.0494 ms 4.0542 ms]
|
shake_128f verify time: [3.9791 ms 4.0046 ms 4.0349 ms]
|
||||||
shake_192f verify time: [5.9527 ms 5.9531 ms 5.9536 ms]
|
shake_192f verify time: [5.7540 ms 5.7838 ms 5.8202 ms]
|
||||||
shake_256f verify time: [5.9491 ms 5.9501 ms 5.9513 ms]
|
shake_256f verify time: [6.1739 ms 6.3059 ms 6.4543 ms]
|
||||||
sha2_128s verify time: [871.01 µs 871.07 µs 871.15 µs]
|
sha2_128s verify time: [887.76 µs 898.88 µs 912.39 µs]
|
||||||
sha2_192s verify time: [1.2818 ms 1.2832 ms 1.2846 ms]
|
sha2_192s verify time: [1.3260 ms 1.3372 ms 1.3522 ms]
|
||||||
sha2_256s verify time: [1.8911 ms 1.8925 ms 1.8942 ms]
|
sha2_256s verify time: [1.9167 ms 1.9321 ms 1.9527 ms]
|
||||||
shake_128s verify time: [1.4506 ms 1.4513 ms 1.4522 ms]
|
shake_128s verify time: [1.3037 ms 1.3070 ms 1.3109 ms]
|
||||||
shake_192s verify time: [2.1333 ms 2.1342 ms 2.1355 ms]
|
shake_192s verify time: [1.9459 ms 1.9595 ms 1.9802 ms]
|
||||||
shake_256s verify time: [2.8122 ms 2.8139 ms 2.8161 ms]
|
shake_256s verify time: [2.8772 ms 2.8875 ms 2.9002 ms]
|
||||||
~~~
|
~~~
|
||||||
|
|
@ -25,42 +25,18 @@ pub fn criterion_benchmark(c: &mut Criterion) {
|
||||||
let (pk_shake_256s, sk_shake_256s) = slh_dsa_shake_256s::KG::try_keygen().unwrap();
|
let (pk_shake_256s, sk_shake_256s) = slh_dsa_shake_256s::KG::try_keygen().unwrap();
|
||||||
let (pk_shake_256f, sk_shake_256f) = slh_dsa_shake_256f::KG::try_keygen().unwrap();
|
let (pk_shake_256f, sk_shake_256f) = slh_dsa_shake_256f::KG::try_keygen().unwrap();
|
||||||
|
|
||||||
let sig_sha2_128s = sk_sha2_128s
|
let sig_sha2_128s = sk_sha2_128s.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.try_sign(&message, b"context", hedged)
|
let sig_sha2_128f = sk_sha2_128f.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.unwrap();
|
let sig_sha2_192s = sk_sha2_192s.try_sign(&message, b"context", hedged).unwrap();
|
||||||
let sig_sha2_128f = sk_sha2_128f
|
let sig_sha2_192f = sk_sha2_192f.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.try_sign(&message, b"context", hedged)
|
let sig_sha2_256s = sk_sha2_256s.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.unwrap();
|
let sig_sha2_256f = sk_sha2_256f.try_sign(&message, b"context", hedged).unwrap();
|
||||||
let sig_sha2_192s = sk_sha2_192s
|
let sig_shake_128s = sk_shake_128s.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.try_sign(&message, b"context", hedged)
|
let sig_shake_128f = sk_shake_128f.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.unwrap();
|
let sig_shake_192s = sk_shake_192s.try_sign(&message, b"context", hedged).unwrap();
|
||||||
let sig_sha2_192f = sk_sha2_192f
|
let sig_shake_192f = sk_shake_192f.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.try_sign(&message, b"context", hedged)
|
let sig_shake_256s = sk_shake_256s.try_sign(&message, b"context", hedged).unwrap();
|
||||||
.unwrap();
|
let sig_shake_256f = sk_shake_256f.try_sign(&message, b"context", hedged).unwrap();
|
||||||
let sig_sha2_256s = sk_sha2_256s
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_sha2_256f = sk_sha2_256f
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_shake_128s = sk_shake_128s
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_shake_128f = sk_shake_128f
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_shake_192s = sk_shake_192s
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_shake_192f = sk_shake_192f
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_shake_256s = sk_shake_256s
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
let sig_shake_256f = sk_shake_256f
|
|
||||||
.try_sign(&message, b"context", hedged)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
c.bench_function("sha2_128f keygen", |b| b.iter(|| slh_dsa_sha2_128f::KG::try_keygen()));
|
c.bench_function("sha2_128f keygen", |b| b.iter(|| slh_dsa_sha2_128f::KG::try_keygen()));
|
||||||
c.bench_function("sha2_192f keygen", |b| b.iter(|| slh_dsa_sha2_192f::KG::try_keygen()));
|
c.bench_function("sha2_192f keygen", |b| b.iter(|| slh_dsa_sha2_192f::KG::try_keygen()));
|
||||||
|
|
|
||||||
10
rustfmt.toml
10
rustfmt.toml
|
|
@ -3,13 +3,13 @@ hard_tabs = false
|
||||||
tab_spaces = 4
|
tab_spaces = 4
|
||||||
newline_style = "Auto"
|
newline_style = "Auto"
|
||||||
indent_style = "Block"
|
indent_style = "Block"
|
||||||
use_small_heuristics = "Default"
|
#use_small_heuristics = "Default"
|
||||||
fn_call_width = 80
|
fn_call_width = 90
|
||||||
attr_fn_like_width = 70
|
attr_fn_like_width = 90
|
||||||
struct_lit_width = 60
|
struct_lit_width = 60
|
||||||
struct_variant_width = 60
|
struct_variant_width = 60
|
||||||
array_width = 60
|
array_width = 60
|
||||||
chain_width = 60
|
chain_width = 90
|
||||||
single_line_if_else_max_width = 50
|
single_line_if_else_max_width = 50
|
||||||
single_line_let_else_max_width = 50
|
single_line_let_else_max_width = 50
|
||||||
wrap_comments = false
|
wrap_comments = false
|
||||||
|
|
@ -70,7 +70,7 @@ color = "Auto"
|
||||||
unstable_features = false
|
unstable_features = false
|
||||||
disable_all_formatting = false
|
disable_all_formatting = false
|
||||||
skip_children = false
|
skip_children = false
|
||||||
hide_parse_errors = false
|
show_parse_errors = true
|
||||||
error_on_line_overflow = false
|
error_on_line_overflow = false
|
||||||
error_on_unformatted = false
|
error_on_unformatted = false
|
||||||
ignore = []
|
ignore = []
|
||||||
|
|
|
||||||
106
src/lib.rs
106
src/lib.rs
|
|
@ -49,6 +49,7 @@
|
||||||
// TODO: Roadmap
|
// TODO: Roadmap
|
||||||
// 1. Additional (external) top-level test vectors, particularly for hash variants (!!)
|
// 1. Additional (external) top-level test vectors, particularly for hash variants (!!)
|
||||||
// 2. Implement fuzz harness, embedded target, code provenance functionality
|
// 2. Implement fuzz harness, embedded target, code provenance functionality
|
||||||
|
// 3. Experiment with struct alignment for performance uplift? (and fixed size 'slices')
|
||||||
|
|
||||||
|
|
||||||
/// All functionality is covered by traits, such that consumers can utilize trait objects as desired.
|
/// All functionality is covered by traits, such that consumers can utilize trait objects as desired.
|
||||||
|
|
@ -102,10 +103,10 @@ macro_rules! functionality {
|
||||||
// ----- PRIMARY FUNCTIONS ---
|
// ----- PRIMARY FUNCTIONS ---
|
||||||
|
|
||||||
/// Generates a public and private key pair specific to this security parameter set. <br>
|
/// Generates a public and private key pair specific to this security parameter set. <br>
|
||||||
/// This function utilizes the OS default random number generator, and makes no (constant)
|
/// This function utilizes the OS default random number generator, and operates in constant
|
||||||
/// timing assurances.
|
/// timing.
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns an error when the random number generator fails; propagates internal errors.
|
/// Returns an error when the random number generator fails.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets.
|
/// use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets.
|
||||||
|
|
@ -116,16 +117,24 @@ macro_rules! functionality {
|
||||||
///
|
///
|
||||||
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
///
|
///
|
||||||
/// // Generate public/private key pair and signature
|
|
||||||
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?; // Generate both public and secret keys
|
|
||||||
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?; // Use the secret key to generate a msg signature
|
|
||||||
///
|
///
|
||||||
/// // Serialize the public key, and send with message and signature bytes
|
/// // Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
/// let (pk_send, msg_send, sig_send) = (pk1.into_bytes(), msg_bytes, sig_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);
|
/// 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
|
///
|
||||||
|
/// // Deserialize the public key. This only fails on a malformed key.
|
||||||
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
/// assert!(v);
|
/// assert!(v);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
|
|
@ -144,20 +153,35 @@ macro_rules! functionality {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets.
|
/// use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets.
|
||||||
/// use fips205::traits::{SerDes, Signer, Verifier};
|
/// use fips205::traits::{SerDes, Signer, Verifier};
|
||||||
/// use rand_chacha::rand_core::SeedableRng;
|
|
||||||
/// # use std::error::Error;
|
/// # use std::error::Error;
|
||||||
|
/// # use rand_core::OsRng;
|
||||||
/// #
|
/// #
|
||||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
///
|
///
|
||||||
/// let message = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
/// let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
|
/// let mut rng = OsRng;
|
||||||
///
|
///
|
||||||
/// // Generate key pair and signature
|
/// // Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
/// let (pk, sk) = slh_dsa_shake_128s::try_keygen_with_rng(&mut rng)?; // Generate both public and secret keys
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen_with_rng(&mut rng)?;
|
||||||
/// let sig = sk.try_sign(&message, b"context", true)?; // Use the secret key to generate a message signature ///
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
/// let v = pk.verify(&message, &sig, b"context");
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
/// assert!(v);
|
/// assert!(v);
|
||||||
/// # Ok(())}
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_keygen_with_rng(
|
pub fn try_keygen_with_rng(
|
||||||
rng: &mut impl CryptoRngCore,
|
rng: &mut impl CryptoRngCore,
|
||||||
|
|
@ -170,6 +194,7 @@ macro_rules! functionality {
|
||||||
type PrivateKey = PrivateKey;
|
type PrivateKey = PrivateKey;
|
||||||
type PublicKey = PublicKey;
|
type PublicKey = PublicKey;
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn try_keygen_with_rng(
|
fn try_keygen_with_rng(
|
||||||
rng: &mut impl CryptoRngCore,
|
rng: &mut impl CryptoRngCore,
|
||||||
) -> Result<(PublicKey, PrivateKey), &'static str> {
|
) -> Result<(PublicKey, PrivateKey), &'static str> {
|
||||||
|
|
@ -182,6 +207,7 @@ macro_rules! functionality {
|
||||||
impl Signer for PrivateKey {
|
impl Signer for PrivateKey {
|
||||||
type Signature = [u8; SIG_LEN];
|
type Signature = [u8; SIG_LEN];
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn try_sign_with_rng(
|
fn try_sign_with_rng(
|
||||||
&self, rng: &mut impl CryptoRngCore, m: &[u8], ctx: &[u8], hedged: bool,
|
&self, rng: &mut impl CryptoRngCore, m: &[u8], ctx: &[u8], hedged: bool,
|
||||||
) -> Result<[u8; SIG_LEN], &'static str> {
|
) -> Result<[u8; SIG_LEN], &'static str> {
|
||||||
|
|
@ -195,8 +221,8 @@ macro_rules! functionality {
|
||||||
sig.map(|s| s.serialize())
|
sig.map(|s| s.serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Errors
|
// Documented in traits.rs
|
||||||
fn try_sign_hash_with_rng(
|
fn try_hash_sign_with_rng(
|
||||||
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph,
|
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph,
|
||||||
hedged: bool,
|
hedged: bool,
|
||||||
) -> Result<Self::Signature, &'static str> {
|
) -> Result<Self::Signature, &'static str> {
|
||||||
|
|
@ -218,9 +244,7 @@ macro_rules! functionality {
|
||||||
sig.map(|s| s.serialize())
|
sig.map(|s| s.serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// As of Oct 2 2024, the NIST test vectors are applied to the **internal** functions rather than
|
// Documented in traits.rs
|
||||||
/// the external API.
|
|
||||||
/// # Errors
|
|
||||||
fn _test_only_raw_sign(
|
fn _test_only_raw_sign(
|
||||||
&self, rng: &mut impl CryptoRngCore, m: &[u8], hedged: bool,
|
&self, rng: &mut impl CryptoRngCore, m: &[u8], hedged: bool,
|
||||||
) -> Result<[u8; SIG_LEN], &'static str> {
|
) -> Result<[u8; SIG_LEN], &'static str> {
|
||||||
|
|
@ -229,8 +253,7 @@ macro_rules! functionality {
|
||||||
// 4: if (hedged) then ▷ or to a random n-byte string
|
// 4: if (hedged) then ▷ or to a random n-byte string
|
||||||
if hedged {
|
if hedged {
|
||||||
// 5: opt_rand ←$ Bn
|
// 5: opt_rand ←$ Bn
|
||||||
rng.try_fill_bytes(&mut opt_rand)
|
rng.try_fill_bytes(&mut opt_rand).map_err(|_| "Alg17: rng failed")?;
|
||||||
.map_err(|_| "Alg17: rng failed")?;
|
|
||||||
|
|
||||||
// 6: end if
|
// 6: end if
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +271,7 @@ macro_rules! functionality {
|
||||||
impl Verifier for PublicKey {
|
impl Verifier for PublicKey {
|
||||||
type Signature = [u8; SIG_LEN];
|
type Signature = [u8; SIG_LEN];
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn verify(&self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ctx: &[u8]) -> bool {
|
fn verify(&self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ctx: &[u8]) -> bool {
|
||||||
if ctx.len() > 255 {
|
if ctx.len() > 255 {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -260,7 +284,8 @@ macro_rules! functionality {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_hash(
|
// Documented in traits.rs
|
||||||
|
fn hash_verify(
|
||||||
&self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ctx: &[u8], ph: &Ph,
|
&self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ctx: &[u8], ph: &Ph,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if ctx.len() > 255 {
|
if ctx.len() > 255 {
|
||||||
|
|
@ -269,15 +294,20 @@ macro_rules! functionality {
|
||||||
let sig = SlhDsaSig::<A, D, HP, K, LEN, N>::deserialize(sig_bytes);
|
let sig = SlhDsaSig::<A, D, HP, K, LEN, N>::deserialize(sig_bytes);
|
||||||
let mut phm = [0u8; 64]; // hashers don't all play well with each other (varying output size)
|
let mut phm = [0u8; 64]; // hashers don't all play well with each other (varying output size)
|
||||||
let (oid, phm_len) = hash_message(m, ph, &mut phm);
|
let (oid, phm_len) = hash_message(m, ph, &mut phm);
|
||||||
let mp: &[&[u8]] = &[&[1u8], &[ctx.len().to_le_bytes()[0]], ctx, &oid, &phm[0..phm_len]];
|
let mp: &[&[u8]] = &[
|
||||||
|
&[1u8],
|
||||||
|
&[ctx.len().to_le_bytes()[0]],
|
||||||
|
ctx,
|
||||||
|
&oid,
|
||||||
|
&phm[0..phm_len],
|
||||||
|
];
|
||||||
let res = crate::slh::slh_verify::<A, D, H, HP, K, LEN, M, N>(
|
let res = crate::slh::slh_verify::<A, D, H, HP, K, LEN, M, N>(
|
||||||
&HASHERS, &mp, &sig, &self.0,
|
&HASHERS, &mp, &sig, &self.0,
|
||||||
);
|
);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// As of Oct 2 2024, the NIST test vectors are applied to the **internal** functions rather than
|
// Documented in traits.rs
|
||||||
/// the external API.
|
|
||||||
fn _test_only_raw_verify(
|
fn _test_only_raw_verify(
|
||||||
&self, m: &[u8], sig_bytes: &[u8; SIG_LEN],
|
&self, m: &[u8], sig_bytes: &[u8; SIG_LEN],
|
||||||
) -> Result<bool, &'static str> {
|
) -> Result<bool, &'static str> {
|
||||||
|
|
@ -298,6 +328,7 @@ macro_rules! functionality {
|
||||||
impl SerDes for PublicKey {
|
impl SerDes for PublicKey {
|
||||||
type ByteArray = [u8; PK_LEN];
|
type ByteArray = [u8; PK_LEN];
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn into_bytes(self) -> Self::ByteArray {
|
fn into_bytes(self) -> Self::ByteArray {
|
||||||
let mut out = [0u8; PK_LEN];
|
let mut out = [0u8; PK_LEN];
|
||||||
out[0..(PK_LEN / 2)].copy_from_slice(&self.0.pk_seed);
|
out[0..(PK_LEN / 2)].copy_from_slice(&self.0.pk_seed);
|
||||||
|
|
@ -305,6 +336,7 @@ macro_rules! functionality {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn try_from_bytes(bytes: &Self::ByteArray) -> Result<Self, &'static str> {
|
fn try_from_bytes(bytes: &Self::ByteArray) -> Result<Self, &'static str> {
|
||||||
// Result: opportunity for validation
|
// Result: opportunity for validation
|
||||||
//let mut pk = SlhPublicKey::default();
|
//let mut pk = SlhPublicKey::default();
|
||||||
|
|
@ -319,6 +351,7 @@ macro_rules! functionality {
|
||||||
impl SerDes for PrivateKey {
|
impl SerDes for PrivateKey {
|
||||||
type ByteArray = [u8; SK_LEN];
|
type ByteArray = [u8; SK_LEN];
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn into_bytes(self) -> Self::ByteArray {
|
fn into_bytes(self) -> Self::ByteArray {
|
||||||
let mut bytes = [0u8; SK_LEN];
|
let mut bytes = [0u8; SK_LEN];
|
||||||
bytes[0..(SK_LEN / 4)].copy_from_slice(&self.0.sk_seed);
|
bytes[0..(SK_LEN / 4)].copy_from_slice(&self.0.sk_seed);
|
||||||
|
|
@ -328,9 +361,8 @@ macro_rules! functionality {
|
||||||
bytes
|
bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Documented in traits.rs
|
||||||
fn try_from_bytes(bytes: &Self::ByteArray) -> Result<Self, &'static str> {
|
fn try_from_bytes(bytes: &Self::ByteArray) -> Result<Self, &'static str> {
|
||||||
// Result: opportunity for validation
|
|
||||||
//let mut sk = SlhPrivateKey::default();
|
|
||||||
let mut sk = SlhPrivateKey {
|
let mut sk = SlhPrivateKey {
|
||||||
sk_seed: [0u8; N],
|
sk_seed: [0u8; N],
|
||||||
sk_prf: [0u8; N],
|
sk_prf: [0u8; N],
|
||||||
|
|
@ -338,10 +370,8 @@ macro_rules! functionality {
|
||||||
pk_root: [0u8; N],
|
pk_root: [0u8; N],
|
||||||
};
|
};
|
||||||
sk.sk_seed.copy_from_slice(&bytes[0..(SK_LEN / 4)]);
|
sk.sk_seed.copy_from_slice(&bytes[0..(SK_LEN / 4)]);
|
||||||
sk.sk_prf
|
sk.sk_prf.copy_from_slice(&bytes[(SK_LEN / 4)..(SK_LEN / 2)]);
|
||||||
.copy_from_slice(&bytes[(SK_LEN / 4)..(SK_LEN / 2)]);
|
sk.pk_seed.copy_from_slice(&bytes[(SK_LEN / 2)..(3 * SK_LEN / 4)]);
|
||||||
sk.pk_seed
|
|
||||||
.copy_from_slice(&bytes[(SK_LEN / 2)..(3 * SK_LEN / 4)]);
|
|
||||||
sk.pk_root.copy_from_slice(&bytes[(3 * SK_LEN / 4)..]);
|
sk.pk_root.copy_from_slice(&bytes[(3 * SK_LEN / 4)..]);
|
||||||
Ok(PrivateKey(sk))
|
Ok(PrivateKey(sk))
|
||||||
}
|
}
|
||||||
|
|
@ -364,20 +394,18 @@ macro_rules! functionality {
|
||||||
let pk2 = PublicKey::try_from_bytes(&pk1_bytes).unwrap();
|
let pk2 = PublicKey::try_from_bytes(&pk1_bytes).unwrap();
|
||||||
let sk2 = PrivateKey::try_from_bytes(&sk1_bytes).unwrap();
|
let sk2 = PrivateKey::try_from_bytes(&sk1_bytes).unwrap();
|
||||||
|
|
||||||
let sig = sk2
|
let sig = sk2.try_sign_with_rng(&mut rng, &message, b"context", true).unwrap();
|
||||||
.try_sign_with_rng(&mut rng, &message, b"context", true)
|
|
||||||
.unwrap();
|
|
||||||
let result = pk2.verify(&message, &sig, b"context");
|
let result = pk2.verify(&message, &sig, b"context");
|
||||||
assert!(result, "Signature failed to verify");
|
assert!(result, "Signature failed to verify");
|
||||||
let result = pk2.verify(&message, &sig, b"some other context");
|
let result = pk2.verify(&message, &sig, b"some other context");
|
||||||
assert!(!result, "Signature should not have verified");
|
assert!(!result, "Signature should not have verified");
|
||||||
for ph in [Ph::SHA256, Ph::SHA512, Ph::SHAKE128, Ph::SHAKE256] {
|
for ph in [Ph::SHA256, Ph::SHA512, Ph::SHAKE128, Ph::SHAKE256] {
|
||||||
let sig = sk2
|
let sig = sk2
|
||||||
.try_sign_hash_with_rng(&mut rng, &message, b"context", &ph, true)
|
.try_hash_sign_with_rng(&mut rng, &message, b"context", &ph, true)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let result = pk2.verify_hash(&message, &sig, b"context", &ph);
|
let result = pk2.hash_verify(&message, &sig, b"context", &ph);
|
||||||
assert!(result, "Signature failed to verify");
|
assert!(result, "Signature failed to verify");
|
||||||
let result = pk2.verify_hash(&message, &sig, b"some other context", &ph);
|
let result = pk2.hash_verify(&message, &sig, b"some other context", &ph);
|
||||||
assert!(!result, "Signature should not have verified");
|
assert!(!result, "Signature should not have verified");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/slh.rs
16
src/slh.rs
|
|
@ -25,18 +25,15 @@ pub(crate) fn slh_keygen_with_rng<
|
||||||
//
|
//
|
||||||
// 1: SK.seed ←$ B^n ▷ Set SK.seed, SK.prf, and PK.seed to random n-byte
|
// 1: SK.seed ←$ B^n ▷ Set SK.seed, SK.prf, and PK.seed to random n-byte
|
||||||
let mut sk_seed = [0u8; N];
|
let mut sk_seed = [0u8; N];
|
||||||
rng.try_fill_bytes(&mut sk_seed)
|
rng.try_fill_bytes(&mut sk_seed).map_err(|_| "Alg17: rng failed1")?;
|
||||||
.map_err(|_| "Alg17: rng failed1")?;
|
|
||||||
|
|
||||||
// 2: SK.prf ←$ B^n ▷ strings using an approved random bit generator
|
// 2: SK.prf ←$ B^n ▷ strings using an approved random bit generator
|
||||||
let mut sk_prf = [0u8; N];
|
let mut sk_prf = [0u8; N];
|
||||||
rng.try_fill_bytes(&mut sk_prf)
|
rng.try_fill_bytes(&mut sk_prf).map_err(|_| "Alg17: rng failed2")?;
|
||||||
.map_err(|_| "Alg17: rng failed2")?;
|
|
||||||
|
|
||||||
// 3: PK.seed ←$ B^n
|
// 3: PK.seed ←$ B^n
|
||||||
let mut pk_seed = [0u8; N];
|
let mut pk_seed = [0u8; N];
|
||||||
rng.try_fill_bytes(&mut pk_seed)
|
rng.try_fill_bytes(&mut pk_seed).map_err(|_| "Alg17: rng failed3")?;
|
||||||
.map_err(|_| "Alg17: rng failed3")?;
|
|
||||||
|
|
||||||
// 4/5/6: implemented by ? operator on the above steps; not timing/order sensitive
|
// 4/5/6: implemented by ? operator on the above steps; not timing/order sensitive
|
||||||
|
|
||||||
|
|
@ -108,7 +105,7 @@ pub(crate) fn slh_sign_with_rng<
|
||||||
// 2: return ⊥ ▷ return an error indication if the context string is too long
|
// 2: return ⊥ ▷ return an error indication if the context string is too long
|
||||||
// 3: end if
|
// 3: end if
|
||||||
// The ctx length is checked in both calling functions (where it is a bit more
|
// The ctx length is checked in both calling functions (where it is a bit more
|
||||||
// visible and immediate): `try_sign_with_rng()` and `try_sign_hash_with_rng()`
|
// visible and immediate): `try_sign_with_rng()` and `try_hash_sign_with_rng()`
|
||||||
|
|
||||||
// 4: 𝑎𝑑𝑑𝑟𝑛𝑑 ←− 𝔹𝑛 ▷ skip lines 4 through 7 for the deterministic variant
|
// 4: 𝑎𝑑𝑑𝑟𝑛𝑑 ←− 𝔹𝑛 ▷ skip lines 4 through 7 for the deterministic variant
|
||||||
let mut opt_rand = sk.pk_seed;
|
let mut opt_rand = sk.pk_seed;
|
||||||
|
|
@ -117,8 +114,7 @@ pub(crate) fn slh_sign_with_rng<
|
||||||
// 6: return ⊥
|
// 6: return ⊥
|
||||||
if hedged {
|
if hedged {
|
||||||
//
|
//
|
||||||
rng.try_fill_bytes(&mut opt_rand)
|
rng.try_fill_bytes(&mut opt_rand).map_err(|_| "Alg17: rng failed")?;
|
||||||
.map_err(|_| "Alg17: rng failed")?;
|
|
||||||
|
|
||||||
// 7: end if
|
// 7: end if
|
||||||
}
|
}
|
||||||
|
|
@ -260,7 +256,7 @@ pub(crate) fn slh_verify<
|
||||||
// 2: return false
|
// 2: return false
|
||||||
// 3: end if
|
// 3: end if
|
||||||
// The ctx length is checked in both calling functions (where it is a bit more
|
// The ctx length is checked in both calling functions (where it is a bit more
|
||||||
// visible and immediate): `verify()` and `verify_hash()`
|
// visible and immediate): `verify()` and `hash_verify()`
|
||||||
|
|
||||||
|
|
||||||
// 4: 𝑀 ′ ← toByte(0, 1) ∥ toByte(|𝑐𝑡𝑥|, 1) ∥ 𝑐𝑡𝑥 ∥ 𝑀
|
// 4: 𝑀 ′ ← toByte(0, 1) ∥ toByte(|𝑐𝑡𝑥|, 1) ∥ 𝑐𝑡𝑥 ∥ 𝑀
|
||||||
|
|
|
||||||
639
src/traits.rs
639
src/traits.rs
|
|
@ -5,11 +5,412 @@ use crate::Ph;
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
|
|
||||||
|
/// The `KeyGen` trait is defined to allow trait objects.
|
||||||
|
pub trait KeyGen {
|
||||||
|
/// A public key specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
||||||
|
type PublicKey;
|
||||||
|
/// A private (secret) key specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
||||||
|
type PrivateKey;
|
||||||
|
|
||||||
|
|
||||||
|
/// Generates a public and private key pair specific to this security parameter set.
|
||||||
|
/// This function utilizes the **OS default** random number generator. This function operates
|
||||||
|
/// in constant-time relative to secret data.
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error when the random number generator fails.
|
||||||
|
/// # Examples
|
||||||
|
/// ```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 both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "default-rng")]
|
||||||
|
fn try_keygen() -> Result<(Self::PublicKey, Self::PrivateKey), &'static str> {
|
||||||
|
Self::try_keygen_with_rng(&mut OsRng)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Generates a public and private key pair specific to this security parameter set.
|
||||||
|
/// This function utilizes the **provided** random number generator. This function operates
|
||||||
|
/// in constant-time relative to secret data.
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error when the random number generator fails.
|
||||||
|
/// # Examples
|
||||||
|
/// ```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;
|
||||||
|
/// # use rand_core::OsRng;
|
||||||
|
/// #
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
///
|
||||||
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
///
|
||||||
|
/// // Generate both public and secret keys. This only fails when the provided rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen_with_rng(&mut rng)?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
fn try_keygen_with_rng(
|
||||||
|
rng: &mut impl CryptoRngCore,
|
||||||
|
) -> Result<(Self::PublicKey, Self::PrivateKey), &'static str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The Signer trait is implemented for the `PrivateKey` struct on each of the security parameter sets
|
||||||
|
pub trait Signer {
|
||||||
|
/// The signature is specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
||||||
|
type Signature;
|
||||||
|
|
||||||
|
|
||||||
|
/// Attempt to sign the given message, returning a digital signature on success, or an error if
|
||||||
|
/// something went wrong. This function utilizes the **OS default** random number generator.
|
||||||
|
/// This function operates in constant-time relative to secret data (excluding the random number
|
||||||
|
/// generator internals). Uses a FIPS 205 context string (default: an empty string).
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error when the random number generator fails.
|
||||||
|
/// # Examples
|
||||||
|
/// ```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 both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "default-rng")]
|
||||||
|
fn try_sign(
|
||||||
|
&self, message: &[u8], ctx: &[u8], hedged: bool,
|
||||||
|
) -> Result<Self::Signature, &'static str> {
|
||||||
|
self.try_sign_with_rng(&mut OsRng, message, ctx, hedged)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Attempt to sign the hash of a given message, returning a digital signature on success, or an
|
||||||
|
/// error if something went wrong. This function utilizes the **OS default** random number
|
||||||
|
/// generator. This function operates in constant-time relative to secret data (excluding the
|
||||||
|
/// random number generator internals). Uses a FIPS 205 context string (default: an empty string).
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error when the random number generator fails.
|
||||||
|
/// # Examples
|
||||||
|
/// ```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;
|
||||||
|
/// # use fips205::Ph;
|
||||||
|
/// #
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
///
|
||||||
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_hash_sign(&msg_bytes, b"context", &Ph::SHA256, true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the signature on the message hash
|
||||||
|
/// let v = pk2.hash_verify(&msg_recv, &sig_recv, b"context", &Ph::SHA256);
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[cfg(feature = "default-rng")]
|
||||||
|
fn try_hash_sign(
|
||||||
|
&self, message: &[u8], ctx: &[u8], ph: &Ph, hedged: bool,
|
||||||
|
) -> Result<Self::Signature, &'static str> {
|
||||||
|
self.try_hash_sign_with_rng(&mut OsRng, message, ctx, ph, hedged)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Attempt to sign a given message, returning a digital signature on success, or an
|
||||||
|
/// error if something went wrong. This function utilizes a **provided** random number generator.
|
||||||
|
/// This function operates in constant-time relative to secret data (excluding the random number
|
||||||
|
/// generator internals). Uses a FIPS 205 context string (default: an empty string).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error when the random number generator fails.
|
||||||
|
/// # Examples
|
||||||
|
/// ```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;
|
||||||
|
/// # use rand_core::OsRng;
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
///
|
||||||
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
///
|
||||||
|
/// // Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The third parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the provided rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign_with_rng(&mut rng, &msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
fn try_sign_with_rng(
|
||||||
|
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], hedged: bool,
|
||||||
|
) -> Result<Self::Signature, &'static str>;
|
||||||
|
|
||||||
|
|
||||||
|
/// Attempt to sign the hash of a given message, returning a digital signature on success, or an
|
||||||
|
/// error if something went wrong. This function utilizes a **provided** random number generator.
|
||||||
|
/// This function operates in constant-time relative to secret data (excluding the random number
|
||||||
|
/// generator internals). Uses a FIPS 205 context string (default: an empty string).
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Returns an error when the random number generator fails.
|
||||||
|
/// # Examples
|
||||||
|
/// ```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;
|
||||||
|
/// # use rand_core::OsRng;
|
||||||
|
/// # use fips205::Ph;
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
///
|
||||||
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
/// let mut rng = OsRng;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The third parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the provided rng fails.
|
||||||
|
/// let sig_bytes =
|
||||||
|
/// sk.try_hash_sign_with_rng(&mut rng, &msg_bytes, b"context", &Ph::SHA512, true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.hash_verify(&msg_recv, &sig_recv, b"context", &Ph::SHA512);
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
fn try_hash_sign_with_rng(
|
||||||
|
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph, hedged: bool,
|
||||||
|
) -> Result<Self::Signature, &'static str>;
|
||||||
|
|
||||||
|
|
||||||
|
/// As of October 4 2024, the available NIST test vectors are applied to the **internal** functions
|
||||||
|
/// rather than the external API. This function should not be used outside of this scenario.
|
||||||
|
/// # Errors
|
||||||
|
#[deprecated = "Temporary function to allow application of internal nist vectors; will be removed"]
|
||||||
|
fn _test_only_raw_sign(
|
||||||
|
&self, rng: &mut impl CryptoRngCore, m: &[u8], hedged: bool,
|
||||||
|
) -> Result<Self::Signature, &'static str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The Verifier trait is implemented for `PublicKey` on each of the security parameter sets
|
||||||
|
pub trait Verifier {
|
||||||
|
/// The signature is specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
||||||
|
type Signature;
|
||||||
|
|
||||||
|
|
||||||
|
/// Verifies a digital signature with respect to a `PublicKey`. This function does not operates on
|
||||||
|
/// secret data, so it need/does not provide constant-time assurances. Uses a FIPS 205 context string
|
||||||
|
/// (default: an empty string).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```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 both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the msg signature
|
||||||
|
/// let v = pk2.verify(&msg_recv, &sig_recv, b"context");
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
fn verify(&self, message: &[u8], signature: &Self::Signature, ctx: &[u8]) -> bool;
|
||||||
|
|
||||||
|
|
||||||
|
/// Verifies a digital signature on the hash of a message with respect to a `PublicKey`. As this
|
||||||
|
/// function operates on purely public data, it need/does not provide constant-time assurances.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```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;
|
||||||
|
/// # use fips205::Ph;
|
||||||
|
/// #
|
||||||
|
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
///
|
||||||
|
/// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7];
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Generate both public and secret keys. This only fails when the OS rng fails.
|
||||||
|
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?;
|
||||||
|
/// // Use the secret key to generate a signature. The second parameter is the
|
||||||
|
/// // context string (often just an empty &[]), and the last parameter selects
|
||||||
|
/// // the preferred hedged variant. This only fails when the OS rng fails.
|
||||||
|
/// let sig_bytes = sk.try_hash_sign(&msg_bytes, b"context", &Ph::SHA256, true)?;
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// // Serialize the public key, and send with message and signature bytes. These
|
||||||
|
/// // statements model sending byte arrays over the wire.
|
||||||
|
/// 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. This only fails on a malformed key.
|
||||||
|
/// let pk2 = slh_dsa_shake_128s::PublicKey::try_from_bytes(&pk_recv)?;
|
||||||
|
/// // Use the public key to verify the signature on the message hash
|
||||||
|
/// let v = pk2.hash_verify(&msg_recv, &sig_recv, b"context", &Ph::SHA256);
|
||||||
|
/// assert!(v);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[must_use]
|
||||||
|
fn hash_verify(&self, message: &[u8], signature: &Self::Signature, ctx: &[u8], ph: &Ph)
|
||||||
|
-> bool;
|
||||||
|
|
||||||
|
|
||||||
|
/// As of October 4 2024, the available NIST test vectors are applied to the **internal** functions
|
||||||
|
/// rather than the external API. This function should not be used outside of this scenario.
|
||||||
|
/// # Errors
|
||||||
|
#[deprecated = "Temporary function to allow application of internal nist vectors; will be removed"]
|
||||||
|
fn _test_only_raw_verify(
|
||||||
|
&self, m: &[u8], sig_bytes: &Self::Signature,
|
||||||
|
) -> Result<bool, &'static str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The `SerDes` trait provides for validated serialization and deserialization of fixed size elements
|
/// The `SerDes` trait provides for validated serialization and deserialization of fixed size elements
|
||||||
pub trait SerDes {
|
pub trait SerDes {
|
||||||
/// The fixed-size byte array to be serialized or deserialized
|
/// The fixed-size byte array to be serialized or deserialized
|
||||||
type ByteArray;
|
type ByteArray;
|
||||||
|
|
||||||
|
|
||||||
/// Produces a byte array of fixed-size specific to the struct being serialized.
|
/// Produces a byte array of fixed-size specific to the struct being serialized.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
|
@ -38,6 +439,7 @@ pub trait SerDes {
|
||||||
/// ```
|
/// ```
|
||||||
fn into_bytes(self) -> Self::ByteArray;
|
fn into_bytes(self) -> Self::ByteArray;
|
||||||
|
|
||||||
|
|
||||||
/// Consumes a byte array of fixed-size specific to the struct being deserialized; performs validation
|
/// Consumes a byte array of fixed-size specific to the struct being deserialized; performs validation
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns an error on malformed input.
|
/// Returns an error on malformed input.
|
||||||
|
|
@ -70,240 +472,3 @@ pub trait SerDes {
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// The `KeyGen` trait is defined to allow trait objects.
|
|
||||||
pub trait KeyGen {
|
|
||||||
/// A public key specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
|
||||||
type PublicKey;
|
|
||||||
/// A private (secret) key specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
|
||||||
type PrivateKey;
|
|
||||||
|
|
||||||
/// Generates a public and private key pair specific to this security parameter set. <br>
|
|
||||||
/// This function utilizes the OS default random number generator, and makes no (constant)
|
|
||||||
/// timing assurances.
|
|
||||||
/// # Errors
|
|
||||||
/// Returns an error when the random number generator fails; propagates internal errors.
|
|
||||||
/// # Examples
|
|
||||||
/// ```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 public/private key pair and signature
|
|
||||||
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?; // Generate both public and secret keys
|
|
||||||
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", 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.verify(&msg_recv, &sig_recv, b"context");
|
|
||||||
/// assert!(v);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[cfg(feature = "default-rng")]
|
|
||||||
fn try_keygen() -> Result<(Self::PublicKey, Self::PrivateKey), &'static str> {
|
|
||||||
Self::try_keygen_with_rng(&mut OsRng)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a public and private key pair specific to this security parameter set. <br>
|
|
||||||
/// This function utilizes a supplied random number generator, and makes no (constant)
|
|
||||||
/// timing assurances..
|
|
||||||
/// # Errors
|
|
||||||
/// Returns an error when the random number generator fails; propagates internal errors.
|
|
||||||
/// # Examples
|
|
||||||
/// ```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 public/private key pair and signature
|
|
||||||
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?; // Generate both public and secret keys
|
|
||||||
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", 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.verify(&msg_recv, &sig_recv, b"context");
|
|
||||||
/// assert!(v);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn try_keygen_with_rng(
|
|
||||||
rng: &mut impl CryptoRngCore,
|
|
||||||
) -> Result<(Self::PublicKey, Self::PrivateKey), &'static str>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// The Signer trait is implemented for the `PrivateKey` struct on each of the security parameter sets
|
|
||||||
pub trait Signer {
|
|
||||||
/// The signature is specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
|
||||||
type Signature;
|
|
||||||
|
|
||||||
/// Attempt to sign the given message, returning a digital signature on success, or an error if
|
|
||||||
/// something went wrong. This function utilizes the default OS RNG and operates in constant time
|
|
||||||
/// with respect to the `PrivateKey` only (not including rejection loop; work in progress).
|
|
||||||
/// Uses a FIPS 205 context string (default: an empty string).
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns an error when the random number generator fails; propagates internal errors.
|
|
||||||
/// # Examples
|
|
||||||
/// ```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 public/private key pair and signature
|
|
||||||
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?; // Generate both public and secret keys
|
|
||||||
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", 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.verify(&msg_recv, &sig_recv, b"context");
|
|
||||||
/// assert!(v);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[cfg(feature = "default-rng")]
|
|
||||||
fn try_sign(
|
|
||||||
&self, message: &[u8], ctx: &[u8], hedged: bool,
|
|
||||||
) -> Result<Self::Signature, &'static str> {
|
|
||||||
self.try_sign_with_rng(&mut OsRng, message, ctx, hedged)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// blah
|
|
||||||
/// # Errors
|
|
||||||
#[cfg(feature = "default-rng")]
|
|
||||||
fn try_sign_hash(
|
|
||||||
&self, message: &[u8], ctx: &[u8], ph: &Ph, hedged: bool,
|
|
||||||
) -> Result<Self::Signature, &'static str> {
|
|
||||||
self.try_sign_hash_with_rng(&mut OsRng, message, ctx, ph, hedged)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to sign the given message, returning a digital signature on success, or an error if
|
|
||||||
/// something went wrong. This function utilizes a supplied RNG and operates in constant time
|
|
||||||
/// with respect to the `PrivateKey` only (not including rejection loop; work in progress).
|
|
||||||
/// Uses a FIPS 205 context string (default: an empty string).
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns an error when the random number generator fails; propagates internal errors.
|
|
||||||
/// # Examples
|
|
||||||
/// ```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 public/private key pair and signature
|
|
||||||
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?; // Generate both public and secret keys
|
|
||||||
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", 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.verify(&msg_recv, &sig_recv, b"context");
|
|
||||||
/// assert!(v);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
fn try_sign_with_rng(
|
|
||||||
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], hedged: bool,
|
|
||||||
) -> Result<Self::Signature, &'static str>;
|
|
||||||
|
|
||||||
|
|
||||||
/// blah
|
|
||||||
/// # Errors
|
|
||||||
fn try_sign_hash_with_rng(
|
|
||||||
&self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph, hedged: bool,
|
|
||||||
) -> Result<Self::Signature, &'static str>;
|
|
||||||
|
|
||||||
|
|
||||||
/// As of Oct 2 2024, the NIST test vectors are applied to the **internal** functions rather than
|
|
||||||
/// the external API.
|
|
||||||
/// # Errors
|
|
||||||
#[deprecated = "Temporary function to allow application of internal nist vectors; will be removed"]
|
|
||||||
fn _test_only_raw_sign(
|
|
||||||
&self, rng: &mut impl CryptoRngCore, m: &[u8], hedged: bool,
|
|
||||||
) -> Result<Self::Signature, &'static str>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// The Verifier trait is implemented for `PublicKey` on each of the security parameter sets
|
|
||||||
pub trait Verifier {
|
|
||||||
/// The signature is specific to the chosen security parameter set, e.g., `slh_dsa_shake_128s`, `slh_dsa_sha2_128s` etc
|
|
||||||
type Signature;
|
|
||||||
|
|
||||||
/// Verifies a digital signature with respect to a `PublicKey`. This function operates in
|
|
||||||
/// variable time. Uses a FIPS 205 context string (default: an empty string).
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
/// ```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 public/private key pair and signature
|
|
||||||
/// let (pk1, sk) = slh_dsa_shake_128s::try_keygen()?; // Generate both public and secret keys
|
|
||||||
/// let sig_bytes = sk.try_sign(&msg_bytes, b"context", 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.verify(&msg_recv, &sig_recv, b"context");
|
|
||||||
/// assert!(v);
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[must_use]
|
|
||||||
fn verify(&self, message: &[u8], signature: &Self::Signature, ctx: &[u8]) -> bool;
|
|
||||||
|
|
||||||
/// blah todo
|
|
||||||
#[must_use]
|
|
||||||
fn verify_hash(&self, message: &[u8], signature: &Self::Signature, ctx: &[u8], ph: &Ph)
|
|
||||||
-> bool;
|
|
||||||
|
|
||||||
|
|
||||||
/// As of Oct 2 2024, the NIST test vectors are applied to the **internal** functions rather than
|
|
||||||
/// the external API.
|
|
||||||
/// # Errors
|
|
||||||
#[deprecated = "Temporary function to allow application of internal nist vectors; will be removed"]
|
|
||||||
fn _test_only_raw_verify(
|
|
||||||
&self, m: &[u8], sig_bytes: &Self::Signature,
|
|
||||||
) -> Result<bool, &'static str>;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -190,10 +190,7 @@ macro_rules! test_sign {
|
||||||
|
|
||||||
// Load private key
|
// Load private key
|
||||||
let sk = PrivateKey::try_from_bytes(
|
let sk = PrivateKey::try_from_bytes(
|
||||||
test.sk
|
test.sk.as_slice().try_into().expect("Wrong length private key"),
|
||||||
.as_slice()
|
|
||||||
.try_into()
|
|
||||||
.expect("Wrong length private key"),
|
|
||||||
)
|
)
|
||||||
.expect("Unable to load private key");
|
.expect("Unable to load private key");
|
||||||
|
|
||||||
|
|
@ -319,20 +316,14 @@ macro_rules! test_verify {
|
||||||
let is_valid: Result<bool, _> = panic::catch_unwind(|| {
|
let is_valid: Result<bool, _> = panic::catch_unwind(|| {
|
||||||
// Load public key
|
// Load public key
|
||||||
let pk = PublicKey::try_from_bytes(
|
let pk = PublicKey::try_from_bytes(
|
||||||
test.pk
|
test.pk.as_slice().try_into().expect("Wrong length public key"),
|
||||||
.as_slice()
|
|
||||||
.try_into()
|
|
||||||
.expect("Wrong length public key"),
|
|
||||||
)
|
)
|
||||||
.expect("Unable to load public key");
|
.expect("Unable to load public key");
|
||||||
|
|
||||||
// Verify signature
|
// Verify signature
|
||||||
pk._test_only_raw_verify(
|
pk._test_only_raw_verify(
|
||||||
test.message.as_slice(),
|
test.message.as_slice(),
|
||||||
test.signature
|
test.signature.as_slice().try_into().expect("Signature length incorrect"),
|
||||||
.as_slice()
|
|
||||||
.try_into()
|
|
||||||
.expect("Signature length incorrect"),
|
|
||||||
)
|
)
|
||||||
.expect("Verification failed")
|
.expect("Verification failed")
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue