Fix for upstream API changes; add ECDH support
I didn't mean for both of these to go into the same commit, but given how small the ECDH code was, and the fact that no commit prior to this one will compile (as both libsecp256k1 and rustc have changed so much), I'm letting it slide.
This commit is contained in:
parent
8d6f384dac
commit
016d781f2e
|
@ -9,7 +9,7 @@ matrix:
|
||||||
install:
|
install:
|
||||||
- git clone https://github.com/bitcoin/secp256k1.git
|
- git clone https://github.com/bitcoin/secp256k1.git
|
||||||
- cd secp256k1
|
- cd secp256k1
|
||||||
- ./autogen.sh && ./configure && make && sudo make install
|
- ./autogen.sh && ./configure --enable-module-ecdh --enable-module-recovery && make && sudo make install
|
||||||
- sudo ldconfig /usr/local/lib
|
- sudo ldconfig /usr/local/lib
|
||||||
- cd ..
|
- cd ..
|
||||||
- |
|
- |
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
// Bitcoin secp256k1 bindings
|
||||||
|
// Written in 2015 by
|
||||||
|
// Andrew Poelstra
|
||||||
|
//
|
||||||
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
|
// copyright and related and neighboring rights to this software to
|
||||||
|
// the public domain worldwide. This software is distributed without
|
||||||
|
// any warranty.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the CC0 Public Domain Dedication
|
||||||
|
// along with this software.
|
||||||
|
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
//! # ECDH
|
||||||
|
//! Support for shared secret computations
|
||||||
|
//!
|
||||||
|
|
||||||
|
use std::ops;
|
||||||
|
|
||||||
|
use super::Secp256k1;
|
||||||
|
use key::{SecretKey, PublicKey};
|
||||||
|
use ffi;
|
||||||
|
|
||||||
|
/// A tag used for recovering the public key from a compact signature
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct SharedSecret(ffi::SharedSecret);
|
||||||
|
|
||||||
|
impl SharedSecret {
|
||||||
|
/// Creates a new shared secret from a pubkey and secret key
|
||||||
|
#[inline]
|
||||||
|
pub fn new(secp: &Secp256k1, point: &PublicKey, scalar: &SecretKey) -> SharedSecret {
|
||||||
|
unsafe {
|
||||||
|
let mut ss = ffi::SharedSecret::blank();
|
||||||
|
let res = ffi::secp256k1_ecdh(secp.ctx, &mut ss, point.as_ptr(), scalar.as_ptr());
|
||||||
|
debug_assert_eq!(res, 1);
|
||||||
|
SharedSecret(ss)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new shared secret from a FFI shared secret
|
||||||
|
#[inline]
|
||||||
|
pub fn from_ffi(ss: ffi::SharedSecret) -> SharedSecret {
|
||||||
|
SharedSecret(ss)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a raw pointer suitable for use with FFI functions
|
||||||
|
#[inline]
|
||||||
|
pub fn as_ptr(&self) -> *const ffi::SharedSecret {
|
||||||
|
&self.0 as *const _
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<usize> for SharedSecret {
|
||||||
|
type Output = u8;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: usize) -> &u8 {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::Range<usize>> for SharedSecret {
|
||||||
|
type Output = [u8];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ops::Range<usize>) -> &[u8] {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::RangeFrom<usize>> for SharedSecret {
|
||||||
|
type Output = [u8];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, index: ops::RangeFrom<usize>) -> &[u8] {
|
||||||
|
&self.0[index.start..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Index<ops::RangeFull> for SharedSecret {
|
||||||
|
type Output = [u8];
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn index(&self, _: ops::RangeFull) -> &[u8] {
|
||||||
|
&self.0[..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use rand::thread_rng;
|
||||||
|
use super::SharedSecret;
|
||||||
|
use super::super::Secp256k1;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ecdh() {
|
||||||
|
let s = Secp256k1::with_caps(::ContextFlag::SignOnly);
|
||||||
|
let (sk1, pk1) = s.generate_keypair(&mut thread_rng()).unwrap();
|
||||||
|
let (sk2, pk2) = s.generate_keypair(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
|
let sec1 = SharedSecret::new(&s, &pk1, &sk2);
|
||||||
|
let sec2 = SharedSecret::new(&s, &pk2, &sk1);
|
||||||
|
let sec_odd = SharedSecret::new(&s, &pk1, &sk1);
|
||||||
|
assert_eq!(sec1, sec2);
|
||||||
|
assert!(sec_odd != sec2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "unstable"))]
|
||||||
|
mod benches {
|
||||||
|
use rand::{Rng, thread_rng};
|
||||||
|
use test::{Bencher, black_box};
|
||||||
|
|
||||||
|
use super::{Secp256k1, Message};
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
pub fn bench_ecdh(bh: &mut Bencher) {
|
||||||
|
let s = Secp256k1::with_caps(::ContextFlag::SignOnly);
|
||||||
|
let (sk, pk) = s.generate_keypair(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
|
let s = Secp256k1::new();
|
||||||
|
let mut r = CounterRng(0);
|
||||||
|
bh.iter( || {
|
||||||
|
let res = SharedSecret::new(&s, &pk, &sk);
|
||||||
|
black_box(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
102
src/ffi.rs
102
src/ffi.rs
|
@ -64,17 +64,45 @@ impl PublicKey {
|
||||||
/// Library-internal representation of a Secp256k1 signature
|
/// Library-internal representation of a Secp256k1 signature
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(raw_pointer_derive)]
|
#[allow(raw_pointer_derive)]
|
||||||
pub struct Signature([c_uchar; 65]);
|
pub struct Signature([c_uchar; 64]);
|
||||||
impl_array_newtype!(Signature, c_uchar, 65);
|
impl_array_newtype!(Signature, c_uchar, 64);
|
||||||
impl_raw_debug!(Signature);
|
impl_raw_debug!(Signature);
|
||||||
|
|
||||||
|
/// Library-internal representation of a Secp256k1 signature + recovery ID
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(raw_pointer_derive)]
|
||||||
|
pub struct RecoverableSignature([c_uchar; 65]);
|
||||||
|
impl_array_newtype!(RecoverableSignature, c_uchar, 65);
|
||||||
|
impl_raw_debug!(RecoverableSignature);
|
||||||
|
|
||||||
impl Signature {
|
impl Signature {
|
||||||
/// Create a new (zeroed) public key usable for the FFI interface
|
/// Create a new (zeroed) signature usable for the FFI interface
|
||||||
pub fn new() -> Signature { Signature([0; 65]) }
|
pub fn new() -> Signature { Signature([0; 64]) }
|
||||||
/// Create a new (uninitialized) public key usable for the FFI interface
|
/// Create a new (uninitialized) signature usable for the FFI interface
|
||||||
pub unsafe fn blank() -> Signature { mem::uninitialized() }
|
pub unsafe fn blank() -> Signature { mem::uninitialized() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RecoverableSignature {
|
||||||
|
/// Create a new (zeroed) signature usable for the FFI interface
|
||||||
|
pub fn new() -> RecoverableSignature { RecoverableSignature([0; 65]) }
|
||||||
|
/// Create a new (uninitialized) signature usable for the FFI interface
|
||||||
|
pub unsafe fn blank() -> RecoverableSignature { mem::uninitialized() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Library-internal representation of an ECDH shared secret
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(raw_pointer_derive)]
|
||||||
|
pub struct SharedSecret([c_uchar; 32]);
|
||||||
|
impl_array_newtype!(SharedSecret, c_uchar, 32);
|
||||||
|
impl_raw_debug!(SharedSecret);
|
||||||
|
|
||||||
|
impl SharedSecret {
|
||||||
|
/// Create a new (zeroed) signature usable for the FFI interface
|
||||||
|
pub fn new() -> SharedSecret { SharedSecret([0; 32]) }
|
||||||
|
/// Create a new (uninitialized) signature usable for the FFI interface
|
||||||
|
pub unsafe fn blank() -> SharedSecret { mem::uninitialized() }
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Send for Context {}
|
unsafe impl Send for Context {}
|
||||||
unsafe impl Sync for Context {}
|
unsafe impl Sync for Context {}
|
||||||
|
|
||||||
|
@ -95,6 +123,13 @@ extern "C" {
|
||||||
seed32: *const c_uchar)
|
seed32: *const c_uchar)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
|
// TODO secp256k1_context_set_illegal_callback
|
||||||
|
// TODO secp256k1_context_set_error_callback
|
||||||
|
// (Actually, I don't really want these exposed; if either of these
|
||||||
|
// are ever triggered it indicates a bug in rust-secp256k1, since
|
||||||
|
// one goal is to use Rust's type system to eliminate all possible
|
||||||
|
// bad inputs.)
|
||||||
|
|
||||||
// Pubkeys
|
// Pubkeys
|
||||||
pub fn secp256k1_ec_pubkey_parse(cx: Context, pk: *mut PublicKey,
|
pub fn secp256k1_ec_pubkey_parse(cx: Context, pk: *mut PublicKey,
|
||||||
input: *const c_uchar, in_len: c_int)
|
input: *const c_uchar, in_len: c_int)
|
||||||
|
@ -110,30 +145,49 @@ extern "C" {
|
||||||
input: *const c_uchar, in_len: c_int)
|
input: *const c_uchar, in_len: c_int)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
pub fn secp256k1_ecdsa_signature_parse_compact(cx: Context, sig: *mut Signature,
|
|
||||||
input64: *const c_uchar, recid: c_int)
|
|
||||||
-> c_int;
|
|
||||||
|
|
||||||
pub fn secp256k1_ecdsa_signature_serialize_der(cx: Context, output: *const c_uchar,
|
pub fn secp256k1_ecdsa_signature_serialize_der(cx: Context, output: *const c_uchar,
|
||||||
out_len: c_int, sig: *const Signature)
|
out_len: c_int, sig: *const Signature)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
pub fn secp256k1_ecdsa_signature_serialize_compact(cx: Context, output64: *const c_uchar,
|
pub fn secp256k1_ecdsa_recoverable_signature_parse_compact(cx: Context, sig: *mut RecoverableSignature,
|
||||||
recid: *mut c_int, sig: *const Signature)
|
input64: *const c_uchar, recid: c_int)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
|
pub fn secp256k1_ecdsa_recoverable_signature_serialize_compact(cx: Context, output64: *const c_uchar,
|
||||||
|
recid: *mut c_int, sig: *const RecoverableSignature)
|
||||||
|
-> c_int;
|
||||||
|
|
||||||
|
pub fn secp256k1_ecdsa_recoverable_signature_convert(cx: Context, sig: *mut Signature,
|
||||||
|
input: *const RecoverableSignature)
|
||||||
|
-> c_int;
|
||||||
|
|
||||||
// ECDSA
|
// ECDSA
|
||||||
pub fn secp256k1_ecdsa_verify(cx: Context, msg32: *const c_uchar,
|
pub fn secp256k1_ecdsa_verify(cx: Context,
|
||||||
sig: *const Signature, pk: *const PublicKey)
|
sig: *const Signature,
|
||||||
|
msg32: *const c_uchar,
|
||||||
|
pk: *const PublicKey)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
pub fn secp256k1_ecdsa_sign(cx: Context, msg32: *const c_uchar,
|
pub fn secp256k1_ecdsa_sign(cx: Context,
|
||||||
sig: *mut Signature, sk: *const c_uchar,
|
sig: *mut Signature,
|
||||||
noncefn: NonceFn, noncedata: *const c_void)
|
msg32: *const c_uchar,
|
||||||
|
sk: *const c_uchar,
|
||||||
|
noncefn: NonceFn,
|
||||||
|
noncedata: *const c_void)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
pub fn secp256k1_ecdsa_recover(cx: Context, msg32: *const c_uchar,
|
pub fn secp256k1_ecdsa_sign_recoverable(cx: Context,
|
||||||
sig: *const Signature, pk: *mut PublicKey)
|
sig: *mut RecoverableSignature,
|
||||||
|
msg32: *const c_uchar,
|
||||||
|
sk: *const c_uchar,
|
||||||
|
noncefn: NonceFn,
|
||||||
|
noncedata: *const c_void)
|
||||||
|
-> c_int;
|
||||||
|
|
||||||
|
pub fn secp256k1_ecdsa_recover(cx: Context,
|
||||||
|
pk: *mut PublicKey,
|
||||||
|
sig: *const RecoverableSignature,
|
||||||
|
msg32: *const c_uchar)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
// EC
|
// EC
|
||||||
|
@ -168,8 +222,14 @@ extern "C" {
|
||||||
|
|
||||||
pub fn secp256k1_ec_pubkey_combine(cx: Context,
|
pub fn secp256k1_ec_pubkey_combine(cx: Context,
|
||||||
out: *mut PublicKey,
|
out: *mut PublicKey,
|
||||||
n: c_int,
|
ins: *const *const PublicKey,
|
||||||
ins: *const *const PublicKey)
|
n: c_int)
|
||||||
-> c_int;
|
-> c_int;
|
||||||
|
|
||||||
|
pub fn secp256k1_ecdh(cx: Context,
|
||||||
|
out: *mut SharedSecret,
|
||||||
|
point: *const PublicKey,
|
||||||
|
scalar: *const c_uchar)
|
||||||
|
-> c_int;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
153
src/lib.rs
153
src/lib.rs
|
@ -50,6 +50,7 @@ use rand::Rng;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod ecdh;
|
||||||
pub mod ffi;
|
pub mod ffi;
|
||||||
pub mod key;
|
pub mod key;
|
||||||
|
|
||||||
|
@ -61,6 +62,10 @@ pub struct RecoveryId(i32);
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Signature(ffi::Signature);
|
pub struct Signature(ffi::Signature);
|
||||||
|
|
||||||
|
/// An ECDSA signature with a recovery ID for pubkey recovery
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct RecoverableSignature(ffi::RecoverableSignature);
|
||||||
|
|
||||||
impl Signature {
|
impl Signature {
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Converts a DER-encoded byte slice to a signature
|
/// Converts a DER-encoded byte slice to a signature
|
||||||
|
@ -77,25 +82,6 @@ impl Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
/// Converts a compact-encoded byte slice to a signature. This
|
|
||||||
/// representation is nonstandard and defined by the libsecp256k1
|
|
||||||
/// library.
|
|
||||||
pub fn from_compact(secp: &Secp256k1, data: &[u8], recid: RecoveryId) -> Result<Signature, Error> {
|
|
||||||
let mut ret = unsafe { ffi::Signature::blank() };
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if data.len() != 64 {
|
|
||||||
Err(Error::InvalidSignature)
|
|
||||||
} else if ffi::secp256k1_ecdsa_signature_parse_compact(secp.ctx, &mut ret,
|
|
||||||
data.as_ptr(), recid.0) == 1 {
|
|
||||||
Ok(Signature(ret))
|
|
||||||
} else {
|
|
||||||
Err(Error::InvalidSignature)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new public key from a FFI public key
|
/// Creates a new public key from a FFI public key
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_ffi(sig: ffi::Signature) -> Signature {
|
pub fn from_ffi(sig: ffi::Signature) -> Signature {
|
||||||
|
@ -109,6 +95,51 @@ impl Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RecoverableSignature {
|
||||||
|
#[inline]
|
||||||
|
/// Converts a compact-encoded byte slice to a signature. This
|
||||||
|
/// representation is nonstandard and defined by the libsecp256k1
|
||||||
|
/// library.
|
||||||
|
pub fn from_compact(secp: &Secp256k1, data: &[u8], recid: RecoveryId) -> Result<RecoverableSignature, Error> {
|
||||||
|
let mut ret = unsafe { ffi::RecoverableSignature::blank() };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if data.len() != 64 {
|
||||||
|
Err(Error::InvalidSignature)
|
||||||
|
} else if ffi::secp256k1_ecdsa_recoverable_signature_parse_compact(secp.ctx, &mut ret,
|
||||||
|
data.as_ptr(), recid.0) == 1 {
|
||||||
|
Ok(RecoverableSignature(ret))
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidSignature)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new public key from a FFI public key
|
||||||
|
#[inline]
|
||||||
|
pub fn from_ffi(sig: ffi::RecoverableSignature) -> RecoverableSignature {
|
||||||
|
RecoverableSignature(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtains a raw pointer suitable for use with FFI functions
|
||||||
|
#[inline]
|
||||||
|
pub fn as_ptr(&self) -> *const ffi::RecoverableSignature {
|
||||||
|
&self.0 as *const _
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts a recoverable signature to a non-recoverable one (this is needed
|
||||||
|
/// for verification
|
||||||
|
#[inline]
|
||||||
|
pub fn to_standard(&self, secp: &Secp256k1) -> Signature {
|
||||||
|
let mut ret = unsafe { ffi::Signature::blank() };
|
||||||
|
unsafe {
|
||||||
|
let err = ffi::secp256k1_ecdsa_recoverable_signature_convert(secp.ctx, &mut ret, self.as_ptr());
|
||||||
|
assert!(err == 1);
|
||||||
|
}
|
||||||
|
Signature(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ops::Index<usize> for Signature {
|
impl ops::Index<usize> for Signature {
|
||||||
type Output = u8;
|
type Output = u8;
|
||||||
|
|
||||||
|
@ -314,17 +345,36 @@ impl Secp256k1 {
|
||||||
unsafe {
|
unsafe {
|
||||||
// We can assume the return value because it's not possible to construct
|
// We can assume the return value because it's not possible to construct
|
||||||
// an invalid signature from a valid `Message` and `SecretKey`
|
// an invalid signature from a valid `Message` and `SecretKey`
|
||||||
assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, msg.as_ptr(), &mut ret,
|
assert_eq!(ffi::secp256k1_ecdsa_sign(self.ctx, &mut ret, msg.as_ptr(),
|
||||||
sk.as_ptr(), ffi::secp256k1_nonce_function_rfc6979,
|
sk.as_ptr(), ffi::secp256k1_nonce_function_rfc6979,
|
||||||
ptr::null()), 1);
|
ptr::null()), 1);
|
||||||
}
|
}
|
||||||
Ok(Signature::from_ffi(ret))
|
Ok(Signature::from_ffi(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce`.
|
||||||
|
/// Requires a signing-capable context.
|
||||||
|
pub fn sign_recoverable(&self, msg: &Message, sk: &key::SecretKey)
|
||||||
|
-> Result<RecoverableSignature, Error> {
|
||||||
|
if self.caps == ContextFlag::VerifyOnly || self.caps == ContextFlag::None {
|
||||||
|
return Err(Error::IncapableContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = unsafe { ffi::RecoverableSignature::blank() };
|
||||||
|
unsafe {
|
||||||
|
// We can assume the return value because it's not possible to construct
|
||||||
|
// an invalid signature from a valid `Message` and `SecretKey`
|
||||||
|
assert_eq!(ffi::secp256k1_ecdsa_sign_recoverable(self.ctx, &mut ret, msg.as_ptr(),
|
||||||
|
sk.as_ptr(), ffi::secp256k1_nonce_function_rfc6979,
|
||||||
|
ptr::null()), 1);
|
||||||
|
}
|
||||||
|
Ok(RecoverableSignature::from_ffi(ret))
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines the public key for which `sig` is a valid signature for
|
/// Determines the public key for which `sig` is a valid signature for
|
||||||
/// `msg`. Returns through the out-pointer `pubkey`. Requires a verify-capable
|
/// `msg`. Returns through the out-pointer `pubkey`. Requires a verify-capable
|
||||||
/// context.
|
/// context.
|
||||||
pub fn recover(&self, msg: &Message, sig: &Signature)
|
pub fn recover(&self, msg: &Message, sig: &RecoverableSignature)
|
||||||
-> Result<key::PublicKey, Error> {
|
-> Result<key::PublicKey, Error> {
|
||||||
if self.caps == ContextFlag::SignOnly || self.caps == ContextFlag::None {
|
if self.caps == ContextFlag::SignOnly || self.caps == ContextFlag::None {
|
||||||
return Err(Error::IncapableContext);
|
return Err(Error::IncapableContext);
|
||||||
|
@ -333,8 +383,8 @@ impl Secp256k1 {
|
||||||
let mut pk = unsafe { ffi::PublicKey::blank() };
|
let mut pk = unsafe { ffi::PublicKey::blank() };
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if ffi::secp256k1_ecdsa_recover(self.ctx, msg.as_ptr(),
|
if ffi::secp256k1_ecdsa_recover(self.ctx, &mut pk,
|
||||||
&sig.0, &mut pk) != 1 {
|
sig.as_ptr(), msg.as_ptr()) != 1 {
|
||||||
return Err(Error::InvalidSignature);
|
return Err(Error::InvalidSignature);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -354,8 +404,8 @@ impl Secp256k1 {
|
||||||
|
|
||||||
if !pk.is_valid() {
|
if !pk.is_valid() {
|
||||||
Err(Error::InvalidPublicKey)
|
Err(Error::InvalidPublicKey)
|
||||||
} else if unsafe { ffi::secp256k1_ecdsa_verify(self.ctx, msg.as_ptr(),
|
} else if unsafe { ffi::secp256k1_ecdsa_verify(self.ctx, sig.as_ptr(), msg.as_ptr(),
|
||||||
&sig.0, pk.as_ptr()) } == 0 {
|
pk.as_ptr()) } == 0 {
|
||||||
Err(Error::IncorrectSignature)
|
Err(Error::IncorrectSignature)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -370,7 +420,7 @@ mod tests {
|
||||||
|
|
||||||
use key::{SecretKey, PublicKey};
|
use key::{SecretKey, PublicKey};
|
||||||
use super::constants;
|
use super::constants;
|
||||||
use super::{Secp256k1, Signature, Message, RecoveryId, ContextFlag};
|
use super::{Secp256k1, Signature, RecoverableSignature, Message, RecoveryId, ContextFlag};
|
||||||
use super::Error::{InvalidMessage, InvalidPublicKey, IncorrectSignature, InvalidSignature,
|
use super::Error::{InvalidMessage, InvalidPublicKey, IncorrectSignature, InvalidSignature,
|
||||||
IncapableContext};
|
IncapableContext};
|
||||||
|
|
||||||
|
@ -397,8 +447,14 @@ mod tests {
|
||||||
assert_eq!(vrfy.sign(&msg, &sk), Err(IncapableContext));
|
assert_eq!(vrfy.sign(&msg, &sk), Err(IncapableContext));
|
||||||
assert!(sign.sign(&msg, &sk).is_ok());
|
assert!(sign.sign(&msg, &sk).is_ok());
|
||||||
assert!(full.sign(&msg, &sk).is_ok());
|
assert!(full.sign(&msg, &sk).is_ok());
|
||||||
|
assert_eq!(none.sign_recoverable(&msg, &sk), Err(IncapableContext));
|
||||||
|
assert_eq!(vrfy.sign_recoverable(&msg, &sk), Err(IncapableContext));
|
||||||
|
assert!(sign.sign_recoverable(&msg, &sk).is_ok());
|
||||||
|
assert!(full.sign_recoverable(&msg, &sk).is_ok());
|
||||||
assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk));
|
assert_eq!(sign.sign(&msg, &sk), full.sign(&msg, &sk));
|
||||||
|
assert_eq!(sign.sign_recoverable(&msg, &sk), full.sign_recoverable(&msg, &sk));
|
||||||
let sig = full.sign(&msg, &sk).unwrap();
|
let sig = full.sign(&msg, &sk).unwrap();
|
||||||
|
let sigr = full.sign_recoverable(&msg, &sk).unwrap();
|
||||||
|
|
||||||
// Try verifying
|
// Try verifying
|
||||||
assert_eq!(none.verify(&msg, &sig, &pk), Err(IncapableContext));
|
assert_eq!(none.verify(&msg, &sig, &pk), Err(IncapableContext));
|
||||||
|
@ -407,18 +463,18 @@ mod tests {
|
||||||
assert!(full.verify(&msg, &sig, &pk).is_ok());
|
assert!(full.verify(&msg, &sig, &pk).is_ok());
|
||||||
|
|
||||||
// Try pk recovery
|
// Try pk recovery
|
||||||
assert_eq!(none.recover(&msg, &sig), Err(IncapableContext));
|
assert_eq!(none.recover(&msg, &sigr), Err(IncapableContext));
|
||||||
assert_eq!(none.recover(&msg, &sig), Err(IncapableContext));
|
assert_eq!(none.recover(&msg, &sigr), Err(IncapableContext));
|
||||||
assert_eq!(sign.recover(&msg, &sig), Err(IncapableContext));
|
assert_eq!(sign.recover(&msg, &sigr), Err(IncapableContext));
|
||||||
assert_eq!(sign.recover(&msg, &sig), Err(IncapableContext));
|
assert_eq!(sign.recover(&msg, &sigr), Err(IncapableContext));
|
||||||
assert!(vrfy.recover(&msg, &sig).is_ok());
|
assert!(vrfy.recover(&msg, &sigr).is_ok());
|
||||||
assert!(vrfy.recover(&msg, &sig).is_ok());
|
assert!(vrfy.recover(&msg, &sigr).is_ok());
|
||||||
assert!(full.recover(&msg, &sig).is_ok());
|
assert!(full.recover(&msg, &sigr).is_ok());
|
||||||
assert!(full.recover(&msg, &sig).is_ok());
|
assert!(full.recover(&msg, &sigr).is_ok());
|
||||||
|
|
||||||
assert_eq!(vrfy.recover(&msg, &sig),
|
assert_eq!(vrfy.recover(&msg, &sigr),
|
||||||
full.recover(&msg, &sig));
|
full.recover(&msg, &sigr));
|
||||||
assert_eq!(full.recover(&msg, &sig), Ok(pk));
|
assert_eq!(full.recover(&msg, &sigr), Ok(pk));
|
||||||
|
|
||||||
// Check that we can produce keys from slices with no precomputation
|
// Check that we can produce keys from slices with no precomputation
|
||||||
let (pk_slice, sk_slice) = (&pk.serialize_vec(&none, true), &sk[..]);
|
let (pk_slice, sk_slice) = (&pk.serialize_vec(&none, true), &sk[..]);
|
||||||
|
@ -437,13 +493,13 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_pubkey() {
|
fn invalid_pubkey() {
|
||||||
let s = Secp256k1::new();
|
let s = Secp256k1::new();
|
||||||
let sig = Signature::from_compact(&s, &[1; 64], RecoveryId(0)).unwrap();
|
let sig = RecoverableSignature::from_compact(&s, &[1; 64], RecoveryId(0)).unwrap();
|
||||||
let pk = PublicKey::new();
|
let pk = PublicKey::new();
|
||||||
let mut msg = [0u8; 32];
|
let mut msg = [0u8; 32];
|
||||||
thread_rng().fill_bytes(&mut msg);
|
thread_rng().fill_bytes(&mut msg);
|
||||||
let msg = Message::from_slice(&msg).unwrap();
|
let msg = Message::from_slice(&msg).unwrap();
|
||||||
|
|
||||||
assert_eq!(s.verify(&msg, &sig, &pk), Err(InvalidPublicKey));
|
assert_eq!(s.verify(&msg, &sig.to_standard(&s), &pk), Err(InvalidPublicKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -456,8 +512,8 @@ mod tests {
|
||||||
let sk = SecretKey::from_slice(&s, &one).unwrap();
|
let sk = SecretKey::from_slice(&s, &one).unwrap();
|
||||||
let msg = Message::from_slice(&one).unwrap();
|
let msg = Message::from_slice(&one).unwrap();
|
||||||
|
|
||||||
let sig = s.sign(&msg, &sk).unwrap();
|
let sig = s.sign_recoverable(&msg, &sk).unwrap();
|
||||||
assert_eq!(Ok(sig), Signature::from_compact(&s, &[
|
assert_eq!(Ok(sig), RecoverableSignature::from_compact(&s, &[
|
||||||
0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
|
0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
|
||||||
0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
|
0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
|
||||||
0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
|
0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
|
||||||
|
@ -533,14 +589,15 @@ mod tests {
|
||||||
|
|
||||||
let (sk, pk) = s.generate_keypair(&mut thread_rng()).unwrap();
|
let (sk, pk) = s.generate_keypair(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
let sig = s.sign(&msg, &sk).unwrap();
|
let sigr = s.sign_recoverable(&msg, &sk).unwrap();
|
||||||
|
let sig = sigr.to_standard(&s);
|
||||||
|
|
||||||
let mut msg = [0u8; 32];
|
let mut msg = [0u8; 32];
|
||||||
thread_rng().fill_bytes(&mut msg);
|
thread_rng().fill_bytes(&mut msg);
|
||||||
let msg = Message::from_slice(&msg).unwrap();
|
let msg = Message::from_slice(&msg).unwrap();
|
||||||
assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature));
|
assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature));
|
||||||
|
|
||||||
let recovered_key = s.recover(&msg, &sig).unwrap();
|
let recovered_key = s.recover(&msg, &sigr).unwrap();
|
||||||
assert!(recovered_key != pk);
|
assert!(recovered_key != pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,7 +612,7 @@ mod tests {
|
||||||
|
|
||||||
let (sk, pk) = s.generate_keypair(&mut thread_rng()).unwrap();
|
let (sk, pk) = s.generate_keypair(&mut thread_rng()).unwrap();
|
||||||
|
|
||||||
let sig = s.sign(&msg, &sk).unwrap();
|
let sig = s.sign_recoverable(&msg, &sk).unwrap();
|
||||||
|
|
||||||
assert_eq!(s.recover(&msg, &sig), Ok(pk));
|
assert_eq!(s.recover(&msg, &sig), Ok(pk));
|
||||||
}
|
}
|
||||||
|
@ -568,10 +625,10 @@ mod tests {
|
||||||
let msg = Message::from_slice(&[0x55; 32]).unwrap();
|
let msg = Message::from_slice(&[0x55; 32]).unwrap();
|
||||||
|
|
||||||
// Zero is not a valid sig
|
// Zero is not a valid sig
|
||||||
let sig = Signature::from_compact(&s, &[0; 64], RecoveryId(0)).unwrap();
|
let sig = RecoverableSignature::from_compact(&s, &[0; 64], RecoveryId(0)).unwrap();
|
||||||
assert_eq!(s.recover(&msg, &sig), Err(InvalidSignature));
|
assert_eq!(s.recover(&msg, &sig), Err(InvalidSignature));
|
||||||
// ...but 111..111 is
|
// ...but 111..111 is
|
||||||
let sig = Signature::from_compact(&s, &[1; 64], RecoveryId(0)).unwrap();
|
let sig = RecoverableSignature::from_compact(&s, &[1; 64], RecoveryId(0)).unwrap();
|
||||||
assert!(s.recover(&msg, &sig).is_ok());
|
assert!(s.recover(&msg, &sig).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,7 +650,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_debug_output() {
|
fn test_debug_output() {
|
||||||
let s = Secp256k1::new();
|
let s = Secp256k1::new();
|
||||||
let sig = Signature::from_compact(&s, &[
|
let sig = RecoverableSignature::from_compact(&s, &[
|
||||||
0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
|
0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
|
||||||
0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
|
0x04, 0x77, 0x2b, 0x6f, 0x92, 0x1f, 0x0b, 0xa6,
|
||||||
0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
|
0xaf, 0x0c, 0x1e, 0x77, 0xfc, 0x43, 0x9e, 0x65,
|
||||||
|
@ -603,7 +660,7 @@ mod tests {
|
||||||
0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06,
|
0xff, 0x20, 0x80, 0xc4, 0xa3, 0x9a, 0xae, 0x06,
|
||||||
0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89],
|
0x8d, 0x12, 0xee, 0xd0, 0x09, 0xb6, 0x8c, 0x89],
|
||||||
RecoveryId(1)).unwrap();
|
RecoveryId(1)).unwrap();
|
||||||
assert_eq!(&format!("{:?}", sig), "Signature(98882e09f4ed6dc3659e43fc771e0cafa60b1f926f2b77041f744721adff7366898cb609d0ee128d06ae9aa3c48020ff9f705e02f80e1280a8ade05216971a4c01)");
|
assert_eq!(&format!("{:?}", sig), "RecoverableSignature(98882e09f4ed6dc3659e43fc771e0cafa60b1f926f2b77041f744721adff7366898cb609d0ee128d06ae9aa3c48020ff9f705e02f80e1280a8ade05216971a4c01)");
|
||||||
|
|
||||||
let msg = Message([1, 2, 3, 4, 5, 6, 7, 8,
|
let msg = Message([1, 2, 3, 4, 5, 6, 7, 8,
|
||||||
9, 10, 11, 12, 13, 14, 15, 16,
|
9, 10, 11, 12, 13, 14, 15, 16,
|
||||||
|
|
Loading…
Reference in New Issue