Skip to main content

der/asn1/
printable_string.rs

1//! ASN.1 `PrintableString` support.
2
3use crate::{FixedTag, Result, StringRef, Tag, asn1::AnyRef};
4use core::{fmt, ops::Deref};
5
6macro_rules! impl_printable_string {
7    ($type: ty) => {
8        impl_printable_string!($type,);
9    };
10    ($type: ty, $($li: lifetime)?) => {
11        impl_string_type!($type, $($li),*);
12
13        impl<$($li),*> FixedTag for $type {
14            const TAG: Tag = Tag::PrintableString;
15        }
16
17        impl<$($li),*> fmt::Debug for $type {
18            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19                write!(f, "PrintableString({:?})", self.as_str())
20            }
21        }
22    };
23}
24
25/// ASN.1 `PrintableString` type.
26///
27/// Supports a subset the ASCII character set (described below).
28///
29/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
30/// For the full ASCII character set, use
31/// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
32///
33/// This is a zero-copy reference type which borrows from the input data.
34///
35/// # Supported characters
36///
37/// The following ASCII characters/ranges are supported:
38///
39/// - `A..Z`
40/// - `a..z`
41/// - `0..9`
42/// - "` `" (i.e. space)
43/// - `\`
44/// - `(`
45/// - `)`
46/// - `+`
47/// - `,`
48/// - `-`
49/// - `.`
50/// - `/`
51/// - `:`
52/// - `=`
53/// - `?`
54#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
55pub struct PrintableStringRef<'a> {
56    /// Inner value
57    inner: &'a StringRef,
58}
59
60impl<'a> PrintableStringRef<'a> {
61    /// Create a new ASN.1 `PrintableString`.
62    ///
63    /// # Errors
64    /// If `input` contains characters outside the allowed range.
65    pub fn new<T>(input: &'a T) -> Result<Self>
66    where
67        T: AsRef<[u8]> + ?Sized,
68    {
69        let input = input.as_ref();
70
71        // Validate all characters are within PrintableString's allowed set
72        for &c in input.iter() {
73            match c {
74                b'A'..=b'Z'
75                | b'a'..=b'z'
76                | b'0'..=b'9'
77                | b' '
78                | b'\''
79                | b'('
80                | b')'
81                | b'+'
82                | b','
83                | b'-'
84                | b'.'
85                | b'/'
86                | b':'
87                | b'='
88                | b'?' => (),
89                _ => return Err(Self::TAG.value_error().into()),
90            }
91        }
92
93        StringRef::from_bytes(input)
94            .map(|inner| Self { inner })
95            .map_err(|_| Self::TAG.value_error().into())
96    }
97
98    /// Borrow the inner `str`.
99    #[must_use]
100    pub fn as_str(&self) -> &'a str {
101        self.inner.as_str()
102    }
103}
104
105impl_printable_string!(PrintableStringRef<'a>, 'a);
106
107impl<'a> Deref for PrintableStringRef<'a> {
108    type Target = StringRef;
109
110    fn deref(&self) -> &Self::Target {
111        self.inner
112    }
113}
114impl<'a> From<&PrintableStringRef<'a>> for PrintableStringRef<'a> {
115    fn from(value: &PrintableStringRef<'a>) -> PrintableStringRef<'a> {
116        *value
117    }
118}
119
120impl<'a> From<PrintableStringRef<'a>> for AnyRef<'a> {
121    fn from(printable_string: PrintableStringRef<'a>) -> AnyRef<'a> {
122        AnyRef::from_tag_and_value(Tag::PrintableString, printable_string.inner.as_ref())
123    }
124}
125
126#[cfg(feature = "alloc")]
127pub use self::allocation::PrintableString;
128
129#[cfg(feature = "alloc")]
130mod allocation {
131    use super::PrintableStringRef;
132
133    use crate::{
134        BytesRef, Error, FixedTag, Result, StringOwned, Tag,
135        asn1::AnyRef,
136        referenced::{OwnedToRef, RefToOwned},
137    };
138    use alloc::{borrow::ToOwned, string::String};
139    use core::{fmt, ops::Deref};
140
141    /// ASN.1 `PrintableString` type.
142    ///
143    /// Supports a subset the ASCII character set (described below).
144    ///
145    /// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`] instead.
146    /// For the full ASCII character set, use
147    /// [`Ia5StringRef`][`crate::asn1::Ia5StringRef`].
148    ///
149    /// # Supported characters
150    ///
151    /// The following ASCII characters/ranges are supported:
152    ///
153    /// - `A..Z`
154    /// - `a..z`
155    /// - `0..9`
156    /// - "` `" (i.e. space)
157    /// - `\`
158    /// - `(`
159    /// - `)`
160    /// - `+`
161    /// - `,`
162    /// - `-`
163    /// - `.`
164    /// - `/`
165    /// - `:`
166    /// - `=`
167    /// - `?`
168    #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
169    pub struct PrintableString {
170        /// Inner value
171        inner: StringOwned,
172    }
173
174    impl PrintableString {
175        /// Create a new ASN.1 `PrintableString`.
176        ///
177        /// # Errors
178        /// If any characters are out-of-range.
179        pub fn new<T>(input: &T) -> Result<Self>
180        where
181            T: AsRef<[u8]> + ?Sized,
182        {
183            let input = input.as_ref();
184            PrintableStringRef::new(input)?;
185
186            StringOwned::from_bytes(input)
187                .map(|inner| Self { inner })
188                .map_err(|_| Self::TAG.value_error().into())
189        }
190    }
191
192    impl_printable_string!(PrintableString);
193
194    impl Deref for PrintableString {
195        type Target = StringOwned;
196
197        fn deref(&self) -> &Self::Target {
198            &self.inner
199        }
200    }
201
202    impl<'a> From<PrintableStringRef<'a>> for PrintableString {
203        fn from(value: PrintableStringRef<'a>) -> PrintableString {
204            let inner =
205                StringOwned::from_bytes(value.inner.as_bytes()).expect("Invalid PrintableString");
206            Self { inner }
207        }
208    }
209
210    impl<'a> From<&'a PrintableString> for AnyRef<'a> {
211        fn from(printable_string: &'a PrintableString) -> AnyRef<'a> {
212            AnyRef::from_tag_and_value(
213                Tag::PrintableString,
214                BytesRef::new(printable_string.inner.as_bytes()).expect("Invalid PrintableString"),
215            )
216        }
217    }
218
219    impl<'a> From<&'a PrintableString> for PrintableStringRef<'a> {
220        fn from(printable_string: &'a PrintableString) -> PrintableStringRef<'a> {
221            printable_string.owned_to_ref()
222        }
223    }
224
225    impl<'a> RefToOwned<'a> for PrintableStringRef<'a> {
226        type Owned = PrintableString;
227        fn ref_to_owned(&self) -> Self::Owned {
228            PrintableString {
229                inner: self.inner.to_owned(),
230            }
231        }
232    }
233
234    impl OwnedToRef for PrintableString {
235        type Borrowed<'a> = PrintableStringRef<'a>;
236        fn owned_to_ref(&self) -> Self::Borrowed<'_> {
237            PrintableStringRef {
238                inner: self.inner.as_ref(),
239            }
240        }
241    }
242
243    impl TryFrom<String> for PrintableString {
244        type Error = Error;
245
246        fn try_from(input: String) -> Result<Self> {
247            PrintableStringRef::new(&input)?;
248
249            StringOwned::new(input)
250                .map(|inner| Self { inner })
251                .map_err(|_| Self::TAG.value_error().into())
252        }
253    }
254}
255
256#[cfg(test)]
257#[allow(clippy::unwrap_used)]
258mod tests {
259    use super::PrintableStringRef;
260    use crate::Decode;
261
262    #[test]
263    fn parse_bytes() {
264        let example_bytes = &[
265            0x13, 0x0b, 0x54, 0x65, 0x73, 0x74, 0x20, 0x55, 0x73, 0x65, 0x72, 0x20, 0x31,
266        ];
267
268        let printable_string = PrintableStringRef::from_der(example_bytes).unwrap();
269        assert_eq!(printable_string.as_str(), "Test User 1");
270    }
271}