xref: /aosp_15_r20/system/security/mls/mls-rs-crypto-boringssl/src/ecdh.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*e1997b9aSAndroid Build Coastguard Worker //
3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e1997b9aSAndroid Build Coastguard Worker //
7*e1997b9aSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*e1997b9aSAndroid Build Coastguard Worker //
9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License.
14*e1997b9aSAndroid Build Coastguard Worker 
15*e1997b9aSAndroid Build Coastguard Worker //! Elliptic curve Diffie–Hellman.
16*e1997b9aSAndroid Build Coastguard Worker 
17*e1997b9aSAndroid Build Coastguard Worker use bssl_crypto::x25519;
18*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::crypto::{CipherSuite, HpkePublicKey, HpkeSecretKey};
19*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::error::IntoAnyError;
20*e1997b9aSAndroid Build Coastguard Worker use mls_rs_crypto_traits::{Curve, DhType};
21*e1997b9aSAndroid Build Coastguard Worker 
22*e1997b9aSAndroid Build Coastguard Worker use core::array::TryFromSliceError;
23*e1997b9aSAndroid Build Coastguard Worker use thiserror::Error;
24*e1997b9aSAndroid Build Coastguard Worker 
25*e1997b9aSAndroid Build Coastguard Worker /// Errors returned from ECDH.
26*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, Error)]
27*e1997b9aSAndroid Build Coastguard Worker pub enum EcdhError {
28*e1997b9aSAndroid Build Coastguard Worker     /// Error returned when conversion from slice to array fails.
29*e1997b9aSAndroid Build Coastguard Worker     #[error(transparent)]
30*e1997b9aSAndroid Build Coastguard Worker     TryFromSliceError(#[from] TryFromSliceError),
31*e1997b9aSAndroid Build Coastguard Worker     /// Error returned when the public key is invalid.
32*e1997b9aSAndroid Build Coastguard Worker     #[error("ECDH public key was invalid")]
33*e1997b9aSAndroid Build Coastguard Worker     InvalidPubKey,
34*e1997b9aSAndroid Build Coastguard Worker     /// Error returned when the private key length is invalid.
35*e1997b9aSAndroid Build Coastguard Worker     #[error("ECDH private key of invalid length {len}, expected length {expected_len}")]
36*e1997b9aSAndroid Build Coastguard Worker     InvalidPrivKeyLen {
37*e1997b9aSAndroid Build Coastguard Worker         /// Invalid key length.
38*e1997b9aSAndroid Build Coastguard Worker         len: usize,
39*e1997b9aSAndroid Build Coastguard Worker         /// Expected key length.
40*e1997b9aSAndroid Build Coastguard Worker         expected_len: usize,
41*e1997b9aSAndroid Build Coastguard Worker     },
42*e1997b9aSAndroid Build Coastguard Worker     /// Error returned when the public key length is invalid.
43*e1997b9aSAndroid Build Coastguard Worker     #[error("ECDH public key of invalid length {len}, expected length {expected_len}")]
44*e1997b9aSAndroid Build Coastguard Worker     InvalidPubKeyLen {
45*e1997b9aSAndroid Build Coastguard Worker         /// Invalid key length.
46*e1997b9aSAndroid Build Coastguard Worker         len: usize,
47*e1997b9aSAndroid Build Coastguard Worker         /// Expected key length.
48*e1997b9aSAndroid Build Coastguard Worker         expected_len: usize,
49*e1997b9aSAndroid Build Coastguard Worker     },
50*e1997b9aSAndroid Build Coastguard Worker     /// Error returned when unsupported cipher suite is requested.
51*e1997b9aSAndroid Build Coastguard Worker     #[error("unsupported cipher suite")]
52*e1997b9aSAndroid Build Coastguard Worker     UnsupportedCipherSuite,
53*e1997b9aSAndroid Build Coastguard Worker }
54*e1997b9aSAndroid Build Coastguard Worker 
55*e1997b9aSAndroid Build Coastguard Worker impl IntoAnyError for EcdhError {
into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self>56*e1997b9aSAndroid Build Coastguard Worker     fn into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self> {
57*e1997b9aSAndroid Build Coastguard Worker         Ok(self.into())
58*e1997b9aSAndroid Build Coastguard Worker     }
59*e1997b9aSAndroid Build Coastguard Worker }
60*e1997b9aSAndroid Build Coastguard Worker 
61*e1997b9aSAndroid Build Coastguard Worker /// DhType implementation backed by BoringSSL.
62*e1997b9aSAndroid Build Coastguard Worker #[derive(Clone, Debug, Eq, PartialEq)]
63*e1997b9aSAndroid Build Coastguard Worker pub struct Ecdh(Curve);
64*e1997b9aSAndroid Build Coastguard Worker 
65*e1997b9aSAndroid Build Coastguard Worker impl Ecdh {
66*e1997b9aSAndroid Build Coastguard Worker     /// Creates a new Ecdh.
new(cipher_suite: CipherSuite) -> Option<Self>67*e1997b9aSAndroid Build Coastguard Worker     pub fn new(cipher_suite: CipherSuite) -> Option<Self> {
68*e1997b9aSAndroid Build Coastguard Worker         Curve::from_ciphersuite(cipher_suite, /*for_sig=*/ false).map(Self)
69*e1997b9aSAndroid Build Coastguard Worker     }
70*e1997b9aSAndroid Build Coastguard Worker }
71*e1997b9aSAndroid Build Coastguard Worker 
72*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
73*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
74*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)]
75*e1997b9aSAndroid Build Coastguard Worker impl DhType for Ecdh {
76*e1997b9aSAndroid Build Coastguard Worker     type Error = EcdhError;
77*e1997b9aSAndroid Build Coastguard Worker 
dh( &self, secret_key: &HpkeSecretKey, public_key: &HpkePublicKey, ) -> Result<Vec<u8>, Self::Error>78*e1997b9aSAndroid Build Coastguard Worker     async fn dh(
79*e1997b9aSAndroid Build Coastguard Worker         &self,
80*e1997b9aSAndroid Build Coastguard Worker         secret_key: &HpkeSecretKey,
81*e1997b9aSAndroid Build Coastguard Worker         public_key: &HpkePublicKey,
82*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Vec<u8>, Self::Error> {
83*e1997b9aSAndroid Build Coastguard Worker         if self.0 != Curve::X25519 {
84*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::UnsupportedCipherSuite);
85*e1997b9aSAndroid Build Coastguard Worker         }
86*e1997b9aSAndroid Build Coastguard Worker         if secret_key.len() != x25519::PRIVATE_KEY_LEN {
87*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::InvalidPrivKeyLen {
88*e1997b9aSAndroid Build Coastguard Worker                 len: secret_key.len(),
89*e1997b9aSAndroid Build Coastguard Worker                 expected_len: x25519::PRIVATE_KEY_LEN,
90*e1997b9aSAndroid Build Coastguard Worker             });
91*e1997b9aSAndroid Build Coastguard Worker         }
92*e1997b9aSAndroid Build Coastguard Worker         if public_key.len() != x25519::PUBLIC_KEY_LEN {
93*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::InvalidPubKeyLen {
94*e1997b9aSAndroid Build Coastguard Worker                 len: public_key.len(),
95*e1997b9aSAndroid Build Coastguard Worker                 expected_len: x25519::PUBLIC_KEY_LEN,
96*e1997b9aSAndroid Build Coastguard Worker             });
97*e1997b9aSAndroid Build Coastguard Worker         }
98*e1997b9aSAndroid Build Coastguard Worker 
99*e1997b9aSAndroid Build Coastguard Worker         let private_key = x25519::PrivateKey(secret_key[..x25519::PRIVATE_KEY_LEN].try_into()?);
100*e1997b9aSAndroid Build Coastguard Worker         match private_key.compute_shared_key(public_key[..x25519::PUBLIC_KEY_LEN].try_into()?) {
101*e1997b9aSAndroid Build Coastguard Worker             Some(x) => Ok(x.to_vec()),
102*e1997b9aSAndroid Build Coastguard Worker             None => Err(EcdhError::InvalidPubKey),
103*e1997b9aSAndroid Build Coastguard Worker         }
104*e1997b9aSAndroid Build Coastguard Worker     }
105*e1997b9aSAndroid Build Coastguard Worker 
to_public(&self, secret_key: &HpkeSecretKey) -> Result<HpkePublicKey, Self::Error>106*e1997b9aSAndroid Build Coastguard Worker     async fn to_public(&self, secret_key: &HpkeSecretKey) -> Result<HpkePublicKey, Self::Error> {
107*e1997b9aSAndroid Build Coastguard Worker         if self.0 != Curve::X25519 {
108*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::UnsupportedCipherSuite);
109*e1997b9aSAndroid Build Coastguard Worker         }
110*e1997b9aSAndroid Build Coastguard Worker         if secret_key.len() != x25519::PRIVATE_KEY_LEN {
111*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::InvalidPrivKeyLen {
112*e1997b9aSAndroid Build Coastguard Worker                 len: secret_key.len(),
113*e1997b9aSAndroid Build Coastguard Worker                 expected_len: x25519::PRIVATE_KEY_LEN,
114*e1997b9aSAndroid Build Coastguard Worker             });
115*e1997b9aSAndroid Build Coastguard Worker         }
116*e1997b9aSAndroid Build Coastguard Worker 
117*e1997b9aSAndroid Build Coastguard Worker         let private_key = x25519::PrivateKey(secret_key[..x25519::PRIVATE_KEY_LEN].try_into()?);
118*e1997b9aSAndroid Build Coastguard Worker         Ok(private_key.to_public().to_vec().into())
119*e1997b9aSAndroid Build Coastguard Worker     }
120*e1997b9aSAndroid Build Coastguard Worker 
generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error>121*e1997b9aSAndroid Build Coastguard Worker     async fn generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
122*e1997b9aSAndroid Build Coastguard Worker         if self.0 != Curve::X25519 {
123*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::UnsupportedCipherSuite);
124*e1997b9aSAndroid Build Coastguard Worker         }
125*e1997b9aSAndroid Build Coastguard Worker 
126*e1997b9aSAndroid Build Coastguard Worker         let (public_key, private_key) = x25519::PrivateKey::generate();
127*e1997b9aSAndroid Build Coastguard Worker         Ok((private_key.0.to_vec().into(), public_key.to_vec().into()))
128*e1997b9aSAndroid Build Coastguard Worker     }
129*e1997b9aSAndroid Build Coastguard Worker 
bitmask_for_rejection_sampling(&self) -> Option<u8>130*e1997b9aSAndroid Build Coastguard Worker     fn bitmask_for_rejection_sampling(&self) -> Option<u8> {
131*e1997b9aSAndroid Build Coastguard Worker         self.0.curve_bitmask()
132*e1997b9aSAndroid Build Coastguard Worker     }
133*e1997b9aSAndroid Build Coastguard Worker 
public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error>134*e1997b9aSAndroid Build Coastguard Worker     fn public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error> {
135*e1997b9aSAndroid Build Coastguard Worker         if self.0 != Curve::X25519 {
136*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::UnsupportedCipherSuite);
137*e1997b9aSAndroid Build Coastguard Worker         }
138*e1997b9aSAndroid Build Coastguard Worker 
139*e1997b9aSAndroid Build Coastguard Worker         // bssl_crypto does not implement validation of curve25519 public keys.
140*e1997b9aSAndroid Build Coastguard Worker         // Note: Neither does x25519_dalek used by RustCrypto's implementation of this function.
141*e1997b9aSAndroid Build Coastguard Worker         if key.len() != x25519::PUBLIC_KEY_LEN {
142*e1997b9aSAndroid Build Coastguard Worker             return Err(EcdhError::InvalidPubKeyLen {
143*e1997b9aSAndroid Build Coastguard Worker                 len: key.len(),
144*e1997b9aSAndroid Build Coastguard Worker                 expected_len: x25519::PUBLIC_KEY_LEN,
145*e1997b9aSAndroid Build Coastguard Worker             });
146*e1997b9aSAndroid Build Coastguard Worker         }
147*e1997b9aSAndroid Build Coastguard Worker         Ok(())
148*e1997b9aSAndroid Build Coastguard Worker     }
149*e1997b9aSAndroid Build Coastguard Worker 
secret_key_size(&self) -> usize150*e1997b9aSAndroid Build Coastguard Worker     fn secret_key_size(&self) -> usize {
151*e1997b9aSAndroid Build Coastguard Worker         self.0.secret_key_size()
152*e1997b9aSAndroid Build Coastguard Worker     }
153*e1997b9aSAndroid Build Coastguard Worker }
154*e1997b9aSAndroid Build Coastguard Worker 
155*e1997b9aSAndroid Build Coastguard Worker #[cfg(all(not(mls_build_async), test))]
156*e1997b9aSAndroid Build Coastguard Worker mod test {
157*e1997b9aSAndroid Build Coastguard Worker     use super::{DhType, Ecdh, EcdhError};
158*e1997b9aSAndroid Build Coastguard Worker     use crate::test_helpers::decode_hex;
159*e1997b9aSAndroid Build Coastguard Worker     use assert_matches::assert_matches;
160*e1997b9aSAndroid Build Coastguard Worker     use mls_rs_core::crypto::{CipherSuite, HpkePublicKey, HpkeSecretKey};
161*e1997b9aSAndroid Build Coastguard Worker 
162*e1997b9aSAndroid Build Coastguard Worker     #[test]
dh()163*e1997b9aSAndroid Build Coastguard Worker     fn dh() {
164*e1997b9aSAndroid Build Coastguard Worker         // https://github.com/C2SP/wycheproof/blob/cd27d6419bedd83cbd24611ec54b6d4bfdb0cdca/testvectors/x25519_test.json#L23
165*e1997b9aSAndroid Build Coastguard Worker         let private_key = HpkeSecretKey::from(
166*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475")
167*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
168*e1997b9aSAndroid Build Coastguard Worker         );
169*e1997b9aSAndroid Build Coastguard Worker         let public_key = HpkePublicKey::from(
170*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829")
171*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
172*e1997b9aSAndroid Build Coastguard Worker         );
173*e1997b9aSAndroid Build Coastguard Worker         let expected_shared_secret: [u8; 32] =
174*e1997b9aSAndroid Build Coastguard Worker             decode_hex("436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320");
175*e1997b9aSAndroid Build Coastguard Worker 
176*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_AES128).unwrap();
177*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(x25519.dh(&private_key, &public_key).unwrap(), expected_shared_secret);
178*e1997b9aSAndroid Build Coastguard Worker     }
179*e1997b9aSAndroid Build Coastguard Worker 
180*e1997b9aSAndroid Build Coastguard Worker     #[test]
dh_invalid_key()181*e1997b9aSAndroid Build Coastguard Worker     fn dh_invalid_key() {
182*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_AES128).unwrap();
183*e1997b9aSAndroid Build Coastguard Worker 
184*e1997b9aSAndroid Build Coastguard Worker         let private_key_short =
185*e1997b9aSAndroid Build Coastguard Worker             HpkeSecretKey::from(decode_hex::<16>("c8a9d5a91091ad851c668b0736c1c9a0").to_vec());
186*e1997b9aSAndroid Build Coastguard Worker         let public_key = HpkePublicKey::from(
187*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829")
188*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
189*e1997b9aSAndroid Build Coastguard Worker         );
190*e1997b9aSAndroid Build Coastguard Worker         assert_matches!(
191*e1997b9aSAndroid Build Coastguard Worker             x25519.dh(&private_key_short, &public_key),
192*e1997b9aSAndroid Build Coastguard Worker             Err(EcdhError::InvalidPrivKeyLen { .. })
193*e1997b9aSAndroid Build Coastguard Worker         );
194*e1997b9aSAndroid Build Coastguard Worker 
195*e1997b9aSAndroid Build Coastguard Worker         let private_key = HpkeSecretKey::from(
196*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475")
197*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
198*e1997b9aSAndroid Build Coastguard Worker         );
199*e1997b9aSAndroid Build Coastguard Worker         let public_key_short =
200*e1997b9aSAndroid Build Coastguard Worker             HpkePublicKey::from(decode_hex::<16>("504a36999f489cd2fdbc08baff3d88fa").to_vec());
201*e1997b9aSAndroid Build Coastguard Worker         assert_matches!(
202*e1997b9aSAndroid Build Coastguard Worker             x25519.dh(&private_key, &public_key_short),
203*e1997b9aSAndroid Build Coastguard Worker             Err(EcdhError::InvalidPubKeyLen { .. })
204*e1997b9aSAndroid Build Coastguard Worker         );
205*e1997b9aSAndroid Build Coastguard Worker     }
206*e1997b9aSAndroid Build Coastguard Worker 
207*e1997b9aSAndroid Build Coastguard Worker     #[test]
to_public()208*e1997b9aSAndroid Build Coastguard Worker     fn to_public() {
209*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1
210*e1997b9aSAndroid Build Coastguard Worker         let private_key = HpkeSecretKey::from(
211*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
212*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
213*e1997b9aSAndroid Build Coastguard Worker         );
214*e1997b9aSAndroid Build Coastguard Worker         let expected_public_key = HpkePublicKey::from(
215*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
216*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
217*e1997b9aSAndroid Build Coastguard Worker         );
218*e1997b9aSAndroid Build Coastguard Worker 
219*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_CHACHA).unwrap();
220*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(x25519.to_public(&private_key).unwrap(), expected_public_key);
221*e1997b9aSAndroid Build Coastguard Worker     }
222*e1997b9aSAndroid Build Coastguard Worker 
223*e1997b9aSAndroid Build Coastguard Worker     #[test]
to_public_invalid_key()224*e1997b9aSAndroid Build Coastguard Worker     fn to_public_invalid_key() {
225*e1997b9aSAndroid Build Coastguard Worker         let private_key_short =
226*e1997b9aSAndroid Build Coastguard Worker             HpkeSecretKey::from(decode_hex::<16>("c8a9d5a91091ad851c668b0736c1c9a0").to_vec());
227*e1997b9aSAndroid Build Coastguard Worker 
228*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_CHACHA).unwrap();
229*e1997b9aSAndroid Build Coastguard Worker         assert_matches!(
230*e1997b9aSAndroid Build Coastguard Worker             x25519.to_public(&private_key_short),
231*e1997b9aSAndroid Build Coastguard Worker             Err(EcdhError::InvalidPrivKeyLen { .. })
232*e1997b9aSAndroid Build Coastguard Worker         );
233*e1997b9aSAndroid Build Coastguard Worker     }
234*e1997b9aSAndroid Build Coastguard Worker 
235*e1997b9aSAndroid Build Coastguard Worker     #[test]
generate()236*e1997b9aSAndroid Build Coastguard Worker     fn generate() {
237*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_AES128).unwrap();
238*e1997b9aSAndroid Build Coastguard Worker         assert!(x25519.generate().is_ok());
239*e1997b9aSAndroid Build Coastguard Worker     }
240*e1997b9aSAndroid Build Coastguard Worker 
241*e1997b9aSAndroid Build Coastguard Worker     #[test]
public_key_validate()242*e1997b9aSAndroid Build Coastguard Worker     fn public_key_validate() {
243*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1
244*e1997b9aSAndroid Build Coastguard Worker         let public_key = HpkePublicKey::from(
245*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
246*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
247*e1997b9aSAndroid Build Coastguard Worker         );
248*e1997b9aSAndroid Build Coastguard Worker 
249*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_AES128).unwrap();
250*e1997b9aSAndroid Build Coastguard Worker         assert!(x25519.public_key_validate(&public_key).is_ok());
251*e1997b9aSAndroid Build Coastguard Worker     }
252*e1997b9aSAndroid Build Coastguard Worker 
253*e1997b9aSAndroid Build Coastguard Worker     #[test]
public_key_validate_invalid_key()254*e1997b9aSAndroid Build Coastguard Worker     fn public_key_validate_invalid_key() {
255*e1997b9aSAndroid Build Coastguard Worker         let public_key_short =
256*e1997b9aSAndroid Build Coastguard Worker             HpkePublicKey::from(decode_hex::<16>("504a36999f489cd2fdbc08baff3d88fa").to_vec());
257*e1997b9aSAndroid Build Coastguard Worker 
258*e1997b9aSAndroid Build Coastguard Worker         let x25519 = Ecdh::new(CipherSuite::CURVE25519_AES128).unwrap();
259*e1997b9aSAndroid Build Coastguard Worker         assert_matches!(
260*e1997b9aSAndroid Build Coastguard Worker             x25519.public_key_validate(&public_key_short),
261*e1997b9aSAndroid Build Coastguard Worker             Err(EcdhError::InvalidPubKeyLen { .. })
262*e1997b9aSAndroid Build Coastguard Worker         );
263*e1997b9aSAndroid Build Coastguard Worker     }
264*e1997b9aSAndroid Build Coastguard Worker 
265*e1997b9aSAndroid Build Coastguard Worker     #[test]
unsupported_cipher_suites()266*e1997b9aSAndroid Build Coastguard Worker     fn unsupported_cipher_suites() {
267*e1997b9aSAndroid Build Coastguard Worker         for suite in vec![
268*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::P256_AES128,
269*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::P384_AES256,
270*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::P521_AES256,
271*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::CURVE448_CHACHA,
272*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::CURVE448_AES256,
273*e1997b9aSAndroid Build Coastguard Worker         ] {
274*e1997b9aSAndroid Build Coastguard Worker             assert_matches!(
275*e1997b9aSAndroid Build Coastguard Worker                 Ecdh::new(suite).unwrap().generate(),
276*e1997b9aSAndroid Build Coastguard Worker                 Err(EcdhError::UnsupportedCipherSuite)
277*e1997b9aSAndroid Build Coastguard Worker             );
278*e1997b9aSAndroid Build Coastguard Worker         }
279*e1997b9aSAndroid Build Coastguard Worker     }
280*e1997b9aSAndroid Build Coastguard Worker }
281