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