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 header.tag().assert_eq(Tag::Sequence)?;
182 let length = (header.encoded_len()? + header.length())?;
183 let bytes = reader.read_slice(length)?;
184
185 Ok(Self {
186 der_bytes: bytes.into(),
187 length,
188 })
189 }
190}
191
192impl Encode for Document {
193 fn encoded_len(&self) -> Result<Length, Error> {
194 Ok(self.len())
195 }
196
197 fn encode(&self, writer: &mut impl Writer) -> Result<(), Error> {
198 writer.write(self.as_bytes())
199 }
200}
201
202impl FixedTag for Document {
203 const TAG: Tag = Tag::Sequence;
204}
205
206impl TryFrom<&[u8]> for Document {
207 type Error = Error;
208
209 fn try_from(der_bytes: &[u8]) -> Result<Self, Error> {
210 Self::from_der(der_bytes)
211 }
212}
213
214impl TryFrom<Vec<u8>> for Document {
215 type Error = Error;
216
217 fn try_from(der_bytes: Vec<u8>) -> Result<Self, Error> {
218 let mut decoder = SliceReader::new(&der_bytes)?;
219 decode_sequence(&mut decoder)?;
220 decoder.finish()?;
221
222 let length = der_bytes.len().try_into()?;
223 Ok(Self { der_bytes, length })
224 }
225}
226
227#[cfg(feature = "zeroize")]
236#[derive(Clone)]
237pub struct SecretDocument(Document);
238
239#[cfg(feature = "zeroize")]
240impl SecretDocument {
241 #[must_use]
243 pub fn as_bytes(&self) -> &[u8] {
244 self.0.as_bytes()
245 }
246
247 #[must_use]
249 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
250 Zeroizing::new(self.0.to_vec())
251 }
252
253 #[must_use]
255 pub fn len(&self) -> Length {
256 self.0.len()
257 }
258
259 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T, T::Error> {
264 self.0.decode_msg()
265 }
266
267 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self, Error> {
272 Document::encode_msg(msg).map(Self)
273 }
274
275 #[cfg(feature = "pem")]
280 pub fn from_pem(pem: &str) -> Result<(&str, Self), Error> {
281 Document::from_pem(pem).map(|(label, doc)| (label, Self(doc)))
282 }
283
284 #[cfg(feature = "pem")]
289 pub fn to_pem(
290 &self,
291 label: &'static str,
292 line_ending: pem::LineEnding,
293 ) -> Result<Zeroizing<String>, Error> {
294 self.0.to_pem(label, line_ending).map(Zeroizing::new)
295 }
296
297 #[cfg(feature = "std")]
302 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self, Error> {
303 Document::read_der_file(path).map(Self)
304 }
305
306 #[cfg(feature = "std")]
311 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<(), Error> {
312 write_secret_file(path, self.as_bytes())
313 }
314
315 #[cfg(all(feature = "pem", feature = "std"))]
320 pub fn read_pem_file(path: impl AsRef<Path>) -> Result<(String, Self), Error> {
321 Document::read_pem_file(path).map(|(label, doc)| (label, Self(doc)))
322 }
323
324 #[cfg(all(feature = "pem", feature = "std"))]
329 pub fn write_pem_file(
330 &self,
331 path: impl AsRef<Path>,
332 label: &'static str,
333 line_ending: pem::LineEnding,
334 ) -> Result<(), Error> {
335 write_secret_file(path, self.to_pem(label, line_ending)?.as_bytes())
336 }
337}
338#[cfg(feature = "zeroize")]
339impl Debug for SecretDocument {
340 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
341 fmt.debug_struct("SecretDocument").finish_non_exhaustive()
342 }
343}
344
345#[cfg(feature = "zeroize")]
346impl Drop for SecretDocument {
347 fn drop(&mut self) {
348 self.0.der_bytes.zeroize();
349 }
350}
351
352#[cfg(feature = "zeroize")]
353impl From<Document> for SecretDocument {
354 fn from(doc: Document) -> SecretDocument {
355 SecretDocument(doc)
356 }
357}
358
359#[cfg(feature = "zeroize")]
360impl TryFrom<&[u8]> for SecretDocument {
361 type Error = Error;
362
363 fn try_from(der_bytes: &[u8]) -> Result<Self, Error> {
364 Document::try_from(der_bytes).map(Self)
365 }
366}
367
368#[cfg(feature = "zeroize")]
369impl TryFrom<Vec<u8>> for SecretDocument {
370 type Error = Error;
371
372 fn try_from(der_bytes: Vec<u8>) -> Result<Self, Error> {
373 Document::try_from(der_bytes).map(Self)
374 }
375}
376
377#[cfg(feature = "zeroize")]
378impl ZeroizeOnDrop for SecretDocument {}
379
380fn decode_sequence<'a>(decoder: &mut SliceReader<'a>) -> Result<&'a [u8], Error> {
383 let header = Header::peek(decoder)?;
384 header.tag().assert_eq(Tag::Sequence)?;
385
386 let len = (header.encoded_len()? + header.length())?;
387 decoder.read_slice(len)
388}
389
390#[cfg(all(unix, feature = "std", feature = "zeroize"))]
393fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<(), Error> {
394 use std::{io::Write, os::unix::fs::OpenOptionsExt};
395
396 #[cfg(unix)]
398 const SECRET_FILE_PERMS: u32 = 0o600;
399
400 fs::OpenOptions::new()
401 .create(true)
402 .write(true)
403 .truncate(true)
404 .mode(SECRET_FILE_PERMS)
405 .open(path)
406 .and_then(|mut file| file.write_all(data))?;
407
408 Ok(())
409}
410
411#[cfg(all(not(unix), feature = "std", feature = "zeroize"))]
414fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<(), Error> {
415 fs::write(path, data)?;
416 Ok(())
417}