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(¶ms, 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(¶ms, 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