Skip to main content

libcrux_hmac/
incremental.rs

1use libcrux_traits::digest::{arrayref, InitializeDigestState};
2
3use crate::{Error, HmacState};
4
5/// Generic streaming HMAC state, parametrized by output length, block size,
6/// and an incremental digest implementation `D`.
7///
8/// Use the type aliases [`HmacSha256`], [`HmacSha384`], or [`HmacSha512`]
9/// rather than spelling out the type parameters directly.
10pub struct HmacStream<const OUTLEN: usize, const BLOCK: usize, D>
11where
12    D: arrayref::DigestIncremental<OUTLEN>,
13    D::IncrementalState: InitializeDigestState,
14{
15    /// Inner hash state already primed with K xor ipad.
16    pub(crate) inner: D::IncrementalState,
17    pub(crate) opad: [u8; BLOCK],
18}
19
20impl<const OUTLEN: usize, const BLOCK: usize, Digest> HmacState<OUTLEN>
21    for HmacStream<OUTLEN, BLOCK, Digest>
22where
23    Digest: arrayref::DigestIncremental<OUTLEN>,
24    Digest::IncrementalState: InitializeDigestState,
25{
26    /// Initialize a new streaming HMAC computation with the given key.
27    fn new(key: &[u8]) -> Result<Self, Error> {
28        debug_assert!(OUTLEN <= BLOCK);
29        debug_assert!(BLOCK <= u32::MAX as usize);
30
31        if key.len() > u32::MAX as usize {
32            return Err(Error::InvalidInputLength);
33        }
34
35        let mut key_block = [0u8; BLOCK];
36        if key.len() <= BLOCK {
37            key_block[..key.len()].copy_from_slice(key);
38        } else {
39            // Key longer than one block: hash it down to OUTLEN bytes.
40            let mut tmp = Digest::IncrementalState::new();
41            Digest::update(&mut tmp, key)
42                .expect("This can't fail because we checked that key.len() <= u32::MAX");
43            let mut hashed = [0u8; OUTLEN];
44            Digest::finish(tmp, &mut hashed);
45            key_block[..OUTLEN].copy_from_slice(&hashed);
46        }
47
48        let mut k_xor_ipad = [0u8; BLOCK];
49        let mut k_xor_opad = [0u8; BLOCK];
50
51        for i in 0..BLOCK {
52            k_xor_ipad[i] = key_block[i] ^ 0x36;
53            k_xor_opad[i] = key_block[i] ^ 0x5c;
54        }
55
56        // Prime the inner state with `k_xor_ipad` so subsequent updates feed data directly.
57        // The update should only fail if we submit too much data, and `k_xor_ipad` is just a single
58        // block long, so short enough.
59        let mut inner = Digest::IncrementalState::new();
60        Digest::update(&mut inner, &k_xor_ipad).expect("HMAC: ipad update failed");
61
62        Ok(Self {
63            inner,
64            opad: k_xor_opad,
65        })
66    }
67
68    fn update(&mut self, data: &[u8]) -> Result<(), Error> {
69        Digest::update(&mut self.inner, data).map_err(|_| Error::InvalidInputLength)
70    }
71
72    fn finalize(self, dst: &mut [u8; OUTLEN]) {
73        // Inner hash: finalize H((K xor ipad) || data).
74        let mut inner_hash = [0u8; OUTLEN];
75        Digest::finish(self.inner, &mut inner_hash);
76
77        // Outer hash: H((K xor opad) || inner_hash).
78        let mut outer = Digest::IncrementalState::new();
79        Digest::update(&mut outer, &self.opad).expect("self.opad.len() is always <= u32::MAX");
80        Digest::update(&mut outer, &inner_hash).expect("inner_hash.len() is always <= u32::MAX");
81        Digest::finish(outer, dst);
82    }
83}
84
85/// Streaming HMAC-SHA-256 state.  Initialize with [`HmacSha256::new`].
86pub type HmacSha256 = HmacStream<32, 64, libcrux_sha2::Sha256Hash>;
87
88/// Streaming HMAC-SHA-384 state.  Initialize with [`HmacSha384::new`].
89pub type HmacSha384 = HmacStream<48, 128, libcrux_sha2::Sha384Hash>;
90
91/// Streaming HMAC-SHA-512 state.  Initialize with [`HmacSha512::new`].
92pub type HmacSha512 = HmacStream<64, 128, libcrux_sha2::Sha512Hash>;
93
94/// Streaming HMAC-SHA3-224 state.  Initialize with [`HmacSha3_224::new`].
95pub type HmacSha3_224 = HmacStream<28, 144, libcrux_sha3::Sha3_224>;
96
97/// Streaming HMAC-SHA3-256 state.  Initialize with [`HmacSha3_256::new`].
98pub type HmacSha3_256 = HmacStream<32, 136, libcrux_sha3::Sha3_256>;
99
100/// Streaming HMAC-SHA3-384 state.  Initialize with [`HmacSha3_384::new`].
101pub type HmacSha3_384 = HmacStream<48, 104, libcrux_sha3::Sha3_384>;
102
103/// Streaming HMAC-SHA3-512 state.  Initialize with [`HmacSha3_512::new`].
104pub type HmacSha3_512 = HmacStream<64, 72, libcrux_sha3::Sha3_512>;
105
106pub fn hmac_slices<const OUTLEN: usize, Hmac: HmacState<OUTLEN>>(
107    dst: &mut [u8; OUTLEN],
108    key: &[u8],
109    slices: &[&[u8]],
110) -> Result<(), Error> {
111    let mut state = Hmac::new(key)?;
112
113    for &s in slices {
114        state.update(s)?;
115    }
116
117    state.finalize(dst);
118    Ok(())
119}
120
121/// Compute HMAC-SHA-256 over logically concatenated, non-contiguous data slices.
122#[inline]
123pub fn hmac_sha2_256_slices(dst: &mut [u8; 32], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
124    hmac_slices::<32, HmacSha256>(dst, key, slices)
125}
126
127/// Compute HMAC-SHA-384 over logically concatenated, non-contiguous data slices.
128#[inline]
129pub fn hmac_sha2_384_slices(dst: &mut [u8; 48], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
130    hmac_slices::<48, HmacSha384>(dst, key, slices)
131}
132
133/// Compute HMAC-SHA-512 over logically concatenated, non-contiguous data slices.
134#[inline]
135pub fn hmac_sha2_512_slices(dst: &mut [u8; 64], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
136    hmac_slices::<64, HmacSha512>(dst, key, slices)
137}
138
139/// Compute HMAC-SHA3-224 over logically concatenated, non-contiguous data slices.
140#[inline]
141pub fn hmac_sha3_224_slices(dst: &mut [u8; 28], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
142    hmac_slices::<28, HmacSha3_224>(dst, key, slices)
143}
144
145/// Compute HMAC-SHA3-256 over logically concatenated, non-contiguous data slices.
146#[inline]
147pub fn hmac_sha3_256_slices(dst: &mut [u8; 32], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
148    hmac_slices::<32, HmacSha3_256>(dst, key, slices)
149}
150
151/// Compute HMAC-SHA3-384 over logically concatenated, non-contiguous data slices.
152#[inline]
153pub fn hmac_sha3_384_slices(dst: &mut [u8; 48], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
154    hmac_slices::<48, HmacSha3_384>(dst, key, slices)
155}
156
157/// Compute HMAC-SHA3-512 over logically concatenated, non-contiguous data slices.
158#[inline]
159pub fn hmac_sha3_512_slices(dst: &mut [u8; 64], key: &[u8], slices: &[&[u8]]) -> Result<(), Error> {
160    hmac_slices::<64, HmacSha3_512>(dst, key, slices)
161}
162
163/// Compute HMAC-SHA3-224.
164#[inline]
165pub fn hmac_sha3_224(dst: &mut [u8; 28], key: &[u8], data: &[u8]) -> Result<(), Error> {
166    hmac_sha3_224_slices(dst, key, &[data])
167}
168
169/// Compute HMAC-SHA3-256.
170#[inline]
171pub fn hmac_sha3_256(dst: &mut [u8; 32], key: &[u8], data: &[u8]) -> Result<(), Error> {
172    hmac_sha3_256_slices(dst, key, &[data])
173}
174
175/// Compute HMAC-SHA3-384.
176#[inline]
177pub fn hmac_sha3_384(dst: &mut [u8; 48], key: &[u8], data: &[u8]) -> Result<(), Error> {
178    hmac_sha3_384_slices(dst, key, &[data])
179}
180
181/// Compute HMAC-SHA3-512.
182#[inline]
183pub fn hmac_sha3_512(dst: &mut [u8; 64], key: &[u8], data: &[u8]) -> Result<(), Error> {
184    hmac_sha3_512_slices(dst, key, &[data])
185}