#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]
use crate::prelude::error::{LatticeArcError, Result};
use rand_core::RngCore;
use subtle::ConstantTimeEq;
use tracing::instrument;
use zeroize::{Zeroize, Zeroizing};
use fn_dsa::{
DOMAIN_NONE, FN_DSA_LOGN_512, FN_DSA_LOGN_1024, HASH_ID_RAW, KeyPairGenerator as _,
KeyPairGeneratorStandard, SigningKey as _, VerifyingKey as _, sign_key_size, signature_size,
vrfy_key_size,
};
#[non_exhaustive]
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FnDsaSecurityLevel {
#[default]
Level512,
Level1024,
}
impl FnDsaSecurityLevel {
#[must_use]
pub fn to_logn(&self) -> u32 {
match self {
FnDsaSecurityLevel::Level512 => FN_DSA_LOGN_512,
FnDsaSecurityLevel::Level1024 => FN_DSA_LOGN_1024,
}
}
#[must_use]
pub fn signature_size(&self) -> usize {
signature_size(self.to_logn())
}
#[must_use]
pub fn signing_key_size(&self) -> usize {
sign_key_size(self.to_logn())
}
#[must_use]
pub fn verifying_key_size(&self) -> usize {
vrfy_key_size(self.to_logn())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Signature {
bytes: Vec<u8>,
}
impl Signature {
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.is_empty() {
return Err(LatticeArcError::InvalidSignature(
"Signature bytes cannot be empty".to_string(),
));
}
Ok(Self { bytes: bytes.to_vec() })
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
#[must_use]
pub fn len(&self) -> usize {
self.bytes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.bytes.is_empty()
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl TryFrom<Vec<u8>> for Signature {
type Error = LatticeArcError;
fn try_from(bytes: Vec<u8>) -> std::result::Result<Self, Self::Error> {
Self::from_bytes(&bytes)
}
}
#[derive(Clone, Debug)]
pub struct VerifyingKey {
security_level: FnDsaSecurityLevel,
inner: FnDsaVerifyingKeyStandard,
bytes: Vec<u8>,
}
impl VerifyingKey {
#[must_use]
pub fn security_level(&self) -> FnDsaSecurityLevel {
self.security_level
}
pub fn from_bytes(bytes: &[u8], security_level: FnDsaSecurityLevel) -> Result<Self> {
if bytes.len() != security_level.verifying_key_size() {
return Err(LatticeArcError::InvalidKey("Invalid verifying key length".to_string()));
}
let inner = FnDsaVerifyingKeyStandard::decode(bytes).ok_or_else(|| {
LatticeArcError::InvalidKey("Failed to decode verifying key".to_string())
})?;
Ok(Self { security_level, inner, bytes: bytes.to_vec() })
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[must_use]
pub fn to_bytes(&self) -> Vec<u8> {
self.bytes.clone()
}
#[instrument(level = "debug", skip(self, message, signature), fields(security_level = ?self.security_level, message_len = message.len(), signature_len = signature.len()))]
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<bool> {
{
let valid = self.inner.verify(signature.as_ref(), &DOMAIN_NONE, &HASH_ID_RAW, message);
Ok(valid)
}
}
}
pub struct SigningKey {
security_level: FnDsaSecurityLevel,
inner: FnDsaSigningKeyStandard,
bytes: Vec<u8>,
verifying_key: VerifyingKey,
}
impl Drop for SigningKey {
fn drop(&mut self) {
for byte in &mut self.bytes {
byte.zeroize();
}
}
}
impl Zeroize for SigningKey {
fn zeroize(&mut self) {
for byte in &mut self.bytes {
byte.zeroize();
}
}
}
impl std::fmt::Debug for SigningKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SigningKey").field("has_inner", &true).finish()
}
}
impl ConstantTimeEq for SigningKey {
fn ct_eq(&self, other: &Self) -> subtle::Choice {
let level_eq = (self.security_level as u8).ct_eq(&(other.security_level as u8));
level_eq & self.bytes.ct_eq(&other.bytes)
}
}
impl SigningKey {
pub fn from_bytes(bytes: &[u8], security_level: FnDsaSecurityLevel) -> Result<Self> {
if bytes.len() != security_level.signing_key_size() {
return Err(LatticeArcError::InvalidKey(format!(
"Invalid FN-DSA signing key length: expected {}, got {}",
security_level.signing_key_size(),
bytes.len()
)));
}
let inner = FnDsaSigningKeyStandard::decode(bytes).ok_or_else(|| {
LatticeArcError::InvalidKey("Failed to decode signing key".to_string())
})?;
let mut vrfy_key_bytes = vec![0u8; security_level.verifying_key_size()];
inner.to_verifying_key(&mut vrfy_key_bytes);
let verifying_key = VerifyingKey::from_bytes(&vrfy_key_bytes, security_level)?;
Ok(Self { security_level, inner, bytes: bytes.to_vec(), verifying_key })
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
#[must_use]
pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
Zeroizing::new(self.bytes.clone())
}
#[must_use]
pub fn verifying_key(&self) -> &VerifyingKey {
&self.verifying_key
}
#[must_use]
pub fn security_level(&self) -> FnDsaSecurityLevel {
self.security_level
}
#[instrument(level = "debug", skip(self, message), fields(security_level = ?self.security_level, message_len = message.len()))]
pub fn sign(&mut self, message: &[u8]) -> Result<Signature> {
self.sign_with_rng(&mut rand::rngs::OsRng {}, message)
}
#[instrument(level = "debug", skip(self, rng, message), fields(security_level = ?self.security_level, message_len = message.len()))]
pub fn sign_with_rng<R: RngCore + rand::CryptoRng>(
&mut self,
rng: &mut R,
message: &[u8],
) -> Result<Signature> {
let logn = match self.security_level {
FnDsaSecurityLevel::Level512 => FN_DSA_LOGN_512,
FnDsaSecurityLevel::Level1024 => FN_DSA_LOGN_1024,
};
let mut sig_bytes = vec![0u8; signature_size(logn)];
self.inner.sign(rng, &DOMAIN_NONE, &HASH_ID_RAW, message, &mut sig_bytes);
Signature::from_bytes(&sig_bytes)
}
}
pub struct KeyPair {
signing_key: SigningKey,
verifying_key: VerifyingKey,
}
impl std::fmt::Debug for KeyPair {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("KeyPair")
.field("signing_key", &"[REDACTED]")
.field("verifying_key", &"[public]")
.finish()
}
}
impl Drop for KeyPair {
fn drop(&mut self) {
self.signing_key.zeroize();
}
}
impl Zeroize for KeyPair {
fn zeroize(&mut self) {
self.signing_key.zeroize();
}
}
impl KeyPair {
#[instrument(level = "debug", fields(security_level = ?security_level))]
pub fn generate(security_level: FnDsaSecurityLevel) -> Result<Self> {
Self::generate_with_rng(&mut rand::rngs::OsRng {}, security_level)
}
#[instrument(level = "debug", skip(rng), fields(security_level = ?security_level))]
pub fn generate_with_rng<R: RngCore + rand::CryptoRng>(
rng: &mut R,
security_level: FnDsaSecurityLevel,
) -> Result<Self> {
let mut kg = KeyPairGeneratorStandard::default();
let logn = security_level.to_logn();
let mut sk_bytes = Zeroizing::new(vec![0u8; sign_key_size(logn)]);
let mut vk_bytes = vec![0u8; vrfy_key_size(logn)];
kg.keygen(logn, rng, &mut sk_bytes, &mut vk_bytes);
let signing_key = SigningKey::from_bytes(&sk_bytes, security_level)?;
let verifying_key = VerifyingKey::from_bytes(&vk_bytes, security_level)?;
let mut keypair = Self { signing_key, verifying_key };
crate::primitives::pct::pct_fn_dsa_keypair(&mut keypair)
.map_err(|e| LatticeArcError::KeyGenerationError(format!("PCT failed: {}", e)))?;
Ok(keypair)
}
#[must_use]
pub fn signing_key(&self) -> &SigningKey {
&self.signing_key
}
#[must_use]
pub fn verifying_key(&self) -> &VerifyingKey {
&self.verifying_key
}
pub fn sign(&mut self, message: &[u8]) -> Result<Signature> {
self.signing_key.sign(message)
}
pub fn sign_with_rng<R: RngCore + rand::CryptoRng>(
&mut self,
rng: &mut R,
message: &[u8],
) -> Result<Signature> {
self.signing_key.sign_with_rng(rng, message)
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<bool> {
self.verifying_key.verify(message, signature)
}
}
pub(crate) type FnDsaSigningKeyStandard = fn_dsa::SigningKeyStandard;
pub(crate) type FnDsaVerifyingKeyStandard = fn_dsa::VerifyingKeyStandard;
#[cfg(test)]
#[allow(clippy::unwrap_used)] #[allow(clippy::expect_used)] mod tests {
use super::*;
use rand::rngs::OsRng;
#[test]
fn test_fndsa_key_generation_512_succeeds() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
assert_eq!(
keypair.signing_key().to_bytes().len(),
FnDsaSecurityLevel::Level512.signing_key_size()
);
assert_eq!(
keypair.verifying_key().to_bytes().len(),
FnDsaSecurityLevel::Level512.verifying_key_size()
);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_key_generation_1024_succeeds() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level1024).unwrap();
assert_eq!(
keypair.signing_key().to_bytes().len(),
FnDsaSecurityLevel::Level1024.signing_key_size()
);
assert_eq!(
keypair.verifying_key().to_bytes().len(),
FnDsaSecurityLevel::Level1024.verifying_key_size()
);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_signature_sign_verify_roundtrip() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let mut keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let message = b"Hello, FN-DSA world!";
let mut rng = OsRng;
let signature = keypair.sign_with_rng(&mut rng, message).unwrap();
let verified = keypair.verify(message, &signature).unwrap();
assert!(verified);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_wrong_message_fails() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let mut keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let message = b"Correct message";
let wrong_message = b"Wrong message";
let mut rng = OsRng;
let signature = keypair.sign_with_rng(&mut rng, message).unwrap();
let verified = keypair.verify(wrong_message, &signature).unwrap();
assert!(!verified);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_key_serialization_roundtrip() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let sk_bytes = keypair.signing_key().to_bytes();
let deserialized_sk =
SigningKey::from_bytes(&sk_bytes, FnDsaSecurityLevel::Level512).unwrap();
assert_eq!(keypair.signing_key().to_bytes(), deserialized_sk.to_bytes());
let vk_bytes = keypair.verifying_key().to_bytes();
let deserialized_vk =
VerifyingKey::from_bytes(&vk_bytes, FnDsaSecurityLevel::Level512).unwrap();
assert_eq!(keypair.verifying_key().to_bytes(), deserialized_vk.to_bytes());
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_signature_serialization_roundtrip() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let mut keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let message = b"Test message";
let mut rng = OsRng;
let signature = keypair.sign_with_rng(&mut rng, message).unwrap();
let sig_bytes = signature.to_bytes();
let deserialized_sig = Signature::from_bytes(&sig_bytes).unwrap();
assert_eq!(signature.to_bytes(), deserialized_sig.to_bytes());
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_security_level_sizes_match_spec_has_correct_size() {
let level512 = FnDsaSecurityLevel::Level512;
let level1024 = FnDsaSecurityLevel::Level1024;
assert_eq!(level512.signature_size(), 666);
assert_eq!(level512.signing_key_size(), 1281);
assert_eq!(level512.verifying_key_size(), 897);
assert_eq!(level1024.signature_size(), 1280);
assert_eq!(level1024.signing_key_size(), 2305);
assert_eq!(level1024.verifying_key_size(), 1793);
}
#[test]
fn test_fndsa_empty_signature_is_rejected() {
let result = Signature::from_bytes(&[]);
assert!(result.is_err());
}
#[test]
fn test_fndsa_invalid_key_length_is_rejected() {
let result = VerifyingKey::from_bytes(&[0u8; 100], FnDsaSecurityLevel::Level512);
assert!(result.is_err());
let result = SigningKey::from_bytes(&[0u8; 100], FnDsaSecurityLevel::Level512);
assert!(result.is_err());
}
#[test]
fn test_fndsa_security_level_default_is_level512_succeeds() {
let level = FnDsaSecurityLevel::default();
assert_eq!(level, FnDsaSecurityLevel::Level512);
}
#[test]
fn test_fndsa_security_level_to_logn_matches_spec_succeeds() {
assert_eq!(FnDsaSecurityLevel::Level512.to_logn(), FN_DSA_LOGN_512);
assert_eq!(FnDsaSecurityLevel::Level1024.to_logn(), FN_DSA_LOGN_1024);
}
#[test]
fn test_fndsa_signing_key_zeroization_clears_bytes_succeeds() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let sk_bytes = keypair.signing_key().to_bytes();
drop(keypair);
let mut signing_key =
SigningKey::from_bytes(&sk_bytes, FnDsaSecurityLevel::Level512).unwrap();
let original_bytes = signing_key.to_bytes();
let key_len = original_bytes.len();
assert!(
original_bytes.iter().any(|&b| b != 0),
"Key bytes should not be all zeros before zeroization"
);
signing_key.zeroize();
let zeroized_bytes = signing_key.to_bytes();
assert_eq!(
zeroized_bytes.len(),
key_len,
"Key bytes length should remain unchanged after zeroization"
);
assert!(
zeroized_bytes.iter().all(|&b| b == 0),
"Key bytes should be all zeros after zeroization"
);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_keypair_zeroization_clears_bytes_succeeds() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let mut keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let original_bytes = keypair.signing_key().to_bytes();
assert!(
original_bytes.iter().any(|&b| b != 0),
"Key bytes should not be all zeros"
);
keypair.zeroize();
let zeroized_bytes = keypair.signing_key().to_bytes();
assert!(
zeroized_bytes.iter().all(|&b| b == 0),
"Key bytes should be all zeros after zeroization"
);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)] #[allow(clippy::expect_used)] mod integration_tests {
use super::*;
use rand::rngs::OsRng;
#[test]
fn test_fndsa_multiple_messages_same_key_all_verify_succeeds() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let mut keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512).unwrap();
let message1 = b"Message 1";
let message2 = b"Message 2";
let mut rng = OsRng;
let sig1 = keypair.sign_with_rng(&mut rng, message1).unwrap();
let mut rng = OsRng;
let sig2 = keypair.sign_with_rng(&mut rng, message2).unwrap();
assert!(keypair.verify(message1, &sig1).unwrap());
assert!(keypair.verify(message2, &sig2).unwrap());
assert!(!keypair.verify(message2, &sig1).unwrap());
assert!(!keypair.verify(message1, &sig2).unwrap());
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
#[test]
fn test_fndsa_level1024_signature_sign_verify_roundtrip() {
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024)
.spawn(|| {
let mut rng = OsRng;
let mut keypair =
KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level1024).unwrap();
let message = b"Test message for FN-DSA-1024";
let mut rng = OsRng;
let signature = keypair.sign_with_rng(&mut rng, message).unwrap();
assert_eq!(signature.len(), 1280);
let verified = keypair.verify(message, &signature).unwrap();
assert!(verified);
})
.expect("Thread spawn failed")
.join()
.expect("Thread join failed");
}
}