Implement deterministic nonce generation with HMAC-SHA512
Testing was done against python-ecdsa; python code in the test case comments.
This commit is contained in:
parent
46f646dabb
commit
17daebf15d
111
src/key.rs
111
src/key.rs
|
@ -16,11 +16,17 @@
|
||||||
//! Public/Private keys
|
//! Public/Private keys
|
||||||
|
|
||||||
use std::intrinsics::copy_nonoverlapping_memory;
|
use std::intrinsics::copy_nonoverlapping_memory;
|
||||||
|
use std::cmp;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::rand::Rng;
|
use std::rand::Rng;
|
||||||
use constants;
|
use constants;
|
||||||
use ffi;
|
use ffi;
|
||||||
|
|
||||||
|
use crypto::digest::Digest;
|
||||||
|
use crypto::sha2::Sha512;
|
||||||
|
use crypto::hmac::Hmac;
|
||||||
|
use crypto::mac::Mac;
|
||||||
|
|
||||||
use super::init;
|
use super::init;
|
||||||
use super::{Result, InvalidNonce, InvalidPublicKey, InvalidSecretKey, Unknown};
|
use super::{Result, InvalidNonce, InvalidPublicKey, InvalidSecretKey, Unknown};
|
||||||
|
|
||||||
|
@ -53,6 +59,17 @@ fn random_32_bytes<R:Rng>(rng: &mut R) -> [u8, ..32] {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// As described in RFC 6979
|
||||||
|
fn bits2octets(data: &[u8]) -> [u8, ..32] {
|
||||||
|
let mut ret = [0, ..32];
|
||||||
|
unsafe {
|
||||||
|
copy_nonoverlapping_memory(ret.as_mut_ptr(),
|
||||||
|
data.as_ptr(),
|
||||||
|
cmp::min(data.len(), 32));
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
impl Nonce {
|
impl Nonce {
|
||||||
/// Creates a new random nonce
|
/// Creates a new random nonce
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -76,6 +93,64 @@ impl Nonce {
|
||||||
_ => Err(InvalidNonce)
|
_ => Err(InvalidNonce)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates a deterministic nonce by RFC6979 with HMAC-SHA512
|
||||||
|
#[inline]
|
||||||
|
#[allow(non_snake_case)] // so we can match the names in the RFC
|
||||||
|
pub fn deterministic(msg: &[u8], key: &SecretKey) -> Nonce {
|
||||||
|
static HMAC_SIZE: uint = 64;
|
||||||
|
|
||||||
|
macro_rules! hmac(
|
||||||
|
($res:expr <- key $key:expr, data $($data:expr),+) => ({
|
||||||
|
let mut hmacker = Hmac::new(Sha512::new(), $key.as_slice());
|
||||||
|
$(hmacker.input($data.as_slice());)+
|
||||||
|
hmacker.raw_result($res.as_mut_slice());
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// Section 3.2a
|
||||||
|
// Goofy block just to avoid marking `msg_hash` as mutable
|
||||||
|
let mut hasher = Sha512::new();
|
||||||
|
hasher.input(msg);
|
||||||
|
let mut x = [0, ..HMAC_SIZE];
|
||||||
|
hasher.result(x.as_mut_slice());
|
||||||
|
let msg_hash = bits2octets(x.as_slice());
|
||||||
|
|
||||||
|
// Section 3.2b
|
||||||
|
let mut V = [0x01u8, ..HMAC_SIZE];
|
||||||
|
// Section 3.2c
|
||||||
|
let mut K = [0x00u8, ..HMAC_SIZE];
|
||||||
|
|
||||||
|
// Section 3.2d
|
||||||
|
hmac!(K <- key K, data V, [0x00], key, msg_hash)
|
||||||
|
|
||||||
|
// Section 3.2e
|
||||||
|
hmac!(V <- key K, data V)
|
||||||
|
|
||||||
|
// Section 3.2f
|
||||||
|
hmac!(K <- key K, data V, [0x01], key, msg_hash)
|
||||||
|
|
||||||
|
// Section 3.2g
|
||||||
|
hmac!(V <- key K, data V)
|
||||||
|
|
||||||
|
// Section 3.2
|
||||||
|
let mut k = Err(InvalidSecretKey);
|
||||||
|
while k.is_err() {
|
||||||
|
// Try to generate the nonce
|
||||||
|
let mut T = [0x00u8, ..HMAC_SIZE];
|
||||||
|
hmac!(T <- key K, data V)
|
||||||
|
|
||||||
|
k = Nonce::from_slice(T.slice_to(constants::NONCE_SIZE));
|
||||||
|
|
||||||
|
// Replace K, V
|
||||||
|
if k.is_err() {
|
||||||
|
hmac!(K <- key K, data V, [0x00])
|
||||||
|
hmac!(V <- key K, data V)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
k.unwrap()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SecretKey {
|
impl SecretKey {
|
||||||
|
@ -319,6 +394,7 @@ impl fmt::Show for SecretKey {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use serialize::hex::FromHex;
|
||||||
use std::rand::task_rng;
|
use std::rand::task_rng;
|
||||||
|
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
@ -414,6 +490,41 @@ mod test {
|
||||||
assert_eq!(PublicKey::from_secret_key(&sk2, true), pk2);
|
assert_eq!(PublicKey::from_secret_key(&sk2, true), pk2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_deterministic() {
|
||||||
|
// nb code in comments is equivalent python
|
||||||
|
|
||||||
|
// from ecdsa import rfc6979
|
||||||
|
// from ecdsa.curves import SECP256k1
|
||||||
|
// # This key was generated randomly
|
||||||
|
// sk = 0x09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81
|
||||||
|
let sk = SecretKey::from_slice(hex_slice!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f81")).unwrap();
|
||||||
|
|
||||||
|
// "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('').digest())
|
||||||
|
let nonce = Nonce::deterministic([], &sk);
|
||||||
|
assert_eq!(nonce.as_slice(),
|
||||||
|
hex_slice!("d954eddd184cac2b60edcd0e6be9ec54d93f633b28b366420d38ed9c346ffe27"));
|
||||||
|
|
||||||
|
// "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('test').digest())
|
||||||
|
let nonce = Nonce::deterministic(b"test", &sk);
|
||||||
|
assert_eq!(nonce.as_slice(),
|
||||||
|
hex_slice!("609cc24acce2f19e46e38a82afc56c1745dee16e04f2b27e24999e1fefeb08bd"));
|
||||||
|
|
||||||
|
// # Decrease the secret key by one
|
||||||
|
// sk = 0x09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f80
|
||||||
|
let sk = SecretKey::from_slice(hex_slice!("09e918bbea76205445e9a73eaad2080a135d1e33e9dd1b3ca8a9a1285e7c1f80")).unwrap();
|
||||||
|
|
||||||
|
// "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('').digest())
|
||||||
|
let nonce = Nonce::deterministic([], &sk);
|
||||||
|
assert_eq!(nonce.as_slice(),
|
||||||
|
hex_slice!("9f45f8d0a28e8956673c8da6db3db86ca4f172f0a2dbd62364fdbf786c7d96df"));
|
||||||
|
|
||||||
|
// "%x" % rfc6979.generate_k(SECP256k1.generator, sk, hashlib.sha512, hashlib.sha512('test').digest())
|
||||||
|
let nonce = Nonce::deterministic(b"test", &sk);
|
||||||
|
assert_eq!(nonce.as_slice(),
|
||||||
|
hex_slice!("355c589ff662c838aee454d62b12c50a87b7e95ede2431c7cfa40b6ba2fddccd"));
|
||||||
|
}
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
pub fn sequence_iterate(bh: &mut Bencher) {
|
pub fn sequence_iterate(bh: &mut Bencher) {
|
||||||
let mut s = Secp256k1::new().unwrap();
|
let mut s = Secp256k1::new().unwrap();
|
||||||
|
|
|
@ -93,3 +93,10 @@ macro_rules! impl_array_newtype(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
macro_rules! hex_slice(
|
||||||
|
($s:expr) => (
|
||||||
|
$s.from_hex().unwrap().as_slice()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
extern crate "rust-crypto" as crypto;
|
extern crate "rust-crypto" as crypto;
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
extern crate serialize;
|
||||||
extern crate sync;
|
extern crate sync;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
|
@ -259,7 +260,7 @@ mod tests {
|
||||||
|
|
||||||
use test::{Bencher, black_box};
|
use test::{Bencher, black_box};
|
||||||
|
|
||||||
use key::PublicKey;
|
use key::{PublicKey, Nonce};
|
||||||
use super::{Secp256k1, Signature};
|
use super::{Secp256k1, Signature};
|
||||||
use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature};
|
use super::{InvalidPublicKey, IncorrectSignature, InvalidSignature};
|
||||||
|
|
||||||
|
@ -360,6 +361,20 @@ mod tests {
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn deterministic_sign() {
|
||||||
|
let mut msg = [0u8, ..32];
|
||||||
|
rand::task_rng().fill_bytes(msg.as_mut_slice());
|
||||||
|
|
||||||
|
let mut s = Secp256k1::new().unwrap();
|
||||||
|
let (sk, pk) = s.generate_keypair(true);
|
||||||
|
let nonce = Nonce::deterministic(msg, &sk);
|
||||||
|
|
||||||
|
let sig = s.sign(msg.as_slice(), &sk, &nonce).unwrap();
|
||||||
|
|
||||||
|
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 s = Secp256k1::new().unwrap();
|
||||||
|
|
Loading…
Reference in New Issue