[API BREAK] update for libsecp256k1 "explicit context" API break

Rather than have global initialization functions, which required
expensive synchronization on the part of the Rust library,
libsecp256k1 now carries its context in thread-local data which
must be passed to every function.

What this means for the rust-secp256k1 API is:
  - Most functions on `PublicKey` and `SecretKey` now require a
    `Secp256k1` to be given to them.

  - `Secp256k1::verify` and `::verify_raw` now take a `&self`

  - `SecretKey::new` now takes a `Secp256k1` rather than a Rng; a
    future commit will allow specifying the Rng in the `Secp256k1`
    so that functionality is not lost.

  - The FFI functions have all changed to take a context argument

  - `secp256k1::init()` is gone, as is the dependency on std::sync

  - There is a `ffi::Context` type which must be handled carefully
    by anyone using it directly (hopefully nobody :))
This commit is contained in:
Andrew Poelstra 2015-04-11 12:00:20 -05:00
parent 609f658bee
commit e52faee98f
3 changed files with 105 additions and 95 deletions

View File

@ -32,65 +32,87 @@ pub type NonceFn = unsafe extern "C" fn(nonce32: *mut c_uchar,
attempt: c_uint,
data: *const c_void);
#[repr(C)]
struct ContextInner;
/// A Secp256k1 context, containing various precomputed values and such
/// needed to do elliptic curve computations. If you create one of these
/// with `secp256k1_context_create` you MUST destroy it with
/// `secp256k1_context_destroy`, or else you will have a memory leak.
/// Furthermore, you MUST NOT use this object after destroying it; it is
/// `Copy` so the compiler will not help you to avoid this. There is no
/// need for ordinary users of this library to ever use this type directly.
#[repr(C)]
#[allow(raw_pointer_derive)]
#[derive(Copy, Clone, Debug)]
pub struct Context(*mut ContextInner);
#[link(name = "secp256k1")]
extern "C" {
pub static secp256k1_nonce_function_rfc6979: NonceFn;
pub static secp256k1_nonce_function_default: NonceFn;
pub fn secp256k1_start(flags: c_uint);
pub fn secp256k1_context_create(flags: c_uint) -> Context;
pub fn secp256k1_stop();
pub fn secp256k1_context_destroy(cx: Context);
pub fn secp256k1_ecdsa_verify(msg32: *const c_uchar,
pub fn secp256k1_ecdsa_verify(cx: Context, msg32: *const c_uchar,
sig: *const c_uchar, sig_len: c_int,
pk: *const c_uchar, pk_len: c_int)
-> c_int;
pub fn secp256k1_ec_pubkey_create(pk: *mut c_uchar, pk_len: *mut c_int,
pub fn secp256k1_ec_pubkey_create(cx: Context,
pk: *mut c_uchar, pk_len: *mut c_int,
sk: *const c_uchar, compressed: c_int)
-> c_int;
pub fn secp256k1_ecdsa_sign(msg32: *const c_uchar,
pub fn secp256k1_ecdsa_sign(cx: Context, msg32: *const c_uchar,
sig: *mut c_uchar, sig_len: *mut c_int,
sk: *const c_uchar,
noncefn: NonceFn, noncedata: *const c_void)
-> c_int;
pub fn secp256k1_ecdsa_sign_compact(msg: *const c_uchar,
pub fn secp256k1_ecdsa_sign_compact(cx: Context, msg: *const c_uchar,
sig64: *mut c_uchar, sk: *const c_uchar,
noncefn: NonceFn, noncedata: *const c_void,
recid: *mut c_int)
-> c_int;
pub fn secp256k1_ecdsa_recover_compact(msg32: *const c_uchar,
pub fn secp256k1_ecdsa_recover_compact(cx: Context, msg32: *const c_uchar,
sig64: *const c_uchar, pk: *mut c_uchar,
pk_len: *mut c_int, compressed: c_int,
recid: c_int) -> c_int;
pub fn secp256k1_ec_seckey_verify(sk: *const c_uchar) -> c_int;
pub fn secp256k1_ec_seckey_verify(cx: Context,
sk: *const c_uchar) -> c_int;
pub fn secp256k1_ec_pubkey_verify(pk: *const c_uchar,
pub fn secp256k1_ec_pubkey_verify(cx: Context,
pk: *const c_uchar,
pk_len: c_int) -> c_int;
//TODO secp256k1_ec_pubkey_decompress
//TODO secp256k1_ec_privkey_export
//TODO secp256k1_ec_privkey_import
pub fn secp256k1_ec_privkey_tweak_add(sk: *mut c_uchar,
pub fn secp256k1_ec_privkey_tweak_add(cx: Context,
sk: *mut c_uchar,
tweak: *const c_uchar)
-> c_int;
pub fn secp256k1_ec_pubkey_tweak_add(pk: *mut c_uchar,
pub fn secp256k1_ec_pubkey_tweak_add(cx: Context,
pk: *mut c_uchar,
pk_len: c_int,
tweak: *const c_uchar)
-> c_int;
pub fn secp256k1_ec_privkey_tweak_mul(sk: *mut c_uchar,
pub fn secp256k1_ec_privkey_tweak_mul(cx: Context,
sk: *mut c_uchar,
tweak: *const c_uchar)
-> c_int;
pub fn secp256k1_ec_pubkey_tweak_mul(pk: *mut c_uchar,
pub fn secp256k1_ec_pubkey_tweak_mul(cx: Context,
pk: *mut c_uchar,
pk_len: c_int,
tweak: *const c_uchar)
-> c_int;

View File

@ -21,7 +21,7 @@ use rand::Rng;
use serialize::{Decoder, Decodable, Encoder, Encodable};
use serde::{Serialize, Deserialize, Serializer, Deserializer};
use super::init;
use super::Secp256k1;
use super::Error::{self, InvalidPublicKey, InvalidSecretKey, Unknown};
use constants;
use ffi;
@ -55,12 +55,11 @@ fn random_32_bytes<R:Rng>(rng: &mut R) -> [u8; 32] {
impl SecretKey {
/// Creates a new random secret key
#[inline]
pub fn new<R:Rng>(rng: &mut R) -> SecretKey {
init();
let mut data = random_32_bytes(rng);
pub fn new(secp: &mut Secp256k1) -> SecretKey {
let mut data = random_32_bytes(&mut secp.rng);
unsafe {
while ffi::secp256k1_ec_seckey_verify(data.as_ptr()) == 0 {
data = random_32_bytes(rng);
while ffi::secp256k1_ec_seckey_verify(secp.ctx, data.as_ptr()) == 0 {
data = random_32_bytes(&mut secp.rng);
}
}
SecretKey(data)
@ -68,13 +67,12 @@ impl SecretKey {
/// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key
#[inline]
pub fn from_slice(data: &[u8]) -> Result<SecretKey, Error> {
init();
pub fn from_slice(secp: &Secp256k1, data: &[u8]) -> Result<SecretKey, Error> {
match data.len() {
constants::SECRET_KEY_SIZE => {
let mut ret = [0; constants::SECRET_KEY_SIZE];
unsafe {
if ffi::secp256k1_ec_seckey_verify(data.as_ptr()) == 0 {
if ffi::secp256k1_ec_seckey_verify(secp.ctx, data.as_ptr()) == 0 {
return Err(InvalidSecretKey);
}
copy_nonoverlapping(data.as_ptr(),
@ -89,13 +87,9 @@ impl SecretKey {
#[inline]
/// Adds one secret key to another, modulo the curve order
/// Marked `unsafe` since you must
/// call `init()` (or construct a `Secp256k1`, which does this for you) before
/// using this function
pub fn add_assign(&mut self, other: &SecretKey) -> Result<(), Error> {
init();
pub fn add_assign(&mut self, secp: &Secp256k1, other: &SecretKey) -> Result<(), Error> {
unsafe {
if ffi::secp256k1_ec_privkey_tweak_add(self.as_mut_ptr(), other.as_ptr()) != 1 {
if ffi::secp256k1_ec_privkey_tweak_add(secp.ctx, self.as_mut_ptr(), other.as_ptr()) != 1 {
Err(Unknown)
} else {
Ok(())
@ -119,16 +113,16 @@ impl PublicKey {
/// Creates a new public key from a secret key.
#[inline]
pub fn from_secret_key(sk: &SecretKey, compressed: bool) -> PublicKey {
pub fn from_secret_key(secp: &Secp256k1, sk: &SecretKey, compressed: bool) -> PublicKey {
let mut pk = PublicKey::new(compressed);
let compressed = if compressed {1} else {0};
let mut len = 0;
init();
unsafe {
// We can assume the return value because it's not possible to construct
// an invalid `SecretKey` without transmute trickery or something
assert_eq!(ffi::secp256k1_ec_pubkey_create(
secp.ctx,
pk.as_mut_ptr(), &mut len,
sk.as_ptr(), compressed), 1);
}
@ -138,12 +132,12 @@ impl PublicKey {
/// Creates a public key directly from a slice
#[inline]
pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> {
pub fn from_slice(secp: &Secp256k1, data: &[u8]) -> Result<PublicKey, Error> {
match data.len() {
constants::COMPRESSED_PUBLIC_KEY_SIZE => {
let mut ret = [0; constants::COMPRESSED_PUBLIC_KEY_SIZE];
unsafe {
if ffi::secp256k1_ec_pubkey_verify(data.as_ptr(),
if ffi::secp256k1_ec_pubkey_verify(secp.ctx, data.as_ptr(),
data.len() as ::libc::c_int) == 0 {
return Err(InvalidPublicKey);
}
@ -210,10 +204,9 @@ impl PublicKey {
#[inline]
/// Adds the pk corresponding to `other` to the pk `self` in place
pub fn add_exp_assign(&mut self, other: &SecretKey) -> Result<(), Error> {
init();
pub fn add_exp_assign(&mut self, secp: &Secp256k1, other: &SecretKey) -> Result<(), Error> {
unsafe {
if ffi::secp256k1_ec_pubkey_tweak_add(self.as_mut_ptr(),
if ffi::secp256k1_ec_pubkey_tweak_add(secp.ctx, self.as_mut_ptr(),
self.len() as ::libc::c_int,
other.as_ptr()) != 1 {
Err(Unknown)
@ -459,23 +452,25 @@ mod test {
#[test]
fn skey_from_slice() {
let sk = SecretKey::from_slice(&[1; 31]);
let s = Secp256k1::new().unwrap();
let sk = SecretKey::from_slice(&s, &[1; 31]);
assert_eq!(sk, Err(InvalidSecretKey));
let sk = SecretKey::from_slice(&[1; 32]);
let sk = SecretKey::from_slice(&s, &[1; 32]);
assert!(sk.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 s = Secp256k1::new().unwrap();
assert_eq!(PublicKey::from_slice(&s, &[]), Err(InvalidPublicKey));
assert_eq!(PublicKey::from_slice(&s, &[1, 2, 3]), Err(InvalidPublicKey));
let uncompressed = PublicKey::from_slice(&[4, 54, 57, 149, 239, 162, 148, 175, 246, 254, 239, 75, 154, 152, 10, 82, 234, 224, 85, 220, 40, 100, 57, 121, 30, 162, 94, 156, 135, 67, 74, 49, 179, 57, 236, 53, 162, 124, 149, 144, 168, 77, 74, 30, 72, 211, 229, 110, 111, 55, 96, 193, 86, 227, 183, 152, 195, 155, 51, 247, 123, 113, 60, 228, 188]);
let uncompressed = PublicKey::from_slice(&s, &[4, 54, 57, 149, 239, 162, 148, 175, 246, 254, 239, 75, 154, 152, 10, 82, 234, 224, 85, 220, 40, 100, 57, 121, 30, 162, 94, 156, 135, 67, 74, 49, 179, 57, 236, 53, 162, 124, 149, 144, 168, 77, 74, 30, 72, 211, 229, 110, 111, 55, 96, 193, 86, 227, 183, 152, 195, 155, 51, 247, 123, 113, 60, 228, 188]);
assert!(uncompressed.is_ok());
assert!(!uncompressed.unwrap().is_compressed());
let compressed = PublicKey::from_slice(&[3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, 140, 11, 3, 51, 41, 111, 180, 110, 143, 114, 134, 88, 73, 198, 174, 52, 184, 78]);
let compressed = PublicKey::from_slice(&s, &[3, 23, 183, 225, 206, 31, 159, 148, 195, 42, 67, 115, 146, 41, 248, 140, 11, 3, 51, 41, 111, 180, 110, 143, 114, 134, 88, 73, 198, 174, 52, 184, 78]);
assert!(compressed.is_ok());
assert!(compressed.unwrap().is_compressed());
}
@ -485,27 +480,30 @@ mod test {
let mut s = Secp256k1::new().unwrap();
let (sk1, pk1) = s.generate_keypair(true);
assert_eq!(SecretKey::from_slice(&sk1[..]), Ok(sk1));
assert_eq!(PublicKey::from_slice(&pk1[..]), Ok(pk1));
assert_eq!(SecretKey::from_slice(&s, &sk1[..]), Ok(sk1));
assert_eq!(PublicKey::from_slice(&s, &pk1[..]), Ok(pk1));
let (sk2, pk2) = s.generate_keypair(false);
assert_eq!(SecretKey::from_slice(&sk2[..]), Ok(sk2));
assert_eq!(PublicKey::from_slice(&pk2[..]), Ok(pk2));
assert_eq!(SecretKey::from_slice(&s, &sk2[..]), Ok(sk2));
assert_eq!(PublicKey::from_slice(&s, &pk2[..]), Ok(pk2));
}
#[test]
fn invalid_secret_key() {
let s = Secp256k1::new().unwrap();
// Zero
assert_eq!(SecretKey::from_slice(&[0; 32]), Err(InvalidSecretKey));
assert_eq!(SecretKey::from_slice(&s, &[0; 32]), Err(InvalidSecretKey));
// -1
assert_eq!(SecretKey::from_slice(&[0xff; 32]), Err(InvalidSecretKey));
assert_eq!(SecretKey::from_slice(&s, &[0xff; 32]), Err(InvalidSecretKey));
// Top of range
assert!(SecretKey::from_slice(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
assert!(SecretKey::from_slice(&s,
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40]).is_ok());
// One past top of range
assert!(SecretKey::from_slice(&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
assert!(SecretKey::from_slice(&s,
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41]).is_err());
@ -578,15 +576,15 @@ mod test {
let (mut sk1, mut pk1) = s.generate_keypair(true);
let (mut sk2, mut pk2) = s.generate_keypair(true);
assert_eq!(PublicKey::from_secret_key(&sk1, true), pk1);
assert!(sk1.add_assign(&sk2).is_ok());
assert!(pk1.add_exp_assign(&sk2).is_ok());
assert_eq!(PublicKey::from_secret_key(&sk1, true), pk1);
assert_eq!(PublicKey::from_secret_key(&s, &sk1, true), pk1);
assert!(sk1.add_assign(&s, &sk2).is_ok());
assert!(pk1.add_exp_assign(&s, &sk2).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk1, true), pk1);
assert_eq!(PublicKey::from_secret_key(&sk2, true), pk2);
assert!(sk2.add_assign(&sk1).is_ok());
assert!(pk2.add_exp_assign(&sk1).is_ok());
assert_eq!(PublicKey::from_secret_key(&sk2, true), pk2);
assert_eq!(PublicKey::from_secret_key(&s, &sk2, true), pk2);
assert!(sk2.add_assign(&s, &sk1).is_ok());
assert!(pk2.add_exp_assign(&s, &sk1).is_ok());
assert_eq!(PublicKey::from_secret_key(&s, &sk2, true), pk2);
}
}

View File

@ -45,7 +45,6 @@ extern crate rand;
use std::intrinsics::copy_nonoverlapping;
use std::{fmt, io, ops, ptr};
use std::sync::{Once, ONCE_INIT};
use libc::c_int;
use rand::{OsRng, Rng, SeedableRng};
@ -212,35 +211,29 @@ impl fmt::Display for Error {
}
}
static mut Secp256k1_init: Once = ONCE_INIT;
/// The secp256k1 engine, used to execute all signature operations
pub struct Secp256k1 {
ctx: ffi::Context,
rng: Fortuna
}
/// Does one-time initialization of the secp256k1 engine. Can be called
/// multiple times, and is called by the `Secp256k1` constructor. This
/// only needs to be called directly if you are using the library without
/// a `Secp256k1` object, e.g. batch key generation through
/// `key::PublicKey::from_secret_key`.
pub fn init() {
unsafe {
Secp256k1_init.call_once(|| {
ffi::secp256k1_start(ffi::SECP256K1_START_VERIFY |
ffi::SECP256K1_START_SIGN);
});
impl Drop for Secp256k1 {
fn drop(&mut self) {
unsafe { ffi::secp256k1_context_destroy(self.ctx); }
}
}
impl Secp256k1 {
/// Constructs a new secp256k1 engine.
pub fn new() -> io::Result<Secp256k1> {
init();
let ctx = unsafe {
ffi::secp256k1_context_create(ffi::SECP256K1_START_VERIFY |
ffi::SECP256K1_START_SIGN)
};
let mut osrng = try!(OsRng::new());
let mut seed = [0; 2048];
osrng.fill_bytes(&mut seed);
Ok(Secp256k1 { rng: SeedableRng::from_seed(&seed[..]) })
Ok(Secp256k1 { ctx: ctx, rng: SeedableRng::from_seed(&seed[..]) })
}
/// Generates a random keypair. Convenience function for `key::SecretKey::new`
@ -249,8 +242,8 @@ impl Secp256k1 {
#[inline]
pub fn generate_keypair(&mut self, compressed: bool)
-> (key::SecretKey, key::PublicKey) {
let sk = key::SecretKey::new(&mut self.rng);
let pk = key::PublicKey::from_secret_key(&sk, compressed);
let sk = key::SecretKey::new(self);
let pk = key::PublicKey::from_secret_key(self, &sk, compressed);
(sk, pk)
}
@ -260,7 +253,7 @@ impl Secp256k1 {
let mut sig = [0; constants::MAX_SIGNATURE_SIZE];
let mut len = constants::MAX_SIGNATURE_SIZE as c_int;
unsafe {
if ffi::secp256k1_ecdsa_sign(msg.as_ptr(), (&mut sig).as_mut_ptr(),
if ffi::secp256k1_ecdsa_sign(self.ctx, msg.as_ptr(), sig.as_mut_ptr(),
&mut len, sk.as_ptr(),
ffi::secp256k1_nonce_function_rfc6979,
ptr::null()) != 1 {
@ -278,7 +271,7 @@ impl Secp256k1 {
let mut sig = [0; constants::MAX_SIGNATURE_SIZE];
let mut recid = 0;
unsafe {
if ffi::secp256k1_ecdsa_sign_compact(msg.as_ptr(),
if ffi::secp256k1_ecdsa_sign_compact(self.ctx, msg.as_ptr(),
sig.as_mut_ptr(), sk.as_ptr(),
ffi::secp256k1_nonce_function_default,
ptr::null(), &mut recid) != 1 {
@ -298,7 +291,7 @@ impl Secp256k1 {
unsafe {
let mut len = 0;
if ffi::secp256k1_ecdsa_recover_compact(msg.as_ptr(),
if ffi::secp256k1_ecdsa_recover_compact(self.ctx, msg.as_ptr(),
sig.as_ptr(), pk.as_mut_ptr(), &mut len,
if compressed {1} else {0},
recid) != 1 {
@ -311,21 +304,17 @@ impl Secp256k1 {
/// Checks that `sig` is a valid ECDSA signature for `msg` using the public
/// key `pubkey`. Returns `Ok(true)` on success. Note that this function cannot
/// be used for Bitcoin consensus checking since there are transactions out
/// there with zero-padded signatures that don't fit in the `Signature` type.
/// Use `verify_raw` instead.
/// be used for Bitcoin consensus checking since there may exist signatures
/// which OpenSSL would verify but not libsecp256k1, or vice-versa.
#[inline]
pub fn verify(msg: &Message, sig: &Signature, pk: &key::PublicKey) -> Result<(), Error> {
Secp256k1::verify_raw(msg, &sig[..], pk)
pub fn verify(&self, msg: &Message, sig: &Signature, pk: &key::PublicKey) -> Result<(), Error> {
self.verify_raw(msg, &sig[..], pk)
}
/// Checks that `sig` is a valid ECDSA signature for `msg` using the public
/// key `pubkey`. Returns `Ok(true)` on success.
#[inline]
pub fn verify_raw(msg: &Message, sig: &[u8], pk: &key::PublicKey) -> Result<(), Error> {
init(); // This is a static function, so we have to init
/// Verifies a signature described as a slice of bytes rather than opaque `Signature`
pub fn verify_raw(&self, msg: &Message, sig: &[u8], pk: &key::PublicKey) -> Result<(), Error> {
let res = unsafe {
ffi::secp256k1_ecdsa_verify(msg.as_ptr(),
ffi::secp256k1_ecdsa_verify(self.ctx, msg.as_ptr(),
sig.as_ptr(), sig.len() as c_int,
pk.as_ptr(), pk.len() as c_int)
};
@ -354,13 +343,14 @@ mod tests {
#[test]
fn invalid_pubkey() {
let s = Secp256k1::new().unwrap();
let sig = Signature::from_slice(&[0; 72]).unwrap();
let pk = PublicKey::new(true);
let mut msg = [0u8; 32];
thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap();
assert_eq!(Secp256k1::verify(&msg, &sig, &pk), Err(InvalidPublicKey));
assert_eq!(s.verify(&msg, &sig, &pk), Err(InvalidPublicKey));
}
#[test]
@ -374,7 +364,7 @@ mod tests {
thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap();
assert_eq!(Secp256k1::verify(&msg, &sig, &pk), Err(InvalidSignature));
assert_eq!(s.verify(&msg, &sig, &pk), Err(InvalidSignature));
}
#[test]
@ -387,7 +377,7 @@ mod tests {
thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap();
assert_eq!(Secp256k1::verify(&msg, &sig, &pk), Err(InvalidSignature));
assert_eq!(s.verify(&msg, &sig, &pk), Err(InvalidSignature));
}
#[test]
@ -415,7 +405,7 @@ mod tests {
let sig = s.sign(&msg, &sk).unwrap();
assert_eq!(Secp256k1::verify(&msg, &sig, &pk), Ok(()));
assert_eq!(s.verify(&msg, &sig, &pk), Ok(()));
}
#[test]
@ -433,7 +423,7 @@ mod tests {
let mut msg = [0u8; 32];
thread_rng().fill_bytes(&mut msg);
let msg = Message::from_slice(&msg).unwrap();
assert_eq!(Secp256k1::verify(&msg, &sig, &pk), Err(IncorrectSignature));
assert_eq!(s.verify(&msg, &sig, &pk), Err(IncorrectSignature));
}
#[test]