From 3becc4280884574c4da61ba9e455fca66076f8ab Mon Sep 17 00:00:00 2001 From: Tibo-lg Date: Wed, 7 Apr 2021 17:11:00 +0900 Subject: [PATCH] Add ecdsa adaptor support in rust-secp256k1-zkp --- src/lib.rs | 3 + src/zkp/ecdsa_adaptor.rs | 205 +++++++++++++++++++++++++++++++++++++++ src/zkp/mod.rs | 2 + 3 files changed, 210 insertions(+) create mode 100644 src/zkp/ecdsa_adaptor.rs diff --git a/src/lib.rs b/src/lib.rs index a075119d..3df8e231 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,6 +95,8 @@ pub enum Error { InvalidRangeProof, /// Bad generator InvalidGenerator, + /// Bad AdaptorSignature + InvalidEcdsaAdaptorSignature, } // Passthrough Debug to Display, since errors should be user-visible @@ -107,6 +109,7 @@ impl fmt::Display for Error { Error::CannotMakeRangeProof => "failed to generate range proof", Error::InvalidRangeProof => "failed to verify range proof", Error::InvalidGenerator => "malformed generator", + Error::InvalidEcdsaAdaptorSignature => "malformed ecdsa adaptor signature", Error::Upstream(inner) => return write!(f, "{}", inner), }; diff --git a/src/zkp/ecdsa_adaptor.rs b/src/zkp/ecdsa_adaptor.rs new file mode 100644 index 00000000..1e935f76 --- /dev/null +++ b/src/zkp/ecdsa_adaptor.rs @@ -0,0 +1,205 @@ +//! # ECDSA Adaptor +//! Support for ECDSA based adaptor signatures. +//! + +use core::{fmt, ptr, str}; +use ffi::{self, CPtr, ECDSA_ADAPTOR_SIGNATURE_LENGTH}; +use {constants, PublicKey, Secp256k1, SecretKey}; +use {from_hex, Error, UpstreamError}; +use {Message, Signing}; +use {Signature, Verification}; + +/// Represents an adaptor signature and dleq proof. +#[derive(Debug, PartialEq, Clone, Copy, Eq)] +pub struct EcdsaAdaptorSignature(ffi::EcdsaAdaptorSignature); + +impl fmt::LowerHex for EcdsaAdaptorSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for ch in self.0.to_bytes().iter() { + write!(f, "{:02x}", ch)?; + } + Ok(()) + } +} + +impl fmt::Display for EcdsaAdaptorSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(self, f) + } +} + +impl str::FromStr for EcdsaAdaptorSignature { + type Err = Error; + fn from_str(s: &str) -> Result { + let mut res = [0; ECDSA_ADAPTOR_SIGNATURE_LENGTH]; + match from_hex(s, &mut res) { + Ok(ECDSA_ADAPTOR_SIGNATURE_LENGTH) => { + EcdsaAdaptorSignature::from_slice(&res[0..ECDSA_ADAPTOR_SIGNATURE_LENGTH]) + } + _ => Err(Error::InvalidEcdsaAdaptorSignature), + } + } +} + +impl CPtr for EcdsaAdaptorSignature { + type Target = ffi::EcdsaAdaptorSignature; + 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() + } +} + +impl EcdsaAdaptorSignature { + /// Creates an AdaptorSignature directly from a slice + #[inline] + pub fn from_slice(data: &[u8]) -> Result { + match data.len() { + ECDSA_ADAPTOR_SIGNATURE_LENGTH => { + let mut ret = [0; ECDSA_ADAPTOR_SIGNATURE_LENGTH]; + ret[..].copy_from_slice(data); + Ok(EcdsaAdaptorSignature(ffi::EcdsaAdaptorSignature::from(ret))) + } + _ => Err(Error::InvalidEcdsaAdaptorSignature), + } + } + + /// Obtains a raw const pointer suitable for use with FFI functions + #[inline] + pub fn as_ptr(&self) -> *const ffi::EcdsaAdaptorSignature { + &self.0 + } + + /// Obtains a raw mutable pointer suitable for use with FFI functions + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut ffi::EcdsaAdaptorSignature { + &mut self.0 + } +} + +impl EcdsaAdaptorSignature { + /// Creates an adaptor signature along with a proof to verify the adaptor signature. + pub fn adaptor_encrypt( + secp: &Secp256k1, + msg: &Message, + sk: &SecretKey, + enckey: &PublicKey, + ) -> EcdsaAdaptorSignature { + let mut adaptor_sig = ffi::EcdsaAdaptorSignature::new(); + + unsafe { + assert_eq!( + 1, + ffi::secp256k1_ecdsa_adaptor_encrypt( + *secp.ctx(), + &mut adaptor_sig, + sk.as_c_ptr(), + enckey.as_c_ptr(), + msg.as_c_ptr(), + None, + ptr::null_mut(), + ) + ); + } + + EcdsaAdaptorSignature(adaptor_sig) + } + + /// Creates an ECDSA signature from an adaptor signature and an adaptor secret. + pub fn adaptor_decrypt(&self, decryption_key: &SecretKey) -> Signature { + unsafe { + let mut signature = ffi::Signature::new(); + assert_eq!( + 1, + ffi::secp256k1_ecdsa_adaptor_decrypt( + ffi::secp256k1_context_no_precomp, + &mut signature, + decryption_key.as_c_ptr(), + self.as_c_ptr(), + ) + ); + Signature::from(signature) + } + } + + /// Extracts the adaptor secret from the complete signature and the adaptor signature. + pub fn adaptor_recover( + &self, + secp: &Secp256k1, + sig: &Signature, + encryption_key: &PublicKey, + ) -> Result { + let mut data: [u8; constants::SECRET_KEY_SIZE] = [0; constants::SECRET_KEY_SIZE]; + + unsafe { + assert_eq!( + 1, + ffi::secp256k1_ecdsa_adaptor_recover( + *secp.ctx(), + data.as_mut_c_ptr(), + sig.as_c_ptr(), + self.as_c_ptr(), + encryption_key.as_c_ptr() + ) + ); + } + + Ok(SecretKey::from_slice(&data)?) + } + + /// Verifies that the adaptor secret can be extracted from the adaptor signature and the completed ECDSA signature. + pub fn adaptor_verify( + &self, + secp: &Secp256k1, + msg: &Message, + pubkey: &PublicKey, + encryption_key: &PublicKey, + ) -> Result<(), Error> { + unsafe { + let res = ffi::secp256k1_ecdsa_adaptor_verify( + *secp.ctx(), + self.as_c_ptr(), + pubkey.as_c_ptr(), + msg.as_c_ptr(), + encryption_key.as_c_ptr(), + ); + return if res == 1 { + Ok(()) + } else { + Err(Error::Upstream(UpstreamError::IncorrectSignature)) + }; + } + } +} + +#[cfg(all(test, feature = "global-context"))] +mod tests { + use super::*; + use super::{Message, Secp256k1}; + use rand::thread_rng; + + #[test] + fn test_ecdsa_adaptor_signature() { + let secp = Secp256k1::new(); + + let (seckey, pubkey) = secp.generate_keypair(&mut thread_rng()); + let (adaptor_secret, adaptor) = secp.generate_keypair(&mut thread_rng()); + let msg = Message::from_slice(&[2u8; 32]).unwrap(); + let adaptor_sig = EcdsaAdaptorSignature::adaptor_encrypt(&secp, &msg, &seckey, &adaptor); + + assert!(adaptor_sig + .adaptor_verify(&secp, &msg, &pubkey, &adaptor) + .is_ok()); + assert!(adaptor_sig + .adaptor_verify(&secp, &msg, &adaptor, &pubkey) + .is_err()); + let sig = adaptor_sig.adaptor_decrypt(&adaptor_secret); + assert!(secp.verify(&msg, &sig, &pubkey).is_ok()); + let recovered_res = adaptor_sig.adaptor_recover(&secp, &sig, &adaptor); + assert!(recovered_res.is_ok()); + let recovered = recovered_res.unwrap(); + assert_eq!(adaptor_secret, recovered); + } +} diff --git a/src/zkp/mod.rs b/src/zkp/mod.rs index b9a08cdb..ec800fc4 100644 --- a/src/zkp/mod.rs +++ b/src/zkp/mod.rs @@ -1,3 +1,4 @@ +mod ecdsa_adaptor; mod generator; #[cfg(feature = "std")] mod pedersen; @@ -7,6 +8,7 @@ mod rangeproof; mod surjection_proof; mod tag; +pub use self::ecdsa_adaptor::*; pub use self::generator::*; #[cfg(feature = "std")] pub use self::pedersen::*;