expose a reset API on hmac which resets the current instance back to its original state keeping the same key as initially used

Change-Id: Ie781e2a20da26b50b34f35ea0a5cfc578b64ee7f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/58565
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/rust/bssl-crypto/src/hmac.rs b/rust/bssl-crypto/src/hmac.rs
index 3da5f31..7dbf39b 100644
--- a/rust/bssl-crypto/src/hmac.rs
+++ b/rust/bssl-crypto/src/hmac.rs
@@ -79,6 +79,11 @@
     pub fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
         self.0.verify_truncated_left(tag)
     }
+
+    /// Resets the hmac instance to its initial state
+    pub fn reset(&mut self) {
+        self.0.reset()
+    }
 }
 
 /// The BoringSSL HMAC-SHA512 implementation. The operations may panic if memory allocation fails
@@ -120,6 +125,11 @@
     pub fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> {
         self.0.verify_truncated_left(tag)
     }
+
+    /// Resets the hmac instance to its initial state
+    pub fn reset(&mut self) {
+        self.0.reset()
+    }
 }
 
 /// Error type for when the output of the hmac operation is not equal to the expected value.
@@ -268,6 +278,27 @@
         .then_some(())
         .ok_or(MacError)
     }
+
+    /// Resets the hmac instance to its original state
+    fn reset(&mut self) {
+        // Passing a null ptr for the key will re-use the existing key
+        // Safety:
+        // - HMAC_Init_ex must be called with a context previously created with HMAC_CTX_new,
+        //   which will always be the case if it is coming from self
+        // - HMAC_Init_ex may return an error if key is null but the md is different from
+        //   before. The MD is guaranteed to be the same because it comes from the same generic param
+        // - HMAC_Init_ex returns 0 on allocation failure in which case we panic
+        let result = unsafe {
+            bssl_sys::HMAC_Init_ex(
+                self.ctx,
+                ptr::null_mut(),
+                0,
+                M::get_md().as_ptr(),
+                ptr::null_mut(),
+            )
+        };
+        assert!(result > 0, "Allocation failure in bssl_sys::HMAC_Init_ex");
+    }
 }
 
 impl<const N: usize, M: Md> Drop for Hmac<N, M> {
@@ -295,8 +326,29 @@
         hmac.update(data);
         let hmac_result: [u8; 32] = hmac.finalize();
 
-        // let hmac_result =
-        //     hmac(Md::sha256(), &key, data, &mut out).expect("Couldn't calculate sha256 hmac");
+        assert_eq!(&hmac_result, &expected_hmac);
+    }
+
+    #[test]
+    fn hmac_sha256_reset_test() {
+        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,
+        ];
+
+        let key: [u8; 20] = [0x0b; 20];
+        let data = b"Hi There";
+        let incorrect_data = b"This data does not match the expected mac";
+
+        let mut hmac = HmacSha256::new_from_slice(&key);
+        hmac.update(incorrect_data);
+        hmac.reset();
+
+        // hmac should be back to original state, so now when we update with the correct data it
+        // should work
+        hmac.update(data);
+        let hmac_result: [u8; 32] = hmac.finalize();
         assert_eq!(&hmac_result, &expected_hmac);
     }