| /* Copyright (c) 2024, Google Inc. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| //! Hybrid Public Key Encryption |
| //! |
| //! HPKE provides public key encryption of arbitrary-length messages. It |
| //! establishes contexts that produce/consume an ordered sequence of |
| //! ciphertexts that are both encrypted and authenticated. |
| //! |
| //! See RFC 9180 for more details. |
| //! |
| //! ``` |
| //! use bssl_crypto::hpke; |
| //! |
| //! let kem = hpke::Kem::X25519HkdfSha256; |
| //! let (pub_key, priv_key) = kem.generate_keypair(); |
| //! // Distribute `pub_key` to people who want to send you messages. |
| //! |
| //! // On the sending side... |
| //! let params = hpke::Params::new(kem, hpke::Kdf::HkdfSha256, hpke::Aead::Aes128Gcm); |
| //! let info : &[u8] = b"mutual context"; |
| //! let (mut sender_ctx, encapsulated_key) = |
| //! hpke::SenderContext::new(¶ms, &pub_key, info).unwrap(); |
| //! // Transmit the `encapsulated_key` to the receiver, followed by one or |
| //! // more ciphertexts... |
| //! let aad = b"associated_data"; |
| //! let plaintext1 : &[u8] = b"plaintext1"; |
| //! let msg1 = sender_ctx.seal(plaintext1, aad); |
| //! let plaintext2 : &[u8] = b"plaintext2"; |
| //! let msg2 = sender_ctx.seal(plaintext2, aad); |
| //! |
| //! // On the receiving side... |
| //! let mut recipient_ctx = hpke::RecipientContext::new( |
| //! ¶ms, |
| //! &priv_key, |
| //! &encapsulated_key, |
| //! info, |
| //! ).unwrap(); |
| //! |
| //! let received_plaintext1 = recipient_ctx.open(&msg1, aad).unwrap(); |
| //! assert_eq!(plaintext1, &received_plaintext1); |
| //! let received_plaintext2 = recipient_ctx.open(&msg2, aad).unwrap(); |
| //! assert_eq!(plaintext2, &received_plaintext2); |
| //! |
| //! // Messages must be processed in order, so trying to `open` the second |
| //! // message first will fail. |
| //! let mut recipient_ctx = hpke::RecipientContext::new( |
| //! ¶ms, |
| //! &priv_key, |
| //! &encapsulated_key, |
| //! info, |
| //! ).unwrap(); |
| //! |
| //! let received_plaintext2 = recipient_ctx.open(&msg2, aad); |
| //! assert!(received_plaintext2.is_none()); |
| //! ``` |
| |
| use crate::{scoped, with_output_vec, with_output_vec_fallible, FfiSlice}; |
| use alloc::vec::Vec; |
| |
| /// Supported KEM algorithms with values detailed in RFC 9180. |
| #[derive(Clone, Copy)] |
| pub enum Kem { |
| #[allow(missing_docs)] |
| X25519HkdfSha256 = 32, |
| } |
| |
| impl Kem { |
| fn as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_KEM { |
| // Safety: this function returns a pointer to static data. |
| unsafe { |
| match self { |
| Kem::X25519HkdfSha256 => bssl_sys::EVP_hpke_x25519_hkdf_sha256(), |
| } |
| } |
| } |
| |
| /// Generate a public and private key for this KEM. |
| pub fn generate_keypair(&self) -> (Vec<u8>, Vec<u8>) { |
| let mut key = scoped::EvpHpkeKey::new(); |
| // Safety: `key` and `self` must be valid and this function doesn't |
| // take ownership of either. |
| let ret = |
| unsafe { bssl_sys::EVP_HPKE_KEY_generate(key.as_mut_ffi_ptr(), self.as_ffi_ptr()) }; |
| // Key generation currently never fails, and out-of-memory is not |
| // handled by this crate. |
| assert_eq!(ret, 1); |
| |
| fn get_value_from_key( |
| key: &scoped::EvpHpkeKey, |
| accessor: unsafe extern "C" fn( |
| *const bssl_sys::EVP_HPKE_KEY, |
| // Output buffer. |
| *mut u8, |
| // Number of bytes written. |
| *mut usize, |
| // Maximum output size. |
| usize, |
| ) -> core::ffi::c_int, |
| max_len: usize, |
| ) -> Vec<u8> { |
| unsafe { |
| with_output_vec(max_len, |out| { |
| let mut out_len = 0usize; |
| let ret = accessor(key.as_ffi_ptr(), out, &mut out_len, max_len); |
| // If `max_len` is correct then these functions never fail. |
| assert_eq!(ret, 1); |
| assert!(out_len <= max_len); |
| // Safety: `out_len` bytes have been written, as required. |
| out_len |
| }) |
| } |
| } |
| |
| let pub_key = get_value_from_key( |
| &key, |
| bssl_sys::EVP_HPKE_KEY_public_key, |
| bssl_sys::EVP_HPKE_MAX_PUBLIC_KEY_LENGTH as usize, |
| ); |
| let priv_key = get_value_from_key( |
| &key, |
| bssl_sys::EVP_HPKE_KEY_private_key, |
| bssl_sys::EVP_HPKE_MAX_PRIVATE_KEY_LENGTH as usize, |
| ); |
| (pub_key, priv_key) |
| } |
| } |
| |
| /// Supported KDF algorithms with values detailed in RFC 9180. |
| #[derive(Clone, Copy)] |
| pub enum Kdf { |
| #[allow(missing_docs)] |
| HkdfSha256 = 1, |
| } |
| |
| /// Supported AEAD algorithms with values detailed in RFC 9180. |
| #[derive(Clone, Copy)] |
| #[allow(missing_docs)] |
| pub enum Aead { |
| Aes128Gcm = 1, |
| Aes256Gcm = 2, |
| Chacha20Poly1305 = 3, |
| } |
| |
| impl Aead { |
| fn from_rfc_id(n: u16) -> Option<Aead> { |
| let ret = match n { |
| 1 => Aead::Aes128Gcm, |
| 2 => Aead::Aes256Gcm, |
| 3 => Aead::Chacha20Poly1305, |
| _ => return None, |
| }; |
| // The mapping above must agree with the values in the enum. |
| assert_eq!(n, ret as u16); |
| Some(ret) |
| } |
| |
| fn as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_AEAD { |
| // Safety: these functions all return pointers to static data. |
| unsafe { |
| match self { |
| Aead::Aes128Gcm => bssl_sys::EVP_hpke_aes_128_gcm(), |
| Aead::Aes256Gcm => bssl_sys::EVP_hpke_aes_256_gcm(), |
| Aead::Chacha20Poly1305 => bssl_sys::EVP_hpke_chacha20_poly1305(), |
| } |
| } |
| } |
| } |
| |
| /// Maximum length of the encapsulated key for all currently supported KEMs. |
| const MAX_ENCAPSULATED_KEY_LEN: usize = bssl_sys::EVP_HPKE_MAX_ENC_LENGTH as usize; |
| |
| /// HPKE parameters, including KEM, KDF, and AEAD. |
| pub struct Params { |
| kem: *const bssl_sys::EVP_HPKE_KEM, |
| kdf: *const bssl_sys::EVP_HPKE_KDF, |
| aead: *const bssl_sys::EVP_HPKE_AEAD, |
| } |
| |
| impl Params { |
| /// New `Params` from KEM, KDF, and AEAD enums. |
| pub fn new(_kem: Kem, _kdf: Kdf, aead: Aead) -> Self { |
| // Safety: EVP_hpke_x25519_hkdf_sha256 and EVP_hpke_hkdf_sha256 just |
| // return pointers to static data. |
| unsafe { |
| Self { |
| // Only one KEM and KDF are supported thus far. |
| kem: bssl_sys::EVP_hpke_x25519_hkdf_sha256(), |
| kdf: bssl_sys::EVP_hpke_hkdf_sha256(), |
| aead: aead.as_ffi_ptr(), |
| } |
| } |
| } |
| |
| /// New `Params` from KEM, KDF, and AEAD IDs as detailed in RFC 9180. |
| pub fn new_from_rfc_ids(kem_id: u16, kdf_id: u16, aead_id: u16) -> Option<Self> { |
| let kem = Kem::X25519HkdfSha256; |
| let kdf = Kdf::HkdfSha256; |
| let aead = Aead::from_rfc_id(aead_id)?; |
| |
| if kem_id != kem as u16 || kdf_id != kdf as u16 { |
| return None; |
| } |
| Some(Self::new(kem, kdf, aead)) |
| } |
| } |
| |
| /// HPKE sender context. Callers may use `seal()` to encrypt messages for the recipient. |
| pub struct SenderContext(scoped::EvpHpkeCtx); |
| |
| impl SenderContext { |
| /// Performs the SetupBaseS HPKE operation and returns a sender context |
| /// plus an encapsulated shared secret for `recipient_pub_key`. |
| /// |
| /// Returns `None` if `recipient_pub_key` is invalid. |
| /// |
| /// On success, callers may use `seal()` to encrypt messages for the recipient. |
| pub fn new(params: &Params, recipient_pub_key: &[u8], info: &[u8]) -> Option<(Self, Vec<u8>)> { |
| let mut ctx = scoped::EvpHpkeCtx::new(); |
| unsafe { |
| with_output_vec_fallible(MAX_ENCAPSULATED_KEY_LEN, |enc_key_buf| { |
| let mut enc_key_len = 0usize; |
| // Safety: EVP_HPKE_CTX_setup_sender |
| // - is called with context created from EVP_HPKE_CTX_new, |
| // - is called with valid buffers with corresponding pointer and length, and |
| // - returns 0 on error. |
| let ret = bssl_sys::EVP_HPKE_CTX_setup_sender( |
| ctx.as_mut_ffi_ptr(), |
| enc_key_buf, |
| &mut enc_key_len, |
| MAX_ENCAPSULATED_KEY_LEN, |
| params.kem, |
| params.kdf, |
| params.aead, |
| recipient_pub_key.as_ffi_ptr(), |
| recipient_pub_key.len(), |
| info.as_ffi_ptr(), |
| info.len(), |
| ); |
| if ret == 1 { |
| Some(enc_key_len) |
| } else { |
| None |
| } |
| }) |
| } |
| .map(|enc_key| (Self(ctx), enc_key)) |
| } |
| |
| /// Seal encrypts `plaintext`, and authenticates `aad`, returning the resulting ciphertext. |
| /// |
| /// Note that HPKE encryption is stateful and ordered. The sender's first call to `seal()` must |
| /// correspond to the recipient's first call to `open()`, etc. |
| /// |
| /// This function panics if adding the `plaintext` length and |
| /// `bssl_sys::EVP_HPKE_CTX_max_overhead` overflows. |
| pub fn seal(&mut self, plaintext: &[u8], aad: &[u8]) -> Vec<u8> { |
| // Safety: EVP_HPKE_CTX_max_overhead panics if ctx is not set up as a sender. |
| #[allow(clippy::expect_used)] |
| let max_out_len = plaintext |
| .len() |
| .checked_add(unsafe { bssl_sys::EVP_HPKE_CTX_max_overhead(self.0.as_ffi_ptr()) }) |
| .expect("Maximum output length calculation overflow"); |
| unsafe { |
| with_output_vec(max_out_len, |out_buf| { |
| let mut out_len = 0usize; |
| // Safety: EVP_HPKE_CTX_seal |
| // - is called with context created from EVP_HPKE_CTX_new and |
| // - is called with valid buffers with corresponding pointer and length. |
| let result = bssl_sys::EVP_HPKE_CTX_seal( |
| self.0.as_mut_ffi_ptr(), |
| out_buf, |
| &mut out_len, |
| max_out_len, |
| plaintext.as_ffi_ptr(), |
| plaintext.len(), |
| aad.as_ffi_ptr(), |
| aad.len(), |
| ); |
| assert_eq!(result, 1); |
| out_len |
| }) |
| } |
| } |
| } |
| |
| /// HPKE recipient context. Callers may use `open()` to decrypt messages from the sender. |
| pub struct RecipientContext(scoped::EvpHpkeCtx); |
| |
| impl RecipientContext { |
| /// New implements the SetupBaseR HPKE operation, which decapsulates the shared secret in |
| /// `encapsulated_key` with `recipient_priv_key` and sets up a recipient context. These are |
| /// stored and returned in the newly created RecipientContext. |
| /// |
| /// Note that `encapsulated_key` may be invalid, in which case this function will return an |
| /// error. |
| /// |
| /// On success, callers may use `open()` to decrypt messages from the sender. |
| pub fn new( |
| params: &Params, |
| recipient_priv_key: &[u8], |
| encapsulated_key: &[u8], |
| info: &[u8], |
| ) -> Option<Self> { |
| let mut hpke_key = scoped::EvpHpkeKey::new(); |
| |
| // Safety: EVP_HPKE_KEY_init returns 0 on error. |
| let result = unsafe { |
| bssl_sys::EVP_HPKE_KEY_init( |
| hpke_key.as_mut_ffi_ptr(), |
| params.kem, |
| recipient_priv_key.as_ffi_ptr(), |
| recipient_priv_key.len(), |
| ) |
| }; |
| if result != 1 { |
| return None; |
| } |
| |
| let mut ctx = scoped::EvpHpkeCtx::new(); |
| |
| // Safety: EVP_HPKE_CTX_setup_recipient |
| // - is called with context created from EVP_HPKE_CTX_new, |
| // - is called with HPKE key created from EVP_HPKE_KEY_init, |
| // - is called with valid buffers with corresponding pointer and length, and |
| // - returns 0 on error. |
| let result = unsafe { |
| bssl_sys::EVP_HPKE_CTX_setup_recipient( |
| ctx.as_mut_ffi_ptr(), |
| hpke_key.as_ffi_ptr(), |
| params.kdf, |
| params.aead, |
| encapsulated_key.as_ffi_ptr(), |
| encapsulated_key.len(), |
| info.as_ffi_ptr(), |
| info.len(), |
| ) |
| }; |
| if result == 1 { |
| Some(Self(ctx)) |
| } else { |
| None |
| } |
| } |
| |
| /// Open authenticates `aad` and decrypts `ciphertext`. It returns an error on failure. |
| /// |
| /// Note that HPKE encryption is stateful and ordered. The sender's first call to `seal()` must |
| /// correspond to the recipient's first call to `open()`, etc. |
| pub fn open(&mut self, ciphertext: &[u8], aad: &[u8]) -> Option<Vec<u8>> { |
| let max_out_len = ciphertext.len(); |
| unsafe { |
| with_output_vec_fallible(max_out_len, |out_buf| { |
| let mut out_len = 0usize; |
| // Safety: EVP_HPKE_CTX_open |
| // - is called with context created from EVP_HPKE_CTX_new and |
| // - is called with valid buffers with corresponding pointer and length. |
| let result = bssl_sys::EVP_HPKE_CTX_open( |
| self.0.as_mut_ffi_ptr(), |
| out_buf, |
| &mut out_len, |
| max_out_len, |
| ciphertext.as_ffi_ptr(), |
| ciphertext.len(), |
| aad.as_ffi_ptr(), |
| aad.len(), |
| ); |
| if result == 1 { |
| Some(out_len) |
| } else { |
| None |
| } |
| }) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod test { |
| use super::*; |
| use crate::test_helpers::decode_hex; |
| |
| struct TestVector { |
| kem_id: u16, |
| kdf_id: u16, |
| aead_id: u16, |
| info: [u8; 20], |
| seed_for_testing: [u8; 32], // skEm |
| recipient_pub_key: [u8; 32], // pkRm |
| recipient_priv_key: [u8; 32], // skRm |
| encapsulated_key: [u8; 32], // enc |
| plaintext: [u8; 29], // pt |
| associated_data: [u8; 7], // aad |
| ciphertext: [u8; 45], // ct |
| } |
| |
| // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.1 |
| fn x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm() -> TestVector { |
| TestVector { |
| kem_id: 32, |
| kdf_id: 1, |
| aead_id: 1, |
| info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), |
| seed_for_testing: decode_hex("52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"), |
| recipient_pub_key: decode_hex("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"), |
| recipient_priv_key: decode_hex("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8"), |
| encapsulated_key: decode_hex("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"), |
| plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), |
| associated_data: decode_hex("436f756e742d30"), |
| ciphertext: decode_hex("f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a"), |
| } |
| } |
| |
| // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.2 |
| fn x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305() -> TestVector { |
| TestVector { |
| kem_id: 32, |
| kdf_id: 1, |
| aead_id: 3, |
| info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), |
| seed_for_testing: decode_hex("f4ec9b33b792c372c1d2c2063507b684ef925b8c75a42dbcbf57d63ccd381600"), |
| recipient_pub_key: decode_hex("4310ee97d88cc1f088a5576c77ab0cf5c3ac797f3d95139c6c84b5429c59662a"), |
| recipient_priv_key: decode_hex("8057991eef8f1f1af18f4a9491d16a1ce333f695d4db8e38da75975c4478e0fb"), |
| encapsulated_key: decode_hex("1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a"), |
| plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), |
| associated_data: decode_hex("436f756e742d30"), |
| ciphertext: decode_hex("1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28"), |
| } |
| } |
| |
| #[test] |
| fn all_algorithms() { |
| let kems = vec![Kem::X25519HkdfSha256]; |
| let kdfs = vec![Kdf::HkdfSha256]; |
| let aeads = vec![Aead::Aes128Gcm, Aead::Aes256Gcm, Aead::Chacha20Poly1305]; |
| let plaintext: &[u8] = b"plaintext"; |
| let aad: &[u8] = b"aad"; |
| let info: &[u8] = b"info"; |
| |
| for kem in &kems { |
| let (pub_key, priv_key) = kem.generate_keypair(); |
| for kdf in &kdfs { |
| for aead in &aeads { |
| let params = |
| Params::new_from_rfc_ids(*kem as u16, *kdf as u16, *aead as u16).unwrap(); |
| |
| let (mut send_ctx, encapsulated_key) = |
| SenderContext::new(¶ms, &pub_key, info).unwrap(); |
| let mut recv_ctx = |
| RecipientContext::new(¶ms, &priv_key, &encapsulated_key, info).unwrap(); |
| assert_eq!( |
| plaintext, |
| recv_ctx |
| .open(send_ctx.seal(plaintext, aad).as_ref(), aad) |
| .unwrap() |
| ); |
| assert_eq!( |
| plaintext, |
| recv_ctx |
| .open(send_ctx.seal(plaintext, aad).as_ref(), aad) |
| .unwrap() |
| ); |
| assert!(recv_ctx.open(b"nonsense", aad).is_none()); |
| } |
| } |
| } |
| } |
| |
| fn new_sender_context_for_testing( |
| params: &Params, |
| recipient_pub_key: &[u8], |
| info: &[u8], |
| seed_for_testing: &[u8], |
| ) -> (SenderContext, Vec<u8>) { |
| let mut ctx = scoped::EvpHpkeCtx::new(); |
| |
| let encapsulated_key = unsafe { |
| with_output_vec_fallible(MAX_ENCAPSULATED_KEY_LEN, |enc_key_buf| { |
| let mut enc_key_len = 0usize; |
| // Safety: EVP_HPKE_CTX_setup_sender_with_seed_for_testing |
| // - is called with context created from EVP_HPKE_CTX_new, |
| // - is called with valid buffers with corresponding pointer and length, and |
| // - returns 0 on error. |
| let result = bssl_sys::EVP_HPKE_CTX_setup_sender_with_seed_for_testing( |
| ctx.as_mut_ffi_ptr(), |
| enc_key_buf, |
| &mut enc_key_len, |
| MAX_ENCAPSULATED_KEY_LEN, |
| params.kem, |
| params.kdf, |
| params.aead, |
| recipient_pub_key.as_ffi_ptr(), |
| recipient_pub_key.len(), |
| info.as_ffi_ptr(), |
| info.len(), |
| seed_for_testing.as_ffi_ptr(), |
| seed_for_testing.len(), |
| ); |
| if result == 1 { |
| Some(enc_key_len) |
| } else { |
| None |
| } |
| }) |
| } |
| .unwrap(); |
| (SenderContext(ctx), encapsulated_key) |
| } |
| |
| #[test] |
| fn seal_with_vector() { |
| for test in vec![ |
| x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), |
| x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), |
| ] { |
| let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); |
| |
| let (mut ctx, encapsulated_key) = new_sender_context_for_testing( |
| ¶ms, |
| &test.recipient_pub_key, |
| &test.info, |
| &test.seed_for_testing, |
| ); |
| |
| assert_eq!(encapsulated_key, test.encapsulated_key.to_vec()); |
| |
| let ciphertext = ctx.seal(&test.plaintext, &test.associated_data); |
| assert_eq!(&ciphertext, test.ciphertext.as_ref()); |
| } |
| } |
| |
| #[test] |
| fn open_with_vector() { |
| for test in vec![ |
| x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), |
| x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), |
| ] { |
| let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); |
| |
| let mut ctx = RecipientContext::new( |
| ¶ms, |
| &test.recipient_priv_key, |
| &test.encapsulated_key, |
| &test.info, |
| ) |
| .unwrap(); |
| |
| let plaintext = ctx.open(&test.ciphertext, &test.associated_data).unwrap(); |
| assert_eq!(&plaintext, test.plaintext.as_ref()); |
| } |
| } |
| |
| #[test] |
| fn disallowed_params_fail() { |
| let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); |
| |
| assert!(Params::new_from_rfc_ids(0, vec.kdf_id, vec.aead_id).is_none()); |
| assert!(Params::new_from_rfc_ids(vec.kem_id, 0, vec.aead_id).is_none()); |
| assert!(Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, 0).is_none()); |
| } |
| |
| #[test] |
| fn bad_recipient_pub_key_fails() { |
| let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); |
| let params = Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, vec.aead_id).unwrap(); |
| |
| assert!(SenderContext::new(¶ms, b"", &vec.info).is_none()); |
| } |
| |
| #[test] |
| fn bad_recipient_priv_key_fails() { |
| let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); |
| let params = Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, vec.aead_id).unwrap(); |
| |
| assert!(RecipientContext::new(¶ms, b"", &vec.encapsulated_key, &vec.info).is_none()); |
| } |
| } |