Skip to content

Commit

Permalink
Add ecdsa adaptor support in rust-secp256k1-zkp
Browse files Browse the repository at this point in the history
  • Loading branch information
Tibo-lg committed Apr 7, 2021
1 parent 02eeefd commit 3becc42
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub enum Error {
InvalidRangeProof,
/// Bad generator
InvalidGenerator,
/// Bad AdaptorSignature
InvalidEcdsaAdaptorSignature,
}

// Passthrough Debug to Display, since errors should be user-visible
Expand All @@ -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),
};

Expand Down
205 changes: 205 additions & 0 deletions src/zkp/ecdsa_adaptor.rs
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);
}
}
2 changes: 2 additions & 0 deletions src/zkp/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod ecdsa_adaptor;
mod generator;
#[cfg(feature = "std")]
mod pedersen;
Expand All @@ -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::*;
Expand Down

0 comments on commit 3becc42

Please sign in to comment.