1use crate::{Decode, Encode, Error, FixedTag, Length, Reader, Result, 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 pub fn as_bytes(&self) -> &[u8] {
42 self.der_bytes.as_slice()
43 }
44
45 #[cfg(feature = "zeroize")]
47 pub fn into_secret(self) -> SecretDocument {
48 SecretDocument(self)
49 }
50
51 pub fn into_vec(self) -> Vec<u8> {
53 self.der_bytes
54 }
55
56 pub fn to_vec(&self) -> Vec<u8> {
58 self.der_bytes.clone()
59 }
60
61 pub fn len(&self) -> Length {
63 self.length
64 }
65
66 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T> {
69 T::from_der(self.as_bytes())
70 }
71
72 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self> {
75 msg.to_der()?.try_into()
76 }
77
78 #[cfg(feature = "pem")]
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 #[cfg(feature = "pem")]
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 #[cfg(feature = "std")]
96 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> {
97 fs::read(path)?.try_into()
98 }
99
100 #[cfg(feature = "std")]
102 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
103 Ok(fs::write(path, self.as_bytes())?)
104 }
105
106 #[cfg(all(feature = "pem", feature = "std"))]
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 #[cfg(all(feature = "pem", feature = "std"))]
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
125impl AsRef<[u8]> for Document {
126 fn as_ref(&self) -> &[u8] {
127 self.as_bytes()
128 }
129}
130
131impl Debug for Document {
132 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
143impl<'a> Decode<'a> for 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
156impl Encode for Document {
157 fn encoded_len(&self) -> Result<Length> {
158 Ok(self.len())
159 }
160
161 fn encode(&self, writer: &mut impl Writer) -> Result<()> {
162 writer.write(self.as_bytes())
163 }
164}
165
166impl FixedTag for Document {
167 const TAG: Tag = Tag::Sequence;
168}
169
170impl TryFrom<&[u8]> for Document {
171 type Error = Error;
172
173 fn try_from(der_bytes: &[u8]) -> Result<Self> {
174 Self::from_der(der_bytes)
175 }
176}
177
178impl TryFrom<Vec<u8>> for Document {
179 type Error = Error;
180
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#[cfg(feature = "zeroize")]
200#[derive(Clone)]
201pub struct SecretDocument(Document);
202
203#[cfg(feature = "zeroize")]
204impl SecretDocument {
205 pub fn as_bytes(&self) -> &[u8] {
207 self.0.as_bytes()
208 }
209
210 pub fn to_bytes(&self) -> Zeroizing<Vec<u8>> {
212 Zeroizing::new(self.0.to_vec())
213 }
214
215 pub fn len(&self) -> Length {
217 self.0.len()
218 }
219
220 pub fn decode_msg<'a, T: Decode<'a>>(&'a self) -> Result<T> {
222 self.0.decode_msg()
223 }
224
225 pub fn encode_msg<T: Encode>(msg: &T) -> Result<Self> {
227 Document::encode_msg(msg).map(Self)
228 }
229
230 #[cfg(feature = "pem")]
232 pub fn from_pem(pem: &str) -> Result<(&str, Self)> {
233 Document::from_pem(pem).map(|(label, doc)| (label, Self(doc)))
234 }
235
236 #[cfg(feature = "pem")]
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 #[cfg(feature = "std")]
248 pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> {
249 Document::read_der_file(path).map(Self)
250 }
251
252 #[cfg(feature = "std")]
254 pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
255 write_secret_file(path, self.as_bytes())
256 }
257
258 #[cfg(all(feature = "pem", feature = "std"))]
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 #[cfg(all(feature = "pem", feature = "std"))]
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")]
276impl Debug for SecretDocument {
277 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
278 fmt.debug_struct("SecretDocument").finish_non_exhaustive()
279 }
280}
281
282#[cfg(feature = "zeroize")]
283impl Drop for SecretDocument {
284 fn drop(&mut self) {
285 self.0.der_bytes.zeroize();
286 }
287}
288
289#[cfg(feature = "zeroize")]
290impl From<Document> for SecretDocument {
291 fn from(doc: Document) -> SecretDocument {
292 SecretDocument(doc)
293 }
294}
295
296#[cfg(feature = "zeroize")]
297impl TryFrom<&[u8]> for SecretDocument {
298 type Error = Error;
299
300 fn try_from(der_bytes: &[u8]) -> Result<Self> {
301 Document::try_from(der_bytes).map(Self)
302 }
303}
304
305#[cfg(feature = "zeroize")]
306impl TryFrom<Vec<u8>> for SecretDocument {
307 type Error = Error;
308
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")]
315impl ZeroizeOnDrop for SecretDocument {}
316
317fn 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#[cfg(all(unix, feature = "std", feature = "zeroize"))]
330fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()> {
331 use std::{io::Write, os::unix::fs::OpenOptionsExt};
332
333 #[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#[cfg(all(not(unix), feature = "std", feature = "zeroize"))]
351fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()> {
352 fs::write(path, data)?;
353 Ok(())
354}