der/asn1/
ia5_string.rs

1//! ASN.1 `IA5String` support.
2
3use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag};
4use core::{fmt, ops::Deref};
5
6macro_rules! impl_ia5_string {
7    ($type: ty) => {
8        impl_ia5_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::Ia5String;
15        }
16
17        impl<$($li),*> fmt::Debug for $type {
18            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19                write!(f, "Ia5String({:?})", self.as_str())
20            }
21        }
22    };
23}
24
25/// ASN.1 `IA5String` type.
26///
27/// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e.
28/// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now
29/// technically known as the International Reference Alphabet or IRA as
30/// specified in the ITU-T's T.50 recommendation).
31///
32/// For UTF-8, use [`Utf8StringRef`][`crate::asn1::Utf8StringRef`].
33///
34/// This is a zero-copy reference type which borrows from the input data.
35///
36/// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29
37#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
38pub struct Ia5StringRef<'a> {
39    /// Inner value
40    inner: StrRef<'a>,
41}
42
43impl<'a> Ia5StringRef<'a> {
44    /// Create a new `IA5String`.
45    pub fn new<T>(input: &'a T) -> Result<Self>
46    where
47        T: AsRef<[u8]> + ?Sized,
48    {
49        let input = input.as_ref();
50
51        // Validate all characters are within IA5String's allowed set
52        if input.iter().any(|&c| c > 0x7F) {
53            return Err(Self::TAG.value_error());
54        }
55
56        StrRef::from_bytes(input)
57            .map(|inner| Self { inner })
58            .map_err(|_| Self::TAG.value_error())
59    }
60}
61
62impl_ia5_string!(Ia5StringRef<'a>, 'a);
63
64impl<'a> Deref for Ia5StringRef<'a> {
65    type Target = StrRef<'a>;
66
67    fn deref(&self) -> &Self::Target {
68        &self.inner
69    }
70}
71
72impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> {
73    fn from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a> {
74        *value
75    }
76}
77
78impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> {
79    fn from(internationalized_string: Ia5StringRef<'a>) -> AnyRef<'a> {
80        AnyRef::from_tag_and_value(Tag::Ia5String, internationalized_string.inner.into())
81    }
82}
83
84#[cfg(feature = "alloc")]
85pub use self::allocation::Ia5String;
86
87#[cfg(feature = "alloc")]
88mod allocation {
89    use super::Ia5StringRef;
90    use crate::{
91        asn1::AnyRef,
92        referenced::{OwnedToRef, RefToOwned},
93        Error, FixedTag, Result, StrOwned, Tag,
94    };
95    use alloc::string::String;
96    use core::{fmt, ops::Deref};
97
98    /// ASN.1 `IA5String` type.
99    ///
100    /// Supports the [International Alphabet No. 5 (IA5)] character encoding, i.e.
101    /// the lower 128 characters of the ASCII alphabet. (Note: IA5 is now
102    /// technically known as the International Reference Alphabet or IRA as
103    /// specified in the ITU-T's T.50 recommendation).
104    ///
105    /// For UTF-8, use [`String`][`alloc::string::String`].
106    ///
107    /// [International Alphabet No. 5 (IA5)]: https://en.wikipedia.org/wiki/T.50_%28standard%29
108    #[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
109    pub struct Ia5String {
110        /// Inner value
111        inner: StrOwned,
112    }
113
114    impl Ia5String {
115        /// Create a new `IA5String`.
116        pub fn new<T>(input: &T) -> Result<Self>
117        where
118            T: AsRef<[u8]> + ?Sized,
119        {
120            let input = input.as_ref();
121            Ia5StringRef::new(input)?;
122
123            StrOwned::from_bytes(input)
124                .map(|inner| Self { inner })
125                .map_err(|_| Self::TAG.value_error())
126        }
127    }
128
129    impl_ia5_string!(Ia5String);
130
131    impl Deref for Ia5String {
132        type Target = StrOwned;
133
134        fn deref(&self) -> &Self::Target {
135            &self.inner
136        }
137    }
138
139    impl<'a> From<Ia5StringRef<'a>> for Ia5String {
140        fn from(international_string: Ia5StringRef<'a>) -> Ia5String {
141            let inner = international_string.inner.into();
142            Self { inner }
143        }
144    }
145
146    impl<'a> From<&'a Ia5String> for AnyRef<'a> {
147        fn from(international_string: &'a Ia5String) -> AnyRef<'a> {
148            AnyRef::from_tag_and_value(Tag::Ia5String, (&international_string.inner).into())
149        }
150    }
151
152    impl<'a> RefToOwned<'a> for Ia5StringRef<'a> {
153        type Owned = Ia5String;
154        fn ref_to_owned(&self) -> Self::Owned {
155            Ia5String {
156                inner: self.inner.ref_to_owned(),
157            }
158        }
159    }
160
161    impl OwnedToRef for Ia5String {
162        type Borrowed<'a> = Ia5StringRef<'a>;
163        fn owned_to_ref(&self) -> Self::Borrowed<'_> {
164            Ia5StringRef {
165                inner: self.inner.owned_to_ref(),
166            }
167        }
168    }
169
170    impl TryFrom<String> for Ia5String {
171        type Error = Error;
172
173        fn try_from(input: String) -> Result<Self> {
174            Ia5StringRef::new(&input)?;
175
176            StrOwned::new(input)
177                .map(|inner| Self { inner })
178                .map_err(|_| Self::TAG.value_error())
179        }
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::Ia5StringRef;
186    use crate::Decode;
187    use hex_literal::hex;
188
189    #[test]
190    fn parse_bytes() {
191        let example_bytes = hex!("16 0d 74 65 73 74 31 40 72 73 61 2e 63 6f 6d");
192        let internationalized_string = Ia5StringRef::from_der(&example_bytes).unwrap();
193        assert_eq!(internationalized_string.as_str(), "test1@rsa.com");
194    }
195}