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
|
||||
|
||||
use std::intrinsics::copy_nonoverlapping_memory;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::rand::Rng;
|
||||
use constants;
|
||||
use ffi;
|
||||
|
||||
use crypto::digest::Digest;
|
||||
use crypto::sha2::Sha512;
|
||||
use crypto::hmac::Hmac;
|
||||
use crypto::mac::Mac;
|
||||
|
||||
use super::init;
|
||||
use super::{Result, InvalidNonce, InvalidPublicKey, InvalidSecretKey, Unknown};
|
||||
|
||||
|
@ -53,6 +59,17 @@ fn random_32_bytes<R:Rng>(rng: &mut R) -> [u8, ..32] {
|
|||
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 {
|
||||
/// Creates a new random nonce
|
||||
#[inline]
|
||||
|
@ -76,6 +93,64 @@ impl Nonce {
|
|||
_ => 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 {
|
||||
|
@ -319,6 +394,7 @@ impl fmt::Show for SecretKey {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serialize::hex::FromHex;
|
||||
use std::rand::task_rng;
|
||||
|
||||
use test::Bencher;
|
||||
|
@ -414,6 +490,41 @@ mod test {
|
|||
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]
|
||||
pub fn sequence_iterate(bh: &mut Bencher) {
|
||||
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 libc;
|
||||
extern crate serialize;
|
||||
extern crate sync;
|
||||
extern crate test;
|
||||
|
||||
|
@ -259,7 +260,7 @@ mod tests {
|
|||
|
||||
use test::{Bencher, black_box};
|
||||
|
||||
use key::PublicKey;
|
||||
use key::{PublicKey, Nonce};
|
||||
use super::{Secp256k1, Signature};
|
||||
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));
|
||||
}
|
||||
|
||||
#[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]
|
||||
pub fn generate_compressed(bh: &mut Bencher) {
|
||||
let mut s = Secp256k1::new().unwrap();
|
||||
|
|
Loading…
Reference in New Issue