docs, bench stats

This commit is contained in:
eschorn1 2024-10-03 14:48:53 -05:00
parent 49bebd533e
commit 5f8a96c02b
8 changed files with 564 additions and 395 deletions

View file

@ -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
let sig_bytes = sk.try_sign(&msg_bytes, b"context", true)?; // Use the secret key to generate 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(())
@ -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

View file

@ -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]
~~~ ~~~

View file

@ -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()));

View file

@ -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 = []

View file

@ -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");
} }
} }

View file

@ -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) ∥ 𝑐𝑡𝑥𝑀

View file

@ -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>;
}

View file

@ -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")
}); });