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"
version = "0.0.1"
authors = [
"Dawid Ciężarkiewicz <dpc@ucore.info>"
]
authors = [ "Dawid Ciężarkiewicz <dpc@ucore.info>",
"Andrew Poelstra <apoelstra@wpsoftware.net" ]
[lib]
name = "bitcoin-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]
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!(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!(PublicKey::from_slice(pk2.as_slice()), Ok(pk2));
}
@ -368,10 +368,10 @@ mod test {
#[test]
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 sk2, mut pk2) = s.generate_keypair(true).unwrap();
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());

View File

@ -36,14 +36,19 @@
#![deny(unused_mut)]
#![warn(missing_doc)]
extern crate "rust-crypto" as crypto;
extern crate libc;
extern crate sync;
extern crate test;
use std::io::{IoError, IoResult};
use std::rand::OsRng;
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;
@ -92,8 +97,6 @@ pub enum Error {
InvalidSecretKey,
/// Bad nonce
InvalidNonce,
/// Rng problem
RngError(IoError),
/// Boolean-returning function returned the wrong boolean
Unknown
}
@ -105,7 +108,7 @@ static mut Secp256k1_init : Once = ONCE_INIT;
/// The secp256k1 engine, used to execute all signature operations
pub struct Secp256k1 {
rng: IoResult<OsRng>
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<Secp256k1> {
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<key::Nonce> {
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);
});
}
}