Skip to main content

sec1/
private_key.rs

1//! SEC1 elliptic curve private key support.
2//!
3//! Support for ASN.1 DER-encoded elliptic curve private keys as described in
4//! SEC1: Elliptic Curve Cryptography (Version 2.0) Appendix C.4 (p.108):
5//!
6//! <https://www.secg.org/sec1-v2.pdf>
7
8use crate::{EcParameters, Error, Result};
9use core::fmt;
10use der::{
11    Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, TagMode,
12    TagNumber, Writer,
13    asn1::{BitStringRef, ContextSpecific, ContextSpecificRef, OctetStringRef},
14};
15
16#[cfg(all(feature = "alloc", feature = "zeroize"))]
17use der::SecretDocument;
18#[cfg(feature = "pem")]
19use der::pem::PemLabel;
20
21/// `ECPrivateKey` version.
22///
23/// From [RFC5913 Section 3]:
24/// > version specifies the syntax version number of the elliptic curve
25/// > private key structure.  For this version of the document, it SHALL
26/// > be set to ecPrivkeyVer1, which is of type INTEGER and whose value
27/// > is one (1).
28///
29/// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
30const VERSION: u8 = 1;
31
32/// Context-specific tag number for the elliptic curve parameters.
33const EC_PARAMETERS_TAG: TagNumber = TagNumber(0);
34
35/// Context-specific tag number for the public key.
36const PUBLIC_KEY_TAG: TagNumber = TagNumber(1);
37
38/// SEC1 elliptic curve private key.
39///
40/// Described in [SEC1: Elliptic Curve Cryptography (Version 2.0)]
41/// Appendix C.4 (p.108) and also [RFC5915 Section 3]:
42///
43/// ```text
44/// ECPrivateKey ::= SEQUENCE {
45///   version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
46///   privateKey     OCTET STRING,
47///   parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
48///   publicKey  [1] BIT STRING OPTIONAL
49/// }
50/// ```
51///
52/// When encoded as PEM (text), keys in this format begin with the following:
53///
54/// ```text
55/// -----BEGIN EC PRIVATE KEY-----
56/// ```
57///
58/// [SEC1: Elliptic Curve Cryptography (Version 2.0)]: https://www.secg.org/sec1-v2.pdf
59/// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
60#[derive(Clone)]
61pub struct EcPrivateKey<'a> {
62    /// Private key data.
63    pub private_key: &'a [u8],
64
65    /// Elliptic curve parameters.
66    pub parameters: Option<EcParameters>,
67
68    /// Public key data, optionally available if version is V2.
69    pub public_key: Option<&'a [u8]>,
70}
71
72impl<'a> EcPrivateKey<'a> {
73    fn context_specific_parameters(&self) -> Option<ContextSpecificRef<'_, EcParameters>> {
74        self.parameters.as_ref().map(|params| ContextSpecificRef {
75            tag_number: EC_PARAMETERS_TAG,
76            tag_mode: TagMode::Explicit,
77            value: params,
78        })
79    }
80
81    fn context_specific_public_key(
82        &self,
83    ) -> der::Result<Option<ContextSpecific<BitStringRef<'a>>>> {
84        self.public_key
85            .map(|pk| {
86                BitStringRef::from_bytes(pk).map(|value| ContextSpecific {
87                    tag_number: PUBLIC_KEY_TAG,
88                    tag_mode: TagMode::Explicit,
89                    value,
90                })
91            })
92            .transpose()
93    }
94}
95
96impl<'a> DecodeValue<'a> for EcPrivateKey<'a> {
97    type Error = der::Error;
98
99    fn decode_value<R: Reader<'a>>(reader: &mut R, _header: Header) -> der::Result<Self> {
100        if u8::decode(reader)? != VERSION {
101            return Err(reader.error(Tag::Integer.value_error()));
102        }
103
104        let private_key = <&OctetStringRef>::decode(reader)?.as_bytes();
105        let parameters = reader.context_specific(EC_PARAMETERS_TAG, TagMode::Explicit)?;
106        let public_key = reader
107            .context_specific::<BitStringRef<'_>>(PUBLIC_KEY_TAG, TagMode::Explicit)?
108            .map(|bs| bs.as_bytes().ok_or_else(|| Tag::BitString.value_error()))
109            .transpose()?;
110
111        Ok(EcPrivateKey {
112            private_key,
113            parameters,
114            public_key,
115        })
116    }
117}
118
119impl EncodeValue for EcPrivateKey<'_> {
120    fn value_len(&self) -> der::Result<Length> {
121        VERSION.encoded_len()?
122            + OctetStringRef::new(self.private_key)?.encoded_len()?
123            + self.context_specific_parameters().encoded_len()?
124            + self.context_specific_public_key()?.encoded_len()?
125    }
126
127    fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
128        VERSION.encode(writer)?;
129        OctetStringRef::new(self.private_key)?.encode(writer)?;
130        self.context_specific_parameters().encode(writer)?;
131        self.context_specific_public_key()?.encode(writer)?;
132        Ok(())
133    }
134}
135
136impl<'a> Sequence<'a> for EcPrivateKey<'a> {}
137
138impl<'a> TryFrom<&'a [u8]> for EcPrivateKey<'a> {
139    type Error = Error;
140
141    fn try_from(bytes: &'a [u8]) -> Result<EcPrivateKey<'a>> {
142        Ok(Self::from_der(bytes)?)
143    }
144}
145
146impl fmt::Debug for EcPrivateKey<'_> {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        f.debug_struct("EcPrivateKey")
149            .field("parameters", &self.parameters)
150            .field("public_key", &self.public_key)
151            .finish_non_exhaustive()
152    }
153}
154
155#[cfg(feature = "alloc")]
156impl TryFrom<EcPrivateKey<'_>> for SecretDocument {
157    type Error = Error;
158
159    fn try_from(private_key: EcPrivateKey<'_>) -> Result<Self> {
160        SecretDocument::try_from(&private_key)
161    }
162}
163
164#[cfg(feature = "alloc")]
165impl TryFrom<&EcPrivateKey<'_>> for SecretDocument {
166    type Error = Error;
167
168    fn try_from(private_key: &EcPrivateKey<'_>) -> Result<Self> {
169        Ok(Self::encode_msg(private_key)?)
170    }
171}
172
173#[cfg(feature = "pem")]
174impl PemLabel for EcPrivateKey<'_> {
175    const PEM_LABEL: &'static str = "EC PRIVATE KEY";
176}