1use crate::{Decode, Encode, Error, FixedTag, Header, Length, Reader, SliceReader, Tag, Writer};
4use alloc::vec::Vec;
5use core::fmt::{self, Debug};
6
7#[cfg(feature = "pem")]
8use {crate::pem, alloc::string::String};
9
10#[cfg(feature = "std")]
11use std::{fs, path::Path};
12
13#[cfg(all(feature = "pem", feature = "std"))]
14use alloc::borrow::ToOwned;
15
16#[cfg(feature = "zeroize")]
17use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
18
19#[derive(Clone, Eq, PartialEq)]
31pub struct Document {
32 der_bytes: Vec<u8>,
34
35 length: Length,
37}
38
39impl Document {
40 #[must_use]
42 pub fn as_bytes(&self) -> &[u8] {
43 self.der_bytes.as_slice()
44 }
45
46 #[cfg(feature = "zeroize")]
48 #[must_use]
49 pub fn into_secret(self) -> SecretDocument {
50 SecretDocument(self)
51 }
52
53 #[must_use]
55 pub fn into_vec(self) -> Vec<u8> {
56 self.der_bytes
57 }
58
59 #[must_use]
61 pub fn to_vec(&self) -> Vec<u8> {
62 self.der_bytes.clone()
63 }
64
65 #[must_use]
67 pub fn len(&self) -> Length {
68 self.length
69 }
70
71 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T, T::Error> {
77 T::from_der(self.as_bytes())
78 }
79
80 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self, Error> {
86 msg.to_der()?.try_into()
87 }
88
89 #[cfg(feature = "pem")]
96 pub fn from_pem(pem: &str) -> Result<(&str, Self), Error> {
97 let (label, der_bytes) = pem::decode_vec(pem.as_bytes())?;
98 Ok((label, der_bytes.try_into()?))
99 }
100
101 #[cfg(feature = "pem")]
107 pub fn to_pem(
108 &self,
109 label: &'static str,
110 line_ending: pem::LineEnding,
111 ) -> Result<String, Error> {
112 Ok(pem::encode_string(label, line_ending, self.as_bytes())?)
113 }
114
115 #[cfg(feature = "std")]
120 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self, Error> {
121 fs::read(path)?.try_into()
122 }
123
124 #[cfg(feature = "std")]
129 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<(), Error> {
130 Ok(fs::write(path, self.as_bytes())?)
131 }
132
133 #[cfg(all(feature = "pem", feature = "std"))]
138 pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self), Error> {
139 Self::from_pem(&fs::read_to_string(path)?).map(|(label, doc)| (label.to_owned(), doc))
140 }
141
142 #[cfg(all(feature = "pem", feature = "std"))]
147 pub fn write_pem_file(
148 &self,
149 path: impl AsRef<Path>,
150 label: &'static str,
151 line_ending: pem::LineEnding,
152 ) -> Result<(), Error> {
153 let pem = self.to_pem(label, line_ending)?;
154 Ok(fs::write(path, pem.as_bytes())?)
155 }
156}
157
158impl AsRef<[u8]> for Document {
159 fn as_ref(&self) -> &[u8] {
160 self.as_bytes()
161 }
162}
163
164impl Debug for Document {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 f.write_str("Document(")?;
167
168 for byte in self.as_bytes() {
169 write!(f, "{byte:02X}")?;
170 }
171
172 f.write_str(")")
173 }
174}
175
176impl<'a> Decode<'a> for Document {
177 type Error = Error;
178
179 fn decode<R: Reader<'a>>(reader: &mut R) -> Result<Document, Error> {
180 let header = Header::peek(reader)?;
181 let length = (header.encoded_len()? + header.length())?;
182 let bytes = reader.read_slice(length)?;
183
184 Ok(Self {
185 der_bytes: bytes.into(),
186 length,
187 })
188 }
189}
190
191impl Encode for Document {
192 fn encoded_len(&self) -> Result<Length, Error> {
193 Ok(self.len())
194 }
195
196 fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
197 writer.write(self.as_bytes())
198 }
199}
200
201impl FixedTag for Document {
202 const TAG: Tag = Tag::Sequence;
203}
204
205impl TryFrom<&[u8]> for Document {
206 type Error = Error;
207
208 fn try_from(der_bytes: &[u8]) -> Result<Self, Error> {
209 Self::from_der(der_bytes)
210 }
211}
212
213impl TryFrom<Vec<u8>> for Document {
214 type Error = Error;
215
216 fn try_from(der_bytes: Vec<u8>) -> Result<Self, Error> {
217 let mut decoder = SliceReader::new(&der_bytes)?;
218 decode_sequence(&mut decoder)?;
219 decoder.finish()?;
220
221 let length = der_bytes.len().try_into()?;
222 Ok(Self { der_bytes, length })
223 }
224}
225
226#[cfg(feature = "zeroize")]
235#[derive(Clone)]
236pub struct SecretDocument(Document);
237
238#[cfg(feature = "zeroize")]
239impl SecretDocument {
240 #[must_use]
242 pub fn as_bytes(&self) -> &[u8] {
243 self.0.as_bytes()
244 }
245
246 #[must_use]
248 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
249 Zeroizing::new(self.0.to_vec())
250 }
251
252 #[must_use]
254 pub fn len(&self) -> Length {
255 self.0.len()
256 }
257
258 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T, T::Error> {
263 self.0.decode_msg()
264 }
265
266 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self, Error> {
271 Document::encode_msg(msg).map(Self)
272 }
273
274 #[cfg(feature = "pem")]
279 pub fn from_pem(pem: &str) -> Result<(&str, Self), Error> {
280 Document::from_pem(pem).map(|(label, doc)| (label, Self(doc)))
281 }
282
283 #[cfg(feature = "pem")]
288 pub fn to_pem(
289 &self,
290 label: &'static str,
291 line_ending: pem::LineEnding,
292 ) -> Result<Zeroizing<String>, Error> {
293 self.0.to_pem(label, line_ending).map(Zeroizing::new)
294 }
295
296 #[cfg(feature = "std")]
301 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self, Error> {
302 Document::read_der_file(path).map(Self)
303 }
304
305 #[cfg(feature = "std")]
310 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<(), Error> {
311 write_secret_file(path, self.as_bytes())
312 }
313
314 #[cfg(all(feature = "pem", feature = "std"))]
319 pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self), Error> {
320 Document::read_pem_file(path).map(|(label, doc)| (label, Self(doc)))
321 }
322
323 #[cfg(all(feature = "pem", feature = "std"))]
328 pub fn write_pem_file(
329 &self,
330 path: impl AsRef<Path>,
331 label: &'static str,
332 line_ending: pem::LineEnding,
333 ) -> Result<(), Error> {
334 write_secret_file(path, self.to_pem(label, line_ending)?.as_bytes())
335 }
336}
337#[cfg(feature = "zeroize")]
338impl Debug for SecretDocument {
339 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
340 fmt.debug_struct("SecretDocument").finish_non_exhaustive()
341 }
342}
343
344#[cfg(feature = "zeroize")]
345impl Drop for SecretDocument {
346 fn drop(&mut self) {
347 self.0.der_bytes.zeroize();
348 }
349}
350
351#[cfg(feature = "zeroize")]
352impl From<Document> for SecretDocument {
353 fn from(doc: Document) -> SecretDocument {
354 SecretDocument(doc)
355 }
356}
357
358#[cfg(feature = "zeroize")]
359impl TryFrom<&[u8]> for SecretDocument {
360 type Error = Error;
361
362 fn try_from(der_bytes: &[u8]) -> Result<Self, Error> {
363 Document::try_from(der_bytes).map(Self)
364 }
365}
366
367#[cfg(feature = "zeroize")]
368impl TryFrom<Vec<u8>> for SecretDocument {
369 type Error = Error;
370
371 fn try_from(der_bytes: Vec<u8>) -> Result<Self, Error> {
372 Document::try_from(der_bytes).map(Self)
373 }
374}
375
376#[cfg(feature = "zeroize")]
377impl ZeroizeOnDrop for SecretDocument {}
378
379fn decode_sequence<'a>(decoder: &mut SliceReader<'a>) -> Result<&'a [u8], Error> {
382 let header = Header::peek(decoder)?;
383 header.tag().assert_eq(Tag::Sequence)?;
384
385 let len = (header.encoded_len()? + header.length())?;
386 decoder.read_slice(len)
387}
388
389#[cfg(all(unix, feature = "std", feature = "zeroize"))]
392fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<(), Error> {
393 use std::{io::Write, os::unix::fs::OpenOptionsExt};
394
395 #[cfg(unix)]
397 const SECRET_FILE_PERMS: u32 = 0o600;
398
399 fs::OpenOptions::new()
400 .create(true)
401 .write(true)
402 .truncate(true)
403 .mode(SECRET_FILE_PERMS)
404 .open(path)
405 .and_then(|mut file| file.write_all(data))?;
406
407 Ok(())
408}
409
410#[cfg(all(not(unix), feature = "std", feature = "zeroize"))]
413fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<(), Error> {
414 fs::write(path, data)?;
415 Ok(())
416}