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
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
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 <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];
// 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_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

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

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

View file

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

View file

@ -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. <br>
/// 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<dyn Error>> {
///
/// 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<Self::Signature, &'static str> {
@ -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::<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 (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>(
&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<bool, &'static str> {
@ -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<Self, &'static str> {
// 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<Self, &'static str> {
// 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");
}
}

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

View file

@ -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<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
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. <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
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<bool, _> = 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")
});