Skip to content

Commit 186f558

Browse files
committed
der: clarify draft
der: more clarify
1 parent ee9dd86 commit 186f558

File tree

6 files changed

+428
-1
lines changed

6 files changed

+428
-1
lines changed

der/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ pem-rfc7468 = { version = "1.0.0-rc.3", optional = true, features = ["alloc"] }
2626
time = { version = "0.3.4", optional = true, default-features = false }
2727
zeroize = { version = "1.8", optional = true, default-features = false }
2828
heapless = { version = "0.8", optional = true, default-features = false }
29+
tynm = { version = "0.2", optional = true, default-features = false }
2930

3031
[dev-dependencies]
3132
hex-literal = "1"
3233
proptest = "1"
3334

3435
[features]
36+
default = ["clarify"]
3537
alloc = ["zeroize?/alloc"]
3638
std = ["alloc"]
3739

@@ -42,6 +44,7 @@ derive = ["dep:der_derive"]
4244
oid = ["dep:const-oid"]
4345
pem = ["dep:pem-rfc7468", "alloc", "zeroize"]
4446
real = []
47+
clarify = ["std", "pem", "dep:tynm"]
4548

4649
[package.metadata.docs.rs]
4750
all-features = true

der/src/writer.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Writer trait.
22
3+
#[cfg(feature = "clarify")]
4+
pub(crate) mod clarify;
35
#[cfg(feature = "pem")]
46
pub(crate) mod pem;
57
pub(crate) mod slice;
@@ -9,6 +11,9 @@ use crate::Result;
911
#[cfg(feature = "std")]
1012
use std::io;
1113

