rustls_mbedcrypto_provider/
hmac.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 */
7
8use crate::log::error;
9use alloc::boxed::Box;
10use rustls::crypto;
11
12/// HMAC using SHA-256.
13pub static HMAC_SHA256: Hmac = Hmac(&super::hash::MBED_SHA_256);
14
15/// HMAC using SHA-384.
16pub static HMAC_SHA384: Hmac = Hmac(&super::hash::MBED_SHA_384);
17
18/// A HMAC algorithm implemented [`crypto::hmac::Hmac`].
19pub struct Hmac(pub(crate) &'static super::hash::Algorithm);
20
21impl crypto::hmac::Hmac for Hmac {
22    fn with_key(&self, key: &[u8]) -> Box<dyn crypto::hmac::Key> {
23        Box::new(HmacKey(MbedHmacKey::new(self.0, key)))
24    }
25
26    fn hash_output_len(&self) -> usize {
27        self.0.output_len
28    }
29}
30
31impl Hmac {
32    #[inline]
33    pub(crate) fn hash_algorithm(&self) -> &'static super::hash::Algorithm {
34        self.0
35    }
36}
37
38struct HmacKey(MbedHmacKey);
39
40impl crypto::hmac::Key for HmacKey {
41    fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag {
42        let mut ctx = self.0.starts();
43        ctx.update(first);
44        for m in middle {
45            ctx.update(m);
46        }
47        ctx.update(last);
48        ctx.finish().into()
49    }
50
51    fn tag_len(&self) -> usize {
52        self.0.hmac_algo.output_len
53    }
54}
55
56struct MbedHmacKey {
57    hmac_algo: &'static super::hash::Algorithm,
58    key: Vec<u8>,
59}
60
61impl MbedHmacKey {
62    pub(crate) fn new(hmac_algo: &'static super::hash::Algorithm, key: &[u8]) -> Self {
63        Self { hmac_algo, key: key.to_vec() }
64    }
65
66    pub(crate) fn starts(&self) -> MbedHmacContext {
67        MbedHmacContext {
68            hmac_algo: self.hmac_algo,
69            ctx: mbedtls::hash::Hmac::new(self.hmac_algo.hash_type, &self.key).expect("input validated"),
70        }
71    }
72}
73
74impl Drop for MbedHmacKey {
75    fn drop(&mut self) {
76        mbedtls::zeroize(&mut self.key)
77    }
78}
79
80struct MbedHmacContext {
81    hmac_algo: &'static super::hash::Algorithm,
82    ctx: mbedtls::hash::Hmac,
83}
84
85impl MbedHmacContext {
86    /// Since the trait does not provider a way to return error, empty vector is returned when getting error from `mbedtls`.
87    pub(crate) fn finish(self) -> Tag {
88        let mut out = Tag::with_len(self.hmac_algo.output_len);
89        match self.ctx.finish(out.as_mut()) {
90            Ok(_) => out,
91            Err(_err) => {
92                error!("Failed to finish hmac, mbedtls error: {_err:?}");
93                Tag::with_len(0)
94            }
95        }
96    }
97
98    pub(crate) fn update(&mut self, data: &[u8]) {
99        match self.ctx.update(data) {
100            Ok(_) => {}
101            Err(_err) => {
102                error!("Failed to update hmac, mbedtls error: {_err:?}");
103            }
104        }
105    }
106}
107
108/// A HMAC tag, stored as a value.
109#[derive(Clone, PartialEq, Eq, Debug)]
110pub(crate) struct Tag {
111    buf: [u8; Self::MAX_LEN],
112    used: usize,
113}
114
115impl Tag {
116    /// Build a tag with given capacity.
117    ///
118    /// The slice can be up to [`Tag::MAX_LEN`] bytes in length.
119    pub(crate) fn with_len(len: usize) -> Self {
120        Self { buf: [0u8; Self::MAX_LEN], used: len }
121    }
122
123    /// Maximum supported HMAC tag size: supports up to SHA512.
124    pub(crate) const MAX_LEN: usize = 64;
125}
126
127impl Drop for Tag {
128    fn drop(&mut self) {
129        mbedtls::zeroize(&mut self.buf)
130    }
131}
132
133impl AsRef<[u8]> for Tag {
134    fn as_ref(&self) -> &[u8] {
135        &self.buf[..self.used]
136    }
137}
138
139impl AsMut<[u8]> for Tag {
140    fn as_mut(&mut self) -> &mut [u8] {
141        &mut self.buf[..self.used]
142    }
143}
144
145impl From<Tag> for crypto::hmac::Tag {
146    fn from(val: Tag) -> Self {
147        Self::new(&val.buf[..val.used])
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use rustls::crypto::hmac::Hmac;
154
155    use super::*;
156
157    #[test]
158    fn test_hmac_sha256_tag_length() {
159        let hmac = &HMAC_SHA256;
160        let key_len = 256 / 8;
161        let key = vec![0u8; key_len];
162        test_hmac_tag_length_helper(hmac, &key, key_len);
163    }
164
165    #[test]
166    fn test_hmac_sha384_tag_length() {
167        let hmac = &HMAC_SHA384;
168        let key_len = 384 / 8;
169        let key = vec![0u8; key_len];
170        test_hmac_tag_length_helper(hmac, &key, key_len);
171    }
172
173    fn test_hmac_tag_length_helper(hmac: &super::Hmac, key: &[u8], key_len: usize) {
174        let hmac_key = hmac.with_key(key);
175        assert_eq!(hmac.hash_output_len(), hmac_key.tag_len());
176        assert_eq!(hmac.hash_output_len(), key_len);
177    }
178
179    #[test]
180    fn test_mbed_hmac_context_error() {
181        let key_len = 256 / 8;
182        let key = vec![0u8; key_len];
183        let mut bad_ctx = MbedHmacContext {
184            hmac_algo: &crate::hash::MBED_SHA_256,
185            ctx: mbedtls::hash::Hmac::new(crate::hash::MBED_SHA_384.hash_type, &key).unwrap(),
186        };
187        bad_ctx.update(&[]);
188        let tag = bad_ctx.finish();
189        assert_eq!(tag, Tag::with_len(0));
190    }
191}