#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]
#![allow(clippy::indexing_slicing)]
use fips205::slh_dsa_shake_128s as shake_128s;
use fips205::slh_dsa_shake_192s as shake_192s;
use fips205::slh_dsa_shake_256s as shake_256s;
use fips205::traits::{SerDes, Signer, Verifier};
use subtle::{Choice, ConstantTimeEq};
use tracing::instrument;
use zeroize::{Zeroize, Zeroizing};
use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum SlhDsaError {
#[error("Random number generation failed")]
RngError,
#[error("Invalid public key")]
InvalidPublicKey,
#[error("Invalid secret key")]
InvalidSecretKey,
#[error("Signature verification failed")]
VerificationFailed,
#[error("Deserialization failed")]
DeserializationError,
#[error("Context string too long (max 255 bytes)")]
ContextTooLong,
#[error("Message exceeds signature resource limit")]
MessageTooLong,
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum SlhDsaSecurityLevel {
Shake128s = 1,
Shake192s = 2,
Shake256s = 3,
}
impl SlhDsaSecurityLevel {
#[must_use]
pub const fn nist_level(&self) -> u8 {
match self {
SlhDsaSecurityLevel::Shake128s => 1,
SlhDsaSecurityLevel::Shake192s => 3,
SlhDsaSecurityLevel::Shake256s => 5,
}
}
#[must_use]
pub const fn public_key_size(&self) -> usize {
match self {
SlhDsaSecurityLevel::Shake128s => shake_128s::PK_LEN,
SlhDsaSecurityLevel::Shake192s => shake_192s::PK_LEN,
SlhDsaSecurityLevel::Shake256s => shake_256s::PK_LEN,
}
}
#[must_use]
pub const fn secret_key_size(&self) -> usize {
match self {
SlhDsaSecurityLevel::Shake128s => shake_128s::SK_LEN,
SlhDsaSecurityLevel::Shake192s => shake_192s::SK_LEN,
SlhDsaSecurityLevel::Shake256s => shake_256s::SK_LEN,
}
}
#[must_use]
pub const fn signature_size(&self) -> usize {
match self {
SlhDsaSecurityLevel::Shake128s => shake_128s::SIG_LEN,
SlhDsaSecurityLevel::Shake192s => shake_192s::SIG_LEN,
SlhDsaSecurityLevel::Shake256s => shake_256s::SIG_LEN,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VerifyingKey {
security_level: SlhDsaSecurityLevel,
bytes: [u8; shake_256s::PK_LEN],
len: usize,
}
impl VerifyingKey {
pub fn new(security_level: SlhDsaSecurityLevel, bytes: &[u8]) -> Result<Self, SlhDsaError> {
let expected_len = security_level.public_key_size();
if bytes.len() != expected_len {
return Err(SlhDsaError::InvalidPublicKey);
}
let mut key_bytes = [0u8; shake_256s::PK_LEN];
key_bytes[..expected_len].copy_from_slice(bytes);
match security_level {
SlhDsaSecurityLevel::Shake128s => {
let pk_bytes: [u8; shake_128s::PK_LEN] =
bytes.try_into().map_err(|_e| SlhDsaError::DeserializationError)?;
shake_128s::PublicKey::try_from_bytes(&pk_bytes)
.map_err(|_e| SlhDsaError::InvalidPublicKey)?;
}
SlhDsaSecurityLevel::Shake192s => {
let pk_bytes: [u8; shake_192s::PK_LEN] =
bytes.try_into().map_err(|_e| SlhDsaError::DeserializationError)?;
shake_192s::PublicKey::try_from_bytes(&pk_bytes)
.map_err(|_e| SlhDsaError::InvalidPublicKey)?;
}
SlhDsaSecurityLevel::Shake256s => {
let pk_bytes: [u8; shake_256s::PK_LEN] =
bytes.try_into().map_err(|_e| SlhDsaError::DeserializationError)?;
shake_256s::PublicKey::try_from_bytes(&pk_bytes)
.map_err(|_e| SlhDsaError::InvalidPublicKey)?;
}
}
Ok(VerifyingKey { security_level, bytes: key_bytes, len: expected_len })
}
#[must_use]
pub fn security_level(&self) -> SlhDsaSecurityLevel {
self.security_level
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
pub fn from_bytes(
bytes: &[u8],
security_level: SlhDsaSecurityLevel,
) -> Result<Self, SlhDsaError> {
Self::new(security_level, bytes)
}
#[instrument(level = "debug", skip(self, message, signature, context), fields(security_level = ?self.security_level, message_len = message.len(), signature_len = signature.len(), context_len = context.len()))]
pub fn verify(
&self,
message: &[u8],
signature: &[u8],
context: &[u8],
) -> Result<bool, SlhDsaError> {
let ctx = context;
if ctx.len() > 255 {
return Err(SlhDsaError::ContextTooLong);
}
let is_valid = match self.security_level {
SlhDsaSecurityLevel::Shake128s => {
let pk_bytes: [u8; shake_128s::PK_LEN] =
self.as_bytes().try_into().map_err(|_e| SlhDsaError::InvalidPublicKey)?;
let pk = shake_128s::PublicKey::try_from_bytes(&pk_bytes)
.map_err(|_e| SlhDsaError::InvalidPublicKey)?;
let sig_bytes: [u8; shake_128s::SIG_LEN] =
signature.try_into().map_err(|_e| SlhDsaError::VerificationFailed)?;
pk.verify(message, &sig_bytes, ctx)
}
SlhDsaSecurityLevel::Shake192s => {
let pk_bytes: [u8; shake_192s::PK_LEN] =
self.as_bytes().try_into().map_err(|_e| SlhDsaError::InvalidPublicKey)?;
let pk = shake_192s::PublicKey::try_from_bytes(&pk_bytes)
.map_err(|_e| SlhDsaError::InvalidPublicKey)?;
let sig_bytes: [u8; shake_192s::SIG_LEN] =
signature.try_into().map_err(|_e| SlhDsaError::VerificationFailed)?;
pk.verify(message, &sig_bytes, ctx)
}
SlhDsaSecurityLevel::Shake256s => {
let pk_bytes: [u8; shake_256s::PK_LEN] =
self.as_bytes().try_into().map_err(|_e| SlhDsaError::InvalidPublicKey)?;
let pk = shake_256s::PublicKey::try_from_bytes(&pk_bytes)
.map_err(|_e| SlhDsaError::InvalidPublicKey)?;
let sig_bytes: [u8; shake_256s::SIG_LEN] =
signature.try_into().map_err(|_e| SlhDsaError::VerificationFailed)?;
pk.verify(message, &sig_bytes, ctx)
}
};
Ok(is_valid)
}
}
pub struct SigningKey {
security_level: SlhDsaSecurityLevel,
bytes: [u8; shake_256s::SK_LEN],
len: usize,
verifying_key: VerifyingKey,
}
impl ConstantTimeEq for SigningKey {
fn ct_eq(&self, other: &Self) -> Choice {
let level_eq = (self.security_level as u8).ct_eq(&(other.security_level as u8));
let len_eq = self.len.ct_eq(&other.len);
let bytes_eq = self.bytes[..self.len].ct_eq(&other.bytes[..other.len]);
level_eq & len_eq & bytes_eq
}
}
impl PartialEq for SigningKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
impl Eq for SigningKey {}
impl SigningKey {
#[instrument(level = "debug", fields(security_level = ?security_level, nist_level = security_level.nist_level()))]
pub fn generate(
security_level: SlhDsaSecurityLevel,
) -> Result<(Self, VerifyingKey), SlhDsaError> {
let (signing_key, verifying_key) = match security_level {
SlhDsaSecurityLevel::Shake128s => {
let (pk, sk) = shake_128s::try_keygen().map_err(|_e| SlhDsaError::RngError)?;
let verifying_key = VerifyingKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::PK_LEN];
let pk_bytes = pk.into_bytes();
b[..pk_bytes.len()].copy_from_slice(&pk_bytes);
b
},
len: shake_128s::PK_LEN,
};
let signing_key = SigningKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::SK_LEN];
let sk_bytes = sk.into_bytes();
b[..sk_bytes.len()].copy_from_slice(&sk_bytes);
b
},
len: shake_128s::SK_LEN,
verifying_key: verifying_key.clone(),
};
(signing_key, verifying_key)
}
SlhDsaSecurityLevel::Shake192s => {
let (pk, sk) = shake_192s::try_keygen().map_err(|_e| SlhDsaError::RngError)?;
let verifying_key = VerifyingKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::PK_LEN];
let pk_bytes = pk.into_bytes();
b[..pk_bytes.len()].copy_from_slice(&pk_bytes);
b
},
len: shake_192s::PK_LEN,
};
let signing_key = SigningKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::SK_LEN];
let sk_bytes = sk.into_bytes();
b[..sk_bytes.len()].copy_from_slice(&sk_bytes);
b
},
len: shake_192s::SK_LEN,
verifying_key: verifying_key.clone(),
};
(signing_key, verifying_key)
}
SlhDsaSecurityLevel::Shake256s => {
let (pk, sk) = shake_256s::try_keygen().map_err(|_e| SlhDsaError::RngError)?;
let verifying_key = VerifyingKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::PK_LEN];
let pk_bytes = pk.into_bytes();
b[..pk_bytes.len()].copy_from_slice(&pk_bytes);
b
},
len: shake_256s::PK_LEN,
};
let signing_key = SigningKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::SK_LEN];
let sk_bytes = sk.into_bytes();
b[..sk_bytes.len()].copy_from_slice(&sk_bytes);
b
},
len: shake_256s::SK_LEN,
verifying_key: verifying_key.clone(),
};
(signing_key, verifying_key)
}
};
crate::primitives::pct::pct_slh_dsa(&verifying_key, &signing_key)
.map_err(|_e| SlhDsaError::RngError)?;
Ok((signing_key, verifying_key))
}
pub fn new(security_level: SlhDsaSecurityLevel, bytes: &[u8]) -> Result<Self, SlhDsaError> {
let expected_len = security_level.secret_key_size();
if bytes.len() != expected_len {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut key_bytes = Zeroizing::new([0u8; shake_256s::SK_LEN]);
key_bytes[..expected_len].copy_from_slice(bytes);
match security_level {
SlhDsaSecurityLevel::Shake128s => {
if bytes.len() != shake_128s::SK_LEN {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut sk_bytes: Zeroizing<[u8; shake_128s::SK_LEN]> =
Zeroizing::new([0u8; shake_128s::SK_LEN]);
sk_bytes.copy_from_slice(bytes);
let sk = shake_128s::PrivateKey::try_from_bytes(&sk_bytes)
.map_err(|_e| SlhDsaError::InvalidSecretKey)?;
let pk_bytes = sk.get_public_key().into_bytes();
let verifying_key = VerifyingKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::PK_LEN];
b[..pk_bytes.len()].copy_from_slice(&pk_bytes);
b
},
len: shake_128s::PK_LEN,
};
Ok(SigningKey {
security_level,
bytes: *key_bytes,
len: expected_len,
verifying_key,
})
}
SlhDsaSecurityLevel::Shake192s => {
if bytes.len() != shake_192s::SK_LEN {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut sk_bytes: Zeroizing<[u8; shake_192s::SK_LEN]> =
Zeroizing::new([0u8; shake_192s::SK_LEN]);
sk_bytes.copy_from_slice(bytes);
let sk = shake_192s::PrivateKey::try_from_bytes(&sk_bytes)
.map_err(|_e| SlhDsaError::InvalidSecretKey)?;
let pk_bytes = sk.get_public_key().into_bytes();
let verifying_key = VerifyingKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::PK_LEN];
b[..pk_bytes.len()].copy_from_slice(&pk_bytes);
b
},
len: shake_192s::PK_LEN,
};
Ok(SigningKey {
security_level,
bytes: *key_bytes,
len: expected_len,
verifying_key,
})
}
SlhDsaSecurityLevel::Shake256s => {
if bytes.len() != shake_256s::SK_LEN {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut sk_bytes: Zeroizing<[u8; shake_256s::SK_LEN]> =
Zeroizing::new([0u8; shake_256s::SK_LEN]);
sk_bytes.copy_from_slice(bytes);
let sk = shake_256s::PrivateKey::try_from_bytes(&sk_bytes)
.map_err(|_e| SlhDsaError::InvalidSecretKey)?;
let pk_bytes = sk.get_public_key().into_bytes();
let verifying_key = VerifyingKey {
security_level,
bytes: {
let mut b = [0u8; shake_256s::PK_LEN];
b[..pk_bytes.len()].copy_from_slice(&pk_bytes);
b
},
len: shake_256s::PK_LEN,
};
Ok(SigningKey {
security_level,
bytes: *key_bytes,
len: expected_len,
verifying_key,
})
}
}
}
#[must_use]
pub fn security_level(&self) -> SlhDsaSecurityLevel {
self.security_level
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len]
}
#[must_use]
pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
Zeroizing::new(self.as_bytes().to_vec())
}
pub fn from_bytes(
bytes: &[u8],
security_level: SlhDsaSecurityLevel,
) -> Result<Self, SlhDsaError> {
Self::new(security_level, bytes)
}
#[must_use]
pub fn verifying_key(&self) -> &VerifyingKey {
&self.verifying_key
}
#[instrument(level = "debug", skip(self, message, context), fields(security_level = ?self.security_level, message_len = message.len(), context_len = context.len()))]
pub fn sign(&self, message: &[u8], context: &[u8]) -> Result<Vec<u8>, SlhDsaError> {
crate::primitives::resource_limits::validate_signature_size(message.len())
.map_err(|_e| SlhDsaError::MessageTooLong)?;
let ctx = context;
if ctx.len() > 255 {
return Err(SlhDsaError::ContextTooLong);
}
match self.security_level {
SlhDsaSecurityLevel::Shake128s => {
if self.as_bytes().len() != shake_128s::SK_LEN {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut sk_bytes: Zeroizing<[u8; shake_128s::SK_LEN]> =
Zeroizing::new([0u8; shake_128s::SK_LEN]);
sk_bytes.copy_from_slice(self.as_bytes());
let sk = shake_128s::PrivateKey::try_from_bytes(&sk_bytes)
.map_err(|_e| SlhDsaError::InvalidSecretKey)?;
let sig = sk.try_sign(message, ctx, true).map_err(|_e| SlhDsaError::RngError)?;
Ok(sig.as_ref().to_vec())
}
SlhDsaSecurityLevel::Shake192s => {
if self.as_bytes().len() != shake_192s::SK_LEN {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut sk_bytes: Zeroizing<[u8; shake_192s::SK_LEN]> =
Zeroizing::new([0u8; shake_192s::SK_LEN]);
sk_bytes.copy_from_slice(self.as_bytes());
let sk = shake_192s::PrivateKey::try_from_bytes(&sk_bytes)
.map_err(|_e| SlhDsaError::InvalidSecretKey)?;
let sig = sk.try_sign(message, ctx, true).map_err(|_e| SlhDsaError::RngError)?;
Ok(sig.as_ref().to_vec())
}
SlhDsaSecurityLevel::Shake256s => {
if self.as_bytes().len() != shake_256s::SK_LEN {
return Err(SlhDsaError::InvalidSecretKey);
}
let mut sk_bytes: Zeroizing<[u8; shake_256s::SK_LEN]> =
Zeroizing::new([0u8; shake_256s::SK_LEN]);
sk_bytes.copy_from_slice(self.as_bytes());
let sk = shake_256s::PrivateKey::try_from_bytes(&sk_bytes)
.map_err(|_e| SlhDsaError::InvalidSecretKey)?;
let sig = sk.try_sign(message, ctx, true).map_err(|_e| SlhDsaError::RngError)?;
Ok(sig.as_ref().to_vec())
}
}
}
pub fn sign_with_key(
&self,
message: &[u8],
context: &[u8],
) -> Result<(Vec<u8>, &VerifyingKey), SlhDsaError> {
let signature = self.sign(message, context)?;
Ok((signature, &self.verifying_key))
}
}
impl Drop for SigningKey {
fn drop(&mut self) {
self.bytes.zeroize();
}
}
impl Zeroize for SigningKey {
fn zeroize(&mut self) {
self.bytes.zeroize();
}
}
impl std::fmt::Debug for SigningKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SigningKey")
.field("security_level", &self.security_level)
.field("bytes", &"[REDACTED]")
.field("verifying_key", &"[PUBLIC KEY]")
.finish()
}
}
#[cfg(test)]
#[allow(clippy::expect_used)] #[allow(clippy::explicit_iter_loop)] #[allow(clippy::redundant_clone)] #[allow(clippy::indexing_slicing)]
#[allow(clippy::unnecessary_cast)]
mod tests {
use super::*;
#[test]
fn test_slh_dsa_key_generation_all_levels_succeeds() {
for level in [
SlhDsaSecurityLevel::Shake128s,
SlhDsaSecurityLevel::Shake192s,
SlhDsaSecurityLevel::Shake256s,
] {
let (sk, pk) = SigningKey::generate(level).expect("Key generation failed");
assert_eq!(sk.security_level(), level);
assert_eq!(pk.security_level(), level);
assert_eq!(
pk.as_bytes().len(),
level.public_key_size(),
"Public key size mismatch for {:?}",
level
);
assert_eq!(
sk.as_bytes().len(),
level.secret_key_size(),
"Secret key size mismatch for {:?}",
level
);
}
}
#[test]
fn test_sign_verify_roundtrip() {
for level in [
SlhDsaSecurityLevel::Shake128s,
SlhDsaSecurityLevel::Shake192s,
SlhDsaSecurityLevel::Shake256s,
] {
let (sk, pk) = SigningKey::generate(level).expect("Key generation failed");
let message = b"Test message for SLH-DSA";
let signature = sk.sign(message, &[]).expect("Signing failed");
assert_eq!(
signature.len(),
level.signature_size(),
"Signature size mismatch for {:?}",
level
);
let is_valid = pk.verify(message, &signature, &[]).expect("Verification failed");
assert!(is_valid, "Signature verification failed for {:?}", level);
}
}
#[test]
fn test_verify_invalid_signature_fails() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = b"Test message";
let mut signature = sk.sign(message, &[]).expect("Signing failed");
signature[0] ^= 0xFF;
let is_valid = pk.verify(message, &signature, &[]).expect("Verification failed");
assert!(!is_valid, "Verification should fail for corrupted signature");
}
#[test]
fn test_verify_wrong_message_fails() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = b"Test message";
let wrong_message = b"Wrong message";
let signature = sk.sign(message, &[]).expect("Signing failed");
let is_valid = pk.verify(wrong_message, &signature, &[]).expect("Verification failed");
assert!(!is_valid, "Verification should fail for wrong message");
}
#[test]
fn test_slh_dsa_serialization_roundtrip() {
for level in [
SlhDsaSecurityLevel::Shake128s,
SlhDsaSecurityLevel::Shake192s,
SlhDsaSecurityLevel::Shake256s,
] {
let (sk, pk) = SigningKey::generate(level).expect("Key generation failed");
let pk_bytes = pk.to_bytes();
let pk_restored = VerifyingKey::from_bytes(&pk_bytes, level)
.expect("Public key deserialization failed");
assert_eq!(pk, pk_restored);
let sk_bytes = sk.to_bytes();
let sk_restored = SigningKey::from_bytes(&sk_bytes, level)
.expect("Secret key deserialization failed");
assert_eq!(sk.security_level(), sk_restored.security_level());
assert_eq!(sk.as_bytes(), sk_restored.as_bytes());
let message = b"Test message";
let signature = sk_restored.sign(message, &[]).expect("Signing failed");
let is_valid =
pk_restored.verify(message, &signature, &[]).expect("Verification failed");
assert!(is_valid, "Signature verification failed after deserialization");
}
}
#[test]
fn test_invalid_key_handling_returns_error() {
let result = VerifyingKey::new(SlhDsaSecurityLevel::Shake128s, &[0u8; 16]);
assert!(matches!(result, Err(SlhDsaError::InvalidPublicKey)));
let result = SigningKey::new(SlhDsaSecurityLevel::Shake128s, &[0u8; 16]);
assert!(matches!(result, Err(SlhDsaError::InvalidSecretKey)));
}
#[test]
fn test_slh_dsa_context_string_sign_verify_roundtrip() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = b"Test message";
let context = b"Test context";
let signature = sk.sign(message, context).expect("Signing with context failed");
let is_valid = pk.verify(message, &signature, context).expect("Verification failed");
assert!(is_valid, "Signature verification failed with context");
let is_valid =
pk.verify(message, &signature, b"Wrong context").expect("Verification failed");
assert!(!is_valid, "Verification should fail with wrong context");
}
#[test]
fn test_context_too_long_returns_error() {
let (sk, _) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = b"Test message";
let long_context = vec![0u8; 256];
let result = sk.sign(message, &long_context);
assert!(matches!(result, Err(SlhDsaError::ContextTooLong)));
}
#[test]
fn test_slh_dsa_empty_message_sign_verify_roundtrip() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = b"";
let signature = sk.sign(message, &[]).expect("Signing empty message failed");
let is_valid = pk.verify(message, &signature, &[]).expect("Verification failed");
assert!(is_valid, "Signature verification failed for empty message");
}
#[test]
fn test_slh_dsa_large_message_sign_verify_roundtrip() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = vec![0u8; 65536];
let signature = sk.sign(&message, &[]).expect("Signing large message failed");
let is_valid = pk.verify(&message, &signature, &[]).expect("Verification failed");
assert!(is_valid, "Signature verification failed for large message");
}
#[test]
fn test_slh_dsa_multiple_signatures_all_verify_succeeds() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
for i in 0..10 {
let message = format!("Test message {}", i).as_bytes().to_vec();
let signature = sk.sign(&message, &[]).expect("Signing failed");
let is_valid = pk.verify(&message, &signature, &[]).expect("Verification failed");
assert!(is_valid, "Signature verification failed for message {}", i);
}
}
#[test]
fn test_slh_dsa_security_level_constants_match_spec_succeeds() {
assert_eq!(SlhDsaSecurityLevel::Shake128s.nist_level(), 1);
assert_eq!(SlhDsaSecurityLevel::Shake192s.nist_level(), 3);
assert_eq!(SlhDsaSecurityLevel::Shake256s.nist_level(), 5);
assert_eq!(SlhDsaSecurityLevel::Shake128s.public_key_size(), shake_128s::PK_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake128s.secret_key_size(), shake_128s::SK_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake128s.signature_size(), shake_128s::SIG_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake192s.public_key_size(), shake_192s::PK_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake192s.secret_key_size(), shake_192s::SK_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake192s.signature_size(), shake_192s::SIG_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake256s.public_key_size(), shake_256s::PK_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake256s.secret_key_size(), shake_256s::SK_LEN);
assert_eq!(SlhDsaSecurityLevel::Shake256s.signature_size(), shake_256s::SIG_LEN);
}
#[test]
fn test_slh_dsa_secret_key_zeroization_succeeds() {
let (mut sk, _pk) = SigningKey::generate(SlhDsaSecurityLevel::Shake128s)
.expect("Key generation should succeed");
let sk_bytes_before = sk.as_bytes().to_vec();
assert!(
!sk_bytes_before.iter().all(|&b| b == 0),
"Secret key should contain non-zero data"
);
sk.zeroize();
let sk_bytes_after = sk.as_bytes();
assert!(sk_bytes_after.iter().all(|&b| b == 0), "Secret key should be zeroized");
}
#[test]
fn test_slh_dsa_all_security_levels_zeroization_succeeds() {
let levels = [
SlhDsaSecurityLevel::Shake128s,
SlhDsaSecurityLevel::Shake192s,
SlhDsaSecurityLevel::Shake256s,
];
for level in levels.iter() {
let (mut sk, _pk) =
SigningKey::generate(*level).expect("Key generation should succeed");
let sk_bytes_before = sk.as_bytes().to_vec();
assert!(
!sk_bytes_before.iter().all(|&b| b == 0),
"Secret key for {:?} should contain non-zero data",
level
);
sk.zeroize();
let sk_bytes_after = sk.as_bytes();
assert!(
sk_bytes_after.iter().all(|&b| b == 0),
"Secret key for {:?} should be zeroized",
level
);
}
}
#[test]
fn test_slh_dsa_signing_after_zeroization_succeeds() {
let (mut sk, pk) = SigningKey::generate(SlhDsaSecurityLevel::Shake128s)
.expect("Key generation should succeed");
let message = b"Test message";
let signature_before = sk.sign(message, &[]).expect("Signing should succeed");
let is_valid_before =
pk.verify(message, &signature_before, &[]).expect("Verification should succeed");
assert!(is_valid_before, "Signature should be valid before zeroization");
sk.zeroize();
let result = sk.sign(message, &[]);
assert!(result.is_err(), "Signing should fail after zeroization");
}
#[test]
fn test_slh_dsa_verify_returns_ok_result_succeeds() {
let (sk, pk) =
SigningKey::generate(SlhDsaSecurityLevel::Shake128s).expect("Key generation failed");
let message = b"Test message";
let signature = sk.sign(message, &[]).expect("Signing failed");
let result = pk.verify(message, &signature, &[]);
assert!(matches!(result, Ok(true)));
let mut invalid_sig = signature.clone();
invalid_sig[0] ^= 0xFF;
let result = pk.verify(message, &invalid_sig, &[]);
assert!(matches!(result, Ok(false)));
}
}