Skip to main content

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