1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8#![forbid(unsafe_code)]
9#![warn(missing_docs, rust_2018_idioms, missing_debug_implementations)]
10
11pub use crypto_common::{
12 self, Block, Key, KeyInit, ParBlocks, Reset, array,
13 typenum::{self, consts},
14};
15
16use core::slice;
17use crypto_common::{BlockSizeUser, BlockSizes, ParBlocksSizeUser, array::Array};
18use subtle::ConstantTimeEq;
19use typenum::Unsigned;
20
21pub trait UhfBackend: ParBlocksSizeUser {
23 fn proc_block(&mut self, block: &Block<Self>);
25
26 #[inline(always)]
28 fn proc_par_blocks(&mut self, blocks: &ParBlocks<Self>) {
29 for block in blocks {
30 self.proc_block(block);
31 }
32 }
33
34 fn blocks_needed_to_align(&self) -> usize {
38 0
39 }
40}
41
42pub trait UhfClosure: BlockSizeUser {
46 fn call<B: UhfBackend<BlockSize = Self::BlockSize>>(self, backend: &mut B);
48}
49
50pub trait UniversalHash: BlockSizeUser + Sized {
53 fn update_with_backend(&mut self, f: impl UhfClosure<BlockSize = Self::BlockSize>);
55
56 #[inline]
58 fn update(&mut self, blocks: &[Block<Self>]) {
59 struct Ctx<'a, BS: BlockSizes> {
60 blocks: &'a [Block<Self>],
61 }
62
63 impl<BS: BlockSizes> BlockSizeUser for Ctx<'_, BS> {
64 type BlockSize = BS;
65 }
66
67 impl<BS: BlockSizes> UhfClosure for Ctx<'_, BS> {
68 #[inline(always)]
69 fn call<B: UhfBackend<BlockSize = BS>>(self, backend: &mut B) {
70 let pb = B::ParBlocksSize::USIZE;
71 if pb > 1 {
72 let (par_blocks, tail) = Array::slice_as_chunks(self.blocks);
73 for par_block in par_blocks {
74 backend.proc_par_blocks(par_block);
75 }
76 for block in tail {
77 backend.proc_block(block);
78 }
79 } else {
80 for block in self.blocks {
81 backend.proc_block(block);
82 }
83 }
84 }
85 }
86
87 self.update_with_backend(Ctx { blocks });
88 }
89
90 #[inline]
97 fn update_padded(&mut self, data: &[u8]) {
98 let (blocks, tail) = Array::slice_as_chunks(data);
99
100 self.update(blocks);
101
102 if !tail.is_empty() {
103 let mut padded_block = Array::default();
104 padded_block[..tail.len()].copy_from_slice(tail);
105 self.update(slice::from_ref(&padded_block));
106 }
107 }
108
109 fn finalize(self) -> Block<Self>;
111
112 #[inline]
115 fn finalize_reset(&mut self) -> Block<Self>
116 where
117 Self: Clone + Reset,
118 {
119 let ret = self.clone().finalize();
120 self.reset();
121 ret
122 }
123
124 #[inline]
130 fn verify(self, expected: &Block<Self>) -> Result<(), Error> {
131 if self.finalize().ct_eq(expected).into() {
132 Ok(())
133 } else {
134 Err(Error)
135 }
136 }
137}
138
139#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
142pub struct Error;
143
144impl core::fmt::Display for Error {
145 #[inline]
146 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
147 f.write_str("UHF output mismatch")
148 }
149}
150
151impl core::error::Error for Error {}