der/asn1/integer/
uint.rs

1//! Unsigned integer decoders/encoders.
2
3use crate::{Length, Result, Tag, Writer};
4
5/// Decode an unsigned integer into a big endian byte slice with all leading
6/// zeroes removed.
7///
8/// Returns a byte array of the requested size containing a big endian integer.
9pub(crate) fn decode_to_slice(bytes: &[u8]) -> Result<&[u8]> {
10    // The `INTEGER` type always encodes a signed value, so for unsigned
11    // values the leading `0x00` byte may need to be removed.
12    //
13    // We also disallow a leading byte which would overflow a signed ASN.1
14    // integer (since we're decoding an unsigned integer).
15    // We expect all such cases to have a leading `0x00` byte.
16    match bytes {
17        [] => Err(Tag::Integer.non_canonical_error()),
18        [0] => Ok(bytes),
19        [0, byte, ..] if *byte < 0x80 => Err(Tag::Integer.non_canonical_error()),
20        [0, rest @ ..] => Ok(rest),
21        [byte, ..] if *byte >= 0x80 => Err(Tag::Integer.value_error()),
22        _ => Ok(bytes),
23    }
24}
25
26/// Decode an unsigned integer into a byte array of the requested size
27/// containing a big endian integer.
28pub(super) fn decode_to_array<const N: usize>(bytes: &[u8]) -> Result<[u8; N]> {
29    let input = decode_to_slice(bytes)?;
30
31    // Compute number of leading zeroes to add
32    let num_zeroes = N
33        .checked_sub(input.len())
34        .ok_or_else(|| Tag::Integer.length_error())?;
35
36    // Copy input into `N`-sized output buffer with leading zeroes
37    let mut output = [0u8; N];
38    output[num_zeroes..].copy_from_slice(input);
39    Ok(output)
40}
41
42/// Encode the given big endian bytes representing an integer as ASN.1 DER.
43pub(crate) fn encode_bytes<W>(encoder: &mut W, bytes: &[u8]) -> Result<()>
44where
45    W: Writer + ?Sized,
46{
47    let bytes = strip_leading_zeroes(bytes);
48
49    if needs_leading_zero(bytes) {
50        encoder.write_byte(0)?;
51    }
52
53    encoder.write(bytes)
54}
55
56/// Get the encoded length for the given unsigned integer serialized as bytes.
57#[inline]
58pub(crate) fn encoded_len(bytes: &[u8]) -> Result<Length> {
59    let bytes = strip_leading_zeroes(bytes);
60    Length::try_from(bytes.len())? + u8::from(needs_leading_zero(bytes))
61}
62
63/// Strip the leading zeroes from the given byte slice
64pub(crate) fn strip_leading_zeroes(mut bytes: &[u8]) -> &[u8] {
65    while let Some((byte, rest)) = bytes.split_first() {
66        if *byte == 0 && !rest.is_empty() {
67            bytes = rest;
68        } else {
69            break;
70        }
71    }
72
73    bytes
74}
75
76/// Does the given integer need a leading zero?
77fn needs_leading_zero(bytes: &[u8]) -> bool {
78    matches!(bytes.get(0), Some(byte) if *byte >= 0x80)
79}
80
81#[cfg(test)]
82mod tests {
83    use super::decode_to_array;
84    use crate::{ErrorKind, Tag};
85
86    #[test]
87    fn decode_to_array_no_leading_zero() {
88        let arr = decode_to_array::<4>(&[1, 2]).unwrap();
89        assert_eq!(arr, [0, 0, 1, 2]);
90    }
91
92    #[test]
93    fn decode_to_array_leading_zero() {
94        let arr = decode_to_array::<4>(&[0x00, 0xFF, 0xFE]).unwrap();
95        assert_eq!(arr, [0x00, 0x00, 0xFF, 0xFE]);
96    }
97
98    #[test]
99    fn decode_to_array_extra_zero() {
100        let err = decode_to_array::<4>(&[0, 1, 2]).err().unwrap();
101        assert_eq!(err.kind(), ErrorKind::Noncanonical { tag: Tag::Integer });
102    }
103
104    #[test]
105    fn decode_to_array_missing_zero() {
106        // We're decoding an unsigned integer, but this value would be signed
107        let err = decode_to_array::<4>(&[0xFF, 0xFE]).err().unwrap();
108        assert_eq!(err.kind(), ErrorKind::Value { tag: Tag::Integer });
109    }
110
111    #[test]
112    fn decode_to_array_oversized_input() {
113        let err = decode_to_array::<1>(&[1, 2, 3]).err().unwrap();
114        assert_eq!(err.kind(), ErrorKind::Length { tag: Tag::Integer });
115    }
116}