1use alloc::vec::Vec;
4use const_oid::db::{
5 rfc4519::{COUNTRY_NAME, DOMAIN_COMPONENT, SERIAL_NUMBER},
6 Database, DB,
7};
8use core::{
9 fmt::{self, Write},
10 str::FromStr,
11};
12use der::{
13 asn1::{
14 Any, Ia5StringRef, ObjectIdentifier, PrintableStringRef, SetOfVec, TeletexStringRef,
15 Utf8StringRef,
16 },
17 Decode, Encode, Error, ErrorKind, Sequence, Tag, Tagged, ValueOrd,
18};
19
20pub type AttributeType = ObjectIdentifier;
28
29pub type AttributeValue = Any;
37
38#[derive(Clone, Debug, PartialEq, Eq, Sequence, ValueOrd)]
61#[allow(missing_docs)]
62pub struct Attribute {
63 pub oid: AttributeType,
64 pub values: SetOfVec<AttributeValue>,
65}
66
67pub type Attributes = SetOfVec<Attribute>;
75
76#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
87#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Sequence, ValueOrd)]
88#[allow(missing_docs)]
89pub struct AttributeTypeAndValue {
90 pub oid: AttributeType,
91 pub value: AttributeValue,
92}
93
94#[derive(Copy, Clone)]
95enum Escape {
96 None,
97 Some,
98 Hex(u8),
99}
100
101struct Parser {
102 state: Escape,
103 bytes: Vec<u8>,
104}
105
106impl Parser {
107 pub fn new() -> Self {
108 Self {
109 state: Escape::None,
110 bytes: Vec::new(),
111 }
112 }
113
114 fn push(&mut self, c: u8) {
115 self.state = Escape::None;
116 self.bytes.push(c);
117 }
118
119 pub fn add(&mut self, c: u8) -> Result<(), Error> {
120 match (self.state, c) {
121 (Escape::Hex(p), b'0'..=b'9') => self.push(p | (c - b'0')),
122 (Escape::Hex(p), b'a'..=b'f') => self.push(p | (c - b'a' + 10)),
123 (Escape::Hex(p), b'A'..=b'F') => self.push(p | (c - b'A' + 10)),
124
125 (Escape::Some, b'0'..=b'9') => self.state = Escape::Hex((c - b'0') << 4),
126 (Escape::Some, b'a'..=b'f') => self.state = Escape::Hex((c - b'a' + 10) << 4),
127 (Escape::Some, b'A'..=b'F') => self.state = Escape::Hex((c - b'A' + 10) << 4),
128
129 (Escape::Some, b' ' | b'"' | b'#' | b'=' | b'\\') => self.push(c),
130 (Escape::Some, b'+' | b',' | b';' | b'<' | b'>') => self.push(c),
131
132 (Escape::None, b'\\') => self.state = Escape::Some,
133 (Escape::None, ..) => self.push(c),
134
135 _ => return Err(ErrorKind::Failed.into()),
136 }
137
138 Ok(())
139 }
140
141 pub fn as_bytes(&self) -> &[u8] {
142 &self.bytes
143 }
144}
145
146impl AttributeTypeAndValue {
147 fn from_hex(oid: ObjectIdentifier, val: &str) -> Result<Self, Error> {
149 let mut iter = match val.len() % 2 {
151 0 => [].iter().cloned().chain(val.bytes()),
152 1 => [0u8].iter().cloned().chain(val.bytes()),
153 _ => unreachable!(),
154 };
155
156 let mut bytes = Vec::with_capacity((val.len() + 1) / 2);
158
159 while let (Some(h), Some(l)) = (iter.next(), iter.next()) {
160 let mut byte = 0u8;
161
162 for (half, shift) in [(h, 4), (l, 0)] {
163 match half {
164 b'0'..=b'9' => byte |= (half - b'0') << shift,
165 b'a'..=b'f' => byte |= (half - b'a' + 10) << shift,
166 b'A'..=b'F' => byte |= (half - b'A' + 10) << shift,
167 _ => return Err(ErrorKind::Failed.into()),
168 }
169 }
170
171 bytes.push(byte);
172 }
173
174 Ok(Self {
175 oid,
176 value: Any::from_der(&bytes)?,
177 })
178 }
179
180 fn from_delimited_str(oid: ObjectIdentifier, val: &str) -> Result<Self, Error> {
182 let mut parser = Parser::new();
184 for c in val.bytes() {
185 parser.add(c)?;
186 }
187
188 let tag = match oid {
189 COUNTRY_NAME => Tag::PrintableString,
190 DOMAIN_COMPONENT => Tag::Ia5String,
191 SERIAL_NUMBER => Tag::PrintableString,
194 _ => Tag::Utf8String,
195 };
196
197 Ok(Self {
198 oid,
199 value: Any::new(tag, parser.as_bytes())?,
200 })
201 }
202
203 #[deprecated(
209 since = "0.2.1",
210 note = "use AttributeTypeAndValue::from_str(...)?.to_der()"
211 )]
212 pub fn encode_from_string(s: &str) -> Result<Vec<u8>, Error> {
213 Self::from_str(s)?.to_der()
214 }
215}
216
217impl FromStr for AttributeTypeAndValue {
223 type Err = Error;
224
225 fn from_str(s: &str) -> der::Result<Self> {
226 let idx = s.find('=').ok_or_else(|| Error::from(ErrorKind::Failed))?;
227 let (key, val) = s.split_at(idx);
228 let val = &val[1..];
229
230 let oid = match DB.by_name(key) {
232 Some(oid) => *oid,
233 None => ObjectIdentifier::new(key)?,
234 };
235
236 match val.strip_prefix('#') {
238 Some(val) => Self::from_hex(oid, val),
239 None => Self::from_delimited_str(oid, val),
240 }
241 }
242}
243
244impl fmt::Display for AttributeTypeAndValue {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 let val = match self.value.tag() {
250 Tag::PrintableString => PrintableStringRef::try_from(&self.value)
251 .ok()
252 .map(|s| s.as_str()),
253 Tag::Utf8String => Utf8StringRef::try_from(&self.value)
254 .ok()
255 .map(|s| s.as_str()),
256 Tag::Ia5String => Ia5StringRef::try_from(&self.value).ok().map(|s| s.as_str()),
257 Tag::TeletexString => TeletexStringRef::try_from(&self.value)
258 .ok()
259 .map(|s| s.as_str()),
260 _ => None,
261 };
262
263 if let (Some(key), Some(val)) = (DB.shortest_name_by_oid(&self.oid), val) {
264 write!(f, "{}=", key.to_ascii_uppercase())?;
265
266 let mut iter = val.char_indices().peekable();
267 while let Some((i, c)) = iter.next() {
268 match c {
269 '#' if i == 0 => write!(f, "\\#")?,
270 ' ' if i == 0 || iter.peek().is_none() => write!(f, "\\ ")?,
271 '"' | '+' | ',' | ';' | '<' | '>' | '\\' => write!(f, "\\{}", c)?,
272 '\x00'..='\x1f' | '\x7f' => write!(f, "\\{:02x}", c as u8)?,
273 _ => f.write_char(c)?,
274 }
275 }
276 } else {
277 let value = self.value.to_der().or(Err(fmt::Error))?;
278
279 write!(f, "{}=#", self.oid)?;
280 for c in value {
281 write!(f, "{:02x}", c)?;
282 }
283 }
284
285 Ok(())
286 }
287}
288
289trait ShortestName {
291 fn shortest_name_by_oid(&self, oid: &ObjectIdentifier) -> Option<&str>;
292}
293
294impl<'a> ShortestName for Database<'a> {
295 fn shortest_name_by_oid(&self, oid: &ObjectIdentifier) -> Option<&'a str> {
296 let mut best_match: Option<&'a str> = None;
297
298 for m in self.find_names_for_oid(*oid) {
299 if let Some(previous) = best_match {
300 if m.len() < previous.len() {
301 best_match = Some(m);
302 }
303 } else {
304 best_match = Some(m);
305 }
306 }
307
308 best_match
309 }
310}