Skip to main content

bssl_crypto/
hpke.rs

1/* Copyright 2024 The BoringSSL Authors
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
76use crate::{scoped, with_output_vec, with_output_vec_fallible, FfiSlice};
77use alloc::vec::Vec;
78
79/// Supported KEM algorithms with values detailed in RFC 9180.
80#[derive(Clone, Copy)]
81pub enum Kem {
82    #[allow(missing_docs)]
83    X25519HkdfSha256 = 32,
84}
85
86impl Kem {
87    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.
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)]
149pub 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)]
157pub enum Aead {
158    Aes128Gcm = 1,
159    Aes256Gcm = 2,
160    Chacha20Poly1305 = 3,
161}
162
163impl 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
176    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.
189const MAX_ENCAPSULATED_KEY_LEN: usize = bssl_sys::EVP_HPKE_MAX_ENC_LENGTH as usize;
190
191/// HPKE parameters, including KEM, KDF, and AEAD.
192pub 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
198impl Params {
199    /// New `Params` from KEM, KDF, and AEAD enums.
200    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.
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.
227pub struct SenderContext(scoped::EvpHpkeCtx);
228
229impl 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.
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.
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.
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.
328pub struct RecipientContext(scoped::EvpHpkeCtx);
329
330impl 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.
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.
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.
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)]
441mod 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
462    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
481    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]
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
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]
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]
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]
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]
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]
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]
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}