1 //! X.509 serial number
2 
3 use core::{fmt::Display, marker::PhantomData};
4 
5 use der::{
6     asn1::{self, Int},
7     DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd,
8     Writer,
9 };
10 
11 use crate::certificate::{Profile, Rfc5280};
12 
13 /// [RFC 5280 Section 4.1.2.2.]  Serial Number
14 ///
15 ///   The serial number MUST be a positive integer assigned by the CA to
16 ///   each certificate.  It MUST be unique for each certificate issued by a
17 ///   given CA (i.e., the issuer name and serial number identify a unique
18 ///   certificate).  CAs MUST force the serialNumber to be a non-negative
19 ///   integer.
20 ///
21 ///   Given the uniqueness requirements above, serial numbers can be
22 ///   expected to contain long integers.  Certificate users MUST be able to
23 ///   handle serialNumber values up to 20 octets.  Conforming CAs MUST NOT
24 ///   use serialNumber values longer than 20 octets.
25 ///
26 ///   Note: Non-conforming CAs may issue certificates with serial numbers
27 ///   that are negative or zero.  Certificate users SHOULD be prepared to
28 ///   gracefully handle such certificates.
29 #[derive(Clone, Debug, Eq, PartialEq, ValueOrd, PartialOrd, Ord)]
30 pub struct SerialNumber<P: Profile = Rfc5280> {
31     pub(crate) inner: Int,
32     _profile: PhantomData<P>,
33 }
34 
35 impl<P: Profile> SerialNumber<P> {
36     /// Maximum length in bytes for a [`SerialNumber`]
37     pub const MAX_LEN: Length = Length::new(20);
38 
39     /// See notes in `SerialNumber::new` and `SerialNumber::decode_value`.
40     pub(crate) const MAX_DECODE_LEN: Length = Length::new(21);
41 
42     /// Create a new [`SerialNumber`] from a byte slice.
43     ///
44     /// The byte slice **must** represent a positive integer.
new(bytes: &[u8]) -> Result<Self>45     pub fn new(bytes: &[u8]) -> Result<Self> {
46         let inner = asn1::Uint::new(bytes)?;
47 
48         // The user might give us a 20 byte unsigned integer with a high MSB,
49         // which we'd then encode with 21 octets to preserve the sign bit.
50         // RFC 5280 is ambiguous about whether this is valid, so we limit
51         // `SerialNumber` *encodings* to 20 bytes or fewer while permitting
52         // `SerialNumber` *decodings* to have up to 21 bytes below.
53         if inner.value_len()? > Self::MAX_LEN {
54             return Err(ErrorKind::Overlength.into());
55         }
56 
57         Ok(Self {
58             inner: inner.into(),
59             _profile: PhantomData,
60         })
61     }
62 
63     /// Borrow the inner byte slice which contains the least significant bytes
64     /// of a big endian integer value with all leading zeros stripped.
as_bytes(&self) -> &[u8]65     pub fn as_bytes(&self) -> &[u8] {
66         self.inner.as_bytes()
67     }
68 }
69 
70 impl<P: Profile> EncodeValue for SerialNumber<P> {
value_len(&self) -> Result<Length>71     fn value_len(&self) -> Result<Length> {
72         self.inner.value_len()
73     }
74 
encode_value(&self, writer: &mut impl Writer) -> Result<()>75     fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
76         self.inner.encode_value(writer)
77     }
78 }
79 
80 impl<'a, P: Profile> DecodeValue<'a> for SerialNumber<P> {
decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self>81     fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
82         let inner = Int::decode_value(reader, header)?;
83         let serial = Self {
84             inner,
85             _profile: PhantomData,
86         };
87 
88         P::check_serial_number(&serial)?;
89 
90         Ok(serial)
91     }
92 }
93 
94 impl<P: Profile> FixedTag for SerialNumber<P> {
95     const TAG: Tag = <Int as FixedTag>::TAG;
96 }
97 
98 impl Display for SerialNumber {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result99     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100         let mut iter = self.as_bytes().iter().peekable();
101 
102         while let Some(byte) = iter.next() {
103             match iter.peek() {
104                 Some(_) => write!(f, "{:02X}:", byte)?,
105                 None => write!(f, "{:02X}", byte)?,
106             }
107         }
108 
109         Ok(())
110     }
111 }
112 
113 macro_rules! impl_from {
114     ($source:ty) => {
115         impl From<$source> for SerialNumber {
116             fn from(inner: $source) -> SerialNumber {
117                 let serial_number = &inner.to_be_bytes()[..];
118                 let serial_number = asn1::Uint::new(serial_number).unwrap();
119 
120                 // This could only fail if the big endian representation was to be more than 20
121                 // bytes long. Because it's only implemented for up to u64 / usize (8 bytes).
122                 SerialNumber::new(serial_number.as_bytes()).unwrap()
123             }
124         }
125     };
126 }
127 
128 impl_from!(u8);
129 impl_from!(u16);
130 impl_from!(u32);
131 impl_from!(u64);
132 impl_from!(usize);
133 
134 // Implement by hand because the derive would create invalid values.
135 // Use the constructor to create a valid value.
136 #[cfg(feature = "arbitrary")]
137 impl<'a, P: Profile> arbitrary::Arbitrary<'a> for SerialNumber<P> {
arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self>138     fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
139         let len = u.int_in_range(0u32..=Self::MAX_LEN.into())?;
140 
141         Self::new(u.bytes(len as usize)?).map_err(|_| arbitrary::Error::IncorrectFormat)
142     }
143 
size_hint(depth: usize) -> (usize, Option<usize>)144     fn size_hint(depth: usize) -> (usize, Option<usize>) {
145         arbitrary::size_hint::and(u32::size_hint(depth), (0, None))
146     }
147 }
148 
149 #[cfg(test)]
150 mod tests {
151     use alloc::string::ToString;
152 
153     use super::*;
154 
155     #[test]
serial_number_invariants()156     fn serial_number_invariants() {
157         // Creating a new serial with an oversized encoding (due to high MSB) fails.
158         {
159             let too_big = [0x80; 20];
160             assert!(SerialNumber::<Rfc5280>::new(&too_big).is_err());
161         }
162 
163         // Creating a new serial with the maximum encoding succeeds.
164         {
165             let just_enough = [
166                 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
167                 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
168             ];
169             assert!(SerialNumber::<Rfc5280>::new(&just_enough).is_ok());
170         }
171     }
172 
173     #[test]
serial_number_display()174     fn serial_number_display() {
175         {
176             let sn = SerialNumber::new(&[0x11, 0x22, 0x33]).unwrap();
177 
178             assert_eq!(sn.to_string(), "11:22:33")
179         }
180 
181         {
182             let sn = SerialNumber::new(&[0xAA, 0xBB, 0xCC, 0x01, 0x10, 0x00, 0x11]).unwrap();
183 
184             // We force the user's serial to be positive if they give us a negative one.
185             assert_eq!(sn.to_string(), "00:AA:BB:CC:01:10:00:11")
186         }
187 
188         {
189             let sn = SerialNumber::new(&[0x00, 0x00, 0x01]).unwrap();
190 
191             // Leading zeroes are ignored, due to canonicalization.
192             assert_eq!(sn.to_string(), "01")
193         }
194     }
195 }
196