• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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