Skip to content

Commit 0ac9bf4

Browse files
authored
Add from_*slice_truncated methods to Encoding trait (#1301)
The primary goal here is to make it possible to construct both `Uint` and `BoxedUint` from slice-based inputs in a generic context, particularly for implementing the `bits2int` function in the `rfc6979` crate, but also useful for e.g. `elliptic-curve` when we have field elements backed by big integers that are larger than the field modulus, as is the case for NIST P-521. It was possible to provide a basic impl for these functions in order to make the change non-breaking (since `Encoding` isn't sealed): it ensures the provided `bits_precision` matches the `Repr` size, and still implements big/little endian truncation, but doesn't handle short inputs and just thunks through `from_be_bytes`/`from_le_bytes`. This is particularly helpful as currently no impl for `Int` is provided, so it's using the default.
1 parent da7ca41 commit 0ac9bf4

4 files changed

Lines changed: 103 additions & 21 deletions

File tree

src/encoding.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Shared encoding support.
22
3+
use crate::bitlen;
34
use core::fmt;
45

56
#[cfg(feature = "hybrid-array")]
@@ -56,6 +57,7 @@ pub trait ArrayDecoding {
5657
}
5758

5859
/// Encoding support.
60+
// TODO(tarcieri): seal this trait in the next breaking release.
5961
pub trait Encoding: Sized {
6062
/// Byte array representation.
6163
type Repr: AsRef<[u8]>
@@ -81,6 +83,41 @@ pub trait Encoding: Sized {
8183
}
8284
}
8385

86+
/// Decode from the provided big endian bytes, truncating to the least significant bits in the
87+
/// event the given amount of data exceeds `bits_precision`.
88+
///
89+
/// Implementations may panic if `bits_precision` exceeds their underlying size.
90+
#[must_use]
91+
fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
92+
assert_eq!(bits_precision, bitlen::from_bytes(size_of::<Self::Repr>()));
93+
let bytes = truncate_be(bytes, bits_precision);
94+
Self::from_be_bytes(bytes.try_into().expect("input too short"))
95+
}
96+
97+
/// Decode from the provided little endian bytes, truncating to the least significant bits in
98+
/// the event the given amount of data exceeds `bits_precision`.
99+
///
100+
/// Implementations may panic if `bits_precision` exceeds their underlying size.
101+
#[must_use]
102+
fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
103+
assert_eq!(bits_precision, bitlen::from_bytes(size_of::<Self::Repr>()));
104+
let bytes = truncate_le(bytes, bits_precision);
105+
Self::from_le_bytes(bytes.try_into().expect("input too short"))
106+
}
107+
108+
/// Decode from the provided bytes, interpreting them using the specified [`ByteOrder`],
109+
/// truncating to the least significant bits in the event the given amount of data exceeds
110+
/// `bits_precision`.
111+
///
112+
/// Implementations may panic if `bits_precision` exceeds their underlying size.
113+
#[must_use]
114+
fn from_slice_truncated(bytes: &[u8], bits_precision: u32, byte_order: ByteOrder) -> Self {
115+
match byte_order {
116+
ByteOrder::BigEndian => Self::from_be_slice_truncated(bytes, bits_precision),
117+
ByteOrder::LittleEndian => Self::from_le_slice_truncated(bytes, bits_precision),
118+
}
119+
}
120+
84121
/// Encode to big endian bytes.
85122
#[must_use]
86123
fn to_be_bytes(&self) -> Self::Repr;
@@ -138,3 +175,25 @@ impl fmt::Display for DecodeError {
138175
}
139176

140177
impl core::error::Error for DecodeError {}
178+
179+
/// Interpret `bytes` as a big endian integer and extract `bits_precision` number of least
180+
/// significant bits, returning a truncated input if it exceeds the requested precision.
181+
pub(crate) fn truncate_be(bytes: &[u8], bits_precision: u32) -> &[u8] {
182+
let bytes_precision = bitlen::to_bytes(bits_precision);
183+
if bytes.len() > bytes_precision {
184+
&bytes[bytes.len().saturating_sub(bytes_precision)..]
185+
} else {
186+
bytes
187+
}
188+
}
189+
190+
/// Interpret `bytes` as a little endian integer and extract `bits_precision` number of least
191+
/// significant bits, returning a truncated input if it exceeds the requested precision.
192+
pub(crate) fn truncate_le(bytes: &[u8], bits_precision: u32) -> &[u8] {
193+
let bytes_precision = bitlen::to_bytes(bits_precision);
194+
if bytes.len() > bytes_precision {
195+
&bytes[..bytes_precision]
196+
} else {
197+
bytes
198+
}
199+
}

src/limb/encoding.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ impl Encoding for Limb {
138138
Self::from_le_bytes(bytes)
139139
}
140140

