1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
//! CMS content envelopes, preserving original elements and producing readers.
use crate::{Error, ErrorCode, Octets, Result};
use structured_asn1::{Element, Oid, Reader, Span, Tag, universal};
/// The CMS `ContentInfo` envelope (RFC 5652 §3).
///
/// Its explicit content field is mandatory. Detached `SignedData` instead omits
/// the inner [`Encapsulated`] `eContent` (RFC 5652 §5.2). This type does not use
/// the older PKCS#7 optional outer-content grammar from RFC 2315 §7.
#[derive(Debug)]
pub struct ContentInfo<'a> {
kind: Oid<'a>,
content: Element<'a>,
span: Span,
}
impl<'a> ContentInfo<'a> {
/// Reads one complete envelope, refusing bytes after that envelope.
///
/// The returned reader produced the content element and retains the same
/// parser budget, execution account, diagnostic path and inherited depth.
/// It is used directly by the selected content schema; no encode/reparse
/// adapter or replacement parser is created.
pub fn read(reader: &mut Reader<'a>) -> Result<(Self, Reader<'a>)> {
let outer = reader
.expect(Tag::constructed(universal::SEQUENCE))
.map_err(|error| Error::encoding(error, "contentInfo"))?;
let result = Self::read_element(reader, &outer)?;
if !reader.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
"contentInfo",
reader.offset(),
));
}
Ok(result)
}
/// Reads an embedded envelope from its original producing parent.
///
/// Unlike [`Self::read`], surrounding siblings belong to the enclosing
/// schema and are not refused. Envelope components themselves remain exact.
pub fn read_element(parent: &Reader<'a>, element: &Element<'a>) -> Result<(Self, Reader<'a>)> {
const FIELD: &str = "contentInfo";
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
FIELD,
element.span().start,
));
}
let mut inside = parent
.enter(element)
.map_err(|error| Error::encoding(error, FIELD))?;
let kind = inside
.read_oid()
.map_err(|error| Error::encoding(error, "contentInfo.contentType"))?;
// RFC5652 §§3/12.1 require outer content (no OPTIONAL), unlike PKCS#7
// RFC2315 §7. Detached CMS omits only EncapsulatedContentInfo.eContent
// (§5.2); it does not omit this wrapper or its single inner value.
// https://www.rfc-editor.org/rfc/rfc5652#section-3
let wrapper = inside
.expect(Tag::context(true, 0))
.map_err(|error| Error::encoding(error, "contentInfo.content"))?;
let mut explicit = inside
.enter(&wrapper)
.map_err(|error| Error::encoding(error, "contentInfo.content"))?;
let content = explicit
.next_element()
.map_err(|error| Error::encoding(error, "contentInfo.content"))?
.ok_or_else(|| {
Error::schema(
ErrorCode::MissingField,
"contentInfo.content",
wrapper.span().start,
)
})?;
if !explicit.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
"contentInfo.content",
explicit.offset(),
));
}
if !inside.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
FIELD,
inside.offset(),
));
}
Ok((
Self {
kind,
content,
span: element.span(),
},
explicit,
))
}
/// The original content type identifier, without an application interpretation.
#[must_use]
pub fn kind(&self) -> Oid<'a> {
self.kind
}
/// The original embedded content element, including its encoding.
#[must_use]
pub fn content(&self) -> &Element<'a> {
&self.content
}
/// The envelope's original source range.
#[must_use]
pub fn span(&self) -> Span {
self.span
}
}
/// Attached or absent encapsulated content (RFC 5652 §5.2).
#[derive(Debug)]
pub struct Encapsulated<'a> {
kind: Oid<'a>,
kind_encoding: &'a [u8],
content: Option<Octets<'a>>,
span: Span,
}
impl<'a> Encapsulated<'a> {
/// Reads type and optional explicit OCTET STRING under the original parent.
///
/// Absence denotes detached content. Empty attached content is a distinct
/// value, and no caller-supplied payload silently replaces attached bytes.
pub fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
const FIELD: &str = "signedData.encapContentInfo";
const KIND: &str = "signedData.encapContentInfo.eContentType";
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
FIELD,
element.span().start,
));
}
let mut inside = parent
.enter(element)
.map_err(|error| Error::encoding(error, FIELD))?;
let kind_element = inside
.expect(Tag::primitive(universal::OID))
.map_err(|error| Error::encoding(error, KIND))?;
let kind = inside
.oid(&kind_element)
.map_err(|error| Error::encoding(error, KIND))?;
let content = if let Some(wrapper) = inside
.next_element()
.map_err(|error| Error::encoding(error, FIELD))?
{
const CONTENT: &str = "signedData.encapContentInfo.eContent";
if wrapper.tag() != Tag::context(true, 0) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
CONTENT,
wrapper.span().start,
));
}
let mut explicit = inside
.enter(&wrapper)
.map_err(|error| Error::encoding(error, CONTENT))?;
let octets = explicit
.next_element()
.map_err(|error| Error::encoding(error, CONTENT))?
.ok_or_else(|| {
Error::schema(ErrorCode::MissingField, CONTENT, wrapper.span().start)
})?;
let content = Octets::read(&explicit, &octets, CONTENT)?;
if !explicit.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
CONTENT,
explicit.offset(),
));
}
Some(content)
} else {
None
};
if !inside.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
FIELD,
inside.offset(),
));
}
Ok(Self {
kind,
kind_encoding: kind_element.as_bytes(),
content,
span: element.span(),
})
}
/// The original content type identifier.
#[must_use]
pub fn kind(&self) -> Oid<'a> {
self.kind
}
/// The original `eContentType` encoding, including its tag and length octets.
#[must_use]
pub fn kind_encoding(&self) -> &'a [u8] {
self.kind_encoding
}
/// Attached logical octets, or `None` for detached content.
#[must_use]
pub fn content(&self) -> Option<&Octets<'a>> {
self.content.as_ref()
}
/// Whether the envelope omitted the content field.
#[must_use]
pub fn is_detached(&self) -> bool {
self.content.is_none()
}
/// The encapsulated content-info's original range.
#[must_use]
pub fn span(&self) -> Span {
self.span
}
}