mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
rfc6979: initial crate (#409)
Splits out the RFC6979 deterministic nonce generation so it can (eventually) be used for DSA as well as ECDSA. The implementation is generic over a `Digest` as well as a `UInt` type as defined by `crypto-bigint`. It's also `no_std` friendly and avoids making heap allocations.
This commit is contained in:
Generated
+13
-4
@@ -161,9 +161,9 @@ name = "ecdsa"
|
||||
version = "0.13.0-pre"
|
||||
dependencies = [
|
||||
"der 0.5.1",
|
||||
"elliptic-curve 0.11.0",
|
||||
"elliptic-curve 0.11.1",
|
||||
"hex-literal",
|
||||
"hmac",
|
||||
"rfc6979",
|
||||
"sha2",
|
||||
"signature",
|
||||
]
|
||||
@@ -221,9 +221,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "elliptic-curve"
|
||||
version = "0.11.0"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be5ff748dcb04505ce82b6447d4bad4ef9cf653a1d811bed2b04083127a44c3f"
|
||||
checksum = "f73d21281779c2c22cade2a4ab34eee34271869942546a364f7d552d30c62434"
|
||||
dependencies = [
|
||||
"crypto-bigint 0.3.2",
|
||||
"der 0.5.1",
|
||||
@@ -473,6 +473,15 @@ dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"crypto-bigint 0.3.2",
|
||||
"hmac",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.16.20"
|
||||
|
||||
+5
-1
@@ -1,3 +1,7 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["ecdsa", "ed25519"]
|
||||
members = [
|
||||
"ecdsa",
|
||||
"ed25519",
|
||||
"rfc6979"
|
||||
]
|
||||
|
||||
+7
-7
@@ -2,12 +2,12 @@
|
||||
name = "ecdsa"
|
||||
version = "0.13.0-pre" # Also update html_root_url in lib.rs when bumping this
|
||||
description = """
|
||||
Signature and elliptic curve types providing interoperable support for the
|
||||
Elliptic Curve Digital Signature Algorithm (ECDSA)
|
||||
Pure Rust implementation of the Elliptic Curve Digital Signature Algorithm
|
||||
(ECDSA) as specified in FIPS 186-4 (Digital Signature Standard)
|
||||
"""
|
||||
authors = ["RustCrypto Developers"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
repository = "https://github.com/RustCrypto/signatures"
|
||||
repository = "https://github.com/RustCrypto/signatures/tree/master/ecdsa"
|
||||
readme = "README.md"
|
||||
categories = ["cryptography", "no-std"]
|
||||
keywords = ["crypto", "ecc", "nist", "secp256k1", "signature"]
|
||||
@@ -15,12 +15,12 @@ edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
[dependencies]
|
||||
elliptic-curve = { version = "0.11", default-features = false, features = ["sec1"] }
|
||||
hmac = { version = "0.11", optional = true, default-features = false }
|
||||
signature = { version = ">= 1.3.1", default-features = false, features = ["rand-preview"] }
|
||||
elliptic-curve = { version = "0.11.1", default-features = false, features = ["sec1"] }
|
||||
signature = { version = ">= 1.3.1, <1.5", default-features = false, features = ["rand-preview"] }
|
||||
|
||||
# optional dependencies
|
||||
der = { version = "0.5", optional = true }
|
||||
rfc6979 = { version = "0", optional = true, path = "../rfc6979" }
|
||||
|
||||
[dev-dependencies]
|
||||
elliptic-curve = { version = "0.11", default-features = false, features = ["dev"] }
|
||||
@@ -37,7 +37,7 @@ hazmat = []
|
||||
pkcs8 = ["elliptic-curve/pkcs8", "der"]
|
||||
pem = ["elliptic-curve/pem", "pkcs8"]
|
||||
serde = ["elliptic-curve/serde"]
|
||||
sign = ["arithmetic", "digest", "hazmat", "hmac"]
|
||||
sign = ["arithmetic", "digest", "hazmat", "rfc6979"]
|
||||
std = ["alloc", "elliptic-curve/std", "signature/std"]
|
||||
verify = ["arithmetic", "digest", "hazmat"]
|
||||
|
||||
|
||||
+7
-3
@@ -1,4 +1,4 @@
|
||||
# RustCrypto: ECDSA
|
||||
# [RustCrypto]: ECDSA
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
@@ -57,8 +57,12 @@ dual licensed as above, without any additional terms or conditions.
|
||||
[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg
|
||||
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
|
||||
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260048-signatures
|
||||
[build-image]: https://github.com/RustCrypto/signatures/workflows/ecdsa/badge.svg?branch=master&event=push
|
||||
[build-link]: https://github.com/RustCrypto/signatures/actions?query=workflow%3Aecdsa
|
||||
[build-image]: https://github.com/RustCrypto/signatures/actions/workflows/ecdsa.yml/badge.svg
|
||||
[build-link]: https://github.com/RustCrypto/signatures/actions/workflows/ecdsa.yml
|
||||
|
||||
[//]: # (links)
|
||||
|
||||
[RustCrypto]: https://github.com/RustCrypto
|
||||
|
||||
[//]: # (footnotes)
|
||||
|
||||
|
||||
+6
-6
@@ -69,10 +69,6 @@ pub mod dev;
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "hazmat")))]
|
||||
pub mod hazmat;
|
||||
|
||||
#[cfg(feature = "sign")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
|
||||
pub mod rfc6979;
|
||||
|
||||
#[cfg(feature = "sign")]
|
||||
mod sign;
|
||||
|
||||
@@ -87,13 +83,17 @@ pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve};
|
||||
// Re-export the `signature` crate (and select types)
|
||||
pub use signature::{self, Error, Result};
|
||||
|
||||
#[cfg(feature = "rfc6979")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))]
|
||||
pub use rfc6979;
|
||||
|
||||
#[cfg(feature = "sign")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
|
||||
pub use sign::SigningKey;
|
||||
pub use crate::sign::SigningKey;
|
||||
|
||||
#[cfg(feature = "verify")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
|
||||
pub use verify::VerifyingKey;
|
||||
pub use crate::verify::VerifyingKey;
|
||||
|
||||
use core::{
|
||||
fmt::{self, Debug},
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
//! Support for computing deterministic ECDSA ephemeral scalar (`k`).
|
||||
//!
|
||||
//! Implementation of the algorithm described in RFC 6979 (Section 3.2):
|
||||
//! <https://tools.ietf.org/html/rfc6979#section-3>
|
||||
|
||||
use elliptic_curve::{
|
||||
generic_array::GenericArray,
|
||||
group::ff::PrimeField,
|
||||
ops::{Invert, Reduce},
|
||||
zeroize::{Zeroize, Zeroizing},
|
||||
FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar,
|
||||
};
|
||||
use hmac::{Hmac, Mac, NewMac};
|
||||
use signature::digest::{BlockInput, FixedOutput, Reset, Update};
|
||||
|
||||
/// Generate ephemeral scalar `k` from the secret scalar and a digest of the
|
||||
/// input message.
|
||||
pub fn generate_k<C, D>(
|
||||
secret_scalar: &NonZeroScalar<C>,
|
||||
msg_digest: D,
|
||||
additional_data: &[u8],
|
||||
) -> Zeroizing<NonZeroScalar<C>>
|
||||
where
|
||||
C: PrimeCurve + ProjectiveArithmetic,
|
||||
D: FixedOutput<OutputSize = FieldSize<C>> + BlockInput + Clone + Default + Reset + Update,
|
||||
Scalar<C>: Invert<Output = Scalar<C>> + Reduce<C::UInt>,
|
||||
{
|
||||
let mut x = secret_scalar.to_repr();
|
||||
let h1 = Scalar::<C>::from_be_bytes_reduced(msg_digest.finalize_fixed()).to_repr();
|
||||
let mut hmac_drbg = HmacDrbg::<D>::new(&x, &h1, additional_data);
|
||||
x.zeroize();
|
||||
|
||||
loop {
|
||||
let mut tmp = FieldBytes::<C>::default();
|
||||
hmac_drbg.generate_into(&mut tmp);
|
||||
if let Some(k) = NonZeroScalar::from_repr(tmp).into() {
|
||||
return Zeroizing::new(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal implementation of `HMAC_DRBG` as described in NIST SP800-90A:
|
||||
/// <https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final>
|
||||
///
|
||||
/// This is a HMAC-based deterministic random bit generator used internally
|
||||
/// to compute a deterministic ECDSA ephemeral scalar `k`.
|
||||
// TODO(tarcieri): use `hmac-drbg` crate when sorpaas/rust-hmac-drbg#3 is merged
|
||||
struct HmacDrbg<D>
|
||||
where
|
||||
D: BlockInput + FixedOutput + Clone + Default + Reset + Update,
|
||||
{
|
||||
/// HMAC key `K` (see RFC 6979 Section 3.2.c)
|
||||
k: Hmac<D>,
|
||||
|
||||
/// Chaining value `V` (see RFC 6979 Section 3.2.c)
|
||||
v: GenericArray<u8, D::OutputSize>,
|
||||
}
|
||||
|
||||
impl<D> HmacDrbg<D>
|
||||
where
|
||||
D: BlockInput + FixedOutput + Clone + Default + Reset + Update,
|
||||
{
|
||||
/// Initialize `HMAC_DRBG`
|
||||
pub fn new(entropy_input: &[u8], nonce: &[u8], additional_data: &[u8]) -> Self {
|
||||
let mut k = Hmac::new(&Default::default());
|
||||
let mut v = GenericArray::default();
|
||||
|
||||
for b in &mut v {
|
||||
*b = 0x01;
|
||||
}
|
||||
|
||||
for i in 0..=1 {
|
||||
k.update(&v);
|
||||
k.update(&[i]);
|
||||
k.update(entropy_input);
|
||||
k.update(nonce);
|
||||
k.update(additional_data);
|
||||
k = Hmac::new_from_slice(&k.finalize().into_bytes()).expect("HMAC error");
|
||||
|
||||
// Steps 3.2.e,g: v = HMAC_k(v)
|
||||
k.update(&v);
|
||||
v = k.finalize_reset().into_bytes();
|
||||
}
|
||||
|
||||
Self { k, v }
|
||||
}
|
||||
|
||||
/// Get the next `HMAC_DRBG` output
|
||||
pub fn generate_into(&mut self, out: &mut [u8]) {
|
||||
for out_chunk in out.chunks_mut(self.v.len()) {
|
||||
self.k.update(&self.v);
|
||||
self.v = self.k.finalize_reset().into_bytes();
|
||||
out_chunk.copy_from_slice(&self.v[..out_chunk.len()]);
|
||||
}
|
||||
|
||||
self.k.update(&self.v);
|
||||
self.k.update(&[0x00]);
|
||||
self.k = Hmac::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("HMAC error");
|
||||
self.k.update(&self.v);
|
||||
self.v = self.k.finalize_reset().into_bytes();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::generate_k;
|
||||
use elliptic_curve::{dev::NonZeroScalar, group::ff::PrimeField};
|
||||
use hex_literal::hex;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
/// Test vector from RFC 6979 Appendix 2.5 (NIST P-256 + SHA-256)
|
||||
/// <https://tools.ietf.org/html/rfc6979#appendix-A.2.5>
|
||||
#[test]
|
||||
fn appendix_2_5_test_vector() {
|
||||
let x = NonZeroScalar::from_repr(
|
||||
hex!("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721").into(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let digest = Sha256::new().chain("sample");
|
||||
let k = generate_k(&x, digest, &[]);
|
||||
|
||||
assert_eq!(
|
||||
k.to_repr().as_slice(),
|
||||
&hex!("a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60")[..]
|
||||
);
|
||||
}
|
||||
}
|
||||
+25
-5
@@ -13,7 +13,9 @@ use elliptic_curve::{
|
||||
ops::{Invert, Reduce},
|
||||
subtle::{Choice, ConstantTimeEq},
|
||||
zeroize::Zeroize,
|
||||
FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar, SecretKey,
|
||||
zeroize::Zeroizing,
|
||||
FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar, ScalarCore,
|
||||
SecretKey,
|
||||
};
|
||||
use signature::{
|
||||
digest::{BlockInput, Digest, FixedOutput, Reset, Update},
|
||||
@@ -176,8 +178,17 @@ where
|
||||
/// computed using the algorithm described in RFC 6979 (Section 3.2):
|
||||
/// <https://tools.ietf.org/html/rfc6979#section-3>
|
||||
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature<C>> {
|
||||
let k = rfc6979::generate_k(&self.inner, msg_digest.clone(), &[]);
|
||||
let x = Zeroizing::new(ScalarCore::<C>::from(self.inner));
|
||||
let msg_scalar = Scalar::<C>::from_be_bytes_reduced(msg_digest.finalize_fixed());
|
||||
let k = Zeroizing::new(
|
||||
NonZeroScalar::<C>::from_uint(*rfc6979::generate_k::<D, _>(
|
||||
x.as_uint(),
|
||||
&C::ORDER,
|
||||
&msg_scalar.to_repr(),
|
||||
&[],
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
Ok(self.inner.try_sign_prehashed(**k, msg_scalar)?.0)
|
||||
}
|
||||
}
|
||||
@@ -209,11 +220,20 @@ where
|
||||
mut rng: impl CryptoRng + RngCore,
|
||||
msg_digest: D,
|
||||
) -> Result<Signature<C>> {
|
||||
let mut added_entropy = FieldBytes::<C>::default();
|
||||
rng.fill_bytes(&mut added_entropy);
|
||||
let mut entropy = FieldBytes::<C>::default();
|
||||
rng.fill_bytes(&mut entropy);
|
||||
|
||||
let k = rfc6979::generate_k(&self.inner, msg_digest.clone(), &added_entropy);
|
||||
let x = Zeroizing::new(ScalarCore::<C>::from(self.inner));
|
||||
let msg_scalar = Scalar::<C>::from_be_bytes_reduced(msg_digest.finalize_fixed());
|
||||
let k = Zeroizing::new(
|
||||
NonZeroScalar::<C>::from_uint(*rfc6979::generate_k::<D, _>(
|
||||
x.as_uint(),
|
||||
&C::ORDER,
|
||||
&msg_scalar.to_repr(),
|
||||
&entropy,
|
||||
))
|
||||
.unwrap(),
|
||||
);
|
||||
Ok(self.inner.try_sign_prehashed(**k, msg_scalar)?.0)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -1,4 +1,4 @@
|
||||
# RustCrypto: Ed25519
|
||||
# [RustCrypto]: Ed25519
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
@@ -57,7 +57,11 @@ dual licensed as above, without any additional terms or conditions.
|
||||
[build-image]: https://github.com/RustCrypto/signatures/workflows/ed25519/badge.svg?branch=master&event=push
|
||||
[build-link]: https://github.com/RustCrypto/signatures/actions?query=workflow%3Aed25519
|
||||
|
||||
[//]: # (general links)
|
||||
[//]: # (links)
|
||||
|
||||
[RustCrypto]: https://github.com/RustCrypto
|
||||
|
||||
[//]: # (footnotes)
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/EdDSA
|
||||
[2]: https://tools.ietf.org/html/rfc8032
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
@@ -0,0 +1,24 @@
|
||||
[package]
|
||||
name = "rfc6979"
|
||||
version = "0.0.0" # Also update html_root_url in lib.rs when bumping this
|
||||
description = """
|
||||
Pure Rust implementation of RFC6979: Deterministic Usage of the
|
||||
Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)
|
||||
"""
|
||||
authors = ["RustCrypto Developers"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
repository = "https://github.com/RustCrypto/signatures/tree/master/rfc6979"
|
||||
readme = "README.md"
|
||||
categories = ["cryptography", "no-std"]
|
||||
keywords = ["dsa", "ecdsa", "signature"]
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
|
||||
[dependencies]
|
||||
crypto-bigint = { version = "0.3", default-features = false, features = ["generic-array", "zeroize"] }
|
||||
hmac = { version = "0.11", default-features = false }
|
||||
zeroize = { version = "1", default-features = false }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
@@ -0,0 +1,55 @@
|
||||
# [RustCrypto]: RFC6979 Deterministic Signatures
|
||||
|
||||
[![crate][crate-image]][crate-link]
|
||||
[![Docs][docs-image]][docs-link]
|
||||
![Apache2/MIT licensed][license-image]
|
||||
![MSRV][rustc-image]
|
||||
[![Project Chat][chat-image]][chat-link]
|
||||
[![Build Status][build-image]][build-link]
|
||||
|
||||
Pure Rust implementation of RFC6979: Deterministic Usage of the
|
||||
Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA).
|
||||
|
||||
Algorithm described in RFC 6979 § 3.2:
|
||||
<https://tools.ietf.org/html/rfc6979#section-3>
|
||||
|
||||
[Documentation][docs-link]
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
This crate requires **Rust 1.56** at a minimum.
|
||||
|
||||
We may change the MSRV in the future, but it will be accompanied by a minor
|
||||
version bump.
|
||||
|
||||
## License
|
||||
|
||||
All crates licensed under either of
|
||||
|
||||
* [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* [MIT license](http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
[//]: # (badges)
|
||||
|
||||
[crate-image]: https://img.shields.io/crates/v/rfc6979.svg
|
||||
[crate-link]: https://crates.io/crates/rfc6979
|
||||
[docs-image]: https://docs.rs/rfc6979/badge.svg
|
||||
[docs-link]: https://docs.rs/rfc6979/
|
||||
[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
|
||||
[rustc-image]: https://img.shields.io/badge/rustc-1.56+-blue.svg
|
||||
[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
|
||||
[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260048-signatures
|
||||
[build-image]: https://github.com/RustCrypto/signatures/actions/workflows/rfc6979.yml/badge.svg
|
||||
[build-link]: https://github.com/RustCrypto/signatures/actions/workflows/rfc6979.yml
|
||||
|
||||
[//]: # (links)
|
||||
|
||||
[RustCrypto]: https://github.com/RustCrypto
|
||||
@@ -0,0 +1,113 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
//! ## Usage
|
||||
//!
|
||||
//! See the documentation for the [`generate_k`] function.
|
||||
|
||||
#![no_std]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![forbid(unsafe_code, clippy::unwrap_used)]
|
||||
#![warn(missing_docs, rust_2018_idioms)]
|
||||
#![doc(
|
||||
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
|
||||
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
|
||||
html_root_url = "https://docs.rs/rfc6979/0.0.0"
|
||||
)]
|
||||
|
||||
use crypto_bigint::{ArrayEncoding, ByteArray, Integer};
|
||||
use hmac::{
|
||||
digest::{generic_array::GenericArray, BlockInput, FixedOutput, Reset, Update},
|
||||
Hmac, Mac, NewMac,
|
||||
};
|
||||
use zeroize::{Zeroize, Zeroizing};
|
||||
|
||||
/// Deterministically generate ephemeral scalar `k`.
|
||||
///
|
||||
/// Accepts the following parameters and inputs:
|
||||
///
|
||||
/// - `x`: secret key
|
||||
/// - `n`: field modulus
|
||||
/// - `h`: hash/digest of input message: must be reduced modulo `n` in advance
|
||||
/// - `data`: additional associated data, e.g. CSRNG output used as added entropy
|
||||
#[inline]
|
||||
pub fn generate_k<D, I>(x: &I, n: &I, h: &ByteArray<I>, data: &[u8]) -> Zeroizing<I>
|
||||
where
|
||||
D: FixedOutput<OutputSize = I::ByteSize> + BlockInput + Clone + Default + Reset + Update,
|
||||
I: ArrayEncoding + Integer + Zeroize,
|
||||
{
|
||||
let mut x = x.to_be_byte_array();
|
||||
let mut hmac_drbg = HmacDrbg::<D>::new(&x, h, data);
|
||||
x.zeroize();
|
||||
|
||||
loop {
|
||||
let mut bytes = ByteArray::<I>::default();
|
||||
hmac_drbg.fill_bytes(&mut bytes);
|
||||
let k = I::from_be_byte_array(bytes);
|
||||
|
||||
if (!k.is_zero() & k.ct_lt(n)).into() {
|
||||
return Zeroizing::new(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal implementation of `HMAC_DRBG` as described in NIST SP800-90A.
|
||||
///
|
||||
/// <https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final>
|
||||
///
|
||||
/// This is a HMAC-based deterministic random bit generator used compute a
|
||||
/// deterministic ephemeral scalar `k`.
|
||||
pub struct HmacDrbg<D>
|
||||
where
|
||||
D: BlockInput + FixedOutput + Clone + Default + Reset + Update,
|
||||
{
|
||||
/// HMAC key `K` (see RFC 6979 Section 3.2.c)
|
||||
k: Hmac<D>,
|
||||
|
||||
/// Chaining value `V` (see RFC 6979 Section 3.2.c)
|
||||
v: GenericArray<u8, D::OutputSize>,
|
||||
}
|
||||
|
||||
impl<D> HmacDrbg<D>
|
||||
where
|
||||
D: BlockInput + FixedOutput + Clone + Default + Reset + Update,
|
||||
{
|
||||
/// Initialize `HMAC_DRBG`
|
||||
pub fn new(entropy_input: &[u8], nonce: &[u8], additional_data: &[u8]) -> Self {
|
||||
let mut k = Hmac::new(&Default::default());
|
||||
let mut v = GenericArray::default();
|
||||
|
||||
for b in &mut v {
|
||||
*b = 0x01;
|
||||
}
|
||||
|
||||
for i in 0..=1 {
|
||||
k.update(&v);
|
||||
k.update(&[i]);
|
||||
k.update(entropy_input);
|
||||
k.update(nonce);
|
||||
k.update(additional_data);
|
||||
k = Hmac::new_from_slice(&k.finalize().into_bytes()).expect("HMAC error");
|
||||
|
||||
// Steps 3.2.e,g: v = HMAC_k(v)
|
||||
k.update(&v);
|
||||
v = k.finalize_reset().into_bytes();
|
||||
}
|
||||
|
||||
Self { k, v }
|
||||
}
|
||||
|
||||
/// Write the next `HMAC_DRBG` output to the given byte slice.
|
||||
pub fn fill_bytes(&mut self, out: &mut [u8]) {
|
||||
for out_chunk in out.chunks_mut(self.v.len()) {
|
||||
self.k.update(&self.v);
|
||||
self.v = self.k.finalize_reset().into_bytes();
|
||||
out_chunk.copy_from_slice(&self.v[..out_chunk.len()]);
|
||||
}
|
||||
|
||||
self.k.update(&self.v);
|
||||
self.k.update(&[0x00]);
|
||||
self.k = Hmac::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("HMAC error");
|
||||
self.k.update(&self.v);
|
||||
self.v = self.k.finalize_reset().into_bytes();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//! Smoke tests which use `MockCurve`
|
||||
|
||||
#![cfg(feature = "dev")]
|
||||
|
||||
use elliptic_curve::dev::MockCurve;
|
||||
|
||||
type Signature = ecdsa::Signature<MockCurve>;
|
||||
type SignatureBytes = ecdsa::SignatureBytes<MockCurve>;
|
||||
|
||||
#[test]
|
||||
fn rejects_all_zero_signature() {
|
||||
let all_zero_bytes = SignatureBytes::default();
|
||||
assert!(Signature::try_from(all_zero_bytes.as_ref()).is_err());
|
||||
}
|
||||
Reference in New Issue
Block a user