• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params, &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 //!     &params,
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 //!     &params,
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(&params, &pub_key, info).unwrap();
517                     let mut recv_ctx =
518                         RecipientContext::new(&params, &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                 &params,
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                 &params,
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                 &params,
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                 &params,
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(&params, 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(&params, b"", &vec.encapsulated_key, &vec.info).is_none());
676     }
677 }
678