Skip to main content

crypto_bigint/uint/encoding/
der.rs

1//! Support for decoding/encoding [`Uint`] as an ASN.1 DER `INTEGER`.
2
3use crate::{ArrayEncoding, Limb, Uint, UintRef, hybrid_array::Array};
4use ::der::{
5    DecodeValue, EncodeValue, FixedTag, Length, Tag,
6    asn1::{AnyRef, UintRef as Asn1UintRef},
7};
8
9impl<'a, const LIMBS: usize> TryFrom<AnyRef<'a>> for Uint<LIMBS>
10where
11    Uint<LIMBS>: ArrayEncoding,
12{
13    type Error = der::Error;
14
15    fn try_from(any: AnyRef<'a>) -> der::Result<Uint<LIMBS>> {
16        Asn1UintRef::try_from(any)?.try_into()
17    }
18}
19
20impl<'a, const LIMBS: usize> TryFrom<Asn1UintRef<'a>> for Uint<LIMBS>
21where
22    Uint<LIMBS>: ArrayEncoding,
23{
24    type Error = der::Error;
25
26    fn try_from(bytes: Asn1UintRef<'a>) -> der::Result<Uint<LIMBS>> {
27        let mut array = Array::default();
28        let len = usize::try_from(bytes.len())?;
29        let offset = array
30            .len()
31            .checked_sub(len)
32            .ok_or_else(|| Tag::Integer.length_error())?;
33        let out = array
34            .get_mut(offset..)
35            .ok_or_else(|| Tag::Integer.length_error())?;
36
37        out.copy_from_slice(bytes.as_bytes());
38        Ok(Uint::from_be_byte_array(array))
39    }
40}
41
42impl<'a, const LIMBS: usize> DecodeValue<'a> for Uint<LIMBS>
43where
44    Uint<LIMBS>: ArrayEncoding,
45{
46    type Error = der::Error;
47
48    fn decode_value<R: der::Reader<'a>>(reader: &mut R, header: der::Header) -> der::Result<Self> {
49        Asn1UintRef::decode_value(reader, header)?.try_into()
50    }
51}
52
53impl<const LIMBS: usize> EncodeValue for Uint<LIMBS>
54where
55    Uint<LIMBS>: ArrayEncoding,
56{
57    fn value_len(&self) -> der::Result<Length> {
58        Ok(count_der_be_bytes(&self.limbs).into())
59    }
60
61    fn encode_value(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
62        let array = self.to_be_byte_array();
63        Asn1UintRef::new(&array)?.encode_value(encoder)
64    }
65}
66
67impl<const LIMBS: usize> FixedTag for Uint<LIMBS>
68where
69    Uint<LIMBS>: ArrayEncoding,
70{
71    const TAG: Tag = Tag::Integer;
72}
73
74/// Counts bytes in DER ASN.1 `INTEGER` big-endian encoding, without leading zero bytes.
75#[inline]
76#[allow(clippy::integer_division_remainder_used, reason = "needs triage")]
77pub(crate) fn count_der_be_bytes(limbs: &[Limb]) -> u32 {
78    // Number of 0x00 bytes (also index of first non-zero byte)
79    let leading_zero_bytes = UintRef::new(limbs).leading_zeros() / 8;
80
81    // Limbs indexed in reverse
82    let limb_index = limbs
83        .len()
84        .saturating_sub(1)
85        .saturating_sub(leading_zero_bytes as usize / Limb::BYTES);
86
87    // Limb bytes encoded as big-endian
88    let first_nonzero_byte = limbs.get(limb_index).cloned().map_or(0x00, |limb| {
89        limb.to_be_bytes()[leading_zero_bytes as usize % Limb::BYTES]
90    });
91
92    // Does the given integer need a leading zero?
93    let needs_leading_zero = first_nonzero_byte >= 0x80;
94
95    // Number of bytes in all limbs
96    let max_len = Limb::BYTES
97        .checked_mul(limbs.len())
98        .and_then(|bytes| u32::try_from(bytes).ok())
99        .expect("u32 overflow"); // TODO(tarcieri): don't panic
100
101    // If all bytes are zeros:
102    if leading_zero_bytes == max_len {
103        // we're encoding 0x00, so one byte remains
104        1
105    } else {
106        max_len
107            .saturating_sub(leading_zero_bytes)
108            .wrapping_add(u32::from(needs_leading_zero))
109    }
110}
111
112#[cfg(feature = "alloc")]
113pub mod allocating {
114    use super::count_der_be_bytes;
115    use crate::{BoxedUint, bitlen};
116    use der::{DecodeValue, EncodeValue, FixedTag, Length, Tag, asn1::UintRef as Asn1UintRef};
117
118    impl EncodeValue for BoxedUint {
119        fn value_len(&self) -> der::Result<Length> {
120            Ok(count_der_be_bytes(&self.limbs).into())
121        }
122
123        fn encode_value(&self, encoder: &mut impl der::Writer) -> der::Result<()> {
124            let array = self.to_be_bytes();
125            Asn1UintRef::new(&array)?.encode_value(encoder)
126        }
127    }
128
129    impl<'a> DecodeValue<'a> for BoxedUint {
130        type Error = der::Error;
131
132        fn decode_value<R: der::Reader<'a>>(
133            reader: &mut R,
134            header: der::Header,
135        ) -> der::Result<Self> {
136            let value = Asn1UintRef::decode_value(reader, header)?;
137            let bits_precision = bitlen::from_bytes(value.as_bytes().len());
138
139            let value = BoxedUint::from_be_slice(value.as_bytes(), bits_precision)
140                .map_err(|_| Asn1UintRef::TAG.value_error())?;
141            Ok(value)
142        }
143    }
144
145    impl FixedTag for BoxedUint {
146        const TAG: Tag = Tag::Integer;
147    }
148}
149
150#[cfg(test)]
151pub mod test {
152    use crate::{ArrayEncoding, U128, Uint};
153    use der::{DecodeValue, EncodeValue, Header, Tag};
154    extern crate std;
155
156    #[cfg(feature = "alloc")]
157    use crate::BoxedUint;
158
159    #[allow(clippy::cast_possible_truncation, clippy::panic_in_result_fn)]
160    fn assert_valid_uint_value_len<const LIMBS: usize>(n: &Uint<LIMBS>) -> der::Result<()>
161    where
162        Uint<LIMBS>: ArrayEncoding,
163    {
164        let mut buf = [0u8; 128];
165        let encoded_value = {
166            let mut writer = der::SliceWriter::new(&mut buf);
167            n.encode_value(&mut writer)?;
168            writer.finish()?
169        };
170
171        let computed_len: u32 = n.value_len()?.into();
172        let encoded_len: u32 = encoded_value.len() as u32;
173        assert_eq!(
174            computed_len, encoded_len,
175            "computed_len: {computed_len}, encoded_len: {encoded_len}, n:{n:?}"
176        );
177
178        let decoded = {
179            let mut reader = der::SliceReader::new(encoded_value)?;
180            let header = Header::new(Tag::Integer, encoded_value.len().try_into()?);
181            Uint::<LIMBS>::decode_value(&mut reader, header)?
182        };
183
184        assert_eq!(n, &decoded);
185        Ok(())
186    }
187
188    #[cfg(feature = "alloc")]
189    #[allow(clippy::cast_possible_truncation, clippy::panic_in_result_fn)]
190    fn assert_valid_boxeduint_value_len(n: &BoxedUint) -> der::Result<()> {
191        let mut buf = [0u8; 128];
192        let encoded_value = {
193            let mut writer = der::SliceWriter::new(&mut buf);
194            n.encode_value(&mut writer)?;
195            writer.finish()?
196        };
197
198        let computed_len: u32 = n.value_len()?.into();
199        let encoded_len: u32 = encoded_value.len() as u32;
200        assert_eq!(
201            computed_len, encoded_len,
202            "computed_len: {computed_len}, encoded_len: {encoded_len}, n:{n:?}"
203        );
204        let decoded = {
205            let mut reader = der::SliceReader::new(encoded_value)?;
206            let header = Header::new(Tag::Integer, encoded_value.len().try_into()?);
207            BoxedUint::decode_value(&mut reader, header)?
208        };
209
210        assert_eq!(n, &decoded);
211
212        Ok(())
213    }
214
215    fn assert_valid_value_len_hex(hex_uint: &str) {
216        let n = U128::from_str_radix_vartime(hex_uint, 16).expect("error decoding");
217        assert_valid_uint_value_len(&n).expect("error from der: Uint");
218
219        #[cfg(feature = "alloc")]
220        assert_valid_boxeduint_value_len(&BoxedUint::from(n)).expect("error from der: BoxedUint");
221    }
222    #[test]
223    fn encode_uint() {
224        assert_valid_value_len_hex("00");
225        assert_valid_value_len_hex("10");
226        assert_valid_value_len_hex("3210");
227
228        assert_valid_value_len_hex("00000000000000007fdcba9876543210");
229        assert_valid_value_len_hex("0000000000000000fedcba9876543210");
230        assert_valid_value_len_hex("0000000000000010fedcba9876543210");
231        assert_valid_value_len_hex("0000000000000080fedcba9876543210");
232
233        assert_valid_value_len_hex("0000008076543210fedcba9876543210");
234        assert_valid_value_len_hex("00dcba9876543210fedcba9876543210");
235        assert_valid_value_len_hex("0fdcba9876543210fedcba9876543210");
236        assert_valid_value_len_hex("7fdcba9876543210fedcba9876543210");
237        assert_valid_value_len_hex("fedcba9876543210fedcba9876543210");
238    }
239
240    #[test]
241    fn fixed_uint_rejects_oversized_integer_without_panic() {
242        use der::Decode;
243
244        let mut der = vec![0x02, 17, 0x01];
245        der.extend_from_slice(&[0u8; 16]);
246
247        assert!(U128::from_der(&der).is_err());
248    }
249}