1 //! ASN.1 DER-encoded documents stored on the heap.
2
3 use crate::{Decode, Encode, Error, FixedTag, Length, Reader, Result, SliceReader, Tag, Writer};
4 use alloc::vec::Vec;
5 use core::fmt::{self, Debug};
6
7 #[cfg(feature = "pem")]
8 use {crate::pem, alloc::string::String};
9
10 #[cfg(feature = "std")]
11 use std::{fs, path::Path};
12
13 #[cfg(all(feature = "pem", feature = "std"))]
14 use alloc::borrow::ToOwned;
15
16 #[cfg(feature = "zeroize")]
17 use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
18
19 /// ASN.1 DER-encoded document.
20 ///
21 /// This type wraps an encoded ASN.1 DER message. The document checked to
22 /// ensure it contains a valid DER-encoded `SEQUENCE`.
23 ///
24 /// It implements common functionality related to encoding/decoding such
25 /// documents, such as PEM encapsulation as well as reading/writing documents
26 /// from/to the filesystem.
27 ///
28 /// The [`SecretDocument`] provides a wrapper for this type with additional
29 /// hardening applied.
30 #[derive(Clone, Eq, PartialEq)]
31 pub struct Document {
32 /// ASN.1 DER encoded bytes.
33 der_bytes: Vec<u8>,
34
35 /// Length of this document.
36 length: Length,
37 }
38
39 impl Document {
40 /// Get the ASN.1 DER-encoded bytes of this document.
as_bytes(&self) -> &[u8]41 pub fn as_bytes(&self) -> &[u8] {
42 self.der_bytes.as_slice()
43 }
44
45 /// Convert to a [`SecretDocument`].
46 #[cfg(feature = "zeroize")]
into_secret(self) -> SecretDocument47 pub fn into_secret(self) -> SecretDocument {
48 SecretDocument(self)
49 }
50
51 /// Convert to an ASN.1 DER-encoded byte vector.
into_vec(self) -> Vec<u8>52 pub fn into_vec(self) -> Vec<u8> {
53 self.der_bytes
54 }
55
56 /// Return an ASN.1 DER-encoded byte vector.
to_vec(&self) -> Vec<u8>57 pub fn to_vec(&self) -> Vec<u8> {
58 self.der_bytes.clone()
59 }
60
61 /// Get the length of the encoded ASN.1 DER in bytes.
len(&self) -> Length62 pub fn len(&self) -> Length {
63 self.length
64 }
65
66 /// Try to decode the inner ASN.1 DER message contained in this
67 /// [`Document`] as the given type.
decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T>68 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T> {
69 T::from_der(self.as_bytes())
70 }
71
72 /// Encode the provided type as ASN.1 DER, storing the resulting encoded DER
73 /// as a [`Document`].
encode_msg<T: Encode>(msg: &T) -> Result<Self>74 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self> {
75 msg.to_der()?.try_into()
76 }
77
78 /// Decode ASN.1 DER document from PEM.
79 ///
80 /// Returns the PEM label and decoded [`Document`] on success.
81 #[cfg(feature = "pem")]
from_pem(pem: &str) -> Result<(&str, Self)>82 pub fn from_pem(pem: &str) -> Result<(&str, Self)> {
83 let (label, der_bytes) = pem::decode_vec(pem.as_bytes())?;
84 Ok((label, der_bytes.try_into()?))
85 }
86
87 /// Encode ASN.1 DER document as a PEM string with encapsulation boundaries
88 /// containing the provided PEM type `label` (e.g. `CERTIFICATE`).
89 #[cfg(feature = "pem")]
to_pem(&self, label: &'static str, line_ending: pem::LineEnding) -> Result<String>90 pub fn to_pem(&self, label: &'static str, line_ending: pem::LineEnding) -> Result<String> {
91 Ok(pem::encode_string(label, line_ending, self.as_bytes())?)
92 }
93
94 /// Read ASN.1 DER document from a file.
95 #[cfg(feature = "std")]
read_der_file(path: impl AsRef<Path>) -> Result<Self>96 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> {
97 fs::read(path)?.try_into()
98 }
99
100 /// Write ASN.1 DER document to a file.
101 #[cfg(feature = "std")]
write_der_file(&self, path: impl AsRef<Path>) -> Result<()>102 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
103 Ok(fs::write(path, self.as_bytes())?)
104 }
105
106 /// Read PEM-encoded ASN.1 DER document from a file.
107 #[cfg(all(feature = "pem", feature = "std"))]
read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self)>108 pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self)> {
109 Self::from_pem(&fs::read_to_string(path)?).map(|(label, doc)| (label.to_owned(), doc))
110 }
111
112 /// Write PEM-encoded ASN.1 DER document to a file.
113 #[cfg(all(feature = "pem", feature = "std"))]
write_pem_file( &self, path: impl AsRef<Path>, label: &'static str, line_ending: pem::LineEnding, ) -> Result<()>114 pub fn write_pem_file(
115 &self,
116 path: impl AsRef<Path>,
117 label: &'static str,
118 line_ending: pem::LineEnding,
119 ) -> Result<()> {
120 let pem = self.to_pem(label, line_ending)?;
121 Ok(fs::write(path, pem.as_bytes())?)
122 }
123 }
124
125 impl AsRef<[u8]> for Document {
as_ref(&self) -> &[u8]126 fn as_ref(&self) -> &[u8] {
127 self.as_bytes()
128 }
129 }
130
131 impl Debug for Document {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 f.write_str("Document(")?;
134
135 for byte in self.as_bytes() {
136 write!(f, "{:02X}", byte)?;
137 }
138
139 f.write_str(")")
140 }
141 }
142
143 impl<'a> Decode<'a> for Document {
decode<R: Reader<'a>>(reader: &mut R) -> Result<Document>144 fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Document> {
145 let header = reader.peek_header()?;
146 let length = (header.encoded_len()? + header.length)?;
147 let bytes = reader.read_slice(length)?;
148
149 Ok(Self {
150 der_bytes: bytes.into(),
151 length,
152 })
153 }
154 }
155
156 impl Encode for Document {
encoded_len(&self) -> Result<Length>157 fn encoded_len(&self) -> Result<Length> {
158 Ok(self.len())
159 }
160
encode(&self, writer: &mut impl Writer) -> Result<()>161 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
162 writer.write(self.as_bytes())
163 }
164 }
165
166 impl FixedTag for Document {
167 const TAG: Tag = Tag::Sequence;
168 }
169
170 impl TryFrom<&[u8]> for Document {
171 type Error = Error;
172
try_from(der_bytes: &[u8]) -> Result<Self>173 fn try_from(der_bytes: &[u8]) -> Result<Self> {
174 Self::from_der(der_bytes)
175 }
176 }
177
178 impl TryFrom<Vec<u8>> for Document {
179 type Error = Error;
180
try_from(der_bytes: Vec<u8>) -> Result<Self>181 fn try_from(der_bytes: Vec<u8>) -> Result<Self> {
182 let mut decoder = SliceReader::new(&der_bytes)?;
183 decode_sequence(&mut decoder)?;
184 decoder.finish(())?;
185
186 let length = der_bytes.len().try_into()?;
187 Ok(Self { der_bytes, length })
188 }
189 }
190
191 /// Secret [`Document`] type.
192 ///
193 /// Useful for formats which represent potentially secret data, such as
194 /// cryptographic keys.
195 ///
196 /// This type provides additional hardening such as ensuring that the contents
197 /// are zeroized-on-drop, and also using more restrictive file permissions when
198 /// writing files to disk.
199 #[cfg(feature = "zeroize")]
200 #[derive(Clone)]
201 pub struct SecretDocument(Document);
202
203 #[cfg(feature = "zeroize")]
204 impl SecretDocument {
205 /// Borrow the inner serialized bytes of this document.
as_bytes(&self) -> &[u8]206 pub fn as_bytes(&self) -> &[u8] {
207 self.0.as_bytes()
208 }
209
210 /// Return an allocated ASN.1 DER serialization as a byte vector.
to_bytes(&self) -> Zeroizing<Vec<u8>>211 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
212 Zeroizing::new(self.0.to_vec())
213 }
214
215 /// Get the length of the encoded ASN.1 DER in bytes.
len(&self) -> Length216 pub fn len(&self) -> Length {
217 self.0.len()
218 }
219
220 /// Try to decode the inner ASN.1 DER message as the given type.
decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T>221 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T> {
222 self.0.decode_msg()
223 }
224
225 /// Encode the provided type as ASN.1 DER.
encode_msg<T: Encode>(msg: &T) -> Result<Self>226 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self> {
227 Document::encode_msg(msg).map(Self)
228 }
229
230 /// Decode ASN.1 DER document from PEM.
231 #[cfg(feature = "pem")]
from_pem(pem: &str) -> Result<(&str, Self)>232 pub fn from_pem(pem: &str) -> Result<(&str, Self)> {
233 Document::from_pem(pem).map(|(label, doc)| (label, Self(doc)))
234 }
235
236 /// Encode ASN.1 DER document as a PEM string.
237 #[cfg(feature = "pem")]
to_pem( &self, label: &'static str, line_ending: pem::LineEnding, ) -> Result<Zeroizing<String>>238 pub fn to_pem(
239 &self,
240 label: &'static str,
241 line_ending: pem::LineEnding,
242 ) -> Result<Zeroizing<String>> {
243 self.0.to_pem(label, line_ending).map(Zeroizing::new)
244 }
245
246 /// Read ASN.1 DER document from a file.
247 #[cfg(feature = "std")]
read_der_file(path: impl AsRef<Path>) -> Result<Self>248 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> {
249 Document::read_der_file(path).map(Self)
250 }
251
252 /// Write ASN.1 DER document to a file.
253 #[cfg(feature = "std")]
write_der_file(&self, path: impl AsRef<Path>) -> Result<()>254 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
255 write_secret_file(path, self.as_bytes())
256 }
257
258 /// Read PEM-encoded ASN.1 DER document from a file.
259 #[cfg(all(feature = "pem", feature = "std"))]
read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self)>260 pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self)> {
261 Document::read_pem_file(path).map(|(label, doc)| (label, Self(doc)))
262 }
263
264 /// Write PEM-encoded ASN.1 DER document to a file.
265 #[cfg(all(feature = "pem", feature = "std"))]
write_pem_file( &self, path: impl AsRef<Path>, label: &'static str, line_ending: pem::LineEnding, ) -> Result<()>266 pub fn write_pem_file(
267 &self,
268 path: impl AsRef<Path>,
269 label: &'static str,
270 line_ending: pem::LineEnding,
271 ) -> Result<()> {
272 write_secret_file(path, self.to_pem(label, line_ending)?.as_bytes())
273 }
274 }
275 #[cfg(feature = "zeroize")]
276 impl Debug for SecretDocument {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result277 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
278 fmt.debug_struct("SecretDocument").finish_non_exhaustive()
279 }
280 }
281
282 #[cfg(feature = "zeroize")]
283 impl Drop for SecretDocument {
drop(&mut self)284 fn drop(&mut self) {
285 self.0.der_bytes.zeroize();
286 }
287 }
288
289 #[cfg(feature = "zeroize")]
290 impl From<Document> for SecretDocument {
from(doc: Document) -> SecretDocument291 fn from(doc: Document) -> SecretDocument {
292 SecretDocument(doc)
293 }
294 }
295
296 #[cfg(feature = "zeroize")]
297 impl TryFrom<&[u8]> for SecretDocument {
298 type Error = Error;
299
try_from(der_bytes: &[u8]) -> Result<Self>300 fn try_from(der_bytes: &[u8]) -> Result<Self> {
301 Document::try_from(der_bytes).map(Self)
302 }
303 }
304
305 #[cfg(feature = "zeroize")]
306 impl TryFrom<Vec<u8>> for SecretDocument {
307 type Error = Error;
308
try_from(der_bytes: Vec<u8>) -> Result<Self>309 fn try_from(der_bytes: Vec<u8>) -> Result<Self> {
310 Document::try_from(der_bytes).map(Self)
311 }
312 }
313
314 #[cfg(feature = "zeroize")]
315 impl ZeroizeOnDrop for SecretDocument {}
316
317 /// Attempt to decode a ASN.1 `SEQUENCE` from the given decoder, returning the
318 /// entire sequence including the header.
decode_sequence<'a>(decoder: &mut SliceReader<'a>) -> Result<&'a [u8]>319 fn decode_sequence<'a>(decoder: &mut SliceReader<'a>) -> Result<&'a [u8]> {
320 let header = decoder.peek_header()?;
321 header.tag.assert_eq(Tag::Sequence)?;
322
323 let len = (header.encoded_len()? + header.length)?;
324 decoder.read_slice(len)
325 }
326
327 /// Write a file containing secret data to the filesystem, restricting the
328 /// file permissions so it's only readable by the owner
329 #[cfg(all(unix, feature = "std", feature = "zeroize"))]
write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()>330 fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()> {
331 use std::{io::Write, os::unix::fs::OpenOptionsExt};
332
333 /// File permissions for secret data
334 #[cfg(unix)]
335 const SECRET_FILE_PERMS: u32 = 0o600;
336
337 fs::OpenOptions::new()
338 .create(true)
339 .write(true)
340 .truncate(true)
341 .mode(SECRET_FILE_PERMS)
342 .open(path)
343 .and_then(|mut file| file.write_all(data))?;
344
345 Ok(())
346 }
347
348 /// Write a file containing secret data to the filesystem
349 // TODO(tarcieri): permissions hardening on Windows
350 #[cfg(all(not(unix), feature = "std", feature = "zeroize"))]
write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()>351 fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()> {
352 fs::write(path, data)?;
353 Ok(())
354 }
355