1 //! Traits for parsing objects from SEC1 encoded documents
2 
3 use crate::Result;
4 
5 #[cfg(feature = "alloc")]
6 use der::SecretDocument;
7 
8 #[cfg(feature = "pem")]
9 use {crate::LineEnding, alloc::string::String, der::pem::PemLabel};
10 
11 #[cfg(feature = "pkcs8")]
12 use {
13     crate::{EcPrivateKey, ALGORITHM_OID},
14     der::Decode,
15 };
16 
17 #[cfg(feature = "std")]
18 use std::path::Path;
19 
20 #[cfg(feature = "pem")]
21 use zeroize::Zeroizing;
22 
23 /// Parse an [`EcPrivateKey`] from a SEC1-encoded document.
24 pub trait DecodeEcPrivateKey: Sized {
25     /// Deserialize SEC1 private key from ASN.1 DER-encoded data
26     /// (binary format).
from_sec1_der(bytes: &[u8]) -> Result<Self>27     fn from_sec1_der(bytes: &[u8]) -> Result<Self>;
28 
29     /// Deserialize SEC1-encoded private key from PEM.
30     ///
31     /// Keys in this format begin with the following:
32     ///
33     /// ```text
34     /// -----BEGIN EC PRIVATE KEY-----
35     /// ```
36     #[cfg(feature = "pem")]
from_sec1_pem(s: &str) -> Result<Self>37     fn from_sec1_pem(s: &str) -> Result<Self> {
38         let (label, doc) = SecretDocument::from_pem(s)?;
39         EcPrivateKey::validate_pem_label(label)?;
40         Self::from_sec1_der(doc.as_bytes())
41     }
42 
43     /// Load SEC1 private key from an ASN.1 DER-encoded file on the local
44     /// filesystem (binary format).
45     #[cfg(feature = "std")]
read_sec1_der_file(path: impl AsRef<Path>) -> Result<Self>46     fn read_sec1_der_file(path: impl AsRef<Path>) -> Result<Self> {
47         Self::from_sec1_der(SecretDocument::read_der_file(path)?.as_bytes())
48     }
49 
50     /// Load SEC1 private key from a PEM-encoded file on the local filesystem.
51     #[cfg(all(feature = "pem", feature = "std"))]
read_sec1_pem_file(path: impl AsRef<Path>) -> Result<Self>52     fn read_sec1_pem_file(path: impl AsRef<Path>) -> Result<Self> {
53         let (label, doc) = SecretDocument::read_pem_file(path)?;
54         EcPrivateKey::validate_pem_label(&label)?;
55         Self::from_sec1_der(doc.as_bytes())
56     }
57 }
58 
59 /// Serialize a [`EcPrivateKey`] to a SEC1 encoded document.
60 #[cfg(feature = "alloc")]
61 pub trait EncodeEcPrivateKey {
62     /// Serialize a [`SecretDocument`] containing a SEC1-encoded private key.
to_sec1_der(&self) -> Result<SecretDocument>63     fn to_sec1_der(&self) -> Result<SecretDocument>;
64 
65     /// Serialize this private key as PEM-encoded SEC1 with the given [`LineEnding`].
66     ///
67     /// To use the OS's native line endings, pass `Default::default()`.
68     #[cfg(feature = "pem")]
to_sec1_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>>69     fn to_sec1_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
70         let doc = self.to_sec1_der()?;
71         Ok(doc.to_pem(EcPrivateKey::PEM_LABEL, line_ending)?)
72     }
73 
74     /// Write ASN.1 DER-encoded SEC1 private key to the given path.
75     #[cfg(feature = "std")]
write_sec1_der_file(&self, path: impl AsRef<Path>) -> Result<()>76     fn write_sec1_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
77         Ok(self.to_sec1_der()?.write_der_file(path)?)
78     }
79 
80     /// Write ASN.1 DER-encoded SEC1 private key to the given path.
81     #[cfg(all(feature = "pem", feature = "std"))]
write_sec1_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()>82     fn write_sec1_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> {
83         let doc = self.to_sec1_der()?;
84         Ok(doc.write_pem_file(path, EcPrivateKey::PEM_LABEL, line_ending)?)
85     }
86 }
87 
88 #[cfg(feature = "pkcs8")]
89 impl<T> DecodeEcPrivateKey for T
90 where
91     T: for<'a> TryFrom<pkcs8::PrivateKeyInfo<'a>, Error = pkcs8::Error>,
92 {
from_sec1_der(private_key: &[u8]) -> Result<Self>93     fn from_sec1_der(private_key: &[u8]) -> Result<Self> {
94         let params_oid = EcPrivateKey::from_der(private_key)?
95             .parameters
96             .and_then(|params| params.named_curve());
97 
98         let algorithm = pkcs8::AlgorithmIdentifierRef {
99             oid: ALGORITHM_OID,
100             parameters: params_oid.as_ref().map(Into::into),
101         };
102 
103         Ok(Self::try_from(pkcs8::PrivateKeyInfo {
104             algorithm,
105             private_key,
106             public_key: None,
107         })?)
108     }
109 }
110 
111 #[cfg(all(feature = "alloc", feature = "pkcs8"))]
112 impl<T: pkcs8::EncodePrivateKey> EncodeEcPrivateKey for T {
to_sec1_der(&self) -> Result<SecretDocument>113     fn to_sec1_der(&self) -> Result<SecretDocument> {
114         let doc = self.to_pkcs8_der()?;
115         let pkcs8_key = pkcs8::PrivateKeyInfo::from_der(doc.as_bytes())?;
116         pkcs8_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
117 
118         let mut pkcs1_key = EcPrivateKey::from_der(pkcs8_key.private_key)?;
119         pkcs1_key.parameters = Some(pkcs8_key.algorithm.parameters_oid()?.into());
120         pkcs1_key.try_into()
121     }
122 }
123