From bb3c4fe7ae8bbb2bef1b3872c9eb08f0bb2c06f0 Mon Sep 17 00:00:00 2001 From: Tibo-lg Date: Tue, 15 Sep 2020 10:46:19 +0900 Subject: [PATCH] Add BIP340 module --- src/bip340.rs | 462 +++++++++++++++++++++++++++++++++++++++++++++++ src/constants.rs | 9 + src/lib.rs | 1 + 3 files changed, 472 insertions(+) create mode 100644 src/bip340.rs diff --git a/src/bip340.rs b/src/bip340.rs new file mode 100644 index 000000000..03c8a2b18 --- /dev/null +++ b/src/bip340.rs @@ -0,0 +1,462 @@ +//! # BIP340sig +//! Support for BIP340 signatures. +//! + +#[cfg(any(test, feature = "rand"))] +use rand::Rng; + +use super::Error::{InvalidPublicKey, InvalidSignature, InvalidSecretKey}; +use super::{from_hex, Error}; +use core::{fmt, str}; +use ffi::{self, CPtr}; +use {constants, Secp256k1, SecretKey}; +use {Message, Signing}; + +/// Represents a BIP340 signature. +pub struct Signature([u8; constants::BIP340_SIGNATURE_SIZE]); +impl_array_newtype!(Signature, u8, constants::BIP340_SIGNATURE_SIZE); +impl_pretty_debug!(Signature); + +impl fmt::LowerHex for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in &self.0[..] { + write!(f, "{:02x}", ch)?; + } + Ok(()) + } +} + +impl fmt::Display for Signature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for Signature { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::BIP340_SIGNATURE_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::BIP340_SIGNATURE_SIZE) => { + Signature::from_slice(&res[0..constants::BIP340_SIGNATURE_SIZE]) + } + _ => Err(Error::InvalidSignature), + } + } +} + +/// A Secp256k1 public key, used for verification of signatures +#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)] +pub struct PublicKey(ffi::XOnlyPublicKey); + +impl fmt::LowerHex for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let ser = self.serialize(); + for ch in &ser[..] { + write!(f, "{:02x}", *ch)?; + } + Ok(()) + } +} + +impl fmt::Display for PublicKey { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for PublicKey { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; constants::BIP340_PUBLIC_KEY_SIZE]; + match from_hex(s, &mut res) { + Ok(constants::BIP340_PUBLIC_KEY_SIZE) => { + PublicKey::from_slice(&res[0..constants::BIP340_PUBLIC_KEY_SIZE]) + } + _ => Err(InvalidPublicKey), + } + } +} + +impl Signature { + /// Creates a Signature directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + match data.len() { + constants::BIP340_SIGNATURE_SIZE => { + let mut ret = [0; constants::BIP340_SIGNATURE_SIZE]; + ret[..].copy_from_slice(data); + Ok(Signature(ret)) + } + _ => Err(InvalidSignature), + } + } +} + +impl PublicKey { + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey { + &self.0 as *const _ + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut ffi::XOnlyPublicKey { + &mut self.0 as *mut _ + } + + /// Creates a new BIP340 public key from a secret key. + #[inline] + pub fn from_secret_key(secp: &Secp256k1, sk: &SecretKey) -> PublicKey { + let mut keypair = ffi::KeyPair::new(); + let mut xonly_pk = ffi::XOnlyPublicKey::new(); + let mut pk_parity = 0; + unsafe { + let mut ret = ffi::secp256k1_keypair_create(secp.ctx, &mut keypair, sk.as_c_ptr()); + debug_assert_eq!(ret, 1); + ret = + ffi::secp256k1_keypair_xonly_pub(secp.ctx, &mut xonly_pk, &mut pk_parity, &keypair); + debug_assert_eq!(ret, 1); + } + PublicKey(xonly_pk) + } + + /// Creates a BIP340 public key directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + if data.is_empty() || data.len() != constants::BIP340_PUBLIC_KEY_SIZE { + return Err(InvalidPublicKey); + } + + let mut pk = ffi::XOnlyPublicKey::new(); + unsafe { + if ffi::secp256k1_xonly_pubkey_parse( + ffi::secp256k1_context_no_precomp, + &mut pk, + data.as_c_ptr(), + ) == 1 + { + Ok(PublicKey(pk)) + } else { + Err(InvalidPublicKey) + } + } + } + + #[inline] + /// Serialize the key as a byte-encoded pair of values. In compressed form + /// the y-coordinate is represented by only a single bit, as x determines + /// it up to one bit. + pub fn serialize(&self) -> [u8; constants::BIP340_PUBLIC_KEY_SIZE] { + let mut ret = [0; constants::BIP340_PUBLIC_KEY_SIZE]; + + unsafe { + let err = ffi::secp256k1_xonly_pubkey_serialize( + ffi::secp256k1_context_no_precomp, + ret.as_mut_c_ptr(), + self.as_c_ptr(), + ); + debug_assert_eq!(err, 1); + } + ret + } +} + +impl CPtr for PublicKey { + type Target = ffi::XOnlyPublicKey; + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +/// Creates a new BIP340 public key from a FFI x-only public key +impl From for PublicKey { + #[inline] + fn from(pk: ffi::XOnlyPublicKey) -> PublicKey { + PublicKey(pk) + } +} + +serde_impl_from_slice!(PublicKey); + +impl Secp256k1 { + /// Create a BIP340 signature. + pub fn bip340_sign( + &self, + msg: &Message, + sk: &SecretKey, + aux_rand: &[u8; 32], + ) -> Result { + unsafe { + let mut keypair = ffi::KeyPair::new(); + let ret = ffi::secp256k1_keypair_create(self.ctx, &mut keypair, sk.as_c_ptr()); + if ret == 0 { + return Err(InvalidSecretKey); + } + + let mut sig = [0u8; constants::BIP340_SIGNATURE_SIZE]; + assert_eq!( + 1, + ffi::secp256k1_schnorrsig_sign( + self.ctx, + sig.as_mut_c_ptr(), + msg.as_c_ptr(), + &keypair, + ffi::secp256k1_nonce_function_bip340, + aux_rand.as_c_ptr() as *const ffi::types::c_void + ) + ); + + Ok(Signature(sig)) + } + } + + /// Verify a BIP340 signature. + pub fn bip340_verify( + &self, + sig: &Signature, + msg: &Message, + pubkey: &PublicKey, + ) -> Result<(), Error> { + unsafe { + let ret = ffi::secp256k1_schnorrsig_verify( + self.ctx, + sig.as_c_ptr(), + msg.as_c_ptr(), + pubkey.as_c_ptr(), + ); + + return if ret == 1 { + Ok(()) + } else { + Err(Error::InvalidSignature) + }; + } + } + + /// Generates a random BIP340 keypair. Convenience function for `bip340::SecretKey::new` + /// and `bip340::PublicKey::from_secret_key`; call those functions directly for + /// batch key generation. Requires a signing-capable context. Requires compilation + /// with the "rand" feature. + #[inline] + #[cfg(any(test, feature = "rand"))] + pub fn generate_bip340_keypair( + &self, + rng: &mut R, + ) -> (SecretKey, PublicKey) { + let sk = SecretKey::new(rng); + let pubkey = PublicKey::from_secret_key(self, &sk); + (sk, pubkey) + } +} + +#[cfg(test)] +mod tests { + use super::super::Error::InvalidPublicKey; + use super::super::{constants, from_hex, Message, Secp256k1, SecretKey}; + use super::{PublicKey, Signature}; + use rand::{thread_rng, Error, ErrorKind, RngCore}; + use rand_core::impls; + use std::iter; + use std::str::FromStr; + + macro_rules! hex_32 { + ($hex:expr) => {{ + let mut result = [0; 32]; + from_hex($hex, &mut result).expect("valid hex string"); + result + }}; + } + + #[test] + fn test_bip340() { + let secp = Secp256k1::new(); + + let mut rng = thread_rng(); + let (seckey, pubkey) = secp.generate_bip340_keypair(&mut rng); + let mut aux_rand = [0; 32]; + let mut msg = [0; 32]; + + for _ in 0..100 { + rng.fill_bytes(&mut aux_rand); + rng.fill_bytes(&mut msg); + let msg = Message::from_slice(&msg).unwrap(); + + let sig = secp.bip340_sign(&msg, &seckey, &aux_rand).unwrap(); + + assert!(secp.bip340_verify(&sig, &msg, &pubkey).is_ok()); + } + } + + #[test] + fn test_bip340_sign() { + let secp = Secp256k1::new(); + + let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sk = + SecretKey::from_str("688C77BC2D5AAFF5491CF309D4753B732135470D05B7B2CD21ADD0744FE97BEF") + .unwrap(); + let aux_rand: [u8; 32] = + hex_32!("02CCE08E913F22A36C5648D6405A2C7C50106E7AA2F1649E381C7F09D16B80AB"); + let expected_sig = Signature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); + + let sig = secp.bip340_sign(&msg, &sk, &aux_rand).unwrap(); + + assert_eq!(expected_sig, sig); + } + + #[test] + fn test_bip340_verify() { + let secp = Secp256k1::new(); + + let hex_msg = hex_32!("E48441762FB75010B2AA31A512B62B4148AA3FB08EB0765D76B252559064A614"); + let msg = Message::from_slice(&hex_msg).unwrap(); + let sig = Signature::from_str("6470FD1303DDA4FDA717B9837153C24A6EAB377183FC438F939E0ED2B620E9EE5077C4A8B8DCA28963D772A94F5F0DDF598E1C47C137F91933274C7C3EDADCE8").unwrap(); + let pubkey = PublicKey::from_str( + "B33CC9EDC096D0A83416964BD3C6247B8FECD256E4EFA7870D2C854BDEB33390", + ) + .unwrap(); + + assert!(secp.bip340_verify(&sig, &msg, &pubkey).is_ok()); + } + + #[test] + fn pubkey_from_slice() { + assert_eq!( + PublicKey::from_slice(&[]), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice(&[1, 2, 3]), + Err(InvalidPublicKey) + ); + let pk = PublicKey::from_slice(&[ + 0xB3, 0x3C, 0xC9, 0xED, 0xC0, 0x96, 0xD0, 0xA8, 0x34, 0x16, 0x96, 0x4B, 0xD3, 0xC6, + 0x24, 0x7B, 0x8F, 0xEC, 0xD2, 0x56, 0xE4, 0xEF, 0xA7, 0x87, 0x0D, 0x2C, 0x85, 0x4B, + 0xDE, 0xB3, 0x33, 0x90, + ]); + assert!(pk.is_ok()); + } + + #[test] + fn pubkey_serialize_roundtrip() { + let secp = Secp256k1::new(); + let (_, pubkey) = secp.generate_bip340_keypair(&mut thread_rng()); + let ser = pubkey.serialize(); + let pubkey2 = PublicKey::from_slice(&ser).unwrap(); + assert_eq!(pubkey, pubkey2); + } + + #[test] + fn test_pubkey_from_bad_slice() { + // Bad sizes + assert_eq!( + PublicKey::from_slice(&[0; constants::BIP340_PUBLIC_KEY_SIZE - 1]), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice(&[0; constants::BIP340_PUBLIC_KEY_SIZE + 1]), + Err(InvalidPublicKey) + ); + + // Bad parse + assert_eq!( + PublicKey::from_slice(&[0xff; constants::BIP340_PUBLIC_KEY_SIZE]), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice(&[0x55; constants::BIP340_PUBLIC_KEY_SIZE]), + Err(InvalidPublicKey) + ); + assert_eq!( + PublicKey::from_slice(&[]), + Err(InvalidPublicKey) + ); + } + + #[test] + fn test_pubkey_display_output() { + static SK_BYTES: [u8; 32] = [ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x63, + ]; + + let s = Secp256k1::signing_only(); + let sk = SecretKey::from_slice(&SK_BYTES).expect("sk"); + let pk = PublicKey::from_secret_key(&s, &sk); + + assert_eq!( + pk.to_string(), + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + ); + assert_eq!( + PublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166" + ) + .unwrap(), + pk + ); + + assert!(PublicKey::from_str( + "00000000000000000000000000000000000000000000000000000000000000000" + ) + .is_err()); + assert!(PublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16601" + ) + .is_err()); + assert!(PublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd16" + ) + .is_err()); + assert!(PublicKey::from_str( + "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + assert!(PublicKey::from_str( + "xx18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd1" + ) + .is_err()); + + let long_str: String = iter::repeat('a').take(1024 * 1024).collect(); + assert!(PublicKey::from_str(&long_str).is_err()); + } + + #[test] + fn test_pubkey_serialize() { + struct DumbRng(u32); + impl RngCore for DumbRng { + fn next_u32(&mut self) -> u32 { + self.0 = self.0.wrapping_add(1); + self.0 + } + fn next_u64(&mut self) -> u64 { + self.next_u32() as u64 + } + fn try_fill_bytes(&mut self, _dest: &mut [u8]) -> Result<(), Error> { + Err(Error::new(ErrorKind::Unavailable, "not implemented")) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + impls::fill_bytes_via_next(self, dest); + } + } + + let s = Secp256k1::new(); + let (_, pubkey) = s.generate_bip340_keypair(&mut DumbRng(0)); + assert_eq!( + &pubkey.serialize()[..], + &[ + 124, 121, 49, 14, 253, 63, 197, 50, 39, 194, 107, 17, 193, 219, 108, 154, 126, 9, + 181, 248, 2, 12, 149, 233, 198, 71, 149, 134, 250, 184, 154, 229 + ][..] + ); + } +} diff --git a/src/constants.rs b/src/constants.rs index cb185e4fc..73dd19b0b 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -34,6 +34,15 @@ pub const MAX_SIGNATURE_SIZE: usize = 72; /// The maximum size of a compact signature pub const COMPACT_SIGNATURE_SIZE: usize = 64; +/// Size of a BIP340 signature +pub const BIP340_SIGNATURE_SIZE: usize = 64; + +/// Size of a x-only public key +pub const BIP340_PUBLIC_KEY_SIZE: usize = 32; + +/// Size of a key pair +pub const KEY_PAIR_SIZE: usize = 96; + /// The Prime for the secp256k1 field element. pub const FIELD_SIZE: [u8; 32] = [ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, diff --git a/src/lib.rs b/src/lib.rs index 4d887a0c1..9b4d44174 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,6 +169,7 @@ mod context; pub mod constants; pub mod ecdh; pub mod key; +pub mod bip340; #[cfg(feature = "recovery")] pub mod recovery;