1 use crate::error::ErrorStack;
2 use crate::md::MdRef;
3 use crate::{cvt, cvt_p};
4 use ffi::HMAC_CTX;
5 use foreign_types::ForeignTypeRef;
6 use libc::{c_uint, c_void};
7 use openssl_macros::corresponds;
8 use std::convert::TryFrom;
9 use std::ptr;
10
11 /// Computes the HMAC as a one-shot operation.
12 ///
13 /// Calculates the HMAC of data, using the given |key|
14 /// and hash function |md|, and returns the result re-using the space from
15 /// buffer |out|. On entry, |out| must contain at least |EVP_MD_size| bytes
16 /// of space. The actual length of the result is used to resize the returned
17 /// slice. An output size of |EVP_MAX_MD_SIZE| will always be large enough.
18 /// It returns a resized |out| or ErrorStack on error.
19 #[corresponds(HMAC)]
20 #[inline]
hmac<'a>( md: &MdRef, key: &[u8], data: &[u8], out: &'a mut [u8], ) -> Result<&'a [u8], ErrorStack>21 pub fn hmac<'a>(
22 md: &MdRef,
23 key: &[u8],
24 data: &[u8],
25 out: &'a mut [u8],
26 ) -> Result<&'a [u8], ErrorStack> {
27 assert!(out.len() >= md.size());
28 let mut out_len = c_uint::try_from(out.len()).unwrap();
29 unsafe {
30 cvt_p(ffi::HMAC(
31 md.as_ptr(),
32 key.as_ptr() as *const c_void,
33 key.len(),
34 data.as_ptr(),
35 data.len(),
36 out.as_mut_ptr(),
37 &mut out_len,
38 ))?;
39 }
40 Ok(&out[..out_len as usize])
41 }
42
43 /// A context object used to perform HMAC operations.
44 ///
45 /// HMAC is a MAC (message authentication code), i.e. a keyed hash function used for message
46 /// authentication, which is based on a hash function.
47 ///
48 /// Note: Only available in boringssl. For openssl, use `PKey::hmac` instead.
49 #[cfg(boringssl)]
50 pub struct HmacCtx {
51 ctx: *mut HMAC_CTX,
52 output_size: usize,
53 }
54
55 #[cfg(boringssl)]
56 impl HmacCtx {
57 /// Creates a new [HmacCtx] to use the hash function `md` and key `key`.
58 #[corresponds(HMAC_Init_ex)]
new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack>59 pub fn new(key: &[u8], md: &MdRef) -> Result<Self, ErrorStack> {
60 unsafe {
61 // Safety: If an error occurred, the resulting null from HMAC_CTX_new is converted into
62 // ErrorStack in the returned result by `cvt_p`.
63 let ctx = cvt_p(ffi::HMAC_CTX_new())?;
64 // Safety:
65 // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new,
66 // which is the line above.
67 // - HMAC_Init_ex may return an error if key is null but the md is different from
68 // before. This is avoided here since key is guaranteed to be non-null.
69 cvt(ffi::HMAC_Init_ex(
70 ctx,
71 key.as_ptr() as *const c_void,
72 key.len(),
73 md.as_ptr(),
74 ptr::null_mut(),
75 ))?;
76 Ok(Self {
77 ctx,
78 output_size: md.size(),
79 })
80 }
81 }
82
83 /// `update` can be called repeatedly with chunks of the message `data` to be authenticated.
84 #[corresponds(HMAC_Update)]
update(&mut self, data: &[u8]) -> Result<(), ErrorStack>85 pub fn update(&mut self, data: &[u8]) -> Result<(), ErrorStack> {
86 unsafe {
87 // Safety: HMAC_Update returns 0 on error, and that is converted into ErrorStack in the
88 // returned result by `cvt`.
89 cvt(ffi::HMAC_Update(self.ctx, data.as_ptr(), data.len())).map(|_| ())
90 }
91 }
92
93 /// Finishes the HMAC process, and places the message authentication code in `output`.
94 /// The number of bytes written to `output` is returned.
95 ///
96 /// # Panics
97 ///
98 /// Panics if the `output` is smaller than the required size. The output size is indicated by
99 /// `md.size()` for the `Md` instance passed in [new]. An output size of |EVP_MAX_MD_SIZE| will
100 /// always be large enough.
101 #[corresponds(HMAC_Final)]
finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack>102 pub fn finalize(&mut self, output: &mut [u8]) -> Result<usize, ErrorStack> {
103 assert!(output.len() >= self.output_size);
104 unsafe {
105 // Safety: The length assertion above makes sure that `HMAC_Final` will not write longer
106 // than the length of `output`.
107 let mut size: c_uint = 0;
108 cvt(ffi::HMAC_Final(
109 self.ctx,
110 output.as_mut_ptr(),
111 &mut size as *mut c_uint,
112 ))
113 .map(|_| size as usize)
114 }
115 }
116 }
117
118 impl Drop for HmacCtx {
119 #[corresponds(HMAC_CTX_free)]
drop(&mut self)120 fn drop(&mut self) {
121 unsafe {
122 ffi::HMAC_CTX_free(self.ctx);
123 }
124 }
125 }
126
127 #[cfg(test)]
128 mod tests {
129 use super::*;
130 use crate::md::Md;
131
132 const SHA_256_DIGEST_SIZE: usize = 32;
133
134 #[test]
hmac_sha256_test()135 fn hmac_sha256_test() {
136 let expected_hmac = [
137 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
138 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
139 0x2e, 0x32, 0xcf, 0xf7,
140 ];
141 let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
142 let key: [u8; 20] = [0x0b; 20];
143 let data = b"Hi There";
144 let hmac_result =
145 hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
146 assert_eq!(&hmac_result, &expected_hmac);
147 }
148
149 #[test]
150 #[should_panic]
hmac_sha256_output_too_short()151 fn hmac_sha256_output_too_short() {
152 let mut out = vec![0_u8; 1];
153 let key: [u8; 20] = [0x0b; 20];
154 let data = b"Hi There";
155 hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
156 }
157
158 #[test]
hmac_sha256_test_big_buffer()159 fn hmac_sha256_test_big_buffer() {
160 let expected_hmac = [
161 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
162 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
163 0x2e, 0x32, 0xcf, 0xf7,
164 ];
165 let mut out: [u8; 100] = [0; 100];
166 let key: [u8; 20] = [0x0b; 20];
167 let data = b"Hi There";
168 let hmac_result =
169 hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
170 assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE);
171 assert_eq!(&hmac_result, &expected_hmac);
172 }
173
174 #[test]
hmac_sha256_update_test()175 fn hmac_sha256_update_test() {
176 let expected_hmac = [
177 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
178 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
179 0x2e, 0x32, 0xcf, 0xf7,
180 ];
181 let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
182 let key: [u8; 20] = [0x0b; 20];
183 let data = b"Hi There";
184 let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
185 hmac_ctx.update(data).unwrap();
186 let size = hmac_ctx.finalize(&mut out).unwrap();
187 assert_eq!(&out, &expected_hmac);
188 assert_eq!(size, SHA_256_DIGEST_SIZE);
189 }
190
191 #[test]
hmac_sha256_update_chunks_test()192 fn hmac_sha256_update_chunks_test() {
193 let expected_hmac = [
194 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
195 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
196 0x2e, 0x32, 0xcf, 0xf7,
197 ];
198 let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
199 let key: [u8; 20] = [0x0b; 20];
200 let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
201 hmac_ctx.update(b"Hi").unwrap();
202 hmac_ctx.update(b" There").unwrap();
203 let size = hmac_ctx.finalize(&mut out).unwrap();
204 assert_eq!(&out, &expected_hmac);
205 assert_eq!(size, SHA_256_DIGEST_SIZE);
206 }
207
208 #[test]
209 #[should_panic]
hmac_sha256_update_output_too_short()210 fn hmac_sha256_update_output_too_short() {
211 let mut out = vec![0_u8; 1];
212 let key: [u8; 20] = [0x0b; 20];
213 let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
214 hmac_ctx.update(b"Hi There").unwrap();
215 hmac_ctx.finalize(&mut out).unwrap();
216 }
217 }
218