Skip to main content

hmac_sha512/
lib.rs

1//! A small, self-contained SHA512, HMAC-SHA512, and HKDF-SHA512 implementation.
2//!
3//! Also includes SHA384, HMAC-SHA384, and HKDF-SHA384 when the `sha384` feature is enabled (default).
4//!
5//! (C) Frank Denis <fdenis [at] fastly [dot] com>
6
7#![no_std]
8#![allow(
9    non_snake_case,
10    clippy::cast_lossless,
11    clippy::eq_op,
12    clippy::identity_op,
13    clippy::many_single_char_names,
14    clippy::unreadable_literal
15)]
16
17pub const BLOCKBYTES: usize = 128;
18pub const BYTES: usize = 64;
19
20#[inline(always)]
21fn load_be(base: &[u8], offset: usize) -> u64 {
22    let addr = &base[offset..];
23    (addr[7] as u64)
24        | (addr[6] as u64) << 8
25        | (addr[5] as u64) << 16
26        | (addr[4] as u64) << 24
27        | (addr[3] as u64) << 32
28        | (addr[2] as u64) << 40
29        | (addr[1] as u64) << 48
30        | (addr[0] as u64) << 56
31}
32
33#[inline(always)]
34fn store_be(base: &mut [u8], offset: usize, x: u64) {
35    let addr = &mut base[offset..];
36    addr[7] = x as u8;
37    addr[6] = (x >> 8) as u8;
38    addr[5] = (x >> 16) as u8;
39    addr[4] = (x >> 24) as u8;
40    addr[3] = (x >> 32) as u8;
41    addr[2] = (x >> 40) as u8;
42    addr[1] = (x >> 48) as u8;
43    addr[0] = (x >> 56) as u8;
44}
45
46fn verify(x: &[u8], y: &[u8]) -> bool {
47    if x.len() != y.len() {
48        return false;
49    }
50    let mut v: u32 = 0;
51
52    #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
53    {
54        let (mut h1, mut h2) = (0u32, 0u32);
55        for (b1, b2) in x.iter().zip(y.iter()) {
56            h1 ^= (h1 << 5).wrapping_add((h1 >> 2) ^ *b1 as u32);
57            h2 ^= (h2 << 5).wrapping_add((h2 >> 2) ^ *b2 as u32);
58        }
59        v |= h1 ^ h2;
60    }
61    for (a, b) in x.iter().zip(y.iter()) {
62        v |= (a ^ b) as u32;
63    }
64    let v = unsafe { core::ptr::read_volatile(&v) };
65    v == 0
66}
67
68struct W([u64; 16]);
69
70#[derive(Copy, Clone)]
71struct State([u64; 8]);
72
73impl W {
74    fn new(input: &[u8]) -> Self {
75        let mut w = [0u64; 16];
76        for (i, e) in w.iter_mut().enumerate() {
77            *e = load_be(input, i * 8)
78        }
79        W(w)
80    }
81
82    #[inline(always)]
83    fn Ch(x: u64, y: u64, z: u64) -> u64 {
84        (x & y) ^ (!x & z)
85    }
86
87    #[inline(always)]
88    fn Maj(x: u64, y: u64, z: u64) -> u64 {
89        (x & y) ^ (x & z) ^ (y & z)
90    }
91
92    #[inline(always)]
93    fn Sigma0(x: u64) -> u64 {
94        x.rotate_right(28) ^ x.rotate_right(34) ^ x.rotate_right(39)
95    }
96
97    #[inline(always)]
98    fn Sigma1(x: u64) -> u64 {
99        x.rotate_right(14) ^ x.rotate_right(18) ^ x.rotate_right(41)
100    }
101
102    #[inline(always)]
103    fn sigma0(x: u64) -> u64 {
104        x.rotate_right(1) ^ x.rotate_right(8) ^ (x >> 7)
105    }
106
107    #[inline(always)]
108    fn sigma1(x: u64) -> u64 {
109        x.rotate_right(19) ^ x.rotate_right(61) ^ (x >> 6)
110    }
111
112    #[cfg_attr(feature = "opt_size", inline(never))]
113    #[cfg_attr(not(feature = "opt_size"), inline(always))]
114    fn M(&mut self, a: usize, b: usize, c: usize, d: usize) {
115        let w = &mut self.0;
116        w[a] = w[a]
117            .wrapping_add(Self::sigma1(w[b]))
118            .wrapping_add(w[c])
119            .wrapping_add(Self::sigma0(w[d]));
120    }
121
122    #[inline]
123    fn expand(&mut self) {
124        self.M(0, (0 + 14) & 15, (0 + 9) & 15, (0 + 1) & 15);
125        self.M(1, (1 + 14) & 15, (1 + 9) & 15, (1 + 1) & 15);
126        self.M(2, (2 + 14) & 15, (2 + 9) & 15, (2 + 1) & 15);
127        self.M(3, (3 + 14) & 15, (3 + 9) & 15, (3 + 1) & 15);
128        self.M(4, (4 + 14) & 15, (4 + 9) & 15, (4 + 1) & 15);
129        self.M(5, (5 + 14) & 15, (5 + 9) & 15, (5 + 1) & 15);
130        self.M(6, (6 + 14) & 15, (6 + 9) & 15, (6 + 1) & 15);
131        self.M(7, (7 + 14) & 15, (7 + 9) & 15, (7 + 1) & 15);
132        self.M(8, (8 + 14) & 15, (8 + 9) & 15, (8 + 1) & 15);
133        self.M(9, (9 + 14) & 15, (9 + 9) & 15, (9 + 1) & 15);
134        self.M(10, (10 + 14) & 15, (10 + 9) & 15, (10 + 1) & 15);
135        self.M(11, (11 + 14) & 15, (11 + 9) & 15, (11 + 1) & 15);
136        self.M(12, (12 + 14) & 15, (12 + 9) & 15, (12 + 1) & 15);
137        self.M(13, (13 + 14) & 15, (13 + 9) & 15, (13 + 1) & 15);
138        self.M(14, (14 + 14) & 15, (14 + 9) & 15, (14 + 1) & 15);
139        self.M(15, (15 + 14) & 15, (15 + 9) & 15, (15 + 1) & 15);
140    }
141
142    #[cfg_attr(feature = "opt_size", inline(never))]
143    #[cfg_attr(not(feature = "opt_size"), inline(always))]
144    fn F(&mut self, state: &mut State, i: usize, k: u64) {
145        let t = &mut state.0;
146        t[(16 - i + 7) & 7] = t[(16 - i + 7) & 7]
147            .wrapping_add(Self::Sigma1(t[(16 - i + 4) & 7]))
148            .wrapping_add(Self::Ch(
149                t[(16 - i + 4) & 7],
150                t[(16 - i + 5) & 7],
151                t[(16 - i + 6) & 7],
152            ))
153            .wrapping_add(k)
154            .wrapping_add(self.0[i]);
155        t[(16 - i + 3) & 7] = t[(16 - i + 3) & 7].wrapping_add(t[(16 - i + 7) & 7]);
156        t[(16 - i + 7) & 7] = t[(16 - i + 7) & 7]
157            .wrapping_add(Self::Sigma0(t[(16 - i + 0) & 7]))
158            .wrapping_add(Self::Maj(
159                t[(16 - i + 0) & 7],
160                t[(16 - i + 1) & 7],
161                t[(16 - i + 2) & 7],
162            ));
163    }
164
165    fn G(&mut self, state: &mut State, s: usize) {
166        const ROUND_CONSTANTS: [u64; 80] = [
167            0x428a2f98d728ae22,
168            0x7137449123ef65cd,
169            0xb5c0fbcfec4d3b2f,
170            0xe9b5dba58189dbbc,
171            0x3956c25bf348b538,
172            0x59f111f1b605d019,
173            0x923f82a4af194f9b,
174            0xab1c5ed5da6d8118,
175            0xd807aa98a3030242,
176            0x12835b0145706fbe,
177            0x243185be4ee4b28c,
178            0x550c7dc3d5ffb4e2,
179            0x72be5d74f27b896f,
180            0x80deb1fe3b1696b1,
181            0x9bdc06a725c71235,
182            0xc19bf174cf692694,
183            0xe49b69c19ef14ad2,
184            0xefbe4786384f25e3,
185            0x0fc19dc68b8cd5b5,
186            0x240ca1cc77ac9c65,
187            0x2de92c6f592b0275,
188            0x4a7484aa6ea6e483,
189            0x5cb0a9dcbd41fbd4,
190            0x76f988da831153b5,
191            0x983e5152ee66dfab,
192            0xa831c66d2db43210,
193            0xb00327c898fb213f,
194            0xbf597fc7beef0ee4,
195            0xc6e00bf33da88fc2,
196            0xd5a79147930aa725,
197            0x06ca6351e003826f,
198            0x142929670a0e6e70,
199            0x27b70a8546d22ffc,
200            0x2e1b21385c26c926,
201            0x4d2c6dfc5ac42aed,
202            0x53380d139d95b3df,
203            0x650a73548baf63de,
204            0x766a0abb3c77b2a8,
205            0x81c2c92e47edaee6,
206            0x92722c851482353b,
207            0xa2bfe8a14cf10364,
208            0xa81a664bbc423001,
209            0xc24b8b70d0f89791,
210            0xc76c51a30654be30,
211            0xd192e819d6ef5218,
212            0xd69906245565a910,
213            0xf40e35855771202a,
214            0x106aa07032bbd1b8,
215            0x19a4c116b8d2d0c8,
216            0x1e376c085141ab53,
217            0x2748774cdf8eeb99,
218            0x34b0bcb5e19b48a8,
219            0x391c0cb3c5c95a63,
220            0x4ed8aa4ae3418acb,
221            0x5b9cca4f7763e373,
222            0x682e6ff3d6b2b8a3,
223            0x748f82ee5defb2fc,
224            0x78a5636f43172f60,
225            0x84c87814a1f0ab72,
226            0x8cc702081a6439ec,
227            0x90befffa23631e28,
228            0xa4506cebde82bde9,
229            0xbef9a3f7b2c67915,
230            0xc67178f2e372532b,
231            0xca273eceea26619c,
232            0xd186b8c721c0c207,
233            0xeada7dd6cde0eb1e,
234            0xf57d4f7fee6ed178,
235            0x06f067aa72176fba,
236            0x0a637dc5a2c898a6,
237            0x113f9804bef90dae,
238            0x1b710b35131c471b,
239            0x28db77f523047d84,
240            0x32caab7b40c72493,
241            0x3c9ebe0a15c9bebc,
242            0x431d67c49c100d4c,
243            0x4cc5d4becb3e42b6,
244            0x597f299cfc657e2a,
245            0x5fcb6fab3ad6faec,
246            0x6c44198c4a475817,
247        ];
248        let rc = &ROUND_CONSTANTS[s * 16..];
249        self.F(state, 0, rc[0]);
250        self.F(state, 1, rc[1]);
251        self.F(state, 2, rc[2]);
252        self.F(state, 3, rc[3]);
253        self.F(state, 4, rc[4]);
254        self.F(state, 5, rc[5]);
255        self.F(state, 6, rc[6]);
256        self.F(state, 7, rc[7]);
257        self.F(state, 8, rc[8]);
258        self.F(state, 9, rc[9]);
259        self.F(state, 10, rc[10]);
260        self.F(state, 11, rc[11]);
261        self.F(state, 12, rc[12]);
262        self.F(state, 13, rc[13]);
263        self.F(state, 14, rc[14]);
264        self.F(state, 15, rc[15]);
265    }
266}
267
268impl State {
269    fn new() -> Self {
270        const IV: [u8; 64] = [
271            0x6a, 0x09, 0xe6, 0x67, 0xf3, 0xbc, 0xc9, 0x08, 0xbb, 0x67, 0xae, 0x85, 0x84, 0xca,
272            0xa7, 0x3b, 0x3c, 0x6e, 0xf3, 0x72, 0xfe, 0x94, 0xf8, 0x2b, 0xa5, 0x4f, 0xf5, 0x3a,
273            0x5f, 0x1d, 0x36, 0xf1, 0x51, 0x0e, 0x52, 0x7f, 0xad, 0xe6, 0x82, 0xd1, 0x9b, 0x05,
274            0x68, 0x8c, 0x2b, 0x3e, 0x6c, 0x1f, 0x1f, 0x83, 0xd9, 0xab, 0xfb, 0x41, 0xbd, 0x6b,
275            0x5b, 0xe0, 0xcd, 0x19, 0x13, 0x7e, 0x21, 0x79,
276        ];
277        let mut t = [0u64; 8];
278        for (i, e) in t.iter_mut().enumerate() {
279            *e = load_be(&IV, i * 8)
280        }
281        State(t)
282    }
283
284    #[inline(always)]
285    fn add(&mut self, x: &State) {
286        let sx = &mut self.0;
287        let ex = &x.0;
288        sx[0] = sx[0].wrapping_add(ex[0]);
289        sx[1] = sx[1].wrapping_add(ex[1]);
290        sx[2] = sx[2].wrapping_add(ex[2]);
291        sx[3] = sx[3].wrapping_add(ex[3]);
292        sx[4] = sx[4].wrapping_add(ex[4]);
293        sx[5] = sx[5].wrapping_add(ex[5]);
294        sx[6] = sx[6].wrapping_add(ex[6]);
295        sx[7] = sx[7].wrapping_add(ex[7]);
296    }
297
298    fn store(&self, out: &mut [u8]) {
299        for (i, &e) in self.0.iter().enumerate() {
300            store_be(out, i * 8, e);
301        }
302    }
303
304    fn blocks(&mut self, mut input: &[u8]) -> usize {
305        let mut t = *self;
306        let mut inlen = input.len();
307        while inlen >= 128 {
308            let mut w = W::new(input);
309            w.G(&mut t, 0);
310            w.expand();
311            w.G(&mut t, 1);
312            w.expand();
313            w.G(&mut t, 2);
314            w.expand();
315            w.G(&mut t, 3);
316            w.expand();
317            w.G(&mut t, 4);
318            t.add(self);
319            self.0 = t.0;
320            input = &input[128..];
321            inlen -= 128;
322        }
323        inlen
324    }
325}
326
327#[derive(Copy, Clone)]
328pub struct Hash {
329    state: State,
330    w: [u8; 128],
331    r: usize,
332    len: usize,
333}
334
335impl Hash {
336    pub fn new() -> Hash {
337        Hash {
338            state: State::new(),
339            r: 0,
340            w: [0u8; 128],
341            len: 0,
342        }
343    }
344
345    fn _update<T: AsRef<[u8]>>(&mut self, input: T) {
346        let input = input.as_ref();
347        let mut n = input.len();
348        self.len += n;
349        let av = 128 - self.r;
350        let tc = ::core::cmp::min(n, av);
351        self.w[self.r..self.r + tc].copy_from_slice(&input[0..tc]);
352        self.r += tc;
353        n -= tc;
354        let pos = tc;
355        if self.r == 128 {
356            self.state.blocks(&self.w);
357            self.r = 0;
358        }
359        if self.r == 0 && n > 0 {
360            let rb = self.state.blocks(&input[pos..]);
361            if rb > 0 {
362                self.w[..rb].copy_from_slice(&input[pos + n - rb..]);
363                self.r = rb;
364            }
365        }
366    }
367
368    /// Absorb content
369    pub fn update<T: AsRef<[u8]>>(&mut self, input: T) {
370        self._update(input)
371    }
372
373    /// Compute SHA512(absorbed content)
374    pub fn finalize(mut self) -> [u8; 64] {
375        let mut padded = [0u8; 256];
376        padded[..self.r].copy_from_slice(&self.w[..self.r]);
377        padded[self.r] = 0x80;
378        let r = if self.r < 112 { 128 } else { 256 };
379        let bits = self.len * 8;
380        for i in 0..8 {
381            padded[r - 8 + i] = (bits as u64 >> (56 - i * 8)) as u8;
382        }
383        self.state.blocks(&padded[..r]);
384        let mut out = [0u8; 64];
385        self.state.store(&mut out);
386        out
387    }
388
389    /// Compute SHA512(`input`)
390    pub fn hash<T: AsRef<[u8]>>(input: T) -> [u8; 64] {
391        let mut h = Hash::new();
392        h.update(input);
393        h.finalize()
394    }
395
396    /// Verify that the hash matches an expected value
397    pub fn verify(self, expected: &[u8; 64]) -> bool {
398        let out = self.finalize();
399        verify(&out, expected)
400    }
401}
402
403impl Default for Hash {
404    fn default() -> Self {
405        Self::new()
406    }
407}
408
409#[cfg(feature = "traits09")]
410mod digest_trait {
411    use digest09::consts::{U128, U64};
412    use digest09::{BlockInput, FixedOutputDirty, Output, Reset, Update};
413
414    use super::Hash;
415
416    impl BlockInput for Hash {
417        type BlockSize = U128;
418    }
419
420    impl Update for Hash {
421        fn update(&mut self, input: impl AsRef<[u8]>) {
422            self._update(input);
423        }
424    }
425
426    impl FixedOutputDirty for Hash {
427        type OutputSize = U64;
428
429        fn finalize_into_dirty(&mut self, out: &mut Output<Self>) {
430            let h = self.finalize();
431            out.copy_from_slice(&h);
432        }
433    }
434
435    impl Reset for Hash {
436        fn reset(&mut self) {
437            *self = Self::new();
438        }
439    }
440}
441
442/// Wrapped `Hash` type for the `Digest` trait.
443#[cfg(feature = "traits010")]
444pub type WrappedHash = digest010::core_api::CoreWrapper<Hash>;
445
446#[cfg(feature = "traits010")]
447mod digest_trait010 {
448    use core::fmt;
449
450    use digest010::{
451        block_buffer::Eager,
452        const_oid::{AssociatedOid, ObjectIdentifier},
453        consts::{U128, U64},
454        core_api::{
455            AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
456            OutputSizeUser, Reset, UpdateCore,
457        },
458        FixedOutput, FixedOutputReset, HashMarker, Output, Update,
459    };
460
461    use super::Hash;
462
463    impl AssociatedOid for Hash {
464        const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
465    }
466
467    impl AlgorithmName for Hash {
468        fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
469            f.write_str("Sha512")
470        }
471    }
472
473    impl HashMarker for Hash {}
474
475    impl BufferKindUser for Hash {
476        type BufferKind = Eager;
477    }
478
479    impl BlockSizeUser for Hash {
480        type BlockSize = U128;
481    }
482
483    impl OutputSizeUser for Hash {
484        type OutputSize = U64;
485    }
486
487    impl UpdateCore for Hash {
488        #[inline]
489        fn update_blocks(&mut self, blocks: &[Block<Self>]) {
490            for block in blocks {
491                self._update(block);
492            }
493        }
494    }
495
496    impl Update for Hash {
497        #[inline]
498        fn update(&mut self, data: &[u8]) {
499            self._update(data);
500        }
501    }
502
503    impl FixedOutputCore for Hash {
504        fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
505            self._update(buffer.get_data());
506            let h = self.finalize();
507            out.copy_from_slice(&h);
508        }
509    }
510
511    impl FixedOutput for Hash {
512        fn finalize_into(self, out: &mut Output<Self>) {
513            let h = self.finalize();
514            out.copy_from_slice(&h);
515        }
516    }
517
518    impl Reset for Hash {
519        fn reset(&mut self) {
520            *self = Self::new()
521        }
522    }
523
524    impl FixedOutputReset for Hash {
525        fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
526            self.finalize_into(out);
527            self.reset();
528        }
529    }
530}
531
532#[cfg(feature = "traits011")]
533mod digest_trait011 {
534    use digest011::{
535        const_oid::{AssociatedOid, ObjectIdentifier},
536        consts::U64,
537        FixedOutput, FixedOutputReset, HashMarker, Output, OutputSizeUser, Reset, Update,
538    };
539
540    use super::Hash;
541
542    impl AssociatedOid for Hash {
543        const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
544    }
545
546    impl HashMarker for Hash {}
547
548    impl OutputSizeUser for Hash {
549        type OutputSize = U64;
550    }
551
552    impl Update for Hash {
553        #[inline]
554        fn update(&mut self, data: &[u8]) {
555            self._update(data);
556        }
557    }
558
559    impl FixedOutput for Hash {
560        fn finalize_into(self, out: &mut Output<Self>) {
561            let h = self.finalize();
562            out.copy_from_slice(&h);
563        }
564    }
565
566    impl Reset for Hash {
567        fn reset(&mut self) {
568            *self = Self::new()
569        }
570    }
571
572    impl FixedOutputReset for Hash {
573        fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
574            let h = self.finalize();
575            out.copy_from_slice(&h);
576            self.reset();
577        }
578    }
579}
580
581pub struct HMAC {
582    ih: Hash,
583    padded: [u8; 128],
584}
585
586impl HMAC {
587    /// Compute HMAC-SHA512(`input`, `k`)
588    pub fn mac<T: AsRef<[u8]>, U: AsRef<[u8]>>(input: T, k: U) -> [u8; 64] {
589        let input = input.as_ref();
590        let k = k.as_ref();
591        let mut hk = [0u8; 64];
592        let k2 = if k.len() > 128 {
593            hk.copy_from_slice(&Hash::hash(k));
594            &hk
595        } else {
596            k
597        };
598        let mut ih = Hash::new();
599        let mut padded = [0x36; 128];
600        for (p, &k) in padded.iter_mut().zip(k2.iter()) {
601            *p ^= k;
602        }
603        ih.update(&padded[..]);
604        ih.update(input);
605
606        let mut oh = Hash::new();
607        padded = [0x5c; 128];
608        for (p, &k) in padded.iter_mut().zip(k2.iter()) {
609            *p ^= k;
610        }
611        oh.update(&padded[..]);
612        oh.update(&ih.finalize()[..]);
613        oh.finalize()
614    }
615
616    /// Creates a new HMAC-SHA512 instance with the given key.
617    pub fn new(k: impl AsRef<[u8]>) -> HMAC {
618        let k = k.as_ref();
619        let mut hk = [0u8; 64];
620        let k2 = if k.len() > 128 {
621            hk.copy_from_slice(&Hash::hash(k));
622            &hk
623        } else {
624            k
625        };
626        let mut padded = [0x36; 128];
627        for (p, &k) in padded.iter_mut().zip(k2.iter()) {
628            *p ^= k;
629        }
630        let mut ih = Hash::new();
631        ih.update(&padded[..]);
632        HMAC { ih, padded }
633    }
634
635    /// Absorbs content into the HMAC state.
636    pub fn update(&mut self, input: impl AsRef<[u8]>) {
637        self.ih.update(input);
638    }
639
640    /// Computes the HMAC-SHA512 of all previously absorbed content.
641    pub fn finalize(mut self) -> [u8; 64] {
642        for p in self.padded.iter_mut() {
643            *p ^= 0x6a;
644        }
645        let mut oh = Hash::new();
646        oh.update(&self.padded[..]);
647        oh.update(self.ih.finalize());
648        oh.finalize()
649    }
650
651    /// Verifies that the HMAC of absorbed content matches the expected MAC.
652    pub fn finalize_verify(self, expected: &[u8; 64]) -> bool {
653        let out = self.finalize();
654        verify(&out, expected)
655    }
656
657    /// Verify that a message's HMAC matches the expected value
658    pub fn verify<T: AsRef<[u8]>, U: AsRef<[u8]>>(input: T, k: U, expected: &[u8; 64]) -> bool {
659        let mac = Self::mac(input, k);
660        verify(&mac, expected)
661    }
662}
663
664/// HMAC-based Key Derivation Function (HKDF) implementation using SHA-512.
665///
666/// HKDF is a key derivation function based on HMAC, standardized in RFC 5869.
667/// It derives cryptographically strong keys from input keying material.
668///
669/// The HKDF process consists of two stages:
670/// 1. Extract: Takes input keying material and an optional salt, produces a pseudorandom key (PRK)
671/// 2. Expand: Takes the PRK and optional context info, generates output keying material
672///
673/// # Examples
674///
675/// ```
676/// let prk = hmac_sha512::HKDF::extract(b"salt value", b"input key material");
677///
678/// let mut okm = [0u8; 128];
679/// hmac_sha512::HKDF::expand(&mut okm, prk, b"application info");
680/// ```
681pub struct HKDF;
682
683impl HKDF {
684    /// Performs the HKDF-Extract function.
685    ///
686    /// Extracts a pseudorandom key from the input keying material using the optional salt.
687    ///
688    /// # Arguments
689    ///
690    /// * `salt` - Optional salt value (a non-secret random value)
691    /// * `ikm` - Input keying material (the secret input)
692    ///
693    /// # Returns
694    ///
695    /// A 64-byte pseudorandom key
696    pub fn extract(salt: impl AsRef<[u8]>, ikm: impl AsRef<[u8]>) -> [u8; 64] {
697        HMAC::mac(ikm, salt)
698    }
699
700    /// Performs the HKDF-Expand function.
701    ///
702    /// Expands the pseudorandom key into output keying material of the desired length.
703    ///
704    /// # Arguments
705    ///
706    /// * `out` - Buffer to receive the output keying material
707    /// * `prk` - Pseudorandom key (from the extract step)
708    /// * `info` - Optional context and application specific information
709    ///
710    /// # Panics
711    ///
712    /// Panics if the requested output length is greater than 255 * 64 bytes (16320 bytes).
713    pub fn expand(out: &mut [u8], prk: impl AsRef<[u8]>, info: impl AsRef<[u8]>) {
714        let info = info.as_ref();
715        let mut counter: u8 = 1;
716        assert!(out.len() < 0xff * 64);
717        let mut i: usize = 0;
718        while i < out.len() {
719            let mut hmac = HMAC::new(&prk);
720            if i != 0 {
721                hmac.update(&out[i - 64..][..64]);
722            }
723            hmac.update(info);
724            hmac.update([counter]);
725            let left = core::cmp::min(64, out.len() - i);
726            out[i..][..left].copy_from_slice(&hmac.finalize()[..left]);
727            counter += 1;
728            i += 64;
729        }
730    }
731}
732
733#[cfg(feature = "sha384")]
734pub mod sha384 {
735    use super::load_be;
736
737    fn new_state() -> super::State {
738        const IV: [u8; 64] = [
739            0xcb, 0xbb, 0x9d, 0x5d, 0xc1, 0x05, 0x9e, 0xd8, 0x62, 0x9a, 0x29, 0x2a, 0x36, 0x7c,
740            0xd5, 0x07, 0x91, 0x59, 0x01, 0x5a, 0x30, 0x70, 0xdd, 0x17, 0x15, 0x2f, 0xec, 0xd8,
741            0xf7, 0x0e, 0x59, 0x39, 0x67, 0x33, 0x26, 0x67, 0xff, 0xc0, 0x0b, 0x31, 0x8e, 0xb4,
742            0x4a, 0x87, 0x68, 0x58, 0x15, 0x11, 0xdb, 0x0c, 0x2e, 0x0d, 0x64, 0xf9, 0x8f, 0xa7,
743            0x47, 0xb5, 0x48, 0x1d, 0xbe, 0xfa, 0x4f, 0xa4,
744        ];
745        let mut t = [0u64; 8];
746        for (i, e) in t.iter_mut().enumerate() {
747            *e = load_be(&IV, i * 8)
748        }
749        super::State(t)
750    }
751
752    #[derive(Copy, Clone)]
753    pub struct Hash(super::Hash);
754
755    impl Hash {
756        pub fn new() -> Hash {
757            Hash(super::Hash {
758                state: new_state(),
759                r: 0,
760                w: [0u8; 128],
761                len: 0,
762            })
763        }
764
765        fn _update<T: AsRef<[u8]>>(&mut self, input: T) {
766            self.0.update(input)
767        }
768
769        /// Absorb content
770        pub fn update<T: AsRef<[u8]>>(&mut self, input: T) {
771            self._update(input)
772        }
773
774        /// Compute SHA384(absorbed content)
775        pub fn finalize(self) -> [u8; 48] {
776            let mut h = [0u8; 48];
777            h.copy_from_slice(&self.0.finalize()[..48]);
778            h
779        }
780
781        /// Compute SHA384(`input`)
782        pub fn hash<T: AsRef<[u8]>>(input: T) -> [u8; 48] {
783            let mut h = Hash::new();
784            h.update(input);
785            h.finalize()
786        }
787    }
788
789    impl Default for Hash {
790        fn default() -> Self {
791            Self::new()
792        }
793    }
794
795    pub struct HMAC {
796        ih: Hash,
797        padded: [u8; 128],
798    }
799
800    impl HMAC {
801        /// Compute HMAC-SHA384(`input`, `k`)
802        pub fn mac<T: AsRef<[u8]>, U: AsRef<[u8]>>(input: T, k: U) -> [u8; 48] {
803            let mut hmac = Self::new(k);
804            hmac.update(input);
805            hmac.finalize()
806        }
807
808        /// Creates a new HMAC-SHA384 instance with the given key.
809        pub fn new(k: impl AsRef<[u8]>) -> HMAC {
810            let k = k.as_ref();
811            let mut hk = [0u8; 48];
812            let k2 = if k.len() > 128 {
813                hk.copy_from_slice(&Hash::hash(k));
814                &hk[..]
815            } else {
816                k
817            };
818            let mut padded = [0x36; 128];
819            for (p, &k) in padded.iter_mut().zip(k2.iter()) {
820                *p ^= k;
821            }
822            let mut ih = Hash::new();
823            ih.update(&padded[..]);
824            HMAC { ih, padded }
825        }
826
827        /// Absorbs content into the HMAC state.
828        pub fn update(&mut self, input: impl AsRef<[u8]>) {
829            self.ih.update(input);
830        }
831
832        /// Computes the HMAC-SHA384 of all previously absorbed content.
833        pub fn finalize(mut self) -> [u8; 48] {
834            for p in self.padded.iter_mut() {
835                *p ^= 0x6a;
836            }
837            let mut oh = Hash::new();
838            oh.update(&self.padded[..]);
839            oh.update(&self.ih.finalize()[..]);
840            oh.finalize()
841        }
842
843        /// Verifies that the HMAC of absorbed content matches the expected MAC.
844        pub fn finalize_verify(self, expected: &[u8; 48]) -> bool {
845            let out = self.finalize();
846            super::verify(&out, expected)
847        }
848
849        /// Verify that a message's HMAC matches the expected value
850        pub fn verify<T: AsRef<[u8]>, U: AsRef<[u8]>>(input: T, k: U, expected: &[u8; 48]) -> bool {
851            let mac = Self::mac(input, k);
852            super::verify(&mac, expected)
853        }
854    }
855
856    /// HMAC-based Key Derivation Function (HKDF) implementation using SHA-384.
857    ///
858    /// HKDF is a key derivation function based on HMAC, standardized in RFC 5869.
859    /// It derives cryptographically strong keys from input keying material.
860    ///
861    /// The HKDF process consists of two stages:
862    /// 1. Extract: Takes input keying material and an optional salt, produces a pseudorandom key (PRK)
863    /// 2. Expand: Takes the PRK and optional context info, generates output keying material
864    ///
865    /// # Examples
866    ///
867    /// ```
868    /// let prk = hmac_sha512::sha384::HKDF::extract(b"salt value", b"input key material");
869    ///
870    /// let mut okm = [0u8; 96];
871    /// hmac_sha512::sha384::HKDF::expand(&mut okm, prk, b"application info");
872    /// ```
873    pub struct HKDF;
874
875    impl HKDF {
876        /// Performs the HKDF-Extract function.
877        ///
878        /// Extracts a pseudorandom key from the input keying material using the optional salt.
879        ///
880        /// # Arguments
881        ///
882        /// * `salt` - Optional salt value (a non-secret random value)
883        /// * `ikm` - Input keying material (the secret input)
884        ///
885        /// # Returns
886        ///
887        /// A 48-byte pseudorandom key
888        pub fn extract(salt: impl AsRef<[u8]>, ikm: impl AsRef<[u8]>) -> [u8; 48] {
889            HMAC::mac(ikm, salt)
890        }
891
892        /// Performs the HKDF-Expand function.
893        ///
894        /// Expands the pseudorandom key into output keying material of the desired length.
895        ///
896        /// # Arguments
897        ///
898        /// * `out` - Buffer to receive the output keying material
899        /// * `prk` - Pseudorandom key (from the extract step)
900        /// * `info` - Optional context and application specific information
901        ///
902        /// # Panics
903        ///
904        /// Panics if the requested output length is greater than 255 * 48 bytes (12240 bytes).
905        pub fn expand(out: &mut [u8], prk: impl AsRef<[u8]>, info: impl AsRef<[u8]>) {
906            let info = info.as_ref();
907            let mut counter: u8 = 1;
908            assert!(out.len() < 0xff * 48);
909            let mut i: usize = 0;
910            while i < out.len() {
911                let mut hmac = HMAC::new(&prk);
912                if i != 0 {
913                    hmac.update(&out[i - 48..][..48]);
914                }
915                hmac.update(info);
916                hmac.update([counter]);
917                let left = core::cmp::min(48, out.len() - i);
918                out[i..][..left].copy_from_slice(&hmac.finalize()[..left]);
919                counter += 1;
920                i += 48;
921            }
922        }
923    }
924
925    #[cfg(feature = "traits09")]
926    mod digest_trait09 {
927        use digest09::consts::{U128, U48};
928        use digest09::{BlockInput, FixedOutputDirty, Output, Reset, Update};
929
930        use super::Hash;
931
932        impl BlockInput for Hash {
933            type BlockSize = U128;
934        }
935
936        impl Update for Hash {
937            fn update(&mut self, input: impl AsRef<[u8]>) {
938                self._update(input);
939            }
940        }
941
942        impl FixedOutputDirty for Hash {
943            type OutputSize = U48;
944
945            fn finalize_into_dirty(&mut self, out: &mut Output<Self>) {
946                let h = self.finalize();
947                out.copy_from_slice(&h);
948            }
949        }
950
951        impl Reset for Hash {
952            fn reset(&mut self) {
953                *self = Self::new();
954            }
955        }
956    }
957
958    /// Wrapped `Hash` type for the `Digest` trait.
959    #[cfg(feature = "traits010")]
960    pub type WrappedHash = digest010::core_api::CoreWrapper<Hash>;
961
962    #[cfg(feature = "traits010")]
963    mod digest_trait010 {
964        use core::fmt;
965
966        use digest010::{
967            block_buffer::Eager,
968            const_oid::{AssociatedOid, ObjectIdentifier},
969            consts::{U128, U48},
970            core_api::{
971                AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, FixedOutputCore,
972                OutputSizeUser, Reset, UpdateCore,
973            },
974            FixedOutput, FixedOutputReset, HashMarker, Output, Update,
975        };
976
977        use super::Hash;
978
979        impl AssociatedOid for Hash {
980            const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
981        }
982
983        impl AlgorithmName for Hash {
984            fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
985                f.write_str("Sha384")
986            }
987        }
988
989        impl HashMarker for Hash {}
990
991        impl BufferKindUser for Hash {
992            type BufferKind = Eager;
993        }
994
995        impl BlockSizeUser for Hash {
996            type BlockSize = U128;
997        }
998
999        impl OutputSizeUser for Hash {
1000            type OutputSize = U48;
1001        }
1002
1003        impl UpdateCore for Hash {
1004            #[inline]
1005            fn update_blocks(&mut self, blocks: &[Block<Self>]) {
1006                for block in blocks {
1007                    self._update(block);
1008                }
1009            }
1010        }
1011
1012        impl Update for Hash {
1013            #[inline]
1014            fn update(&mut self, data: &[u8]) {
1015                self._update(data);
1016            }
1017        }
1018
1019        impl FixedOutputCore for Hash {
1020            fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
1021                self._update(buffer.get_data());
1022                let h = self.finalize();
1023                out.copy_from_slice(&h);
1024            }
1025        }
1026
1027        impl FixedOutput for Hash {
1028            fn finalize_into(self, out: &mut Output<Self>) {
1029                let h = self.finalize();
1030                out.copy_from_slice(&h);
1031            }
1032        }
1033
1034        impl Reset for Hash {
1035            fn reset(&mut self) {
1036                *self = Self::new()
1037            }
1038        }
1039
1040        impl FixedOutputReset for Hash {
1041            fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
1042                self.finalize_into(out);
1043                self.reset();
1044            }
1045        }
1046    }
1047
1048    #[cfg(feature = "traits011")]
1049    mod digest_trait011 {
1050        use digest011::{
1051            const_oid::{AssociatedOid, ObjectIdentifier},
1052            consts::U48,
1053            FixedOutput, FixedOutputReset, HashMarker, Output, OutputSizeUser, Reset, Update,
1054        };
1055
1056        use super::Hash;
1057
1058        impl AssociatedOid for Hash {
1059            const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
1060        }
1061
1062        impl HashMarker for Hash {}
1063
1064        impl OutputSizeUser for Hash {
1065            type OutputSize = U48;
1066        }
1067
1068        impl Update for Hash {
1069            #[inline]
1070            fn update(&mut self, data: &[u8]) {
1071                self._update(data);
1072            }
1073        }
1074
1075        impl FixedOutput for Hash {
1076            fn finalize_into(self, out: &mut Output<Self>) {
1077                let h = self.finalize();
1078                out.copy_from_slice(&h);
1079            }
1080        }
1081
1082        impl Reset for Hash {
1083            fn reset(&mut self) {
1084                *self = Self::new()
1085            }
1086        }
1087
1088        impl FixedOutputReset for Hash {
1089            fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
1090                let h = self.finalize();
1091                out.copy_from_slice(&h);
1092                self.reset();
1093            }
1094        }
1095    }
1096}
1097
1098#[test]
1099fn main() {
1100    let h = HMAC::mac([], [0u8; 32]);
1101    let expected: [u8; 64] = [
1102        185, 54, 206, 232, 108, 159, 135, 170, 93, 60, 111, 46, 132, 203, 90, 66, 57, 165, 254, 80,
1103        72, 10, 110, 198, 107, 112, 171, 91, 31, 74, 198, 115, 12, 108, 81, 84, 33, 179, 39, 236,
1104        29, 105, 64, 46, 83, 223, 180, 154, 215, 56, 30, 176, 103, 179, 56, 253, 123, 12, 178, 34,
1105        71, 34, 93, 71,
1106    ];
1107    assert_eq!(h.to_vec(), expected.to_vec());
1108    assert!(HMAC::verify([], [0u8; 32], &expected));
1109
1110    let h = HMAC::mac([42u8; 69], []);
1111    let expected: [u8; 64] = [
1112        56, 224, 189, 205, 65, 104, 107, 85, 241, 188, 253, 35, 238, 174, 69, 191, 206, 183, 205,
1113        71, 196, 180, 56, 122, 106, 55, 136, 7, 208, 183, 99, 67, 229, 213, 255, 154, 107, 136, 11,
1114        154, 11, 187, 75, 214, 172, 117, 14, 248, 189, 48, 193, 62, 37, 208, 159, 227, 115, 59, 54,
1115        91, 143, 143, 254, 220,
1116    ];
1117    assert_eq!(h.to_vec(), expected.to_vec());
1118    assert!(HMAC::verify([42u8; 69], [], &expected));
1119
1120    let h = HMAC::mac([69u8; 250], [42u8; 50]);
1121    let expected: [u8; 64] = [
1122        122, 111, 187, 241, 74, 194, 22, 106, 95, 206, 80, 215, 75, 207, 11, 78, 37, 94, 125, 110,
1123        125, 42, 254, 103, 224, 21, 112, 247, 233, 229, 36, 200, 58, 238, 211, 156, 121, 231, 15,
1124        202, 128, 90, 126, 179, 188, 37, 194, 106, 223, 218, 45, 211, 149, 91, 131, 226, 117, 184,
1125        175, 85, 224, 197, 82, 175,
1126    ];
1127    assert_eq!(h.to_vec(), expected.to_vec());
1128    assert!(HMAC::verify([69u8; 250], [42u8; 50], &expected));
1129
1130    let h = HMAC::mac(b"Hi There", [0x0b; 20]);
1131    let expected: [u8; 64] = [
1132        135, 170, 124, 222, 165, 239, 97, 157, 79, 240, 180, 36, 26, 29, 108, 176, 35, 121, 244,
1133        226, 206, 78, 194, 120, 122, 208, 179, 5, 69, 225, 124, 222, 218, 168, 51, 183, 214, 184,
1134        167, 2, 3, 139, 39, 78, 174, 163, 244, 228, 190, 157, 145, 78, 235, 97, 241, 112, 46, 105,
1135        108, 32, 58, 18, 104, 84,
1136    ];
1137    assert_eq!(h.to_vec(), expected.to_vec());
1138    assert!(HMAC::verify(b"Hi There", [0x0b; 20], &expected));
1139
1140    // Test verify with incorrect expected value
1141    let incorrect: [u8; 64] = [0u8; 64];
1142    assert!(!HMAC::verify(b"Hi There", [0x0b; 20], &incorrect));
1143}
1144
1145#[cfg(feature = "sha384")]
1146#[test]
1147fn sha384() {
1148    let h = sha384::HMAC::mac(b"Hi There", [0x0b; 20]);
1149    let expected: [u8; 48] = [
1150        175, 208, 57, 68, 216, 72, 149, 98, 107, 8, 37, 244, 171, 70, 144, 127, 21, 249, 218, 219,
1151        228, 16, 30, 198, 130, 170, 3, 76, 124, 235, 197, 156, 250, 234, 158, 169, 7, 110, 222,
1152        127, 74, 241, 82, 232, 178, 250, 156, 182,
1153    ];
1154    assert_eq!(h.to_vec(), expected.to_vec());
1155    assert!(sha384::HMAC::verify(b"Hi There", [0x0b; 20], &expected));
1156
1157    // Test verify with incorrect expected value
1158    let incorrect: [u8; 48] = [0u8; 48];
1159    assert!(!sha384::HMAC::verify(b"Hi There", [0x0b; 20], &incorrect));
1160}
1161
1162#[cfg(feature = "sha384")]
1163#[test]
1164fn hkdf_sha384() {
1165    // Test Case 1 - RFC 5869 equivalent for SHA-384
1166    let ikm = [0x0bu8; 22];
1167    let salt: [u8; 13] = [
1168        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1169    ];
1170    let info: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
1171
1172    let prk = sha384::HKDF::extract(&salt, &ikm);
1173    let mut okm = [0u8; 42];
1174    sha384::HKDF::expand(&mut okm, &prk, &info);
1175
1176    // Expected value computed with reference implementation
1177    let expected_okm: [u8; 42] = [
1178        0x9b, 0x50, 0x97, 0xa8, 0x60, 0x38, 0xb8, 0x05, 0x30, 0x90, 0x76, 0xa4, 0x4b, 0x3a, 0x9f,
1179        0x38, 0x06, 0x3e, 0x25, 0xb5, 0x16, 0xdc, 0xbf, 0x36, 0x9f, 0x39, 0x4c, 0xfa, 0xb4, 0x36,
1180        0x85, 0xf7, 0x48, 0xb6, 0x45, 0x77, 0x63, 0xe4, 0xf0, 0x20, 0x4f, 0xc5,
1181    ];
1182    assert_eq!(okm, expected_okm);
1183
1184    // Test Case 2 - empty salt and info
1185    let ikm = [0x0bu8; 22];
1186    let prk = sha384::HKDF::extract([], &ikm);
1187    let mut okm = [0u8; 42];
1188    sha384::HKDF::expand(&mut okm, &prk, []);
1189
1190    let expected_okm: [u8; 42] = [
1191        0xc8, 0xc9, 0x6e, 0x71, 0x0f, 0x89, 0xb0, 0xd7, 0x99, 0x0b, 0xca, 0x68, 0xbc, 0xde, 0xc8,
1192        0xcf, 0x85, 0x40, 0x62, 0xe5, 0x4c, 0x73, 0xa7, 0xab, 0xc7, 0x43, 0xfa, 0xde, 0x9b, 0x24,
1193        0x2d, 0xaa, 0xcc, 0x1c, 0xea, 0x56, 0x70, 0x41, 0x5b, 0x52, 0x84, 0x9c,
1194    ];
1195    assert_eq!(okm, expected_okm);
1196}
1197
1198#[cfg(feature = "traits011")]
1199#[test]
1200fn digest011_sha512() {
1201    use digest011::Digest;
1202
1203    let mut hasher = Hash::new();
1204    hasher.update(b"hello");
1205    hasher.update(b" world");
1206    let result = hasher.finalize();
1207
1208    let expected = Hash::digest(b"hello world");
1209    assert_eq!(result.as_slice(), expected.as_slice());
1210}
1211
1212#[cfg(all(feature = "traits011", feature = "sha384"))]
1213#[test]
1214fn digest011_sha384() {
1215    use digest011::Digest;
1216
1217    let mut hasher = sha384::Hash::new();
1218    hasher.update(b"hello");
1219    hasher.update(b" world");
1220    let result = hasher.finalize();
1221
1222    let expected = sha384::Hash::digest(b"hello world");
1223    assert_eq!(result.as_slice(), expected.as_slice());
1224}
1225
1226#[test]
1227fn hkdf() {
1228    // Test Case 1 from brycx/Test-Vector-Generation (RFC 5869 equivalent for SHA512)
1229    let ikm = [0x0bu8; 22];
1230    let salt: [u8; 13] = [
1231        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
1232    ];
1233    let info: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
1234    let expected_okm: [u8; 42] = [
1235        0x83, 0x23, 0x90, 0x08, 0x6c, 0xda, 0x71, 0xfb, 0x47, 0x62, 0x5b, 0xb5, 0xce, 0xb1, 0x68,
1236        0xe4, 0xc8, 0xe2, 0x6a, 0x1a, 0x16, 0xed, 0x34, 0xd9, 0xfc, 0x7f, 0xe9, 0x2c, 0x14, 0x81,
1237        0x57, 0x93, 0x38, 0xda, 0x36, 0x2c, 0xb8, 0xd9, 0xf9, 0x25, 0xd7, 0xcb,
1238    ];
1239
1240    let prk = HKDF::extract(&salt, &ikm);
1241    let mut okm = [0u8; 42];
1242    HKDF::expand(&mut okm, &prk, &info);
1243    assert_eq!(okm, expected_okm);
1244
1245    // Test Case 3 - empty salt and info
1246    let ikm = [0x0bu8; 22];
1247    let expected_okm: [u8; 42] = [
1248        0xf5, 0xfa, 0x02, 0xb1, 0x82, 0x98, 0xa7, 0x2a, 0x8c, 0x23, 0x89, 0x8a, 0x87, 0x03, 0x47,
1249        0x2c, 0x6e, 0xb1, 0x79, 0xdc, 0x20, 0x4c, 0x03, 0x42, 0x5c, 0x97, 0x0e, 0x3b, 0x16, 0x4b,
1250        0xf9, 0x0f, 0xff, 0x22, 0xd0, 0x48, 0x36, 0xd0, 0xe2, 0x34, 0x3b, 0xac,
1251    ];
1252
1253    let prk = HKDF::extract([], &ikm);
1254    let mut okm = [0u8; 42];
1255    HKDF::expand(&mut okm, &prk, []);
1256    assert_eq!(okm, expected_okm);
1257}
1258
1259#[test]
1260fn hmac_streaming() {
1261    // Test that streaming HMAC produces the same result as one-shot
1262    let key = b"secret key";
1263    let message = b"Hello, World!";
1264
1265    let oneshot = HMAC::mac(message, key);
1266
1267    let mut streaming = HMAC::new(key);
1268    streaming.update(b"Hello, ");
1269    streaming.update(b"World!");
1270    let result = streaming.finalize();
1271
1272    assert_eq!(oneshot, result);
1273
1274    // Test finalize_verify
1275    let mut streaming = HMAC::new(key);
1276    streaming.update(message);
1277    assert!(streaming.finalize_verify(&oneshot));
1278}
1279
1280#[cfg(feature = "sha384")]
1281#[test]
1282fn hmac_sha384_streaming() {
1283    let key = b"secret key";
1284    let message = b"Hello, World!";
1285
1286    let oneshot = sha384::HMAC::mac(message, key);
1287
1288    let mut streaming = sha384::HMAC::new(key);
1289    streaming.update(b"Hello, ");
1290    streaming.update(b"World!");
1291    let result = streaming.finalize();
1292
1293    assert_eq!(oneshot, result);
1294
1295    // Test finalize_verify
1296    let mut streaming = sha384::HMAC::new(key);
1297    streaming.update(message);
1298    assert!(streaming.finalize_verify(&oneshot));
1299}