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,
57};
58use crate::ec::{encoding, evp_key_generate};
59use crate::error::{KeyRejected, Unspecified};
60use crate::hex;
61use crate::ptr::ConstPointer;
62pub use ephemeral::{agree_ephemeral, EphemeralPrivateKey};
63
64use crate::aws_lc::{
65    EVP_PKEY_derive, EVP_PKEY_derive_init, EVP_PKEY_derive_set_peer, EVP_PKEY_get0_EC_KEY,
66    NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, EVP_PKEY, EVP_PKEY_X25519, NID_X25519,
67};
68
69use crate::buffer::Buffer;
70use crate::ec;
71use crate::ec::encoding::rfc5915::parse_rfc5915_private_key;
72use crate::encoding::{
73    AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der,
74    EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der,
75};
76use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
77use crate::fips::indicator_check;
78use crate::ptr::LcPtr;
79use core::fmt;
80use core::fmt::{Debug, Formatter};
81use core::ptr::null_mut;
82
83#[allow(non_camel_case_types)]
84#[derive(PartialEq, Eq)]
85enum AlgorithmID {
86    ECDH_P256,
87    ECDH_P384,
88    ECDH_P521,
89    X25519,
90}
91
92impl AlgorithmID {
93    #[inline]
94    const fn nid(&self) -> i32 {
95        match self {
96            AlgorithmID::ECDH_P256 => NID_X9_62_prime256v1,
97            AlgorithmID::ECDH_P384 => NID_secp384r1,
98            AlgorithmID::ECDH_P521 => NID_secp521r1,
99            AlgorithmID::X25519 => NID_X25519,
100        }
101    }
102
103    // Uncompressed public key length in bytes
104    #[inline]
105    const fn pub_key_len(&self) -> usize {
106        match self {
107            AlgorithmID::ECDH_P256 => ec::uncompressed_public_key_size_bytes(256),
108            AlgorithmID::ECDH_P384 => ec::uncompressed_public_key_size_bytes(384),
109            AlgorithmID::ECDH_P521 => ec::uncompressed_public_key_size_bytes(521),
110            AlgorithmID::X25519 => 32,
111        }
112    }
113
114    #[inline]
115    const fn private_key_len(&self) -> usize {
116        match self {
117            AlgorithmID::ECDH_P256 | AlgorithmID::X25519 => 32,
118            AlgorithmID::ECDH_P384 => 48,
119            AlgorithmID::ECDH_P521 => 66,
120        }
121    }
122}
123
124impl Debug for AlgorithmID {
125    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
126        let output = match self {
127            AlgorithmID::ECDH_P256 => "curve: P256",
128            AlgorithmID::ECDH_P384 => "curve: P384",
129            AlgorithmID::ECDH_P521 => "curve: P521",
130            AlgorithmID::X25519 => "curve: Curve25519",
131        };
132        f.write_str(output)
133    }
134}
135
136/// A key agreement algorithm.
137#[derive(PartialEq, Eq)]
138pub struct Algorithm {
139    id: AlgorithmID,
140}
141
142impl Debug for Algorithm {
143    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
144        f.write_str(&format!("Algorithm {{ {:?} }}", self.id))
145    }
146}
147
148/// ECDH using the NSA Suite B P-256 (secp256r1) curve.
149pub const ECDH_P256: Algorithm = Algorithm {
150    id: AlgorithmID::ECDH_P256,
151};
152
153/// ECDH using the NSA Suite B P-384 (secp384r1) curve.
154pub const ECDH_P384: Algorithm = Algorithm {
155    id: AlgorithmID::ECDH_P384,
156};
157
158/// ECDH using the NSA Suite B P-521 (secp521r1) curve.
159pub const ECDH_P521: Algorithm = Algorithm {
160    id: AlgorithmID::ECDH_P521,
161};
162
163/// X25519 (ECDH using Curve25519) as described in [RFC 7748].
164///
165/// Everything is as described in RFC 7748. Key agreement will fail if the
166/// result of the X25519 operation is zero; see the notes on the
167/// "all-zero value" in [RFC 7748 section 6.1].
168///
169/// [RFC 7748]: https://tools.ietf.org/html/rfc7748
170/// [RFC 7748 section 6.1]: https://tools.ietf.org/html/rfc7748#section-6.1
171pub const X25519: Algorithm = Algorithm {
172    id: AlgorithmID::X25519,
173};
174
175#[allow(non_camel_case_types)]
176enum KeyInner {
177    ECDH_P256(LcPtr<EVP_PKEY>),
178    ECDH_P384(LcPtr<EVP_PKEY>),
179    ECDH_P521(LcPtr<EVP_PKEY>),
180    X25519(LcPtr<EVP_PKEY>),
181}
182
183impl Clone for KeyInner {
184    fn clone(&self) -> KeyInner {
185        match self {
186            KeyInner::ECDH_P256(evp_pkey) => KeyInner::ECDH_P256(evp_pkey.clone()),
187            KeyInner::ECDH_P384(evp_pkey) => KeyInner::ECDH_P384(evp_pkey.clone()),
188            KeyInner::ECDH_P521(evp_pkey) => KeyInner::ECDH_P521(evp_pkey.clone()),
189            KeyInner::X25519(evp_pkey) => KeyInner::X25519(evp_pkey.clone()),
190        }
191    }
192}
193
194/// A private key for use (only) with `agree`. The
195/// signature of `agree` allows `PrivateKey` to be
196/// used for more than one key agreement.
197pub struct PrivateKey {
198    inner_key: KeyInner,
199}
200
201impl KeyInner {
202    #[inline]
203    fn algorithm(&self) -> &'static Algorithm {
204        match self {
205            KeyInner::ECDH_P256(..) => &ECDH_P256,
206            KeyInner::ECDH_P384(..) => &ECDH_P384,
207            KeyInner::ECDH_P521(..) => &ECDH_P521,
208            KeyInner::X25519(..) => &X25519,
209        }
210    }
211
212    fn get_evp_pkey(&self) -> &LcPtr<EVP_PKEY> {
213        match self {
214            KeyInner::ECDH_P256(evp_pkey)
215            | KeyInner::ECDH_P384(evp_pkey)
216            | KeyInner::ECDH_P521(evp_pkey)
217            | KeyInner::X25519(evp_pkey) => evp_pkey,
218        }
219    }
220}
221
222unsafe impl Send for PrivateKey {}
223
224// https://github.com/awslabs/aws-lc/blob/main/include/openssl/ec_key.h#L88
225// An |EC_KEY| object represents a public or private EC 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 Sync for PrivateKey {}
231
232impl Debug for PrivateKey {
233    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
234        f.write_str(&format!(
235            "PrivateKey {{ algorithm: {:?} }}",
236            self.inner_key.algorithm()
237        ))
238    }
239}
240
241impl PrivateKey {
242    fn new(alg: &'static Algorithm, evp_pkey: LcPtr<EVP_PKEY>) -> Self {
243        match alg.id {
244            AlgorithmID::X25519 => Self {
245                inner_key: KeyInner::X25519(evp_pkey),
246            },
247            AlgorithmID::ECDH_P256 => Self {
248                inner_key: KeyInner::ECDH_P256(evp_pkey),
249            },
250            AlgorithmID::ECDH_P384 => Self {
251                inner_key: KeyInner::ECDH_P384(evp_pkey),
252            },
253            AlgorithmID::ECDH_P521 => Self {
254                inner_key: KeyInner::ECDH_P521(evp_pkey),
255            },
256        }
257    }
258
259    #[inline]
260    /// Generate a new private key for the given algorithm.
261    // # FIPS
262    // Use this function with one of the following algorithms:
263    // * `ECDH_P256`
264    // * `ECDH_P384`
265    // * `ECDH_P521`
266    //
267    /// # Errors
268    /// `error::Unspecified` when operation fails due to internal error.
269    pub fn generate(alg: &'static Algorithm) -> Result<Self, Unspecified> {
270        let evp_pkey = match alg.id {
271            AlgorithmID::X25519 => generate_x25519()?,
272            _ => evp_key_generate(alg.id.nid())?,
273        };
274        Ok(Self::new(alg, evp_pkey))
275    }
276
277    /// Deserializes a DER-encoded private key structure to produce a `agreement::PrivateKey`.
278    ///
279    /// This function is typically used to deserialize RFC 5915 encoded private keys, but it will
280    /// attempt to automatically detect other key formats. This function supports unencrypted
281    /// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats.
282    ///
283    /// X25519 keys are not supported. See `PrivateKey::as_der`.
284    ///
285    /// # Errors
286    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
287    ///
288    /// # Panics
289    pub fn from_private_key_der(
290        alg: &'static Algorithm,
291        key_bytes: &[u8],
292    ) -> Result<Self, KeyRejected> {
293        if AlgorithmID::X25519 == alg.id {
294            return Err(KeyRejected::invalid_encoding());
295        }
296        let evp_pkey = parse_rfc5915_private_key(key_bytes, alg.id.nid())?;
297        Ok(Self::new(alg, evp_pkey))
298    }
299
300    /// Constructs an ECDH key from private key bytes
301    ///
302    /// The private key must encoded as a big-endian fixed-length integer. For
303    /// example, a P-256 private key must be 32 bytes prefixed with leading
304    /// zeros as needed.
305    ///
306    /// # Errors
307    /// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
308    pub fn from_private_key(
309        alg: &'static Algorithm,
310        key_bytes: &[u8],
311    ) -> Result<Self, KeyRejected> {
312        if key_bytes.len() != alg.id.private_key_len() {
313            return Err(KeyRejected::wrong_algorithm());
314        }
315        let evp_pkey = if AlgorithmID::X25519 == alg.id {
316            LcPtr::<EVP_PKEY>::parse_raw_private_key(key_bytes, EVP_PKEY_X25519)?
317        } else {
318            parse_sec1_private_bn(key_bytes, alg.id.nid())?
319        };
320        Ok(Self::new(alg, evp_pkey))
321    }
322
323    #[cfg(test)]
324    #[allow(clippy::missing_errors_doc, missing_docs)]
325    pub fn generate_for_test(
326        alg: &'static Algorithm,
327        rng: &dyn crate::rand::SecureRandom,
328    ) -> Result<Self, Unspecified> {
329        match alg.id {
330            AlgorithmID::X25519 => {
331                let mut priv_key = [0u8; AlgorithmID::X25519.private_key_len()];
332                rng.fill(&mut priv_key)?;
333                Self::from_x25519_private_key(&priv_key)
334            }
335            AlgorithmID::ECDH_P256 => {
336                let mut priv_key = [0u8; AlgorithmID::ECDH_P256.private_key_len()];
337                rng.fill(&mut priv_key)?;
338                Self::from_p256_private_key(&priv_key)
339            }
340            AlgorithmID::ECDH_P384 => {
341                let mut priv_key = [0u8; AlgorithmID::ECDH_P384.private_key_len()];
342                rng.fill(&mut priv_key)?;
343                Self::from_p384_private_key(&priv_key)
344            }
345            AlgorithmID::ECDH_P521 => {
346                let mut priv_key = [0u8; AlgorithmID::ECDH_P521.private_key_len()];
347                rng.fill(&mut priv_key)?;
348                Self::from_p521_private_key(&priv_key)
349            }
350        }
351    }
352
353    #[cfg(test)]
354    fn from_x25519_private_key(
355        priv_key: &[u8; AlgorithmID::X25519.private_key_len()],
356    ) -> Result<Self, Unspecified> {
357        let pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(priv_key, EVP_PKEY_X25519)?;
358
359        Ok(PrivateKey {
360            inner_key: KeyInner::X25519(pkey),
361        })
362    }
363
364    #[cfg(test)]
365    fn from_p256_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
366        let pkey = parse_sec1_private_bn(priv_key, ECDH_P256.id.nid())?;
367        Ok(PrivateKey {
368            inner_key: KeyInner::ECDH_P256(pkey),
369        })
370    }
371
372    #[cfg(test)]
373    fn from_p384_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
374        let pkey = parse_sec1_private_bn(priv_key, ECDH_P384.id.nid())?;
375        Ok(PrivateKey {
376            inner_key: KeyInner::ECDH_P384(pkey),
377        })
378    }
379
380    #[cfg(test)]
381    fn from_p521_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
382        let pkey = parse_sec1_private_bn(priv_key, ECDH_P521.id.nid())?;
383        Ok(PrivateKey {
384            inner_key: KeyInner::ECDH_P521(pkey),
385        })
386    }
387
388    /// Computes the public key from the private key.
389    ///
390    /// # Errors
391    /// `error::Unspecified` when operation fails due to internal error.
392    pub fn compute_public_key(&self) -> Result<PublicKey, Unspecified> {
393        match &self.inner_key {
394            KeyInner::ECDH_P256(evp_pkey)
395            | KeyInner::ECDH_P384(evp_pkey)
396            | KeyInner::ECDH_P521(evp_pkey) => {
397                let mut public_key = [0u8; MAX_PUBLIC_KEY_LEN];
398                let len = marshal_sec1_public_point_into_buffer(&mut public_key, evp_pkey, false)?;
399                Ok(PublicKey {
400                    inner_key: self.inner_key.clone(),
401                    public_key,
402                    len,
403                })
404            }
405            KeyInner::X25519(priv_key) => {
406                let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN];
407                let out_len = priv_key.marshal_raw_public_to_buffer(&mut buffer)?;
408                Ok(PublicKey {
409                    inner_key: self.inner_key.clone(),
410                    public_key: buffer,
411                    len: out_len,
412                })
413            }
414        }
415    }
416
417    /// The algorithm for the private key.
418    #[inline]
419    #[must_use]
420    pub fn algorithm(&self) -> &'static Algorithm {
421        self.inner_key.algorithm()
422    }
423}
424
425impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey {
426    /// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure.
427    ///
428    /// X25519 is not supported.
429    ///
430    /// # Errors
431    /// `error::Unspecified`  if serialization failed.
432    fn as_der(&self) -> Result<EcPrivateKeyRfc5915Der<'static>, Unspecified> {
433        if AlgorithmID::X25519 == self.inner_key.algorithm().id {
434            return Err(Unspecified);
435        }
436
437        let mut outp = null_mut::<u8>();
438        let ec_key = {
439            ConstPointer::new(unsafe {
440                EVP_PKEY_get0_EC_KEY(*self.inner_key.get_evp_pkey().as_const())
441            })?
442        };
443        let length = usize::try_from(unsafe { aws_lc::i2d_ECPrivateKey(*ec_key, &mut outp) })
444            .map_err(|_| Unspecified)?;
445        let mut outp = LcPtr::new(outp)?;
446        Ok(EcPrivateKeyRfc5915Der::take_from_slice(unsafe {
447            core::slice::from_raw_parts_mut(*outp.as_mut(), length)
448        }))
449    }
450}
451
452impl AsBigEndian<EcPrivateKeyBin<'static>> for PrivateKey {
453    /// Exposes the private key encoded as a big-endian fixed-length integer.
454    ///
455    /// X25519 is not supported.
456    ///
457    /// # Errors
458    /// `error::Unspecified` if serialization failed.
459    fn as_be_bytes(&self) -> Result<EcPrivateKeyBin<'static>, Unspecified> {
460        if AlgorithmID::X25519 == self.inner_key.algorithm().id {
461            return Err(Unspecified);
462        }
463        let buffer = marshal_sec1_private_key(self.inner_key.get_evp_pkey())?;
464        Ok(EcPrivateKeyBin::new(buffer))
465    }
466}
467
468impl AsBigEndian<Curve25519SeedBin<'static>> for PrivateKey {
469    /// Exposes the seed encoded as a big-endian fixed-length integer.
470    ///
471    /// Only X25519 is supported.
472    ///
473    /// # Errors
474    /// `error::Unspecified` if serialization failed.
475    fn as_be_bytes(&self) -> Result<Curve25519SeedBin<'static>, Unspecified> {
476        if AlgorithmID::X25519 != self.inner_key.algorithm().id {
477            return Err(Unspecified);
478        }
479        let evp_pkey = self.inner_key.get_evp_pkey();
480        Ok(Curve25519SeedBin::new(evp_pkey.marshal_raw_private_key()?))
481    }
482}
483
484pub(crate) fn generate_x25519() -> Result<LcPtr<EVP_PKEY>, Unspecified> {
485    LcPtr::<EVP_PKEY>::generate(EVP_PKEY_X25519, No_EVP_PKEY_CTX_consumer)
486}
487
488const MAX_PUBLIC_KEY_LEN: usize = ec::PUBLIC_KEY_MAX_LEN;
489
490/// A public key for key agreement.
491pub struct PublicKey {
492    inner_key: KeyInner,
493    public_key: [u8; MAX_PUBLIC_KEY_LEN],
494    len: usize,
495}
496
497impl PublicKey {
498    /// The algorithm for the public key.
499    #[must_use]
500    pub fn algorithm(&self) -> &'static Algorithm {
501        self.inner_key.algorithm()
502    }
503}
504
505unsafe impl Send for PublicKey {}
506unsafe impl Sync for PublicKey {}
507
508impl Debug for PublicKey {
509    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
510        f.write_str(&format!(
511            "PublicKey {{ algorithm: {:?}, bytes: \"{}\" }}",
512            self.inner_key.algorithm(),
513            hex::encode(&self.public_key[0..self.len])
514        ))
515    }
516}
517
518impl AsRef<[u8]> for PublicKey {
519    /// Serializes the public key in an uncompressed form (X9.62) using the
520    /// Octet-String-to-Elliptic-Curve-Point algorithm in
521    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
522    fn as_ref(&self) -> &[u8] {
523        &self.public_key[0..self.len]
524    }
525}
526
527impl Clone for PublicKey {
528    fn clone(&self) -> Self {
529        PublicKey {
530            inner_key: self.inner_key.clone(),
531            public_key: self.public_key,
532            len: self.len,
533        }
534    }
535}
536
537impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
538    /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
539    /// # Errors
540    /// Returns an error if the public key fails to marshal to X.509.
541    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> {
542        match &self.inner_key {
543            KeyInner::ECDH_P256(evp_pkey)
544            | KeyInner::ECDH_P384(evp_pkey)
545            | KeyInner::ECDH_P521(evp_pkey)
546            | KeyInner::X25519(evp_pkey) => {
547                let der = evp_pkey.marshal_rfc5280_public_key()?;
548                Ok(PublicKeyX509Der::from(Buffer::new(der)))
549            }
550        }
551    }
552}
553
554impl AsBigEndian<EcPublicKeyCompressedBin<'static>> for PublicKey {
555    /// Provides the public key elliptic curve point to a compressed point format.
556    /// # Errors
557    /// Returns an error if the underlying implementation is unable to marshal the public key to this format.
558    fn as_be_bytes(&self) -> Result<EcPublicKeyCompressedBin<'static>, crate::error::Unspecified> {
559        let evp_pkey = match &self.inner_key {
560            KeyInner::ECDH_P256(evp_pkey)
561            | KeyInner::ECDH_P384(evp_pkey)
562            | KeyInner::ECDH_P521(evp_pkey) => evp_pkey,
563            KeyInner::X25519(_) => return Err(Unspecified),
564        };
565        let pub_point = marshal_sec1_public_point(evp_pkey, true)?;
566        Ok(EcPublicKeyCompressedBin::new(pub_point))
567    }
568}
569
570impl AsBigEndian<EcPublicKeyUncompressedBin<'static>> for PublicKey {
571    /// Provides the public key elliptic curve point to a compressed point format.
572    ///
573    /// Equivalent to [`PublicKey::as_ref`] for ECDH key types, except that it provides you a copy instead of a reference.
574    ///
575    /// # Errors
576    /// Returns an error if the underlying implementation is unable to marshal the public key to this format.
577    fn as_be_bytes(
578        &self,
579    ) -> Result<EcPublicKeyUncompressedBin<'static>, crate::error::Unspecified> {
580        if self.algorithm().id == AlgorithmID::X25519 {
581            return Err(Unspecified);
582        }
583
584        let mut buffer = vec![0u8; self.len];
585        buffer.copy_from_slice(&self.public_key[0..self.len]);
586
587        Ok(EcPublicKeyUncompressedBin::new(buffer))
588    }
589}
590
591/// An unparsed, possibly malformed, public key for key agreement.
592#[derive(Clone)]
593pub struct UnparsedPublicKey<B: AsRef<[u8]>> {
594    alg: &'static Algorithm,
595    bytes: B,
596}
597
598impl<B: Copy + AsRef<[u8]>> Copy for UnparsedPublicKey<B> {}
599
600impl<B: Debug + AsRef<[u8]>> Debug for UnparsedPublicKey<B> {
601    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
602        f.write_str(&format!(
603            "UnparsedPublicKey {{ algorithm: {:?}, bytes: {:?} }}",
604            self.alg,
605            hex::encode(self.bytes.as_ref())
606        ))
607    }
608}
609
610impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
611    /// Constructs a new `UnparsedPublicKey`.
612    pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
613        UnparsedPublicKey {
614            alg: algorithm,
615            bytes,
616        }
617    }
618
619    /// The agreement algorithm associated with this public key
620    pub fn algorithm(&self) -> &'static Algorithm {
621        self.alg
622    }
623
624    /// The bytes provided for this public key
625    pub fn bytes(&self) -> &B {
626        &self.bytes
627    }
628}
629
630/// Performs a key agreement with a private key and the given public key.
631///
632/// `my_private_key` is the private key to use. Only a reference to the key
633/// is required, allowing the key to continue to be used.
634///
635/// `peer_public_key` is the peer's public key. `agree` will return
636/// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve.
637/// `agree` verifies that it is encoded in the standard form for the
638/// algorithm and that the key is *valid*; see the algorithm's documentation for
639/// details on how keys are to be encoded and what constitutes a valid key for
640/// that algorithm.
641///
642/// `error_value` is the value to return if an error occurs before `kdf` is
643/// called, e.g. when decoding of the peer's public key fails or when the public
644/// key is otherwise invalid.
645///
646/// After the key agreement is done, `agree` calls `kdf` with the raw
647/// key material from the key agreement operation and then returns what `kdf`
648/// returns.
649// # FIPS
650// Use this function with one of the following key algorithms:
651// * `ECDH_P256`
652// * `ECDH_P384`
653// * `ECDH_P521`
654//
655/// # Errors
656/// `error_value` on internal failure.
657#[inline]
658#[allow(clippy::missing_panics_doc)]
659pub fn agree<B: AsRef<[u8]>, F, R, E>(
660    my_private_key: &PrivateKey,
661    peer_public_key: &UnparsedPublicKey<B>,
662    error_value: E,
663    kdf: F,
664) -> Result<R, E>
665where
666    F: FnOnce(&[u8]) -> Result<R, E>,
667{
668    let expected_alg = my_private_key.algorithm();
669    let expected_nid = expected_alg.id.nid();
670
671    if peer_public_key.alg != expected_alg {
672        return Err(error_value);
673    }
674
675    let peer_pub_bytes = peer_public_key.bytes.as_ref();
676
677    let mut buffer = [0u8; MAX_AGREEMENT_SECRET_LEN];
678
679    let secret: &[u8] = match &my_private_key.inner_key {
680        KeyInner::X25519(priv_key) => {
681            x25519_diffie_hellman(&mut buffer, priv_key, peer_pub_bytes).or(Err(error_value))?
682        }
683        KeyInner::ECDH_P256(priv_key)
684        | KeyInner::ECDH_P384(priv_key)
685        | KeyInner::ECDH_P521(priv_key) => {
686            ec_key_ecdh(&mut buffer, priv_key, peer_pub_bytes, expected_nid).or(Err(error_value))?
687        }
688    };
689    kdf(secret)
690}
691
692// Current max secret length is P-521's.
693const MAX_AGREEMENT_SECRET_LEN: usize = AlgorithmID::ECDH_P521.private_key_len();
694
695#[inline]
696#[allow(clippy::needless_pass_by_value)]
697fn ec_key_ecdh<'a>(
698    buffer: &'a mut [u8; MAX_AGREEMENT_SECRET_LEN],
699    priv_key: &LcPtr<EVP_PKEY>,
700    peer_pub_key_bytes: &[u8],
701    nid: i32,
702) -> Result<&'a [u8], Unspecified> {
703    let mut pub_key = encoding::parse_ec_public_key(peer_pub_key_bytes, nid)?;
704
705    let mut pkey_ctx = priv_key.create_EVP_PKEY_CTX()?;
706
707    if 1 != unsafe { EVP_PKEY_derive_init(*pkey_ctx.as_mut()) } {
708        return Err(Unspecified);
709    }
710
711    if 1 != unsafe { EVP_PKEY_derive_set_peer(*pkey_ctx.as_mut(), *pub_key.as_mut()) } {
712        return Err(Unspecified);
713    }
714
715    let mut out_key_len = buffer.len();
716
717    if 1 != indicator_check!(unsafe {
718        EVP_PKEY_derive(*pkey_ctx.as_mut(), buffer.as_mut_ptr(), &mut out_key_len)
719    }) {
720        return Err(Unspecified);
721    }
722
723    if 0 == out_key_len {
724        return Err(Unspecified);
725    }
726
727    Ok(&buffer[0..out_key_len])
728}
729
730#[inline]
731fn x25519_diffie_hellman<'a>(
732    buffer: &'a mut [u8; MAX_AGREEMENT_SECRET_LEN],
733    priv_key: &LcPtr<EVP_PKEY>,
734    peer_pub_key: &[u8],
735) -> Result<&'a [u8], ()> {
736    let mut pkey_ctx = priv_key.create_EVP_PKEY_CTX()?;
737
738    if 1 != unsafe { EVP_PKEY_derive_init(*pkey_ctx.as_mut()) } {
739        return Err(());
740    }
741
742    let mut pub_key = try_parse_x25519_public_key_bytes(peer_pub_key)?;
743
744    if 1 != unsafe { EVP_PKEY_derive_set_peer(*pkey_ctx.as_mut(), *pub_key.as_mut()) } {
745        return Err(());
746    }
747
748    let mut out_key_len = buffer.len();
749
750    if 1 != indicator_check!(unsafe {
751        EVP_PKEY_derive(*pkey_ctx.as_mut(), buffer.as_mut_ptr(), &mut out_key_len)
752    }) {
753        return Err(());
754    }
755
756    debug_assert!(out_key_len == AlgorithmID::X25519.pub_key_len());
757
758    Ok(&buffer[0..AlgorithmID::X25519.pub_key_len()])
759}
760
761pub(crate) fn try_parse_x25519_public_key_bytes(
762    key_bytes: &[u8],
763) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
764    LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(key_bytes, EVP_PKEY_X25519)
765        .or(try_parse_x25519_public_key_raw_bytes(key_bytes))
766}
767
768fn try_parse_x25519_public_key_raw_bytes(key_bytes: &[u8]) -> Result<LcPtr<EVP_PKEY>, Unspecified> {
769    let expected_pub_key_len = X25519.id.pub_key_len();
770    if key_bytes.len() != expected_pub_key_len {
771        return Err(Unspecified);
772    }
773
774    Ok(LcPtr::<EVP_PKEY>::parse_raw_public_key(
775        key_bytes,
776        EVP_PKEY_X25519,
777    )?)
778}
779
780#[cfg(test)]
781mod tests {
782    use crate::agreement::{
783        agree, Algorithm, PrivateKey, PublicKey, UnparsedPublicKey, ECDH_P256, ECDH_P384,
784        ECDH_P521, X25519,
785    };
786    use crate::encoding::{
787        AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der,
788        EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der,
789    };
790    use crate::{rand, test};
791
792    #[test]
793    fn test_agreement_x25519() {
794        let alg = &X25519;
795        let peer_public = UnparsedPublicKey::new(
796            alg,
797            test::from_dirty_hex(
798                "e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
799            ),
800        );
801
802        let my_private = test::from_dirty_hex(
803            "a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
804        );
805
806        let my_private = {
807            let rng = test::rand::FixedSliceRandom { bytes: &my_private };
808            PrivateKey::generate_for_test(alg, &rng).unwrap()
809        };
810
811        let my_public = test::from_dirty_hex(
812            "1c9fd88f45606d932a80c71824ae151d15d73e77de38e8e000852e614fae7019",
813        );
814        let output = test::from_dirty_hex(
815            "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
816        );
817
818        assert_eq!(my_private.algorithm(), alg);
819
820        let be_private_key_buffer: Curve25519SeedBin = my_private.as_be_bytes().unwrap();
821        let be_private_key =
822            PrivateKey::from_private_key(&X25519, be_private_key_buffer.as_ref()).unwrap();
823        {
824            let result = agree(&be_private_key, &peer_public, (), |key_material| {
825                assert_eq!(key_material, &output[..]);
826                Ok(())
827            });
828            assert_eq!(result, Ok(()));
829        }
830
831        let computed_public = my_private.compute_public_key().unwrap();
832        assert_eq!(computed_public.as_ref(), &my_public[..]);
833
834        assert_eq!(computed_public.algorithm(), alg);
835        {
836            let result = agree(&my_private, &peer_public, (), |key_material| {
837                assert_eq!(key_material, &output[..]);
838                Ok(())
839            });
840            assert_eq!(result, Ok(()));
841        }
842        {
843            let result = agree(&my_private, &peer_public, (), |key_material| {
844                assert_eq!(key_material, &output[..]);
845                Ok(())
846            });
847            assert_eq!(result, Ok(()));
848        }
849    }
850
851    #[test]
852    fn test_agreement_invalid_keys() {
853        fn test_with_key(alg: &'static Algorithm, my_private_key: &PrivateKey, test_key: &[u8]) {
854            assert!(PrivateKey::from_private_key(alg, test_key).is_err());
855            assert!(PrivateKey::from_private_key_der(alg, test_key).is_err());
856            assert!(agree(
857                my_private_key,
858                &UnparsedPublicKey::new(alg, test_key),
859                (),
860                |_| Ok(())
861            )
862            .is_err());
863        }
864
865        let alg_variants: [&'static Algorithm; 4] = [&X25519, &ECDH_P256, &ECDH_P384, &ECDH_P521];
866
867        for alg in alg_variants {
868            let my_private_key = PrivateKey::generate(alg).unwrap();
869
870            let empty_key = [];
871            test_with_key(alg, &my_private_key, &empty_key);
872
873            let wrong_size_key: [u8; 31] = [
874                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
875                23, 24, 25, 26, 27, 28, 29, 30,
876            ];
877            test_with_key(alg, &my_private_key, &wrong_size_key);
878        }
879    }
880
881    #[test]
882    fn test_agreement_ecdh_p256() {
883        let alg = &ECDH_P256;
884        let peer_public = UnparsedPublicKey::new(
885            alg,
886            test::from_dirty_hex(
887                "04D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF6356FBF3CA366CC23E8157854C13C58D6AAC23F046ADA30F8353E74F33039872AB",
888            ),
889        );
890        assert_eq!(peer_public.algorithm(), alg);
891        assert_eq!(peer_public.bytes(), &peer_public.bytes);
892
893        let my_private = test::from_dirty_hex(
894            "C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433",
895        );
896
897        let my_private = {
898            let rng = test::rand::FixedSliceRandom { bytes: &my_private };
899            PrivateKey::generate_for_test(alg, &rng).unwrap()
900        };
901
902        let my_public = test::from_dirty_hex(
903            "04DAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C37725811805271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3",
904        );
905        let output = test::from_dirty_hex(
906            "D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE",
907        );
908
909        assert_eq!(my_private.algorithm(), alg);
910
911        let be_private_key_buffer: EcPrivateKeyBin = my_private.as_be_bytes().unwrap();
912        let be_private_key =
913            PrivateKey::from_private_key(&ECDH_P256, be_private_key_buffer.as_ref()).unwrap();
914        {
915            let result = agree(&be_private_key, &peer_public, (), |key_material| {
916                assert_eq!(key_material, &output[..]);
917                Ok(())
918            });
919            assert_eq!(result, Ok(()));
920        }
921
922        let der_private_key_buffer: EcPrivateKeyRfc5915Der = my_private.as_der().unwrap();
923        let der_private_key =
924            PrivateKey::from_private_key_der(&ECDH_P256, der_private_key_buffer.as_ref()).unwrap();
925        {
926            let result = agree(&der_private_key, &peer_public, (), |key_material| {
927                assert_eq!(key_material, &output[..]);
928                Ok(())
929            });
930            assert_eq!(result, Ok(()));
931        }
932
933        let computed_public = my_private.compute_public_key().unwrap();
934        assert_eq!(computed_public.as_ref(), &my_public[..]);
935
936        assert_eq!(computed_public.algorithm(), alg);
937
938        {
939            let result = agree(&my_private, &peer_public, (), |key_material| {
940                assert_eq!(key_material, &output[..]);
941                Ok(())
942            });
943            assert_eq!(result, Ok(()));
944        }
945
946        {
947            let result = agree(&my_private, &peer_public, (), |key_material| {
948                assert_eq!(key_material, &output[..]);
949                Ok(())
950            });
951            assert_eq!(result, Ok(()));
952        }
953    }
954
955    #[test]
956    fn test_agreement_ecdh_p384() {
957        let alg = &ECDH_P384;
958        let peer_public = UnparsedPublicKey::new(
959            alg,
960            test::from_dirty_hex(
961                "04E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571DCFBEC7AACF3196472169E838430367F66EEBE3C6E70C416DD5F0C68759DD1FFF83FA40142209DFF5EAAD96DB9E6386C",
962            ),
963        );
964
965        let my_private = test::from_dirty_hex(
966            "099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194",
967        );
968
969        let my_private = {
970            let rng = test::rand::FixedSliceRandom { bytes: &my_private };
971            PrivateKey::generate_for_test(alg, &rng).unwrap()
972        };
973
974        let my_public = test::from_dirty_hex(
975            "04667842D7D180AC2CDE6F74F37551F55755C7645C20EF73E31634FE72B4C55EE6DE3AC808ACB4BDB4C88732AEE95F41AA9482ED1FC0EEB9CAFC4984625CCFC23F65032149E0E144ADA024181535A0F38EEB9FCFF3C2C947DAE69B4C634573A81C",
976        );
977        let output = test::from_dirty_hex(
978            "11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746",
979        );
980
981        assert_eq!(my_private.algorithm(), alg);
982
983        let be_private_key_buffer: EcPrivateKeyBin = my_private.as_be_bytes().unwrap();
984        let be_private_key =
985            PrivateKey::from_private_key(&ECDH_P384, be_private_key_buffer.as_ref()).unwrap();
986        {
987            let result = agree(&be_private_key, &peer_public, (), |key_material| {
988                assert_eq!(key_material, &output[..]);
989                Ok(())
990            });
991            assert_eq!(result, Ok(()));
992        }
993
994        let der_private_key_buffer: EcPrivateKeyRfc5915Der = my_private.as_der().unwrap();
995        let der_private_key =
996            PrivateKey::from_private_key_der(&ECDH_P384, der_private_key_buffer.as_ref()).unwrap();
997        {
998            let result = agree(&der_private_key, &peer_public, (), |key_material| {
999                assert_eq!(key_material, &output[..]);
1000                Ok(())
1001            });
1002            assert_eq!(result, Ok(()));
1003        }
1004
1005        let computed_public = my_private.compute_public_key().unwrap();
1006        assert_eq!(computed_public.as_ref(), &my_public[..]);
1007
1008        assert_eq!(computed_public.algorithm(), alg);
1009
1010        {
1011            let result = agree(&my_private, &peer_public, (), |key_material| {
1012                assert_eq!(key_material, &output[..]);
1013                Ok(())
1014            });
1015            assert_eq!(result, Ok(()));
1016        }
1017    }
1018
1019    #[test]
1020    fn test_agreement_ecdh_p521() {
1021        let alg = &ECDH_P521;
1022        let peer_public = UnparsedPublicKey::new(
1023            alg,
1024            test::from_dirty_hex(
1025                "0401a32099b02c0bd85371f60b0dd20890e6c7af048c8179890fda308b359dbbc2b7a832bb8c6526c4af99a7ea3f0b3cb96ae1eb7684132795c478ad6f962e4a6f446d017627357b39e9d7632a1370b3e93c1afb5c851b910eb4ead0c9d387df67cde85003e0e427552f1cd09059aad0262e235cce5fba8cedc4fdc1463da76dcd4b6d1a46",
1026            ),
1027        );
1028
1029        let my_private = test::from_dirty_hex(
1030            "00df14b1f1432a7b0fb053965fd8643afee26b2451ecb6a8a53a655d5fbe16e4c64ce8647225eb11e7fdcb23627471dffc5c2523bd2ae89957cba3a57a23933e5a78",
1031        );
1032
1033        let my_private = {
1034            let rng = test::rand::FixedSliceRandom { bytes: &my_private };
1035            PrivateKey::generate_for_test(alg, &rng).unwrap()
1036        };
1037
1038        let my_public = test::from_dirty_hex(
1039            "04004e8583bbbb2ecd93f0714c332dff5ab3bc6396e62f3c560229664329baa5138c3bb1c36428abd4e23d17fcb7a2cfcc224b2e734c8941f6f121722d7b6b9415457601cf0874f204b0363f020864672fadbf87c8811eb147758b254b74b14fae742159f0f671a018212bbf25b8519e126d4cad778cfff50d288fd39ceb0cac635b175ec0",
1040        );
1041        let output = test::from_dirty_hex(
1042            "01aaf24e5d47e4080c18c55ea35581cd8da30f1a079565045d2008d51b12d0abb4411cda7a0785b15d149ed301a3697062f42da237aa7f07e0af3fd00eb1800d9c41",
1043        );
1044
1045        assert_eq!(my_private.algorithm(), alg);
1046
1047        let be_private_key_buffer: EcPrivateKeyBin = my_private.as_be_bytes().unwrap();
1048        let be_private_key =
1049            PrivateKey::from_private_key(&ECDH_P521, be_private_key_buffer.as_ref()).unwrap();
1050        {
1051            let result = agree(&be_private_key, &peer_public, (), |key_material| {
1052                assert_eq!(key_material, &output[..]);
1053                Ok(())
1054            });
1055            assert_eq!(result, Ok(()));
1056        }
1057
1058        let der_private_key_buffer: EcPrivateKeyRfc5915Der = my_private.as_der().unwrap();
1059        let der_private_key =
1060            PrivateKey::from_private_key_der(&ECDH_P521, der_private_key_buffer.as_ref()).unwrap();
1061        {
1062            let result = agree(&der_private_key, &peer_public, (), |key_material| {
1063                assert_eq!(key_material, &output[..]);
1064                Ok(())
1065            });
1066            assert_eq!(result, Ok(()));
1067        }
1068
1069        let computed_public = my_private.compute_public_key().unwrap();
1070        assert_eq!(computed_public.as_ref(), &my_public[..]);
1071
1072        assert_eq!(computed_public.algorithm(), alg);
1073        {
1074            let result = agree(&my_private, &peer_public, (), |key_material| {
1075                assert_eq!(key_material, &output[..]);
1076                Ok(())
1077            });
1078            assert_eq!(result, Ok(()));
1079        }
1080        {
1081            let result = agree(&my_private, &peer_public, (), |key_material| {
1082                assert_eq!(key_material, &output[..]);
1083                Ok(())
1084            });
1085            assert_eq!(result, Ok(()));
1086        }
1087    }
1088
1089    #[test]
1090    fn agreement_traits() {
1091        use crate::test;
1092        use regex::{self, Regex};
1093
1094        let rng = rand::SystemRandom::new();
1095        let private_key = PrivateKey::generate_for_test(&ECDH_P256, &rng).unwrap();
1096
1097        test::compile_time_assert_send::<PrivateKey>();
1098        test::compile_time_assert_sync::<PrivateKey>();
1099
1100        assert_eq!(
1101            format!("{:?}", &private_key),
1102            "PrivateKey { algorithm: Algorithm { curve: P256 } }"
1103        );
1104
1105        let ephemeral_private_key = PrivateKey::generate_for_test(&ECDH_P256, &rng).unwrap();
1106
1107        test::compile_time_assert_send::<PrivateKey>();
1108        test::compile_time_assert_sync::<PrivateKey>();
1109
1110        assert_eq!(
1111            format!("{:?}", &ephemeral_private_key),
1112            "PrivateKey { algorithm: Algorithm { curve: P256 } }"
1113        );
1114
1115        let public_key = private_key.compute_public_key().unwrap();
1116        let pubkey_re = Regex::new(
1117            "PublicKey \\{ algorithm: Algorithm \\{ curve: P256 \\}, bytes: \"[0-9a-f]+\" \\}",
1118        )
1119        .unwrap();
1120        let pubkey_debug = format!("{:?}", &public_key);
1121
1122        assert!(
1123            pubkey_re.is_match(&pubkey_debug),
1124            "pubkey_debug: {pubkey_debug}"
1125        );
1126
1127        #[allow(clippy::redundant_clone)]
1128        let pubkey_clone = public_key.clone();
1129        assert_eq!(public_key.as_ref(), pubkey_clone.as_ref());
1130        assert_eq!(pubkey_debug, format!("{:?}", &pubkey_clone));
1131
1132        test::compile_time_assert_clone::<PublicKey>();
1133        test::compile_time_assert_send::<PublicKey>();
1134        test::compile_time_assert_sync::<PublicKey>();
1135
1136        // Verify `PublicKey` implements `Debug`.
1137        //
1138        // TODO: Test the actual output.
1139        let _: &dyn core::fmt::Debug = &public_key;
1140
1141        test::compile_time_assert_clone::<UnparsedPublicKey<&[u8]>>();
1142        test::compile_time_assert_copy::<UnparsedPublicKey<&[u8]>>();
1143        test::compile_time_assert_sync::<UnparsedPublicKey<&[u8]>>();
1144
1145        test::compile_time_assert_clone::<UnparsedPublicKey<Vec<u8>>>();
1146        test::compile_time_assert_sync::<UnparsedPublicKey<Vec<u8>>>();
1147
1148        let bytes = [0x01, 0x02, 0x03];
1149
1150        let unparsed_public_key = UnparsedPublicKey::new(&X25519, &bytes);
1151        let unparsed_pubkey_clone = unparsed_public_key;
1152        assert_eq!(
1153            format!("{unparsed_public_key:?}"),
1154            r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"#
1155        );
1156        assert_eq!(
1157            format!("{unparsed_pubkey_clone:?}"),
1158            r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"#
1159        );
1160
1161        let unparsed_public_key = UnparsedPublicKey::new(&X25519, Vec::from(bytes));
1162        #[allow(clippy::redundant_clone)]
1163        let unparsed_pubkey_clone = unparsed_public_key.clone();
1164        assert_eq!(
1165            format!("{unparsed_public_key:?}"),
1166            r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"#
1167        );
1168        assert_eq!(
1169            format!("{unparsed_pubkey_clone:?}"),
1170            r#"UnparsedPublicKey { algorithm: Algorithm { curve: Curve25519 }, bytes: "010203" }"#
1171        );
1172    }
1173
1174    #[test]
1175    fn test_agreement_random() {
1176        let test_algorithms = [&ECDH_P256, &ECDH_P384, &ECDH_P521, &X25519];
1177
1178        for alg in test_algorithms {
1179            test_agreement_random_helper(alg);
1180        }
1181    }
1182
1183    fn test_agreement_random_helper(alg: &'static Algorithm) {
1184        let peer_private = PrivateKey::generate(alg).unwrap();
1185        let my_private = PrivateKey::generate(alg).unwrap();
1186
1187        let peer_public_keys =
1188            public_key_formats_helper(&peer_private.compute_public_key().unwrap());
1189
1190        let my_public_keys = public_key_formats_helper(&my_private.compute_public_key().unwrap());
1191
1192        let mut results: Vec<Vec<u8>> = Vec::new();
1193
1194        for peer_public in peer_public_keys {
1195            let peer_public = UnparsedPublicKey::new(alg, peer_public);
1196            let result = agree(&my_private, &peer_public, (), |key_material| {
1197                results.push(key_material.to_vec());
1198                Ok(())
1199            });
1200            assert_eq!(result, Ok(()));
1201        }
1202
1203        for my_public in my_public_keys {
1204            let my_public = UnparsedPublicKey::new(alg, my_public);
1205            let result = agree(&peer_private, &my_public, (), |key_material| {
1206                results.push(key_material.to_vec());
1207                Ok(())
1208            });
1209            assert_eq!(result, Ok(()));
1210        }
1211
1212        let key_types_tested = match alg.id {
1213            crate::agreement::AlgorithmID::ECDH_P256
1214            | crate::agreement::AlgorithmID::ECDH_P384
1215            | crate::agreement::AlgorithmID::ECDH_P521 => 4,
1216            crate::agreement::AlgorithmID::X25519 => 2,
1217        };
1218
1219        assert_eq!(results.len(), key_types_tested * 2); // Multiplied by two because we tested the other direction
1220
1221        assert_eq!(results[0..key_types_tested], results[key_types_tested..]);
1222    }
1223
1224    fn public_key_formats_helper(public_key: &PublicKey) -> Vec<Vec<u8>> {
1225        let verify_ec_raw_traits = matches!(
1226            public_key.algorithm().id,
1227            crate::agreement::AlgorithmID::ECDH_P256
1228                | crate::agreement::AlgorithmID::ECDH_P384
1229                | crate::agreement::AlgorithmID::ECDH_P521
1230        );
1231
1232        let mut public_keys = Vec::<Vec<u8>>::new();
1233        public_keys.push(public_key.as_ref().into());
1234
1235        if verify_ec_raw_traits {
1236            let raw = AsBigEndian::<EcPublicKeyCompressedBin>::as_be_bytes(public_key).unwrap();
1237            public_keys.push(raw.as_ref().into());
1238            let raw = AsBigEndian::<EcPublicKeyUncompressedBin>::as_be_bytes(public_key).unwrap();
1239            public_keys.push(raw.as_ref().into());
1240        }
1241
1242        let peer_x509 = AsDer::<PublicKeyX509Der>::as_der(public_key).unwrap();
1243        public_keys.push(peer_x509.as_ref().into());
1244
1245        public_keys
1246    }
1247
1248    #[test]
1249    fn private_key_drop() {
1250        let private_key = PrivateKey::generate(&ECDH_P256).unwrap();
1251        let public_key = private_key.compute_public_key().unwrap();
1252        // PublicKey maintains a reference counted pointer to private keys EVP_PKEY so we test that with drop
1253        drop(private_key);
1254        let _ = AsBigEndian::<EcPublicKeyCompressedBin>::as_be_bytes(&public_key).unwrap();
1255        let _ = AsBigEndian::<EcPublicKeyUncompressedBin>::as_be_bytes(&public_key).unwrap();
1256        let _ = AsDer::<PublicKeyX509Der>::as_der(&public_key).unwrap();
1257    }
1258}