-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ecdsa adaptor support in rust-secp256k1-zkp
- Loading branch information
Showing
3 changed files
with
210 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<EcdsaAdaptorSignature, Error> { | ||
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<EcdsaAdaptorSignature, Error> { | ||
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<C: Signing>( | ||
secp: &Secp256k1<C>, | ||
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<C: Signing>( | ||
&self, | ||
secp: &Secp256k1<C>, | ||
sig: &Signature, | ||
encryption_key: &PublicKey, | ||
) -> Result<SecretKey, Error> { | ||
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<C: Verification>( | ||
&self, | ||
secp: &Secp256k1<C>, | ||
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters