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.
This commit is contained in:
Andrew Poelstra 2014-09-11 22:36:15 -05:00
parent 62504165e4
commit 9889090784
4 changed files with 265 additions and 248 deletions

View File

@ -1,7 +1,7 @@
[package] [package]
name = "bitcoin-secp256k1-rs" name = "bitcoin-secp256k1-rs"
version = "0.0.1" version = "0.1.1"
authors = [ "Dawid Ciężarkiewicz <dpc@ucore.info>", authors = [ "Dawid Ciężarkiewicz <dpc@ucore.info>",
"Andrew Poelstra <apoelstra@wpsoftware.net" ] "Andrew Poelstra <apoelstra@wpsoftware.net" ]
@ -12,3 +12,6 @@ path = "src/secp256k1.rs"
[dependencies.rust-crypto] [dependencies.rust-crypto]
git = "https://github.com/DaGenix/rust-crypto.git" git = "https://github.com/DaGenix/rust-crypto.git"
[dependencies.secretdata]
git = "https://github.com/apoelstra/secretdata.git"

View File

@ -17,10 +17,13 @@
use std::intrinsics::copy_nonoverlapping_memory; use std::intrinsics::copy_nonoverlapping_memory;
use std::cmp; use std::cmp;
use std::default::Default;
use std::fmt; use std::fmt;
use std::ptr::zero_memory;
use std::rand::Rng; use std::rand::Rng;
use serialize::{Decoder, Decodable, Encoder, Encodable}; use serialize::{Decoder, Decodable, Encoder, Encodable};
use secretdata::SecretData;
use crypto::digest::Digest; use crypto::digest::Digest;
use crypto::sha2::Sha512; use crypto::sha2::Sha512;
use crypto::hmac::Hmac; use crypto::hmac::Hmac;
@ -36,14 +39,17 @@ pub struct Nonce([u8, ..constants::NONCE_SIZE]);
impl_array_newtype!(Nonce, u8, constants::NONCE_SIZE) impl_array_newtype!(Nonce, u8, constants::NONCE_SIZE)
/// Secret 256-bit key used as `x` in an ECDSA signature /// Secret 256-bit key used as `x` in an ECDSA signature
pub struct SecretKey([u8, ..constants::SECRET_KEY_SIZE]); pub struct SecretKey<'a>(SecretData<'a, SecretKeyData>);
impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE)
/// The number 1 encoded as a secret key /// Secret 256-bit key used as `x` in an ECDSA signature
pub static ONE: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0, struct SecretKeyData([u8, ..constants::SECRET_KEY_SIZE]);
0, 0, 0, 0, 0, 0, 0, 0, impl_array_newtype!(SecretKeyData, u8, constants::SECRET_KEY_SIZE)
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]); impl Default for SecretKeyData {
fn default() -> SecretKeyData {
SecretKeyData([0, ..constants::SECRET_KEY_SIZE])
}
}
/// Public key /// Public key
#[deriving(Clone, PartialEq, Eq, Show)] #[deriving(Clone, PartialEq, Eq, Show)]
@ -98,7 +104,7 @@ impl Nonce {
/// Generates a deterministic nonce by RFC6979 with HMAC-SHA512 /// Generates a deterministic nonce by RFC6979 with HMAC-SHA512
#[inline] #[inline]
#[allow(non_snake_case)] // so we can match the names in the RFC #[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; static HMAC_SIZE: uint = 64;
macro_rules! hmac( 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 /// Creates a new random secret key
#[inline] #[inline]
pub fn new<R:Rng>(rng: &mut R) -> SecretKey { pub fn init_rng<R:Rng>(&'a mut self, rng: &mut R) {
init(); init();
let mut data = random_32_bytes(rng); let mut data = random_32_bytes(rng);
unsafe { unsafe {
@ -165,36 +177,47 @@ impl SecretKey {
data = random_32_bytes(rng); 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] #[inline]
pub fn from_slice(data: &[u8]) -> Result<SecretKey> { pub fn init_slice(&'a mut self, data: &mut [u8]) -> Result<()> {
init(); init();
match data.len() { match data.len() {
constants::SECRET_KEY_SIZE => { constants::SECRET_KEY_SIZE => {
let mut ret = [0, ..constants::SECRET_KEY_SIZE]; let &SecretKey(ref mut selfdata) = self;
unsafe { unsafe {
if ffi::secp256k1_ecdsa_seckey_verify(data.as_ptr()) == 0 { if ffi::secp256k1_ecdsa_seckey_verify(data.as_ptr()) == 0 {
return Err(InvalidSecretKey); return Err(InvalidSecretKey);
} }
copy_nonoverlapping_memory(ret.as_mut_ptr(), copy_nonoverlapping_memory(selfdata.data_mut().as_mut_ptr(),
data.as_ptr(), data.as_ptr(),
data.len()); data.len());
zero_memory(data.as_mut_ptr(), data.len());
} }
Ok(SecretKey(ret)) Ok(())
} }
_ => Err(InvalidSecretKey) _ => 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] #[inline]
/// Adds one secret key to another, modulo the curve order /// Adds one secret key to another, modulo the curve order
/// Marked `unsafe` since you must /// Marked `unsafe` since you must
/// call `init()` (or construct a `Secp256k1`, which does this for you) before /// call `init()` (or construct a `Secp256k1`, which does this for you) before
/// using this function /// using this function
pub fn add_assign(&mut self, other: &SecretKey) -> Result<()> { pub fn add_assign<'b>(&mut self, other: &SecretKey<'b>) -> Result<()> {
init(); init();
unsafe { unsafe {
if ffi::secp256k1_ecdsa_privkey_tweak_add(self.as_mut_ptr(), other.as_ptr()) != 1 { if ffi::secp256k1_ecdsa_privkey_tweak_add(self.as_mut_ptr(), other.as_ptr()) != 1 {
@ -206,24 +229,24 @@ impl SecretKey {
} }
#[inline] #[inline]
/// Returns an iterator for the (sk, pk) pairs starting one after this one, /// Returns an immutable view of the data as a byteslice
/// and incrementing by one each time pub fn as_slice<'b>(&'b self) -> &'b [u8] {
pub fn sequence(&self, compressed: bool) -> Sequence { let &SecretKey(ref selfdata) = self;
Sequence { last_sk: *self, compressed: compressed } 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] #[inline]
fn next(&mut self) -> Option<(SecretKey, PublicKey)> { /// Returns a raw pointer to the underlying secret key data
self.last_sk.add_assign(&ONE).unwrap(); pub fn as_ptr(&self) -> *const u8 {
Some((self.last_sk, PublicKey::from_secret_key(&self.last_sk, self.compressed))) 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. /// Creates a new public key from a secret key.
#[inline] #[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 mut pk = PublicKey::new(compressed);
let compressed = if compressed {1} else {0}; let compressed = if compressed {1} else {0};
let mut len = 0; let mut len = 0;
@ -337,7 +360,7 @@ impl PublicKey {
#[inline] #[inline]
/// Adds the pk corresponding to `other` to the pk `self` in place /// 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(); init();
unsafe { unsafe {
if ffi::secp256k1_ecdsa_pubkey_tweak_add(self.as_mut_ptr(), if ffi::secp256k1_ecdsa_pubkey_tweak_add(self.as_mut_ptr(),
@ -421,9 +444,17 @@ impl <E: Encoder<S>, S> Encodable<E, S> 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 { 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 serialize::hex::FromHex;
use std::rand::task_rng; use std::rand::task_rng;
use test::Bencher; use super::super::{InvalidNonce, InvalidPublicKey, InvalidSecretKey};
use super::super::{Secp256k1, InvalidNonce, InvalidPublicKey, InvalidSecretKey};
use super::{Nonce, PublicKey, SecretKey}; use super::{Nonce, PublicKey, SecretKey};
#[test] #[test]
@ -442,17 +471,16 @@ mod test {
let n = Nonce::from_slice([1, ..31]); let n = Nonce::from_slice([1, ..31]);
assert_eq!(n, Err(InvalidNonce)); assert_eq!(n, Err(InvalidNonce));
let n = SecretKey::from_slice([1, ..32]); let mut n = SecretKey::new();
assert!(n.is_ok()); assert_eq!(n.init_slice([1, ..32]), Ok(()));
} }
#[test] #[test]
fn skey_from_slice() { fn skey_from_slice() {
let sk = SecretKey::from_slice([1, ..31]); let mut sk = SecretKey::new();
assert_eq!(sk, Err(InvalidSecretKey)); assert_eq!(sk.init_slice([1, ..31]), Err(InvalidSecretKey));
let mut sk = SecretKey::new();
let sk = SecretKey::from_slice([1, ..32]); assert_eq!(sk.init_slice([1, ..32]), Ok(()));
assert!(sk.is_ok());
} }
#[test] #[test]
@ -471,14 +499,17 @@ mod test {
#[test] #[test]
fn keypair_slice_round_trip() { 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!(sk1, sk2);
assert_eq!(SecretKey::from_slice(sk1.as_slice()), Ok(sk1));
let pk1 = PublicKey::from_secret_key(&sk1, false);
assert_eq!(PublicKey::from_slice(pk1.as_slice()), Ok(pk1)); assert_eq!(PublicKey::from_slice(pk1.as_slice()), Ok(pk1));
let pk2 = PublicKey::from_secret_key(&sk1, true);
let (sk2, pk2) = s.generate_keypair(false);
assert_eq!(SecretKey::from_slice(sk2.as_slice()), Ok(sk2));
assert_eq!(PublicKey::from_slice(pk2.as_slice()), Ok(pk2)); assert_eq!(PublicKey::from_slice(pk2.as_slice()), Ok(pk2));
} }
@ -491,28 +522,33 @@ mod test {
#[test] #[test]
fn invalid_secret_key() { fn invalid_secret_key() {
let mut sk = SecretKey::new();
// Zero // Zero
assert_eq!(SecretKey::from_slice([0, ..32]), Err(InvalidSecretKey)); assert_eq!(sk.init_slice([0, ..32]), Err(InvalidSecretKey));
// -1 // -1
assert_eq!(SecretKey::from_slice([0xff, ..32]), Err(InvalidSecretKey)); assert_eq!(sk.init_slice([0xff, ..32]), Err(InvalidSecretKey));
// Top of range // Top of range
assert!(SecretKey::from_slice([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, assert_eq!(sk.init_slice([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40]).is_ok()); 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x40]), Ok(()));
// One past top of range // One past top of range
assert!(SecretKey::from_slice([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, assert_eq!(sk.init_slice([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE,
0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B,
0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41]).is_err()); 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41]), Err(InvalidSecretKey));
} }
#[test] #[test]
fn test_addition() { 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 sk1 = SecretKey::new();
let (mut sk2, mut pk2) = s.generate_keypair(true); 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_eq!(PublicKey::from_secret_key(&sk1, true), pk1);
assert!(sk1.add_assign(&sk2).is_ok()); assert!(sk1.add_assign(&sk2).is_ok());
@ -533,7 +569,10 @@ mod test {
// from ecdsa.curves import SECP256k1 // from ecdsa.curves import SECP256k1
// # This key was generated randomly // # This key was generated randomly
// sk = 0x09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81 // 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()) // "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('').digest())
let nonce = Nonce::deterministic([], &sk); let nonce = Nonce::deterministic([], &sk);
@ -547,7 +586,7 @@ mod test {
// # Decrease the secret key by one // # Decrease the secret key by one
// sk = 0x09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f80 // 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()) // "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('').digest())
let nonce = Nonce::deterministic([], &sk); let nonce = Nonce::deterministic([], &sk);
@ -559,14 +598,6 @@ mod test {
assert_eq!(nonce.as_slice(), assert_eq!(nonce.as_slice(),
hex_slice!("355c589ff662c838aee454d62b12c50a87b7e95ede2431c7cfa40b6ba2fddccd")); 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())
}
} }

View File

@ -27,6 +27,7 @@ macro_rules! impl_array_newtype(
} }
#[inline] #[inline]
#[allow(dead_code)]
/// Provides an immutable view into the object from index `s` inclusive to `e` exclusive /// 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] { pub fn slice<'a>(&'a self, s: uint, e: uint) -> &'a [$ty] {
let &$thing(ref dat) = self; let &$thing(ref dat) = self;
@ -34,6 +35,7 @@ macro_rules! impl_array_newtype(
} }
#[inline] #[inline]
#[allow(dead_code)]
/// Provides an immutable view into the object, up to index `n` exclusive /// Provides an immutable view into the object, up to index `n` exclusive
pub fn slice_to<'a>(&'a self, n: uint) -> &'a [$ty] { pub fn slice_to<'a>(&'a self, n: uint) -> &'a [$ty] {
let &$thing(ref dat) = self; let &$thing(ref dat) = self;
@ -41,6 +43,7 @@ macro_rules! impl_array_newtype(
} }
#[inline] #[inline]
#[allow(dead_code)]
/// Provides an immutable view into the object, starting from index `n` /// Provides an immutable view into the object, starting from index `n`
pub fn slice_from<'a>(&'a self, n: uint) -> &'a [$ty] { pub fn slice_from<'a>(&'a self, n: uint) -> &'a [$ty] {
let &$thing(ref dat) = self; let &$thing(ref dat) = self;
@ -62,6 +65,7 @@ macro_rules! impl_array_newtype(
} }
#[inline] #[inline]
#[allow(dead_code)]
/// Returns the length of the object as an array /// Returns the length of the object as an array
pub fn len(&self) -> uint { $len } 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()
)
)

View File

@ -37,6 +37,7 @@
#![warn(missing_doc)] #![warn(missing_doc)]
extern crate "rust-crypto" as crypto; extern crate "rust-crypto" as crypto;
extern crate secretdata;
extern crate libc; extern crate libc;
extern crate serialize; extern crate serialize;
@ -44,13 +45,9 @@ extern crate sync;
extern crate test; extern crate test;
use std::intrinsics::copy_nonoverlapping_memory; use std::intrinsics::copy_nonoverlapping_memory;
use std::io::IoResult;
use std::rand::{OsRng, Rng, SeedableRng};
use libc::c_int; use libc::c_int;
use sync::one::{Once, ONCE_INIT}; use sync::one::{Once, ONCE_INIT};
use crypto::fortuna::Fortuna;
mod macros; mod macros;
pub mod constants; pub mod constants;
pub mod ffi; pub mod ffi;
@ -135,11 +132,6 @@ pub type Result<T> = ::std::prelude::Result<T, Error>;
static mut Secp256k1_init : Once = ONCE_INIT; 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 /// Does one-time initialization of the secp256k1 engine. Can be called
/// multiple times, and is called by the `Secp256k1` constructor. This /// multiple times, and is called by the `Secp256k1` constructor. This
/// only needs to be called directly if you are using the library without /// only needs to be called directly if you are using the library without
@ -153,114 +145,86 @@ pub fn init() {
} }
} }
impl Secp256k1 { /// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce`
/// Constructs a new secp256k1 engine. pub fn sign<'a>(msg: &[u8], sk: &key::SecretKey<'a>, nonce: &key::Nonce)
pub fn new() -> IoResult<Secp256k1> { -> Result<Signature> {
init(); let mut sig = [0, ..constants::MAX_SIGNATURE_SIZE];
let mut osrng = try!(OsRng::new()); let mut len = constants::MAX_SIGNATURE_SIZE as c_int;
let mut seed = [0, ..2048]; unsafe {
osrng.fill_bytes(seed.as_mut_slice()); if ffi::secp256k1_ecdsa_sign(msg.as_ptr(), msg.len() as c_int,
Ok(Secp256k1 { rng: SeedableRng::from_seed(seed.as_slice()) }) sig.as_mut_slice().as_mut_ptr(), &mut len,
} sk.as_ptr(), nonce.as_ptr()) != 1 {
return Err(InvalidNonce);
/// Generates a random keypair. Convenience function for `key::SecretKey::new` }
/// and `key::PublicKey::from_secret_key`; call those functions directly for // This assertation is probably too late :)
/// batch key generation. assert!(len as uint <= constants::MAX_SIGNATURE_SIZE);
#[inline] };
pub fn generate_keypair(&mut self, compressed: bool) Ok(Signature(len as uint, sig))
-> (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<Signature> {
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` /// Constructs a compact signature for `msg` using the secret key `sk`
pub fn sign_compact(&self, msg: &[u8], sk: &key::SecretKey, nonce: &key::Nonce) pub fn sign_compact<'a>(msg: &[u8], sk: &key::SecretKey<'a>, nonce: &key::Nonce)
-> Result<(Signature, RecoveryId)> { -> Result<(Signature, RecoveryId)> {
let mut sig = [0, ..constants::MAX_SIGNATURE_SIZE]; let mut sig = [0, ..constants::MAX_SIGNATURE_SIZE];
let mut recid = 0; let mut recid = 0;
unsafe { unsafe {
if ffi::secp256k1_ecdsa_sign_compact(msg.as_ptr(), msg.len() as c_int, if ffi::secp256k1_ecdsa_sign_compact(msg.as_ptr(), msg.len() as c_int,
sig.as_mut_slice().as_mut_ptr(), sk.as_ptr(), sig.as_mut_slice().as_mut_ptr(), sk.as_ptr(),
nonce.as_ptr(), &mut recid) != 1 { nonce.as_ptr(), &mut recid) != 1 {
return Err(InvalidNonce); 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<key::PublicKey> {
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!()
} }
};
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<key::PublicKey> {
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 test::{Bencher, black_box};
use key::{PublicKey, Nonce}; use key::{SecretKey, PublicKey, Nonce};
use super::{Secp256k1, Signature}; use super::{verify, sign, sign_compact, recover_compact};
use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature}; use super::{Signature, InvalidPublicKey, IncorrectSignature, InvalidSignature};
#[test] #[test]
fn invalid_pubkey() { fn invalid_pubkey() {
@ -284,126 +248,134 @@ mod tests {
rand::task_rng().fill_bytes(msg.as_mut_slice()); 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] #[test]
fn valid_pubkey_uncompressed() { fn valid_pubkey_uncompressed() {
let mut s = Secp256k1::new().unwrap(); let mut sk = SecretKey::new();
sk.init_rng(&mut rand::task_rng());
let (_, pk) = s.generate_keypair(false); let pk = PublicKey::from_secret_key(&sk, false);
let mut msg = Vec::from_elem(32, 0u8); let mut msg = Vec::from_elem(32, 0u8);
let sig = Signature::from_slice([0, ..72]).unwrap(); let sig = Signature::from_slice([0, ..72]).unwrap();
rand::task_rng().fill_bytes(msg.as_mut_slice()); 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] #[test]
fn valid_pubkey_compressed() { 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 mut msg = Vec::from_elem(32, 0u8);
let sig = Signature::from_slice([0, ..72]).unwrap(); let sig = Signature::from_slice([0, ..72]).unwrap();
rand::task_rng().fill_bytes(msg.as_mut_slice()); 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] #[test]
fn sign() { fn sign_random() {
let mut s = Secp256k1::new().unwrap(); let mut rng = rand::task_rng();
let mut sk = SecretKey::new();
sk.init_rng(&mut rng);
let mut msg = [0u8, ..32]; let mut msg = [0u8, ..32];
rand::task_rng().fill_bytes(msg); rng.fill_bytes(msg);
let (sk, _) = s.generate_keypair(false); let nonce = Nonce::new(&mut rng);
let nonce = s.generate_nonce();
s.sign(msg.as_slice(), &sk, &nonce).unwrap(); sign(msg.as_slice(), &sk, &nonce).unwrap();
} }
#[test] #[test]
fn sign_and_verify() { fn sign_and_verify() {
let mut s = Secp256k1::new().unwrap(); let mut rng = rand::task_rng();
let mut msg = Vec::from_elem(32, 0u8); let mut sk = SecretKey::new();
rand::task_rng().fill_bytes(msg.as_mut_slice()); 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 sig = sign(msg.as_slice(), &sk, &nonce).unwrap();
let nonce = s.generate_nonce(); assert_eq!(verify(msg.as_slice(), &sig, &pk), Ok(()));
let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap();
assert_eq!(Secp256k1::verify(msg.as_slice(), &sig, &pk), Ok(()));
} }
#[test] #[test]
fn sign_and_verify_fail() { 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); let mut sk = SecretKey::new();
rand::task_rng().fill_bytes(msg.as_mut_slice()); 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 sig = sign(msg.as_slice(), &sk, &nonce).unwrap();
let nonce = s.generate_nonce(); rng.fill_bytes(msg.as_mut_slice());
assert_eq!(verify(msg.as_slice(), &sig, &pk), Err(IncorrectSignature));
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));
} }
#[test] #[test]
fn sign_compact_with_recovery() { 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]; 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 (sig, recid) = sign_compact(msg.as_slice(), &sk, &nonce).unwrap();
let nonce = s.generate_nonce();
let (sig, recid) = s.sign_compact(msg.as_slice(), &sk, &nonce).unwrap(); 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));
assert_eq!(s.recover_compact(msg.as_slice(), sig.as_slice(), false, recid), Ok(pk));
} }
#[test] #[test]
fn deterministic_sign() { fn deterministic_sign() {
let mut msg = [0u8, ..32]; let mut rng = rand::task_rng();
rand::task_rng().fill_bytes(msg.as_mut_slice());
let mut s = Secp256k1::new().unwrap(); let mut sk = SecretKey::new();
let (sk, pk) = s.generate_keypair(true); 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 nonce = Nonce::deterministic(msg, &sk);
let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); let sig = sign(msg.as_slice(), &sk, &nonce).unwrap();
assert_eq!(verify(msg.as_slice(), &sig, &pk), Ok(()));
assert_eq!(Secp256k1::verify(msg.as_slice(), &sig, &pk), Ok(()));
} }
#[bench] #[bench]
pub fn generate_compressed(bh: &mut Bencher) { 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( || { bh.iter( || {
let (sk, pk) = s.generate_keypair(true); sk.init_rng(&mut rng);
black_box(sk); black_box(PublicKey::from_secret_key(&sk, true));
black_box(pk);
}); });
} }
#[bench] #[bench]
pub fn generate_uncompressed(bh: &mut Bencher) { 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( || { bh.iter( || {
let (sk, pk) = s.generate_keypair(false); sk.init_rng(&mut rng);
black_box(sk); black_box(PublicKey::from_secret_key(&sk, false));
black_box(pk);
}); });
} }
} }