Skip to content

Commit

Permalink
limb: Use Limb::from_be_bytes instead of bit shifting.
Browse files Browse the repository at this point in the history
Convert from bytes to limbs using `Limb::from_be_bytes`, in a
generally more efficient manner, and in a way that avoids doing
any arithmetic on the bytes. This addresses the TODO comment
about making the constant-timedness of the code clearer.
  • Loading branch information
briansmith committed Dec 8, 2024
1 parent c1c8a44 commit e6b5fa1
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 34 deletions.
62 changes: 28 additions & 34 deletions src/limb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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]) {
Expand Down
15 changes: 15 additions & 0 deletions src/polyfill/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ pub fn as_chunks_mut<T, const N: usize>(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<T, const N: usize>(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<T, const N: usize>(slice: &[[T; N]]) -> &[T] {
Expand Down

0 comments on commit e6b5fa1

Please sign in to comment.