diff --git a/src/digest.rs b/src/digest.rs index 1435da425f..e667953133 100644 --- a/src/digest.rs +++ b/src/digest.rs @@ -170,12 +170,6 @@ impl FinishError { } } -impl From for error::Unspecified { - fn from(_: FinishError) -> Self { - Self - } -} - /// A context for multi-step (Init-Update-Finish) digest calculations. /// /// # Examples @@ -270,13 +264,23 @@ impl Context { pub fn finish(self) -> Digest { let cpu = cpu::features(); self.try_finish(cpu) - .map_err(error::Unspecified::from) + .map_err(error::erase::>) .unwrap() } - pub(crate) fn try_finish(mut self, cpu_features: cpu::Features) -> Result { + pub(crate) fn try_finish( + mut self, + cpu_features: cpu::Features, + ) -> Result> { self.block .try_finish(&mut self.pending, self.num_pending, cpu_features) + .map_err(|err| match err { + FinishError::InputTooLong(i) => i, + FinishError::PendingNotAPartialBlock(_) => { + // Due to invariant. + unreachable!() + } + }) } /// The algorithm that this context is using. @@ -304,7 +308,7 @@ impl Context { pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest { let cpu = cpu::features(); Digest::compute_from(algorithm, data, cpu) - .map_err(error::Unspecified::from) + .map_err(error::erase::>) .unwrap() } @@ -322,7 +326,7 @@ impl Digest { algorithm: &'static Algorithm, data: &[u8], cpu: cpu::Features, - ) -> Result { + ) -> Result> { let mut ctx = Context::new(algorithm); ctx.update(data); ctx.try_finish(cpu) diff --git a/src/error/input_too_long.rs b/src/error/input_too_long.rs index 1a3429a382..1917712afd 100644 --- a/src/error/input_too_long.rs +++ b/src/error/input_too_long.rs @@ -37,3 +37,14 @@ impl InputTooLongError { } } } + +impl InputTooLongError { + #[cold] + #[inline(never)] + pub(crate) fn into_usize_saturating(self) -> InputTooLongError { + let imprecise_input_length = self.imprecise_input_length.try_into().unwrap_or(usize::MAX); + InputTooLongError { + imprecise_input_length, + } + } +} diff --git a/src/hmac.rs b/src/hmac.rs index c7d0d62c4c..bb03d11470 100644 --- a/src/hmac.rs +++ b/src/hmac.rs @@ -111,8 +111,9 @@ use crate::{ constant_time, cpu, - digest::{self, Digest}, - error, hkdf, rand, + digest::{self, Digest, FinishError}, + error::{self, InputTooLongError}, + hkdf, rand, }; /// An HMAC algorithm. @@ -193,7 +194,7 @@ impl Key { let mut key_bytes = [0; digest::MAX_OUTPUT_LEN]; let key_bytes = &mut key_bytes[..algorithm.0.output_len()]; fill(key_bytes)?; - Self::try_new(algorithm, key_bytes, cpu).map_err(error::Unspecified::from) + Self::try_new(algorithm, key_bytes, cpu).map_err(error::erase::>) } /// Construct an HMAC signing key using the given digest algorithm and key @@ -217,7 +218,7 @@ impl Key { /// removed in a future version of *ring*. pub fn new(algorithm: Algorithm, key_value: &[u8]) -> Self { Self::try_new(algorithm, key_value, cpu::features()) - .map_err(error::Unspecified::from) + .map_err(error::erase::>) .unwrap() } @@ -225,7 +226,7 @@ impl Key { algorithm: Algorithm, key_value: &[u8], cpu_features: cpu::Features, - ) -> Result { + ) -> Result { let digest_alg = algorithm.0; let mut key = Self { inner: digest::BlockContext::new(digest_alg), @@ -238,7 +239,8 @@ impl Key { let key_value = if key_value.len() <= block_len { key_value } else { - key_hash = Digest::compute_from(digest_alg, key_value, cpu_features)?; + key_hash = Digest::compute_from(digest_alg, key_value, cpu_features) + .map_err(InputTooLongError::into_usize_saturating)?; key_hash.as_ref() }; @@ -274,14 +276,16 @@ impl Key { Algorithm(self.inner.algorithm) } - fn sign(&self, data: &[u8], cpu: cpu::Features) -> Result { + fn sign(&self, data: &[u8], cpu: cpu::Features) -> Result> { let mut ctx = Context::with_key(self); ctx.update(data); ctx.try_sign(cpu) } fn verify(&self, data: &[u8], tag: &[u8], cpu: cpu::Features) -> Result<(), VerifyError> { - let computed = self.sign(data, cpu).map_err(VerifyError::DigestError)?; + let computed = self + .sign(data, cpu) + .map_err(VerifyError::InputTooLongError)?; constant_time::verify_slices_are_equal(computed.as_ref(), tag) .map_err(|_: error::Unspecified| VerifyError::Mismatch) } @@ -341,11 +345,15 @@ impl Context { /// instead. pub fn sign(self) -> Tag { self.try_sign(cpu::features()) - .map_err(error::Unspecified::from) + .map_err(error::erase::>) .unwrap() } - fn try_sign(self, cpu_features: cpu::Features) -> Result { + fn try_sign(self, cpu_features: cpu::Features) -> Result> { + // Consequently, `num_pending` is valid. + debug_assert_eq!(self.inner.algorithm(), self.outer.algorithm); + debug_assert!(self.inner.algorithm().output_len() < self.outer.algorithm.block_len()); + let inner = self.inner.try_finish(cpu_features)?; let inner = inner.as_ref(); let num_pending = inner.len(); @@ -353,9 +361,24 @@ impl Context { const _BUFFER_IS_LARGE_ENOUGH_TO_HOLD_INNER: () = assert!(digest::MAX_OUTPUT_LEN < digest::MAX_BLOCK_LEN); buffer[..num_pending].copy_from_slice(inner); + self.outer .try_finish(buffer, num_pending, cpu_features) .map(Tag) + .map_err(|err| match err { + FinishError::InputTooLong(i) => { + // Unreachable, as we gave the inner context exactly the + // same input we gave the outer context, and + // `inner.try_finish` already succeeded. However, it is + // quite difficult to prove this, and we already return + // `InputTooLongError`, so just forward it along. + i + } + FinishError::PendingNotAPartialBlock(_) => { + // Follows from the assertions above. + unreachable!() + } + }) } } @@ -367,7 +390,7 @@ impl Context { /// return value of `sign` to a tag. Use `verify` for verification instead. pub fn sign(key: &Key, data: &[u8]) -> Tag { key.sign(data, cpu::features()) - .map_err(error::Unspecified::from) + .map_err(error::erase::>) .unwrap() } @@ -385,7 +408,7 @@ pub fn verify(key: &Key, data: &[u8], tag: &[u8]) -> Result<(), error::Unspecifi enum VerifyError { #[allow(dead_code)] - DigestError(digest::FinishError), + InputTooLongError(InputTooLongError), Mismatch, }