1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! 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 crate::hazmat::FromDigest;
use elliptic_curve::{
    generic_array::GenericArray,
    group::ff::PrimeField,
    ops::Invert,
    weierstrass::Curve,
    zeroize::{Zeroize, Zeroizing},
    FieldBytes, FieldSize, NonZeroScalar, 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: Curve + ProjectiveArithmetic,
    D: FixedOutput<OutputSize = FieldSize<C>> + BlockInput + Clone + Default + Reset + Update,
    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + Zeroize,
{
    let mut x = secret_scalar.to_repr();
    let h1 = Scalar::<C>::from_digest(msg_digest).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) {
            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")[..]
        );
    }
}