diff --git a/src/limb.rs b/src/limb.rs index 44757340f..1a3ebbb0d 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -18,7 +18,10 @@ //! Limbs ordered least-significant-limb to most-significant-limb. The bits //! limbs use the native endianness. -use crate::{c, constant_time, error, polyfill::ArrayFlatMap}; +use crate::{ + c, constant_time, error, + polyfill::{slice, ArrayFlatMap}, +}; #[cfg(any(test, feature = "alloc"))] use crate::{bits, polyfill::usize_from_u32}; @@ -154,44 +157,35 @@ pub fn parse_big_endian_and_pad_consttime( input: untrusted::Input, result: &mut [Limb], ) -> Result<(), error::Unspecified> { - if input.is_empty() { - return Err(error::Unspecified); - } + let (partial, whole) = slice::as_rchunks(input.as_slice_less_safe()); + + let mut partial_padded: [u8; LIMB_BYTES]; + let partial_padded = match (partial, whole) { + (partial @ [_, ..], _) => { + partial_padded = [0; LIMB_BYTES]; + partial_padded[(LIMB_BYTES - partial.len())..].copy_from_slice(partial); + Some(partial_padded) + } + ([], [_, ..]) => None, + ([], []) => { + // Empty input is not allowed. + return Err(error::Unspecified); + } + }; - // `bytes_in_current_limb` is the number of bytes in the current limb. - // It will be `LIMB_BYTES` for all limbs except maybe the highest-order - // limb. - let mut bytes_in_current_limb = input.len() % LIMB_BYTES; - if bytes_in_current_limb == 0 { - bytes_in_current_limb = LIMB_BYTES; - } + let input = whole.iter().rev().chain(partial_padded.iter()); + let mut result = result.iter_mut(); - let num_encoded_limbs = (input.len() / LIMB_BYTES) - + (if bytes_in_current_limb == LIMB_BYTES { - 0 - } else { - 1 - }); - if num_encoded_limbs > result.len() { - return Err(error::Unspecified); + for input in input { + // The result isn't allowed to be shorter than the input. + let r = result.next().ok_or_else(|| error::Unspecified)?; + *r = Limb::from_be_bytes(*input); } - result.fill(0); + // Pad the result. + result.for_each(|r| *r = 0); - // XXX: Questionable as far as constant-timedness is concerned. - // TODO: Improve this. - input.read_all(error::Unspecified, |input| { - for i in 0..num_encoded_limbs { - let mut limb: Limb = 0; - for _ in 0..bytes_in_current_limb { - let b: Limb = input.read_byte()?.into(); - limb = (limb << 8) | b; - } - result[num_encoded_limbs - i - 1] = limb; - bytes_in_current_limb = LIMB_BYTES; - } - Ok(()) - }) + Ok(()) } pub fn big_endian_from_limbs(limbs: &[Limb], out: &mut [u8]) { diff --git a/src/polyfill/slice.rs b/src/polyfill/slice.rs index 0e9d2e294..e9101cf5e 100644 --- a/src/polyfill/slice.rs +++ b/src/polyfill/slice.rs @@ -55,6 +55,21 @@ pub fn as_chunks_mut(slice: &mut [T]) -> (&mut [[T; N]], &mut (chunked, remainder) } +// TODO(MSRV feature(slice_as_chunks)): Use `slice::as_rchunks` instead. +// This is adapted from the above implementation of `as_chunks`. +#[inline(always)] +pub fn as_rchunks(slice: &[T]) -> (&[T], &[[T; N]]) { + assert!(N != 0, "chunk size must be non-zero"); + let len = slice.len() / N; + let (remainder, multiple_of_n) = slice.split_at(slice.len() - (len * N)); + // SAFETY: We already panicked for zero, and ensured by construction + // that the length of the subslice is a multiple of N. + // SAFETY: We cast a slice of `new_len * N` elements into + // a slice of `new_len` many `N` elements chunks. + let chunked = unsafe { core::slice::from_raw_parts(multiple_of_n.as_ptr().cast(), len) }; + (remainder, chunked) +} + // TODO(MSRV feature(slice_flatten)): Use `slice::flatten` instead. // This is derived from the libcore implementation, using only stable APIs. pub fn flatten(slice: &[[T; N]]) -> &[T] {