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