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_ec_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_ec_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
132                .as_const()
133                .marshal_rfc5208_private_key(Version::V1)?,
134        ))
135    }
136
137    /// Constructs an ECDSA key pair from the private key and public key bytes
138    ///
139    /// The private key must encoded as a big-endian fixed-length integer. For
140    /// example, a P-256 private key must be 32 bytes prefixed with leading
141    /// zeros as needed.
142    ///
143    /// The public key is encoding in uncompressed form using the
144    /// Octet-String-to-Elliptic-Curve-Point algorithm in
145    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
146    ///
147    /// This is intended for use by code that deserializes key pairs. It is
148    /// recommended to use `EcdsaKeyPair::from_pkcs8()` (with a PKCS#8-encoded
149    /// key) instead.
150    ///
151    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]:
152    ///     http://www.secg.org/sec1-v2.pdf
153    ///
154    /// # Errors
155    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
156    pub fn from_private_key_and_public_key(
157        alg: &'static EcdsaSigningAlgorithm,
158        private_key: &[u8],
159        public_key: &[u8],
160    ) -> Result<Self, KeyRejected> {
161        let priv_evp_pkey = parse_sec1_private_bn(private_key, alg.id.nid())?;
162        let pub_evp_pkey = parse_sec1_public_point(public_key, alg.id.nid())?;
163        // EVP_PKEY_cmp only compares params and public key
164        if !priv_evp_pkey.eq(&pub_evp_pkey) {
165            return Err(KeyRejected::inconsistent_components());
166        }
167
168        let key_pair = Self::new(alg, priv_evp_pkey)?;
169        Ok(key_pair)
170    }
171
172    /// Deserializes a DER-encoded private key structure to produce a `EcdsaKeyPair`.
173    ///
174    /// This function is typically used to deserialize RFC 5915 encoded private keys, but it will
175    /// attempt to automatically detect other key formats. This function supports unencrypted
176    /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats.
177    ///
178    /// See `EcdsaPrivateKey::as_der`.
179    ///
180    /// # Errors
181    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
182    ///
183    /// # Panics
184    pub fn from_private_key_der(
185        alg: &'static EcdsaSigningAlgorithm,
186        private_key: &[u8],
187    ) -> Result<Self, KeyRejected> {
188        let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(private_key, EVP_PKEY_EC)
189            .or(parse_rfc5915_private_key(private_key, alg.id.nid()))?;
190        #[cfg(not(feature = "fips"))]
191        verify_evp_key_nid(&evp_pkey.as_const(), alg.id.nid())?;
192        #[cfg(feature = "fips")]
193        validate_ec_evp_key(&evp_pkey.as_const(), alg.id.nid())?;
194
195        Ok(Self::new(alg, evp_pkey)?)
196    }
197
198    /// Access functions related to the private key.
199    #[must_use]
200    pub fn private_key(&self) -> PrivateKey<'_> {
201        PrivateKey(self)
202    }
203
204    /// [`EcdsaSigningAlgorithm`] which was used to create this [`EcdsaKeyPair`]
205    #[must_use]
206    pub fn algorithm(&self) -> &'static EcdsaSigningAlgorithm {
207        self.algorithm
208    }
209
210    /// Returns the signature of the message using a random nonce.
211    ///
212    /// # *ring* Compatibility
213    /// Our implementation ignores the `SecureRandom` parameter.
214    ///
215    /// # Errors
216    /// `error::Unspecified` on internal error.
217    //
218    // # FIPS
219    // The following conditions must be met:
220    // * NIST Elliptic Curves: P256, P384, P521
221    // * Digest Algorithms: SHA256, SHA384, SHA512
222    #[inline]
223    pub fn sign(&self, _rng: &dyn SecureRandom, message: &[u8]) -> Result<Signature, Unspecified> {
224        let out_sig = self.evp_pkey.sign(
225            message,
226            Some(self.algorithm.digest),
227            No_EVP_PKEY_CTX_consumer,
228        )?;
229
230        Ok(match self.algorithm.sig_format {
231            EcdsaSignatureFormat::ASN1 => Signature::new(|slice| {
232                slice[..out_sig.len()].copy_from_slice(&out_sig);
233                out_sig.len()
234            }),
235            EcdsaSignatureFormat::Fixed => ec::ecdsa_asn1_to_fixed(self.algorithm.id, &out_sig)?,
236        })
237    }
238}
239
240/// Elliptic curve private key.
241pub struct PrivateKey<'a>(&'a EcdsaKeyPair);
242
243impl Debug for PrivateKey<'_> {
244    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
245        f.write_str(&format!("EcdsaPrivateKey({:?})", self.0.algorithm.id))
246    }
247}
248
249impl AsBigEndian<EcPrivateKeyBin<'static>> for PrivateKey<'_> {
250    /// Exposes the private key encoded as a big-endian fixed-length integer.
251    ///
252    /// For most use-cases, `EcdsaKeyPair::to_pkcs8()` should be preferred.
253    ///
254    /// # Errors
255    /// `error::Unspecified` if serialization failed.
256    fn as_be_bytes(&self) -> Result<EcPrivateKeyBin<'static>, Unspecified> {
257        let buffer = marshal_sec1_private_key(&self.0.evp_pkey)?;
258        Ok(EcPrivateKeyBin::new(buffer))
259    }
260}
261
262impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey<'_> {
263    /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure.
264    ///
265    /// # Errors
266    /// `error::Unspecified`  if serialization failed.
267    fn as_der(&self) -> Result<EcPrivateKeyRfc5915Der<'static>, Unspecified> {
268        let bytes = marshal_rfc5915_private_key(&self.0.evp_pkey)?;
269        Ok(EcPrivateKeyRfc5915Der::new(bytes))
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use crate::encoding::AsDer;
276    use crate::signature::{
277        EcdsaKeyPair, ECDSA_P256K1_SHA256_ASN1_SIGNING, ECDSA_P256_SHA256_FIXED_SIGNING,
278        ECDSA_P384_SHA3_384_FIXED_SIGNING, ECDSA_P521_SHA512_FIXED_SIGNING,
279    };
280
281    #[test]
282    fn test_reject_wrong_curve() {
283        let supported_algs = [
284            &ECDSA_P256_SHA256_FIXED_SIGNING,
285            &ECDSA_P384_SHA3_384_FIXED_SIGNING,
286            &ECDSA_P521_SHA512_FIXED_SIGNING,
287            &ECDSA_P256K1_SHA256_ASN1_SIGNING,
288        ];
289
290        for marshal_alg in supported_algs {
291            let key_pair = EcdsaKeyPair::generate(marshal_alg).unwrap();
292            let key_pair_doc = key_pair.to_pkcs8v1().unwrap();
293            let key_pair_bytes = key_pair_doc.as_ref();
294
295            for parse_alg in supported_algs {
296                if parse_alg == marshal_alg {
297                    continue;
298                }
299
300                let result = EcdsaKeyPair::from_private_key_der(parse_alg, key_pair_bytes);
301                assert!(result.is_err());
302            }
303        }
304    }
305
306    #[test]
307    fn test_from_private_key_der() {
308        let key_pair = EcdsaKeyPair::generate(&ECDSA_P256_SHA256_FIXED_SIGNING).unwrap();
309
310        let bytes_5208 = key_pair.to_pkcs8v1().unwrap();
311        let bytes_5915 = key_pair.private_key().as_der().unwrap();
312
313        let key_pair_5208 = EcdsaKeyPair::from_private_key_der(
314            &ECDSA_P256_SHA256_FIXED_SIGNING,
315            bytes_5208.as_ref(),
316        )
317        .unwrap();
318        let key_pair_5915 = EcdsaKeyPair::from_private_key_der(
319            &ECDSA_P256_SHA256_FIXED_SIGNING,
320            bytes_5915.as_ref(),
321        )
322        .unwrap();
323
324        assert_eq!(key_pair.evp_pkey, key_pair_5208.evp_pkey);
325        assert_eq!(key_pair.evp_pkey, key_pair_5915.evp_pkey);
326        assert_eq!(key_pair_5208.evp_pkey, key_pair_5915.evp_pkey);
327        assert_eq!(key_pair_5915.algorithm, &ECDSA_P256_SHA256_FIXED_SIGNING);
328    }
329}