1#![no_std]
4#![cfg_attr(docsrs, feature(doc_cfg))]
5#![doc(
6 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
7 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
8 html_root_url = "https://docs.rs/crypto-mac/0.11.1"
9)]
10#![forbid(unsafe_code)]
11#![warn(missing_docs, rust_2018_idioms)]
12
13#[cfg(feature = "std")]
14extern crate std;
15
16#[cfg(feature = "cipher")]
17pub use cipher;
18#[cfg(feature = "cipher")]
19use cipher::{BlockCipher, NewBlockCipher};
20
21#[cfg(feature = "dev")]
22#[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
23pub mod dev;
24
25mod errors;
26
27pub use crate::errors::{InvalidKeyLength, MacError};
28pub use generic_array::{self, typenum::consts};
29
30use generic_array::typenum::Unsigned;
31use generic_array::{ArrayLength, GenericArray};
32use subtle::{Choice, ConstantTimeEq};
33
34pub type Key<M> = GenericArray<u8, <M as NewMac>::KeySize>;
36
37pub trait NewMac: Sized {
39 type KeySize: ArrayLength<u8>;
41
42 fn new(key: &Key<Self>) -> Self;
44
45 fn new_from_slice(key: &[u8]) -> Result<Self, InvalidKeyLength> {
50 if key.len() != Self::KeySize::to_usize() {
51 Err(InvalidKeyLength)
52 } else {
53 Ok(Self::new(GenericArray::from_slice(key)))
54 }
55 }
56}
57
58pub trait Mac: Clone {
60 type OutputSize: ArrayLength<u8>;
62
63 fn update(&mut self, data: &[u8]);
65
66 fn reset(&mut self);
68
69 fn finalize(self) -> Output<Self>;
72
73 fn finalize_reset(&mut self) -> Output<Self> {
76 let res = self.clone().finalize();
77 self.reset();
78 res
79 }
80
81 fn verify(self, tag: &[u8]) -> Result<(), MacError> {
83 let choice = self.finalize().bytes.ct_eq(tag);
84
85 if choice.unwrap_u8() == 1 {
86 Ok(())
87 } else {
88 Err(MacError)
89 }
90 }
91}
92
93#[derive(Clone)]
96pub struct Output<M: Mac> {
97 bytes: GenericArray<u8, M::OutputSize>,
98}
99
100impl<M: Mac> Output<M> {
101 pub fn new(bytes: GenericArray<u8, M::OutputSize>) -> Output<M> {
103 Output { bytes }
104 }
105
106 pub fn into_bytes(self) -> GenericArray<u8, M::OutputSize> {
112 self.bytes
113 }
114}
115
116impl<M: Mac> ConstantTimeEq for Output<M> {
117 fn ct_eq(&self, other: &Self) -> Choice {
118 self.bytes.ct_eq(&other.bytes)
119 }
120}
121
122impl<M: Mac> PartialEq for Output<M> {
123 fn eq(&self, x: &Output<M>) -> bool {
124 self.ct_eq(x).unwrap_u8() == 1
125 }
126}
127
128impl<M: Mac> Eq for Output<M> {}
129
130#[cfg(feature = "cipher")]
131#[cfg_attr(docsrs, doc(cfg(feature = "cipher")))]
132pub trait FromBlockCipher {
134 type Cipher: BlockCipher;
136
137 fn from_cipher(cipher: Self::Cipher) -> Self;
139}
140
141#[cfg(feature = "cipher")]
142#[cfg_attr(docsrs, doc(cfg(feature = "cipher")))]
143impl<T> NewMac for T
144where
145 T: FromBlockCipher,
146 T::Cipher: NewBlockCipher,
147{
148 type KeySize = <<Self as FromBlockCipher>::Cipher as NewBlockCipher>::KeySize;
149
150 fn new(key: &Key<Self>) -> Self {
151 let cipher = <Self as FromBlockCipher>::Cipher::new(key);
152 Self::from_cipher(cipher)
153 }
154
155 fn new_from_slice(key: &[u8]) -> Result<Self, InvalidKeyLength> {
156 <Self as FromBlockCipher>::Cipher::new_from_slice(key)
157 .map_err(|_| InvalidKeyLength)
158 .map(Self::from_cipher)
159 }
160}