141+
#[inline]
142+
fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
143+
Self::from_be_slice_truncated(bytes, bits_precision)
144+
}
145+
146+
#[inline]
147+
fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
148+
Self::from_le_slice_truncated(bytes, bits_precision)
149+
}
150+
141151
#[inline]
142152
fn to_be_bytes(&self) -> Self::Repr {
143153
self.to_be_bytes()

src/uint/boxed/encoding.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -262,20 +262,28 @@ impl BoxedUint {
262262
impl Encoding for BoxedUint {
263263
type Repr = Box<[u8]>;
264264

265-
fn to_be_bytes(&self) -> Self::Repr {
266-
BoxedUint::to_be_bytes(self)
265+
fn from_be_bytes(bytes: Self::Repr) -> Self {
266+
Self::from_be_slice_vartime(&bytes)
267267
}
268268

269-
fn to_le_bytes(&self) -> Self::Repr {
270-
BoxedUint::to_le_bytes(self)
269+
fn from_le_bytes(bytes: Self::Repr) -> Self {
270+
Self::from_le_slice_vartime(&bytes)
271271
}
272272

273-
fn from_be_bytes(bytes: Self::Repr) -> Self {
274-
BoxedUint::from_be_slice_vartime(&bytes)
273+
fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
274+
Self::from_be_slice_truncated(bytes, bits_precision)
275275
}
276276

277-
fn from_le_bytes(bytes: Self::Repr) -> Self {
278-
BoxedUint::from_le_slice_vartime(&bytes)
277+
fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
278+
Self::from_le_slice_truncated(bytes, bits_precision)
279+
}
280+
281+
fn to_be_bytes(&self) -> Self::Repr {
282+
Self::to_be_bytes(self)
283+
}
284+
285+
fn to_le_bytes(&self) -> Self::Repr {
286+
Self::to_le_bytes(self)
279287
}
280288
}
281289

src/uint/encoding.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ mod der;
66
mod rlp;
77

88
use super::Uint;
9-
use crate::{ByteOrder, DecodeError, EncodedSize, Encoding, Limb, Word, bitlen};
9+
use crate::{
10+
ByteOrder, DecodeError, EncodedSize, Encoding, Limb, Word, bitlen,
11+
encoding::{truncate_be, truncate_le},
12+
};
1013
use core::{fmt, ops::Deref};
1114

1215
#[cfg(feature = "alloc")]
@@ -324,19 +327,15 @@ impl<const LIMBS: usize> Uint<LIMBS> {
324327
///
325328
/// Fills the supplied `limbs` with the decoded `bytes` after truncating to `bits_precision`.
326329
pub(crate) fn fill_limbs_from_be_slice_truncated(
327-
mut bytes: &[u8],
330+
bytes: &[u8],
328331
limbs: &mut [Limb],
329332
bits_precision: u32,
330333
) -> Result<(), DecodeError> {
331334
if bitlen::from_limbs(limbs.len()) < bits_precision {
332335
return Err(DecodeError::Precision);
333336
}
334337

335-
let bytes_precision = bitlen::to_bytes(bits_precision);
336-
if bytes.len() > bytes_precision {
337-
bytes = &bytes[bytes.len().saturating_sub(bytes_precision)..];
338-
}
339-
338+
let bytes = truncate_be(bytes, bits_precision);
340339
for (chunk, limb) in bytes.rchunks(Limb::BYTES).zip(limbs.iter_mut()) {
341340
*limb = Limb::from_be_slice(chunk);
342341
}
@@ -350,19 +349,15 @@ pub(crate) fn fill_limbs_from_be_slice_truncated(
350349
///
351350
/// Fills the supplied `limbs` with the decoded `bytes` after truncating to `bits_precision`.
352351
pub(crate) fn fill_limbs_from_le_slice_truncated(
353-
mut bytes: &[u8],
352+
bytes: &[u8],
354353
limbs: &mut [Limb],
355354
bits_precision: u32,
356355
) -> Result<(), DecodeError> {
357356
if bitlen::from_limbs(limbs.len()) < bits_precision {
358357
return Err(DecodeError::Precision);
359358
}
360359

361-
let bytes_precision = bitlen::to_bytes(bits_precision);
362-
if bytes.len() > bytes_precision {
363-
bytes = &bytes[..bytes_precision];
364-
}
365-
360+
let bytes = truncate_le(bytes, bits_precision);
366361
for (chunk, limb) in bytes.chunks(Limb::BYTES).zip(limbs.iter_mut()) {
367362
*limb = Limb::from_le_slice(chunk);
368363
}
@@ -577,6 +572,16 @@ impl<const LIMBS: usize> Encoding for Uint<LIMBS> {
577572
Self::from_le_slice(bytes.as_ref())
578573
}
579574

575+
#[inline]
576+
fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
577+
Self::from_be_slice_truncated(bytes, bits_precision)
578+
}
579+
580+
#[inline]
581+
fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
582+
Self::from_le_slice_truncated(bytes, bits_precision)
583+
}
584+
580585
#[inline]
581586
fn to_be_bytes(&self) -> Self::Repr {
582587
self.to_be_bytes()

0 commit comments

Comments
 (0)