From d94345f7210eac68031e16a57f6e17666c8e7f39 Mon Sep 17 00:00:00 2001 From: Andrew Poelstra Date: Sun, 31 Aug 2014 22:26:02 -0500 Subject: [PATCH] Generate keys from Fortuna rather than always using the OsRng When creating a Secp256k1, we attach a Fortuna CSRNG seeded from the OS RNG, rather than using the OS RNG all the time. This moves the potential RNG failure to the creation of the object, rather than at every single place that keys are generated. It also reduces trust in the operating system RNG. This does mean that Secp256k1::new() now returns an IoResult while the generate_* methods no longer return Results, so this is a breaking change. Also add a benchmark for key generation. On my system I get: test tests::generate_compressed ... bench: 492990 ns/iter (+/- 27981) test tests::generate_uncompressed ... bench: 495148 ns/iter (+/- 29829) Contrast the numbers with OsRng: test tests::generate_compressed ... bench: 66691 ns/iter (+/- 3640) test tests::generate_uncompressed ... bench: 67148 ns/iter (+/- 3806) Not too shabby :) [breaking-change] --- Cargo.toml | 9 +++-- src/key.rs | 12 +++---- src/secp256k1.rs | 94 ++++++++++++++++++++++++++++-------------------- 3 files changed, 67 insertions(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c0ff678..cadb48d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,13 @@ name = "bitcoin-secp256k1-rs" version = "0.0.1" -authors = [ - "Dawid Ciężarkiewicz " - ] +authors = [ "Dawid Ciężarkiewicz ", + "Andrew Poelstra + rng: Fortuna } /// Does one-time initialization of the secp256k1 engine. Can be called @@ -123,32 +126,27 @@ pub fn init() { impl Secp256k1 { /// Constructs a new secp256k1 engine. - pub fn new() -> Secp256k1 { + pub fn new() -> IoResult { init(); - Secp256k1 { rng: OsRng::new() } + 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. pub fn generate_keypair(&mut self, compressed: bool) - -> Result<(key::SecretKey, key::PublicKey)> { - match self.rng { - Ok(ref mut rng) => { - let sk = key::SecretKey::new(rng); - Ok((sk, key::PublicKey::from_secret_key(&sk, compressed))) - } - Err(ref e) => Err(RngError(e.clone())) - } + -> (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 - pub fn generate_nonce(&mut self) -> Result { - match self.rng { - Ok(ref mut rng) => Ok(key::Nonce::new(rng)), - Err(ref e) => Err(RngError(e.clone())) - } + 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` @@ -226,17 +224,19 @@ impl Secp256k1 { #[cfg(test)] -mod test { +mod tests { use std::rand; use std::rand::Rng; - use key::PublicKey; + use test::Bencher; + + use key::PublicKey; use super::Secp256k1; use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature}; #[test] fn invalid_pubkey() { - let s = Secp256k1::new(); + let s = Secp256k1::new().unwrap(); let mut msg = Vec::from_elem(32, 0u8); let sig = Vec::from_elem(32, 0u8); @@ -249,9 +249,9 @@ mod test { #[test] fn valid_pubkey_uncompressed() { - let mut s = Secp256k1::new(); + let mut s = Secp256k1::new().unwrap(); - let (_, pk) = s.generate_keypair(false).unwrap(); + let (_, pk) = s.generate_keypair(false); let mut msg = Vec::from_elem(32, 0u8); let sig = Vec::from_elem(32, 0u8); @@ -263,9 +263,9 @@ mod test { #[test] fn valid_pubkey_compressed() { - let mut s = Secp256k1::new(); + let mut s = Secp256k1::new().unwrap(); - let (_, pk) = s.generate_keypair(true).unwrap(); + let (_, pk) = s.generate_keypair(true); let mut msg = Vec::from_elem(32, 0u8); let sig = Vec::from_elem(32, 0u8); @@ -276,26 +276,26 @@ mod test { #[test] fn sign() { - let mut s = Secp256k1::new(); + let mut s = Secp256k1::new().unwrap(); let mut msg = [0u8, ..32]; rand::task_rng().fill_bytes(msg); - let (sk, _) = s.generate_keypair(false).unwrap(); - let nonce = s.generate_nonce().unwrap(); + let (sk, _) = s.generate_keypair(false); + let nonce = s.generate_nonce(); s.sign(msg.as_slice(), &sk, &nonce).unwrap(); } #[test] fn sign_and_verify() { - let mut s = Secp256k1::new(); + let mut s = Secp256k1::new().unwrap(); let mut msg = Vec::from_elem(32, 0u8); rand::task_rng().fill_bytes(msg.as_mut_slice()); - let (sk, pk) = s.generate_keypair(false).unwrap(); - let nonce = s.generate_nonce().unwrap(); + let (sk, pk) = s.generate_keypair(false); + let nonce = s.generate_nonce(); let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); @@ -304,13 +304,13 @@ mod test { #[test] fn sign_and_verify_fail() { - let mut s = Secp256k1::new(); + let mut s = Secp256k1::new().unwrap(); let mut msg = Vec::from_elem(32, 0u8); rand::task_rng().fill_bytes(msg.as_mut_slice()); - let (sk, pk) = s.generate_keypair(false).unwrap(); - let nonce = s.generate_nonce().unwrap(); + let (sk, pk) = s.generate_keypair(false); + let nonce = s.generate_nonce(); let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); @@ -320,16 +320,32 @@ mod test { #[test] fn sign_compact_with_recovery() { - let mut s = Secp256k1::new(); + let mut s = Secp256k1::new().unwrap(); let mut msg = [0u8, ..32]; rand::task_rng().fill_bytes(msg.as_mut_slice()); - let (sk, pk) = s.generate_keypair(false).unwrap(); - let nonce = s.generate_nonce().unwrap(); + let (sk, pk) = s.generate_keypair(false); + let nonce = s.generate_nonce(); 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)); } + + #[bench] + pub fn generate_compressed(bh: &mut Bencher) { + let mut s = Secp256k1::new().unwrap(); + bh.iter( || { + let (_, _) = s.generate_keypair(true); + }); + } + + #[bench] + pub fn generate_uncompressed(bh: &mut Bencher) { + let mut s = Secp256k1::new().unwrap(); + bh.iter( || { + let (_, _) = s.generate_keypair(false); + }); + } }