x509_cert/
serial_number.rs1use core::{fmt::Display, marker::PhantomData};
4
5use der::{
6 asn1::{self, Int},
7 DecodeValue, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader, Result, Tag, ValueOrd,
8 Writer,
9};
10
11use crate::certificate::{Profile, Rfc5280};
12
13#[derive(Clone, Debug, Eq, PartialEq, ValueOrd, PartialOrd, Ord)]
30pub struct SerialNumber<P: Profile = Rfc5280> {
31 pub(crate) inner: Int,
32 _profile: PhantomData<P>,
33}
34
35impl<P: Profile> SerialNumber<P> {
36 pub const MAX_LEN: Length = Length::new(20);
38
39 pub(crate) const MAX_DECODE_LEN: Length = Length::new(21);
41
42 pub fn new(bytes: &[u8]) -> Result<Self> {
46 let inner = asn1::Uint::new(bytes)?;
47
48 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 pub fn as_bytes(&self) -> &[u8] {
66 self.inner.as_bytes()
67 }
68}
69
70impl<P: Profile> EncodeValue for SerialNumber<P> {
71 fn value_len(&self) -> Result<Length> {
72 self.inner.value_len()
73 }
74
75 fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
76 self.inner.encode_value(writer)
77 }
78}
79
80impl<'a, P: Profile> DecodeValue<'a> for SerialNumber<P> {
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
94impl<P: Profile> FixedTag for SerialNumber<P> {
95 const TAG: Tag = <Int as FixedTag>::TAG;
96}
97
98impl Display for SerialNumber {
99 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
113macro_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 SerialNumber::new(serial_number.as_bytes()).unwrap()
123 }
124 }
125 };
126}
127
128impl_from!(u8);
129impl_from!(u16);
130impl_from!(u32);
131impl_from!(u64);
132impl_from!(usize);
133
134#[cfg(feature = "arbitrary")]
137impl<'a, P: Profile> arbitrary::Arbitrary<'a> for SerialNumber<P> {
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
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#[allow(clippy::unwrap_used)]
151mod tests {
152 use alloc::string::ToString;
153
154 use super::*;
155
156 #[test]
157 fn serial_number_invariants() {
158 {
160 let too_big = [0x80; 20];
161 assert!(SerialNumber::<Rfc5280>::new(&too_big).is_err());
162 }
163
164 {
166 let just_enough = [
167 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
168 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
169 ];
170 assert!(SerialNumber::<Rfc5280>::new(&just_enough).is_ok());
171 }
172 }
173
174 #[test]
175 fn serial_number_display() {
176 {
177 let sn = SerialNumber::new(&[0x11, 0x22, 0x33]).unwrap();
178
179 assert_eq!(sn.to_string(), "11:22:33")
180 }
181
182 {
183 let sn = SerialNumber::new(&[0xAA, 0xBB, 0xCC, 0x01, 0x10, 0x00, 0x11]).unwrap();
184
185 assert_eq!(sn.to_string(), "00:AA:BB:CC:01:10:00:11")
187 }
188
189 {
190 let sn = SerialNumber::new(&[0x00, 0x00, 0x01]).unwrap();
191
192 assert_eq!(sn.to_string(), "01")
194 }
195 }
196}