|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | use num_bigint_dig::BigUint; | 
|  | use num_traits::Num; | 
|  | use std::cmp::Ordering; | 
|  | use std::fmt; | 
|  | use std::iter; | 
|  | use thiserror::Error; | 
|  |  | 
|  | use crate::util::parse_int::ParseInt; | 
|  |  | 
|  | #[derive(Error, Debug, Clone, PartialEq, Eq)] | 
|  | pub enum ParseBigIntError { | 
|  | #[error("integer is too large")] | 
|  | Overflow, | 
|  | #[error("integer is too small")] | 
|  | Underflow, | 
|  | #[error(transparent)] | 
|  | ParseBigIntError(#[from] num_bigint_dig::ParseBigIntError), | 
|  | } | 
|  |  | 
|  | /// A fixed-size unsigned big integer. | 
|  | /// | 
|  | /// This struct wraps a `BigUint` to facilitate defining new fixed-size unsigned integer types for | 
|  | /// better type safety. | 
|  | /// | 
|  | /// An integer stored in this type is fixed-size in the sense that the minimum number of bits | 
|  | /// required to represent it, i.e. its bit length, is at most `BIT_LEN`. This size can be specified | 
|  | /// using the const parameters `BIT_LEN` and `EXACT_LEN` as follows: | 
|  | ///   - When `EXACT_LEN` is `false`, the bit length of the integer can be at most `BIT_LEN` bits, | 
|  | ///     e.g. SHA-256 digests (at most 256 bits) or RSA-3072 signatures (at most 3072 bits), | 
|  | ///   - When `EXACT_LEN` is `true`, the number of bits required to represent the integer must be | 
|  | ///     exactly `BIT_LEN` bits, e.g. RSA-3072 moduli (exactly 3072 bits). | 
|  | /// Note that while the type encapsulates the size information, the actual check is performed at | 
|  | /// runtime when an instance is created (see `check_len()`). | 
|  | /// | 
|  | /// This struct is not meant to be used directly, please see the `fixed_size_bigint` macro which | 
|  | /// also generates the required boilerplate code for new types. | 
|  | #[derive(Debug, Clone, Eq, PartialEq)] | 
|  | pub(crate) struct FixedSizeBigInt<const BIT_LEN: usize, const EXACT_LEN: bool>(BigUint); | 
|  |  | 
|  | impl<const BIT_LEN: usize, const EXACT_LEN: bool> FixedSizeBigInt<BIT_LEN, EXACT_LEN> { | 
|  | const BYTE_LEN: usize = BIT_LEN.saturating_add(u8::BITS as usize - 1) / u8::BITS as usize; | 
|  |  | 
|  | /// Checks the bit length of the `FixedSizeBigInt`. | 
|  | /// | 
|  | /// Bit length of a `FixedSizeBigInt` can be at most `BIT_LEN` if `EXACT_LEN` is `false`, must | 
|  | /// be exactly `BIT_LEN` otherwise. | 
|  | fn new_from_biguint(biguint: BigUint) -> Result<Self, ParseBigIntError> { | 
|  | match (biguint.bits().cmp(&BIT_LEN), EXACT_LEN) { | 
|  | (Ordering::Greater, _) => Err(ParseBigIntError::Overflow), | 
|  | (Ordering::Equal, _) => Ok(Self(biguint)), | 
|  | (Ordering::Less, true) => Err(ParseBigIntError::Underflow), | 
|  | (Ordering::Less, false) => Ok(Self(biguint)), | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Creates a `FixedSizeBigInt` from little-endian bytes. | 
|  | pub(crate) fn from_le_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, ParseBigIntError> { | 
|  | Self::new_from_biguint(BigUint::from_bytes_le(bytes.as_ref())) | 
|  | } | 
|  |  | 
|  | /// Creates a `FixedSizeBigInt` from big-endian bytes. | 
|  | pub(crate) fn from_be_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, ParseBigIntError> { | 
|  | Self::new_from_biguint(BigUint::from_bytes_be(bytes.as_ref())) | 
|  | } | 
|  |  | 
|  | /// Returns the bit length. | 
|  | /// | 
|  | /// Bit length of `FixedSizeBigInt` is the minimum number of bits required to represent its | 
|  | /// value. The underlying storage may be larger. | 
|  | pub(crate) fn bit_len(&self) -> usize { | 
|  | self.0.bits() | 
|  | } | 
|  |  | 
|  | /// Returns the byte representation in little-endian order. | 
|  | pub(crate) fn to_le_bytes(&self) -> Vec<u8> { | 
|  | let mut v = self.0.to_bytes_le(); | 
|  | assert!(Self::BYTE_LEN >= v.len()); | 
|  | // Append since `v` is little-endian. | 
|  | v.resize(Self::BYTE_LEN, 0); | 
|  | v | 
|  | } | 
|  |  | 
|  | /// Returns the byte representation in big-endian order. | 
|  | pub(crate) fn to_be_bytes(&self) -> Vec<u8> { | 
|  | let mut v = self.0.to_bytes_be(); | 
|  | assert!(Self::BYTE_LEN >= v.len()); | 
|  | // Prepend since `v` is big-endian. | 
|  | v.splice(0..0, iter::repeat(0).take(Self::BYTE_LEN - v.len())); | 
|  | v | 
|  | } | 
|  |  | 
|  | /// Returns the underlying `BigUint`. | 
|  | pub(crate) fn as_biguint(&self) -> &BigUint { | 
|  | &self.0 | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<const BIT_LEN: usize, const EXACT_LEN: bool> ParseInt for FixedSizeBigInt<BIT_LEN, EXACT_LEN> { | 
|  | type FromStrRadixErr = ParseBigIntError; | 
|  |  | 
|  | fn from_str_radix(src: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { | 
|  | Self::new_from_biguint( | 
|  | BigUint::from_str_radix(src, radix).map_err(ParseBigIntError::ParseBigIntError)?, | 
|  | ) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl<const BIT_LEN: usize, const EXACT_LEN: bool> fmt::Display | 
|  | for FixedSizeBigInt<BIT_LEN, EXACT_LEN> | 
|  | { | 
|  | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
|  | fmt::Display::fmt( | 
|  | &format_args!("{:#0width$x}", self.0, width = Self::BYTE_LEN * 2 + 2), | 
|  | f, | 
|  | ) | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Helper macro for the `fixed_size_bigint` macro. | 
|  | macro_rules! fixed_size_bigint_impl { | 
|  | ($struct_name:ident, $bit_len:expr, $exact_len:expr) => { | 
|  | #[derive(serde::Serialize, Debug, Clone, Eq, PartialEq)] | 
|  | #[serde(into = "String")] | 
|  | pub struct $struct_name($crate::util::bigint::FixedSizeBigInt<$bit_len, $exact_len>); | 
|  |  | 
|  | const _: () = { | 
|  | use num_bigint_dig::BigUint; | 
|  | use std::fmt; | 
|  | use std::result::Result; | 
|  |  | 
|  | use $crate::util::bigint::{FixedSizeBigInt, ParseBigIntError}; | 
|  | use $crate::util::parse_int::ParseInt; | 
|  |  | 
|  | impl $struct_name { | 
|  | pub fn from_le_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, ParseBigIntError> { | 
|  | Ok($struct_name( | 
|  | FixedSizeBigInt::<$bit_len, $exact_len>::from_le_bytes(bytes)?, | 
|  | )) | 
|  | } | 
|  |  | 
|  | pub fn from_be_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, ParseBigIntError> { | 
|  | Ok($struct_name( | 
|  | FixedSizeBigInt::<$bit_len, $exact_len>::from_be_bytes(bytes)?, | 
|  | )) | 
|  | } | 
|  |  | 
|  | pub fn bit_len(&self) -> usize { | 
|  | self.0.bit_len() | 
|  | } | 
|  |  | 
|  | pub fn to_le_bytes(&self) -> Vec<u8> { | 
|  | self.0.to_le_bytes() | 
|  | } | 
|  |  | 
|  | pub fn to_be_bytes(&self) -> Vec<u8> { | 
|  | self.0.to_be_bytes() | 
|  | } | 
|  |  | 
|  | pub fn as_biguint(&self) -> &BigUint { | 
|  | self.0.as_biguint() | 
|  | } | 
|  | } | 
|  |  | 
|  | impl ParseInt for $struct_name { | 
|  | type FromStrRadixErr = ParseBigIntError; | 
|  |  | 
|  | fn from_str_radix(src: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { | 
|  | Ok($struct_name( | 
|  | FixedSizeBigInt::<$bit_len, $exact_len>::from_str_radix(src, radix)?, | 
|  | )) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl fmt::Display for $struct_name { | 
|  | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 
|  | fmt::Display::fmt(&self.0, f) | 
|  | } | 
|  | } | 
|  |  | 
|  | impl From<$struct_name> for String { | 
|  | fn from(s: $struct_name) -> String { | 
|  | s.0.to_string() | 
|  | } | 
|  | } | 
|  | }; | 
|  | }; | 
|  | } | 
|  |  | 
|  | pub(crate) use fixed_size_bigint_impl; | 
|  |  | 
|  | /// Macro for defining a new fixed-size unsigned big integer type. | 
|  | /// | 
|  | /// Defines a new type that wraps a `FixedSizeBigInt`. This macro is intended to be used within this | 
|  | /// crate to define types which can then be exported as needed: | 
|  | /// | 
|  | /// ``` | 
|  | /// use crate::util::bigint::fixed_size_bigint; | 
|  | /// | 
|  | /// // Define a type for RSA-3072 moduli (exactly 3072 bits long): | 
|  | /// fixed_size_bigint!(Rsa3072Modulus, 3072); | 
|  | /// | 
|  | /// // Define a type for SHA-256 digests (at most 256 bits long): | 
|  | /// fixed_size_bigint!(Sha256Digest, at_most 256); | 
|  | /// ``` | 
|  | macro_rules! fixed_size_bigint { | 
|  | ($struct_name:ident, $bit_len:expr) => { | 
|  | $crate::util::bigint::fixed_size_bigint_impl!($struct_name, $bit_len, true); | 
|  | }; | 
|  | ($struct_name:ident, at_most $bit_len:expr) => { | 
|  | $crate::util::bigint::fixed_size_bigint_impl!($struct_name, $bit_len, false); | 
|  | }; | 
|  | } | 
|  |  | 
|  | pub(crate) use fixed_size_bigint; | 
|  |  | 
|  | #[cfg(test)] | 
|  | mod tests { | 
|  | use super::*; | 
|  |  | 
|  | fixed_size_bigint!(TestArray, at_most 16); | 
|  | fixed_size_bigint!(TestArrayExact, 16); | 
|  |  | 
|  | #[test] | 
|  | fn test_from_to_le_bytes() { | 
|  | fn check(slice: &[u8], data: &[u8]) { | 
|  | assert_eq!(TestArray::from_le_bytes(slice).unwrap().to_le_bytes(), data); | 
|  | } | 
|  | check(&[], &[0, 0]); | 
|  | check(&[1], &[1, 0]); | 
|  | check(&[0, 1], &[0, 1]); | 
|  | check(&[1, 0], &[1, 0]); | 
|  |  | 
|  | assert!(TestArray::from_le_bytes([1, 2, 3]).is_err()); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_from_to_le_bytes_exact_len() { | 
|  | fn check(slice: &[u8], data: &[u8]) { | 
|  | assert_eq!( | 
|  | TestArrayExact::from_le_bytes(slice).unwrap().to_le_bytes(), | 
|  | data | 
|  | ); | 
|  | } | 
|  | check(&[0, 128], &[0, 128]); | 
|  | check(&[255, 255, 0], &[255, 255]); | 
|  |  | 
|  | assert!(TestArrayExact::from_le_bytes([1]).is_err()); | 
|  | assert!(TestArrayExact::from_le_bytes([255, 127]).is_err()); | 
|  | assert!(TestArrayExact::from_le_bytes([0, 0, 1]).is_err()); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_from_to_be_bytes() { | 
|  | fn check(slice: &[u8], data: &[u8]) { | 
|  | assert_eq!(TestArray::from_be_bytes(slice).unwrap().to_be_bytes(), data); | 
|  | } | 
|  | check(&[1], &[0, 1]); | 
|  | check(&[1, 0], &[1, 0]); | 
|  | check(&[0, 1], &[0, 1]); | 
|  |  | 
|  | assert!(TestArray::from_be_bytes([1, 2, 1]).is_err()); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_from_to_be_bytes_exact_len() { | 
|  | fn check(slice: &[u8], data: &[u8]) { | 
|  | assert_eq!( | 
|  | TestArrayExact::from_be_bytes(slice).unwrap().to_be_bytes(), | 
|  | data | 
|  | ); | 
|  | } | 
|  | check(&[128, 1], &[128, 1]); | 
|  | check(&[0, 255, 255], &[255, 255]); | 
|  |  | 
|  | assert!(TestArrayExact::from_be_bytes([1]).is_err()); | 
|  | assert!(TestArrayExact::from_be_bytes([127, 1]).is_err()); | 
|  | assert!(TestArrayExact::from_be_bytes([1, 0, 0]).is_err()); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_bit_len() { | 
|  | fn check(slice: &[u8], bit_len: usize) { | 
|  | assert_eq!(TestArray::from_le_bytes(slice).unwrap().bit_len(), bit_len); | 
|  | } | 
|  | check(&[1], 1); | 
|  | check(&[1, 0], 1); | 
|  | check(&[255], 8); | 
|  | check(&[0, 1], 9); | 
|  | check(&[0, 128], 16); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_from_str() { | 
|  | assert_eq!(TestArray::from_str("0x01").unwrap().to_le_bytes(), [1, 0]); | 
|  | assert_eq!( | 
|  | TestArray::from_str("0x00201").unwrap().to_le_bytes(), | 
|  | [1, 2] | 
|  | ); | 
|  | assert!(TestArray::from_str("0x030201").is_err()); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_from_str_exact_len() { | 
|  | assert_eq!( | 
|  | TestArrayExact::from_str("0x08001").unwrap().to_le_bytes(), | 
|  | [1, 128] | 
|  | ); | 
|  |  | 
|  | assert!(TestArrayExact::from_str("0x01").is_err()); | 
|  | assert!(TestArrayExact::from_str("0x0201").is_err()); | 
|  | assert!(TestArrayExact::from_str("0x030201").is_err()); | 
|  | } | 
|  |  | 
|  | #[test] | 
|  | fn test_fmt() { | 
|  | let exact = TestArrayExact::from_str("0xabcd").unwrap(); | 
|  | assert_eq!(exact.to_string(), "0xabcd"); | 
|  |  | 
|  | let at_most = TestArray::from_str("0xab").unwrap(); | 
|  | assert_eq!(at_most.to_string(), "0x00ab"); | 
|  |  | 
|  | let at_most_full = TestArray::from_str("0xabcd").unwrap(); | 
|  | assert_eq!(at_most_full.to_string(), "0xabcd"); | 
|  | } | 
|  | } |