1 /* Copyright (c) 2024, Google Inc. 2 * 3 * Permission to use, copy, modify, and/or distribute this software for any 4 * purpose with or without fee is hereby granted, provided that the above 5 * copyright notice and this permission notice appear in all copies. 6 * 7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 */ 15 16 //! Hybrid Public Key Encryption 17 //! 18 //! HPKE provides public key encryption of arbitrary-length messages. It 19 //! establishes contexts that produce/consume an ordered sequence of 20 //! ciphertexts that are both encrypted and authenticated. 21 //! 22 //! See RFC 9180 for more details. 23 //! 24 //! ``` 25 //! use bssl_crypto::hpke; 26 //! 27 //! let kem = hpke::Kem::X25519HkdfSha256; 28 //! let (pub_key, priv_key) = kem.generate_keypair(); 29 //! // Distribute `pub_key` to people who want to send you messages. 30 //! 31 //! // On the sending side... 32 //! let params = hpke::Params::new(kem, hpke::Kdf::HkdfSha256, hpke::Aead::Aes128Gcm); 33 //! let info : &[u8] = b"mutual context"; 34 //! let (mut sender_ctx, encapsulated_key) = 35 //! hpke::SenderContext::new(¶ms, &pub_key, info).unwrap(); 36 //! // Transmit the `encapsulated_key` to the receiver, followed by one or 37 //! // more ciphertexts... 38 //! let aad = b"associated_data"; 39 //! let plaintext1 : &[u8] = b"plaintext1"; 40 //! let msg1 = sender_ctx.seal(plaintext1, aad); 41 //! let plaintext2 : &[u8] = b"plaintext2"; 42 //! let msg2 = sender_ctx.seal(plaintext2, aad); 43 //! 44 //! // On the receiving side... 45 //! let mut recipient_ctx = hpke::RecipientContext::new( 46 //! ¶ms, 47 //! &priv_key, 48 //! &encapsulated_key, 49 //! info, 50 //! ).unwrap(); 51 //! 52 //! let received_plaintext1 = recipient_ctx.open(&msg1, aad).unwrap(); 53 //! assert_eq!(plaintext1, &received_plaintext1); 54 //! let received_plaintext2 = recipient_ctx.open(&msg2, aad).unwrap(); 55 //! assert_eq!(plaintext2, &received_plaintext2); 56 //! 57 //! // Messages must be processed in order, so trying to `open` the second 58 //! // message first will fail. 59 //! let mut recipient_ctx = hpke::RecipientContext::new( 60 //! ¶ms, 61 //! &priv_key, 62 //! &encapsulated_key, 63 //! info, 64 //! ).unwrap(); 65 //! 66 //! let received_plaintext2 = recipient_ctx.open(&msg2, aad); 67 //! assert!(received_plaintext2.is_none()); 68 //! 69 //! // There is also an interface for exporting secrets from both sender 70 //! // and recipient contexts. 71 //! let sender_export = sender_ctx.export(b"ctx", 32); 72 //! let recipient_export = recipient_ctx.export(b"ctx", 32); 73 //! assert_eq!(sender_export, recipient_export); 74 //! ``` 75 76 use crate::{scoped, with_output_vec, with_output_vec_fallible, FfiSlice}; 77 use alloc::vec::Vec; 78 79 /// Supported KEM algorithms with values detailed in RFC 9180. 80 #[derive(Clone, Copy)] 81 pub enum Kem { 82 #[allow(missing_docs)] 83 X25519HkdfSha256 = 32, 84 } 85 86 impl Kem { as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_KEM87 fn as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_KEM { 88 // Safety: this function returns a pointer to static data. 89 unsafe { 90 match self { 91 Kem::X25519HkdfSha256 => bssl_sys::EVP_hpke_x25519_hkdf_sha256(), 92 } 93 } 94 } 95 96 /// Generate a public and private key for this KEM. generate_keypair(&self) -> (Vec<u8>, Vec<u8>)97 pub fn generate_keypair(&self) -> (Vec<u8>, Vec<u8>) { 98 let mut key = scoped::EvpHpkeKey::new(); 99 // Safety: `key` and `self` must be valid and this function doesn't 100 // take ownership of either. 101 let ret = 102 unsafe { bssl_sys::EVP_HPKE_KEY_generate(key.as_mut_ffi_ptr(), self.as_ffi_ptr()) }; 103 // Key generation currently never fails, and out-of-memory is not 104 // handled by this crate. 105 assert_eq!(ret, 1); 106 107 fn get_value_from_key( 108 key: &scoped::EvpHpkeKey, 109 accessor: unsafe extern "C" fn( 110 *const bssl_sys::EVP_HPKE_KEY, 111 // Output buffer. 112 *mut u8, 113 // Number of bytes written. 114 *mut usize, 115 // Maximum output size. 116 usize, 117 ) -> core::ffi::c_int, 118 max_len: usize, 119 ) -> Vec<u8> { 120 unsafe { 121 with_output_vec(max_len, |out| { 122 let mut out_len = 0usize; 123 let ret = accessor(key.as_ffi_ptr(), out, &mut out_len, max_len); 124 // If `max_len` is correct then these functions never fail. 125 assert_eq!(ret, 1); 126 assert!(out_len <= max_len); 127 // Safety: `out_len` bytes have been written, as required. 128 out_len 129 }) 130 } 131 } 132 133 let pub_key = get_value_from_key( 134 &key, 135 bssl_sys::EVP_HPKE_KEY_public_key, 136 bssl_sys::EVP_HPKE_MAX_PUBLIC_KEY_LENGTH as usize, 137 ); 138 let priv_key = get_value_from_key( 139 &key, 140 bssl_sys::EVP_HPKE_KEY_private_key, 141 bssl_sys::EVP_HPKE_MAX_PRIVATE_KEY_LENGTH as usize, 142 ); 143 (pub_key, priv_key) 144 } 145 } 146 147 /// Supported KDF algorithms with values detailed in RFC 9180. 148 #[derive(Clone, Copy)] 149 pub enum Kdf { 150 #[allow(missing_docs)] 151 HkdfSha256 = 1, 152 } 153 154 /// Supported AEAD algorithms with values detailed in RFC 9180. 155 #[derive(Clone, Copy)] 156 #[allow(missing_docs)] 157 pub enum Aead { 158 Aes128Gcm = 1, 159 Aes256Gcm = 2, 160 Chacha20Poly1305 = 3, 161 } 162 163 impl Aead { from_rfc_id(n: u16) -> Option<Aead>164 fn from_rfc_id(n: u16) -> Option<Aead> { 165 let ret = match n { 166 1 => Aead::Aes128Gcm, 167 2 => Aead::Aes256Gcm, 168 3 => Aead::Chacha20Poly1305, 169 _ => return None, 170 }; 171 // The mapping above must agree with the values in the enum. 172 assert_eq!(n, ret as u16); 173 Some(ret) 174 } 175 as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_AEAD176 fn as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_AEAD { 177 // Safety: these functions all return pointers to static data. 178 unsafe { 179 match self { 180 Aead::Aes128Gcm => bssl_sys::EVP_hpke_aes_128_gcm(), 181 Aead::Aes256Gcm => bssl_sys::EVP_hpke_aes_256_gcm(), 182 Aead::Chacha20Poly1305 => bssl_sys::EVP_hpke_chacha20_poly1305(), 183 } 184 } 185 } 186 } 187 188 /// Maximum length of the encapsulated key for all currently supported KEMs. 189 const MAX_ENCAPSULATED_KEY_LEN: usize = bssl_sys::EVP_HPKE_MAX_ENC_LENGTH as usize; 190 191 /// HPKE parameters, including KEM, KDF, and AEAD. 192 pub struct Params { 193 kem: *const bssl_sys::EVP_HPKE_KEM, 194 kdf: *const bssl_sys::EVP_HPKE_KDF, 195 aead: *const bssl_sys::EVP_HPKE_AEAD, 196 } 197 198 impl Params { 199 /// New `Params` from KEM, KDF, and AEAD enums. new(_kem: Kem, _kdf: Kdf, aead: Aead) -> Self200 pub fn new(_kem: Kem, _kdf: Kdf, aead: Aead) -> Self { 201 // Safety: EVP_hpke_x25519_hkdf_sha256 and EVP_hpke_hkdf_sha256 just 202 // return pointers to static data. 203 unsafe { 204 Self { 205 // Only one KEM and KDF are supported thus far. 206 kem: bssl_sys::EVP_hpke_x25519_hkdf_sha256(), 207 kdf: bssl_sys::EVP_hpke_hkdf_sha256(), 208 aead: aead.as_ffi_ptr(), 209 } 210 } 211 } 212 213 /// New `Params` from KEM, KDF, and AEAD IDs as detailed in RFC 9180. new_from_rfc_ids(kem_id: u16, kdf_id: u16, aead_id: u16) -> Option<Self>214 pub fn new_from_rfc_ids(kem_id: u16, kdf_id: u16, aead_id: u16) -> Option<Self> { 215 let kem = Kem::X25519HkdfSha256; 216 let kdf = Kdf::HkdfSha256; 217 let aead = Aead::from_rfc_id(aead_id)?; 218 219 if kem_id != kem as u16 || kdf_id != kdf as u16 { 220 return None; 221 } 222 Some(Self::new(kem, kdf, aead)) 223 } 224 } 225 226 /// HPKE sender context. Callers may use `seal()` to encrypt messages for the recipient. 227 pub struct SenderContext(scoped::EvpHpkeCtx); 228 229 impl SenderContext { 230 /// Performs the SetupBaseS HPKE operation and returns a sender context 231 /// plus an encapsulated shared secret for `recipient_pub_key`. 232 /// 233 /// Returns `None` if `recipient_pub_key` is invalid. 234 /// 235 /// On success, callers may use `seal()` to encrypt messages for the recipient. new(params: &Params, recipient_pub_key: &[u8], info: &[u8]) -> Option<(Self, Vec<u8>)>236 pub fn new(params: &Params, recipient_pub_key: &[u8], info: &[u8]) -> Option<(Self, Vec<u8>)> { 237 let mut ctx = scoped::EvpHpkeCtx::new(); 238 unsafe { 239 with_output_vec_fallible(MAX_ENCAPSULATED_KEY_LEN, |enc_key_buf| { 240 let mut enc_key_len = 0usize; 241 // Safety: EVP_HPKE_CTX_setup_sender 242 // - is called with context created from EVP_HPKE_CTX_new, 243 // - is called with valid buffers with corresponding pointer and length, and 244 // - returns 0 on error. 245 let ret = bssl_sys::EVP_HPKE_CTX_setup_sender( 246 ctx.as_mut_ffi_ptr(), 247 enc_key_buf, 248 &mut enc_key_len, 249 MAX_ENCAPSULATED_KEY_LEN, 250 params.kem, 251 params.kdf, 252 params.aead, 253 recipient_pub_key.as_ffi_ptr(), 254 recipient_pub_key.len(), 255 info.as_ffi_ptr(), 256 info.len(), 257 ); 258 if ret == 1 { 259 Some(enc_key_len) 260 } else { 261 None 262 } 263 }) 264 } 265 .map(|enc_key| (Self(ctx), enc_key)) 266 } 267 268 /// Seal encrypts `plaintext`, and authenticates `aad`, returning the resulting ciphertext. 269 /// 270 /// Note that HPKE encryption is stateful and ordered. The sender's first call to `seal()` must 271 /// correspond to the recipient's first call to `open()`, etc. 272 /// 273 /// This function panics if adding the `plaintext` length and 274 /// `bssl_sys::EVP_HPKE_CTX_max_overhead` overflows. seal(&mut self, plaintext: &[u8], aad: &[u8]) -> Vec<u8>275 pub fn seal(&mut self, plaintext: &[u8], aad: &[u8]) -> Vec<u8> { 276 // Safety: EVP_HPKE_CTX_max_overhead panics if ctx is not set up as a sender. 277 #[allow(clippy::expect_used)] 278 let max_out_len = plaintext 279 .len() 280 .checked_add(unsafe { bssl_sys::EVP_HPKE_CTX_max_overhead(self.0.as_ffi_ptr()) }) 281 .expect("Maximum output length calculation overflow"); 282 unsafe { 283 with_output_vec(max_out_len, |out_buf| { 284 let mut out_len = 0usize; 285 // Safety: EVP_HPKE_CTX_seal 286 // - is called with context created from EVP_HPKE_CTX_new and 287 // - is called with valid buffers with corresponding pointer and length. 288 let result = bssl_sys::EVP_HPKE_CTX_seal( 289 self.0.as_mut_ffi_ptr(), 290 out_buf, 291 &mut out_len, 292 max_out_len, 293 plaintext.as_ffi_ptr(), 294 plaintext.len(), 295 aad.as_ffi_ptr(), 296 aad.len(), 297 ); 298 assert_eq!(result, 1); 299 out_len 300 }) 301 } 302 } 303 304 /// Exports a secret of length `out_len` from the HPKE context using `context` as the context 305 /// string. export(&mut self, context: &[u8], out_len: usize) -> Vec<u8>306 pub fn export(&mut self, context: &[u8], out_len: usize) -> Vec<u8> { 307 unsafe { 308 with_output_vec(out_len, |out_buf| { 309 // Safety: EVP_HPKE_CTX_export 310 // - is called with context created from EVP_HPKE_CTX_new, 311 // - is called with valid buffers with corresponding pointer and length, and 312 // - returns 0 on error, which only occurs when OOM. 313 let ret = bssl_sys::EVP_HPKE_CTX_export( 314 self.0.as_mut_ffi_ptr(), 315 out_buf, 316 out_len, 317 context.as_ffi_ptr(), 318 context.len(), 319 ); 320 assert_eq!(ret, 1); 321 out_len 322 }) 323 } 324 } 325 } 326 327 /// HPKE recipient context. Callers may use `open()` to decrypt messages from the sender. 328 pub struct RecipientContext(scoped::EvpHpkeCtx); 329 330 impl RecipientContext { 331 /// New implements the SetupBaseR HPKE operation, which decapsulates the shared secret in 332 /// `encapsulated_key` with `recipient_priv_key` and sets up a recipient context. These are 333 /// stored and returned in the newly created RecipientContext. 334 /// 335 /// Note that `encapsulated_key` may be invalid, in which case this function will return an 336 /// error. 337 /// 338 /// On success, callers may use `open()` to decrypt messages from the sender. new( params: &Params, recipient_priv_key: &[u8], encapsulated_key: &[u8], info: &[u8], ) -> Option<Self>339 pub fn new( 340 params: &Params, 341 recipient_priv_key: &[u8], 342 encapsulated_key: &[u8], 343 info: &[u8], 344 ) -> Option<Self> { 345 let mut hpke_key = scoped::EvpHpkeKey::new(); 346 347 // Safety: EVP_HPKE_KEY_init returns 0 on error. 348 let result = unsafe { 349 bssl_sys::EVP_HPKE_KEY_init( 350 hpke_key.as_mut_ffi_ptr(), 351 params.kem, 352 recipient_priv_key.as_ffi_ptr(), 353 recipient_priv_key.len(), 354 ) 355 }; 356 if result != 1 { 357 return None; 358 } 359 360 let mut ctx = scoped::EvpHpkeCtx::new(); 361 362 // Safety: EVP_HPKE_CTX_setup_recipient 363 // - is called with context created from EVP_HPKE_CTX_new, 364 // - is called with HPKE key created from EVP_HPKE_KEY_init, 365 // - is called with valid buffers with corresponding pointer and length, and 366 // - returns 0 on error. 367 let result = unsafe { 368 bssl_sys::EVP_HPKE_CTX_setup_recipient( 369 ctx.as_mut_ffi_ptr(), 370 hpke_key.as_ffi_ptr(), 371 params.kdf, 372 params.aead, 373 encapsulated_key.as_ffi_ptr(), 374 encapsulated_key.len(), 375 info.as_ffi_ptr(), 376 info.len(), 377 ) 378 }; 379 if result == 1 { 380 Some(Self(ctx)) 381 } else { 382 None 383 } 384 } 385 386 /// Open authenticates `aad` and decrypts `ciphertext`. It returns an error on failure. 387 /// 388 /// Note that HPKE encryption is stateful and ordered. The sender's first call to `seal()` must 389 /// correspond to the recipient's first call to `open()`, etc. open(&mut self, ciphertext: &[u8], aad: &[u8]) -> Option<Vec<u8>>390 pub fn open(&mut self, ciphertext: &[u8], aad: &[u8]) -> Option<Vec<u8>> { 391 let max_out_len = ciphertext.len(); 392 unsafe { 393 with_output_vec_fallible(max_out_len, |out_buf| { 394 let mut out_len = 0usize; 395 // Safety: EVP_HPKE_CTX_open 396 // - is called with context created from EVP_HPKE_CTX_new and 397 // - is called with valid buffers with corresponding pointer and length. 398 let result = bssl_sys::EVP_HPKE_CTX_open( 399 self.0.as_mut_ffi_ptr(), 400 out_buf, 401 &mut out_len, 402 max_out_len, 403 ciphertext.as_ffi_ptr(), 404 ciphertext.len(), 405 aad.as_ffi_ptr(), 406 aad.len(), 407 ); 408 if result == 1 { 409 Some(out_len) 410 } else { 411 None 412 } 413 }) 414 } 415 } 416 417 /// Exports a secret of length `out_len` from the HPKE context using `context` as the context 418 /// string. export(&mut self, context: &[u8], out_len: usize) -> Vec<u8>419 pub fn export(&mut self, context: &[u8], out_len: usize) -> Vec<u8> { 420 unsafe { 421 with_output_vec(out_len, |out_buf| { 422 // Safety: EVP_HPKE_CTX_export 423 // - is called with context created from EVP_HPKE_CTX_new, 424 // - is called with valid buffers with corresponding pointer and length, and 425 // - returns 0 on error, which only occurs when OOM. 426 let ret = bssl_sys::EVP_HPKE_CTX_export( 427 self.0.as_mut_ffi_ptr(), 428 out_buf, 429 out_len, 430 context.as_ffi_ptr(), 431 context.len(), 432 ); 433 assert_eq!(ret, 1); 434 out_len 435 }) 436 } 437 } 438 } 439 440 #[cfg(test)] 441 mod test { 442 use super::*; 443 use crate::test_helpers::decode_hex; 444 445 struct TestVector { 446 kem_id: u16, 447 kdf_id: u16, 448 aead_id: u16, 449 info: [u8; 20], 450 seed_for_testing: [u8; 32], // skEm 451 recipient_pub_key: [u8; 32], // pkRm 452 recipient_priv_key: [u8; 32], // skRm 453 encapsulated_key: [u8; 32], // enc 454 plaintext: [u8; 29], // pt 455 associated_data: [u8; 7], // aad 456 ciphertext: [u8; 45], // ct 457 exporter_context: [u8; 11], 458 exported_value: [u8; 32], 459 } 460 461 // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1 x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm() -> TestVector462 fn x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm() -> TestVector { 463 TestVector { 464 kem_id: 32, 465 kdf_id: 1, 466 aead_id: 1, 467 info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), 468 seed_for_testing: decode_hex("52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"), 469 recipient_pub_key: decode_hex("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"), 470 recipient_priv_key: decode_hex("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8"), 471 encapsulated_key: decode_hex("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"), 472 plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), 473 associated_data: decode_hex("436f756e742d30"), 474 ciphertext: decode_hex("f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a"), 475 exporter_context: decode_hex("54657374436f6e74657874"), 476 exported_value: decode_hex("e9e43065102c3836401bed8c3c3c75ae46be1639869391d62c61f1ec7af54931"), 477 } 478 } 479 480 // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.2 x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305() -> TestVector481 fn x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305() -> TestVector { 482 TestVector { 483 kem_id: 32, 484 kdf_id: 1, 485 aead_id: 3, 486 info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), 487 seed_for_testing: decode_hex("f4ec9b33b792c372c1d2c2063507b684ef925b8c75a42dbcbf57d63ccd381600"), 488 recipient_pub_key: decode_hex("4310ee97d88cc1f088a5576c77ab0cf5c3ac797f3d95139c6c84b5429c59662a"), 489 recipient_priv_key: decode_hex("8057991eef8f1f1af18f4a9491d16a1ce333f695d4db8e38da75975c4478e0fb"), 490 encapsulated_key: decode_hex("1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a"), 491 plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), 492 associated_data: decode_hex("436f756e742d30"), 493 ciphertext: decode_hex("1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28"), 494 exporter_context: decode_hex("54657374436f6e74657874"), 495 exported_value: decode_hex("5acb09211139c43b3090489a9da433e8a30ee7188ba8b0a9a1ccf0c229283e53"), 496 } 497 } 498 499 #[test] all_algorithms()500 fn all_algorithms() { 501 let kems = vec![Kem::X25519HkdfSha256]; 502 let kdfs = vec![Kdf::HkdfSha256]; 503 let aeads = vec![Aead::Aes128Gcm, Aead::Aes256Gcm, Aead::Chacha20Poly1305]; 504 let plaintext: &[u8] = b"plaintext"; 505 let aad: &[u8] = b"aad"; 506 let info: &[u8] = b"info"; 507 508 for kem in &kems { 509 let (pub_key, priv_key) = kem.generate_keypair(); 510 for kdf in &kdfs { 511 for aead in &aeads { 512 let params = 513 Params::new_from_rfc_ids(*kem as u16, *kdf as u16, *aead as u16).unwrap(); 514 515 let (mut send_ctx, encapsulated_key) = 516 SenderContext::new(¶ms, &pub_key, info).unwrap(); 517 let mut recv_ctx = 518 RecipientContext::new(¶ms, &priv_key, &encapsulated_key, info).unwrap(); 519 assert_eq!( 520 plaintext, 521 recv_ctx 522 .open(send_ctx.seal(plaintext, aad).as_ref(), aad) 523 .unwrap() 524 ); 525 assert_eq!( 526 plaintext, 527 recv_ctx 528 .open(send_ctx.seal(plaintext, aad).as_ref(), aad) 529 .unwrap() 530 ); 531 assert!(recv_ctx.open(b"nonsense", aad).is_none()); 532 } 533 } 534 } 535 } 536 new_sender_context_for_testing( params: &Params, recipient_pub_key: &[u8], info: &[u8], seed_for_testing: &[u8], ) -> (SenderContext, Vec<u8>)537 fn new_sender_context_for_testing( 538 params: &Params, 539 recipient_pub_key: &[u8], 540 info: &[u8], 541 seed_for_testing: &[u8], 542 ) -> (SenderContext, Vec<u8>) { 543 let mut ctx = scoped::EvpHpkeCtx::new(); 544 545 let encapsulated_key = unsafe { 546 with_output_vec_fallible(MAX_ENCAPSULATED_KEY_LEN, |enc_key_buf| { 547 let mut enc_key_len = 0usize; 548 // Safety: EVP_HPKE_CTX_setup_sender_with_seed_for_testing 549 // - is called with context created from EVP_HPKE_CTX_new, 550 // - is called with valid buffers with corresponding pointer and length, and 551 // - returns 0 on error. 552 let result = bssl_sys::EVP_HPKE_CTX_setup_sender_with_seed_for_testing( 553 ctx.as_mut_ffi_ptr(), 554 enc_key_buf, 555 &mut enc_key_len, 556 MAX_ENCAPSULATED_KEY_LEN, 557 params.kem, 558 params.kdf, 559 params.aead, 560 recipient_pub_key.as_ffi_ptr(), 561 recipient_pub_key.len(), 562 info.as_ffi_ptr(), 563 info.len(), 564 seed_for_testing.as_ffi_ptr(), 565 seed_for_testing.len(), 566 ); 567 if result == 1 { 568 Some(enc_key_len) 569 } else { 570 None 571 } 572 }) 573 } 574 .unwrap(); 575 (SenderContext(ctx), encapsulated_key) 576 } 577 578 #[test] seal_with_vector()579 fn seal_with_vector() { 580 for test in vec![ 581 x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), 582 x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), 583 ] { 584 let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); 585 586 let (mut ctx, encapsulated_key) = new_sender_context_for_testing( 587 ¶ms, 588 &test.recipient_pub_key, 589 &test.info, 590 &test.seed_for_testing, 591 ); 592 593 assert_eq!(encapsulated_key, test.encapsulated_key.to_vec()); 594 595 let ciphertext = ctx.seal(&test.plaintext, &test.associated_data); 596 assert_eq!(&ciphertext, test.ciphertext.as_ref()); 597 } 598 } 599 600 #[test] open_with_vector()601 fn open_with_vector() { 602 for test in vec![ 603 x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), 604 x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), 605 ] { 606 let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); 607 608 let mut ctx = RecipientContext::new( 609 ¶ms, 610 &test.recipient_priv_key, 611 &test.encapsulated_key, 612 &test.info, 613 ) 614 .unwrap(); 615 616 let plaintext = ctx.open(&test.ciphertext, &test.associated_data).unwrap(); 617 assert_eq!(&plaintext, test.plaintext.as_ref()); 618 } 619 } 620 621 #[test] export_with_vector()622 fn export_with_vector() { 623 for test in vec![ 624 x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), 625 x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), 626 ] { 627 let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); 628 629 let (mut sender_ctx, _encapsulated_key) = new_sender_context_for_testing( 630 ¶ms, 631 &test.recipient_pub_key, 632 &test.info, 633 &test.seed_for_testing, 634 ); 635 assert_eq!( 636 test.exported_value.as_ref(), 637 sender_ctx.export(&test.exporter_context, test.exported_value.len()) 638 ); 639 640 let mut recipient_ctx = RecipientContext::new( 641 ¶ms, 642 &test.recipient_priv_key, 643 &test.encapsulated_key, 644 &test.info, 645 ).unwrap(); 646 assert_eq!( 647 test.exported_value.as_ref(), 648 recipient_ctx.export(&test.exporter_context, test.exported_value.len()) 649 ); 650 } 651 } 652 653 #[test] disallowed_params_fail()654 fn disallowed_params_fail() { 655 let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); 656 657 assert!(Params::new_from_rfc_ids(0, vec.kdf_id, vec.aead_id).is_none()); 658 assert!(Params::new_from_rfc_ids(vec.kem_id, 0, vec.aead_id).is_none()); 659 assert!(Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, 0).is_none()); 660 } 661 662 #[test] bad_recipient_pub_key_fails()663 fn bad_recipient_pub_key_fails() { 664 let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); 665 let params = Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, vec.aead_id).unwrap(); 666 667 assert!(SenderContext::new(¶ms, b"", &vec.info).is_none()); 668 } 669 670 #[test] bad_recipient_priv_key_fails()671 fn bad_recipient_priv_key_fails() { 672 let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); 673 let params = Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, vec.aead_id).unwrap(); 674 675 assert!(RecipientContext::new(¶ms, b"", &vec.encapsulated_key, &vec.info).is_none()); 676 } 677 } 678