#[cfg(any(feature = "ml-kem", feature = "ml-dsa"))]
use crate::error::{PqcError, Result};
#[cfg(feature = "ml-kem")]
use crate::{
decapsulate_shared_secret, encapsulate_shared_secret, KyberKeys,
};
#[cfg(feature = "ml-dsa")]
use crate::{
sign_message, verify_signature,
DilithiumPublicKey, DilithiumSecretKey,
};
#[cfg(feature = "ml-kem")]
pub fn kyber_pct(keys: &KyberKeys) -> Result<()> {
let (ciphertext, ss_encap) = encapsulate_shared_secret(&keys.pk);
let ss_decap = decapsulate_shared_secret(&keys.sk, &ciphertext);
if ss_encap == ss_decap {
Ok(())
} else {
Err(PqcError::PairwiseConsistencyTestFailure)
}
}
#[cfg(feature = "ml-dsa")]
pub fn dilithium_pct(pk: &DilithiumPublicKey, sk: &DilithiumSecretKey) -> Result<()> {
const PCT_MESSAGE: &[u8] = b"FIPS 140-3 Pair-wise Consistency Test";
let signature = sign_message(sk, PCT_MESSAGE);
if verify_signature(pk, PCT_MESSAGE, &signature) {
Ok(())
} else {
Err(PqcError::PairwiseConsistencyTestFailure)
}
}
#[cfg(test)]
mod tests {
#[cfg(any(feature = "ml-kem", feature = "ml-dsa"))]
use super::*;
#[test]
#[cfg(all(feature = "ml-kem", feature = "std"))]
fn test_kyber_pct_success() {
use crate::KyberKeys;
let keys = KyberKeys::generate_key_pair();
assert!(kyber_pct(&keys).is_ok(), "Kyber PCT should pass for valid keys");
}
#[test]
#[cfg(all(feature = "ml-dsa", feature = "std"))]
fn test_dilithium_pct_success() {
use crate::generate_dilithium_keypair;
let (pk, sk) = generate_dilithium_keypair();
assert!(
dilithium_pct(&pk, &sk).is_ok(),
"Dilithium PCT should pass for valid keys"
);
}
#[test]
#[cfg(all(feature = "ml-kem", feature = "std"))]
fn test_kyber_pct_failure_mismatched_keys() {
use crate::KyberKeys;
let keys1 = KyberKeys::generate_key_pair();
let keys2 = KyberKeys::generate_key_pair();
let mismatched = KyberKeys {
pk: keys1.pk,
sk: keys2.sk,
};
let result = kyber_pct(&mismatched);
assert!(result.is_err(), "Kyber PCT should fail for mismatched keys");
assert_eq!(
result.unwrap_err(),
PqcError::PairwiseConsistencyTestFailure
);
}
#[test]
#[cfg(all(feature = "ml-dsa", feature = "std"))]
fn test_dilithium_pct_failure_mismatched_keys() {
use crate::generate_dilithium_keypair;
let (pk1, _sk1) = generate_dilithium_keypair();
let (_pk2, sk2) = generate_dilithium_keypair();
let result = dilithium_pct(&pk1, &sk2);
assert!(result.is_err(), "Dilithium PCT should fail for mismatched keys");
assert_eq!(
result.unwrap_err(),
PqcError::PairwiseConsistencyTestFailure
);
}
#[test]
#[cfg(all(feature = "std", feature = "ml-kem", feature = "ml-dsa"))]
fn test_pct_multiple_iterations() {
use crate::{KyberKeys, generate_dilithium_keypair};
for _ in 0..10 {
let keys = KyberKeys::generate_key_pair();
assert!(kyber_pct(&keys).is_ok());
let (pk, sk) = generate_dilithium_keypair();
assert!(dilithium_pct(&pk, &sk).is_ok());
}
}
}