initial
This commit is contained in:
151
src/mac.rs
Normal file
151
src/mac.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use hmac::{Hmac, Mac};
|
||||
use sha2::Sha512;
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use crate::error::{OpaqueError, Result};
|
||||
|
||||
pub const MAC_LEN: usize = 64;
|
||||
|
||||
type HmacSha512 = Hmac<Sha512>;
|
||||
|
||||
pub fn compute(key: &[u8], data: &[u8]) -> [u8; MAC_LEN] {
|
||||
let mut mac = HmacSha512::new_from_slice(key).expect("HMAC accepts any key length");
|
||||
mac.update(data);
|
||||
mac.finalize().into_bytes().into()
|
||||
}
|
||||
|
||||
pub fn verify(key: &[u8], data: &[u8], expected: &[u8]) -> Result<()> {
|
||||
if expected.len() != MAC_LEN {
|
||||
return Err(OpaqueError::MacVerificationFailed);
|
||||
}
|
||||
|
||||
let computed = compute(key, data);
|
||||
if computed.ct_eq(expected).into() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(OpaqueError::MacVerificationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_multi(key: &[u8], parts: &[&[u8]]) -> [u8; MAC_LEN] {
|
||||
let mut mac = HmacSha512::new_from_slice(key).expect("HMAC accepts any key length");
|
||||
for part in parts {
|
||||
mac.update(part);
|
||||
}
|
||||
mac.finalize().into_bytes().into()
|
||||
}
|
||||
|
||||
pub struct HmacContext {
|
||||
mac: HmacSha512,
|
||||
}
|
||||
|
||||
impl HmacContext {
|
||||
#[must_use]
|
||||
pub fn new(key: &[u8]) -> Self {
|
||||
let mac = HmacSha512::new_from_slice(key).expect("HMAC accepts any key length");
|
||||
Self { mac }
|
||||
}
|
||||
|
||||
pub fn update(&mut self, data: &[u8]) {
|
||||
self.mac.update(data);
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn finalize(self) -> [u8; MAC_LEN] {
|
||||
self.mac.finalize().into_bytes().into()
|
||||
}
|
||||
|
||||
pub fn verify(self, expected: &[u8]) -> Result<()> {
|
||||
if expected.len() != MAC_LEN {
|
||||
return Err(OpaqueError::MacVerificationFailed);
|
||||
}
|
||||
|
||||
let computed = self.finalize();
|
||||
if computed.ct_eq(expected).into() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(OpaqueError::MacVerificationFailed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_compute_and_verify() {
|
||||
let key = b"secret key";
|
||||
let data = b"message to authenticate";
|
||||
|
||||
let tag = compute(key, data);
|
||||
assert_eq!(tag.len(), MAC_LEN);
|
||||
|
||||
assert!(verify(key, data, &tag).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_wrong_tag() {
|
||||
let key = b"secret key";
|
||||
let data = b"message";
|
||||
let wrong_tag = [0u8; MAC_LEN];
|
||||
|
||||
assert!(verify(key, data, &wrong_tag).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_wrong_length() {
|
||||
let key = b"secret key";
|
||||
let data = b"message";
|
||||
let short_tag = [0u8; 32];
|
||||
|
||||
assert!(verify(key, data, &short_tag).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_multi() {
|
||||
let key = b"secret key";
|
||||
let part1 = b"hello ";
|
||||
let part2 = b"world";
|
||||
|
||||
let tag_multi = compute_multi(key, &[part1, part2]);
|
||||
let tag_single = compute(key, b"hello world");
|
||||
|
||||
assert_eq!(tag_multi, tag_single);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_context() {
|
||||
let key = b"secret key";
|
||||
let data = b"message to authenticate";
|
||||
|
||||
let mut ctx = HmacContext::new(key);
|
||||
ctx.update(data);
|
||||
let tag1 = ctx.finalize();
|
||||
|
||||
let tag2 = compute(key, data);
|
||||
|
||||
assert_eq!(tag1, tag2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hmac_context_verify() {
|
||||
let key = b"secret key";
|
||||
let data = b"message";
|
||||
|
||||
let tag = compute(key, data);
|
||||
|
||||
let mut ctx = HmacContext::new(key);
|
||||
ctx.update(data);
|
||||
assert!(ctx.verify(&tag).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_different_keys_different_tags() {
|
||||
let data = b"message";
|
||||
let tag1 = compute(b"key1", data);
|
||||
let tag2 = compute(b"key2", data);
|
||||
|
||||
assert_ne!(tag1, tag2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user