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:
parent
059c72aa60
commit
d94345f721
|
@ -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"
|
||||||
|
|
||||||
|
|
12
src/key.rs
12
src/key.rs
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue