use fn_dsa::{
DOMAIN_NONE, FN_DSA_LOGN_1024, HASH_ID_RAW, KeyPairGenerator, KeyPairGeneratorStandard,
SigningKey, SigningKeyStandard, VerifyingKey, VerifyingKeyStandard, sign_key_size,
signature_size, vrfy_key_size,
};
use rand_core::OsRng;
use zeroize::Zeroizing;
use crate::errors::CryptoError;
pub const PUBLIC_KEY_BYTES: usize = vrfy_key_size(FN_DSA_LOGN_1024);
pub const SECRET_KEY_BYTES: usize = sign_key_size(FN_DSA_LOGN_1024);
pub const SIGNATURE_BYTES: usize = signature_size(FN_DSA_LOGN_1024);
pub type PublicKeyBytes = [u8; PUBLIC_KEY_BYTES];
pub type SecretKeyBytes = [u8; SECRET_KEY_BYTES];
pub type SignatureBytes = [u8; SIGNATURE_BYTES];
pub trait ViewOperations {
fn pub_key_bytes(&self) -> &PublicKeyBytes;
fn verify(&self, msg: &[u8], sig: &[u8]) -> bool {
VerifyingKeyStandard::decode(self.pub_key_bytes())
.is_some_and(|vk| vk.verify(sig, &DOMAIN_NONE, &HASH_ID_RAW, msg))
}
}
#[derive(Clone)]
pub struct VerifierPair {
pub_key: PublicKeyBytes,
}
impl VerifierPair {
pub fn new(pub_key: &[u8]) -> Result<Self, CryptoError> {
let pub_key: PublicKeyBytes = pub_key
.try_into()
.map_err(|_| CryptoError::IncongruentLength(PUBLIC_KEY_BYTES, pub_key.len()))?;
VerifyingKeyStandard::decode(&pub_key).ok_or(CryptoError::InvalidKey)?;
Ok(Self { pub_key })
}
pub fn from_bytes(pub_key: &[u8]) -> Result<Self, CryptoError> {
Self::new(pub_key)
}
pub fn to_bytes(&self) -> PublicKeyBytes {
self.pub_key
}
}
impl ViewOperations for VerifierPair {
fn pub_key_bytes(&self) -> &PublicKeyBytes {
&self.pub_key
}
}
pub struct SignerPair {
pub_key: PublicKeyBytes,
sec_key: Zeroizing<SecretKeyBytes>,
signing_key: Box<SigningKeyStandard>,
}
impl SignerPair {
pub fn create() -> Self {
let mut pub_key = [0u8; PUBLIC_KEY_BYTES];
let mut sec_key = Zeroizing::new([0u8; SECRET_KEY_BYTES]);
KeyPairGeneratorStandard::default().keygen(
FN_DSA_LOGN_1024,
&mut OsRng,
&mut sec_key[..],
&mut pub_key,
);
let signing_key = decode_signing_key(&sec_key).expect("freshly generated key decodes");
Self {
pub_key,
sec_key,
signing_key,
}
}
pub fn sign(&mut self, msg: &[u8]) -> Result<SignatureBytes, CryptoError> {
let mut sig = [0u8; SIGNATURE_BYTES];
self.signing_key
.sign(&mut OsRng, &DOMAIN_NONE, &HASH_ID_RAW, msg, &mut sig)
.ok_or(CryptoError::InvalidKey)?;
Ok(sig)
}
pub fn from_bytes(pub_key: &[u8], sec_key: &[u8]) -> Result<Self, CryptoError> {
let sec_key: Zeroizing<SecretKeyBytes> = Zeroizing::new(
sec_key
.try_into()
.map_err(|_| CryptoError::IncongruentLength(SECRET_KEY_BYTES, sec_key.len()))?,
);
let signing_key = decode_signing_key(&sec_key).ok_or(CryptoError::InvalidKey)?;
let mut derived = [0u8; PUBLIC_KEY_BYTES];
signing_key.to_verifying_key(&mut derived);
if derived[..] != *pub_key {
return Err(CryptoError::InvalidKey);
}
Ok(Self {
pub_key: derived,
sec_key,
signing_key,
})
}
pub fn to_bytes(&self) -> (PublicKeyBytes, Zeroizing<SecretKeyBytes>) {
(self.pub_key, self.sec_key.clone())
}
pub fn to_bytes_uniform(&self) -> Zeroizing<Vec<u8>> {
Zeroizing::new([&self.pub_key[..], &self.sec_key[..]].concat())
}
pub fn from_bytes_uniform(bytes: &[u8]) -> Result<Self, CryptoError> {
if bytes.len() != PUBLIC_KEY_BYTES + SECRET_KEY_BYTES {
return Err(CryptoError::IncongruentLength(
PUBLIC_KEY_BYTES + SECRET_KEY_BYTES,
bytes.len(),
));
}
let (pub_key, sec_key) = bytes.split_at(PUBLIC_KEY_BYTES);
Self::from_bytes(pub_key, sec_key)
}
}
impl Clone for SignerPair {
fn clone(&self) -> Self {
Self {
pub_key: self.pub_key,
sec_key: self.sec_key.clone(),
signing_key: decode_signing_key(&self.sec_key).expect("a decoded key decodes again"),
}
}
}
impl ViewOperations for SignerPair {
fn pub_key_bytes(&self) -> &PublicKeyBytes {
&self.pub_key
}
}
fn decode_signing_key(sec_key: &SecretKeyBytes) -> Option<Box<SigningKeyStandard>> {
let decode = || SigningKeyStandard::decode(sec_key).map(Box::new);
std::thread::scope(|scope| {
match std::thread::Builder::new()
.stack_size(8 << 20)
.spawn_scoped(scope, decode)
{
Ok(handle) => handle
.join()
.unwrap_or_else(|panic| std::panic::resume_unwind(panic)),
Err(_) => decode(),
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_signer_sign() {
let mut signer = SignerPair::create();
let msg = b"Hello, World!";
let sig = signer.sign(msg).unwrap();
assert!(signer.verify(msg, &sig));
assert!(!signer.verify(b"Hello, World?", &sig));
}
#[test]
fn test_verifier_verify() {
let mut signer = SignerPair::create();
let msg = b"Hello, World!";
let sig = signer.sign(msg).unwrap();
let verifier = VerifierPair::new(signer.pub_key_bytes()).unwrap();
assert!(verifier.verify(msg, &sig));
let other = VerifierPair::new(SignerPair::create().pub_key_bytes()).unwrap();
assert!(!other.verify(msg, &sig));
}
#[test]
fn test_signer_bytes() {
let signer = SignerPair::create();
let (pub_key, sec_key) = signer.to_bytes();
let mut signer2 = SignerPair::from_bytes(&pub_key, &sec_key[..]).unwrap();
assert_eq!(signer.pub_key_bytes(), signer2.pub_key_bytes());
assert!(signer.verify(b"msg", &signer2.sign(b"msg").unwrap()));
let signer3 = SignerPair::from_bytes_uniform(&signer.to_bytes_uniform()).unwrap();
assert_eq!(signer.pub_key_bytes(), signer3.pub_key_bytes());
assert!(signer.verify(b"msg", &signer3.clone().sign(b"msg").unwrap()));
}
#[test]
fn test_invalid_inputs() {
let mut signer = SignerPair::create();
let (pub_key, sec_key) = signer.to_bytes();
assert!(matches!(
SignerPair::from_bytes_uniform(&[0u8; 10]),
Err(CryptoError::IncongruentLength(n, 10)) if n == PUBLIC_KEY_BYTES + SECRET_KEY_BYTES
));
assert!(matches!(
SignerPair::from_bytes(&pub_key, &sec_key[1..]),
Err(CryptoError::IncongruentLength(SECRET_KEY_BYTES, _))
));
let mut bad_sec = sec_key.clone();
bad_sec[0] ^= 0xFF;
assert!(matches!(
SignerPair::from_bytes(&pub_key, &bad_sec[..]),
Err(CryptoError::InvalidKey)
));
let mut bad_pub = pub_key;
bad_pub[0] ^= 0xFF;
assert!(matches!(
VerifierPair::new(&bad_pub),
Err(CryptoError::InvalidKey)
));
let sig = signer.sign(b"msg").unwrap();
assert!(!signer.verify(b"msg", &sig[..10]));
assert!(!signer.verify(b"msg", &[]));
let mut flipped = sig;
flipped[100] ^= 1;
assert!(!signer.verify(b"msg", &flipped));
}
#[test]
fn test_works_on_small_stack() {
let bytes = SignerPair::create().to_bytes_uniform();
std::thread::Builder::new()
.stack_size(512 << 10)
.spawn(move || {
let mut signer = SignerPair::from_bytes_uniform(&bytes).unwrap();
let sig = signer.sign(b"msg").unwrap();
assert!(signer.verify(b"msg", &sig));
assert!(signer.clone().verify(b"msg", &sig));
})
.unwrap()
.join()
.unwrap();
}
#[test]
fn test_signer_rejects_foreign_pubkey() {
let signer = SignerPair::create();
let other = SignerPair::create();
let result = SignerPair::from_bytes(other.pub_key_bytes(), &signer.to_bytes().1[..]);
assert!(matches!(result, Err(CryptoError::InvalidKey)));
}
#[test]
fn test_verifier_bytes() {
let signer = SignerPair::create();
let verifier = VerifierPair::new(signer.pub_key_bytes()).unwrap();
let pub_key = verifier.to_bytes();
let verifier2 = VerifierPair::from_bytes(&pub_key).unwrap();
assert_eq!(verifier.pub_key_bytes(), verifier2.pub_key_bytes());
assert!(matches!(
VerifierPair::new(&pub_key[1..]),
Err(CryptoError::IncongruentLength(PUBLIC_KEY_BYTES, _))
));
}
}