1 //! ASN.1 DER-encoded documents stored on the heap.
3 use crate::{Decode, Encode, Error, FixedTag, Length, Reader, Result, SliceReader, Tag, Writer}
;
5 use core
::fmt
::{self, Debug}
;
7 #[cfg(feature = "pem")]
8 use {crate::pem, alloc::string::String}
;
10 #[cfg(feature = "std")]
11 use std
::{fs, path::Path}
;
13 #[cfg(all(feature = "pem", feature = "std"))]
14 use alloc
::borrow
::ToOwned
;
16 #[cfg(feature = "zeroize")]
17 use zeroize
::{Zeroize, ZeroizeOnDrop, Zeroizing}
;
19 /// ASN.1 DER-encoded document.
21 /// This type wraps an encoded ASN.1 DER message. The document checked to
22 /// ensure it contains a valid DER-encoded `SEQUENCE`.
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.
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)]
33 /// ASN.1 DER encoded bytes.
36 /// Length of this 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()
46 /// Convert to a [`SecretDocument`].
47 #[cfg(feature = "zeroize")]
48 #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
49 pub fn into_secret(self) -> SecretDocument
{
53 /// Convert to an ASN.1 DER-encoded byte vector.
54 pub fn into_vec(self) -> Vec
<u8> {
58 /// Return an ASN.1 DER-encoded byte vector.
59 pub fn to_vec(&self) -> Vec
<u8> {
60 self.der_bytes
.clone()
63 /// Get the length of the encoded ASN.1 DER in bytes.
64 pub fn len(&self) -> Length
{
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())
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()
80 /// Decode ASN.1 DER document from PEM.
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()?
))
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())?
)
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()
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())?
)
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
))
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(
124 path
: impl AsRef
<Path
>,
126 line_ending
: pem
::LineEnding
,
128 let pem
= self.to_pem(label
, line_ending
)?
;
129 Ok(fs
::write(path
, pem
.as_bytes())?
)
133 impl AsRef
<[u8]> for Document
{
134 fn as_ref(&self) -> &[u8] {
139 impl Debug
for Document
{
140 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
141 f
.write_str("Document(")?
;
143 for byte
in self.as_bytes() {
144 write
!(f
, "{:02X}", byte
)?
;
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
)?
;
158 der_bytes
: bytes
.into(),
164 impl Encode
for Document
{
165 fn encoded_len(&self) -> Result
<Length
> {
169 fn encode(&self, writer
: &mut dyn Writer
) -> Result
<()> {
170 writer
.write(self.as_bytes())
174 impl FixedTag
for Document
{
175 const TAG
: Tag
= Tag
::Sequence
;
178 impl TryFrom
<&[u8]> for Document
{
181 fn try_from(der_bytes
: &[u8]) -> Result
<Self> {
182 Self::from_der(der_bytes
)
186 impl TryFrom
<Vec
<u8>> for Document
{
189 fn try_from(der_bytes
: Vec
<u8>) -> Result
<Self> {
190 let mut decoder
= SliceReader
::new(&der_bytes
)?
;
191 decode_sequence(&mut decoder
)?
;
194 let length
= der_bytes
.len().try_into()?
;
195 Ok(Self { der_bytes, length }
)
199 /// Secret [`Document`] type.
201 /// Useful for formats which represent potentially secret data, such as
202 /// cryptographic keys.
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"))))]
210 pub struct SecretDocument(Document
);
212 #[cfg(feature = "zeroize")]
213 impl SecretDocument
{
214 /// Borrow the inner serialized bytes of this document.
215 pub fn as_bytes(&self) -> &[u8] {
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())
224 /// Get the length of the encoded ASN.1 DER in bytes.
225 pub fn len(&self) -> Length
{
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
> {
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)
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
)))
246 /// Encode ASN.1 DER document as a PEM string.
247 #[cfg(feature = "pem")]
248 #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
252 line_ending
: pem
::LineEnding
,
253 ) -> Result
<Zeroizing
<String
>> {
254 self.0.to_pem(label
, line_ending
).map(Zeroizing
::new
)
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)
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())
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
)))
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(
283 path
: impl AsRef
<Path
>,
285 line_ending
: pem
::LineEnding
,
287 write_secret_file(path
, self.to_pem(label
, line_ending
)?
.as_bytes())
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()
297 #[cfg(feature = "zeroize")]
298 impl Drop
for SecretDocument
{
300 self.0.der_bytes
.zeroize();
304 #[cfg(feature = "zeroize")]
305 impl From
<Document
> for SecretDocument
{
306 fn from(doc
: Document
) -> SecretDocument
{
311 #[cfg(feature = "zeroize")]
312 impl TryFrom
<&[u8]> for SecretDocument
{
315 fn try_from(der_bytes
: &[u8]) -> Result
<Self> {
316 Document
::try_from(der_bytes
).map(Self)
320 #[cfg(feature = "zeroize")]
321 impl TryFrom
<Vec
<u8>> for SecretDocument
{
324 fn try_from(der_bytes
: Vec
<u8>) -> Result
<Self> {
325 Document
::try_from(der_bytes
).map(Self)
329 #[cfg(feature = "zeroize")]
330 impl ZeroizeOnDrop
for SecretDocument {}
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
)?
;
338 let len
= (header
.encoded_len()?
+ header
.length
)?
;
339 decoder
.read_slice(len
)
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}
;
348 /// File permissions for secret data
350 const SECRET_FILE_PERMS
: u32 = 0o600;
352 fs
::OpenOptions
::new()
356 .mode(SECRET_FILE_PERMS
)
358 .and_then(|mut file
| file
.write_all(data
))?
;
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
)?
;