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