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