1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
//! X.509 serial number

use core::{fmt::Display, marker::PhantomData};

use der::{
    asn1::{self, Int},
    DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd,
    Writer,
};

use crate::certificate::{Profile, Rfc5280};

/// [RFC 5280 Section 4.1.2.2.]  Serial Number
///
///   The serial number MUST be a positive integer assigned by the CA to
///   each certificate.  It MUST be unique for each certificate issued by a
///   given CA (i.e., the issuer name and serial number identify a unique
///   certificate).  CAs MUST force the serialNumber to be a non-negative
///   integer.
///
///   Given the uniqueness requirements above, serial numbers can be
///   expected to contain long integers.  Certificate users MUST be able to
///   handle serialNumber values up to 20 octets.  Conforming CAs MUST NOT
///   use serialNumber values longer than 20 octets.
///
///   Note: Non-conforming CAs may issue certificates with serial numbers
///   that are negative or zero.  Certificate users SHOULD be prepared to
///   gracefully handle such certificates.
#[derive(Clone, Debug, Eq, PartialEq, ValueOrd, PartialOrd, Ord)]
pub struct SerialNumber<P: Profile = Rfc5280> {
    pub(crate) inner: Int,
    _profile: PhantomData<P>,
}

impl<P: Profile> SerialNumber<P> {
    /// Maximum length in bytes for a [`SerialNumber`]
    pub const MAX_LEN: Length = Length::new(20);

    /// See notes in `SerialNumber::new` and `SerialNumber::decode_value`.
    pub(crate) const MAX_DECODE_LEN: Length = Length::new(21);

    /// Create a new [`SerialNumber`] from a byte slice.
    ///
    /// The byte slice **must** represent a positive integer.
    pub fn new(bytes: &[u8]) -> Result<Self> {
        let inner = asn1::Uint::new(bytes)?;

        // The user might give us a 20 byte unsigned integer with a high MSB,
        // which we'd then encode with 21 octets to preserve the sign bit.
        // RFC 5280 is ambiguous about whether this is valid, so we limit
        // `SerialNumber` *encodings* to 20 bytes or fewer while permitting
        // `SerialNumber` *decodings* to have up to 21 bytes below.
        if inner.value_len()? > Self::MAX_LEN {
            return Err(ErrorKind::Overlength.into());
        }

        Ok(Self {
            inner: inner.into(),
            _profile: PhantomData,
        })
    }

    /// Borrow the inner byte slice which contains the least significant bytes
    /// of a big endian integer value with all leading zeros stripped.
    pub fn as_bytes(&self) -> &[u8] {
        self.inner.as_bytes()
    }
}

impl<P: Profile> EncodeValue for SerialNumber<P> {
    fn value_len(&self) -> Result<Length> {
        self.inner.value_len()
    }

    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
        self.inner.encode_value(writer)
    }
}

impl<'a, P: Profile> DecodeValue<'a> for SerialNumber<P> {
    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
        let inner = Int::decode_value(reader, header)?;
        let serial = Self {
            inner,
            _profile: PhantomData,
        };

        P::check_serial_number(&serial)?;

        Ok(serial)
    }
}

impl<P: Profile> FixedTag for SerialNumber<P> {
    const TAG: Tag = <Int as FixedTag>::TAG;
}

impl Display for SerialNumber {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let mut iter = self.as_bytes().iter().peekable();

        while let Some(byte) = iter.next() {
            match iter.peek() {
                Some(_) => write!(f, "{:02X}:", byte)?,
                None => write!(f, "{:02X}", byte)?,
            }
        }

        Ok(())
    }
}

macro_rules! impl_from {
    ($source:ty) => {
        impl From<$source> for SerialNumber {
            fn from(inner: $source) -> SerialNumber {
                let serial_number = &inner.to_be_bytes()[..];
                let serial_number = asn1::Uint::new(serial_number).unwrap();

                // This could only fail if the big endian representation was to be more than 20
                // bytes long. Because it's only implemented for up to u64 / usize (8 bytes).
                SerialNumber::new(serial_number.as_bytes()).unwrap()
            }
        }
    };
}

impl_from!(u8);
impl_from!(u16);
impl_from!(u32);
impl_from!(u64);
impl_from!(usize);

// Implement by hand because the derive would create invalid values.
// Use the constructor to create a valid value.
#[cfg(feature = "arbitrary")]
impl<'a, P: Profile> arbitrary::Arbitrary<'a> for SerialNumber<P> {
    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
        let len = u.int_in_range(0u32..=Self::MAX_LEN.into())?;

        Self::new(u.bytes(len as usize)?).map_err(|_| arbitrary::Error::IncorrectFormat)
    }

    fn size_hint(depth: usize) -> (usize, Option<usize>) {
        arbitrary::size_hint::and(u32::size_hint(depth), (0, None))
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use alloc::string::ToString;

    use super::*;

    #[test]
    fn serial_number_invariants() {
        // Creating a new serial with an oversized encoding (due to high MSB) fails.
        {
            let too_big = [0x80; 20];
            assert!(SerialNumber::<Rfc5280>::new(&too_big).is_err());
        }

        // Creating a new serial with the maximum encoding succeeds.
        {
            let just_enough = [
                0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            ];
            assert!(SerialNumber::<Rfc5280>::new(&just_enough).is_ok());
        }
    }

    #[test]
    fn serial_number_display() {
        {
            let sn = SerialNumber::new(&[0x11, 0x22, 0x33]).unwrap();

            assert_eq!(sn.to_string(), "11:22:33")
        }

        {
            let sn = SerialNumber::new(&[0xAA, 0xBB, 0xCC, 0x01, 0x10, 0x00, 0x11]).unwrap();

            // We force the user's serial to be positive if they give us a negative one.
            assert_eq!(sn.to_string(), "00:AA:BB:CC:01:10:00:11")
        }

        {
            let sn = SerialNumber::new(&[0x00, 0x00, 0x01]).unwrap();

            // Leading zeroes are ignored, due to canonicalization.
            assert_eq!(sn.to_string(), "01")
        }
    }
}