1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Traits for parsing objects from SEC1 encoded documents

use crate::Result;

#[cfg(feature = "alloc")]
use der::SecretDocument;

#[cfg(feature = "pem")]
use {crate::LineEnding, alloc::string::String, der::pem::PemLabel};

#[cfg(feature = "pkcs8")]
use {
    crate::{EcPrivateKey, ALGORITHM_OID},
    der::Decode,
};

#[cfg(feature = "std")]
use std::path::Path;

#[cfg(feature = "pem")]
use zeroize::Zeroizing;

/// Parse an [`EcPrivateKey`] from a SEC1-encoded document.
#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
pub trait DecodeEcPrivateKey: Sized {
    /// Deserialize SEC1 private key from ASN.1 DER-encoded data
    /// (binary format).
    fn from_sec1_der(bytes: &[u8]) -> Result<Self>;

    /// Deserialize SEC1-encoded private key from PEM.
    ///
    /// Keys in this format begin with the following:
    ///
    /// ```text
    /// -----BEGIN EC PRIVATE KEY-----
    /// ```
    #[cfg(feature = "pem")]
    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
    fn from_sec1_pem(s: &str) -> Result<Self> {
        let (label, doc) = SecretDocument::from_pem(s)?;
        EcPrivateKey::validate_pem_label(label)?;
        Self::from_sec1_der(doc.as_bytes())
    }

    /// Load SEC1 private key from an ASN.1 DER-encoded file on the local
    /// filesystem (binary format).
    #[cfg(feature = "std")]
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
    fn read_sec1_der_file(path: impl AsRef<Path>) -> Result<Self> {
        Self::from_sec1_der(SecretDocument::read_der_file(path)?.as_bytes())
    }

    /// Load SEC1 private key from a PEM-encoded file on the local filesystem.
    #[cfg(all(feature = "pem", feature = "std"))]
    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
    fn read_sec1_pem_file(path: impl AsRef<Path>) -> Result<Self> {
        let (label, doc) = SecretDocument::read_pem_file(path)?;
        EcPrivateKey::validate_pem_label(&label)?;
        Self::from_sec1_der(doc.as_bytes())
    }
}

/// Serialize a [`EcPrivateKey`] to a SEC1 encoded document.
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "der"))))]
pub trait EncodeEcPrivateKey {
    /// Serialize a [`SecretDocument`] containing a SEC1-encoded private key.
    fn to_sec1_der(&self) -> Result<SecretDocument>;

    /// Serialize this private key as PEM-encoded SEC1 with the given [`LineEnding`].
    ///
    /// To use the OS's native line endings, pass `Default::default()`.
    #[cfg(feature = "pem")]
    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
    fn to_sec1_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
        let doc = self.to_sec1_der()?;
        Ok(doc.to_pem(EcPrivateKey::PEM_LABEL, line_ending)?)
    }

    /// Write ASN.1 DER-encoded SEC1 private key to the given path.
    #[cfg(feature = "std")]
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
    fn write_sec1_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
        Ok(self.to_sec1_der()?.write_der_file(path)?)
    }

    /// Write ASN.1 DER-encoded SEC1 private key to the given path.
    #[cfg(all(feature = "pem", feature = "std"))]
    #[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
    fn write_sec1_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> {
        let doc = self.to_sec1_der()?;
        Ok(doc.write_pem_file(path, EcPrivateKey::PEM_LABEL, line_ending)?)
    }
}

#[cfg(feature = "pkcs8")]
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
impl<T: pkcs8::DecodePrivateKey> DecodeEcPrivateKey for T {
    fn from_sec1_der(private_key: &[u8]) -> Result<Self> {
        let params_oid = EcPrivateKey::from_der(private_key)?
            .parameters
            .and_then(|params| params.named_curve());

        let algorithm = pkcs8::AlgorithmIdentifier {
            oid: ALGORITHM_OID,
            parameters: params_oid.as_ref().map(Into::into),
        };

        Ok(Self::try_from(pkcs8::PrivateKeyInfo {
            algorithm,
            private_key,
            public_key: None,
        })?)
    }
}

#[cfg(all(feature = "alloc", feature = "pkcs8"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs8"))))]
impl<T: pkcs8::EncodePrivateKey> EncodeEcPrivateKey for T {
    fn to_sec1_der(&self) -> Result<SecretDocument> {
        let doc = self.to_pkcs8_der()?;
        let pkcs8_key = pkcs8::PrivateKeyInfo::from_der(doc.as_bytes())?;
        pkcs8_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;

        let mut pkcs1_key = EcPrivateKey::from_der(pkcs8_key.private_key)?;
        pkcs1_key.parameters = Some(pkcs8_key.algorithm.parameters_oid()?.into());
        pkcs1_key.try_into()
    }
}