From 98890907849326a0e265d0ee12d3caa232b19602 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Thu, 11 Sep 2014 22:36:15 -0500 Subject: [PATCH] Overhaul interface to use zero-on-free SecretKeys Using the `secretdata` library, we can store SecretKeys in such a way that they cannot be moved or copied, and their memory is zeroed out on drop. This gives us some assurance that in the case of memory unsafety, there is not secret key data lying around anywhere that we don't expect. Unfortunately, it means that we cannot construct secret keys and then return them, which forces the interface to change a fair bit. I removed the `generate_keypair` function from Secp256k1, then `generate_nonce` for symmetry, then dropped the `Secp256k1` struct entirely because it turned out that none of the remaining functions used the `self` param. So here we are. I bumped the version number. Sorry about this. --- Cargo.toml | 5 +- src/key.rs | 181 ++++++++++++++++----------- src/macros.rs | 11 ++ src/secp256k1.rs | 316 +++++++++++++++++++++-------------------------- 4 files changed, 265 insertions(+), 248 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cadb48d..d7a609b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bitcoin-secp256k1-rs" -version = "0.0.1" +version = "0.1.1" authors = [ "Dawid Ciężarkiewicz ", "Andrew Poelstra (SecretData<'a, SecretKeyData>); -/// The number 1 encoded as a secret key -pub static ONE: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 1]); +/// Secret 256-bit key used as `x` in an ECDSA signature +struct SecretKeyData([u8, ..constants::SECRET_KEY_SIZE]); +impl_array_newtype!(SecretKeyData, u8, constants::SECRET_KEY_SIZE) + +impl Default for SecretKeyData { + fn default() -> SecretKeyData { + SecretKeyData([0, ..constants::SECRET_KEY_SIZE]) + } +} /// Public key #[deriving(Clone, PartialEq, Eq, Show)] @@ -98,7 +104,7 @@ impl Nonce { /// Generates a deterministic nonce by RFC6979 with HMAC-SHA512 #[inline] #[allow(non_snake_case)] // so we can match the names in the RFC - pub fn deterministic(msg: &[u8], key: &SecretKey) -> Nonce { + pub fn deterministic<'a>(msg: &[u8], key: &SecretKey<'a>) -> Nonce { static HMAC_SIZE: uint = 64; macro_rules! hmac( @@ -154,10 +160,16 @@ impl Nonce { } } -impl SecretKey { +impl<'a> SecretKey<'a> { + /// Creates a new zeroed-out secret key + #[inline] + pub fn new() -> SecretKey<'a> { + SecretKey(SecretData::new()) + } + /// Creates a new random secret key #[inline] - pub fn new(rng: &mut R) -> SecretKey { + pub fn init_rng(&'a mut self, rng: &mut R) { init(); let mut data = random_32_bytes(rng); unsafe { @@ -165,36 +177,47 @@ impl SecretKey { data = random_32_bytes(rng); } } - SecretKey(data) + let &SecretKey(ref mut selfdata) = self; + selfdata.move(&mut SecretKeyData(data)) } - /// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key + /// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key, + /// zeroing out the original data #[inline] - pub fn from_slice(data: &[u8]) -> Result { + pub fn init_slice(&'a mut self, data: &mut [u8]) -> Result<()> { init(); match data.len() { constants::SECRET_KEY_SIZE => { - let mut ret = [0, ..constants::SECRET_KEY_SIZE]; + let &SecretKey(ref mut selfdata) = self; unsafe { if ffi::secp256k1_ecdsa_seckey_verify(data.as_ptr()) == 0 { return Err(InvalidSecretKey); } - copy_nonoverlapping_memory(ret.as_mut_ptr(), + copy_nonoverlapping_memory(selfdata.data_mut().as_mut_ptr(), data.as_ptr(), data.len()); + zero_memory(data.as_mut_ptr(), data.len()); } - Ok(SecretKey(ret)) + Ok(()) } _ => Err(InvalidSecretKey) } } + /// Copies the data from one key to another without zeroing anyth out + #[inline] + pub fn clone_from<'b>(&'a mut self, other: &SecretKey<'b>) { + let &SecretKey(ref mut selfdata) = self; + let &SecretKey(ref otherdata) = other; + selfdata.clone_from(otherdata); + } + #[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<()> { + pub fn add_assign<'b>(&mut self, other: &SecretKey<'b>) -> Result<()> { init(); unsafe { if ffi::secp256k1_ecdsa_privkey_tweak_add(self.as_mut_ptr(), other.as_ptr()) != 1 { @@ -206,24 +229,24 @@ impl SecretKey { } #[inline] - /// Returns an iterator for the (sk, pk) pairs starting one after this one, - /// and incrementing by one each time - pub fn sequence(&self, compressed: bool) -> Sequence { - Sequence { last_sk: *self, compressed: compressed } + /// Returns an immutable view of the data as a byteslice + pub fn as_slice<'b>(&'b self) -> &'b [u8] { + let &SecretKey(ref selfdata) = self; + selfdata.data().as_slice() } -} -/// An iterator of keypairs `(sk + 1, pk*G)`, `(sk + 2, pk*2G)`, ... -pub struct Sequence { - compressed: bool, - last_sk: SecretKey, -} - -impl<'a> Iterator<(SecretKey, PublicKey)> for Sequence { #[inline] - fn next(&mut self) -> Option<(SecretKey, PublicKey)> { - self.last_sk.add_assign(&ONE).unwrap(); - Some((self.last_sk, PublicKey::from_secret_key(&self.last_sk, self.compressed))) + /// Returns a raw pointer to the underlying secret key data + pub fn as_ptr(&self) -> *const u8 { + let &SecretKey(ref selfdata) = self; + selfdata.data().as_ptr() + } + + #[inline] + /// Returns a mutable raw pointer to the underlying secret key data + pub fn as_mut_ptr(&mut self) -> *mut u8 { + let &SecretKey(ref mut selfdata) = self; + selfdata.data_mut().as_mut_ptr() } } @@ -239,7 +262,7 @@ 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<'a>(sk: &SecretKey<'a>, compressed: bool) -> PublicKey { let mut pk = PublicKey::new(compressed); let compressed = if compressed {1} else {0}; let mut len = 0; @@ -337,7 +360,7 @@ 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<()> { + pub fn add_exp_assign<'a>(&mut self, other: &SecretKey<'a>) -> Result<()> { init(); unsafe { if ffi::secp256k1_ecdsa_pubkey_tweak_add(self.as_mut_ptr(), @@ -421,9 +444,17 @@ impl , S> Encodable for PublicKey { } } -impl fmt::Show for SecretKey { +impl<'a> PartialEq for SecretKey<'a> { + fn eq(&self, other: &SecretKey<'a>) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl<'a> Eq for SecretKey<'a> {} + +impl<'a> fmt::Show for SecretKey<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.as_slice().fmt(f) + write!(f, "[secret data]") } } @@ -432,9 +463,7 @@ mod test { use serialize::hex::FromHex; use std::rand::task_rng; - use test::Bencher; - - use super::super::{Secp256k1, InvalidNonce, InvalidPublicKey, InvalidSecretKey}; + use super::super::{InvalidNonce, InvalidPublicKey, InvalidSecretKey}; use super::{Nonce, PublicKey, SecretKey}; #[test] @@ -442,17 +471,16 @@ mod test { let n = Nonce::from_slice([1, ..31]); assert_eq!(n, Err(InvalidNonce)); - let n = SecretKey::from_slice([1, ..32]); - assert!(n.is_ok()); + let mut n = SecretKey::new(); + assert_eq!(n.init_slice([1, ..32]), Ok(())); } #[test] fn skey_from_slice() { - let sk = SecretKey::from_slice([1, ..31]); - assert_eq!(sk, Err(InvalidSecretKey)); - - let sk = SecretKey::from_slice([1, ..32]); - assert!(sk.is_ok()); + let mut sk = SecretKey::new(); + assert_eq!(sk.init_slice([1, ..31]), Err(InvalidSecretKey)); + let mut sk = SecretKey::new(); + assert_eq!(sk.init_slice([1, ..32]), Ok(())); } #[test] @@ -471,14 +499,17 @@ mod test { #[test] fn keypair_slice_round_trip() { - let mut s = Secp256k1::new().unwrap(); + let mut rng = task_rng(); + let mut sk1 = SecretKey::new(); + sk1.init_rng(&mut rng); + let mut sk2 = SecretKey::new(); + sk2.clone_from(&sk1); - let (sk1, pk1) = s.generate_keypair(true); - assert_eq!(SecretKey::from_slice(sk1.as_slice()), Ok(sk1)); + assert_eq!(sk1, sk2); + + let pk1 = PublicKey::from_secret_key(&sk1, false); assert_eq!(PublicKey::from_slice(pk1.as_slice()), Ok(pk1)); - - let (sk2, pk2) = s.generate_keypair(false); - assert_eq!(SecretKey::from_slice(sk2.as_slice()), Ok(sk2)); + let pk2 = PublicKey::from_secret_key(&sk1, true); assert_eq!(PublicKey::from_slice(pk2.as_slice()), Ok(pk2)); } @@ -491,28 +522,33 @@ mod test { #[test] fn invalid_secret_key() { + let mut sk = SecretKey::new(); // Zero - assert_eq!(SecretKey::from_slice([0, ..32]), Err(InvalidSecretKey)); + assert_eq!(sk.init_slice([0, ..32]), Err(InvalidSecretKey)); // -1 - assert_eq!(SecretKey::from_slice([0xff, ..32]), Err(InvalidSecretKey)); + assert_eq!(sk.init_slice([0xff, ..32]), Err(InvalidSecretKey)); // Top of range - assert!(SecretKey::from_slice([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()); + assert_eq!(sk.init_slice([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]), Ok(())); // One past top of range - assert!(SecretKey::from_slice([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()); + assert_eq!(sk.init_slice([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]), Err(InvalidSecretKey)); } #[test] fn test_addition() { - let mut s = Secp256k1::new().unwrap(); + let mut rng = task_rng(); - let (mut sk1, mut pk1) = s.generate_keypair(true); - let (mut sk2, mut pk2) = s.generate_keypair(true); + let mut sk1 = SecretKey::new(); + let mut sk2 = SecretKey::new(); + sk1.init_rng(&mut rng); + sk2.init_rng(&mut rng); + let mut pk1 = PublicKey::from_secret_key(&sk1, true); + let mut pk2 = PublicKey::from_secret_key(&sk2, true); assert_eq!(PublicKey::from_secret_key(&sk1, true), pk1); assert!(sk1.add_assign(&sk2).is_ok()); @@ -533,7 +569,10 @@ mod test { // from ecdsa.curves import SECP256k1 // # This key was generated randomly // sk = 0x09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81 - let sk = SecretKey::from_slice(hex_slice!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81")).unwrap(); + let mut sk = SecretKey::new(); + sk.init_slice(hex_slice_mut!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81")).unwrap(); + assert_eq!(sk.as_slice(), + hex_slice!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81")); // "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('').digest()) let nonce = Nonce::deterministic([], &sk); @@ -547,7 +586,7 @@ mod test { // # Decrease the secret key by one // sk = 0x09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f80 - let sk = SecretKey::from_slice(hex_slice!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f80")).unwrap(); + sk.init_slice(hex_slice_mut!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f80")).unwrap(); // "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('').digest()) let nonce = Nonce::deterministic([], &sk); @@ -559,14 +598,6 @@ mod test { assert_eq!(nonce.as_slice(), hex_slice!("355c589ff662c838aee454d62b12c50a87b7e95ede2431c7cfa40b6ba2fddccd")); } - - #[bench] - pub fn sequence_iterate(bh: &mut Bencher) { - let mut s = Secp256k1::new().unwrap(); - let (sk, _) = s.generate_keypair(true); - let mut iter = sk.sequence(true); - bh.iter(|| iter.next()) - } } diff --git a/src/macros.rs b/src/macros.rs index f29bf45..33dad7a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -27,6 +27,7 @@ macro_rules! impl_array_newtype( } #[inline] + #[allow(dead_code)] /// Provides an immutable view into the object from index `s` inclusive to `e` exclusive pub fn slice<'a>(&'a self, s: uint, e: uint) -> &'a [$ty] { let &$thing(ref dat) = self; @@ -34,6 +35,7 @@ macro_rules! impl_array_newtype( } #[inline] + #[allow(dead_code)] /// Provides an immutable view into the object, up to index `n` exclusive pub fn slice_to<'a>(&'a self, n: uint) -> &'a [$ty] { let &$thing(ref dat) = self; @@ -41,6 +43,7 @@ macro_rules! impl_array_newtype( } #[inline] + #[allow(dead_code)] /// Provides an immutable view into the object, starting from index `n` pub fn slice_from<'a>(&'a self, n: uint) -> &'a [$ty] { let &$thing(ref dat) = self; @@ -62,6 +65,7 @@ macro_rules! impl_array_newtype( } #[inline] + #[allow(dead_code)] /// Returns the length of the object as an array pub fn len(&self) -> uint { $len } } @@ -129,3 +133,10 @@ macro_rules! hex_slice( ) ) +macro_rules! hex_slice_mut( + ($s:expr) => ( + $s.from_hex().unwrap().as_mut_slice() + ) +) + + diff --git a/src/secp256k1.rs b/src/secp256k1.rs index dd62976..524d2f1 100644 --- a/src/secp256k1.rs +++ b/src/secp256k1.rs @@ -37,6 +37,7 @@ #![warn(missing_doc)] extern crate "rust-crypto" as crypto; +extern crate secretdata; extern crate libc; extern crate serialize; @@ -44,13 +45,9 @@ extern crate sync; extern crate test; use std::intrinsics::copy_nonoverlapping_memory; -use std::io::IoResult; -use std::rand::{OsRng, Rng, SeedableRng}; use libc::c_int; use sync::one::{Once, ONCE_INIT}; -use crypto::fortuna::Fortuna; - mod macros; pub mod constants; pub mod ffi; @@ -135,11 +132,6 @@ pub type Result = ::std::prelude::Result; static mut Secp256k1_init : Once = ONCE_INIT; -/// The secp256k1 engine, used to execute all signature operations -pub struct Secp256k1 { - 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 @@ -153,114 +145,86 @@ pub fn init() { } } -impl Secp256k1 { - /// Constructs a new secp256k1 engine. - pub fn new() -> IoResult { - init(); - let mut osrng = try!(OsRng::new()); - let mut seed = [0, ..2048]; - osrng.fill_bytes(seed.as_mut_slice()); - Ok(Secp256k1 { rng: SeedableRng::from_seed(seed.as_slice()) }) - } - - /// Generates a random keypair. Convenience function for `key::SecretKey::new` - /// and `key::PublicKey::from_secret_key`; call those functions directly for - /// batch key generation. - #[inline] - pub fn generate_keypair(&mut self, compressed: bool) - -> (key::SecretKey, key::PublicKey) { - let sk = key::SecretKey::new(&mut self.rng); - (sk, key::PublicKey::from_secret_key(&sk, compressed)) - } - - /// Generates a random nonce. Convenience function for `key::Nonce::new`; call - /// that function directly for batch nonce generation - #[inline] - pub fn generate_nonce(&mut self) -> key::Nonce { - key::Nonce::new(&mut self.rng) - } - - /// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce` - pub fn sign(&self, msg: &[u8], sk: &key::SecretKey, nonce: &key::Nonce) - -> Result { - 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(), msg.len() as c_int, - sig.as_mut_slice().as_mut_ptr(), &mut len, - sk.as_ptr(), nonce.as_ptr()) != 1 { - return Err(InvalidNonce); - } - // This assertation is probably too late :) - assert!(len as uint <= constants::MAX_SIGNATURE_SIZE); - }; - Ok(Signature(len as uint, sig)) - } +/// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce` +pub fn sign<'a>(msg: &[u8], sk: &key::SecretKey<'a>, nonce: &key::Nonce) + -> Result { + 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(), msg.len() as c_int, + sig.as_mut_slice().as_mut_ptr(), &mut len, + sk.as_ptr(), nonce.as_ptr()) != 1 { + return Err(InvalidNonce); + } + // This assertation is probably too late :) + assert!(len as uint <= constants::MAX_SIGNATURE_SIZE); + }; + Ok(Signature(len as uint, sig)) +} /// Constructs a compact signature for `msg` using the secret key `sk` - pub fn sign_compact(&self, msg: &[u8], sk: &key::SecretKey, nonce: &key::Nonce) - -> Result<(Signature, RecoveryId)> { - let mut sig = [0, ..constants::MAX_SIGNATURE_SIZE]; - let mut recid = 0; - unsafe { - if ffi::secp256k1_ecdsa_sign_compact(msg.as_ptr(), msg.len() as c_int, - sig.as_mut_slice().as_mut_ptr(), sk.as_ptr(), - nonce.as_ptr(), &mut recid) != 1 { - return Err(InvalidNonce); - } - }; - Ok((Signature(constants::MAX_COMPACT_SIGNATURE_SIZE, sig), RecoveryId(recid))) - } - - /// Determines the public key for which `sig` is a valid signature for - /// `msg`. Returns through the out-pointer `pubkey`. - pub fn recover_compact(&self, msg: &[u8], sig: &[u8], - compressed: bool, recid: RecoveryId) - -> Result { - let mut pk = key::PublicKey::new(compressed); - let RecoveryId(recid) = recid; - - unsafe { - let mut len = 0; - if ffi::secp256k1_ecdsa_recover_compact(msg.as_ptr(), msg.len() as c_int, - sig.as_ptr(), pk.as_mut_ptr(), &mut len, - if compressed {1} else {0}, - recid) != 1 { - return Err(InvalidSignature); - } - assert_eq!(len as uint, pk.len()); - }; - Ok(pk) - } - - /// 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. - #[inline] - pub fn verify(msg: &[u8], sig: &Signature, pk: &key::PublicKey) -> Result<()> { - Secp256k1::verify_raw(msg, sig.as_slice(), 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: &[u8], sig: &[u8], pk: &key::PublicKey) -> Result<()> { - init(); // This is a static function, so we have to init - let res = unsafe { - ffi::secp256k1_ecdsa_verify(msg.as_ptr(), msg.len() as c_int, - sig.as_ptr(), sig.len() as c_int, - pk.as_ptr(), pk.len() as c_int) - }; - - match res { - 1 => Ok(()), - 0 => Err(IncorrectSignature), - -1 => Err(InvalidPublicKey), - -2 => Err(InvalidSignature), - _ => unreachable!() +pub fn sign_compact<'a>(msg: &[u8], sk: &key::SecretKey<'a>, nonce: &key::Nonce) + -> Result<(Signature, RecoveryId)> { + let mut sig = [0, ..constants::MAX_SIGNATURE_SIZE]; + let mut recid = 0; + unsafe { + if ffi::secp256k1_ecdsa_sign_compact(msg.as_ptr(), msg.len() as c_int, + sig.as_mut_slice().as_mut_ptr(), sk.as_ptr(), + nonce.as_ptr(), &mut recid) != 1 { + return Err(InvalidNonce); } + }; + Ok((Signature(constants::MAX_COMPACT_SIGNATURE_SIZE, sig), RecoveryId(recid))) +} + +/// Determines the public key for which `sig` is a valid signature for +/// `msg`. Returns through the out-pointer `pubkey`. +pub fn recover_compact(msg: &[u8], sig: &[u8], + compressed: bool, recid: RecoveryId) + -> Result { + let mut pk = key::PublicKey::new(compressed); + let RecoveryId(recid) = recid; + + unsafe { + let mut len = 0; + if ffi::secp256k1_ecdsa_recover_compact(msg.as_ptr(), msg.len() as c_int, + sig.as_ptr(), pk.as_mut_ptr(), &mut len, + if compressed {1} else {0}, + recid) != 1 { + return Err(InvalidSignature); + } + assert_eq!(len as uint, pk.len()); + }; + Ok(pk) +} + +/// 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. +#[inline] +pub fn verify(msg: &[u8], sig: &Signature, pk: &key::PublicKey) -> Result<()> { + verify_raw(msg, sig.as_slice(), 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: &[u8], sig: &[u8], pk: &key::PublicKey) -> Result<()> { + init(); // This is a static function, so we have to init + let res = unsafe { + ffi::secp256k1_ecdsa_verify(msg.as_ptr(), msg.len() as c_int, + sig.as_ptr(), sig.len() as c_int, + pk.as_ptr(), pk.len() as c_int) + }; + + match res { + 1 => Ok(()), + 0 => Err(IncorrectSignature), + -1 => Err(InvalidPublicKey), + -2 => Err(InvalidSignature), + _ => unreachable!() } } @@ -272,9 +236,9 @@ mod tests { use test::{Bencher, black_box}; - use key::{PublicKey, Nonce}; - use super::{Secp256k1, Signature}; - use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature}; + use key::{SecretKey, PublicKey, Nonce}; + use super::{verify, sign, sign_compact, recover_compact}; + use super::{Signature, InvalidPublicKey, IncorrectSignature, InvalidSignature}; #[test] fn invalid_pubkey() { @@ -284,126 +248,134 @@ mod tests { rand::task_rng().fill_bytes(msg.as_mut_slice()); - assert_eq!(Secp256k1::verify(msg.as_mut_slice(), &sig, &pk), Err(InvalidPublicKey)); + assert_eq!(verify(msg.as_mut_slice(), &sig, &pk), Err(InvalidPublicKey)); } #[test] fn valid_pubkey_uncompressed() { - let mut s = Secp256k1::new().unwrap(); - - let (_, pk) = s.generate_keypair(false); + let mut sk = SecretKey::new(); + sk.init_rng(&mut rand::task_rng()); + let pk = PublicKey::from_secret_key(&sk, false); let mut msg = Vec::from_elem(32, 0u8); let sig = Signature::from_slice([0, ..72]).unwrap(); rand::task_rng().fill_bytes(msg.as_mut_slice()); - assert_eq!(Secp256k1::verify(msg.as_mut_slice(), &sig, &pk), Err(InvalidSignature)); + assert_eq!(verify(msg.as_mut_slice(), &sig, &pk), Err(InvalidSignature)); } #[test] fn valid_pubkey_compressed() { - let mut s = Secp256k1::new().unwrap(); + let mut sk = SecretKey::new(); + sk.init_rng(&mut rand::task_rng()); + let pk = PublicKey::from_secret_key(&sk, true); - let (_, pk) = s.generate_keypair(true); let mut msg = Vec::from_elem(32, 0u8); let sig = Signature::from_slice([0, ..72]).unwrap(); rand::task_rng().fill_bytes(msg.as_mut_slice()); - assert_eq!(Secp256k1::verify(msg.as_mut_slice(), &sig, &pk), Err(InvalidSignature)); + assert_eq!(verify(msg.as_mut_slice(), &sig, &pk), Err(InvalidSignature)); } #[test] - fn sign() { - let mut s = Secp256k1::new().unwrap(); + fn sign_random() { + let mut rng = rand::task_rng(); + + let mut sk = SecretKey::new(); + sk.init_rng(&mut rng); let mut msg = [0u8, ..32]; - rand::task_rng().fill_bytes(msg); + rng.fill_bytes(msg); - let (sk, _) = s.generate_keypair(false); - let nonce = s.generate_nonce(); + let nonce = Nonce::new(&mut rng); - s.sign(msg.as_slice(), &sk, &nonce).unwrap(); + sign(msg.as_slice(), &sk, &nonce).unwrap(); } #[test] fn sign_and_verify() { - let mut s = Secp256k1::new().unwrap(); + let mut rng = rand::task_rng(); - let mut msg = Vec::from_elem(32, 0u8); - rand::task_rng().fill_bytes(msg.as_mut_slice()); + let mut sk = SecretKey::new(); + sk.init_rng(&mut rng); + let pk = PublicKey::from_secret_key(&sk, true); + let mut msg = [0u8, ..32]; + rng.fill_bytes(msg); + let nonce = Nonce::new(&mut rng); - let (sk, pk) = s.generate_keypair(false); - let nonce = s.generate_nonce(); - - let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); - - assert_eq!(Secp256k1::verify(msg.as_slice(), &sig, &pk), Ok(())); + let sig = sign(msg.as_slice(), &sk, &nonce).unwrap(); + assert_eq!(verify(msg.as_slice(), &sig, &pk), Ok(())); } #[test] fn sign_and_verify_fail() { - let mut s = Secp256k1::new().unwrap(); + let mut rng = rand::task_rng(); - let mut msg = Vec::from_elem(32, 0u8); - rand::task_rng().fill_bytes(msg.as_mut_slice()); + let mut sk = SecretKey::new(); + sk.init_rng(&mut rng); + let pk = PublicKey::from_secret_key(&sk, true); + let mut msg = [0u8, ..32]; + rng.fill_bytes(msg); + let nonce = Nonce::new(&mut rng); - let (sk, pk) = s.generate_keypair(false); - let nonce = s.generate_nonce(); - - let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); - - rand::task_rng().fill_bytes(msg.as_mut_slice()); - assert_eq!(Secp256k1::verify(msg.as_slice(), &sig, &pk), Err(IncorrectSignature)); + let sig = sign(msg.as_slice(), &sk, &nonce).unwrap(); + rng.fill_bytes(msg.as_mut_slice()); + assert_eq!(verify(msg.as_slice(), &sig, &pk), Err(IncorrectSignature)); } #[test] fn sign_compact_with_recovery() { - let mut s = Secp256k1::new().unwrap(); + let mut rng = rand::task_rng(); + let mut sk = SecretKey::new(); + sk.init_rng(&mut rng); + assert!(sk != SecretKey::new()); + let pk = PublicKey::from_secret_key(&sk, false); + let pk_comp = PublicKey::from_secret_key(&sk, true); let mut msg = [0u8, ..32]; - rand::task_rng().fill_bytes(msg.as_mut_slice()); + rng.fill_bytes(msg); + let nonce = Nonce::new(&mut rng); - let (sk, pk) = s.generate_keypair(false); - let nonce = s.generate_nonce(); + let (sig, recid) = sign_compact(msg.as_slice(), &sk, &nonce).unwrap(); - let (sig, recid) = s.sign_compact(msg.as_slice(), &sk, &nonce).unwrap(); - - assert_eq!(s.recover_compact(msg.as_slice(), sig.as_slice(), false, recid), Ok(pk)); + assert_eq!(recover_compact(msg.as_slice(), sig.as_slice(), false, recid), Ok(pk)); + assert_eq!(recover_compact(msg.as_slice(), sig.as_slice(), true, recid), Ok(pk_comp)); } #[test] fn deterministic_sign() { - let mut msg = [0u8, ..32]; - rand::task_rng().fill_bytes(msg.as_mut_slice()); + let mut rng = rand::task_rng(); - let mut s = Secp256k1::new().unwrap(); - let (sk, pk) = s.generate_keypair(true); + let mut sk = SecretKey::new(); + sk.init_rng(&mut rng); + let pk = PublicKey::from_secret_key(&sk, true); + let mut msg = [0u8, ..32]; + rng.fill_bytes(msg); let nonce = Nonce::deterministic(msg, &sk); - let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); - - assert_eq!(Secp256k1::verify(msg.as_slice(), &sig, &pk), Ok(())); + let sig = sign(msg.as_slice(), &sk, &nonce).unwrap(); + assert_eq!(verify(msg.as_slice(), &sig, &pk), Ok(())); } #[bench] pub fn generate_compressed(bh: &mut Bencher) { - let mut s = Secp256k1::new().unwrap(); + let mut rng = rand::task_rng(); + let mut sk = SecretKey::new(); bh.iter( || { - let (sk, pk) = s.generate_keypair(true); - black_box(sk); - black_box(pk); + sk.init_rng(&mut rng); + black_box(PublicKey::from_secret_key(&sk, true)); }); } #[bench] pub fn generate_uncompressed(bh: &mut Bencher) { - let mut s = Secp256k1::new().unwrap(); + let mut rng = rand::task_rng(); + let mut sk = SecretKey::new(); bh.iter( || { - let (sk, pk) = s.generate_keypair(false); - black_box(sk); - black_box(pk); + sk.init_rng(&mut rng); + black_box(PublicKey::from_secret_key(&sk, false)); }); } }