ecdsa_flow/
rfc6979.rs

1//! Support for computing deterministic ECDSA ephemeral scalar (`k`).
2//!
3//! Implementation of the algorithm described in RFC 6979 (Section 3.2):
4//! <https://tools.ietf.org/html/rfc6979#section-3>
5
6use crate::hazmat::FromDigest;
7use elliptic_curve_flow::{
8    generic_array::GenericArray,
9    group::ff::PrimeField,
10    ops::Invert,
11    zeroize::{Zeroize, Zeroizing},
12    FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar,
13};
14use hmac::{Hmac, Mac, NewMac};
15use signature_flow::digest::{BlockInput, FixedOutput, Reset, Update};
16
17/// Generate ephemeral scalar `k` from the secret scalar and a digest of the
18/// input message.
19pub fn generate_k<C, D>(
20    secret_scalar: &NonZeroScalar<C>,
21    msg_digest: D,
22    additional_data: &[u8],
23) -> Zeroizing<NonZeroScalar<C>>
24where
25    C: PrimeCurve + ProjectiveArithmetic,
26    D: FixedOutput<OutputSize = FieldSize<C>> + BlockInput + Clone + Default + Reset + Update,
27    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + Zeroize,
28{
29    let mut x = secret_scalar.to_repr();
30    let h1 = Scalar::<C>::from_digest(msg_digest).to_repr();
31    let mut hmac_drbg = HmacDrbg::<D>::new(&x, &h1, additional_data);
32    x.zeroize();
33
34    loop {
35        let mut tmp = FieldBytes::<C>::default();
36        hmac_drbg.generate_into(&mut tmp);
37        if let Some(k) = NonZeroScalar::from_repr(tmp).into() {
38            return Zeroizing::new(k);
39        }
40    }
41}
42
43/// Internal implementation of `HMAC_DRBG` as described in NIST SP800-90A:
44/// <https://csrc.nist.gov/publications/detail/sp/800-90a/rev-1/final>
45///
46/// This is a HMAC-based deterministic random bit generator used internally
47/// to compute a deterministic ECDSA ephemeral scalar `k`.
48// TODO(tarcieri): use `hmac-drbg` crate when sorpaas/rust-hmac-drbg#3 is merged
49struct HmacDrbg<D>
50where
51    D: BlockInput + FixedOutput + Clone + Default + Reset + Update,
52{
53    /// HMAC key `K` (see RFC 6979 Section 3.2.c)
54    k: Hmac<D>,
55
56    /// Chaining value `V` (see RFC 6979 Section 3.2.c)
57    v: GenericArray<u8, D::OutputSize>,
58}
59
60impl<D> HmacDrbg<D>
61where
62    D: BlockInput + FixedOutput + Clone + Default + Reset + Update,
63{
64    /// Initialize `HMAC_DRBG`
65    pub fn new(entropy_input: &[u8], nonce: &[u8], additional_data: &[u8]) -> Self {
66        let mut k = Hmac::new(&Default::default());
67        let mut v = GenericArray::default();
68
69        for b in &mut v {
70            *b = 0x01;
71        }
72
73        for i in 0..=1 {
74            k.update(&v);
75            k.update(&[i]);
76            k.update(entropy_input);
77            k.update(nonce);
78            k.update(additional_data);
79            k = Hmac::new_from_slice(&k.finalize().into_bytes()).expect("HMAC error");
80
81            // Steps 3.2.e,g: v = HMAC_k(v)
82            k.update(&v);
83            v = k.finalize_reset().into_bytes();
84        }
85
86        Self { k, v }
87    }
88
89    /// Get the next `HMAC_DRBG` output
90    pub fn generate_into(&mut self, out: &mut [u8]) {
91        for out_chunk in out.chunks_mut(self.v.len()) {
92            self.k.update(&self.v);
93            self.v = self.k.finalize_reset().into_bytes();
94            out_chunk.copy_from_slice(&self.v[..out_chunk.len()]);
95        }
96
97        self.k.update(&self.v);
98        self.k.update(&[0x00]);
99        self.k = Hmac::new_from_slice(&self.k.finalize_reset().into_bytes()).expect("HMAC error");
100        self.k.update(&self.v);
101        self.v = self.k.finalize_reset().into_bytes();
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::generate_k;
108    use elliptic_curve_flow::{dev::NonZeroScalar, group::ff::PrimeField};
109    use hex_literal::hex;
110    use sha2::{Digest, Sha256};
111
112    /// Test vector from RFC 6979 Appendix 2.5 (NIST P-256 + SHA-256)
113    /// <https://tools.ietf.org/html/rfc6979#appendix-A.2.5>
114    #[test]
115    fn appendix_2_5_test_vector() {
116        let x = NonZeroScalar::from_repr(
117            hex!("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721").into(),
118        )
119        .unwrap();
120
121        let digest = Sha256::new().chain("sample");
122        let k = generate_k(&x, digest, &[]);
123
124        assert_eq!(
125            k.to_repr().as_slice(),
126            &hex!("a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60")[..]
127        );
128    }
129}