picky_asn1_x509/
directory_string.rs1use picky_asn1::restricted_string::{PrintableString, Utf8String};
2use picky_asn1::tag::{Tag, TagPeeker};
3use picky_asn1::wrapper::{BmpStringAsn1, PrintableStringAsn1};
4use serde::{de, ser};
5use std::borrow::Cow;
6use std::fmt;
7
8#[derive(Debug, PartialEq, Eq, Clone)]
21pub enum DirectoryString {
22 PrintableString(PrintableStringAsn1),
24 Utf8String(String),
26 BmpString(BmpStringAsn1),
27}
28
29impl fmt::Display for DirectoryString {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "{}", self.to_utf8_lossy())
32 }
33}
34
35impl DirectoryString {
36 pub fn to_utf8_lossy(&self) -> Cow<str> {
37 match &self {
38 DirectoryString::PrintableString(string) => String::from_utf8_lossy(string.as_bytes()),
39 DirectoryString::Utf8String(string) => Cow::Borrowed(string.as_str()),
40 DirectoryString::BmpString(string) => Cow::Owned(utf16_to_utf8_lossy(string.as_bytes())),
41 }
42 }
43
44 pub fn as_bytes(&self) -> &[u8] {
45 match &self {
46 DirectoryString::PrintableString(string) => string.as_bytes(),
47 DirectoryString::Utf8String(string) => string.as_bytes(),
48 DirectoryString::BmpString(string) => string.as_bytes(),
49 }
50 }
51}
52
53impl From<&str> for DirectoryString {
54 fn from(string: &str) -> Self {
55 Self::Utf8String(string.to_owned())
56 }
57}
58
59impl From<String> for DirectoryString {
60 fn from(string: String) -> Self {
61 Self::Utf8String(string)
62 }
63}
64
65impl From<PrintableString> for DirectoryString {
66 fn from(string: PrintableString) -> Self {
67 Self::PrintableString(string.into())
68 }
69}
70
71impl From<Utf8String> for DirectoryString {
72 fn from(string: Utf8String) -> Self {
73 Self::Utf8String(String::from_utf8(string.into_bytes()).expect("Utf8String has the right charset"))
74 }
75}
76
77impl From<PrintableStringAsn1> for DirectoryString {
78 fn from(string: PrintableStringAsn1) -> Self {
79 Self::PrintableString(string)
80 }
81}
82
83impl From<BmpStringAsn1> for DirectoryString {
84 fn from(string: BmpStringAsn1) -> Self {
85 Self::BmpString(string)
86 }
87}
88
89impl From<DirectoryString> for String {
90 fn from(ds: DirectoryString) -> Self {
91 match ds {
92 DirectoryString::PrintableString(string) => String::from_utf8_lossy(string.as_bytes()).into(),
93 DirectoryString::Utf8String(string) => string,
94 DirectoryString::BmpString(string) => utf16_to_utf8_lossy(string.as_bytes()),
95 }
96 }
97}
98
99impl ser::Serialize for DirectoryString {
100 fn serialize<S>(&self, serializer: S) -> Result<<S as ser::Serializer>::Ok, <S as ser::Serializer>::Error>
101 where
102 S: ser::Serializer,
103 {
104 match &self {
105 DirectoryString::PrintableString(string) => string.serialize(serializer),
106 DirectoryString::Utf8String(string) => string.serialize(serializer),
107 DirectoryString::BmpString(string) => string.serialize(serializer),
108 }
109 }
110}
111
112impl<'de> de::Deserialize<'de> for DirectoryString {
113 fn deserialize<D>(deserializer: D) -> Result<Self, <D as de::Deserializer<'de>>::Error>
114 where
115 D: de::Deserializer<'de>,
116 {
117 struct Visitor;
118
119 impl<'de> de::Visitor<'de> for Visitor {
120 type Value = DirectoryString;
121
122 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
123 formatter.write_str("a valid DER-encoded DirectoryString")
124 }
125
126 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
127 where
128 A: de::SeqAccess<'de>,
129 {
130 let tag_peeker: TagPeeker = seq_next_element!(seq, DirectoryString, "choice tag");
131 match tag_peeker.next_tag {
132 Tag::UTF8_STRING => Ok(DirectoryString::Utf8String(seq_next_element!(
133 seq,
134 DirectoryString,
135 "Utf8String"
136 ))),
137 Tag::PRINTABLE_STRING => Ok(DirectoryString::PrintableString(seq_next_element!(
138 seq,
139 DirectoryString,
140 "PrintableString"
141 ))),
142 Tag::BMP_STRING => Ok(DirectoryString::BmpString(seq_next_element!(
143 seq,
144 DirectoryString,
145 "BmpString"
146 ))),
147 Tag::TELETEX_STRING => Err(serde_invalid_value!(
148 DirectoryString,
149 "TeletexString not supported",
150 "a supported string type"
151 )),
152 Tag::VIDEOTEX_STRING => Err(serde_invalid_value!(
153 DirectoryString,
154 "VideotexString not supported",
155 "a supported string type"
156 )),
157 Tag::IA5_STRING => Err(serde_invalid_value!(
158 DirectoryString,
159 "IA5String not supported",
160 "a supported string type"
161 )),
162 _ => Err(serde_invalid_value!(
163 DirectoryString,
164 "unknown string type",
165 "a known supported string type"
166 )),
167 }
168 }
169 }
170
171 deserializer.deserialize_enum("DirectoryString", &["PrintableString", "Utf8String"], Visitor)
172 }
173}
174
175fn utf16_to_utf8_lossy(data: &[u8]) -> String {
176 debug_assert_eq!(data.len() % 2, 0);
177 String::from_utf16_lossy(
178 &data
179 .chunks(2)
180 .map(|c| u16::from_be_bytes(c.try_into().unwrap()))
181 .collect::<Vec<u16>>(),
182 )
183}