Skip to content

Commit 4ce2844

Browse files
committed
[WIP] rfc6979: integrate with crypto-bigint
Implements functions which are a 1:1 mapping to the ones defined in RFC6979, operating over `U: Unsigned + Encoding`. Notably this also implements the modular reduction instead of leaving it to the caller to perform. As the RFC notes, it's easily implemented as conditional subtraction.
1 parent 83e53c9 commit 4ce2844

6 files changed

Lines changed: 401 additions & 283 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rfc6979/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
name = "rfc6979"
33
version = "0.6.0-pre.0"
44
description = """
5-
Pure Rust implementation of RFC6979: Deterministic Usage of the
6-
Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)
5+
Pure Rust implementation of RFC6979: Deterministic Usage of the Digital Signature Algorithm (DSA)
6+
and Elliptic Curve Digital Signature Algorithm (ECDSA)
77
"""
88
authors = ["RustCrypto Developers"]
99
license = "Apache-2.0 OR MIT"
@@ -16,6 +16,7 @@ edition = "2024"
1616
rust-version = "1.85"
1717

1818
[dependencies]
19+
bigint = { package = "crypto-bigint", version = "0.7.5", default-features = false }
1920
ctutils = "0.4"
2021
hmac = { version = "0.13", default-features = false }
2122

rfc6979/src/ct.rs

Lines changed: 0 additions & 102 deletions
This file was deleted.

rfc6979/src/hmac_drbg.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use core::fmt::{self, Debug};
2+
use hmac::{
3+
KeyInit, SimpleHmacReset,
4+
digest::{Digest, FixedOutputReset, Mac, array::Array, block_api::BlockSizeUser},
5+
};
6+
7+
/// Implementation of `HMAC_DRBG` as described in NIST SP800-90A.
8+
///
9+
/// <https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final>
10+
///
11+
/// This is a HMAC-based deterministic random bit generator.
12+
// TODO(tarcieri): extract this into its own crate
13+
pub(crate) struct HmacDrbg<D>
14+
where
15+
D: Digest + BlockSizeUser + FixedOutputReset,
16+
{
17+
/// HMAC key `K` (see RFC 6979 Section 3.2.c)
18+
k: SimpleHmacReset<D>,
19+
20+
/// Chaining value `V` (see RFC 6979 Section 3.2.c)
21+
v: Array<u8, D::OutputSize>,
22+
}
23+
24+
impl<D> HmacDrbg<D>
25+
where
26+
D: Digest + BlockSizeUser + FixedOutputReset,
27+
{
28+
/// Initialize `HMAC_DRBG`.
29+
#[must_use]
30+
#[allow(clippy::missing_panics_doc, reason = "should not panic")]
31+
pub(crate) fn new(entropy_input: &[u8], nonce: &[u8], personalization_string: &[u8]) -> Self {
32+
let mut k = SimpleHmacReset::new(&Default::default());
33+
let mut v = Array::default();
34+
35+
v.fill(0x01);
36+
37+
for i in 0..=1 {
38+
k.update(&v);
39+
k.update(&[i]);
40+
k.update(entropy_input);
41+
k.update(nonce);
42+
k.update(personalization_string);
43+
k = SimpleHmacReset::new_from_slice(&k.finalize().into_bytes()).expect("should work");
44+
45+
// Steps 3.2.e,g: v = HMAC_k(v)
46+
k.update(&v);
47+
v = k.finalize_reset().into_bytes();
48+
}
49+
50+
Self { k, v }
51+
}
52+
53+
/// Write the next `HMAC_DRBG` output to the given byte slice.
54+
#[allow(clippy::missing_panics_doc, reason = "should not panic")]
55+
pub(crate) fn fill_bytes(&mut self, out: &mut [u8]) {
56+
let mut out_chunks = out.chunks_exact_mut(self.v.len());
57+
58+
for out_chunk in &mut out_chunks {
59+
self.k.update(&self.v);
60+
self.v = self.k.finalize_reset().into_bytes();
61+
out_chunk.copy_from_slice(&self.v[..out_chunk.len()]);
62+
}
63+
64+
let out_remainder = out_chunks.into_remainder();
65+
if !out_remainder.is_empty() {
66+
self.k.update(&self.v);
67+
self.v = self.k.finalize_reset().into_bytes();
68+
out_remainder.copy_from_slice(&self.v[..out_remainder.len()]);
69+
}
70+
71+
self.k.update(&self.v);
72+
self.k.update(&[0x00]);
73+
self.k = SimpleHmacReset::new_from_slice(&self.k.finalize_reset().into_bytes())
74+
.expect("should work");
75+
self.k.update(&self.v);
76+
self.v = self.k.finalize_reset().into_bytes();
77+
}
78+
}
79+
80+
impl<D> Debug for HmacDrbg<D>
81+
where
82+
D: Digest + BlockSizeUser + FixedOutputReset,
83+
{
84+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85+
f.debug_struct("HmacDrbg").finish_non_exhaustive()
86+
}
87+
}
88+
89+
#[cfg(test)]
90+
mod tests {
91+
use super::HmacDrbg;
92+
use hex_literal::hex;
93+
use sha2::Sha256;
94+
95+
// ECDSA with K-163+SHA-256.
96+
#[test]
97+
fn rfc6979_appendix_a1_k163() {
98+
let x = hex!("009A4D6792295A7F730FC3F2B49CBC0F62E862272F");
99+
100+
// NOTE: this input is `SHA256("sample")` with `bits2octets` applied in advance
101+
let h = hex!("01795EDF0D54DB760F156D0DAC04C0322B3A204224");
102+
103+
let mut drbg = HmacDrbg::<Sha256>::new(&x, &h, &[]);
104+
let mut out = [0u8; 21];
105+
drbg.fill_bytes(&mut out);
106+
assert_eq!(out, hex!("9305A46DE7FF8EB107194DEBD3FD48AA20D5E7656C"));
107+
}
108+
}

0 commit comments

Comments
 (0)