1use core::{fmt::Display, marker::PhantomData};
4
5use der::{
6 DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd,
7 Writer,
8 asn1::{self, Int},
9};
10#[cfg(feature = "builder")]
11use {alloc::vec, signature::rand_core::CryptoRng};
12
13use crate::certificate::{Profile, Rfc5280};
14
15#[derive(Clone, Debug, Eq, PartialEq, ValueOrd, PartialOrd, Ord)]
32pub struct SerialNumber<P: Profile = Rfc5280> {
33 pub(crate) inner: Int,
34 _profile: PhantomData<P>,
35}
36
37impl<P: Profile> SerialNumber<P> {
38 pub const MAX_LEN: Length = Length::new(20);
40
41 pub(crate) const MAX_DECODE_LEN: Length = Length::new(21);
43
44 pub fn new(bytes: &[u8]) -> Result<Self> {
48 let inner = asn1::Uint::new(bytes)?;
49
50 if inner.value_len()? > Self::MAX_LEN {
56 return Err(ErrorKind::Overlength.into());
57 }
58
59 Ok(Self {
60 inner: inner.into(),
61 _profile: PhantomData,
62 })
63 }
64
65 pub fn as_bytes(&self) -> &[u8] {
68 self.inner.as_bytes()
69 }
70}
71
72#[cfg(feature = "builder")]
73impl<P: Profile> SerialNumber<P> {
74 pub fn generate<R: CryptoRng + ?Sized>(rng: &mut R) -> Self {
81 Self::generate_with_prefix(&[], 17, rng)
82 .expect("a random of 17 is acceptable, and rng may not fail")
83 }
84
85 pub fn generate_with_prefix<R: CryptoRng + ?Sized>(
95 prefix: &[u8],
96 rand_len: usize,
97 rng: &mut R,
98 ) -> Result<Self> {
99 if rand_len < 8 {
101 return Err(ErrorKind::Failed.into());
102 }
103
104 if rand_len + prefix.len() > 19 {
105 return Err(ErrorKind::Failed.into());
106 }
107
108 let mut buf = vec![0; prefix.len() + rand_len];
109 buf[..prefix.len()].copy_from_slice(prefix);
110
111 let rand_buf = &mut buf[prefix.len()..];
112
113 while rand_buf[0] == 0 {
116 rng.fill_bytes(rand_buf);
117 }
118
119 Self::new(&buf)
120 }
121}
122
123impl<P: Profile> EncodeValue for SerialNumber<P> {
124 fn value_len(&self) -> Result<Length> {
125 self.inner.value_len()
126 }
127
128 fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
129 self.inner.encode_value(writer)
130 }
131}
132
133impl<'a, P: Profile> DecodeValue<'a> for SerialNumber<P> {
134 type Error = der::Error;
135
136 fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
137 let inner = Int::decode_value(reader, header)?;
138 let serial = Self {
139 inner,
140 _profile: PhantomData,
141 };
142
143 P::check_serial_number(&serial)?;
144
145 Ok(serial)
146 }
147}
148
149impl<P: Profile> FixedTag for SerialNumber<P> {
150 const TAG: Tag = <Int as FixedTag>::TAG;
151}
152
153impl Display for SerialNumber {
154 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
155 let mut iter = self.as_bytes().iter().peekable();
156
157 while let Some(byte) = iter.next() {
158 match iter.peek() {
159 Some(_) => write!(f, "{byte:02X}:")?,
160 None => write!(f, "{byte:02X}")?,
161 }
162 }
163
164 Ok(())
165 }
166}
167
168macro_rules! impl_from {
169 ($source:ty) => {
170 impl From<$source> for SerialNumber {
171 fn from(inner: $source) -> SerialNumber {
172 let serial_number = &inner.to_be_bytes()[..];
173 let serial_number = asn1::Uint::new(serial_number).unwrap();
174
175 SerialNumber::new(serial_number.as_bytes()).unwrap()
178 }
179 }
180 };
181}
182
183impl_from!(u8);
184impl_from!(u16);
185impl_from!(u32);
186impl_from!(u64);
187impl_from!(usize);
188
189#[cfg(feature = "arbitrary")]
192impl<'a, P: Profile> arbitrary::Arbitrary<'a> for SerialNumber<P> {
193 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
194 let len = u.int_in_range(0u32..=Self::MAX_LEN.into())?;
195
196 Self::new(u.bytes(len as usize)?).map_err(|_| arbitrary::Error::IncorrectFormat)
197 }
198
199 fn size_hint(depth: usize) -> (usize, Option<usize>) {
200 arbitrary::size_hint::and(u32::size_hint(depth), (0, None))
201 }
202}
203
204#[cfg(test)]
205#[allow(clippy::unwrap_used)]
206mod tests {
207 use alloc::string::ToString;
208
209 use super::*;
210
211 #[test]
212 fn serial_number_invariants() {
213 {
215 let too_big = [0x80; 20];
216 assert!(SerialNumber::<Rfc5280>::new(&too_big).is_err());
217 }
218
219 {
221 let just_enough = [
222 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
223 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
224 ];
225 assert!(SerialNumber::<Rfc5280>::new(&just_enough).is_ok());
226 }
227 }
228
229 #[test]
230 fn serial_number_display() {
231 {
232 let sn = SerialNumber::new(&[0x11, 0x22, 0x33]).unwrap();
233
234 assert_eq!(sn.to_string(), "11:22:33")
235 }
236
237 {
238 let sn = SerialNumber::new(&[0xAA, 0xBB, 0xCC, 0x01, 0x10, 0x00, 0x11]).unwrap();
239
240 assert_eq!(sn.to_string(), "00:AA:BB:CC:01:10:00:11")
242 }
243
244 {
245 let sn = SerialNumber::new(&[0x00, 0x00, 0x01]).unwrap();
246
247 assert_eq!(sn.to_string(), "01")
249 }
250 }
251
252 #[cfg(feature = "builder")]
253 #[test]
254 fn serial_number_generate() {
255 let sn = SerialNumber::<Rfc5280>::generate(&mut rand::rng());
256
257 assert!(matches!(sn.as_bytes().len(), 17..=18));
261
262 let sn = SerialNumber::<Rfc5280>::generate_with_prefix(&[], 8, &mut rand::rng()).unwrap();
263 assert!(matches!(sn.as_bytes().len(), 8..=9));
264
265 let sn =
266 SerialNumber::<Rfc5280>::generate_with_prefix(&[1, 2, 3], 8, &mut rand::rng()).unwrap();
267 assert!(matches!(sn.as_bytes().len(), 11..=12));
268 assert_eq!(&sn.as_bytes()[..3], &[1, 2, 3]);
269
270 let sn = SerialNumber::<Rfc5280>::generate_with_prefix(&[], 7, &mut rand::rng());
271 assert!(sn.is_err());
272
273 let sn = SerialNumber::<Rfc5280>::generate_with_prefix(&[], 20, &mut rand::rng());
274 assert!(sn.is_err());
275
276 let sn = SerialNumber::<Rfc5280>::generate_with_prefix(&[], 19, &mut rand::rng());
277 assert!(sn.is_ok());
278
279 let sn = SerialNumber::<Rfc5280>::generate_with_prefix(&[1], 19, &mut rand::rng());
280 assert!(sn.is_err());
281 }
282}