14+
#[cfg(feature = "clarify")]
15+
use crate::Tag;
16+
1217
/// Writer trait which outputs encoded DER.
1318
pub trait Writer {
1419
/// Write the given DER-encoded bytes as output.
@@ -18,6 +23,48 @@ pub trait Writer {
1823
fn write_byte(&mut self, byte: u8) -> Result<()> {
1924
self.write(&[byte])
2025
}
26+
27+
#[cfg(feature = "clarify")]
28+
/// Should return true for clarify writers
29+
fn is_clarify(&self) -> bool {
30+
false
31+
}
32+
33+
#[cfg(feature = "clarify")]
34+
/// Called when starting next TLV value
35+
fn clarify_start_value_type<T>(&mut self) {
36+
// can be overrided
37+
}
38+
39+
#[cfg(feature = "clarify")]
40+
/// Called when ending next TLV value
41+
fn clarify_end_value_type<T>(&mut self) {
42+
// can be overrided
43+
}
44+
45+
#[cfg(feature = "clarify")]
46+
/// Called when starting next TLV tag
47+
fn clarify_start_tag(&mut self, _tag: &Tag) {
48+
// can be overrided
49+
}
50+
51+
#[cfg(feature = "clarify")]
52+
/// Called when ending next TLV tag
53+
fn clarify_end_tag(&mut self, _tag: &Tag) {
54+
// can be overrided
55+
}
56+
57+
#[cfg(feature = "clarify")]
58+
/// Called when writing field with name
59+
fn clarify_field_name(&mut self, _field_name: &str) {
60+
// can be overrided
61+
}
62+
63+
#[cfg(feature = "clarify")]
64+
// Called when writing choice, e.g. enum name: "DnsName"
65+
fn clarify_choice(&mut self, _choice_name: &[u8]) {
66+
// can be overrided
67+
}
2168
}
2269

2370
#[cfg(feature = "std")]

der/src/writer/clarify.rs

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
pub(crate) mod commentwriter;
2+
pub(crate) mod hexdisplaylines;
3+
4+
use crate::std::io::Write;
5+
use crate::{Encode, SliceWriter};
6+
use crate::{EncodeValue, ErrorKind, Header, Length, Result, Tag, TagMode, TagNumber, Tagged};
7+
use commentwriter::{CommentWriter, JavaCommentWriter, XmlCommentWriter};
8+
use core::cell::RefCell;
9+
use core::ops::DerefMut;
10+
use hexdisplaylines::HexDisplayLines;
11+
use std::borrow::Cow;
12+
use std::string::String;
13+
use std::{boxed::Box, rc::Rc, vec::Vec};
14+
15+
use super::Writer;
16+
17+
static INDENT_STR: &str =
18+
"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
19+
20+
/// [`Writer`] which encodes DER as hex with comments.
21+
pub struct ClarifySliceWriter<'a> {
22+
writer: SliceWriter<'a>,
23+
24+
// Buffer into which debug HEX and comments are written
25+
debug_ref: Rc<RefCell<Vec<u8>>>,
26+
27+
/// Used for debug indentation
28+
depth: Vec<u32>,
29+
30+
indent_enabled: bool,
31+
comment_writer: Box<dyn CommentWriter>,
32+
}
33+
34+
/// Returned by .finish()
35+
pub struct FinishOutputs<'a> {
36+
pub raw: Result<&'a [u8]>,
37+
//pub debug_ref: Vec<u8>,
38+
}
39+
40+
impl<'a> ClarifySliceWriter<'a> {
41+
/// Create a new encoder with the given byte slice as a backing buffer.
42+
pub fn new(bytes: &'a mut [u8], debug_ref: Rc<RefCell<Vec<u8>>>, comment_xml: bool) -> Self {
43+
Self {
44+
writer: SliceWriter::new(bytes),
45+
debug_ref,
46+
depth: Vec::new(),
47+
indent_enabled: true,
48+
comment_writer: if comment_xml {
49+
Box::new(XmlCommentWriter::default())
50+
} else {
51+
Box::new(JavaCommentWriter::default())
52+
},
53+
}
54+
}
55+
56+
// /// Encode a value which impls the [`Encode`] trait.
57+
// pub fn encode<T: Encode>(&mut self, encodable: &T) -> Result<()> {
58+
// self.writer.encode(encodable)
59+
// }
60+
61+
// /// Return an error with the given [`ErrorKind`], annotating it with
62+
// /// context about where the error occurred.
63+
// pub fn error<T>(&mut self, kind: ErrorKind) -> Result<T> {
64+
// self.writer.error(kind)
65+
// }
66+
67+
// /// Did the decoding operation fail due to an error?
68+
// pub fn is_failed(&self) -> bool {
69+
// self.writer.is_failed()
70+
// }
71+
72+
// /// Finish encoding to the buffer, returning a slice containing the data
73+
// /// written to the buffer.
74+
// pub fn finish_internal(&self) -> Result<&'a [u8]> {
75+
// self.writer.finish()
76+
// }
77+
78+
/// Finish encoding to the buffer, returning a slice containing the data
79+
/// written to the buffer.
80+
pub fn finish(self) -> FinishOutputs<'a> {
81+
FinishOutputs {
82+
raw: self.writer.finish(),
83+
//debug_buf: self.debug.expect("debug buf not taken"),
84+
}
85+
}
86+
87+
// /// Encode a `CONTEXT-SPECIFIC` field with the provided tag number and mode.
88+
// pub fn context_specific<T>(
89+
// &mut self,
90+
// tag_number: TagNumber,
91+
// tag_mode: TagMode,
92+
// value: &T,
93+
// ) -> Result<()>
94+
// where
95+
// T: EncodeValue + Tagged,
96+
// {
97+
// self.writer.context_specific(tag_number, tag_mode, value)
98+
// }
99+
100+
// /// Encode an ASN.1 `SEQUENCE` of the given length.
101+
// ///
102+
// /// Spawns a nested slice writer which is expected to be exactly the
103+
// /// specified length upon completion.
104+
// pub fn sequence<F>(&mut self, length: Length, f: F) -> Result<()>
105+
// where
106+
// F: FnOnce(&mut DebugSliceWriter<'_>) -> Result<()>,
107+
// {
108+
// Header::new(Tag::Sequence, length).and_then(|header| header.encode(self))?;
109+
110+
// let debug_ref = self.debug_ref.clone();
111+
// let mut nested_encoder = DebugSliceWriter::new(self.reserve(length)?, debug_ref, true);
112+
// f(&mut nested_encoder)?;
113+
114+
// let nresult: FinishOutputs<'_> = nested_encoder.finish();
115+
// if nresult.raw?.len() == usize::try_from(length)? {
116+
// Ok(())
117+
// } else {
118+
// self.error(ErrorKind::Length { tag: Tag::Sequence })
119+
// }
120+
// }
121+
/// Returns indentation, for example "\n\t" for depth == 1
122+
pub fn indent_str(&self) -> &'static str {
123+
let ilen = self.depth.len() * 1;
124+
let ilen = ilen.min(INDENT_STR.len());
125+
&INDENT_STR[..ilen]
126+
}
127+
128+
/// Writes indentation to debug output, for example "\n\t" for depth == 1
129+
pub fn write_clarify_indent(&mut self) {
130+
let indent = self.indent_str();
131+
let mut debugbuf = self.debug_ref.borrow_mut();
132+
{
133+
self.comment_writer
134+
.before_new_line(&mut debugbuf.deref_mut());
135+
write!(debugbuf, "\n{}", indent).unwrap();
136+
}
137+
}
138+
139+
/// Writes hex bytes to debug output, for example "30 04 "
140+
pub fn write_clarify_hex(&mut self, slice: &[u8]) {
141+
let indent = self.indent_str();
142+
let mut debugbuf = self.debug_ref.borrow_mut();
143+
{
144+
write!(debugbuf, "{}", HexDisplayLines(&slice, indent)).unwrap();
145+
}
146+
}
147+
148+
/// Writes string to debug output, for example a comment "// SEQUENCE"
149+
pub fn write_clarify_str(&mut self, s: &str) {
150+
let mut debugbuf = self.debug_ref.borrow_mut();
151+
{
152+
write!(debugbuf, "{}", s).unwrap();
153+
}
154+
}
155+
/// Writes string to debug output, for example a comment: `// SEQUENCE: name`
156+
pub fn write_clarify_type_str(&mut self, start_end: &str, type_name: &str) {
157+
//let mut debugbuf = self.debug_ref.borrow_mut();
158+
159+
let comment = format!("{}: {} ", start_end, type_name);
160+
self.comment_writer.comment(&comment);
161+
}
162+
163+
/// Writes string to debug output, for example a comment: `// "abc"`
164+
pub fn write_clarify_value_quote(&mut self, type_name: &str, value: &[u8]) {
165+
//let mut debugbuf = self.debug_ref.borrow_mut();
166+
167+
let contains_control = value.iter().any(|&c| c == 0x7F || (c < 0x20 && c != b'\n'));
168+
if value.len() > 2 && !contains_control {
169+
let type_name = strip_transparent_types(type_name);
170+
let comment = format!("{} {:?} ", type_name, String::from_utf8_lossy(value));
171+
self.comment_writer.comment(&comment);
172+
}
173+
}
174+
175+
/// Writes int to debug output, for example a comment: `// integer: 16dec`
176+
pub fn write_clarify_int(&mut self, value: i64) {
177+
if value >= 10 || value < 0 {
178+
let comment = format!("integer: {value}dec ");
179+
self.comment_writer.comment(&comment);
180+
}
181+
}
182+
183+
/// Reserve a portion of the internal buffer, updating the internal cursor
184+
/// position and returning a mutable slice.
185+
fn reserve(&mut self, len: impl TryInto<Length>) -> Result<&mut [u8]> {
186+
self.writer.reserve(len)
187+
}
188+
189+
fn clarify_start_value_type_str(&mut self, type_name: &str) {
190+
self.indent_enabled = true;
191+
self.depth.push(u32::from(self.writer.position()));
192+
193+
let type_name = strip_transparent_types(type_name);
194+
self.write_clarify_type_str("type", &type_name);
195+
}
196+
197+
fn clarify_end_value_type_str(&mut self, type_name: &str) {
198+
let current = u32::from(self.writer.position());
199+
let last_pos = self.depth.pop().unwrap_or(current);
200+
let diff = current - last_pos;
201+
202+
if diff > 16 {
203+
let type_name = strip_transparent_types(type_name);
204+
self.write_clarify_indent();
205+
self.write_clarify_type_str("end", type_name.as_ref());
206+
}
207+
}
208+
}
209+
210+
impl<'a> Writer for ClarifySliceWriter<'a> {
211+
fn write(&mut self, slice: &[u8]) -> Result<()> {
212+
self.reserve(slice.len())?.copy_from_slice(slice);
213+
214+
if self.indent_enabled {
215+
self.write_clarify_indent();
216+
}
217+
218+
self.write_clarify_hex(slice);
219+
Ok(())
220+
}
221+
222+
fn clarify_start_value_type<T>(&mut self) {
223+
self.clarify_start_value_type_str(&tynm::type_name::<T>());
224+
}
225+
fn clarify_end_value_type<T>(&mut self) {
226+
self.clarify_end_value_type_str(&tynm::type_name::<T>());
227+
}
228+
229+
/// for better tag-length pretty-printing inline
230+
fn clarify_end_tag(&mut self, _tag: &Tag) {
231+
// just to print a single length byte without indent
232+
self.indent_enabled = false;
233+
}
234+
235+
// fn debug_set_indent_enabled(&mut self, enabled: bool) {
236+
// if !enabled {
237+
// // Write tabs before we switch to in-line mode
238+
// self.write_debug_indent();
239+
// }
240+
// self.indent_enabled = enabled;
241+
// }
242+
243+
fn clarify_field_name(&mut self, field_name: &str) {
244+
self.write_clarify_indent();
245+
self.write_clarify_type_str("field", field_name);
246+
}
247+
248+
// fn clarify_end_length(&mut self, tag: Option<&Tag>, length: Length) {
249+
// self.indent_enabled = true;
250+
// if let Some(tag) = tag {
251+
// self.write_debug_type_str("tag", &format!("{}", tag));
252+
// }
253+
// if u32::from(length) >= 10 {
254+
// self.write_debug_type_str("len", &format!("{}", length));
255+
// }
256+
// }
257+
258+
// fn clarify_value_quote(&mut self, _type_name: &str, tag_name: &[u8]) {
259+
// //self.write_debug_value_quote(type_name, tag_name);
260+
// self.write_debug_value_quote("", tag_name);
261+
// }
262+
263+
// fn debug_int(&mut self, value: i64) {
264+
// self.write_debug_int(value);
265+
// }
266+
267+
fn clarify_choice(&mut self, choice_name: &[u8]) {
268+
self.write_clarify_indent();
269+
if let Ok(choice_name) = std::str::from_utf8(choice_name) {
270+
self.write_clarify_type_str("CHOICE", choice_name);
271+
}
272+
}
273+
274+
fn is_clarify(&self) -> bool {
275+
true
276+
}
277+
}
278+
279+
fn strip_transparent_types(type_name: &str) -> Cow<'_, str> {
280+
// EncodeValueRef is commonly used and it is completely transparent
281+
let type_name = if let Some(stripped) = type_name.strip_prefix("EncodeValueRef<") {
282+
let stripped = stripped.strip_suffix(">").unwrap_or(stripped);
283+
stripped
284+
} else {
285+
type_name
286+
};
287+
288+
let type_name = if let Some(stripped) = type_name.strip_prefix("ApplicationRef<") {
289+
let stripped = stripped.strip_suffix(">").unwrap_or(stripped);
290+
stripped
291+
} else {
292+
type_name
293+
};
294+
295+
let type_name = if let Some(stripped) = type_name.strip_prefix("ContextSpecificRef<") {
296+
let stripped = stripped.strip_suffix(">").unwrap_or(stripped);
297+
stripped
298+
} else {
299+
type_name
300+
};
301+
302+
Cow::Borrowed(type_name)
303+
}

0 commit comments

Comments
 (0)