1#![no_std]
21#![forbid(unsafe_code)]
22#![doc(
23 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
24 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
25 html_root_url = "https://docs.rs/universal-hash/0.4.1"
26)]
27#![warn(missing_docs, rust_2018_idioms)]
28
29#[cfg(feature = "std")]
30extern crate std;
31
32pub use generic_array::{self, typenum::consts};
33
34use generic_array::typenum::Unsigned;
35use generic_array::{ArrayLength, GenericArray};
36use subtle::{Choice, ConstantTimeEq};
37
38pub type Key<U> = GenericArray<u8, <U as NewUniversalHash>::KeySize>;
40
41pub type Block<U> = GenericArray<u8, <U as UniversalHash>::BlockSize>;
43
44pub trait NewUniversalHash: Sized {
46 type KeySize: ArrayLength<u8>;
48
49 fn new(key: &Key<Self>) -> Self;
51}
52
53pub trait UniversalHash: Clone {
56 type BlockSize: ArrayLength<u8>;
58
59 fn update(&mut self, block: &Block<Self>);
61
62 fn update_padded(&mut self, data: &[u8]) {
69 let mut chunks = data.chunks_exact(Self::BlockSize::to_usize());
70
71 for chunk in &mut chunks {
72 self.update(GenericArray::from_slice(chunk));
73 }
74
75 let rem = chunks.remainder();
76
77 if !rem.is_empty() {
78 let mut padded_block = GenericArray::default();
79 padded_block[..rem.len()].copy_from_slice(rem);
80 self.update(&padded_block);
81 }
82 }
83
84 fn reset(&mut self);
86
87 fn finalize(self) -> Output<Self>;
89
90 fn finalize_reset(&mut self) -> Output<Self> {
93 let res = self.clone().finalize();
94 self.reset();
95 res
96 }
97
98 fn verify(self, other: &Block<Self>) -> Result<(), Error> {
102 if self.finalize() == other.into() {
103 Ok(())
104 } else {
105 Err(Error)
106 }
107 }
108}
109
110#[derive(Clone)]
115pub struct Output<U: UniversalHash> {
116 bytes: GenericArray<u8, U::BlockSize>,
117}
118
119impl<U> Output<U>
120where
121 U: UniversalHash,
122{
123 pub fn new(bytes: Block<U>) -> Output<U> {
125 Output { bytes }
126 }
127
128 pub fn into_bytes(self) -> Block<U> {
130 self.bytes
131 }
132}
133
134impl<U> From<Block<U>> for Output<U>
135where
136 U: UniversalHash,
137{
138 fn from(bytes: Block<U>) -> Self {
139 Output { bytes }
140 }
141}
142
143impl<'a, U> From<&'a Block<U>> for Output<U>
144where
145 U: UniversalHash,
146{
147 fn from(bytes: &'a Block<U>) -> Self {
148 bytes.clone().into()
149 }
150}
151
152impl<U> ConstantTimeEq for Output<U>
153where
154 U: UniversalHash,
155{
156 fn ct_eq(&self, other: &Self) -> Choice {
157 self.bytes.ct_eq(&other.bytes)
158 }
159}
160
161impl<U> PartialEq for Output<U>
162where
163 U: UniversalHash,
164{
165 fn eq(&self, x: &Output<U>) -> bool {
166 self.ct_eq(x).unwrap_u8() == 1
167 }
168}
169
170impl<U: UniversalHash> Eq for Output<U> {}
171
172#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
175pub struct Error;
176
177impl core::fmt::Display for Error {
178 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
179 f.write_str("UHF output mismatch")
180 }
181}
182
183#[cfg(feature = "std")]
184impl std::error::Error for Error {}