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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use super::{get_der_key, IPAD, OPAD};
use core::{fmt, slice};
use digest::{
    block_buffer::Eager,
    core_api::{
        AlgorithmName, Block, BlockSizeUser, Buffer, BufferKindUser, CoreWrapper, FixedOutputCore,
        OutputSizeUser, UpdateCore,
    },
    crypto_common::{Key, KeySizeUser},
    HashMarker, InvalidLength, KeyInit, MacMarker, Output,
};

/// Generic HMAC instance.
pub type Hmac<D> = CoreWrapper<HmacCore<D>>;

/// Trait implemented by eager hashes which expose their block-level core.
pub trait EagerHash {
    /// Block-level core type of the hash.
    type Core: HashMarker
        + UpdateCore
        + FixedOutputCore
        + BufferKindUser<BufferKind = Eager>
        + Default
        + Clone;
}

impl<C> EagerHash for CoreWrapper<C>
where
    C: HashMarker
        + UpdateCore
        + FixedOutputCore
        + BufferKindUser<BufferKind = Eager>
        + Default
        + Clone,
{
    type Core = C;
}

/// Generic core HMAC instance, which operates over blocks.
pub struct HmacCore<D: EagerHash> {
    digest: D::Core,
    opad_digest: D::Core,
    #[cfg(feature = "reset")]
    ipad_digest: D::Core,
}

impl<D: EagerHash> Clone for HmacCore<D> {
    fn clone(&self) -> Self {
        Self {
            digest: self.digest.clone(),
            opad_digest: self.opad_digest.clone(),
            #[cfg(feature = "reset")]
            ipad_digest: self.ipad_digest.clone(),
        }
    }
}

impl<D: EagerHash> MacMarker for HmacCore<D> {}

impl<D: EagerHash> BufferKindUser for HmacCore<D> {
    type BufferKind = Eager;
}

impl<D: EagerHash> KeySizeUser for HmacCore<D> {
    type KeySize = <<D as EagerHash>::Core as BlockSizeUser>::BlockSize;
}

impl<D: EagerHash> BlockSizeUser for HmacCore<D> {
    type BlockSize = <<D as EagerHash>::Core as BlockSizeUser>::BlockSize;
}

impl<D: EagerHash> OutputSizeUser for HmacCore<D> {
    type OutputSize = <<D as EagerHash>::Core as OutputSizeUser>::OutputSize;
}

impl<D: EagerHash> KeyInit for HmacCore<D> {
    #[inline(always)]
    fn new(key: &Key<Self>) -> Self {
        Self::new_from_slice(key.as_slice()).unwrap()
    }

    #[inline(always)]
    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
        let mut buf = get_der_key::<CoreWrapper<D::Core>>(key);
        for b in buf.iter_mut() {
            *b ^= IPAD;
        }
        let mut digest = D::Core::default();
        digest.update_blocks(slice::from_ref(&buf));

        for b in buf.iter_mut() {
            *b ^= IPAD ^ OPAD;
        }

        let mut opad_digest = D::Core::default();
        opad_digest.update_blocks(slice::from_ref(&buf));

        Ok(Self {
            #[cfg(feature = "reset")]
            ipad_digest: digest.clone(),
            opad_digest,
            digest,
        })
    }
}

impl<D: EagerHash> UpdateCore for HmacCore<D> {
    #[inline(always)]
    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
        self.digest.update_blocks(blocks);
    }
}

impl<D: EagerHash> FixedOutputCore for HmacCore<D> {
    #[inline(always)]
    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
        let mut hash = Output::<D::Core>::default();
        self.digest.finalize_fixed_core(buffer, &mut hash);
        // finalize_fixed_core should reset the buffer as well, but
        // to be extra safe we reset it explicitly again.
        buffer.reset();
        #[cfg(not(feature = "reset"))]
        let h = &mut self.opad_digest;
        #[cfg(feature = "reset")]
        let mut h = self.opad_digest.clone();
        buffer.digest_blocks(&hash, |b| h.update_blocks(b));
        h.finalize_fixed_core(buffer, out);
    }
}

#[cfg(feature = "reset")]
#[cfg_attr(docsrs, doc(cfg(feature = "reset")))]
impl<D: EagerHash> digest::Reset for HmacCore<D> {
    #[inline(always)]
    fn reset(&mut self) {
        self.digest = self.ipad_digest.clone();
    }
}

impl<D: EagerHash> AlgorithmName for HmacCore<D>
where
    D::Core: AlgorithmName,
{
    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Hmac<")?;
        <D::Core as AlgorithmName>::write_alg_name(f)?;
        f.write_str(">")
    }
}

impl<D: EagerHash> fmt::Debug for HmacCore<D>
where
    D::Core: AlgorithmName,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("HmacCore<")?;
        <D::Core as AlgorithmName>::write_alg_name(f)?;
        f.write_str("> { ... }")
    }
}