use crate::{
log_crypto_operation_complete, log_crypto_operation_error, log_crypto_operation_start,
};
use tracing::{debug, warn};
use crate::primitives::sig::{
fndsa::{
FnDsaSecurityLevel, Signature as FnDsaSignature, SigningKey as FnDsaSigningKey,
VerifyingKey as FnDsaVerifyingKey,
},
ml_dsa::{MlDsaParameterSet, MlDsaPublicKey, MlDsaSecretKey, MlDsaSignature},
slh_dsa::{
SigningKey as SlhDsaSigningKey, SlhDsaSecurityLevel, VerifyingKey as SlhDsaVerifyingKey,
},
};
use crate::types::types::SecurityLevel;
use crate::unified_api::CoreConfig;
use crate::unified_api::error::{CoreError, Result};
use crate::unified_api::zero_trust::SecurityMode;
use crate::primitives::resource_limits::validate_signature_size;
#[allow(deprecated)] fn expected_ml_dsa_params(security_level: SecurityLevel) -> MlDsaParameterSet {
match security_level {
SecurityLevel::Standard => MlDsaParameterSet::MlDsa44,
SecurityLevel::High => MlDsaParameterSet::MlDsa65,
SecurityLevel::Maximum | SecurityLevel::Quantum => MlDsaParameterSet::MlDsa87,
}
}
fn check_ml_dsa_config_consistency(explicit: MlDsaParameterSet, config: &CoreConfig) {
let expected = expected_ml_dsa_params(config.security_level);
if expected != explicit {
warn!(
"Explicit MlDsaParameterSet ({:?}) differs from CoreConfig security_level ({:?} \
→ {:?}). Using explicit parameter.",
explicit, config.security_level, expected
);
}
}
#[allow(deprecated)] fn expected_slh_dsa_level(security_level: SecurityLevel) -> SlhDsaSecurityLevel {
match security_level {
SecurityLevel::Standard => SlhDsaSecurityLevel::Shake128s,
SecurityLevel::High => SlhDsaSecurityLevel::Shake192s,
SecurityLevel::Maximum | SecurityLevel::Quantum => SlhDsaSecurityLevel::Shake256s,
}
}
fn check_slh_dsa_config_consistency(explicit: SlhDsaSecurityLevel, config: &CoreConfig) {
let expected = expected_slh_dsa_level(config.security_level);
if expected != explicit {
warn!(
"Explicit SlhDsaSecurityLevel ({:?}) differs from CoreConfig security_level ({:?} \
→ {:?}). Using explicit parameter.",
explicit, config.security_level, expected
);
}
}
fn sign_pq_ml_dsa_internal(
message: &[u8],
ml_dsa_sk: &[u8],
parameter_set: MlDsaParameterSet,
) -> Result<Vec<u8>> {
log_crypto_operation_start!("ml_dsa_sign", algorithm = ?parameter_set, message_len = message.len());
validate_signature_size(message.len()).map_err(|e| {
log_crypto_operation_error!("ml_dsa_sign", e);
CoreError::ResourceExceeded(e.to_string())
})?;
let sk = MlDsaSecretKey::new(parameter_set, ml_dsa_sk.to_vec()).map_err(|e| {
log_crypto_operation_error!("ml_dsa_sign", e);
CoreError::InvalidInput("Invalid ML-DSA private key format".to_string())
})?;
let signature = crate::primitives::sig::ml_dsa::sign(&sk, message, &[]).map_err(|e| {
log_crypto_operation_error!("ml_dsa_sign", e);
CoreError::SignatureFailed(format!("ML-DSA signing failed: {}", e))
})?;
let sig_bytes = signature.as_bytes().to_vec();
log_crypto_operation_complete!("ml_dsa_sign", algorithm = ?parameter_set, signature_len = sig_bytes.len());
debug!(algorithm = ?parameter_set, "Created ML-DSA signature");
Ok(sig_bytes)
}
fn verify_pq_ml_dsa_internal(
message: &[u8],
signature: &[u8],
ml_dsa_pk: &[u8],
parameter_set: MlDsaParameterSet,
) -> Result<bool> {
log_crypto_operation_start!("ml_dsa_verify", algorithm = ?parameter_set, message_len = message.len());
validate_signature_size(message.len()).map_err(|e| {
log_crypto_operation_error!("ml_dsa_verify", e);
CoreError::ResourceExceeded(e.to_string())
})?;
let pk = MlDsaPublicKey::new(parameter_set, ml_dsa_pk.to_vec()).map_err(|e| {
log_crypto_operation_error!("ml_dsa_verify", e);
CoreError::InvalidInput("Invalid ML-DSA public key format".to_string())
})?;
let sig = MlDsaSignature::new(parameter_set, signature.to_vec()).map_err(|e| {
log_crypto_operation_error!("ml_dsa_verify", e);
CoreError::InvalidInput(format!("Invalid ML-DSA signature: {}", e))
})?;
let result = match crate::primitives::sig::ml_dsa::verify(&pk, message, &sig, &[]) {
Ok(true) => Ok(true),
Ok(false) => Err(CoreError::VerificationFailed),
Err(e) => Err(CoreError::InvalidInput(format!("ML-DSA verification error: {}", e))),
};
match &result {
Ok(valid) => {
log_crypto_operation_complete!("ml_dsa_verify", algorithm = ?parameter_set, valid = *valid);
debug!(algorithm = ?parameter_set, valid = *valid, "ML-DSA verification completed");
}
Err(e) => {
log_crypto_operation_error!("ml_dsa_verify", e);
}
}
result
}
fn sign_pq_slh_dsa_internal(
message: &[u8],
slh_dsa_sk: &[u8],
security_level: SlhDsaSecurityLevel,
) -> Result<Vec<u8>> {
log_crypto_operation_start!("slh_dsa_sign", algorithm = ?security_level, message_len = message.len());
validate_signature_size(message.len()).map_err(|e| {
log_crypto_operation_error!("slh_dsa_sign", e);
CoreError::ResourceExceeded(e.to_string())
})?;
let sk = SlhDsaSigningKey::from_bytes(slh_dsa_sk, security_level).map_err(|e| {
log_crypto_operation_error!("slh_dsa_sign", e);
CoreError::InvalidInput("Invalid SLH-DSA private key format".to_string())
})?;
let signature = sk.sign(message, Some(b"context")).map_err(|e| {
log_crypto_operation_error!("slh_dsa_sign", e);
CoreError::SignatureFailed(format!("SLH-DSA signing failed: {}", e))
})?;
log_crypto_operation_complete!("slh_dsa_sign", algorithm = ?security_level, signature_len = signature.len());
debug!(algorithm = ?security_level, "Created SLH-DSA signature");
Ok(signature)
}
fn verify_pq_slh_dsa_internal(
message: &[u8],
signature: &[u8],
slh_dsa_pk: &[u8],
security_level: SlhDsaSecurityLevel,
) -> Result<bool> {
log_crypto_operation_start!("slh_dsa_verify", algorithm = ?security_level, message_len = message.len());
validate_signature_size(message.len()).map_err(|e| {
log_crypto_operation_error!("slh_dsa_verify", e);
CoreError::ResourceExceeded(e.to_string())
})?;
let pk = SlhDsaVerifyingKey::from_bytes(slh_dsa_pk, security_level).map_err(|e| {
log_crypto_operation_error!("slh_dsa_verify", e);
CoreError::InvalidInput("Invalid SLH-DSA public key format".to_string())
})?;
let result = match pk.verify(message, signature, Some(b"context")) {
Ok(true) => Ok(true),
Ok(false) => Err(CoreError::VerificationFailed),
Err(e) => Err(CoreError::InvalidInput(format!("SLH-DSA verification error: {}", e))),
};
match &result {
Ok(valid) => {
log_crypto_operation_complete!("slh_dsa_verify", algorithm = ?security_level, valid = *valid);
debug!(algorithm = ?security_level, valid = *valid, "SLH-DSA verification completed");
}
Err(e) => {
log_crypto_operation_error!("slh_dsa_verify", e);
}
}
result
}
fn sign_pq_fn_dsa_internal(
message: &[u8],
fn_dsa_sk: &[u8],
security_level: FnDsaSecurityLevel,
) -> Result<Vec<u8>> {
log_crypto_operation_start!(
"fn_dsa_sign",
algorithm = "FN-DSA",
security_level = ?security_level,
message_len = message.len()
);
validate_signature_size(message.len()).map_err(|e| {
log_crypto_operation_error!("fn_dsa_sign", e);
CoreError::ResourceExceeded(e.to_string())
})?;
let mut sk = FnDsaSigningKey::from_bytes(fn_dsa_sk, security_level).map_err(|e| {
log_crypto_operation_error!("fn_dsa_sign", e);
CoreError::InvalidInput("Invalid FN-DSA private key format".to_string())
})?;
let signature = sk.sign(message).map_err(|e| {
log_crypto_operation_error!("fn_dsa_sign", e);
CoreError::SignatureFailed(format!("FN-DSA signing failed: {}", e))
})?;
let sig_bytes = signature.to_bytes();
log_crypto_operation_complete!(
"fn_dsa_sign",
algorithm = "FN-DSA",
signature_len = sig_bytes.len()
);
debug!(algorithm = "FN-DSA", "Created FN-DSA signature");
Ok(sig_bytes)
}
fn verify_pq_fn_dsa_internal(
message: &[u8],
signature: &[u8],
fn_dsa_pk: &[u8],
security_level: FnDsaSecurityLevel,
) -> Result<bool> {
log_crypto_operation_start!(
"fn_dsa_verify",
algorithm = "FN-DSA",
security_level = ?security_level,
message_len = message.len()
);
validate_signature_size(message.len()).map_err(|e| {
log_crypto_operation_error!("fn_dsa_verify", e);
CoreError::ResourceExceeded(e.to_string())
})?;
let pk = FnDsaVerifyingKey::from_bytes(fn_dsa_pk, security_level).map_err(|e| {
log_crypto_operation_error!("fn_dsa_verify", e);
CoreError::InvalidInput("Invalid FN-DSA public key format".to_string())
})?;
let sig = FnDsaSignature::from_bytes(signature).map_err(|e| {
log_crypto_operation_error!("fn_dsa_verify", e);
CoreError::InvalidInput(format!("Invalid FN-DSA signature: {}", e))
})?;
let result = match pk.verify(message, &sig) {
Ok(true) => Ok(true),
Ok(false) => Err(CoreError::VerificationFailed),
Err(e) => Err(CoreError::InvalidInput(format!("FN-DSA verification error: {}", e))),
};
match &result {
Ok(valid) => {
log_crypto_operation_complete!("fn_dsa_verify", algorithm = "FN-DSA", valid = *valid);
debug!(algorithm = "FN-DSA", valid = *valid, "FN-DSA verification completed");
}
Err(e) => {
log_crypto_operation_error!("fn_dsa_verify", e);
}
}
result
}
pub fn sign_pq_ml_dsa(
message: &[u8],
private_key: &[u8],
params: MlDsaParameterSet,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
sign_pq_ml_dsa_internal(message, private_key, params)
}
pub fn verify_pq_ml_dsa(
message: &[u8],
signature: &[u8],
public_key: &[u8],
params: MlDsaParameterSet,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
verify_pq_ml_dsa_internal(message, signature, public_key, params)
}
pub fn sign_pq_slh_dsa(
message: &[u8],
private_key: &[u8],
security_level: SlhDsaSecurityLevel,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
sign_pq_slh_dsa_internal(message, private_key, security_level)
}
pub fn verify_pq_slh_dsa(
message: &[u8],
signature: &[u8],
public_key: &[u8],
security_level: SlhDsaSecurityLevel,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
verify_pq_slh_dsa_internal(message, signature, public_key, security_level)
}
pub fn sign_pq_fn_dsa(
message: &[u8],
private_key: &[u8],
security_level: FnDsaSecurityLevel,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
sign_pq_fn_dsa_internal(message, private_key, security_level)
}
pub fn verify_pq_fn_dsa(
message: &[u8],
signature: &[u8],
public_key: &[u8],
security_level: FnDsaSecurityLevel,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
verify_pq_fn_dsa_internal(message, signature, public_key, security_level)
}
pub fn sign_pq_ml_dsa_with_config(
message: &[u8],
private_key: &[u8],
params: MlDsaParameterSet,
config: &CoreConfig,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
config.validate()?;
check_ml_dsa_config_consistency(params, config);
sign_pq_ml_dsa_internal(message, private_key, params)
}
pub fn verify_pq_ml_dsa_with_config(
message: &[u8],
signature: &[u8],
public_key: &[u8],
params: MlDsaParameterSet,
config: &CoreConfig,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
config.validate()?;
check_ml_dsa_config_consistency(params, config);
verify_pq_ml_dsa_internal(message, signature, public_key, params)
}
pub fn sign_pq_slh_dsa_with_config(
message: &[u8],
private_key: &[u8],
security_level: SlhDsaSecurityLevel,
config: &CoreConfig,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
config.validate()?;
check_slh_dsa_config_consistency(security_level, config);
sign_pq_slh_dsa_internal(message, private_key, security_level)
}
pub fn verify_pq_slh_dsa_with_config(
message: &[u8],
signature: &[u8],
public_key: &[u8],
security_level: SlhDsaSecurityLevel,
config: &CoreConfig,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
config.validate()?;
check_slh_dsa_config_consistency(security_level, config);
verify_pq_slh_dsa_internal(message, signature, public_key, security_level)
}
pub fn sign_pq_fn_dsa_with_config(
message: &[u8],
private_key: &[u8],
security_level: FnDsaSecurityLevel,
config: &CoreConfig,
mode: SecurityMode,
) -> Result<Vec<u8>> {
mode.validate()?;
config.validate()?;
sign_pq_fn_dsa_internal(message, private_key, security_level)
}
pub fn verify_pq_fn_dsa_with_config(
message: &[u8],
signature: &[u8],
public_key: &[u8],
security_level: FnDsaSecurityLevel,
config: &CoreConfig,
mode: SecurityMode,
) -> Result<bool> {
mode.validate()?;
config.validate()?;
verify_pq_fn_dsa_internal(message, signature, public_key, security_level)
}
pub fn sign_pq_ml_dsa_unverified(
message: &[u8],
ml_dsa_sk: &[u8],
parameter_set: MlDsaParameterSet,
) -> Result<Vec<u8>> {
sign_pq_ml_dsa(message, ml_dsa_sk, parameter_set, SecurityMode::Unverified)
}
pub fn verify_pq_ml_dsa_unverified(
message: &[u8],
signature: &[u8],
ml_dsa_pk: &[u8],
parameter_set: MlDsaParameterSet,
) -> Result<bool> {
verify_pq_ml_dsa(message, signature, ml_dsa_pk, parameter_set, SecurityMode::Unverified)
}
pub fn sign_pq_ml_dsa_with_config_unverified(
message: &[u8],
ml_dsa_sk: &[u8],
parameter_set: MlDsaParameterSet,
config: &CoreConfig,
) -> Result<Vec<u8>> {
sign_pq_ml_dsa_with_config(message, ml_dsa_sk, parameter_set, config, SecurityMode::Unverified)
}
pub fn verify_pq_ml_dsa_with_config_unverified(
message: &[u8],
signature: &[u8],
ml_dsa_pk: &[u8],
parameter_set: MlDsaParameterSet,
config: &CoreConfig,
) -> Result<bool> {
verify_pq_ml_dsa_with_config(
message,
signature,
ml_dsa_pk,
parameter_set,
config,
SecurityMode::Unverified,
)
}
pub fn sign_pq_slh_dsa_unverified(
message: &[u8],
slh_dsa_sk: &[u8],
security_level: SlhDsaSecurityLevel,
) -> Result<Vec<u8>> {
sign_pq_slh_dsa(message, slh_dsa_sk, security_level, SecurityMode::Unverified)
}
pub fn verify_pq_slh_dsa_unverified(
message: &[u8],
signature: &[u8],
slh_dsa_pk: &[u8],
security_level: SlhDsaSecurityLevel,
) -> Result<bool> {
verify_pq_slh_dsa(message, signature, slh_dsa_pk, security_level, SecurityMode::Unverified)
}
pub fn sign_pq_slh_dsa_with_config_unverified(
message: &[u8],
slh_dsa_sk: &[u8],
security_level: SlhDsaSecurityLevel,
config: &CoreConfig,
) -> Result<Vec<u8>> {
sign_pq_slh_dsa_with_config(
message,
slh_dsa_sk,
security_level,
config,
SecurityMode::Unverified,
)
}
pub fn verify_pq_slh_dsa_with_config_unverified(
message: &[u8],
signature: &[u8],
slh_dsa_pk: &[u8],
security_level: SlhDsaSecurityLevel,
config: &CoreConfig,
) -> Result<bool> {
verify_pq_slh_dsa_with_config(
message,
signature,
slh_dsa_pk,
security_level,
config,
SecurityMode::Unverified,
)
}
pub fn sign_pq_fn_dsa_unverified(
message: &[u8],
fn_dsa_sk: &[u8],
security_level: FnDsaSecurityLevel,
) -> Result<Vec<u8>> {
sign_pq_fn_dsa(message, fn_dsa_sk, security_level, SecurityMode::Unverified)
}
pub fn verify_pq_fn_dsa_unverified(
message: &[u8],
signature: &[u8],
fn_dsa_pk: &[u8],
security_level: FnDsaSecurityLevel,
) -> Result<bool> {
verify_pq_fn_dsa(message, signature, fn_dsa_pk, security_level, SecurityMode::Unverified)
}
pub fn sign_pq_fn_dsa_with_config_unverified(
message: &[u8],
fn_dsa_sk: &[u8],
security_level: FnDsaSecurityLevel,
config: &CoreConfig,
) -> Result<Vec<u8>> {
sign_pq_fn_dsa_with_config(message, fn_dsa_sk, security_level, config, SecurityMode::Unverified)
}
pub fn verify_pq_fn_dsa_with_config_unverified(
message: &[u8],
signature: &[u8],
fn_dsa_pk: &[u8],
security_level: FnDsaSecurityLevel,
config: &CoreConfig,
) -> Result<bool> {
verify_pq_fn_dsa_with_config(
message,
signature,
fn_dsa_pk,
security_level,
config,
SecurityMode::Unverified,
)
}
#[cfg(test)]
#[allow(
clippy::panic,
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::arithmetic_side_effects,
clippy::panic_in_result_fn,
clippy::unnecessary_wraps,
clippy::redundant_clone,
clippy::useless_vec,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::clone_on_copy,
clippy::len_zero,
clippy::single_match,
clippy::unnested_or_patterns,
clippy::default_constructed_unit_structs,
clippy::redundant_closure_for_method_calls,
clippy::semicolon_if_nothing_returned,
clippy::unnecessary_unwrap,
clippy::redundant_pattern_matching,
clippy::missing_const_for_thread_local,
clippy::get_first,
clippy::float_cmp,
clippy::needless_borrows_for_generic_args,
unused_qualifications
)]
mod tests {
use super::*;
use crate::primitives::sig::ml_dsa::MlDsaParameterSet;
use crate::primitives::sig::slh_dsa::SlhDsaSecurityLevel;
use crate::unified_api::convenience::keygen::{
generate_fn_dsa_keypair, generate_ml_dsa_keypair, generate_slh_dsa_keypair,
};
use crate::{SecurityMode, VerifiedSession, generate_keypair};
#[test]
fn test_sign_verify_pq_ml_dsa_unverified_44_succeeds() -> Result<()> {
let message = b"Test message for ML-DSA-44";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44)?;
let signature =
sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa44)?;
assert!(!signature.is_empty());
let is_valid = verify_pq_ml_dsa_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa44,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_ml_dsa_unverified_65_succeeds() -> Result<()> {
let message = b"Test message for ML-DSA-65";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let signature =
sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa65)?;
let is_valid = verify_pq_ml_dsa_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_ml_dsa_unverified_87_succeeds() -> Result<()> {
let message = b"Test message for ML-DSA-87";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa87)?;
let signature =
sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa87)?;
let is_valid = verify_pq_ml_dsa_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa87,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ml_dsa_verify_invalid_signature_fails() {
let message = b"Test message";
let (pk, _sk) =
generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65).expect("keygen should succeed");
let invalid_signature = vec![0u8; 100];
let result = verify_pq_ml_dsa_unverified(
message,
&invalid_signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
);
assert!(result.is_err(), "Verification should fail for invalid signature");
}
#[test]
fn test_ml_dsa_verify_wrong_message_fails() {
let message = b"Original message";
let wrong_message = b"Wrong message";
let (pk, sk) =
generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65).expect("keygen should succeed");
let signature = sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa65)
.expect("signing should succeed");
let result = verify_pq_ml_dsa_unverified(
wrong_message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
);
assert!(result.is_err(), "Verification should fail for wrong message");
}
#[test]
fn test_sign_verify_pq_ml_dsa_with_config_unverified_succeeds() -> Result<()> {
let message = b"Test with config";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let config = CoreConfig::default();
let signature = sign_pq_ml_dsa_with_config_unverified(
message,
sk.as_ref(),
MlDsaParameterSet::MlDsa65,
&config,
)?;
let is_valid = verify_pq_ml_dsa_with_config_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
&config,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_ml_dsa_verified_succeeds() -> Result<()> {
let message = b"Test with verified session";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_pq_ml_dsa(
message,
sk.as_ref(),
MlDsaParameterSet::MlDsa65,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_pq_ml_dsa(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_ml_dsa_unverified_mode_succeeds() -> Result<()> {
let message = b"Test unverified mode";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let signature = sign_pq_ml_dsa(
message,
sk.as_ref(),
MlDsaParameterSet::MlDsa65,
SecurityMode::Unverified,
)?;
let is_valid = verify_pq_ml_dsa(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
SecurityMode::Unverified,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_slh_dsa_unverified_128s_succeeds() -> Result<()> {
let message = b"Test SLH-DSA-128s";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
let signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake128s)?;
let is_valid = verify_pq_slh_dsa_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_slh_dsa_unverified_128f_succeeds() -> Result<()> {
let message = b"Test SLH-DSA-128f";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake192s)?;
let signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake192s)?;
let is_valid = verify_pq_slh_dsa_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake192s,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_slh_dsa_verify_wrong_message_fails() {
let message = b"Original message";
let wrong_message = b"Wrong message";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)
.expect("keygen should succeed");
let signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake128s)
.expect("signing should succeed");
let result = verify_pq_slh_dsa_unverified(
wrong_message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
);
assert!(result.is_err(), "Verification should fail for wrong message");
}
#[test]
fn test_sign_verify_pq_slh_dsa_with_config_unverified_succeeds() -> Result<()> {
let message = b"Test SLH-DSA with config";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
let config = CoreConfig::default();
let signature = sign_pq_slh_dsa_with_config_unverified(
message,
sk.as_ref(),
SlhDsaSecurityLevel::Shake128s,
&config,
)?;
let is_valid = verify_pq_slh_dsa_with_config_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
&config,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_slh_dsa_verified_succeeds() -> Result<()> {
let message = b"Test SLH-DSA verified";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_pq_slh_dsa(
message,
sk.as_ref(),
SlhDsaSecurityLevel::Shake128s,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_pq_slh_dsa(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_fn_dsa_unverified_succeeds() -> Result<()> {
let message = b"Test FN-DSA";
let (pk, sk) = generate_fn_dsa_keypair()?;
let signature =
sign_pq_fn_dsa_unverified(message, sk.as_ref(), FnDsaSecurityLevel::Level512)?;
let is_valid = verify_pq_fn_dsa_unverified(
message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_fn_dsa_verify_wrong_message_fails() -> Result<()> {
let message = b"Original message";
let wrong_message = b"Wrong message";
let (pk, sk) = generate_fn_dsa_keypair()?;
let signature =
sign_pq_fn_dsa_unverified(message, sk.as_ref(), FnDsaSecurityLevel::Level512)?;
let result = verify_pq_fn_dsa_unverified(
wrong_message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
);
assert!(result.is_err(), "FN-DSA verify with wrong message should fail");
Ok(())
}
#[test]
fn test_sign_verify_pq_fn_dsa_with_config_unverified_succeeds() -> Result<()> {
let message = b"Test FN-DSA with config";
let (pk, sk) = generate_fn_dsa_keypair()?;
let config = CoreConfig::default();
let signature = sign_pq_fn_dsa_with_config_unverified(
message,
sk.as_ref(),
FnDsaSecurityLevel::Level512,
&config,
)?;
let is_valid = verify_pq_fn_dsa_with_config_unverified(
message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
&config,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_fn_dsa_verified_succeeds() -> Result<()> {
let message = b"Test FN-DSA verified";
let (pk, sk) = generate_fn_dsa_keypair()?;
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_pq_fn_dsa(
message,
sk.as_ref(),
FnDsaSecurityLevel::Level512,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_pq_fn_dsa(
message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ml_dsa_empty_message_roundtrip_succeeds() -> Result<()> {
let message = b"";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let signature =
sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa65)?;
let is_valid = verify_pq_ml_dsa_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ml_dsa_large_message_roundtrip_succeeds() -> Result<()> {
let message = vec![0xAB; 10000];
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let signature =
sign_pq_ml_dsa_unverified(&message, sk.as_ref(), MlDsaParameterSet::MlDsa65)?;
let is_valid = verify_pq_ml_dsa_unverified(
&message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ml_dsa_signature_is_deterministic() -> Result<()> {
let message = b"Same message";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let sig1 = sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa65)?;
let sig2 = sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa65)?;
let valid1 =
verify_pq_ml_dsa_unverified(message, &sig1, pk.as_slice(), MlDsaParameterSet::MlDsa65)?;
let valid2 =
verify_pq_ml_dsa_unverified(message, &sig2, pk.as_slice(), MlDsaParameterSet::MlDsa65)?;
assert!(valid1 && valid2);
Ok(())
}
#[test]
fn test_all_ml_dsa_security_levels_succeed_succeeds() -> Result<()> {
let message = b"Test all levels";
let levels = vec![
MlDsaParameterSet::MlDsa44,
MlDsaParameterSet::MlDsa65,
MlDsaParameterSet::MlDsa87,
];
for level in levels {
let (pk, sk) = generate_ml_dsa_keypair(level)?;
let signature = sign_pq_ml_dsa_unverified(message, sk.as_ref(), level)?;
let is_valid = verify_pq_ml_dsa_unverified(message, &signature, pk.as_slice(), level)?;
assert!(is_valid, "Verification failed for {:?}", level);
}
Ok(())
}
#[test]
fn test_all_slh_dsa_security_levels_succeed_succeeds() -> Result<()> {
let message = b"Test all SLH-DSA levels";
let levels = vec![SlhDsaSecurityLevel::Shake128s, SlhDsaSecurityLevel::Shake192s];
for level in levels {
let (pk, sk) = generate_slh_dsa_keypair(level)?;
let signature = sign_pq_slh_dsa_unverified(message, sk.as_ref(), level)?;
let is_valid = verify_pq_slh_dsa_unverified(message, &signature, pk.as_slice(), level)?;
assert!(is_valid, "Verification failed for {:?}", level);
}
Ok(())
}
#[test]
fn test_slh_dsa_shake256s_succeeds() -> Result<()> {
let message = b"Test SLH-DSA Shake256s";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake256s)?;
let signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake256s)?;
let is_valid = verify_pq_slh_dsa_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake256s,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_slh_dsa_empty_message_roundtrip_succeeds() -> Result<()> {
let message = b"";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
let signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake128s)?;
let is_valid = verify_pq_slh_dsa_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_slh_dsa_large_message_roundtrip_succeeds() -> Result<()> {
let message = vec![0xCD; 10_000];
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake192s)?;
let signature =
sign_pq_slh_dsa_unverified(&message, sk.as_ref(), SlhDsaSecurityLevel::Shake192s)?;
let is_valid = verify_pq_slh_dsa_unverified(
&message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake192s,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_slh_dsa_binary_data_roundtrip_succeeds() -> Result<()> {
let message = vec![0x00, 0xFF, 0x7F, 0x80, 0x01, 0xFE];
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
let signature =
sign_pq_slh_dsa_unverified(&message, sk.as_ref(), SlhDsaSecurityLevel::Shake128s)?;
let is_valid = verify_pq_slh_dsa_unverified(
&message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_fn_dsa_empty_message_roundtrip_succeeds() -> Result<()> {
let message = b"";
let (pk, sk) = generate_fn_dsa_keypair()?;
let signature =
sign_pq_fn_dsa_unverified(message, sk.as_ref(), FnDsaSecurityLevel::Level512)?;
let is_valid = verify_pq_fn_dsa_unverified(
message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_fn_dsa_large_message_roundtrip_succeeds() -> Result<()> {
let message = vec![0xEF; 10_000];
let (pk, sk) = generate_fn_dsa_keypair()?;
let signature =
sign_pq_fn_dsa_unverified(&message, sk.as_ref(), FnDsaSecurityLevel::Level512)?;
let is_valid = verify_pq_fn_dsa_unverified(
&message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ml_dsa_cross_keypair_fails() {
let message = b"Test message";
let (_pk1, sk1) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44).expect("keypair 1");
let (pk2, _sk2) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44).expect("keypair 2");
let signature =
sign_pq_ml_dsa_unverified(message, sk1.as_ref(), MlDsaParameterSet::MlDsa44)
.expect("signing");
let result = verify_pq_ml_dsa_unverified(
message,
&signature,
pk2.as_slice(),
MlDsaParameterSet::MlDsa44,
);
assert!(result.is_err());
}
#[test]
fn test_slh_dsa_cross_keypair_fails() {
let message = b"Test message";
let (_pk1, sk1) =
generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s).expect("keypair 1");
let (pk2, _sk2) =
generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s).expect("keypair 2");
let signature =
sign_pq_slh_dsa_unverified(message, sk1.as_ref(), SlhDsaSecurityLevel::Shake128s)
.expect("signing");
let result = verify_pq_slh_dsa_unverified(
message,
&signature,
pk2.as_slice(),
SlhDsaSecurityLevel::Shake128s,
);
assert!(result.is_err());
}
#[test]
fn test_ml_dsa_tampered_signature_fails() {
let message = b"Original message";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44).expect("keypair");
let mut signature =
sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa44)
.expect("signing");
if !signature.is_empty() {
signature[0] ^= 0xFF;
}
let result = verify_pq_ml_dsa_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa44,
);
assert!(result.is_err());
}
#[test]
fn test_slh_dsa_tampered_signature_fails() {
let message = b"Original message";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s).expect("keypair");
let mut signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake128s)
.expect("signing");
if !signature.is_empty() {
signature[0] ^= 0xFF;
}
let result = verify_pq_slh_dsa_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
);
assert!(result.is_err());
}
#[test]
fn test_ml_dsa_binary_data_roundtrip_succeeds() -> Result<()> {
let message = vec![0x00, 0xFF, 0x7F, 0x80, 0x01, 0xFE];
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44)?;
let signature =
sign_pq_ml_dsa_unverified(&message, sk.as_ref(), MlDsaParameterSet::MlDsa44)?;
let is_valid = verify_pq_ml_dsa_unverified(
&message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa44,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_fn_dsa_binary_data_roundtrip_succeeds() -> Result<()> {
let message = vec![0x00, 0xFF, 0x7F, 0x80, 0x01, 0xFE];
let (pk, sk) = generate_fn_dsa_keypair()?;
let signature =
sign_pq_fn_dsa_unverified(&message, sk.as_ref(), FnDsaSecurityLevel::Level512)?;
let is_valid = verify_pq_fn_dsa_unverified(
&message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_ml_dsa_multiple_messages_succeed_succeeds() -> Result<()> {
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let messages = vec![b"First".as_ref(), b"Second".as_ref(), b"Third".as_ref()];
for message in messages {
let signature =
sign_pq_ml_dsa_unverified(message, sk.as_ref(), MlDsaParameterSet::MlDsa65)?;
let is_valid = verify_pq_ml_dsa_unverified(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
)?;
assert!(is_valid);
}
Ok(())
}
#[test]
fn test_slh_dsa_multiple_messages_succeed_succeeds() -> Result<()> {
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake192s)?;
let messages = vec![b"First".as_ref(), b"Second".as_ref(), b"Third".as_ref()];
for message in messages {
let signature =
sign_pq_slh_dsa_unverified(message, sk.as_ref(), SlhDsaSecurityLevel::Shake192s)?;
let is_valid = verify_pq_slh_dsa_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake192s,
)?;
assert!(is_valid);
}
Ok(())
}
#[test]
fn test_ml_dsa_with_config_all_params_succeeds() -> Result<()> {
let message = b"Test with config";
let config = CoreConfig::default();
let params = vec![
MlDsaParameterSet::MlDsa44,
MlDsaParameterSet::MlDsa65,
MlDsaParameterSet::MlDsa87,
];
for param in params {
let (pk, sk) = generate_ml_dsa_keypair(param)?;
let signature =
sign_pq_ml_dsa_with_config_unverified(message, sk.as_ref(), param, &config)?;
let is_valid = verify_pq_ml_dsa_with_config_unverified(
message,
&signature,
pk.as_slice(),
param,
&config,
)?;
assert!(is_valid);
}
Ok(())
}
#[test]
fn test_slh_dsa_256s_with_config_succeeds() -> Result<()> {
let message = b"Test SLH-DSA-256s with config";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake256s)?;
let config = CoreConfig::default();
let signature = sign_pq_slh_dsa_with_config_unverified(
message,
sk.as_ref(),
SlhDsaSecurityLevel::Shake256s,
&config,
)?;
let is_valid = verify_pq_slh_dsa_with_config_unverified(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake256s,
&config,
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_slh_dsa_with_config_verified_succeeds() -> Result<()> {
let message = b"SLH-DSA with config verified";
let (pk, sk) = generate_slh_dsa_keypair(SlhDsaSecurityLevel::Shake128s)?;
let config = CoreConfig::default();
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_pq_slh_dsa_with_config(
message,
sk.as_ref(),
SlhDsaSecurityLevel::Shake128s,
&config,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_pq_slh_dsa_with_config(
message,
&signature,
pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
&config,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_fn_dsa_with_config_verified_succeeds() -> Result<()> {
let message = b"FN-DSA with config verified";
let (pk, sk) = generate_fn_dsa_keypair()?;
let config = CoreConfig::default();
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_pq_fn_dsa_with_config(
message,
sk.as_ref(),
FnDsaSecurityLevel::Level512,
&config,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_pq_fn_dsa_with_config(
message,
&signature,
pk.as_slice(),
FnDsaSecurityLevel::Level512,
&config,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_verify_pq_ml_dsa_with_config_verified_succeeds() -> Result<()> {
let message = b"ML-DSA with config verified";
let (pk, sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa65)?;
let config = CoreConfig::default();
let (auth_pk, auth_sk) = generate_keypair()?;
let session = VerifiedSession::establish(auth_pk.as_slice(), auth_sk.as_ref())?;
let signature = sign_pq_ml_dsa_with_config(
message,
sk.as_ref(),
MlDsaParameterSet::MlDsa65,
&config,
SecurityMode::Verified(&session),
)?;
let is_valid = verify_pq_ml_dsa_with_config(
message,
&signature,
pk.as_slice(),
MlDsaParameterSet::MlDsa65,
&config,
SecurityMode::Verified(&session),
)?;
assert!(is_valid);
Ok(())
}
#[test]
fn test_sign_pq_ml_dsa_invalid_sk_returns_error() {
let bad_sk = vec![0u8; 10]; let result = sign_pq_ml_dsa_unverified(b"msg", &bad_sk, MlDsaParameterSet::MlDsa44);
assert!(result.is_err(), "Invalid ML-DSA secret key should fail");
}
#[test]
fn test_verify_pq_ml_dsa_invalid_pk_returns_error() {
let bad_pk = vec![0u8; 10];
let bad_sig = vec![0u8; 100];
let result = verify_pq_ml_dsa_unverified(
b"msg",
&bad_sig,
bad_pk.as_slice(),
MlDsaParameterSet::MlDsa44,
);
assert!(result.is_err(), "Invalid ML-DSA public key should fail");
}
#[test]
fn test_verify_pq_ml_dsa_invalid_signature_returns_error() {
let (pk, _sk) = generate_ml_dsa_keypair(MlDsaParameterSet::MlDsa44).expect("keygen");
let bad_sig = vec![0u8; 10]; let result = verify_pq_ml_dsa_unverified(
b"msg",
&bad_sig,
pk.as_slice(),
MlDsaParameterSet::MlDsa44,
);
assert!(result.is_err(), "Invalid ML-DSA signature should fail");
}
#[test]
fn test_sign_pq_slh_dsa_invalid_sk_returns_error() {
let bad_sk = vec![0u8; 10];
let result = sign_pq_slh_dsa_unverified(b"msg", &bad_sk, SlhDsaSecurityLevel::Shake128s);
assert!(result.is_err(), "Invalid SLH-DSA secret key should fail");
}
#[test]
fn test_verify_pq_slh_dsa_invalid_pk_returns_error() {
let bad_pk = vec![0u8; 10];
let bad_sig = vec![0u8; 100];
let result = verify_pq_slh_dsa_unverified(
b"msg",
&bad_sig,
bad_pk.as_slice(),
SlhDsaSecurityLevel::Shake128s,
);
assert!(result.is_err(), "Invalid SLH-DSA public key should fail");
}
#[test]
fn test_sign_pq_fn_dsa_invalid_sk_returns_error() {
let bad_sk = vec![0u8; 10];
let result = sign_pq_fn_dsa_unverified(b"msg", &bad_sk, FnDsaSecurityLevel::Level512);
assert!(result.is_err(), "Invalid FN-DSA secret key should fail");
}
#[test]
fn test_verify_pq_fn_dsa_invalid_pk_returns_error() {
let bad_pk = vec![0u8; 10];
let bad_sig = vec![0u8; 100];
let result = verify_pq_fn_dsa_unverified(
b"msg",
&bad_sig,
bad_pk.as_slice(),
FnDsaSecurityLevel::Level512,
);
assert!(result.is_err(), "Invalid FN-DSA public key should fail");
}
}