From 5f8a96c02bcb3e33e5dbc6222cd420f9f5742c24 Mon Sep 17 00:00:00 2001 From: eschorn1 Date: Thu, 3 Oct 2024 14:48:53 -0500 Subject: [PATCH] docs, bench stats --- README.md | 43 ++- benches/README.md | 82 ++--- benches/benchmark.rs | 48 +-- rustfmt.toml | 10 +- src/lib.rs | 106 ++++-- src/slh.rs | 16 +- src/traits.rs | 639 +++++++++++++++++++++------------ tests/nist_acvp_vectors/mod.rs | 15 +- 8 files changed, 564 insertions(+), 395 deletions(-) diff --git a/README.md b/README.md index 80c60af..e14a5b6 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,13 @@ desktop, browser and embedded applications. The source repository includes examples demonstrating 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 -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 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 implements the FIPS 205 **final/released** standard in pure Rust with minimal and mainstream dependencies, +and without any unsafe code. All twelve (!!) security parameter sets are fully functional. The implementation's +key- and signature-generation functionality operates in constant-time, 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 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 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]; -// Generate 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 signature + +// 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 + +// 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, 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)?; +// Use the public key to verify the msg signature let v = pk2.verify(&msg_recv, &sig_recv, b"context"); assert!(v); # Ok(()) @@ -50,15 +60,16 @@ desired [security parameter](#modules) below. ## Notes -* This crate is fully functional and corresponds to the final/released FIPS 205, including - the pre-hash variants which formalize methods for signing a hash of the message instead 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. +* This crate is fully functional and corresponds to the final/released FIPS 205 (August 13, 2024), + including the pre-hash variants which formalize methods for signing a hash of the message instead + of the message itself (along with metadata about the hasher used). +* 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`. * 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`. -* 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 diff --git a/benches/README.md b/benches/README.md index 547f772..c4b605a 100644 --- a/benches/README.md +++ b/benches/README.md @@ -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 -// Intel® Core™ i7-7700K CPU @ 4.20GHz × 8 +October 3, 2024 +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_192f keygen time: [2.6420 ms 2.6425 ms 2.6432 ms] -sha2_256f keygen time: [6.9202 ms 6.9300 ms 6.9481 ms] -shake_128f keygen time: [2.9981 ms 3.0007 ms 3.0050 ms] -shake_192f keygen time: [4.2558 ms 4.2562 ms 4.2568 ms] -shake_256f keygen time: [11.221 ms 11.236 ms 11.266 ms] -sha2_128s keygen time: [115.98 ms 116.09 ms 116.20 ms] -sha2_192s keygen time: [169.23 ms 169.36 ms 169.49 ms] -sha2_256s keygen time: [110.80 ms 110.83 ms 110.86 ms] -shake_128s keygen time: [186.05 ms 186.39 ms 186.98 ms] -shake_192s keygen time: [272.52 ms 272.68 ms 272.86 ms] -shake_256s keygen time: [178.79 ms 178.95 ms 179.17 ms] +sha2_128f keygen time: [1.7823 ms 1.7830 ms 1.7839 ms] +sha2_192f keygen time: [2.6234 ms 2.6256 ms 2.6279 ms] +sha2_256f keygen time: [6.8753 ms 6.8797 ms 6.8858 ms] +shake_128f keygen time: [2.7946 ms 2.7953 ms 2.7961 ms] +shake_192f keygen time: [4.0918 ms 4.0954 ms 4.0993 ms] +shake_256f keygen time: [10.704 ms 10.717 ms 10.739 ms] +sha2_128s keygen time: [113.89 ms 113.90 ms 113.92 ms] +sha2_192s keygen time: [166.62 ms 166.63 ms 166.65 ms] +sha2_256s keygen time: [109.25 ms 109.34 ms 109.43 ms] +shake_128s keygen time: [178.32 ms 178.41 ms 178.52 ms] +shake_192s keygen time: [261.50 ms 261.55 ms 261.63 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_192f sign time: [69.770 ms 69.801 ms 69.859 ms] -sha2_256f sign time: [142.39 ms 142.45 ms 142.54 ms] -shake_128f sign time: [67.953 ms 67.966 ms 67.986 ms] -shake_192f sign time: [109.94 ms 109.95 ms 109.96 ms] -shake_256f sign time: [224.70 ms 224.71 ms 224.73 ms] -sha2_128s sign time: [878.51 ms 878.70 ms 878.91 ms] -sha2_192s sign time: [1.5740 s 1.5750 s 1.5761 s] -sha2_256s sign time: [1.3848 s 1.3855 s 1.3865 s] -shake_128s sign time: [1.4197 s 1.4206 s 1.4216 s] -shake_192s sign time: [2.4545 s 2.4554 s 2.4565 s] -shake_256s sign time: [2.1544 s 2.1550 s 2.1557 s] +sha2_128f sign time: [41.623 ms 41.635 ms 41.654 ms] +sha2_192f sign time: [68.686 ms 68.886 ms 69.138 ms] +sha2_256f sign time: [141.52 ms 141.54 ms 141.56 ms] +shake_128f sign time: [65.349 ms 65.364 ms 65.381 ms] +shake_192f sign time: [106.67 ms 106.68 ms 106.70 ms] +shake_256f sign time: [217.07 ms 217.25 ms 217.45 ms] +sha2_128s sign time: [867.34 ms 868.15 ms 869.14 ms] +sha2_192s sign time: [1.5404 s 1.5414 s 1.5426 s] +sha2_256s sign time: [1.3559 s 1.3563 s 1.3568 s] +shake_128s sign time: [1.3682 s 1.3730 s 1.3788 s] +shake_192s sign time: [2.3982 s 2.4085 s 2.4198 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_192f verify time: [3.7586 ms 3.7621 ms 3.7659 ms] -sha2_256f verify time: [3.8196 ms 3.8216 ms 3.8242 ms] -shake_128f verify time: [4.0462 ms 4.0494 ms 4.0542 ms] -shake_192f verify time: [5.9527 ms 5.9531 ms 5.9536 ms] -shake_256f verify time: [5.9491 ms 5.9501 ms 5.9513 ms] -sha2_128s verify time: [871.01 µs 871.07 µs 871.15 µs] -sha2_192s verify time: [1.2818 ms 1.2832 ms 1.2846 ms] -sha2_256s verify time: [1.8911 ms 1.8925 ms 1.8942 ms] -shake_128s verify time: [1.4506 ms 1.4513 ms 1.4522 ms] -shake_192s verify time: [2.1333 ms 2.1342 ms 2.1355 ms] -shake_256s verify time: [2.8122 ms 2.8139 ms 2.8161 ms] +sha2_128f verify time: [2.5693 ms 2.5735 ms 2.5781 ms] +sha2_192f verify time: [3.8974 ms 3.9857 ms 4.0836 ms] +sha2_256f verify time: [3.8619 ms 3.8760 ms 3.8925 ms] +shake_128f verify time: [3.9791 ms 4.0046 ms 4.0349 ms] +shake_192f verify time: [5.7540 ms 5.7838 ms 5.8202 ms] +shake_256f verify time: [6.1739 ms 6.3059 ms 6.4543 ms] +sha2_128s verify time: [887.76 µs 898.88 µs 912.39 µs] +sha2_192s verify time: [1.3260 ms 1.3372 ms 1.3522 ms] +sha2_256s verify time: [1.9167 ms 1.9321 ms 1.9527 ms] +shake_128s verify time: [1.3037 ms 1.3070 ms 1.3109 ms] +shake_192s verify time: [1.9459 ms 1.9595 ms 1.9802 ms] +shake_256s verify time: [2.8772 ms 2.8875 ms 2.9002 ms] ~~~ \ No newline at end of file diff --git a/benches/benchmark.rs b/benches/benchmark.rs index afbfa3e..5d8586b 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -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_256f, sk_shake_256f) = slh_dsa_shake_256f::KG::try_keygen().unwrap(); - let sig_sha2_128s = sk_sha2_128s - .try_sign(&message, b"context", hedged) - .unwrap(); - let sig_sha2_128f = sk_sha2_128f - .try_sign(&message, b"context", hedged) - .unwrap(); - let sig_sha2_192s = sk_sha2_192s - .try_sign(&message, b"context", hedged) - .unwrap(); - let sig_sha2_192f = sk_sha2_192f - .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(); + let sig_sha2_128s = sk_sha2_128s.try_sign(&message, b"context", hedged).unwrap(); + let sig_sha2_128f = sk_sha2_128f.try_sign(&message, b"context", hedged).unwrap(); + let sig_sha2_192s = sk_sha2_192s.try_sign(&message, b"context", hedged).unwrap(); + let sig_sha2_192f = sk_sha2_192f.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_192f keygen", |b| b.iter(|| slh_dsa_sha2_192f::KG::try_keygen())); diff --git a/rustfmt.toml b/rustfmt.toml index 1996cff..dd03932 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -3,13 +3,13 @@ hard_tabs = false tab_spaces = 4 newline_style = "Auto" indent_style = "Block" -use_small_heuristics = "Default" -fn_call_width = 80 -attr_fn_like_width = 70 +#use_small_heuristics = "Default" +fn_call_width = 90 +attr_fn_like_width = 90 struct_lit_width = 60 struct_variant_width = 60 array_width = 60 -chain_width = 60 +chain_width = 90 single_line_if_else_max_width = 50 single_line_let_else_max_width = 50 wrap_comments = false @@ -70,7 +70,7 @@ color = "Auto" unstable_features = false disable_all_formatting = false skip_children = false -hide_parse_errors = false +show_parse_errors = true error_on_line_overflow = false error_on_unformatted = false ignore = [] diff --git a/src/lib.rs b/src/lib.rs index 3a71887..e34790a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,6 +49,7 @@ // TODO: Roadmap // 1. Additional (external) top-level test vectors, particularly for hash variants (!!) // 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. @@ -102,10 +103,10 @@ macro_rules! functionality { // ----- PRIMARY FUNCTIONS --- /// Generates a public and private key pair specific to this security parameter set.
- /// This function utilizes the OS default random number generator, and makes no (constant) - /// timing assurances. + /// This function utilizes the OS default random number generator, and operates in constant + /// timing. /// # Errors - /// Returns an error when the random number generator fails; propagates internal 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. @@ -116,16 +117,24 @@ macro_rules! functionality { /// /// 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_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)?; + /// // Use the public key to verify the msg signature /// let v = pk2.verify(&msg_recv, &sig_recv, b"context"); /// assert!(v); /// # Ok(()) @@ -144,20 +153,35 @@ macro_rules! functionality { /// ```rust /// use fips205::slh_dsa_shake_128s; // Could use any of the twelve security parameter sets. /// use fips205::traits::{SerDes, Signer, Verifier}; - /// use rand_chacha::rand_core::SeedableRng; /// # use std::error::Error; + /// # use rand_core::OsRng; /// # /// # fn main() -> Result<(), Box> { /// - /// let message = [0u8, 1, 2, 3, 4, 5, 6, 7]; - /// let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123); + /// let msg_bytes = [0u8, 1, 2, 3, 4, 5, 6, 7]; + /// let mut rng = OsRng; /// - /// // Generate key pair and signature - /// let (pk, sk) = slh_dsa_shake_128s::try_keygen_with_rng(&mut rng)?; // Generate both public and secret keys - /// let sig = sk.try_sign(&message, b"context", true)?; // Use the secret key to generate a message signature /// - /// let v = pk.verify(&message, &sig, b"context"); + /// // Generate both public and secret keys. This only fails when the OS 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(())} + /// # Ok(()) + /// # } /// ``` pub fn try_keygen_with_rng( rng: &mut impl CryptoRngCore, @@ -170,6 +194,7 @@ macro_rules! functionality { type PrivateKey = PrivateKey; type PublicKey = PublicKey; + // Documented in traits.rs fn try_keygen_with_rng( rng: &mut impl CryptoRngCore, ) -> Result<(PublicKey, PrivateKey), &'static str> { @@ -182,6 +207,7 @@ macro_rules! functionality { impl Signer for PrivateKey { type Signature = [u8; SIG_LEN]; + // Documented in traits.rs fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, m: &[u8], ctx: &[u8], hedged: bool, ) -> Result<[u8; SIG_LEN], &'static str> { @@ -195,8 +221,8 @@ macro_rules! functionality { sig.map(|s| s.serialize()) } - /// # Errors - fn try_sign_hash_with_rng( + // Documented in traits.rs + fn try_hash_sign_with_rng( &self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph, hedged: bool, ) -> Result { @@ -218,9 +244,7 @@ macro_rules! functionality { sig.map(|s| s.serialize()) } - /// As of Oct 2 2024, the NIST test vectors are applied to the **internal** functions rather than - /// the external API. - /// # Errors + // Documented in traits.rs fn _test_only_raw_sign( &self, rng: &mut impl CryptoRngCore, m: &[u8], hedged: bool, ) -> Result<[u8; SIG_LEN], &'static str> { @@ -229,8 +253,7 @@ macro_rules! functionality { // 4: if (hedged) then ▷ or to a random n-byte string if hedged { // 5: opt_rand ←$ Bn - rng.try_fill_bytes(&mut opt_rand) - .map_err(|_| "Alg17: rng failed")?; + rng.try_fill_bytes(&mut opt_rand).map_err(|_| "Alg17: rng failed")?; // 6: end if } @@ -248,6 +271,7 @@ macro_rules! functionality { impl Verifier for PublicKey { type Signature = [u8; SIG_LEN]; + // Documented in traits.rs fn verify(&self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ctx: &[u8]) -> bool { if ctx.len() > 255 { return false; @@ -260,7 +284,8 @@ macro_rules! functionality { res } - fn verify_hash( + // Documented in traits.rs + fn hash_verify( &self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ctx: &[u8], ph: &Ph, ) -> bool { if ctx.len() > 255 { @@ -269,15 +294,20 @@ macro_rules! functionality { let sig = SlhDsaSig::::deserialize(sig_bytes); 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 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::( &HASHERS, &mp, &sig, &self.0, ); res } - /// As of Oct 2 2024, the NIST test vectors are applied to the **internal** functions rather than - /// the external API. + // Documented in traits.rs fn _test_only_raw_verify( &self, m: &[u8], sig_bytes: &[u8; SIG_LEN], ) -> Result { @@ -298,6 +328,7 @@ macro_rules! functionality { impl SerDes for PublicKey { type ByteArray = [u8; PK_LEN]; + // Documented in traits.rs fn into_bytes(self) -> Self::ByteArray { let mut out = [0u8; PK_LEN]; out[0..(PK_LEN / 2)].copy_from_slice(&self.0.pk_seed); @@ -305,6 +336,7 @@ macro_rules! functionality { out } + // Documented in traits.rs fn try_from_bytes(bytes: &Self::ByteArray) -> Result { // Result: opportunity for validation //let mut pk = SlhPublicKey::default(); @@ -319,6 +351,7 @@ macro_rules! functionality { impl SerDes for PrivateKey { type ByteArray = [u8; SK_LEN]; + // Documented in traits.rs fn into_bytes(self) -> Self::ByteArray { let mut bytes = [0u8; SK_LEN]; bytes[0..(SK_LEN / 4)].copy_from_slice(&self.0.sk_seed); @@ -328,9 +361,8 @@ macro_rules! functionality { bytes } + // Documented in traits.rs fn try_from_bytes(bytes: &Self::ByteArray) -> Result { - // Result: opportunity for validation - //let mut sk = SlhPrivateKey::default(); let mut sk = SlhPrivateKey { sk_seed: [0u8; N], sk_prf: [0u8; N], @@ -338,10 +370,8 @@ macro_rules! functionality { pk_root: [0u8; N], }; sk.sk_seed.copy_from_slice(&bytes[0..(SK_LEN / 4)]); - sk.sk_prf - .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.sk_prf.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_root.copy_from_slice(&bytes[(3 * SK_LEN / 4)..]); Ok(PrivateKey(sk)) } @@ -364,20 +394,18 @@ macro_rules! functionality { let pk2 = PublicKey::try_from_bytes(&pk1_bytes).unwrap(); let sk2 = PrivateKey::try_from_bytes(&sk1_bytes).unwrap(); - let sig = sk2 - .try_sign_with_rng(&mut rng, &message, b"context", true) - .unwrap(); + let sig = sk2.try_sign_with_rng(&mut rng, &message, b"context", true).unwrap(); let result = pk2.verify(&message, &sig, b"context"); assert!(result, "Signature failed to verify"); let result = pk2.verify(&message, &sig, b"some other context"); assert!(!result, "Signature should not have verified"); for ph in [Ph::SHA256, Ph::SHA512, Ph::SHAKE128, Ph::SHAKE256] { 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(); - 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"); - 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"); } } diff --git a/src/slh.rs b/src/slh.rs index 9581664..1711f55 100644 --- a/src/slh.rs +++ b/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 let mut sk_seed = [0u8; N]; - rng.try_fill_bytes(&mut sk_seed) - .map_err(|_| "Alg17: rng failed1")?; + rng.try_fill_bytes(&mut sk_seed).map_err(|_| "Alg17: rng failed1")?; // 2: SK.prf ←$ B^n ▷ strings using an approved random bit generator let mut sk_prf = [0u8; N]; - rng.try_fill_bytes(&mut sk_prf) - .map_err(|_| "Alg17: rng failed2")?; + rng.try_fill_bytes(&mut sk_prf).map_err(|_| "Alg17: rng failed2")?; // 3: PK.seed ←$ B^n let mut pk_seed = [0u8; N]; - rng.try_fill_bytes(&mut pk_seed) - .map_err(|_| "Alg17: rng failed3")?; + rng.try_fill_bytes(&mut pk_seed).map_err(|_| "Alg17: rng failed3")?; // 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 // 3: end if // 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 let mut opt_rand = sk.pk_seed; @@ -117,8 +114,7 @@ pub(crate) fn slh_sign_with_rng< // 6: return ⊥ if hedged { // - rng.try_fill_bytes(&mut opt_rand) - .map_err(|_| "Alg17: rng failed")?; + rng.try_fill_bytes(&mut opt_rand).map_err(|_| "Alg17: rng failed")?; // 7: end if } @@ -260,7 +256,7 @@ pub(crate) fn slh_verify< // 2: return false // 3: end if // 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) ∥ 𝑐𝑡𝑥 ∥ 𝑀 diff --git a/src/traits.rs b/src/traits.rs index 43b68e8..14f0fa3 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -5,11 +5,412 @@ use crate::Ph; 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> { + /// + /// 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> { + /// + /// 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> { + /// + /// 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.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> { + /// + /// 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.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> { + /// + /// 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; + + + /// 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> { + /// + /// 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; + + + /// 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; +} + + +/// 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> { + /// + /// 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> { + /// + /// 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; +} + + /// The `SerDes` trait provides for validated serialization and deserialization of fixed size elements pub trait SerDes { /// The fixed-size byte array to be serialized or deserialized type ByteArray; + /// Produces a byte array of fixed-size specific to the struct being serialized. /// # Examples /// ```rust @@ -38,6 +439,7 @@ pub trait SerDes { /// ``` fn into_bytes(self) -> Self::ByteArray; + /// Consumes a byte array of fixed-size specific to the struct being deserialized; performs validation /// # Errors /// Returns an error on malformed input. @@ -70,240 +472,3 @@ pub trait SerDes { where 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.
- /// 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> { - /// - /// 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.
- /// 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> { - /// - /// 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> { - /// - /// 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.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.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> { - /// - /// 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; - - - /// blah - /// # Errors - fn try_sign_hash_with_rng( - &self, rng: &mut impl CryptoRngCore, message: &[u8], ctx: &[u8], ph: &Ph, hedged: bool, - ) -> Result; - - - /// 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; -} - - -/// 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> { - /// - /// 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; -} diff --git a/tests/nist_acvp_vectors/mod.rs b/tests/nist_acvp_vectors/mod.rs index e980cb3..664641c 100644 --- a/tests/nist_acvp_vectors/mod.rs +++ b/tests/nist_acvp_vectors/mod.rs @@ -190,10 +190,7 @@ macro_rules! test_sign { // Load private key let sk = PrivateKey::try_from_bytes( - test.sk - .as_slice() - .try_into() - .expect("Wrong length private key"), + test.sk.as_slice().try_into().expect("Wrong length private key"), ) .expect("Unable to load private key"); @@ -319,20 +316,14 @@ macro_rules! test_verify { let is_valid: Result = panic::catch_unwind(|| { // Load public key let pk = PublicKey::try_from_bytes( - test.pk - .as_slice() - .try_into() - .expect("Wrong length public key"), + test.pk.as_slice().try_into().expect("Wrong length public key"), ) .expect("Unable to load public key"); // Verify signature pk._test_only_raw_verify( test.message.as_slice(), - test.signature - .as_slice() - .try_into() - .expect("Signature length incorrect"), + test.signature.as_slice().try_into().expect("Signature length incorrect"), ) .expect("Verification failed") });