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    asn1::{BitStringRef, ContextSpecific, OctetStringRef},
12    Decode, DecodeValue, Encode, Header, Reader, Sequence, Tag, TagMode, TagNumber,
13};
14
15#[cfg(feature = "alloc")]
16use der::SecretDocument;
17
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::new(0);
34
35/// Context-specific tag number for the public key.
36const PUBLIC_KEY_TAG: TagNumber = TagNumber::new(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)]
61#[cfg_attr(docsrs, doc(cfg(feature = "der")))]
62pub struct EcPrivateKey<'a> {
63    /// Private key data.
64    pub private_key: &'a [u8],
65
66    /// Elliptic curve parameters.
67    pub parameters: Option<EcParameters>,
68
69    /// Public key data, optionally available if version is V2.
70    pub public_key: Option<&'a [u8]>,
71}
72
73impl<'a> DecodeValue<'a> for EcPrivateKey<'a> {
74    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
75        reader.read_nested(header.length, |reader| {
76            if u8::decode(reader)? != VERSION {
77                return Err(der::Tag::Integer.value_error());
78            }
79
80            let private_key = OctetStringRef::decode(reader)?.as_bytes();
81            let parameters = reader.context_specific(EC_PARAMETERS_TAG, TagMode::Explicit)?;
82            let public_key = reader
83                .context_specific::<BitStringRef<'_>>(PUBLIC_KEY_TAG, TagMode::Explicit)?
84                .map(|bs| bs.as_bytes().ok_or_else(|| Tag::BitString.value_error()))
85                .transpose()?;
86
87            Ok(EcPrivateKey {
88                private_key,
89                parameters,
90                public_key,
91            })
92        })
93    }
94}
95
96impl<'a> Sequence<'a> for EcPrivateKey<'a> {
97    fn fields<F, T>(&self, f: F) -> der::Result<T>
98    where
99        F: FnOnce(&[&dyn Encode]) -> der::Result<T>,
100    {
101        f(&[
102            &VERSION,
103            &OctetStringRef::new(self.private_key)?,
104            &self.parameters.as_ref().map(|params| ContextSpecific {
105                tag_number: EC_PARAMETERS_TAG,
106                tag_mode: TagMode::Explicit,
107                value: *params,
108            }),
109            &self
110                .public_key
111                .map(|pk| {
112                    BitStringRef::from_bytes(pk).map(|value| ContextSpecific {
113                        tag_number: PUBLIC_KEY_TAG,
114                        tag_mode: TagMode::Explicit,
115                        value,
116                    })
117                })
118                .transpose()?,
119        ])
120    }
121}
122
123impl<'a> TryFrom<&'a [u8]> for EcPrivateKey<'a> {
124    type Error = Error;
125
126    fn try_from(bytes: &'a [u8]) -> Result<EcPrivateKey<'a>> {
127        Ok(Self::from_der(bytes)?)
128    }
129}
130
131impl<'a> fmt::Debug for EcPrivateKey<'a> {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        f.debug_struct("EcPrivateKey")
134            .field("parameters", &self.parameters)
135            .field("public_key", &self.public_key)
136            .finish_non_exhaustive()
137    }
138}
139
140#[cfg(feature = "alloc")]
141#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
142impl TryFrom<EcPrivateKey<'_>> for SecretDocument {
143    type Error = Error;
144
145    fn try_from(private_key: EcPrivateKey<'_>) -> Result<Self> {
146        SecretDocument::try_from(&private_key)
147    }
148}
149
150#[cfg(feature = "alloc")]
151#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
152impl TryFrom<&EcPrivateKey<'_>> for SecretDocument {
153    type Error = Error;
154
155    fn try_from(private_key: &EcPrivateKey<'_>) -> Result<Self> {
156        Ok(Self::encode_msg(private_key)?)
157    }
158}
159
160#[cfg(feature = "pem")]
161#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
162impl PemLabel for EcPrivateKey<'_> {
163    const PEM_LABEL: &'static str = "EC PRIVATE KEY";
164}