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]
This commit is contained in:
Andrew Poelstra 2014-08-31 22:26:02 -05:00
parent 059c72aa60
commit d94345f721
3 changed files with 67 additions and 48 deletions

View File

@ -2,10 +2,13 @@
name = "bitcoin-secp256k1-rs" name = "bitcoin-secp256k1-rs"
version = "0.0.1" version = "0.0.1"
authors = [ authors = [ "Dawid Ciężarkiewicz <dpc@ucore.info>",
"Dawid Ciężarkiewicz <dpc@ucore.info>" "Andrew Poelstra <apoelstra@wpsoftware.net" ]
]
[lib] [lib]
name = "bitcoin-secp256k1-rs" name = "bitcoin-secp256k1-rs"
path = "src/secp256k1.rs" path = "src/secp256k1.rs"
[dependencies.rust-crypto]
git = "https://github.com/DaGenix/rust-crypto.git"

View File

@ -330,13 +330,13 @@ mod test {
#[test] #[test]
fn keypair_slice_round_trip() { fn keypair_slice_round_trip() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new().unwrap();
let (sk1, pk1) = s.generate_keypair(true).unwrap(); let (sk1, pk1) = s.generate_keypair(true);
assert_eq!(SecretKey::from_slice(sk1.as_slice()), Ok(sk1)); assert_eq!(SecretKey::from_slice(sk1.as_slice()), Ok(sk1));
assert_eq!(PublicKey::from_slice(pk1.as_slice()), Ok(pk1)); assert_eq!(PublicKey::from_slice(pk1.as_slice()), Ok(pk1));
let (sk2, pk2) = s.generate_keypair(false).unwrap(); let (sk2, pk2) = s.generate_keypair(false);
assert_eq!(SecretKey::from_slice(sk2.as_slice()), Ok(sk2)); 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));
} }
@ -368,10 +368,10 @@ mod test {
#[test] #[test]
fn test_addition() { fn test_addition() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new().unwrap();
let (mut sk1, mut pk1) = s.generate_keypair(true).unwrap(); let (mut sk1, mut pk1) = s.generate_keypair(true);
let (mut sk2, mut pk2) = s.generate_keypair(true).unwrap(); let (mut sk2, mut pk2) = s.generate_keypair(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());

View File

@ -36,14 +36,19 @@
#![deny(unused_mut)] #![deny(unused_mut)]
#![warn(missing_doc)] #![warn(missing_doc)]
extern crate "rust-crypto" as crypto;
extern crate libc; extern crate libc;
extern crate sync; extern crate sync;
extern crate test;
use std::io::{IoError, IoResult}; use std::io::IoResult;
use std::rand::OsRng; 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;
@ -92,8 +97,6 @@ pub enum Error {
InvalidSecretKey, InvalidSecretKey,
/// Bad nonce /// Bad nonce
InvalidNonce, InvalidNonce,
/// Rng problem
RngError(IoError),
/// Boolean-returning function returned the wrong boolean /// Boolean-returning function returned the wrong boolean
Unknown Unknown
} }
@ -105,7 +108,7 @@ static mut Secp256k1_init : Once = ONCE_INIT;
/// The secp256k1 engine, used to execute all signature operations /// The secp256k1 engine, used to execute all signature operations
pub struct Secp256k1 { pub struct Secp256k1 {
rng: IoResult<OsRng> rng: Fortuna
} }
/// Does one-time initialization of the secp256k1 engine. Can be called /// Does one-time initialization of the secp256k1 engine. Can be called
@ -123,32 +126,27 @@ pub fn init() {
impl Secp256k1 { impl Secp256k1 {
/// Constructs a new secp256k1 engine. /// Constructs a new secp256k1 engine.
pub fn new() -> Secp256k1 { pub fn new() -> IoResult<Secp256k1> {
init(); 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` /// Generates a random keypair. Convenience function for `key::SecretKey::new`
/// and `key::PublicKey::from_secret_key`; call those functions directly for /// and `key::PublicKey::from_secret_key`; call those functions directly for
/// batch key generation. /// batch key generation.
pub fn generate_keypair(&mut self, compressed: bool) pub fn generate_keypair(&mut self, compressed: bool)
-> Result<(key::SecretKey, key::PublicKey)> { -> (key::SecretKey, key::PublicKey) {
match self.rng { let sk = key::SecretKey::new(&mut self.rng);
Ok(ref mut rng) => { (sk, key::PublicKey::from_secret_key(&sk, compressed))
let sk = key::SecretKey::new(rng);
Ok((sk, key::PublicKey::from_secret_key(&sk, compressed)))
}
Err(ref e) => Err(RngError(e.clone()))
}
} }
/// Generates a random nonce. Convenience function for `key::Nonce::new`; call /// Generates a random nonce. Convenience function for `key::Nonce::new`; call
/// that function directly for batch nonce generation /// that function directly for batch nonce generation
pub fn generate_nonce(&mut self) -> Result<key::Nonce> { pub fn generate_nonce(&mut self) -> key::Nonce {
match self.rng { key::Nonce::new(&mut self.rng)
Ok(ref mut rng) => Ok(key::Nonce::new(rng)),
Err(ref e) => Err(RngError(e.clone()))
}
} }
/// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce` /// Constructs a signature for `msg` using the secret key `sk` and nonce `nonce`
@ -226,17 +224,19 @@ impl Secp256k1 {
#[cfg(test)] #[cfg(test)]
mod test { mod tests {
use std::rand; use std::rand;
use std::rand::Rng; use std::rand::Rng;
use key::PublicKey;
use test::Bencher;
use key::PublicKey;
use super::Secp256k1; use super::Secp256k1;
use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature}; use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature};
#[test] #[test]
fn invalid_pubkey() { fn invalid_pubkey() {
let s = Secp256k1::new(); let s = Secp256k1::new().unwrap();
let mut msg = Vec::from_elem(32, 0u8); let mut msg = Vec::from_elem(32, 0u8);
let sig = Vec::from_elem(32, 0u8); let sig = Vec::from_elem(32, 0u8);
@ -249,9 +249,9 @@ mod test {
#[test] #[test]
fn valid_pubkey_uncompressed() { 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 mut msg = Vec::from_elem(32, 0u8);
let sig = Vec::from_elem(32, 0u8); let sig = Vec::from_elem(32, 0u8);
@ -263,9 +263,9 @@ mod test {
#[test] #[test]
fn valid_pubkey_compressed() { 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 mut msg = Vec::from_elem(32, 0u8);
let sig = Vec::from_elem(32, 0u8); let sig = Vec::from_elem(32, 0u8);
@ -276,26 +276,26 @@ mod test {
#[test] #[test]
fn sign() { fn sign() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new().unwrap();
let mut msg = [0u8, ..32]; let mut msg = [0u8, ..32];
rand::task_rng().fill_bytes(msg); rand::task_rng().fill_bytes(msg);
let (sk, _) = s.generate_keypair(false).unwrap(); let (sk, _) = s.generate_keypair(false);
let nonce = s.generate_nonce().unwrap(); let nonce = s.generate_nonce();
s.sign(msg.as_slice(), &sk, &nonce).unwrap(); s.sign(msg.as_slice(), &sk, &nonce).unwrap();
} }
#[test] #[test]
fn sign_and_verify() { fn sign_and_verify() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new().unwrap();
let mut msg = Vec::from_elem(32, 0u8); let mut msg = Vec::from_elem(32, 0u8);
rand::task_rng().fill_bytes(msg.as_mut_slice()); rand::task_rng().fill_bytes(msg.as_mut_slice());
let (sk, pk) = s.generate_keypair(false).unwrap(); let (sk, pk) = s.generate_keypair(false);
let nonce = s.generate_nonce().unwrap(); let nonce = s.generate_nonce();
let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap();
@ -304,13 +304,13 @@ mod test {
#[test] #[test]
fn sign_and_verify_fail() { fn sign_and_verify_fail() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new().unwrap();
let mut msg = Vec::from_elem(32, 0u8); let mut msg = Vec::from_elem(32, 0u8);
rand::task_rng().fill_bytes(msg.as_mut_slice()); rand::task_rng().fill_bytes(msg.as_mut_slice());
let (sk, pk) = s.generate_keypair(false).unwrap(); let (sk, pk) = s.generate_keypair(false);
let nonce = s.generate_nonce().unwrap(); let nonce = s.generate_nonce();
let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap(); let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap();
@ -320,16 +320,32 @@ mod test {
#[test] #[test]
fn sign_compact_with_recovery() { fn sign_compact_with_recovery() {
let mut s = Secp256k1::new(); let mut s = Secp256k1::new().unwrap();
let mut msg = [0u8, ..32]; let mut msg = [0u8, ..32];
rand::task_rng().fill_bytes(msg.as_mut_slice()); rand::task_rng().fill_bytes(msg.as_mut_slice());
let (sk, pk) = s.generate_keypair(false).unwrap(); let (sk, pk) = s.generate_keypair(false);
let nonce = s.generate_nonce().unwrap(); let nonce = s.generate_nonce();
let (sig, recid) = s.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!(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);
});
}
} }