Skip to content

Commit b967bdf

Browse files
committed
digest internals: Provide an infallible I-U-F API internally.
Return an error instead of panic when too much input is processed by a `BlockContext`, or when the precondition of `BlockContext::finish` is violated. Jump through some hoops to get the compiler to prefer the non-error path. This is a step towards providing an non-panicking variant of the API.
1 parent 39a5e7b commit b967bdf

File tree

2 files changed

+63
-16
lines changed

2 files changed

+63
-16
lines changed

src/digest.rs

+55-14
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use self::{
2424
};
2525
use crate::{
2626
bits::{BitLength, FromByteLen as _},
27-
cpu, debug,
27+
cpu, debug, error,
2828
polyfill::{self, slice, sliceutil},
2929
};
3030
use core::num::Wrapping;
@@ -74,12 +74,19 @@ impl BlockContext {
7474
// `num_pending < self.algorithm.block_len`.
7575
//
7676
// `block` may be arbitrarily overwritten.
77-
pub(crate) fn finish(
77+
pub(crate) fn try_finish(
7878
mut self,
7979
block: &mut [u8; MAX_BLOCK_LEN],
8080
num_pending: usize,
8181
cpu_features: cpu::Features,
82-
) -> Digest {
82+
) -> Result<Digest, FinishError> {
83+
let completed_bytes = self
84+
.completed_bytes
85+
.checked_add(polyfill::u64_from_usize(num_pending))
86+
.ok_or_else(|| FinishError::too_much_input(self.completed_bytes))?;
87+
let copmleted_bits = BitLength::from_byte_len(completed_bytes)
88+
.map_err(|_: error::Unspecified| FinishError::too_much_input(self.completed_bytes))?;
89+
8390
let block_len = self.algorithm.block_len();
8491
let block = &mut block[..block_len];
8592

@@ -89,7 +96,11 @@ impl BlockContext {
8996
padding
9097
}
9198
// Precondition violated.
92-
Some([]) | None => unreachable!(),
99+
unreachable => {
100+
return Err(FinishError::pending_not_a_partial_block(
101+
unreachable.as_deref(),
102+
));
103+
}
93104
};
94105

95106
let padding = match padding
@@ -107,23 +118,17 @@ impl BlockContext {
107118
}
108119
};
109120

110-
let completed_bytes = self
111-
.completed_bytes
112-
.checked_add(polyfill::u64_from_usize(num_pending))
113-
.unwrap();
114-
let copmleted_bits = BitLength::from_byte_len(completed_bytes).unwrap();
115-
116121
let (to_zero, len) = padding.split_at_mut(padding.len() - 8);
117122
to_zero.fill(0);
118123
len.copy_from_slice(&copmleted_bits.to_be_bytes());
119124

120125
let (completed_bytes, leftover) = self.block_data_order(block, cpu_features);
121126
debug_assert_eq!((completed_bytes, leftover.len()), (block_len, 0));
122127

123-
Digest {
128+
Ok(Digest {
124129
algorithm: self.algorithm,
125130
value: (self.algorithm.format_output)(self.state),
126-
}
131+
})
127132
}
128133

129134
#[must_use]
@@ -136,6 +141,37 @@ impl BlockContext {
136141
}
137142
}
138143

144+
pub(crate) enum FinishError {
145+
#[allow(dead_code)]
146+
TooMuchInput(u64),
147+
#[allow(dead_code)]
148+
PendingNotAPartialBlock(usize),
149+
}
150+
151+
impl FinishError {
152+
#[cold]
153+
#[inline(never)]
154+
fn too_much_input(completed_bytes: u64) -> Self {
155+
Self::TooMuchInput(completed_bytes)
156+
}
157+
158+
// unreachable
159+
#[cold]
160+
#[inline(never)]
161+
fn pending_not_a_partial_block(padding: Option<&[u8]>) -> Self {
162+
match padding {
163+
None => Self::PendingNotAPartialBlock(0),
164+
Some(padding) => Self::PendingNotAPartialBlock(padding.len()),
165+
}
166+
}
167+
}
168+
169+
impl From<FinishError> for error::Unspecified {
170+
fn from(_: FinishError) -> Self {
171+
Self
172+
}
173+
}
174+
139175
/// A context for multi-step (Init-Update-Finish) digest calculations.
140176
///
141177
/// # Examples
@@ -227,10 +263,15 @@ impl Context {
227263
///
228264
/// `finish` consumes the context so it cannot be (mis-)used after `finish`
229265
/// has been called.
230-
pub fn finish(mut self) -> Digest {
266+
pub fn finish(self) -> Digest {
267+
self.try_finish().unwrap()
268+
}
269+
270+
fn try_finish(mut self) -> Result<Digest, error::Unspecified> {
231271
let cpu_features = cpu::features();
232272
self.block
233-
.finish(&mut self.pending, self.num_pending, cpu_features)
273+
.try_finish(&mut self.pending, self.num_pending, cpu_features)
274+
.map_err(error::Unspecified::from)
234275
}
235276

236277
/// The algorithm that this context is using.

src/hmac.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -312,13 +312,19 @@ impl Context {
312312
/// the return value of `sign` to a tag. Use `verify` for verification
313313
/// instead.
314314
pub fn sign(self) -> Tag {
315-
let cpu_features = cpu::features();
315+
self.try_sign().unwrap()
316+
}
316317

318+
fn try_sign(self) -> Result<Tag, error::Unspecified> {
319+
let cpu_features = cpu::features();
317320
let algorithm = self.inner.algorithm();
318321
let buffer = &mut [0u8; digest::MAX_BLOCK_LEN];
319322
let num_pending = algorithm.output_len();
320323
buffer[..num_pending].copy_from_slice(self.inner.finish().as_ref());
321-
Tag(self.outer.finish(buffer, num_pending, cpu_features))
324+
self.outer
325+
.try_finish(buffer, num_pending, cpu_features)
326+
.map(Tag)
327+
.map_err(error::Unspecified::from)
322328
}
323329
}
324330

0 commit comments

Comments
 (0)