diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 7e1a1ad..fef52ef 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -11,6 +11,7 @@ program-methods = { path = "program_methods" } serde = "1.0.219" sha2 = "0.10.9" secp256k1 = "0.31.1" +rand = "0.8" [dev-dependencies] test-program-methods = { path = "test_program_methods" } diff --git a/nssa/src/signature/mod.rs b/nssa/src/signature/mod.rs index 13a0fe3..1204ed5 100644 --- a/nssa/src/signature/mod.rs +++ b/nssa/src/signature/mod.rs @@ -1,8 +1,76 @@ +mod encoding; mod private_key; mod public_key; mod signature; -mod encoding; pub use private_key::PrivateKey; pub use public_key::PublicKey; pub use signature::Signature; + +#[cfg(test)] +mod tests { + use crate::{PrivateKey, PublicKey, Signature}; + + fn hex_to_bytes(hex: &str) -> [u8; N] { + hex::decode(hex).unwrap().try_into().unwrap() + } + + pub struct TestVector { + pub seckey: Option, + pub pubkey: PublicKey, + pub aux_rand: Option<[u8; 32]>, + pub message: Option>, + pub signature: Signature, + pub verification_result: bool, + } + + /// Test vectors from + /// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv + // + pub fn test_vectors() -> Vec { + vec![ + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "0000000000000000000000000000000000000000000000000000000000000003", + ))), + pubkey: PublicKey(hex_to_bytes( + "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000000", + )), + message: Some( + hex::decode("0000000000000000000000000000000000000000000000000000000000000000") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", + ), + }, + verification_result: true, + }, + TestVector { + seckey: Some(PrivateKey(hex_to_bytes( + "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", + ))), + pubkey: PublicKey(hex_to_bytes( + "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", + )), + aux_rand: Some(hex_to_bytes::<32>( + "0000000000000000000000000000000000000000000000000000000000000001", + )), + message: Some( + hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89") + .unwrap(), + ), + signature: Signature { + value: hex_to_bytes( + "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", + ), + }, + verification_result: true, + }, + ] + } +} diff --git a/nssa/src/signature/public_key.rs b/nssa/src/signature/public_key.rs index 830f6b1..0ba4b44 100644 --- a/nssa/src/signature/public_key.rs +++ b/nssa/src/signature/public_key.rs @@ -23,45 +23,21 @@ impl PublicKey { #[cfg(test)] mod tests { - use crate::{PrivateKey, PublicKey}; - fn hex_to_32_bytes(hex: &str) -> [u8; 32] { - hex::decode(hex).unwrap().try_into().unwrap() - } - - /// Test vectors from - /// https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv - const BIP340_PK_TEST_VECTORS: [(&str, &str); 5] = [ - ( - "0000000000000000000000000000000000000000000000000000000000000003", - "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", - ), - ( - "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", - "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", - ), - ( - "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", - "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", - ), - ( - "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", - "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", - ), - ( - "0340034003400340034003400340034003400340034003400340034003400340", - "778CAA53B4393AC467774D09497A87224BF9FAB6F6E68B23086497324D6FD117", - ), - ]; + use crate::{PrivateKey, PublicKey, signature::tests::test_vectors}; #[test] - fn test_bip340_pk_test_vectors() { - for (i, (private_key_hex, expected_public_key_hex)) in - BIP340_PK_TEST_VECTORS.iter().enumerate() - { - let key = PrivateKey::try_new(hex_to_32_bytes(private_key_hex)).unwrap(); - let public_key = PublicKey::new(&key); - let expected_public_key = PublicKey(hex_to_32_bytes(expected_public_key_hex)); - assert_eq!(public_key, expected_public_key, "Failed test vector at index {i}"); + fn test_public_key_generation_from_bip340_test_vectors() { + for (i, test_vector) in test_vectors().iter().enumerate() { + let Some(private_key) = &test_vector.seckey else { + continue; + }; + let public_key = PublicKey::new(private_key); + let expected_public_key = &test_vector.pubkey; + assert_eq!( + &public_key, expected_public_key, + "Failed test vector at index {i}" + ); } } + } diff --git a/nssa/src/signature/signature.rs b/nssa/src/signature/signature.rs index 8e4756d..0f956af 100644 --- a/nssa/src/signature/signature.rs +++ b/nssa/src/signature/signature.rs @@ -1,5 +1,7 @@ use std::io::{Cursor, Read}; +use rand::{rngs::OsRng, RngCore}; + use crate::{PrivateKey, PublicKey, error::NssaError, public_transaction::Message}; #[derive(Debug, Clone, PartialEq, Eq)] @@ -9,11 +11,17 @@ pub struct Signature { impl Signature { pub(crate) fn new(key: &PrivateKey, message: &[u8]) -> Self { + let mut aux_random = [0u8; 32]; + OsRng.fill_bytes(&mut aux_random); + Self::new_with_aux_random(key, message, aux_random) + } + + fn new_with_aux_random(key: &PrivateKey, message: &[u8], aux_random: [u8; 32]) -> Self { let value = { let secp = secp256k1::Secp256k1::new(); let secret_key = secp256k1::SecretKey::from_byte_array(key.0).unwrap(); let keypair = secp256k1::Keypair::from_secret_key(&secp, &secret_key); - let signature = secp.sign_schnorr_no_aux_rand(message, &keypair); + let signature = secp.sign_schnorr_with_aux_rand(message, &keypair, &aux_random); signature.to_byte_array() }; Self { value } @@ -27,3 +35,44 @@ impl Signature { } } +#[cfg(test)] +mod tests { + use crate::{Signature, signature::tests::test_vectors}; + + #[test] + fn test_signature_generation_from_bip340_test_vectors() { + for (i, test_vector) in test_vectors().into_iter().enumerate() { + let Some(private_key) = test_vector.seckey else { + continue; + }; + let Some(aux_random) = test_vector.aux_rand else { + continue; + }; + let Some(message) = test_vector.message else { + continue; + }; + if !test_vector.verification_result { + continue; + } + let expected_signature = &test_vector.signature; + + let signature = Signature::new_with_aux_random(&private_key, &message, aux_random); + + assert_eq!(&signature, expected_signature, "Failed test vector {i}"); + } + } + + #[test] + fn test_signature_verification_from_bip340_test_vectors() { + for (i, test_vector) in test_vectors().into_iter().enumerate() { + let message = test_vector.message.unwrap_or(vec![]); + let expected_result = test_vector.verification_result; + + let result = test_vector + .signature + .is_valid_for(&message, &test_vector.pubkey); + + assert_eq!(result, expected_result, "Failed test vector {i}"); + } + } +}