1diff --git a/src/hmac.rs b/src/hmac.rs
2index 601ae01..465781e 100644
3--- a/src/hmac.rs
4+++ b/src/hmac.rs
5@@ -1,10 +1,12 @@
6-use crate::cvt_p;
7 use crate::error::ErrorStack;
8 use crate::md::MdRef;
9+use crate::{cvt, cvt_p};
10+use ffi::HMAC_CTX;
11 use foreign_types::ForeignTypeRef;
12+use libc::{c_uint, c_void};
13 use openssl_macros::corresponds;
14-use libc::{c_void, c_uint};
15 use std::convert::TryFrom;
16+use std::ptr;
17
18 /// Computes the HMAC as a one-shot operation.
19 ///
20@@ -20,8 +22,9 @@ pub fn hmac<'a>(
21     md: &MdRef,
22     key: &[u8],
23     data: &[u8],
24-    out: &'a mut [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@@ -31,38 +34,184 @@ pub fn hmac<'a>(
32             data.as_ptr(),
33             data.len(),
34             out.as_mut_ptr(),
35-            &mut out_len
36-            ))?;
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)]
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)]
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)]
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)]
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-    use crate::memcmp;
132
133-    const SHA_256_DIGEST_SIZE:usize = 32;
134+    const SHA_256_DIGEST_SIZE: usize = 32;
135
136     #[test]
137     fn hmac_sha256_test() {
138-        let expected_hmac = [0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7];
139+        let expected_hmac = [
140+            0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
141+            0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
142+            0x2e, 0x32, 0xcf, 0xf7,
143+        ];
144         let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
145-        let key:[u8; 20] = [0x0b; 20];
146+        let key: [u8; 20] = [0x0b; 20];
147         let data = b"Hi There";
148-        let hmac_result = hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
149-        expect!(memcmp::eq(&hmac_result, &expected_hmac));
150+        let hmac_result =
151+            hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
152+        assert_eq!(&hmac_result, &expected_hmac);
153+    }
154+
155+    #[test]
156+    #[should_panic]
157+    fn hmac_sha256_output_too_short() {
158+        let mut out = vec![0_u8; 1];
159+        let key: [u8; 20] = [0x0b; 20];
160+        let data = b"Hi There";
161+        hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
162     }
163
164     #[test]
165     fn hmac_sha256_test_big_buffer() {
166-        let expected_hmac = [0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7];
167+        let expected_hmac = [
168+            0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
169+            0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
170+            0x2e, 0x32, 0xcf, 0xf7,
171+        ];
172         let mut out: [u8; 100] = [0; 100];
173-        let key:[u8;20] = [0x0b; 20];
174+        let key: [u8; 20] = [0x0b; 20];
175+        let data = b"Hi There";
176+        let hmac_result =
177+            hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
178+        assert_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE);
179+        assert_eq!(&hmac_result, &expected_hmac);
180+    }
181+
182+    #[test]
183+    fn hmac_sha256_update_test() {
184+        let expected_hmac = [
185+            0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
186+            0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
187+            0x2e, 0x32, 0xcf, 0xf7,
188+        ];
189+        let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
190+        let key: [u8; 20] = [0x0b; 20];
191         let data = b"Hi There";
192-        let hmac_result = hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
193-        expect_eq!(hmac_result.len(), SHA_256_DIGEST_SIZE);
194-        expect!(memcmp::eq(&hmac_result, &expected_hmac));
195+        let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
196+        hmac_ctx.update(data).unwrap();
197+        let size = hmac_ctx.finalize(&mut out).unwrap();
198+        assert_eq!(&out, &expected_hmac);
199+        assert_eq!(size, SHA_256_DIGEST_SIZE);
200+    }
201+
202+    #[test]
203+    fn hmac_sha256_update_chunks_test() {
204+        let expected_hmac = [
205+            0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0xb,
206+            0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x0, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c,
207+            0x2e, 0x32, 0xcf, 0xf7,
208+        ];
209+        let mut out: [u8; SHA_256_DIGEST_SIZE] = [0; SHA_256_DIGEST_SIZE];
210+        let key: [u8; 20] = [0x0b; 20];
211+        let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
212+        hmac_ctx.update(b"Hi").unwrap();
213+        hmac_ctx.update(b" There").unwrap();
214+        let size = hmac_ctx.finalize(&mut out).unwrap();
215+        assert_eq!(&out, &expected_hmac);
216+        assert_eq!(size, SHA_256_DIGEST_SIZE);
217+    }
218+
219+    #[test]
220+    #[should_panic]
221+    fn hmac_sha256_update_output_too_short() {
222+        let mut out = vec![0_u8; 1];
223+        let key: [u8; 20] = [0x0b; 20];
224+        let mut hmac_ctx = HmacCtx::new(&key, Md::sha256()).unwrap();
225+        hmac_ctx.update(b"Hi There").unwrap();
226+        hmac_ctx.finalize(&mut out).unwrap();
227     }
228 }
229