use alloc::string::{String, ToString};
use data::{
DETERMINISTIC_SIGNATURE, EXPECTED_SIG, EXPECTED_SIG_POLYS, NUM_TEST_VECTORS, SK_POLYS,
SYNC_DATA, SYNC_DATA_FOR_TEST_VECTOR,
};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;
use super::{FalconFelt, N, Polynomial, PublicKey};
use crate::{
SequentialCommit,
dsa::{
falcon512_common::test_utils::Shake256Testing,
falcon512_eidos::{
PREVERSIONED_NONCE, PREVERSIONED_NONCE_LEN, SIG_NONCE_LEN, SIG_POLY_BYTE_LEN, SecretKey,
},
falcon512_poseidon2,
},
hash::{
eidos::{Eidos, domains::FALCON_PUBLIC_KEY},
poseidon2::Poseidon2,
},
utils::Serializable,
};
mod data;
#[test]
fn eidos_and_poseidon2_falcon_coexist() {
let mut eidos_rng = ChaCha20Rng::from_seed([3_u8; 32]);
let eidos_public_key = SecretKey::with_rng(&mut eidos_rng).public_key();
let elements = eidos_public_key.to_elements();
let commitment = eidos_public_key.to_commitment();
assert_eq!(commitment, Eidos::hash_elements_in_domain(&elements, FALCON_PUBLIC_KEY));
assert_ne!(commitment, Poseidon2::hash_elements(&elements));
let mut poseidon2_rng = ChaCha20Rng::from_seed([5_u8; 32]);
let poseidon2_public_key =
falcon512_poseidon2::SecretKey::with_rng(&mut poseidon2_rng).public_key();
assert_eq!(
poseidon2_public_key.to_commitment(),
Poseidon2::hash_elements(&poseidon2_public_key.to_elements())
);
}
#[test]
fn eidos_public_key_commitment_is_frozen() {
let public_key = PublicKey::from(Polynomial::new(
(0..N).map(|value| FalconFelt::new(value as i16)).collect(),
));
assert_eq!(
public_key.to_commitment().into_elements().map(|value| value.as_canonical_u64()),
[
1_503_226_522_383_133_554,
9_124_963_703_222_507_410,
4_170_264_538_401_487_428,
5_215_050_092_812_883_080,
],
);
}
#[test]
fn test_signature_gen_reference_impl() {
let message = b"data1";
let seed = b"external";
let mut rng_shake = Shake256Testing::new(seed);
rng_shake.sync_rng(&SYNC_DATA);
for i in 0..NUM_TEST_VECTORS {
let [f, g, big_f, big_g] = SK_POLYS[i];
let f = Polynomial::new(f.to_vec());
let g = Polynomial::new(g.to_vec());
let big_f = Polynomial::new(big_f.to_vec());
let big_g = Polynomial::new(big_g.to_vec());
let sk = SecretKey::from_short_lattice_basis([g, f, big_g, big_f]);
let skip_bytes = SYNC_DATA_FOR_TEST_VECTOR[i].0 * 8;
let mut dummy = vec![0_u8; skip_bytes];
rng_shake.fill_bytes(&mut dummy);
let signature = sk.sign_with_rng_testing(message, &mut rng_shake);
let sig_coef = signature.sig_poly().to_balanced_values();
assert_eq!(sig_coef, EXPECTED_SIG_POLYS[i]);
let sig_bytes = &signature.to_bytes();
let expected_sig_bytes = EXPECTED_SIG[i];
let hex_expected_sig_bytes = hex::decode(expected_sig_bytes).unwrap();
let nonce = signature.nonce();
assert_eq!(hex_expected_sig_bytes[1..1 + SIG_NONCE_LEN], nonce.as_bytes());
assert_eq!(
&hex_expected_sig_bytes[1 + SIG_NONCE_LEN..],
&sig_bytes[2..2 + SIG_POLY_BYTE_LEN]
);
}
}
#[test]
fn test_secret_key_debug_redaction() {
let seed = [1_u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
let sk = SecretKey::with_rng(&mut rng);
let debug_output = format!("{sk:?}");
assert_eq!(debug_output, "<elided secret for SecretKey>");
let display_output = format!("{sk}");
assert_eq!(display_output, "<elided secret for SecretKey>");
}
#[test]
fn test_signature_determinism() {
let seed = [0_u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
let sk = SecretKey::with_rng(&mut rng);
let message = b"data";
let signature = sk.sign(message.into());
let serialized_signature = signature.to_bytes();
assert_eq!(serialized_signature, DETERMINISTIC_SIGNATURE);
assert!(sk.public_key().verify(message.into(), &signature));
}
#[test]
fn test_public_key_and_signature_display_hex() {
let seed = [0_u8; 32];
let mut rng = ChaCha20Rng::from_seed(seed);
let sk = SecretKey::with_rng(&mut rng);
let pk = sk.public_key();
let signature = sk.sign(b"data".into());
assert_eq!(pk.to_string(), canonical_hex(&(&pk).to_bytes()));
assert_eq!(signature.to_string(), canonical_hex(&signature.to_bytes()));
assert!(pk.to_string().starts_with("0x"));
assert!(signature.to_string().starts_with("0x"));
}
fn canonical_hex(bytes: &[u8]) -> String {
let mut s = String::from("0x");
for byte in bytes {
s.push_str(&format!("{byte:02x}"));
}
s
}
#[test]
fn check_preversioned_fixed_nonce() {
assert_eq!(build_preversioned_fixed_nonce(), PREVERSIONED_NONCE)
}
fn build_preversioned_fixed_nonce() -> [u8; PREVERSIONED_NONCE_LEN] {
use crate::dsa::falcon512_eidos::LOG_N;
let mut result = [0_u8; 39];
result[0] = LOG_N;
let domain_separator = b"FALCON-EIDOS-DET";
result
.iter_mut()
.skip(1)
.zip(domain_separator.iter())
.for_each(|(dst, src)| *dst = *src);
result
}