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