aws_lc_rs/ec/
key_pair.rs

1// Copyright 2015-2016 Brian Smith.
2// SPDX-License-Identifier: ISC
3// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4// SPDX-License-Identifier: Apache-2.0 OR ISC
5
6use crate::aws_lc::{EVP_PKEY, EVP_PKEY_EC};
7use core::fmt;
8use core::fmt::{Debug, Formatter};
9
10use crate::ec::evp_key_generate;
11use crate::ec::signature::{EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey};
12#[cfg(feature = "fips")]
13use crate::ec::validate_evp_key;
14#[cfg(not(feature = "fips"))]
15use crate::ec::verify_evp_key_nid;
16
17use crate::ec;
18use crate::ec::encoding::rfc5915::{marshal_rfc5915_private_key, parse_rfc5915_private_key};
19use crate::ec::encoding::sec1::{
20    marshal_sec1_private_key, parse_sec1_private_bn, parse_sec1_public_point,
21};
22use crate::encoding::{AsBigEndian, AsDer, EcPrivateKeyBin, EcPrivateKeyRfc5915Der};
23use crate::error::{KeyRejected, Unspecified};
24use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
25use crate::pkcs8::{Document, Version};
26use crate::ptr::LcPtr;
27use crate::rand::SecureRandom;
28use crate::signature::{KeyPair, Signature};
29
30/// An ECDSA key pair, used for signing.
31#[allow(clippy::module_name_repetitions)]
32pub struct EcdsaKeyPair {
33    algorithm: &'static EcdsaSigningAlgorithm,
34    evp_pkey: LcPtr<EVP_PKEY>,
35    pubkey: PublicKey,
36}
37
38impl Debug for EcdsaKeyPair {
39    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
40        f.write_str(&format!("EcdsaKeyPair {{ public_key: {:?} }}", self.pubkey))
41    }
42}
43
44unsafe impl Send for EcdsaKeyPair {}
45
46unsafe impl Sync for EcdsaKeyPair {}
47
48impl KeyPair for EcdsaKeyPair {
49    type PublicKey = PublicKey;
50
51    #[inline]
52    /// Provides the public key.
53    fn public_key(&self) -> &Self::PublicKey {
54        &self.pubkey
55    }
56}
57
58impl EcdsaKeyPair {
59    #[allow(clippy::needless_pass_by_value)]
60    fn new(
61        algorithm: &'static EcdsaSigningAlgorithm,
62        evp_pkey: LcPtr<EVP_PKEY>,
63    ) -> Result<Self, ()> {
64        let pubkey = ec::signature::public_key_from_evp_pkey(&evp_pkey, algorithm)?;
65
66        Ok(Self {
67            algorithm,
68            evp_pkey,
69            pubkey,
70        })
71    }
72
73    /// Generates a new key pair.
74    ///
75    /// # Errors
76    /// `error::Unspecified` on internal error.
77    ///
78    pub fn generate(alg: &'static EcdsaSigningAlgorithm) -> Result<Self, Unspecified> {
79        let evp_pkey = evp_key_generate(alg.0.id.nid())?;
80
81        Ok(Self::new(alg, evp_pkey)?)
82    }
83
84    /// Constructs an ECDSA key pair by parsing an unencrypted PKCS#8 v1
85    /// id-ecPublicKey `ECPrivateKey` key.
86    ///
87    /// # Errors
88    /// `error::KeyRejected` if bytes do not encode an ECDSA key pair or if the key is otherwise not
89    /// acceptable.
90    pub fn from_pkcs8(
91        alg: &'static EcdsaSigningAlgorithm,
92        pkcs8: &[u8],
93    ) -> Result<Self, KeyRejected> {
94        // Includes a call to `EC_KEY_check_key`
95        let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(pkcs8, EVP_PKEY_EC)?;
96
97        #[cfg(not(feature = "fips"))]
98        verify_evp_key_nid(&evp_pkey.as_const(), alg.id.nid())?;
99        #[cfg(feature = "fips")]
100        validate_evp_key(&evp_pkey.as_const(), alg.id.nid())?;
101
102        let key_pair = Self::new(alg, evp_pkey)?;
103
104        Ok(key_pair)
105    }
106
107    /// Generates a new key pair and returns the key pair serialized as a
108    /// PKCS#8 v1 document.
109    ///
110    /// # *ring* Compatibility
111    /// Our implementation ignores the `SecureRandom` parameter.
112    ///
113    /// # Errors
114    /// `error::Unspecified` on internal error.
115    pub fn generate_pkcs8(
116        alg: &'static EcdsaSigningAlgorithm,
117        _rng: &dyn SecureRandom,
118    ) -> Result<Document, Unspecified> {
119        let key_pair = Self::generate(alg)?;
120
121        key_pair.to_pkcs8v1()
122    }
123
124    /// Serializes this `EcdsaKeyPair` into a PKCS#8 v1 document.
125    ///
126    /// # Errors
127    /// `error::Unspecified` on internal error.
128    ///
129    pub fn to_pkcs8v1(&self) -> Result<Document, Unspecified> {
130        Ok(Document::new(
131            self.evp_pkey.marshal_rfc5208_private_key(Version::V1)?,
132        ))
133    }
134
135    /// Constructs an ECDSA key pair from the private key and public key bytes
136    ///
137    /// The private key must encoded as a big-endian fixed-length integer. For
138    /// example, a P-256 private key must be 32 bytes prefixed with leading
139    /// zeros as needed.
140    ///
141    /// The public key is encoding in uncompressed form using the
142    /// Octet-String-to-Elliptic-Curve-Point algorithm in
143    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
144    ///
145    /// This is intended for use by code that deserializes key pairs. It is
146    /// recommended to use `EcdsaKeyPair::from_pkcs8()` (with a PKCS#8-encoded
147    /// key) instead.
148    ///
149    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]:
150    ///     http://www.secg.org/sec1-v2.pdf
151    ///
152    /// # Errors
153    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
154    pub fn from_private_key_and_public_key(
155        alg: &'static EcdsaSigningAlgorithm,
156        private_key: &[u8],
157        public_key: &[u8],
158    ) -> Result<Self, KeyRejected> {
159        let priv_evp_pkey = parse_sec1_private_bn(private_key, alg.id.nid())?;
160        let pub_evp_pkey = parse_sec1_public_point(public_key, alg.id.nid())?;
161        // EVP_PKEY_cmp only compares params and public key
162        if !priv_evp_pkey.eq(&pub_evp_pkey) {
163            return Err(KeyRejected::inconsistent_components());
164        }
165
166        let key_pair = Self::new(alg, priv_evp_pkey)?;
167        Ok(key_pair)
168    }
169
170    /// Deserializes a DER-encoded private key structure to produce a `EcdsaKeyPair`.
171    ///
172    /// This function is typically used to deserialize RFC 5915 encoded private keys, but it will
173    /// attempt to automatically detect other key formats. This function supports unencrypted
174    /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats.
175    ///
176    /// See `EcdsaPrivateKey::as_der`.
177    ///
178    /// # Errors
179    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
180    ///
181    /// # Panics
182    pub fn from_private_key_der(
183        alg: &'static EcdsaSigningAlgorithm,
184        private_key: &[u8],
185    ) -> Result<Self, KeyRejected> {
186        let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(private_key, EVP_PKEY_EC)
187            .or(parse_rfc5915_private_key(private_key, alg.id.nid()))?;
188        #[cfg(not(feature = "fips"))]
189        verify_evp_key_nid(&evp_pkey.as_const(), alg.id.nid())?;
190        #[cfg(feature = "fips")]
191        validate_evp_key(&evp_pkey.as_const(), alg.id.nid())?;
192
193        Ok(Self::new(alg, evp_pkey)?)
194    }
195
196    /// Access functions related to the private key.
197    #[must_use]
198    pub fn private_key(&self) -> PrivateKey<'_> {
199        PrivateKey(self)
200    }
201
202    /// Returns the signature of the message using a random nonce.
203    ///
204    /// # *ring* Compatibility
205    /// Our implementation ignores the `SecureRandom` parameter.
206    ///
207    /// # Errors
208    /// `error::Unspecified` on internal error.
209    //
210    // # FIPS
211    // The following conditions must be met:
212    // * NIST Elliptic Curves: P256, P384, P521
213    // * Digest Algorithms: SHA256, SHA384, SHA512
214    #[inline]
215    pub fn sign(&self, _rng: &dyn SecureRandom, message: &[u8]) -> Result<Signature, Unspecified> {
216        let out_sig = self.evp_pkey.sign(
217            message,
218            Some(self.algorithm.digest),
219            No_EVP_PKEY_CTX_consumer,
220        )?;
221
222        Ok(match self.algorithm.sig_format {
223            EcdsaSignatureFormat::ASN1 => Signature::new(|slice| {
224                slice[..out_sig.len()].copy_from_slice(&out_sig);
225                out_sig.len()
226            }),
227            EcdsaSignatureFormat::Fixed => ec::ecdsa_asn1_to_fixed(self.algorithm.id, &out_sig)?,
228        })
229    }
230}
231
232/// Elliptic curve private key.
233pub struct PrivateKey<'a>(&'a EcdsaKeyPair);
234
235impl Debug for PrivateKey<'_> {
236    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
237        f.write_str(&format!("EcdsaPrivateKey({:?})", self.0.algorithm.id))
238    }
239}
240
241impl AsBigEndian<EcPrivateKeyBin<'static>> for PrivateKey<'_> {
242    /// Exposes the private key encoded as a big-endian fixed-length integer.
243    ///
244    /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred.
245    ///
246    /// # Errors
247    /// `error::Unspecified` if serialization failed.
248    fn as_be_bytes(&self) -> Result<EcPrivateKeyBin<'static>, Unspecified> {
249        let buffer = marshal_sec1_private_key(&self.0.evp_pkey)?;
250        Ok(EcPrivateKeyBin::new(buffer))
251    }
252}
253
254impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey<'_> {
255    /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure.
256    ///
257    /// # Errors
258    /// `error::Unspecified`  if serialization failed.
259    fn as_der(&self) -> Result<EcPrivateKeyRfc5915Der<'static>, Unspecified> {
260        let bytes = marshal_rfc5915_private_key(&self.0.evp_pkey)?;
261        Ok(EcPrivateKeyRfc5915Der::new(bytes))
262    }
263}
264
265#[cfg(test)]
266mod tests {
267    use crate::encoding::AsDer;
268    use crate::signature::{
269        EcdsaKeyPair, ECDSA_P256K1_SHA256_ASN1_SIGNING, ECDSA_P256_SHA256_FIXED_SIGNING,
270        ECDSA_P384_SHA3_384_FIXED_SIGNING, ECDSA_P521_SHA512_FIXED_SIGNING,
271    };
272
273    #[test]
274    fn test_reject_wrong_curve() {
275        let supported_algs = [
276            &ECDSA_P256_SHA256_FIXED_SIGNING,
277            &ECDSA_P384_SHA3_384_FIXED_SIGNING,
278            &ECDSA_P521_SHA512_FIXED_SIGNING,
279            &ECDSA_P256K1_SHA256_ASN1_SIGNING,
280        ];
281
282        for marshal_alg in supported_algs {
283            let key_pair = EcdsaKeyPair::generate(marshal_alg).unwrap();
284            let key_pair_doc = key_pair.to_pkcs8v1().unwrap();
285            let key_pair_bytes = key_pair_doc.as_ref();
286
287            for parse_alg in supported_algs {
288                if parse_alg == marshal_alg {
289                    continue;
290                }
291
292                let result = EcdsaKeyPair::from_private_key_der(parse_alg, key_pair_bytes);
293                assert!(result.is_err());
294            }
295        }
296    }
297
298    #[test]
299    fn test_from_private_key_der() {
300        let key_pair = EcdsaKeyPair::generate(&ECDSA_P256_SHA256_FIXED_SIGNING).unwrap();
301
302        let bytes_5208 = key_pair.to_pkcs8v1().unwrap();
303        let bytes_5915 = key_pair.private_key().as_der().unwrap();
304
305        let key_pair_5208 = EcdsaKeyPair::from_private_key_der(
306            &ECDSA_P256_SHA256_FIXED_SIGNING,
307            bytes_5208.as_ref(),
308        )
309        .unwrap();
310        let key_pair_5915 = EcdsaKeyPair::from_private_key_der(
311            &ECDSA_P256_SHA256_FIXED_SIGNING,
312            bytes_5915.as_ref(),
313        )
314        .unwrap();
315
316        assert_eq!(key_pair.evp_pkey, key_pair_5208.evp_pkey);
317        assert_eq!(key_pair.evp_pkey, key_pair_5915.evp_pkey);
318        assert_eq!(key_pair_5208.evp_pkey, key_pair_5915.evp_pkey);
319    }
320}