aws_lc_rs/
agreement.rs

1// Copyright 2015-2017 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
6//! Key Agreement: ECDH, including X25519.
7//!
8//! # Example
9//!
10//! Note that this example uses X25519, but ECDH using NIST P-256/P-384 is done
11//! exactly the same way, just substituting
12//! `agreement::ECDH_P256`/`agreement::ECDH_P384` for `agreement::X25519`.
13//!
14//! ```
15//! use aws_lc_rs::{agreement, rand};
16//!
17//! let rng = rand::SystemRandom::new();
18//!
19//! let my_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
20//!
21//! // Make `my_public_key` a byte slice containing my public key. In a real
22//! // application, this would be sent to the peer in an encoded protocol
23//! // message.
24//! let my_public_key = my_private_key.compute_public_key()?;
25//!
26//! let peer_public_key = {
27//!     // In a real application, the peer public key would be parsed out of a
28//!     // protocol message. Here we just generate one.
29//!     let peer_public_key = {
30//!         let peer_private_key =
31//!             agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
32//!         peer_private_key.compute_public_key()?
33//!     };
34//!
35//!     agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key)
36//! };
37//!
38//! agreement::agree_ephemeral(
39//!     my_private_key,
40//!     &peer_public_key,
41//!     aws_lc_rs::error::Unspecified,
42//!     |_key_material| {
43//!         // In a real application, we'd apply a KDF to the key material and the
44//!         // public keys (as recommended in RFC 7748) and then derive session
45//!         // keys from the result. We omit all that here.
46//!         Ok(())
47//!     },
48//! )?;
49//!
50//! # Ok::<(), aws_lc_rs::error::Unspecified>(())
51//! ```
52mod ephemeral;
53
54use crate::ec::encoding::sec1::{
55    marshal_sec1_private_key, marshal_sec1_public_point, marshal_sec1_public_point_into_buffer,
56    parse_sec1_private_bn, parse_sec1_public_point,
57};
58#[cfg(not(feature = "fips"))]
59use crate::ec::verify_evp_key_nid;
60use crate::ec::{evp_key_generate, validate_ec_evp_key};
61use crate::error::{KeyRejected, Unspecified};
62use crate::hex;
63pub use ephemeral::{agree_ephemeral, EphemeralPrivateKey};
64
65use crate::aws_lc::{
66    i2d_ECPrivateKey, EVP_PKEY_get0_EC_KEY, NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1,
67    EVP_PKEY, EVP_PKEY_EC, EVP_PKEY_X25519, NID_X25519,
68};
69
70use crate::buffer::Buffer;
71use crate::ec;
72use crate::ec::encoding::rfc5915::parse_rfc5915_private_key;
73use crate::encoding::{
74    AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der,
75    EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, Pkcs8V1Der, PublicKeyX509Der,
76};
77use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
78use crate::pkcs8::Version;
79use crate::ptr::LcPtr;
80use core::fmt;
81use core::fmt::{Debug, Formatter};
82use core::ptr::null_mut;
83
84#[allow(non_camel_case_types)]
85#[derive(PartialEq, Eq)]
86enum AlgorithmID {
87    ECDH_P256,
88    ECDH_P384,
89    ECDH_P521,
90    X25519,
91}
92
93impl AlgorithmID {
94    #[inline]
95    const fn nid(&self) -> i32 {
96        match self {
97            AlgorithmID::ECDH_P256 => NID_X9_62_prime256v1,
98            AlgorithmID::ECDH_P384 => NID_secp384r1,
99            AlgorithmID::ECDH_P521 => NID_secp521r1,
100            AlgorithmID::X25519 => NID_X25519,
101        }
102    }
103
104    // Uncompressed public key length in bytes
105    #[inline]
106    const fn pub_key_len(&self) -> usize {
107        match self {
108            AlgorithmID::ECDH_P256 => ec::uncompressed_public_key_size_bytes(256),
109            AlgorithmID::ECDH_P384 => ec::uncompressed_public_key_size_bytes(384),
110            AlgorithmID::ECDH_P521 => ec::uncompressed_public_key_size_bytes(521),
111            AlgorithmID::X25519 => 32,
112        }
113    }
114
115    #[inline]
116    const fn private_key_len(&self) -> usize {
117        match self {
118            AlgorithmID::ECDH_P256 | AlgorithmID::X25519 => 32,
119            AlgorithmID::ECDH_P384 => 48,
120            AlgorithmID::ECDH_P521 => 66,
121        }
122    }
123}
124
125impl Debug for AlgorithmID {
126    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
127        let output = match self {
128            AlgorithmID::ECDH_P256 => "curve: P256",
129            AlgorithmID::ECDH_P384 => "curve: P384",
130            AlgorithmID::ECDH_P521 => "curve: P521",
131            AlgorithmID::X25519 => "curve: Curve25519",
132        };
133        f.write_str(output)
134    }
135}
136
137/// A key agreement algorithm.
138#[derive(PartialEq, Eq)]
139pub struct Algorithm {
140    id: AlgorithmID,
141}
142
143impl Debug for Algorithm {
144    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
145        f.write_str(&format!("Algorithm {{ {:?} }}", self.id))
146    }
147}
148
149/// ECDH using the NSA Suite B P-256 (secp256r1) curve.
150pub const ECDH_P256: Algorithm = Algorithm {
151    id: AlgorithmID::ECDH_P256,
152};
153
154/// ECDH using the NSA Suite B P-384 (secp384r1) curve.
155pub const ECDH_P384: Algorithm = Algorithm {
156    id: AlgorithmID::ECDH_P384,
157};
158
159/// ECDH using the NSA Suite B P-521 (secp521r1) curve.
160pub const ECDH_P521: Algorithm = Algorithm {
161    id: AlgorithmID::ECDH_P521,
162};
163
164/// X25519 (ECDH using Curve25519) as described in [RFC 7748].
165///
166/// Everything is as described in RFC 7748. Key agreement will fail if the
167/// result of the X25519 operation is zero; see the notes on the
168/// "all-zero value" in [RFC 7748 section 6.1].
169///
170/// [RFC 7748]: https://tools.ietf.org/html/rfc7748
171/// [RFC 7748 section 6.1]: https://tools.ietf.org/html/rfc7748#section-6.1
172pub const X25519: Algorithm = Algorithm {
173    id: AlgorithmID::X25519,
174};
175
176#[allow(non_camel_case_types)]
177enum KeyInner {
178    ECDH_P256(LcPtr<EVP_PKEY>),
179    ECDH_P384(LcPtr<EVP_PKEY>),
180    ECDH_P521(LcPtr<EVP_PKEY>),
181    X25519(LcPtr<EVP_PKEY>),
182}
183
184impl Clone for KeyInner {
185    fn clone(&self) -> KeyInner {
186        match self {
187            KeyInner::ECDH_P256(evp_pkey) => KeyInner::ECDH_P256(evp_pkey.clone()),
188            KeyInner::ECDH_P384(evp_pkey) => KeyInner::ECDH_P384(evp_pkey.clone()),
189            KeyInner::ECDH_P521(evp_pkey) => KeyInner::ECDH_P521(evp_pkey.clone()),
190            KeyInner::X25519(evp_pkey) => KeyInner::X25519(evp_pkey.clone()),
191        }
192    }
193}
194
195/// A private key for use (only) with `agree`. The
196/// signature of `agree` allows `PrivateKey` to be
197/// used for more than one key agreement.
198pub struct PrivateKey {
199    inner_key: KeyInner,
200}
201
202impl KeyInner {
203    #[inline]
204    fn algorithm(&self) -> &'static Algorithm {
205        match self {
206            KeyInner::ECDH_P256(..) => &ECDH_P256,
207            KeyInner::ECDH_P384(..) => &ECDH_P384,
208            KeyInner::ECDH_P521(..) => &ECDH_P521,
209            KeyInner::X25519(..) => &X25519,
210        }
211    }
212
213    fn get_evp_pkey(&self) -> &LcPtr<EVP_PKEY> {
214        match self {
215            KeyInner::ECDH_P256(evp_pkey)
216            | KeyInner::ECDH_P384(evp_pkey)
217            | KeyInner::ECDH_P521(evp_pkey)
218            | KeyInner::X25519(evp_pkey) => evp_pkey,
219        }
220    }
221}
222
223// See EVP_PKEY documentation here:
224// https://github.com/aws/aws-lc/blob/125af14c57451565b875fbf1282a38a6ecf83782/include/openssl/evp.h#L83-L89
225// An |EVP_PKEY| object represents a public or private key. A given object may
226// be used concurrently on multiple threads by non-mutating functions, provided
227// no other thread is concurrently calling a mutating function. Unless otherwise
228// documented, functions which take a |const| pointer are non-mutating and
229// functions which take a non-|const| pointer are mutating.
230unsafe impl Send for PrivateKey {}
231unsafe impl Sync for PrivateKey {}
232
233impl Debug for PrivateKey {
234    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
235        f.write_str(&format!(
236            "PrivateKey {{ algorithm: {:?} }}",
237            self.inner_key.algorithm()
238        ))
239    }
240}
241
242impl PrivateKey {
243    fn new(alg: &'static Algorithm, evp_pkey: LcPtr<EVP_PKEY>) -> Self {
244        match alg.id {
245            AlgorithmID::X25519 => Self {
246                inner_key: KeyInner::X25519(evp_pkey),
247            },
248            AlgorithmID::ECDH_P256 => Self {
249                inner_key: KeyInner::ECDH_P256(evp_pkey),
250            },
251            AlgorithmID::ECDH_P384 => Self {
252                inner_key: KeyInner::ECDH_P384(evp_pkey),
253            },
254            AlgorithmID::ECDH_P521 => Self {
255                inner_key: KeyInner::ECDH_P521(evp_pkey),
256            },
257        }
258    }
259
260    #[inline]
261    /// Generate a new private key for the given algorithm.
262    // # FIPS
263    // Use this function with one of the following algorithms:
264    // * `ECDH_P256`
265    // * `ECDH_P384`
266    // * `ECDH_P521`
267    //
268    /// # Errors
269    /// `error::Unspecified` when operation fails due to internal error.
270    pub fn generate(alg: &'static Algorithm) -> Result<Self, Unspecified> {
271        let evp_pkey = match alg.id {
272            AlgorithmID::X25519 => generate_x25519()?,
273            _ => evp_key_generate(alg.id.nid())?,
274        };
275        Ok(Self::new(alg, evp_pkey))
276    }
277
278    /// Deserializes a DER-encoded private key structure to produce a `agreement::PrivateKey`.
279    ///
280    /// This function is typically used to deserialize RFC 5915 encoded private keys, but it will
281    /// attempt to automatically detect other key formats. This function supports unencrypted
282    /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats.
283    ///
284    /// X25519 keys are not supported. See `PrivateKey::as_der`.
285    ///
286    /// # Errors
287    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
288    ///
289    /// # Panics
290    pub fn from_private_key_der(
291        alg: &'static Algorithm,
292        key_bytes: &[u8],
293    ) -> Result<Self, KeyRejected> {
294        if AlgorithmID::X25519 == alg.id {
295            return Err(KeyRejected::invalid_encoding());
296        }
297        let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(key_bytes, EVP_PKEY_EC)
298            .or(parse_rfc5915_private_key(key_bytes, alg.id.nid()))?;
299        #[cfg(not(feature = "fips"))]
300        verify_evp_key_nid(&evp_pkey.as_const(), alg.id.nid())?;
301        #[cfg(feature = "fips")]
302        validate_ec_evp_key(&evp_pkey.as_const(), alg.id.nid())?;
303
304        Ok(Self::new(alg, evp_pkey))
305    }
306
307    /// Constructs an ECDH key from private key bytes
308    ///
309    /// The private key must encoded as a big-endian fixed-length integer. For
310    /// example, a P-256 private key must be 32 bytes prefixed with leading
311    /// zeros as needed.
312    ///
313    /// # Errors
314    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
315    pub fn from_private_key(
316        alg: &'static Algorithm,
317        key_bytes: &[u8],
318    ) -> Result<Self, KeyRejected> {
319        if key_bytes.len() != alg.id.private_key_len() {
320            return Err(KeyRejected::wrong_algorithm());
321        }
322        let evp_pkey = if AlgorithmID::X25519 == alg.id {
323            LcPtr::<EVP_PKEY>::parse_raw_private_key(key_bytes, EVP_PKEY_X25519)?
324        } else {
325            parse_sec1_private_bn(key_bytes, alg.id.nid())?
326        };
327        Ok(Self::new(alg, evp_pkey))
328    }
329
330    #[cfg(test)]
331    #[allow(missing_docs, clippy::missing_errors_doc)]
332    pub fn generate_for_test(
333        alg: &'static Algorithm,
334        rng: &dyn crate::rand::SecureRandom,
335    ) -> Result<Self, Unspecified> {
336        match alg.id {
337            AlgorithmID::X25519 => {
338                let mut priv_key = [0u8; AlgorithmID::X25519.private_key_len()];
339                rng.fill(&mut priv_key)?;
340                Self::from_x25519_private_key(&priv_key)
341            }
342            AlgorithmID::ECDH_P256 => {
343                let mut priv_key = [0u8; AlgorithmID::ECDH_P256.private_key_len()];
344                rng.fill(&mut priv_key)?;
345                Self::from_p256_private_key(&priv_key)
346            }
347            AlgorithmID::ECDH_P384 => {
348                let mut priv_key = [0u8; AlgorithmID::ECDH_P384.private_key_len()];
349                rng.fill(&mut priv_key)?;
350                Self::from_p384_private_key(&priv_key)
351            }
352            AlgorithmID::ECDH_P521 => {
353                let mut priv_key = [0u8; AlgorithmID::ECDH_P521.private_key_len()];
354                rng.fill(&mut priv_key)?;
355                Self::from_p521_private_key(&priv_key)
356            }
357        }
358    }
359
360    #[cfg(test)]
361    fn from_x25519_private_key(
362        priv_key: &[u8; AlgorithmID::X25519.private_key_len()],
363    ) -> Result<Self, Unspecified> {
364        let pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(priv_key, EVP_PKEY_X25519)?;
365
366        Ok(PrivateKey {
367            inner_key: KeyInner::X25519(pkey),
368        })
369    }
370
371    #[cfg(test)]
372    fn from_p256_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
373        let pkey = parse_sec1_private_bn(priv_key, ECDH_P256.id.nid())?;
374        Ok(PrivateKey {
375            inner_key: KeyInner::ECDH_P256(pkey),
376        })
377    }
378
379    #[cfg(test)]
380    fn from_p384_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
381        let pkey = parse_sec1_private_bn(priv_key, ECDH_P384.id.nid())?;
382        Ok(PrivateKey {
383            inner_key: KeyInner::ECDH_P384(pkey),
384        })
385    }
386
387    #[cfg(test)]
388    fn from_p521_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
389        let pkey = parse_sec1_private_bn(priv_key, ECDH_P521.id.nid())?;
390        Ok(PrivateKey {
391            inner_key: KeyInner::ECDH_P521(pkey),
392        })
393    }
394
395    /// Computes the public key from the private key.
396    ///
397    /// # Errors
398    /// `error::Unspecified` when operation fails due to internal error.
399    pub fn compute_public_key(&self) -> Result<PublicKey, Unspecified> {
400        match &self.inner_key {
401            KeyInner::ECDH_P256(evp_pkey)
402            | KeyInner::ECDH_P384(evp_pkey)
403            | KeyInner::ECDH_P521(evp_pkey) => {
404                let mut public_key = [0u8; MAX_PUBLIC_KEY_LEN];
405                let len = marshal_sec1_public_point_into_buffer(&mut public_key, evp_pkey, false)?;
406                Ok(PublicKey {
407                    inner_key: self.inner_key.clone(),
408                    key_bytes: public_key,
409                    len,
410                })
411            }
412            KeyInner::X25519(priv_key) => {
413                let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN];
414                let out_len = priv_key
415                    .as_const()
416                    .marshal_raw_public_to_buffer(&mut buffer)?;
417                Ok(PublicKey {
418                    inner_key: self.inner_key.clone(),
419                    key_bytes: buffer,
420                    len: out_len,
421                })
422            }
423        }
424    }
425
426    /// The algorithm for the private key.
427    #[inline]
428    #[must_use]
429    pub fn algorithm(&self) -> &'static Algorithm {
430        self.inner_key.algorithm()
431    }
432}
433
434impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey {
435    /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure.
436    ///
437    /// X25519 is not supported.
438    ///
439    /// # Errors
440    /// `error::Unspecified`  if serialization failed.
441    fn as_der(&self) -> Result<EcPrivateKeyRfc5915Der<'static>, Unspecified> {
442        if AlgorithmID::X25519 == self.inner_key.algorithm().id {
443            return Err(Unspecified);
444        }
445
446        let mut outp = null_mut::<u8>();
447        let ec_key = {
448            self.inner_key
449                .get_evp_pkey()
450                .project_const_lifetime(unsafe {
451                    |evp_pkey| EVP_PKEY_get0_EC_KEY(*evp_pkey.as_const())
452                })?
453        };
454        let length = usize::try_from(unsafe { i2d_ECPrivateKey(*ec_key, &mut outp) })
455            .map_err(|_| Unspecified)?;
456        let mut outp = LcPtr::new(outp)?;
457        Ok(EcPrivateKeyRfc5915Der::take_from_slice(unsafe {
458            core::slice::from_raw_parts_mut(*outp.as_mut(), length)
459        }))
460    }
461}
462
463impl AsDer<Pkcs8V1Der<'static>> for PrivateKey {
464    /// Serializes the key as a PKCS #8 private key structure.
465    ///
466    /// X25519 is not supported.
467    ///
468    /// # Errors
469    /// `error::Unspecified`  if serialization failed.
470    fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
471        if AlgorithmID::X25519 == self.inner_key.algorithm().id {
472            return Err(Unspecified);
473        }
474
475        Ok(Pkcs8V1Der::new(
476            self.inner_key
477                .get_evp_pkey()
478                .as_const()
479                .marshal_rfc5208_private_key(Version::V1)?,
480        ))
481    }
482}
483
484impl AsBigEndian<EcPrivateKeyBin<'static>> for PrivateKey {
485    /// Exposes the private key encoded as a big-endian fixed-length integer.
486    ///
487    /// X25519 is not supported.
488    ///
489    /// # Errors
490    /// `error::Unspecified` if serialization failed.
491    fn as_be_bytes(&self) -> Result<EcPrivateKeyBin<'static>, Unspecified> {
492        if AlgorithmID::X25519 == self.inner_key.algorithm().id {
493            return Err(Unspecified);
494        }
495        let buffer = marshal_sec1_private_key(self.inner_key.get_evp_pkey())?;
496        Ok(EcPrivateKeyBin::new(buffer))
497    }
498}
499
500impl AsBigEndian<Curve25519SeedBin<'static>> for PrivateKey {
501    /// Exposes the seed encoded as a big-endian fixed-length integer.
502    ///
503    /// Only X25519 is supported.
504    ///
505    /// # Errors
506    /// `error::Unspecified` if serialization failed.
507    fn as_be_bytes(&self) -> Result<Curve25519SeedBin<'static>, Unspecified> {
508        if AlgorithmID::X25519 != self.inner_key.algorithm().id {
509            return Err(Unspecified);
510        }
511        let evp_pkey = self.inner_key.get_evp_pkey();
512        Ok(Curve25519SeedBin::new(
513            evp_pkey.as_const().marshal_raw_private_key()?,
514        ))
515    }
516}
517
518pub(crate) fn generate_x25519() -> Result<LcPtr<EVP_PKEY>, Unspecified> {
519    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_X25519, No_EVP_PKEY_CTX_consumer)
520}
521
522const MAX_PUBLIC_KEY_LEN: usize = ec::PUBLIC_KEY_MAX_LEN;
523
524/// A public key for key agreement.
525pub struct PublicKey {
526    inner_key: KeyInner,
527    key_bytes: [u8; MAX_PUBLIC_KEY_LEN],
528    len: usize,
529}
530
531impl PublicKey {
532    /// The algorithm for the public key.
533    #[must_use]
534    pub fn algorithm(&self) -> &'static Algorithm {
535        self.inner_key.algorithm()
536    }
537}
538
539// See EVP_PKEY documentation here:
540// https://github.com/aws/aws-lc/blob/125af14c57451565b875fbf1282a38a6ecf83782/include/openssl/evp.h#L83-L89
541// An |EVP_PKEY| object represents a public or private key. A given object may
542// be used concurrently on multiple threads by non-mutating functions, provided
543// no other thread is concurrently calling a mutating function. Unless otherwise
544// documented, functions which take a |const| pointer are non-mutating and
545// functions which take a non-|const| pointer are mutating.
546unsafe impl Send for PublicKey {}
547unsafe impl Sync for PublicKey {}
548
549impl Debug for PublicKey {
550    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
551        f.write_str(&format!(
552            "PublicKey {{ algorithm: {:?}, bytes: \"{}\" }}",
553            self.inner_key.algorithm(),
554            hex::encode(&self.key_bytes[0..self.len])
555        ))
556    }
557}
558
559impl AsRef<[u8]> for PublicKey {
560    /// Serializes the public key in an uncompressed form (X9.62) using the
561    /// Octet-String-to-Elliptic-Curve-Point algorithm in
562    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
563    fn as_ref(&self) -> &[u8] {
564        &self.key_bytes[0..self.len]
565    }
566}
567
568impl Clone for PublicKey {
569    fn clone(&self) -> Self {
570        PublicKey {
571            inner_key: self.inner_key.clone(),
572            key_bytes: self.key_bytes,
573            len: self.len,
574        }
575    }
576}
577
578impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
579    /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
580    /// # Errors
581    /// Returns an error if the public key fails to marshal to X.509.
582    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> {
583        match &self.inner_key {
584            KeyInner::ECDH_P256(evp_pkey)
585            | KeyInner::ECDH_P384(evp_pkey)
586            | KeyInner::ECDH_P521(evp_pkey)
587            | KeyInner::X25519(evp_pkey) => {
588                let der = evp_pkey.as_const().marshal_rfc5280_public_key()?;
589                Ok(PublicKeyX509Der::from(Buffer::new(der)))
590            }
591        }
592    }
593}
594
595impl AsBigEndian<EcPublicKeyCompressedBin<'static>> for PublicKey {
596    /// Provides the public key elliptic curve point to a compressed point format.
597    /// # Errors
598    /// Returns an error if the underlying implementation is unable to marshal the public key to this format.
599    fn as_be_bytes(&self) -> Result<EcPublicKeyCompressedBin<'static>, crate::error::Unspecified> {
600        let evp_pkey = match &self.inner_key {
601            KeyInner::ECDH_P256(evp_pkey)
602            | KeyInner::ECDH_P384(evp_pkey)
603            | KeyInner::ECDH_P521(evp_pkey) => evp_pkey,
604            KeyInner::X25519(_) => return Err(Unspecified),
605        };
606        let pub_point = marshal_sec1_public_point(evp_pkey, true)?;
607        Ok(EcPublicKeyCompressedBin::new(pub_point))
608    }
609}
610
611impl AsBigEndian<EcPublicKeyUncompressedBin<'static>> for PublicKey {
612    /// Provides the public key elliptic curve point to a compressed point format.
613    ///
614    /// Equivalent to [`PublicKey::as_ref`] for ECDH key types, except that it provides you a copy instead of a reference.
615    ///
616    /// # Errors
617    /// Returns an error if the underlying implementation is unable to marshal the public key to this format.
618    fn as_be_bytes(
619        &self,
620    ) -> Result<EcPublicKeyUncompressedBin<'static>, crate::error::Unspecified> {
621        if self.algorithm().id == AlgorithmID::X25519 {
622            return Err(Unspecified);
623        }
624
625        let mut buffer = vec![0u8; self.len];
626        buffer.copy_from_slice(&self.key_bytes[0..self.len]);
627
628        Ok(EcPublicKeyUncompressedBin::new(buffer))
629    }
630}
631
632/// An unparsed, possibly malformed, public key for key agreement.
633#[derive(Clone)]
634pub struct UnparsedPublicKey<B: AsRef<[u8]>> {
635    alg: &'static Algorithm,
636    bytes: B,
637}
638
639impl<B: Copy + AsRef<[u8]>> Copy for UnparsedPublicKey<B> {}
640
641impl<B: Debug + AsRef<[u8]>> Debug for UnparsedPublicKey<B> {
642    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
643        f.write_str(&format!(
644            "UnparsedPublicKey {{ algorithm: {:?}, bytes: {:?} }}",
645            self.alg,
646            hex::encode(self.bytes.as_ref())
647        ))
648    }
649}
650
651impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
652    /// Constructs a new `UnparsedPublicKey`.
653    pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
654        UnparsedPublicKey {
655            alg: algorithm,
656            bytes,
657        }
658    }
659
660    /// The agreement algorithm associated with this public key
661    pub fn algorithm(&self) -> &'static Algorithm {
662        self.alg
663    }
664
665    /// The bytes provided for this public key
666    pub fn bytes(&self) -> &B {
667        &self.bytes
668    }
669}
670
671/// A parsed public key for key agreement.
672///
673/// This represents a public key that has been successfully parsed and validated
674/// from its encoded form. The key can be used with the `agree` function to
675/// perform key agreement operations.
676#[derive(Debug, Clone)]
677pub struct ParsedPublicKey {
678    format: ParsedPublicKeyFormat,
679    nid: i32,
680    key: LcPtr<EVP_PKEY>,
681    bytes: Box<[u8]>,
682}
683
684// See EVP_PKEY documentation here:
685// https://github.com/aws/aws-lc/blob/125af14c57451565b875fbf1282a38a6ecf83782/include/openssl/evp.h#L83-L89
686// An |EVP_PKEY| object represents a public or private key. A given object may
687// be used concurrently on multiple threads by non-mutating functions, provided
688// no other thread is concurrently calling a mutating function. Unless otherwise
689// documented, functions which take a |const| pointer are non-mutating and
690// functions which take a non-|const| pointer are mutating.
691unsafe impl Send for ParsedPublicKey {}
692unsafe impl Sync for ParsedPublicKey {}
693
694#[derive(Clone, Copy, PartialEq, Eq, Debug)]
695/// The format of a parsed public key.
696///
697/// This is used to distinguish between different types of public key formats
698/// supported by *aws-lc-rs*.
699#[non_exhaustive]
700pub enum ParsedPublicKeyFormat {
701    /// The key is in an X.509 SubjectPublicKeyInfo format.
702    X509,
703    /// The key is in an uncompressed form (X9.62).
704    Uncompressed,
705    /// The key is in a compressed form (SEC 1: Elliptic Curve Cryptography, Version 2.0).
706    Compressed,
707    /// The key is in a hybrid form (SEC 1: Elliptic Curve Cryptography, Version 2.0).
708    Hybrid,
709    /// The key is in a raw form. (X25519 only)
710    Raw,
711    /// The key is in an unknown format.
712    Unknown,
713}
714
715/// A parsed public key for key agreement.
716impl ParsedPublicKey {
717    fn nid(&self) -> i32 {
718        self.nid
719    }
720
721    /// The format of the data the public key was parsed from.
722    #[must_use]
723    pub fn format(&self) -> ParsedPublicKeyFormat {
724        self.format
725    }
726
727    pub(crate) fn key(&self) -> &LcPtr<EVP_PKEY> {
728        &self.key
729    }
730
731    /// The algorithm of the public key.
732    #[must_use]
733    #[allow(non_upper_case_globals)]
734    pub fn alg(&self) -> &'static Algorithm {
735        match self.nid() {
736            NID_X25519 => &X25519,
737            NID_X9_62_prime256v1 => &ECDH_P256,
738            NID_secp384r1 => &ECDH_P384,
739            NID_secp521r1 => &ECDH_P521,
740            _ => unreachable!("Unreachable agreement algorithm nid: {}", self.nid()),
741        }
742    }
743}
744
745impl ParsedPublicKey {
746    #[allow(non_upper_case_globals)]
747    pub(crate) fn new(bytes: impl AsRef<[u8]>, nid: i32) -> Result<Self, KeyRejected> {
748        let bytes = bytes.as_ref().to_vec().into_boxed_slice();
749        if bytes.is_empty() {
750            return Err(KeyRejected::unspecified());
751        }
752        match nid {
753            NID_X25519 => {
754                let format: ParsedPublicKeyFormat;
755                let key = if let Ok(evp_pkey) =
756                    LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(&bytes, EVP_PKEY_X25519)
757                {
758                    format = ParsedPublicKeyFormat::X509;
759                    evp_pkey
760                } else {
761                    format = ParsedPublicKeyFormat::Raw;
762                    try_parse_x25519_public_key_raw_bytes(&bytes)?
763                };
764
765                Ok(ParsedPublicKey {
766                    format,
767                    nid,
768                    key,
769                    bytes,
770                })
771            }
772            NID_X9_62_prime256v1 | NID_secp384r1 | NID_secp521r1 => {
773                let format: ParsedPublicKeyFormat;
774                let key = if let Ok(evp_pkey) =
775                    LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(&bytes, EVP_PKEY_EC)
776                {
777                    validate_ec_evp_key(&evp_pkey.as_const(), nid)?;
778                    format = ParsedPublicKeyFormat::X509;
779                    evp_pkey
780                } else if let Ok(evp_pkey) = parse_sec1_public_point(&bytes, nid) {
781                    format = match bytes[0] {
782                        0x02 | 0x03 => ParsedPublicKeyFormat::Compressed,
783                        0x04 => ParsedPublicKeyFormat::Uncompressed,
784                        0x06 | 0x07 => ParsedPublicKeyFormat::Hybrid,
785                        _ => ParsedPublicKeyFormat::Unknown,
786                    };
787                    evp_pkey
788                } else {
789                    return Err(KeyRejected::invalid_encoding());
790                };
791
792                Ok(ParsedPublicKey {
793                    format,
794                    nid,
795                    key,
796                    bytes,
797                })
798            }
799            _ => Err(KeyRejected::unspecified()),
800        }
801    }
802}
803
804impl AsRef<[u8]> for ParsedPublicKey {
805    /// Returns the original bytes from which this key was parsed.
806    fn as_ref(&self) -> &[u8] {
807        &self.bytes
808    }
809}
810
811impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
812    #[allow(dead_code)]
813    fn parse(&self) -> Result<ParsedPublicKey, KeyRejected> {
814        ParsedPublicKey::new(&self.bytes, self.alg.id.nid())
815    }
816}
817
818impl<B: AsRef<[u8]>> TryFrom<&UnparsedPublicKey<B>> for ParsedPublicKey {
819    type Error = KeyRejected;
820    fn try_from(upk: &UnparsedPublicKey<B>) -> Result<Self, Self::Error> {
821        upk.parse()
822    }
823}
824
825impl<B: AsRef<[u8]>> TryFrom<UnparsedPublicKey<B>> for ParsedPublicKey {
826    type Error = KeyRejected;
827    fn try_from(upk: UnparsedPublicKey<B>) -> Result<Self, Self::Error> {
828        upk.parse()
829    }
830}
831
832/// Performs a key agreement with a private key and the given public key.
833///
834/// `my_private_key` is the private key to use. Only a reference to the key
835/// is required, allowing the key to continue to be used.
836///
837/// `peer_public_key` is the peer's public key. `agree` will return
838/// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve.
839/// `agree` verifies that it is encoded in the standard form for the
840/// algorithm and that the key is *valid*; see the algorithm's documentation for
841/// details on how keys are to be encoded and what constitutes a valid key for
842/// that algorithm.
843///
844/// `error_value` is the value to return if an error occurs before `kdf` is
845/// called, e.g. when decoding of the peer's public key fails or when the public
846/// key is otherwise invalid.
847///
848/// After the key agreement is done, `agree` calls `kdf` with the raw
849/// key material from the key agreement operation and then returns what `kdf`
850/// returns.
851// # FIPS
852// Use this function with one of the following key algorithms:
853// * `ECDH_P256`
854// * `ECDH_P384`
855// * `ECDH_P521`
856//
857/// # Errors
858/// `error_value` on internal failure.
859#[inline]
860#[allow(clippy::missing_panics_doc)]
861pub fn agree<B: TryInto<ParsedPublicKey>, F, R, E>(
862    my_private_key: &PrivateKey,
863    peer_public_key: B,
864    error_value: E,
865    kdf: F,
866) -> Result<R, E>
867where
868    F: FnOnce(&[u8]) -> Result<R, E>,
869{
870    let expected_alg = my_private_key.algorithm();
871
872    let parse_result = peer_public_key.try_into();
873
874    if let Ok(peer_pub_key) = parse_result {
875        if peer_pub_key.alg() != expected_alg {
876            return Err(error_value);
877        }
878        let secret = my_private_key
879            .inner_key
880            .get_evp_pkey()
881            .agree(peer_pub_key.key())
882            .or(Err(error_value))?;
883
884        kdf(secret.as_ref())
885    } else {
886        Err(error_value)
887    }
888}
889
890fn try_parse_x25519_public_key_raw_bytes(key_bytes: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
891    let expected_pub_key_len = X25519.id.pub_key_len();
892    if key_bytes.len() != expected_pub_key_len {
893        return Err(KeyRejected::invalid_encoding());
894    }
895
896    LcPtr::<EVP_PKEY>::parse_raw_public_key(key_bytes, EVP_PKEY_X25519)
897}
898
899#[cfg(test)]
900mod agreement_tests;
901#[cfg(test)]
902mod parsed_public_key_tests;