Skip to main content

crypto_bigint/
encoding.rs

1//! Shared encoding support.
2
3use crate::bitlen;
4use core::fmt;
5
6#[cfg(feature = "hybrid-array")]
7use {
8    crate::Integer,
9    core::ops::Add,
10    hybrid_array::{Array, ArraySize, typenum::Unsigned},
11};
12
13/// Byte order used when encoding/decoding field elements as bytestrings.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub enum ByteOrder {
16    /// Big endian.
17    BigEndian,
18
19    /// Little endian.
20    LittleEndian,
21}
22
23/// Alias for a byte array whose size is defined by [`ArrayEncoding::ByteSize`].
24#[cfg(feature = "hybrid-array")]
25pub type ByteArray<T> = Array<u8, <T as ArrayEncoding>::ByteSize>;
26
27/// Support for encoding a big integer as a `Array`.
28#[cfg(feature = "hybrid-array")]
29pub trait ArrayEncoding: Encoding {
30    /// Size of a byte array which encodes a big integer.
31    type ByteSize: ArraySize + Add + Eq + Ord + Unsigned;
32
33    /// Deserialize from a big-endian byte array.
34    fn from_be_byte_array(bytes: ByteArray<Self>) -> Self;
35
36    /// Deserialize from a little-endian byte array.
37    fn from_le_byte_array(bytes: ByteArray<Self>) -> Self;
38
39    /// Deserialize from a byte array with the specified [`ByteOrder`].
40    #[inline]
41    fn from_byte_array(bytes: ByteArray<Self>, byte_order: ByteOrder) -> Self {
42        match byte_order {
43            ByteOrder::BigEndian => Self::from_be_byte_array(bytes),
44            ByteOrder::LittleEndian => Self::from_le_byte_array(bytes),
45        }
46    }
47
48    /// Serialize to a big-endian byte array.
49    fn to_be_byte_array(&self) -> ByteArray<Self>;
50
51    /// Serialize to a little-endian byte array.
52    fn to_le_byte_array(&self) -> ByteArray<Self>;
53
54    /// Serialize to a byte array with the specified [`ByteOrder`].
55    #[inline]
56    fn to_byte_array(&self, byte_order: ByteOrder) -> ByteArray<Self> {
57        match byte_order {
58            ByteOrder::BigEndian => self.to_be_byte_array(),
59            ByteOrder::LittleEndian => self.to_le_byte_array(),
60        }
61    }
62}
63
64/// Support for decoding a `Array` as a big integer.
65#[cfg(feature = "hybrid-array")]
66pub trait ArrayDecoding {
67    /// Big integer which decodes a `Array`.
68    type Output: ArrayEncoding + Integer;
69
70    /// Deserialize from a big-endian `Array`.
71    fn into_uint_be(self) -> Self::Output;
72
73    /// Deserialize from a little-endian `Array`.
74    fn into_uint_le(self) -> Self::Output;
75}
76
77/// Encoding support.
78// TODO(tarcieri): seal this trait in the next breaking release.
79pub trait Encoding: Sized {
80    /// Byte array representation.
81    type Repr: AsRef<[u8]>
82        + AsMut<[u8]>
83        + Clone
84        + Sized
85        + for<'a> TryFrom<&'a [u8], Error: core::error::Error>;
86
87    /// Decode from big endian bytes.
88    #[must_use]
89    fn from_be_bytes(bytes: Self::Repr) -> Self;
90
91    /// Decode from little endian bytes.
92    #[must_use]
93    fn from_le_bytes(bytes: Self::Repr) -> Self;
94
95    /// Decode from bytes using the specified [`ByteOrder`].
96    #[inline]
97    #[must_use]
98    fn from_bytes(bytes: Self::Repr, byte_order: ByteOrder) -> Self {
99        match byte_order {
100            ByteOrder::BigEndian => Self::from_be_bytes(bytes),
101            ByteOrder::LittleEndian => Self::from_le_bytes(bytes),
102        }
103    }
104
105    /// Decode from the provided big endian bytes, truncating to the least significant bits in the
106    /// event the given amount of data exceeds `bits_precision`.
107    ///
108    /// Implementations may panic if `bits_precision` exceeds their underlying size.
109    #[must_use]
110    fn from_be_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
111        assert_eq!(bits_precision, bitlen::from_bytes(size_of::<Self::Repr>()));
112        let bytes = truncate_be(bytes, bits_precision);
113        Self::from_be_bytes(bytes.try_into().expect("input too short"))
114    }
115
116    /// Decode from the provided little endian bytes, truncating to the least significant bits in
117    /// the event the given amount of data exceeds `bits_precision`.
118    ///
119    /// Implementations may panic if `bits_precision` exceeds their underlying size.
120    #[must_use]
121    fn from_le_slice_truncated(bytes: &[u8], bits_precision: u32) -> Self {
122        assert_eq!(bits_precision, bitlen::from_bytes(size_of::<Self::Repr>()));
123        let bytes = truncate_le(bytes, bits_precision);
124        Self::from_le_bytes(bytes.try_into().expect("input too short"))
125    }
126
127    /// Decode from the provided bytes, interpreting them using the specified [`ByteOrder`],
128    /// truncating to the least significant bits in the event the given amount of data exceeds
129    /// `bits_precision`.
130    ///
131    /// Implementations may panic if `bits_precision` exceeds their underlying size.
132    #[inline]
133    #[must_use]
134    fn from_slice_truncated(bytes: &[u8], bits_precision: u32, byte_order: ByteOrder) -> Self {
135        match byte_order {
136            ByteOrder::BigEndian => Self::from_be_slice_truncated(bytes, bits_precision),
137            ByteOrder::LittleEndian => Self::from_le_slice_truncated(bytes, bits_precision),
138        }
139    }
140
141    /// Encode to big endian bytes.
142    #[must_use]
143    fn to_be_bytes(&self) -> Self::Repr;
144
145    /// Encode to little endian bytes.
146    #[must_use]
147    fn to_le_bytes(&self) -> Self::Repr;
148
149    /// Encode to bytes using the specified [`ByteOrder`].
150    #[inline]
151    #[must_use]
152    fn to_bytes(&self, byte_order: ByteOrder) -> Self::Repr {
153        match byte_order {
154            ByteOrder::BigEndian => self.to_be_bytes(),
155            ByteOrder::LittleEndian => self.to_le_bytes(),
156        }
157    }
158}
159
160/// A trait mapping between encoded representations of integers.
161pub trait EncodedSize {
162    /// The equivalent encoded representation.
163    type Target;
164}
165
166/// Possible errors in variable-time integer decoding methods.
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
168pub enum DecodeError {
169    /// The input value was empty.
170    Empty,
171
172    /// The input was not consistent with the format restrictions.
173    InvalidDigit,
174
175    /// Input size is too small to fit in the given precision.
176    InputSize,
177
178    /// The deserialized number is larger than the given precision.
179    Precision,
180}
181
182impl fmt::Display for DecodeError {
183    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184        match self {
185            Self::Empty => write!(f, "empty value provided"),
186            Self::InvalidDigit => {
187                write!(f, "invalid digit character")
188            }
189            Self::InputSize => write!(f, "input size is too small to fit in the given precision"),
190            Self::Precision => write!(
191                f,
192                "the deserialized number is larger than the given precision"
193            ),
194        }
195    }
196}
197
198impl core::error::Error for DecodeError {}
199
200/// Interpret `bytes` as a big endian integer and extract `bits_precision` number of least
201/// significant bits, returning a truncated input if it exceeds the requested precision.
202pub(crate) fn truncate_be(bytes: &[u8], bits_precision: u32) -> &[u8] {
203    let bytes_precision = bitlen::to_bytes(bits_precision);
204    if bytes.len() > bytes_precision {
205        &bytes[bytes.len().saturating_sub(bytes_precision)..]
206    } else {
207        bytes
208    }
209}
210
211/// Interpret `bytes` as a little endian integer and extract `bits_precision` number of least
212/// significant bits, returning a truncated input if it exceeds the requested precision.
213pub(crate) fn truncate_le(bytes: &[u8], bits_precision: u32) -> &[u8] {
214    let bytes_precision = bitlen::to_bytes(bits_precision);
215    if bytes.len() > bytes_precision {
216        &bytes[..bytes_precision]
217    } else {
218        bytes
219    }
220}