1 //! ASN.1 `IA5String` support. 2 3 use crate::{asn1::AnyRef, FixedTag, Result, StrRef, Tag}; 4 use core::{fmt, ops::Deref}; 5 6 macro_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)] 38 pub struct Ia5StringRef<'a> { 39 /// Inner value 40 inner: StrRef<'a>, 41 } 42 43 impl<'a> Ia5StringRef<'a> { 44 /// Create a new `IA5String`. new<T>(input: &'a T) -> Result<Self> where T: AsRef<[u8]> + ?Sized,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 62 impl_ia5_string!(Ia5StringRef<'a>, 'a); 63 64 impl<'a> Deref for Ia5StringRef<'a> { 65 type Target = StrRef<'a>; 66 deref(&self) -> &Self::Target67 fn deref(&self) -> &Self::Target { 68 &self.inner 69 } 70 } 71 72 impl<'a> From<&Ia5StringRef<'a>> for Ia5StringRef<'a> { from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a>73 fn from(value: &Ia5StringRef<'a>) -> Ia5StringRef<'a> { 74 *value 75 } 76 } 77 78 impl<'a> From<Ia5StringRef<'a>> for AnyRef<'a> { from(internationalized_string: Ia5StringRef<'a>) -> 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")] 85 pub use self::allocation::Ia5String; 86 87 #[cfg(feature = "alloc")] 88 mod 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`. new<T>(input: &T) -> Result<Self> where T: AsRef<[u8]> + ?Sized,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 deref(&self) -> &Self::Target134 fn deref(&self) -> &Self::Target { 135 &self.inner 136 } 137 } 138 139 impl<'a> From<Ia5StringRef<'a>> for Ia5String { from(international_string: Ia5StringRef<'a>) -> Ia5String140 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> { from(international_string: &'a Ia5String) -> 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; ref_to_owned(&self) -> Self::Owned154 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>; owned_to_ref(&self) -> Self::Borrowed<'_>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 try_from(input: String) -> Result<Self>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)] 184 mod tests { 185 use super::Ia5StringRef; 186 use crate::Decode; 187 use hex_literal::hex; 188 189 #[test] parse_bytes()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 } 196