1use super::{Attribute, Encoding, Error, Name, OwnedString, Result};
4use structured_asn1::memory::Memory;
5
6impl Name<'_> {
7 pub fn rfc4514(&self, execution: &Memory) -> Result<OwnedString> {
12 render(self, execution, false)
13 }
14
15 pub fn rfc4514_readable(&self, execution: &Memory) -> Result<OwnedString> {
22 render(self, execution, true)
23 }
24}
25
26fn render(name: &Name<'_>, execution: &Memory, readable: bool) -> Result<OwnedString> {
27 let fail = |reason| Error::resource(reason, name.field, name.offset);
28 let mut output = execution.string();
29 execution
30 .charge_work(name.parts.len() as u64)
31 .map_err(fail)?;
32 for (part_at, part) in name.parts.iter().rev().enumerate() {
33 if part_at != 0 {
34 output.try_push(',').map_err(fail)?;
35 }
36 execution
37 .charge_work(part.attributes.len() as u64)
38 .map_err(fail)?;
39 for (at, attribute) in part.attributes.iter().enumerate() {
40 if at != 0 {
41 output.try_push('+').map_err(fail)?;
42 }
43 execution
44 .charge_work(
45 (attribute.kind.as_bytes().len() as u64)
46 .checked_mul(super::selector::REGISTERED_DESCRIPTORS as u64)
47 .ok_or_else(|| fail(structured_asn1::memory::MemoryError::WorkExceeded))?,
48 )
49 .map_err(fail)?;
50 let descriptor = super::selector::registered_name(attribute.kind);
51 if let Some(descriptor) = descriptor {
52 output.try_push_str(descriptor).map_err(fail)?;
53 } else {
54 let oid = attribute.kind.text(execution).map_err(fail)?;
55 output
56 .try_push_str(oid.as_str(execution).map_err(fail)?)
57 .map_err(fail)?;
58 }
59 output.try_push('=').map_err(fail)?;
60 if readable && descriptor.is_some() && text(*attribute, &mut output, execution)? {
65 continue;
66 }
67 output.try_push('#').map_err(fail)?;
68 execution
69 .charge_work(attribute.value.der.len() as u64)
70 .map_err(fail)?;
71 for &byte in attribute.value.der {
72 hex(byte, &mut output).map_err(fail)?;
73 }
74 }
75 }
76 Ok(output)
77}
78
79fn text(attribute: Attribute<'_>, output: &mut OwnedString, execution: &Memory) -> Result<bool> {
80 let value = attribute.value;
81 let fail = |reason| Error::resource(reason, "name.attribute", value.offset);
82 let work = (attribute.kind.as_bytes().len() as u64)
83 .checked_mul(64)
84 .and_then(|kind| {
85 (value.contents.len() as u64)
86 .checked_mul(6)
87 .and_then(|n| kind.checked_add(n))
88 })
89 .ok_or_else(|| fail(structured_asn1::memory::MemoryError::WorkExceeded))?;
90 execution.charge_work(work).map_err(fail)?;
91 if !super::permits(attribute.kind, value.encoding())
94 || !super::well_formed(&attribute)
95 || value.validated_count(|_| true).is_none()
96 || value.encoding() == Encoding::Other
97 {
98 return Ok(false);
99 }
100 let mut characters = value.decoded_chars().peekable();
102 let mut first = true;
103 while let Some(character) = characters.next() {
104 let last = characters.peek().is_none();
105 if character == '\0' || character.is_control() {
106 let mut bytes = [0; 4];
107 for &byte in character.encode_utf8(&mut bytes).as_bytes() {
108 output.try_push('\\').map_err(fail)?;
109 hex(byte, output).map_err(fail)?;
110 }
111 } else {
112 if matches!(character, '"' | '+' | ',' | ';' | '<' | '=' | '>' | '\\')
115 || (first && matches!(character, ' ' | '#'))
116 || (last && character == ' ')
117 {
118 output.try_push('\\').map_err(fail)?;
119 }
120 output.try_push(character).map_err(fail)?;
121 }
122 first = false;
123 }
124 Ok(true)
125}
126
127fn hex(
128 byte: u8,
129 output: &mut OwnedString,
130) -> core::result::Result<(), structured_asn1::memory::MemoryError> {
131 const HEX: &[u8; 16] = b"0123456789ABCDEF";
132 output.try_push(char::from(HEX[usize::from(byte >> 4)]))?;
133 output.try_push(char::from(HEX[usize::from(byte & 15)]))
134}
135
136#[cfg(test)]
138mod tests {
139 use crate::name::Selector;
140 use crate::{fixtures, name, oid};
141 use structured_asn1::Parser;
142
143 #[test]
144 fn additional_registered_descriptors_render_complete_text() {
145 let memory = fixtures::memory();
148 let parser = Parser::der(memory.clone());
149 for (kind, label, tag, value) in [
150 (oid::SERIAL_NUMBER, "serialNumber", 0x13, "ABC"),
151 (oid::SURNAME, "surname", 0x0c, "Doe"),
152 (oid::GIVEN_NAME, "givenName", 0x0c, "Alice"),
153 (oid::INITIALS, "initials", 0x0c, "AB"),
154 (
155 oid::GENERATION_QUALIFIER,
156 "generationQualifier",
157 0x0c,
158 "III",
159 ),
160 (oid::TITLE, "title", 0x0c, "Engineer"),
161 (oid::DN_QUALIFIER, "dnQualifier", 0x13, "ABC"),
162 (
163 oid::EMAIL_ADDRESS,
164 "emailAddress",
165 0x16,
166 "alice@example.com",
167 ),
168 (oid::PSEUDONYM, "pseudonym", 0x0c, "Alice"),
169 ] {
170 let bytes = fixtures::seq(&[fixtures::set(&[fixtures::seq(&[
171 fixtures::oid(kind),
172 fixtures::tlv(tag, value.as_bytes()),
173 ])])]);
174 let mut reader = parser.read(&bytes, 0).unwrap();
175 let element = reader.next_element().unwrap().unwrap();
176 let name = name::read(&reader, &element, "name").unwrap();
177 let output = name.rfc4514_readable(&memory).unwrap();
178 let rendered = output.as_str(&memory).unwrap();
179 assert_eq!(rendered, alloc::format!("{label}={value}"));
180 assert!(
181 Selector::parse(rendered, &memory)
182 .unwrap()
183 .matches_name(&name, &memory)
184 .unwrap()
185 );
186 }
187 let bytes = fixtures::seq(&[fixtures::set(&[fixtures::seq(&[
188 fixtures::oid(oid::ORGANIZATION_IDENTIFIER),
189 fixtures::utf8("ABC"),
190 ])])]);
191 let mut reader = parser.read(&bytes, 0).unwrap();
192 let element = reader.next_element().unwrap().unwrap();
193 let name = name::read(&reader, &element, "name").unwrap();
194 assert_eq!(
195 name.rfc4514_readable(&memory)
196 .unwrap()
197 .as_str(&memory)
198 .unwrap(),
199 "2.5.4.97=#0C03414243"
200 );
201 }
202
203 #[test]
204 fn reversible_and_readable_names_match_without_changing_original_tags() {
205 let memory = fixtures::memory();
208 let parser = Parser::der(memory.clone());
209 let bytes = fixtures::seq(&[
210 fixtures::set(&[fixtures::seq(&[
211 fixtures::oid(oid::COUNTRY),
212 fixtures::tlv(0x13, b"UA"),
213 ])]),
214 fixtures::set(&[fixtures::seq(&[
215 fixtures::oid(oid::COMMON_NAME),
216 fixtures::utf8("A,+B=\\C"),
217 ])]),
218 ]);
219 let mut reader = parser.read(&bytes, 0).unwrap();
220 let element = reader.next_element().unwrap().unwrap();
221 let name = name::read(&reader, &element, "name").unwrap();
222 let exact = name.rfc4514(&memory).unwrap();
223 assert_eq!(
224 exact.as_str(&memory).unwrap(),
225 "CN=#0C07412C2B423D5C43,C=#13025541"
226 );
227 let readable = name.rfc4514_readable(&memory).unwrap();
228 assert_eq!(
229 readable.as_str(&memory).unwrap(),
230 "CN=A\\,\\+B\\=\\\\C,C=UA"
231 );
232 for text in [&exact, &readable] {
233 let selected = Selector::parse(text.as_str(&memory).unwrap(), &memory).unwrap();
234 assert!(selected.matches_name(&name, &memory).unwrap());
235 }
236 }
237
238 #[test]
239 fn readable_output_never_uses_the_display_truncation_ceiling() {
240 let memory = fixtures::memory();
244 let parser = Parser::der(memory.clone());
245 let text = "a".repeat(40_000);
246 let bytes = fixtures::seq(&[fixtures::set(&[fixtures::seq(&[
247 fixtures::oid(oid::USER_ID),
248 fixtures::utf8(&text),
249 ])])]);
250 let mut reader = parser.read(&bytes, 0).unwrap();
251 let element = reader.next_element().unwrap().unwrap();
252 let name = name::read(&reader, &element, "name").unwrap();
253 let output = name.rfc4514_readable(&memory).unwrap();
254 let rendered = output.as_str(&memory).unwrap();
255 assert_eq!(rendered, alloc::format!("UID={text}"));
256 let exact = name.rfc4514(&memory).unwrap();
257 let selector = Selector::parse(exact.as_str(&memory).unwrap(), &memory).unwrap();
258 assert!(selector.matches_name(&name, &memory).unwrap());
259 }
260}