Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add BoxedUint::{from_be_slice, from_le_slice} #307

Merged
merged 1 commit into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ rand_chacha = "0.3"
[features]
default = ["rand"]
alloc = ["serdect?/alloc"]
extra-sizes = []
rand = ["rand_core/std"]
serde = ["dep:serdect"]
extra-sizes = []

[package.metadata.docs.rs]
all-features = true
Expand Down
38 changes: 29 additions & 9 deletions src/boxed/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

mod add;
mod cmp;
pub(crate) mod encoding;
mod mul;
mod sub;

Expand Down Expand Up @@ -30,18 +31,42 @@ pub struct BoxedUint {
}

impl BoxedUint {
/// Get the value `0`, represented as succinctly as possible.
/// Get the value `0` represented as succinctly as possible.
pub fn zero() -> Self {
Self::default()
}

/// Get the value `0` with the given number of bits of precision.
///
/// Panics if the precision is not a multiple of [`Limb::BITS`].
pub fn zero_with_precision(bits_precision: usize) -> Self {
assert_eq!(
bits_precision % Limb::BITS,
0,
"precision is not a multiple of limb size"
);

vec![Limb::ZERO; bits_precision / Limb::BITS].into()
}

/// Get the value `1`, represented as succinctly as possible.
pub fn one() -> Self {
Self {
limbs: vec![Limb::ONE; 1].into(),
}
}

/// Get the value `1` with the given number of bits of precision.
///
/// Panics if the precision is not at least [`Limb::BITS`] or if it is not
/// a multiple thereof.
pub fn one_with_precision(bits_precision: usize) -> Self {
assert!(bits_precision >= Limb::BITS, "precision too small");
let mut ret = Self::zero_with_precision(bits_precision);
ret.limbs[0] = Limb::ONE;
ret
}

/// Is this [`BoxedUint`] equal to zero?
pub fn is_zero(&self) -> Choice {
self.limbs
Expand Down Expand Up @@ -82,22 +107,17 @@ impl BoxedUint {
/// Create a [`BoxedUint`] from an array of [`Word`]s (i.e. word-sized unsigned
/// integers).
#[inline]
pub fn from_words(words: &[Word]) -> Self {
pub fn from_words(words: impl IntoIterator<Item = Word>) -> Self {
Self {
limbs: words.iter().copied().map(Into::into).collect(),
limbs: words.into_iter().map(Into::into).collect(),
}
}

/// Create a boxed slice of [`Word`]s (i.e. word-sized unsigned integers) from
/// a [`BoxedUint`].
#[inline]
pub fn to_words(&self) -> Box<[Word]> {
self.limbs
.iter()
.copied()
.map(Into::into)
.collect::<Vec<_>>()
.into()
self.limbs.iter().copied().map(Into::into).collect()
}

/// Borrow the inner limbs as a slice of [`Word`]s.
Expand Down
221 changes: 221 additions & 0 deletions src/boxed/uint/encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
//! Const-friendly decoding operations for [`BoxedUint`].

use super::BoxedUint;
use crate::Limb;

/// Decoding errors for [`BoxedUint`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DecodeError {
/// Input is not a valid size.
InputSize,

/// Precision is not a multiple of [`Limb::BYTES`].
Precision,
}

impl BoxedUint {
/// Create a new [`BoxedUint`] from the provided big endian bytes.
///
/// The `bits_precision` argument represents the precision of the resulting integer, which is
/// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e.
/// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`].
///
/// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this
/// function will return [`DecodeError::InputSize`].
pub fn from_be_slice(bytes: &[u8], bits_precision: usize) -> Result<Self, DecodeError> {
if bits_precision % Limb::BITS != 0 {
return Err(DecodeError::Precision);
}

if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision {
return Err(DecodeError::InputSize);
}

let mut ret = Self::zero_with_precision(bits_precision);

for (chunk, limb) in bytes.chunks(Limb::BYTES).rev().zip(ret.limbs.iter_mut()) {
*limb = Limb::from_be_slice(chunk);
}

Ok(ret)
}

/// Create a new [`BoxedUint`] from the provided little endian bytes.
///
/// The `bits_precision` argument represents the precision of the resulting integer, which is
/// fixed as this type is not arbitrary-precision. It MUST be a multiple of the limb size, i.e.
/// [`Limb::BITS`], or otherwise this function will return [`DecodeError::Precision`].
///
/// If the length of `bytes` (when interpreted as bits) is larger than `bits_precision`, this
/// function will return [`DecodeError::InputSize`].
pub fn from_le_slice(bytes: &[u8], bits_precision: usize) -> Result<Self, DecodeError> {
if bits_precision % Limb::BITS != 0 {
return Err(DecodeError::Precision);
}

if bytes.len() % Limb::BYTES != 0 || bytes.len() * 8 > bits_precision {
return Err(DecodeError::InputSize);
}

let mut ret = Self::zero_with_precision(bits_precision);

for (chunk, limb) in bytes.chunks(Limb::BYTES).zip(ret.limbs.iter_mut()) {
*limb = Limb::from_le_slice(chunk);
}

Ok(ret)
}
}

#[cfg(test)]
mod tests {
use super::{BoxedUint, DecodeError};
use crate::Limb;
use hex_literal::hex;

#[test]
#[cfg(target_pointer_width = "32")]
fn from_be_slice_eq() {
let bytes = hex!("0011223344556677");
let n = BoxedUint::from_be_slice(&bytes, 64).unwrap();
assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn from_be_slice_eq() {
let bytes = hex!("00112233445566778899aabbccddeeff");
let n = BoxedUint::from_be_slice(&bytes, 128).unwrap();
assert_eq!(
n.as_limbs(),
&[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn from_be_slice_short() {
let bytes = hex!("0011223344556677");
let n = BoxedUint::from_be_slice(&bytes, 128).unwrap();
assert_eq!(
n.as_limbs(),
&[Limb(0x44556677), Limb(0x00112233), Limb::ZERO, Limb::ZERO]
);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn from_be_slice_short() {
let bytes = hex!("00112233445566778899aabbccddeeff");
let n = BoxedUint::from_be_slice(&bytes, 256).unwrap();
assert_eq!(
n.as_limbs(),
&[
Limb(0x8899aabbccddeeff),
Limb(0x0011223344556677),
Limb::ZERO,
Limb::ZERO
]
);
}

#[test]
fn from_be_slice_too_long() {
let bytes = hex!("00112233445566778899aabbccddeeff");
assert_eq!(
BoxedUint::from_be_slice(&bytes, 64),
Err(DecodeError::InputSize)
);
}

#[test]
fn from_be_slice_not_word_sized() {
let bytes = hex!("00112233445566778899aabbccddee");
assert_eq!(
BoxedUint::from_be_slice(&bytes, 128),
Err(DecodeError::InputSize)
);
}

#[test]
fn from_be_slice_bad_precision() {
let bytes = hex!("00112233445566778899aabbccddeeff");
assert_eq!(
BoxedUint::from_be_slice(&bytes, 127),
Err(DecodeError::Precision)
);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn from_le_slice_eq() {
let bytes = hex!("7766554433221100");
let n = BoxedUint::from_le_slice(&bytes, 64).unwrap();
assert_eq!(n.as_limbs(), &[Limb(0x44556677), Limb(0x00112233)]);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn from_le_slice_eq() {
let bytes = hex!("ffeeddccbbaa99887766554433221100");
let n = BoxedUint::from_le_slice(&bytes, 128).unwrap();
assert_eq!(
n.as_limbs(),
&[Limb(0x8899aabbccddeeff), Limb(0x0011223344556677)]
);
}

#[test]
#[cfg(target_pointer_width = "32")]
fn from_le_slice_short() {
let bytes = hex!("7766554433221100");
let n = BoxedUint::from_le_slice(&bytes, 128).unwrap();
assert_eq!(
n.as_limbs(),
&[Limb(0x44556677), Limb(0x00112233), Limb::ZERO, Limb::ZERO]
);
}

#[test]
#[cfg(target_pointer_width = "64")]
fn from_le_slice_short() {
let bytes = hex!("ffeeddccbbaa99887766554433221100");
let n = BoxedUint::from_le_slice(&bytes, 256).unwrap();
assert_eq!(
n.as_limbs(),
&[
Limb(0x8899aabbccddeeff),
Limb(0x0011223344556677),
Limb::ZERO,
Limb::ZERO
]
);
}

#[test]
fn from_le_slice_too_long() {
let bytes = hex!("ffeeddccbbaa99887766554433221100");
assert_eq!(
BoxedUint::from_be_slice(&bytes, 64),
Err(DecodeError::InputSize)
);
}

#[test]
fn from_le_slice_not_word_sized() {
let bytes = hex!("ffeeddccbbaa998877665544332211");
assert_eq!(
BoxedUint::from_be_slice(&bytes, 128),
Err(DecodeError::InputSize)
);
}

#[test]
fn from_le_slice_bad_precision() {
let bytes = hex!("ffeeddccbbaa99887766554433221100");
assert_eq!(
BoxedUint::from_le_slice(&bytes, 127),
Err(DecodeError::Precision)
);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ pub use crate::{
pub use subtle;

#[cfg(feature = "alloc")]
pub use crate::boxed::uint::BoxedUint;
pub use crate::boxed::uint::{encoding::DecodeError, BoxedUint};

#[cfg(feature = "generic-array")]
pub use {
Expand Down
17 changes: 17 additions & 0 deletions src/limb/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ impl Encoding for Limb {
}
}

#[cfg(feature = "alloc")]
impl Limb {
/// Decode limb from a big endian byte slice.
///
/// Panics if the slice is not the same size as [`Limb::Repr`].
pub(crate) fn from_be_slice(bytes: &[u8]) -> Self {
Self::from_be_bytes(bytes.try_into().expect("slice not limb-sized"))
}

/// Decode limb from a little endian byte slice.
///
/// Panics if the slice is not the same size as [`Limb::Repr`].
pub(crate) fn from_le_slice(bytes: &[u8]) -> Self {
Self::from_le_bytes(bytes.try_into().expect("slice not limb-sized"))
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion src/uint/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Const-friendly decoding operations for [`Uint`]
//! Const-friendly decoding/encoding operations for [`Uint`].

#[cfg(all(feature = "der", feature = "generic-array"))]
mod der;
Expand Down