der/asn1/integer/
bigint.rs

1//! "Big" ASN.1 `INTEGER` types.
2
3use super::uint;
4use crate::{
5    asn1::Any, ByteSlice, DecodeValue, Decoder, EncodeValue, Encoder, Error, ErrorKind, FixedTag,
6    Length, Result, Tag,
7};
8
9#[cfg(feature = "bigint")]
10use crypto_bigint::{generic_array::GenericArray, ArrayEncoding, UInt};
11
12/// "Big" unsigned ASN.1 `INTEGER` type.
13///
14/// Provides direct access to the underlying big endian bytes which comprise an
15/// unsigned integer value.
16///
17/// Intended for use cases like very large integers that are used in
18/// cryptographic applications (e.g. keys, signatures).
19#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd)]
20pub struct UIntBytes<'a> {
21    /// Inner value
22    inner: ByteSlice<'a>,
23}
24
25impl<'a> UIntBytes<'a> {
26    /// Create a new [`UIntBytes`] from a byte slice.
27    pub fn new(bytes: &'a [u8]) -> Result<Self> {
28        let inner = ByteSlice::new(uint::strip_leading_zeroes(bytes))
29            .map_err(|_| ErrorKind::Length { tag: Self::TAG })?;
30
31        Ok(Self { inner })
32    }
33
34    /// Borrow the inner byte slice which contains the least significant bytes
35    /// of a big endian integer value with all leading zeros stripped.
36    pub fn as_bytes(&self) -> &'a [u8] {
37        self.inner.as_bytes()
38    }
39
40    /// Get the length of this [`UIntBytes`] in bytes.
41    pub fn len(&self) -> Length {
42        self.inner.len()
43    }
44
45    /// Is the inner byte slice empty?
46    pub fn is_empty(&self) -> bool {
47        self.inner.is_empty()
48    }
49}
50
51impl<'a> DecodeValue<'a> for UIntBytes<'a> {
52    fn decode_value(decoder: &mut Decoder<'a>, length: Length) -> Result<Self> {
53        let bytes = ByteSlice::decode_value(decoder, length)?.as_bytes();
54        let result = Self::new(uint::decode_to_slice(bytes)?)?;
55
56        // Ensure we compute the same encoded length as the original any value.
57        if result.value_len()? != length {
58            return Err(Self::TAG.non_canonical_error());
59        }
60
61        Ok(result)
62    }
63}
64
65impl<'a> EncodeValue for UIntBytes<'a> {
66    fn value_len(&self) -> Result<Length> {
67        uint::encoded_len(self.inner.as_bytes())
68    }
69
70    fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
71        // Add leading `0x00` byte if required
72        if self.value_len()? > self.len() {
73            encoder.byte(0)?;
74        }
75
76        encoder.bytes(self.as_bytes())
77    }
78}
79
80impl<'a> From<&UIntBytes<'a>> for UIntBytes<'a> {
81    fn from(value: &UIntBytes<'a>) -> UIntBytes<'a> {
82        *value
83    }
84}
85
86impl<'a> TryFrom<Any<'a>> for UIntBytes<'a> {
87    type Error = Error;
88
89    fn try_from(any: Any<'a>) -> Result<UIntBytes<'a>> {
90        any.decode_into()
91    }
92}
93
94impl<'a> FixedTag for UIntBytes<'a> {
95    const TAG: Tag = Tag::Integer;
96}
97
98#[cfg(feature = "bigint")]
99#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
100impl<'a, const LIMBS: usize> TryFrom<Any<'a>> for UInt<LIMBS>
101where
102    UInt<LIMBS>: ArrayEncoding,
103{
104    type Error = Error;
105
106    fn try_from(any: Any<'a>) -> Result<UInt<LIMBS>> {
107        UIntBytes::try_from(any)?.try_into()
108    }
109}
110
111#[cfg(feature = "bigint")]
112#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
113impl<'a, const LIMBS: usize> TryFrom<UIntBytes<'a>> for UInt<LIMBS>
114where
115    UInt<LIMBS>: ArrayEncoding,
116{
117    type Error = Error;
118
119    fn try_from(bytes: UIntBytes<'a>) -> Result<UInt<LIMBS>> {
120        let mut array = GenericArray::default();
121        let offset = array.len().saturating_sub(bytes.len().try_into()?);
122        array[offset..].copy_from_slice(bytes.as_bytes());
123        Ok(UInt::from_be_byte_array(array))
124    }
125}
126
127#[cfg(feature = "bigint")]
128#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
129impl<const LIMBS: usize> EncodeValue for UInt<LIMBS>
130where
131    UInt<LIMBS>: ArrayEncoding,
132{
133    fn value_len(&self) -> Result<Length> {
134        // TODO(tarcieri): more efficient length calculation
135        let array = self.to_be_byte_array();
136        UIntBytes::new(&array)?.value_len()
137    }
138
139    fn encode_value(&self, encoder: &mut Encoder<'_>) -> Result<()> {
140        let array = self.to_be_byte_array();
141        UIntBytes::new(&array)?.encode_value(encoder)
142    }
143}
144
145#[cfg(feature = "bigint")]
146#[cfg_attr(docsrs, doc(cfg(feature = "bigint")))]
147impl<const LIMBS: usize> FixedTag for UInt<LIMBS>
148where
149    UInt<LIMBS>: ArrayEncoding,
150{
151    const TAG: Tag = Tag::Integer;
152}
153
154#[cfg(test)]
155mod tests {
156    use super::UIntBytes;
157    use crate::{
158        asn1::{integer::tests::*, Any},
159        Decodable, Encodable, Encoder, ErrorKind, Tag,
160    };
161
162    #[test]
163    fn decode_uint_bytes() {
164        assert_eq!(&[0], UIntBytes::from_der(I0_BYTES).unwrap().as_bytes());
165        assert_eq!(&[127], UIntBytes::from_der(I127_BYTES).unwrap().as_bytes());
166        assert_eq!(&[128], UIntBytes::from_der(I128_BYTES).unwrap().as_bytes());
167        assert_eq!(&[255], UIntBytes::from_der(I255_BYTES).unwrap().as_bytes());
168
169        assert_eq!(
170            &[0x01, 0x00],
171            UIntBytes::from_der(I256_BYTES).unwrap().as_bytes()
172        );
173
174        assert_eq!(
175            &[0x7F, 0xFF],
176            UIntBytes::from_der(I32767_BYTES).unwrap().as_bytes()
177        );
178    }
179
180    #[test]
181    fn encode_uint_bytes() {
182        for &example in &[
183            I0_BYTES,
184            I127_BYTES,
185            I128_BYTES,
186            I255_BYTES,
187            I256_BYTES,
188            I32767_BYTES,
189        ] {
190            let uint = UIntBytes::from_der(example).unwrap();
191
192            let mut buf = [0u8; 128];
193            let mut encoder = Encoder::new(&mut buf);
194            uint.encode(&mut encoder).unwrap();
195
196            let result = encoder.finish().unwrap();
197            assert_eq!(example, result);
198        }
199    }
200
201    #[test]
202    fn reject_oversize_without_extra_zero() {
203        let err = UIntBytes::try_from(Any::new(Tag::Integer, &[0x81]).unwrap())
204            .err()
205            .unwrap();
206
207        assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer });
208    }
209}