xref: /aosp_15_r20/system/security/mls/mls-rs-crypto-boringssl/src/hpke.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 //! Hybrid public key encryption.
16*e1997b9aSAndroid Build Coastguard Worker 
17*e1997b9aSAndroid Build Coastguard Worker use bssl_crypto::hpke;
18*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::crypto::{
19*e1997b9aSAndroid Build Coastguard Worker     CipherSuite, HpkeCiphertext, HpkeContextR, HpkeContextS, HpkePublicKey, HpkeSecretKey,
20*e1997b9aSAndroid Build Coastguard Worker };
21*e1997b9aSAndroid Build Coastguard Worker use mls_rs_core::error::{AnyError, IntoAnyError};
22*e1997b9aSAndroid Build Coastguard Worker use mls_rs_crypto_traits::{DhType, KdfType, KemId, KemResult, KemType};
23*e1997b9aSAndroid Build Coastguard Worker use std::sync::Mutex;
24*e1997b9aSAndroid Build Coastguard Worker use thiserror::Error;
25*e1997b9aSAndroid Build Coastguard Worker 
26*e1997b9aSAndroid Build Coastguard Worker /// Errors returned from HPKE.
27*e1997b9aSAndroid Build Coastguard Worker #[derive(Debug, Error)]
28*e1997b9aSAndroid Build Coastguard Worker pub enum HpkeError {
29*e1997b9aSAndroid Build Coastguard Worker     /// Error returned from BoringSSL.
30*e1997b9aSAndroid Build Coastguard Worker     #[error("BoringSSL error")]
31*e1997b9aSAndroid Build Coastguard Worker     BoringsslError,
32*e1997b9aSAndroid Build Coastguard Worker     /// Error returned from Diffie-Hellman operations.
33*e1997b9aSAndroid Build Coastguard Worker     #[error(transparent)]
34*e1997b9aSAndroid Build Coastguard Worker     DhError(AnyError),
35*e1997b9aSAndroid Build Coastguard Worker     /// Error returned from KDF operations.
36*e1997b9aSAndroid Build Coastguard Worker     #[error(transparent)]
37*e1997b9aSAndroid Build Coastguard Worker     KdfError(AnyError),
38*e1997b9aSAndroid Build Coastguard Worker     /// Error returned when unsupported cipher suite is requested.
39*e1997b9aSAndroid Build Coastguard Worker     #[error("unsupported cipher suite")]
40*e1997b9aSAndroid Build Coastguard Worker     UnsupportedCipherSuite,
41*e1997b9aSAndroid Build Coastguard Worker }
42*e1997b9aSAndroid Build Coastguard Worker 
43*e1997b9aSAndroid Build Coastguard Worker impl IntoAnyError for HpkeError {
into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self>44*e1997b9aSAndroid Build Coastguard Worker     fn into_dyn_error(self) -> Result<Box<dyn std::error::Error + Send + Sync>, Self> {
45*e1997b9aSAndroid Build Coastguard Worker         Ok(self.into())
46*e1997b9aSAndroid Build Coastguard Worker     }
47*e1997b9aSAndroid Build Coastguard Worker }
48*e1997b9aSAndroid Build Coastguard Worker 
49*e1997b9aSAndroid Build Coastguard Worker #[derive(Clone, Debug, Eq, PartialEq)]
50*e1997b9aSAndroid Build Coastguard Worker pub(crate) struct KdfWrapper<KDF: KdfType> {
51*e1997b9aSAndroid Build Coastguard Worker     suite_id: Vec<u8>,
52*e1997b9aSAndroid Build Coastguard Worker     kdf: KDF,
53*e1997b9aSAndroid Build Coastguard Worker }
54*e1997b9aSAndroid Build Coastguard Worker 
55*e1997b9aSAndroid Build Coastguard Worker impl<KDF: KdfType> KdfWrapper<KDF> {
new(suite_id: Vec<u8>, kdf: KDF) -> Self56*e1997b9aSAndroid Build Coastguard Worker     pub fn new(suite_id: Vec<u8>, kdf: KDF) -> Self {
57*e1997b9aSAndroid Build Coastguard Worker         Self { suite_id, kdf }
58*e1997b9aSAndroid Build Coastguard Worker     }
59*e1997b9aSAndroid Build Coastguard Worker 
60*e1997b9aSAndroid Build Coastguard Worker     // https://www.rfc-editor.org/rfc/rfc9180.html#section-4-9
61*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
labeled_extract( &self, salt: &[u8], label: &[u8], ikm: &[u8], ) -> Result<Vec<u8>, <KDF as KdfType>::Error>62*e1997b9aSAndroid Build Coastguard Worker     pub async fn labeled_extract(
63*e1997b9aSAndroid Build Coastguard Worker         &self,
64*e1997b9aSAndroid Build Coastguard Worker         salt: &[u8],
65*e1997b9aSAndroid Build Coastguard Worker         label: &[u8],
66*e1997b9aSAndroid Build Coastguard Worker         ikm: &[u8],
67*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Vec<u8>, <KDF as KdfType>::Error> {
68*e1997b9aSAndroid Build Coastguard Worker         self.kdf.extract(salt, &[b"HPKE-v1" as &[u8], &self.suite_id, label, ikm].concat()).await
69*e1997b9aSAndroid Build Coastguard Worker     }
70*e1997b9aSAndroid Build Coastguard Worker 
71*e1997b9aSAndroid Build Coastguard Worker     // https://www.rfc-editor.org/rfc/rfc9180.html#section-4-9
72*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
labeled_expand( &self, key: &[u8], label: &[u8], info: &[u8], len: usize, ) -> Result<Vec<u8>, <KDF as KdfType>::Error>73*e1997b9aSAndroid Build Coastguard Worker     pub async fn labeled_expand(
74*e1997b9aSAndroid Build Coastguard Worker         &self,
75*e1997b9aSAndroid Build Coastguard Worker         key: &[u8],
76*e1997b9aSAndroid Build Coastguard Worker         label: &[u8],
77*e1997b9aSAndroid Build Coastguard Worker         info: &[u8],
78*e1997b9aSAndroid Build Coastguard Worker         len: usize,
79*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Vec<u8>, <KDF as KdfType>::Error> {
80*e1997b9aSAndroid Build Coastguard Worker         let labeled_info =
81*e1997b9aSAndroid Build Coastguard Worker             [&(len as u16).to_be_bytes() as &[u8], b"HPKE-v1", &self.suite_id, label, info]
82*e1997b9aSAndroid Build Coastguard Worker                 .concat();
83*e1997b9aSAndroid Build Coastguard Worker         self.kdf.expand(key, &labeled_info, len).await
84*e1997b9aSAndroid Build Coastguard Worker     }
85*e1997b9aSAndroid Build Coastguard Worker }
86*e1997b9aSAndroid Build Coastguard Worker 
87*e1997b9aSAndroid Build Coastguard Worker /// KemType implementation backed by BoringSSL.
88*e1997b9aSAndroid Build Coastguard Worker #[derive(Clone, Debug, Eq, PartialEq)]
89*e1997b9aSAndroid Build Coastguard Worker pub struct DhKem<DH: DhType, KDF: KdfType> {
90*e1997b9aSAndroid Build Coastguard Worker     dh: DH,
91*e1997b9aSAndroid Build Coastguard Worker     kdf: KdfWrapper<KDF>,
92*e1997b9aSAndroid Build Coastguard Worker     kem_id: KemId,
93*e1997b9aSAndroid Build Coastguard Worker     n_secret: usize,
94*e1997b9aSAndroid Build Coastguard Worker }
95*e1997b9aSAndroid Build Coastguard Worker 
96*e1997b9aSAndroid Build Coastguard Worker impl<DH: DhType, KDF: KdfType> DhKem<DH, KDF> {
97*e1997b9aSAndroid Build Coastguard Worker     /// Creates a new DhKem.
new(cipher_suite: CipherSuite, dh: DH, kdf: KDF) -> Option<Self>98*e1997b9aSAndroid Build Coastguard Worker     pub fn new(cipher_suite: CipherSuite, dh: DH, kdf: KDF) -> Option<Self> {
99*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1-5
100*e1997b9aSAndroid Build Coastguard Worker         let kem_id = KemId::new(cipher_suite)?;
101*e1997b9aSAndroid Build Coastguard Worker         let suite_id = [b"KEM", &(kem_id as u16).to_be_bytes() as &[u8]].concat();
102*e1997b9aSAndroid Build Coastguard Worker 
103*e1997b9aSAndroid Build Coastguard Worker         let kdf = KdfWrapper::new(suite_id, kdf);
104*e1997b9aSAndroid Build Coastguard Worker 
105*e1997b9aSAndroid Build Coastguard Worker         Some(Self { dh, kdf, kem_id, n_secret: kem_id.n_secret() })
106*e1997b9aSAndroid Build Coastguard Worker     }
107*e1997b9aSAndroid Build Coastguard Worker }
108*e1997b9aSAndroid Build Coastguard Worker 
109*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
110*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
111*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)]
112*e1997b9aSAndroid Build Coastguard Worker impl<DH: DhType, KDF: KdfType> KemType for DhKem<DH, KDF> {
113*e1997b9aSAndroid Build Coastguard Worker     type Error = HpkeError;
114*e1997b9aSAndroid Build Coastguard Worker 
kem_id(&self) -> u16115*e1997b9aSAndroid Build Coastguard Worker     fn kem_id(&self) -> u16 {
116*e1997b9aSAndroid Build Coastguard Worker         self.kem_id as u16
117*e1997b9aSAndroid Build Coastguard Worker     }
118*e1997b9aSAndroid Build Coastguard Worker 
generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error>119*e1997b9aSAndroid Build Coastguard Worker     async fn generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
120*e1997b9aSAndroid Build Coastguard Worker         if self.kem_id != KemId::DhKemX25519Sha256 {
121*e1997b9aSAndroid Build Coastguard Worker             return Err(HpkeError::UnsupportedCipherSuite);
122*e1997b9aSAndroid Build Coastguard Worker         }
123*e1997b9aSAndroid Build Coastguard Worker 
124*e1997b9aSAndroid Build Coastguard Worker         let kem = hpke::Kem::X25519HkdfSha256;
125*e1997b9aSAndroid Build Coastguard Worker         let (public_key, private_key) = kem.generate_keypair();
126*e1997b9aSAndroid Build Coastguard Worker         Ok((private_key.to_vec().into(), public_key.to_vec().into()))
127*e1997b9aSAndroid Build Coastguard Worker     }
128*e1997b9aSAndroid Build Coastguard Worker 
129*e1997b9aSAndroid Build Coastguard Worker     // https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1.3-8
derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error>130*e1997b9aSAndroid Build Coastguard Worker     async fn derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
131*e1997b9aSAndroid Build Coastguard Worker         let dkp_prk = match self.kdf.labeled_extract(&[], b"dkp_prk", ikm).await {
132*e1997b9aSAndroid Build Coastguard Worker             Ok(p) => p,
133*e1997b9aSAndroid Build Coastguard Worker             Err(e) => return Err(HpkeError::KdfError(e.into_any_error())),
134*e1997b9aSAndroid Build Coastguard Worker         };
135*e1997b9aSAndroid Build Coastguard Worker         let sk =
136*e1997b9aSAndroid Build Coastguard Worker             match self.kdf.labeled_expand(&dkp_prk, b"sk", &[], self.dh.secret_key_size()).await {
137*e1997b9aSAndroid Build Coastguard Worker                 Ok(s) => s.into(),
138*e1997b9aSAndroid Build Coastguard Worker                 Err(e) => return Err(HpkeError::KdfError(e.into_any_error())),
139*e1997b9aSAndroid Build Coastguard Worker             };
140*e1997b9aSAndroid Build Coastguard Worker         let pk = match self.dh.to_public(&sk).await {
141*e1997b9aSAndroid Build Coastguard Worker             Ok(p) => p,
142*e1997b9aSAndroid Build Coastguard Worker             Err(e) => return Err(HpkeError::KdfError(e.into_any_error())),
143*e1997b9aSAndroid Build Coastguard Worker         };
144*e1997b9aSAndroid Build Coastguard Worker         Ok((sk, pk))
145*e1997b9aSAndroid Build Coastguard Worker     }
146*e1997b9aSAndroid Build Coastguard Worker 
public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error>147*e1997b9aSAndroid Build Coastguard Worker     fn public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error> {
148*e1997b9aSAndroid Build Coastguard Worker         match self.dh.public_key_validate(key) {
149*e1997b9aSAndroid Build Coastguard Worker             Ok(_) => Ok(()),
150*e1997b9aSAndroid Build Coastguard Worker             Err(e) => Err(HpkeError::DhError(e.into_any_error())),
151*e1997b9aSAndroid Build Coastguard Worker         }
152*e1997b9aSAndroid Build Coastguard Worker     }
153*e1997b9aSAndroid Build Coastguard Worker 
154*e1997b9aSAndroid Build Coastguard Worker     // Using BoringSSL's HPKE implementation so this is not needed.
encap(&self, _remote_pk: &HpkePublicKey) -> Result<KemResult, Self::Error>155*e1997b9aSAndroid Build Coastguard Worker     async fn encap(&self, _remote_pk: &HpkePublicKey) -> Result<KemResult, Self::Error> {
156*e1997b9aSAndroid Build Coastguard Worker         unimplemented!();
157*e1997b9aSAndroid Build Coastguard Worker     }
158*e1997b9aSAndroid Build Coastguard Worker 
159*e1997b9aSAndroid Build Coastguard Worker     // Using BoringSSL's HPKE implementation so this is not needed.
decap( &self, _enc: &[u8], _secret_key: &HpkeSecretKey, _public_key: &HpkePublicKey, ) -> Result<Vec<u8>, Self::Error>160*e1997b9aSAndroid Build Coastguard Worker     async fn decap(
161*e1997b9aSAndroid Build Coastguard Worker         &self,
162*e1997b9aSAndroid Build Coastguard Worker         _enc: &[u8],
163*e1997b9aSAndroid Build Coastguard Worker         _secret_key: &HpkeSecretKey,
164*e1997b9aSAndroid Build Coastguard Worker         _public_key: &HpkePublicKey,
165*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Vec<u8>, Self::Error> {
166*e1997b9aSAndroid Build Coastguard Worker         unimplemented!();
167*e1997b9aSAndroid Build Coastguard Worker     }
168*e1997b9aSAndroid Build Coastguard Worker }
169*e1997b9aSAndroid Build Coastguard Worker 
170*e1997b9aSAndroid Build Coastguard Worker /// HpkeContextS implementation backed by BoringSSL.
171*e1997b9aSAndroid Build Coastguard Worker pub struct ContextS(pub Mutex<hpke::SenderContext>);
172*e1997b9aSAndroid Build Coastguard Worker 
173*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
174*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
175*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)]
176*e1997b9aSAndroid Build Coastguard Worker impl HpkeContextS for ContextS {
177*e1997b9aSAndroid Build Coastguard Worker     type Error = HpkeError;
178*e1997b9aSAndroid Build Coastguard Worker 
179*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
seal(&mut self, aad: Option<&[u8]>, data: &[u8]) -> Result<Vec<u8>, Self::Error>180*e1997b9aSAndroid Build Coastguard Worker     async fn seal(&mut self, aad: Option<&[u8]>, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
181*e1997b9aSAndroid Build Coastguard Worker         Ok(self.0.lock().unwrap().seal(data, aad.unwrap_or_default()))
182*e1997b9aSAndroid Build Coastguard Worker     }
183*e1997b9aSAndroid Build Coastguard Worker 
184*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
export(&self, exporter_context: &[u8], len: usize) -> Result<Vec<u8>, Self::Error>185*e1997b9aSAndroid Build Coastguard Worker     async fn export(&self, exporter_context: &[u8], len: usize) -> Result<Vec<u8>, Self::Error> {
186*e1997b9aSAndroid Build Coastguard Worker         Ok(self.0.lock().unwrap().export(exporter_context, len).to_vec())
187*e1997b9aSAndroid Build Coastguard Worker     }
188*e1997b9aSAndroid Build Coastguard Worker }
189*e1997b9aSAndroid Build Coastguard Worker 
190*e1997b9aSAndroid Build Coastguard Worker /// HpkeContextR implementation backed by BoringSSL.
191*e1997b9aSAndroid Build Coastguard Worker pub struct ContextR(pub Mutex<hpke::RecipientContext>);
192*e1997b9aSAndroid Build Coastguard Worker 
193*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
194*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
195*e1997b9aSAndroid Build Coastguard Worker #[cfg_attr(all(not(target_arch = "wasm32"), mls_build_async), maybe_async::must_be_async)]
196*e1997b9aSAndroid Build Coastguard Worker impl HpkeContextR for ContextR {
197*e1997b9aSAndroid Build Coastguard Worker     type Error = HpkeError;
198*e1997b9aSAndroid Build Coastguard Worker 
199*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
open( &mut self, aad: Option<&[u8]>, ciphertext: &[u8], ) -> Result<Vec<u8>, Self::Error>200*e1997b9aSAndroid Build Coastguard Worker     async fn open(
201*e1997b9aSAndroid Build Coastguard Worker         &mut self,
202*e1997b9aSAndroid Build Coastguard Worker         aad: Option<&[u8]>,
203*e1997b9aSAndroid Build Coastguard Worker         ciphertext: &[u8],
204*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Vec<u8>, Self::Error> {
205*e1997b9aSAndroid Build Coastguard Worker         self.0
206*e1997b9aSAndroid Build Coastguard Worker             .lock()
207*e1997b9aSAndroid Build Coastguard Worker             .unwrap()
208*e1997b9aSAndroid Build Coastguard Worker             .open(ciphertext, aad.unwrap_or_default())
209*e1997b9aSAndroid Build Coastguard Worker             .ok_or(HpkeError::BoringsslError)
210*e1997b9aSAndroid Build Coastguard Worker     }
211*e1997b9aSAndroid Build Coastguard Worker 
212*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
export(&self, exporter_context: &[u8], len: usize) -> Result<Vec<u8>, Self::Error>213*e1997b9aSAndroid Build Coastguard Worker     async fn export(&self, exporter_context: &[u8], len: usize) -> Result<Vec<u8>, Self::Error> {
214*e1997b9aSAndroid Build Coastguard Worker         Ok(self.0.lock().unwrap().export(exporter_context, len).to_vec())
215*e1997b9aSAndroid Build Coastguard Worker     }
216*e1997b9aSAndroid Build Coastguard Worker }
217*e1997b9aSAndroid Build Coastguard Worker 
218*e1997b9aSAndroid Build Coastguard Worker /// HPKE implementation backed by BoringSSL.
219*e1997b9aSAndroid Build Coastguard Worker #[derive(Clone)]
220*e1997b9aSAndroid Build Coastguard Worker pub struct Hpke(pub CipherSuite);
221*e1997b9aSAndroid Build Coastguard Worker 
222*e1997b9aSAndroid Build Coastguard Worker impl Hpke {
223*e1997b9aSAndroid Build Coastguard Worker     /// Creates a new Hpke.
new(cipher_suite: CipherSuite) -> Self224*e1997b9aSAndroid Build Coastguard Worker     pub fn new(cipher_suite: CipherSuite) -> Self {
225*e1997b9aSAndroid Build Coastguard Worker         Self(cipher_suite)
226*e1997b9aSAndroid Build Coastguard Worker     }
227*e1997b9aSAndroid Build Coastguard Worker 
228*e1997b9aSAndroid Build Coastguard Worker     /// Sets up HPKE sender context.
229*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
setup_sender( &self, remote_key: &HpkePublicKey, info: &[u8], ) -> Result<(Vec<u8>, ContextS), HpkeError>230*e1997b9aSAndroid Build Coastguard Worker     pub async fn setup_sender(
231*e1997b9aSAndroid Build Coastguard Worker         &self,
232*e1997b9aSAndroid Build Coastguard Worker         remote_key: &HpkePublicKey,
233*e1997b9aSAndroid Build Coastguard Worker         info: &[u8],
234*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<(Vec<u8>, ContextS), HpkeError> {
235*e1997b9aSAndroid Build Coastguard Worker         let params = Self::cipher_suite_to_params(self.0)?;
236*e1997b9aSAndroid Build Coastguard Worker         match hpke::SenderContext::new(&params, remote_key, info) {
237*e1997b9aSAndroid Build Coastguard Worker             Some((ctx, encapsulated_key)) => Ok((encapsulated_key, ContextS(ctx.into()))),
238*e1997b9aSAndroid Build Coastguard Worker             None => Err(HpkeError::BoringsslError),
239*e1997b9aSAndroid Build Coastguard Worker         }
240*e1997b9aSAndroid Build Coastguard Worker     }
241*e1997b9aSAndroid Build Coastguard Worker 
242*e1997b9aSAndroid Build Coastguard Worker     /// Sets up HPKE sender context and encrypts `pt` with optional associated data `aad`.
243*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
seal( &self, remote_key: &HpkePublicKey, info: &[u8], aad: Option<&[u8]>, pt: &[u8], ) -> Result<HpkeCiphertext, HpkeError>244*e1997b9aSAndroid Build Coastguard Worker     pub async fn seal(
245*e1997b9aSAndroid Build Coastguard Worker         &self,
246*e1997b9aSAndroid Build Coastguard Worker         remote_key: &HpkePublicKey,
247*e1997b9aSAndroid Build Coastguard Worker         info: &[u8],
248*e1997b9aSAndroid Build Coastguard Worker         aad: Option<&[u8]>,
249*e1997b9aSAndroid Build Coastguard Worker         pt: &[u8],
250*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<HpkeCiphertext, HpkeError> {
251*e1997b9aSAndroid Build Coastguard Worker         let (kem_output, mut ctx) = self.setup_sender(remote_key, info).await?;
252*e1997b9aSAndroid Build Coastguard Worker         Ok(HpkeCiphertext { kem_output, ciphertext: ctx.seal(aad, pt).await? })
253*e1997b9aSAndroid Build Coastguard Worker     }
254*e1997b9aSAndroid Build Coastguard Worker 
255*e1997b9aSAndroid Build Coastguard Worker     /// Sets up HPKE receiver context.
256*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
setup_receiver( &self, enc: &[u8], local_secret: &HpkeSecretKey, info: &[u8], ) -> Result<ContextR, HpkeError>257*e1997b9aSAndroid Build Coastguard Worker     pub async fn setup_receiver(
258*e1997b9aSAndroid Build Coastguard Worker         &self,
259*e1997b9aSAndroid Build Coastguard Worker         enc: &[u8],
260*e1997b9aSAndroid Build Coastguard Worker         local_secret: &HpkeSecretKey,
261*e1997b9aSAndroid Build Coastguard Worker         info: &[u8],
262*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<ContextR, HpkeError> {
263*e1997b9aSAndroid Build Coastguard Worker         let params = Self::cipher_suite_to_params(self.0)?;
264*e1997b9aSAndroid Build Coastguard Worker         match hpke::RecipientContext::new(&params, local_secret, enc, info) {
265*e1997b9aSAndroid Build Coastguard Worker             Some(ctx) => Ok(ContextR(ctx.into())),
266*e1997b9aSAndroid Build Coastguard Worker             None => Err(HpkeError::BoringsslError),
267*e1997b9aSAndroid Build Coastguard Worker         }
268*e1997b9aSAndroid Build Coastguard Worker     }
269*e1997b9aSAndroid Build Coastguard Worker 
270*e1997b9aSAndroid Build Coastguard Worker     /// Sets up HPKE receiver context and decrypts `ciphertext` with optional associated data `aad`.
271*e1997b9aSAndroid Build Coastguard Worker     #[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
open( &self, ciphertext: &HpkeCiphertext, local_secret: &HpkeSecretKey, info: &[u8], aad: Option<&[u8]>, ) -> Result<Vec<u8>, HpkeError>272*e1997b9aSAndroid Build Coastguard Worker     pub async fn open(
273*e1997b9aSAndroid Build Coastguard Worker         &self,
274*e1997b9aSAndroid Build Coastguard Worker         ciphertext: &HpkeCiphertext,
275*e1997b9aSAndroid Build Coastguard Worker         local_secret: &HpkeSecretKey,
276*e1997b9aSAndroid Build Coastguard Worker         info: &[u8],
277*e1997b9aSAndroid Build Coastguard Worker         aad: Option<&[u8]>,
278*e1997b9aSAndroid Build Coastguard Worker     ) -> Result<Vec<u8>, HpkeError> {
279*e1997b9aSAndroid Build Coastguard Worker         let mut ctx = self.setup_receiver(&ciphertext.kem_output, local_secret, info).await?;
280*e1997b9aSAndroid Build Coastguard Worker         ctx.open(aad, &ciphertext.ciphertext).await
281*e1997b9aSAndroid Build Coastguard Worker     }
282*e1997b9aSAndroid Build Coastguard Worker 
cipher_suite_to_params(cipher_suite: CipherSuite) -> Result<hpke::Params, HpkeError>283*e1997b9aSAndroid Build Coastguard Worker     fn cipher_suite_to_params(cipher_suite: CipherSuite) -> Result<hpke::Params, HpkeError> {
284*e1997b9aSAndroid Build Coastguard Worker         match cipher_suite {
285*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::CURVE25519_AES128 => Ok(hpke::Params::new(
286*e1997b9aSAndroid Build Coastguard Worker                 hpke::Kem::X25519HkdfSha256,
287*e1997b9aSAndroid Build Coastguard Worker                 hpke::Kdf::HkdfSha256,
288*e1997b9aSAndroid Build Coastguard Worker                 hpke::Aead::Aes128Gcm,
289*e1997b9aSAndroid Build Coastguard Worker             )),
290*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::CURVE25519_CHACHA => Ok(hpke::Params::new(
291*e1997b9aSAndroid Build Coastguard Worker                 hpke::Kem::X25519HkdfSha256,
292*e1997b9aSAndroid Build Coastguard Worker                 hpke::Kdf::HkdfSha256,
293*e1997b9aSAndroid Build Coastguard Worker                 hpke::Aead::Chacha20Poly1305,
294*e1997b9aSAndroid Build Coastguard Worker             )),
295*e1997b9aSAndroid Build Coastguard Worker             _ => Err(HpkeError::UnsupportedCipherSuite),
296*e1997b9aSAndroid Build Coastguard Worker         }
297*e1997b9aSAndroid Build Coastguard Worker     }
298*e1997b9aSAndroid Build Coastguard Worker }
299*e1997b9aSAndroid Build Coastguard Worker 
300*e1997b9aSAndroid Build Coastguard Worker #[cfg(all(not(mls_build_async), test))]
301*e1997b9aSAndroid Build Coastguard Worker mod test {
302*e1997b9aSAndroid Build Coastguard Worker     use super::{DhKem, Hpke, KdfWrapper};
303*e1997b9aSAndroid Build Coastguard Worker     use crate::ecdh::Ecdh;
304*e1997b9aSAndroid Build Coastguard Worker     use crate::kdf::Kdf;
305*e1997b9aSAndroid Build Coastguard Worker     use crate::test_helpers::decode_hex;
306*e1997b9aSAndroid Build Coastguard Worker     use mls_rs_core::crypto::{
307*e1997b9aSAndroid Build Coastguard Worker         CipherSuite, HpkeContextR, HpkeContextS, HpkePublicKey, HpkeSecretKey,
308*e1997b9aSAndroid Build Coastguard Worker     };
309*e1997b9aSAndroid Build Coastguard Worker     use mls_rs_crypto_traits::{AeadId, KdfId, KemId, KemType};
310*e1997b9aSAndroid Build Coastguard Worker     use std::thread;
311*e1997b9aSAndroid Build Coastguard Worker 
312*e1997b9aSAndroid Build Coastguard Worker     // https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1-8
hpke_suite_id(cipher_suite: CipherSuite) -> Vec<u8>313*e1997b9aSAndroid Build Coastguard Worker     fn hpke_suite_id(cipher_suite: CipherSuite) -> Vec<u8> {
314*e1997b9aSAndroid Build Coastguard Worker         [
315*e1997b9aSAndroid Build Coastguard Worker             b"HPKE",
316*e1997b9aSAndroid Build Coastguard Worker             &(KemId::new(cipher_suite).unwrap() as u16).to_be_bytes() as &[u8],
317*e1997b9aSAndroid Build Coastguard Worker             &(KdfId::new(cipher_suite).unwrap() as u16).to_be_bytes() as &[u8],
318*e1997b9aSAndroid Build Coastguard Worker             &(AeadId::new(cipher_suite).unwrap() as u16).to_be_bytes() as &[u8],
319*e1997b9aSAndroid Build Coastguard Worker         ]
320*e1997b9aSAndroid Build Coastguard Worker         .concat()
321*e1997b9aSAndroid Build Coastguard Worker     }
322*e1997b9aSAndroid Build Coastguard Worker 
323*e1997b9aSAndroid Build Coastguard Worker     #[test]
kdf_labeled_extract()324*e1997b9aSAndroid Build Coastguard Worker     fn kdf_labeled_extract() {
325*e1997b9aSAndroid Build Coastguard Worker         let cipher_suite = CipherSuite::CURVE25519_AES128;
326*e1997b9aSAndroid Build Coastguard Worker         let suite_id = hpke_suite_id(cipher_suite);
327*e1997b9aSAndroid Build Coastguard Worker         let kdf = KdfWrapper::new(suite_id, Kdf::new(cipher_suite).unwrap());
328*e1997b9aSAndroid Build Coastguard Worker 
329*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
330*e1997b9aSAndroid Build Coastguard Worker         let shared_secret: [u8; 32] =
331*e1997b9aSAndroid Build Coastguard Worker             decode_hex("fe0e18c9f024ce43799ae393c7e8fe8fce9d218875e8227b0187c04e7d2ea1fc");
332*e1997b9aSAndroid Build Coastguard Worker         let expected_secret: [u8; 32] =
333*e1997b9aSAndroid Build Coastguard Worker             decode_hex("12fff91991e93b48de37e7daddb52981084bd8aa64289c3788471d9a9712f397");
334*e1997b9aSAndroid Build Coastguard Worker         let label = b"secret";
335*e1997b9aSAndroid Build Coastguard Worker 
336*e1997b9aSAndroid Build Coastguard Worker         let secret = kdf.labeled_extract(&shared_secret, label, &[]).unwrap();
337*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(secret, expected_secret);
338*e1997b9aSAndroid Build Coastguard Worker     }
339*e1997b9aSAndroid Build Coastguard Worker 
340*e1997b9aSAndroid Build Coastguard Worker     #[test]
kdf_labeled_expand()341*e1997b9aSAndroid Build Coastguard Worker     fn kdf_labeled_expand() {
342*e1997b9aSAndroid Build Coastguard Worker         let cipher_suite = CipherSuite::CURVE25519_AES128;
343*e1997b9aSAndroid Build Coastguard Worker         let suite_id = hpke_suite_id(cipher_suite);
344*e1997b9aSAndroid Build Coastguard Worker         let kdf = KdfWrapper::new(suite_id, Kdf::new(cipher_suite).unwrap());
345*e1997b9aSAndroid Build Coastguard Worker 
346*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
347*e1997b9aSAndroid Build Coastguard Worker         let secret: [u8; 32] =
348*e1997b9aSAndroid Build Coastguard Worker             decode_hex("12fff91991e93b48de37e7daddb52981084bd8aa64289c3788471d9a9712f397");
349*e1997b9aSAndroid Build Coastguard Worker         let key_schedule_ctx : [u8; 65] = decode_hex("00725611c9d98c07c03f60095cd32d400d8347d45ed67097bbad50fc56da742d07cb6cffde367bb0565ba28bb02c90744a20f5ef37f30523526106f637abb05449");
350*e1997b9aSAndroid Build Coastguard Worker         let expected_key: [u8; 16] = decode_hex("4531685d41d65f03dc48f6b8302c05b0");
351*e1997b9aSAndroid Build Coastguard Worker         let label = b"key";
352*e1997b9aSAndroid Build Coastguard Worker 
353*e1997b9aSAndroid Build Coastguard Worker         let key = kdf.labeled_expand(&secret, label, &key_schedule_ctx, 16).unwrap();
354*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(key, expected_key);
355*e1997b9aSAndroid Build Coastguard Worker     }
356*e1997b9aSAndroid Build Coastguard Worker 
357*e1997b9aSAndroid Build Coastguard Worker     #[test]
dh_kem_kem_id()358*e1997b9aSAndroid Build Coastguard Worker     fn dh_kem_kem_id() {
359*e1997b9aSAndroid Build Coastguard Worker         let cipher_suite = CipherSuite::CURVE25519_CHACHA;
360*e1997b9aSAndroid Build Coastguard Worker         let dh = Ecdh::new(cipher_suite).unwrap();
361*e1997b9aSAndroid Build Coastguard Worker         let kdf = Kdf::new(cipher_suite).unwrap();
362*e1997b9aSAndroid Build Coastguard Worker         let kem = DhKem::new(cipher_suite, dh, kdf).unwrap();
363*e1997b9aSAndroid Build Coastguard Worker 
364*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(kem.kem_id(), 32);
365*e1997b9aSAndroid Build Coastguard Worker     }
366*e1997b9aSAndroid Build Coastguard Worker 
367*e1997b9aSAndroid Build Coastguard Worker     #[test]
dh_kem_generate()368*e1997b9aSAndroid Build Coastguard Worker     fn dh_kem_generate() {
369*e1997b9aSAndroid Build Coastguard Worker         let cipher_suite = CipherSuite::CURVE25519_AES128;
370*e1997b9aSAndroid Build Coastguard Worker         let dh = Ecdh::new(cipher_suite).unwrap();
371*e1997b9aSAndroid Build Coastguard Worker         let kdf = Kdf::new(cipher_suite).unwrap();
372*e1997b9aSAndroid Build Coastguard Worker         let kem = DhKem::new(cipher_suite, dh, kdf).unwrap();
373*e1997b9aSAndroid Build Coastguard Worker 
374*e1997b9aSAndroid Build Coastguard Worker         assert!(kem.generate().is_ok());
375*e1997b9aSAndroid Build Coastguard Worker     }
376*e1997b9aSAndroid Build Coastguard Worker 
377*e1997b9aSAndroid Build Coastguard Worker     #[test]
dh_kem_derive()378*e1997b9aSAndroid Build Coastguard Worker     fn dh_kem_derive() {
379*e1997b9aSAndroid Build Coastguard Worker         let cipher_suite = CipherSuite::CURVE25519_CHACHA;
380*e1997b9aSAndroid Build Coastguard Worker         let dh = Ecdh::new(cipher_suite).unwrap();
381*e1997b9aSAndroid Build Coastguard Worker         let kdf = Kdf::new(cipher_suite).unwrap();
382*e1997b9aSAndroid Build Coastguard Worker         let kem = DhKem::new(cipher_suite, dh, kdf).unwrap();
383*e1997b9aSAndroid Build Coastguard Worker 
384*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.2.1
385*e1997b9aSAndroid Build Coastguard Worker         let ikm: [u8; 32] =
386*e1997b9aSAndroid Build Coastguard Worker             decode_hex("909a9b35d3dc4713a5e72a4da274b55d3d3821a37e5d099e74a647db583a904b"); // ikmE
387*e1997b9aSAndroid Build Coastguard Worker         let expected_sk = HpkeSecretKey::from(
388*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("f4ec9b33b792c372c1d2c2063507b684ef925b8c75a42dbcbf57d63ccd381600")
389*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
390*e1997b9aSAndroid Build Coastguard Worker         ); // skEm
391*e1997b9aSAndroid Build Coastguard Worker         let expected_pk = HpkePublicKey::from(
392*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a")
393*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
394*e1997b9aSAndroid Build Coastguard Worker         ); // pkEm
395*e1997b9aSAndroid Build Coastguard Worker 
396*e1997b9aSAndroid Build Coastguard Worker         let (sk, pk) = kem.derive(&ikm).unwrap();
397*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(sk, expected_sk);
398*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(pk, expected_pk);
399*e1997b9aSAndroid Build Coastguard Worker     }
400*e1997b9aSAndroid Build Coastguard Worker 
401*e1997b9aSAndroid Build Coastguard Worker     #[test]
dh_kem_public_key_validate()402*e1997b9aSAndroid Build Coastguard Worker     fn dh_kem_public_key_validate() {
403*e1997b9aSAndroid Build Coastguard Worker         let cipher_suite = CipherSuite::CURVE25519_AES128;
404*e1997b9aSAndroid Build Coastguard Worker         let dh = Ecdh::new(cipher_suite).unwrap();
405*e1997b9aSAndroid Build Coastguard Worker         let kdf = Kdf::new(cipher_suite).unwrap();
406*e1997b9aSAndroid Build Coastguard Worker         let kem = DhKem::new(cipher_suite, dh, kdf).unwrap();
407*e1997b9aSAndroid Build Coastguard Worker 
408*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1
409*e1997b9aSAndroid Build Coastguard Worker         let public_key = HpkePublicKey::from(
410*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")
411*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
412*e1997b9aSAndroid Build Coastguard Worker         );
413*e1997b9aSAndroid Build Coastguard Worker         assert!(kem.public_key_validate(&public_key).is_ok());
414*e1997b9aSAndroid Build Coastguard Worker     }
415*e1997b9aSAndroid Build Coastguard Worker 
416*e1997b9aSAndroid Build Coastguard Worker     #[test]
hpke_seal_open()417*e1997b9aSAndroid Build Coastguard Worker     fn hpke_seal_open() {
418*e1997b9aSAndroid Build Coastguard Worker         let hpke = Hpke::new(CipherSuite::CURVE25519_AES128);
419*e1997b9aSAndroid Build Coastguard Worker 
420*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
421*e1997b9aSAndroid Build Coastguard Worker         let receiver_pub_key = HpkePublicKey::from(
422*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d")
423*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
424*e1997b9aSAndroid Build Coastguard Worker         );
425*e1997b9aSAndroid Build Coastguard Worker         let receiver_priv_key = HpkeSecretKey::from(
426*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8")
427*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
428*e1997b9aSAndroid Build Coastguard Worker         );
429*e1997b9aSAndroid Build Coastguard Worker 
430*e1997b9aSAndroid Build Coastguard Worker         let info = b"some_info";
431*e1997b9aSAndroid Build Coastguard Worker         let plaintext = b"plaintext";
432*e1997b9aSAndroid Build Coastguard Worker         let associated_data = b"some_ad";
433*e1997b9aSAndroid Build Coastguard Worker 
434*e1997b9aSAndroid Build Coastguard Worker         let ct = hpke.seal(&receiver_pub_key, info, Some(associated_data), plaintext).unwrap();
435*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
436*e1997b9aSAndroid Build Coastguard Worker             plaintext.as_ref(),
437*e1997b9aSAndroid Build Coastguard Worker             hpke.open(&ct, &receiver_priv_key, info, Some(associated_data)).unwrap(),
438*e1997b9aSAndroid Build Coastguard Worker         );
439*e1997b9aSAndroid Build Coastguard Worker     }
440*e1997b9aSAndroid Build Coastguard Worker 
441*e1997b9aSAndroid Build Coastguard Worker     #[test]
hpke_context_seal_open()442*e1997b9aSAndroid Build Coastguard Worker     fn hpke_context_seal_open() {
443*e1997b9aSAndroid Build Coastguard Worker         let hpke = Hpke::new(CipherSuite::CURVE25519_AES128);
444*e1997b9aSAndroid Build Coastguard Worker 
445*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
446*e1997b9aSAndroid Build Coastguard Worker         let receiver_pub_key = HpkePublicKey::from(
447*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d")
448*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
449*e1997b9aSAndroid Build Coastguard Worker         );
450*e1997b9aSAndroid Build Coastguard Worker         let receiver_priv_key = HpkeSecretKey::from(
451*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8")
452*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
453*e1997b9aSAndroid Build Coastguard Worker         );
454*e1997b9aSAndroid Build Coastguard Worker 
455*e1997b9aSAndroid Build Coastguard Worker         let info = b"some_info";
456*e1997b9aSAndroid Build Coastguard Worker         let plaintext = b"plaintext";
457*e1997b9aSAndroid Build Coastguard Worker         let associated_data = b"some_ad";
458*e1997b9aSAndroid Build Coastguard Worker 
459*e1997b9aSAndroid Build Coastguard Worker         let (enc, mut sender_ctx) = hpke.setup_sender(&receiver_pub_key, info).unwrap();
460*e1997b9aSAndroid Build Coastguard Worker         let mut receiver_ctx = hpke.setup_receiver(&enc, &receiver_priv_key, info).unwrap();
461*e1997b9aSAndroid Build Coastguard Worker         let ct = sender_ctx.seal(Some(associated_data), plaintext).unwrap();
462*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(plaintext.as_ref(), receiver_ctx.open(Some(associated_data), &ct).unwrap(),);
463*e1997b9aSAndroid Build Coastguard Worker     }
464*e1997b9aSAndroid Build Coastguard Worker 
465*e1997b9aSAndroid Build Coastguard Worker     #[test]
hpke_context_seal_open_multithreaded()466*e1997b9aSAndroid Build Coastguard Worker     fn hpke_context_seal_open_multithreaded() {
467*e1997b9aSAndroid Build Coastguard Worker         let hpke = Hpke::new(CipherSuite::CURVE25519_AES128);
468*e1997b9aSAndroid Build Coastguard Worker 
469*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
470*e1997b9aSAndroid Build Coastguard Worker         let receiver_pub_key = HpkePublicKey::from(
471*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d")
472*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
473*e1997b9aSAndroid Build Coastguard Worker         );
474*e1997b9aSAndroid Build Coastguard Worker         let receiver_priv_key = HpkeSecretKey::from(
475*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8")
476*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
477*e1997b9aSAndroid Build Coastguard Worker         );
478*e1997b9aSAndroid Build Coastguard Worker 
479*e1997b9aSAndroid Build Coastguard Worker         let info = b"some_info";
480*e1997b9aSAndroid Build Coastguard Worker         let plaintext = b"plaintext";
481*e1997b9aSAndroid Build Coastguard Worker         let associated_data = b"some_ad";
482*e1997b9aSAndroid Build Coastguard Worker 
483*e1997b9aSAndroid Build Coastguard Worker         let (enc, mut sender_ctx) = hpke.setup_sender(&receiver_pub_key, info).unwrap();
484*e1997b9aSAndroid Build Coastguard Worker         let mut receiver_ctx = hpke.setup_receiver(&enc, &receiver_priv_key, info).unwrap();
485*e1997b9aSAndroid Build Coastguard Worker 
486*e1997b9aSAndroid Build Coastguard Worker         let pool = thread::spawn(move || {
487*e1997b9aSAndroid Build Coastguard Worker             for _ in 1..100 {
488*e1997b9aSAndroid Build Coastguard Worker                 let ct = sender_ctx.seal(Some(associated_data), plaintext).unwrap();
489*e1997b9aSAndroid Build Coastguard Worker                 assert_eq!(
490*e1997b9aSAndroid Build Coastguard Worker                     plaintext.as_ref(),
491*e1997b9aSAndroid Build Coastguard Worker                     receiver_ctx.open(Some(associated_data), &ct).unwrap(),
492*e1997b9aSAndroid Build Coastguard Worker                 );
493*e1997b9aSAndroid Build Coastguard Worker             }
494*e1997b9aSAndroid Build Coastguard Worker         });
495*e1997b9aSAndroid Build Coastguard Worker         pool.join().unwrap();
496*e1997b9aSAndroid Build Coastguard Worker     }
497*e1997b9aSAndroid Build Coastguard Worker 
498*e1997b9aSAndroid Build Coastguard Worker     #[test]
hpke_context_export()499*e1997b9aSAndroid Build Coastguard Worker     fn hpke_context_export() {
500*e1997b9aSAndroid Build Coastguard Worker         let hpke = Hpke::new(CipherSuite::CURVE25519_AES128);
501*e1997b9aSAndroid Build Coastguard Worker 
502*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
503*e1997b9aSAndroid Build Coastguard Worker         let receiver_pub_key = HpkePublicKey::from(
504*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d")
505*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
506*e1997b9aSAndroid Build Coastguard Worker         );
507*e1997b9aSAndroid Build Coastguard Worker         let receiver_priv_key = HpkeSecretKey::from(
508*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8")
509*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
510*e1997b9aSAndroid Build Coastguard Worker         );
511*e1997b9aSAndroid Build Coastguard Worker 
512*e1997b9aSAndroid Build Coastguard Worker         let info = b"some_info";
513*e1997b9aSAndroid Build Coastguard Worker         let exporter_ctx = b"export_ctx";
514*e1997b9aSAndroid Build Coastguard Worker 
515*e1997b9aSAndroid Build Coastguard Worker         let (enc, sender_ctx) = hpke.setup_sender(&receiver_pub_key, info).unwrap();
516*e1997b9aSAndroid Build Coastguard Worker         let receiver_ctx = hpke.setup_receiver(&enc, &receiver_priv_key, info).unwrap();
517*e1997b9aSAndroid Build Coastguard Worker         assert_eq!(
518*e1997b9aSAndroid Build Coastguard Worker             sender_ctx.export(exporter_ctx, 32).unwrap(),
519*e1997b9aSAndroid Build Coastguard Worker             receiver_ctx.export(exporter_ctx, 32).unwrap(),
520*e1997b9aSAndroid Build Coastguard Worker         );
521*e1997b9aSAndroid Build Coastguard Worker     }
522*e1997b9aSAndroid Build Coastguard Worker 
523*e1997b9aSAndroid Build Coastguard Worker     #[test]
hpke_unsupported_cipher_suites()524*e1997b9aSAndroid Build Coastguard Worker     fn hpke_unsupported_cipher_suites() {
525*e1997b9aSAndroid Build Coastguard Worker         // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1.1
526*e1997b9aSAndroid Build Coastguard Worker         let receiver_pub_key = HpkePublicKey::from(
527*e1997b9aSAndroid Build Coastguard Worker             decode_hex::<32>("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d")
528*e1997b9aSAndroid Build Coastguard Worker                 .to_vec(),
529*e1997b9aSAndroid Build Coastguard Worker         );
530*e1997b9aSAndroid Build Coastguard Worker 
531*e1997b9aSAndroid Build Coastguard Worker         for suite in vec![
532*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::P256_AES128,
533*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::P384_AES256,
534*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::P521_AES256,
535*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::CURVE448_CHACHA,
536*e1997b9aSAndroid Build Coastguard Worker             CipherSuite::CURVE448_AES256,
537*e1997b9aSAndroid Build Coastguard Worker         ] {
538*e1997b9aSAndroid Build Coastguard Worker             assert!(Hpke::new(suite).setup_sender(&receiver_pub_key, b"some_info").is_err());
539*e1997b9aSAndroid Build Coastguard Worker         }
540*e1997b9aSAndroid Build Coastguard Worker     }
541*e1997b9aSAndroid Build Coastguard Worker }
542