// Copyright 2024 The BoringSSL Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! 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()); //! //! // There is also an interface for exporting secrets from both sender //! // and recipient contexts. //! let sender_export = sender_ctx.export(b"ctx", 32); //! let recipient_export = recipient_ctx.export(b"ctx", 32); //! assert_eq!(sender_export, recipient_export); //! ``` 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 { /// KEM using DHKEM P-256 and HKDF-SHA256. P256HkdfSha256 = 16, // 0x0010 /// KEM using DHKEM X25519 and HKDF-SHA256. X25519HkdfSha256 = 32, // 0x0020 } 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::P256HkdfSha256 => bssl_sys::EVP_hpke_p256_hkdf_sha256(), Kem::X25519HkdfSha256 => bssl_sys::EVP_hpke_x25519_hkdf_sha256(), } } } fn from_rfc_id(n: u16) -> Option { match n { n if n == Kem::P256HkdfSha256 as u16 => Some(Self::P256HkdfSha256), n if n == Kem::X25519HkdfSha256 as u16 => Some(Self::X25519HkdfSha256), _ => None, } } /// Generate a public and private key for this KEM. pub fn generate_keypair(&self) -> (Vec, Vec) { 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); let pub_key = Self::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 = Self::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) } /// Get a private key's corresponding public key, or `None` if the private /// key is invalid. pub fn public_from_private(&self, priv_key: &[u8]) -> Option> { let mut key = scoped::EvpHpkeKey::new(); // Safety: `key`, `self`, and `priv_key` must be valid and this function // doesn't take ownership of any of them. let ret = unsafe { bssl_sys::EVP_HPKE_KEY_init( key.as_mut_ffi_ptr(), self.as_ffi_ptr(), priv_key.as_ptr(), priv_key.len(), ) }; if ret != 1 { return None; } let pub_key = Self::get_value_from_key( &key, bssl_sys::EVP_HPKE_KEY_public_key, bssl_sys::EVP_HPKE_MAX_PUBLIC_KEY_LENGTH as usize, ); Some(pub_key) } 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 { 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 }) } } } /// 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 { 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_hkdf_sha256 just returns pointer to static data. unsafe { Self { kem: kem.as_ffi_ptr(), // Only one KDF is supported thus far. 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 { let kem = Kem::from_rfc_id(kem_id)?; let kdf = Kdf::HkdfSha256; let aead = Aead::from_rfc_id(aead_id)?; if 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)> { 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 { // 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 }) } } /// Exports a secret of length `out_len` from the HPKE context using `context` as the context /// string. pub fn export(&mut self, context: &[u8], out_len: usize) -> Vec { unsafe { with_output_vec(out_len, |out_buf| { // Safety: EVP_HPKE_CTX_export // - 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, which only occurs when OOM. let ret = bssl_sys::EVP_HPKE_CTX_export( self.0.as_mut_ffi_ptr(), out_buf, out_len, context.as_ffi_ptr(), context.len(), ); assert_eq!(ret, 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 { 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> { 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 } }) } } /// Exports a secret of length `out_len` from the HPKE context using `context` as the context /// string. pub fn export(&mut self, context: &[u8], out_len: usize) -> Vec { unsafe { with_output_vec(out_len, |out_buf| { // Safety: EVP_HPKE_CTX_export // - 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, which only occurs when OOM. let ret = bssl_sys::EVP_HPKE_CTX_export( self.0.as_mut_ffi_ptr(), out_buf, out_len, context.as_ffi_ptr(), context.len(), ); assert_eq!(ret, 1); out_len }) } } } #[cfg(test)] mod test { use super::*; use crate::test_helpers::{decode_hex, decode_hex_into_vec}; struct TestVector { kem_id: u16, kdf_id: u16, aead_id: u16, info: [u8; 20], seed_for_testing: [u8; 32], // skEm recipient_pub_key: Vec, // pkRm recipient_priv_key: [u8; 32], // skRm encapsulated_key: Vec, // enc plaintext: [u8; 29], // pt associated_data: [u8; 7], // aad ciphertext: [u8; 45], // ct exporter_context: [u8; 11], exported_value: [u8; 32], } // 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_into_vec("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"), recipient_priv_key: decode_hex("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8"), encapsulated_key: decode_hex_into_vec("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"), plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), associated_data: decode_hex("436f756e742d30"), ciphertext: decode_hex("f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a"), exporter_context: decode_hex("54657374436f6e74657874"), exported_value: decode_hex("e9e43065102c3836401bed8c3c3c75ae46be1639869391d62c61f1ec7af54931"), } } // 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_into_vec("4310ee97d88cc1f088a5576c77ab0cf5c3ac797f3d95139c6c84b5429c59662a"), recipient_priv_key: decode_hex("8057991eef8f1f1af18f4a9491d16a1ce333f695d4db8e38da75975c4478e0fb"), encapsulated_key: decode_hex_into_vec("1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a"), plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), associated_data: decode_hex("436f756e742d30"), ciphertext: decode_hex("1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28"), exporter_context: decode_hex("54657374436f6e74657874"), exported_value: decode_hex("5acb09211139c43b3090489a9da433e8a30ee7188ba8b0a9a1ccf0c229283e53"), } } // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.3 fn p256_hkdf_sha256_hkdf_sha256_aes_128_gcm() -> TestVector { TestVector { kem_id: 16, kdf_id: 1, aead_id: 1, info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), seed_for_testing: decode_hex("4270e54ffd08d79d5928020af4686d8f6b7d35dbe470265f1f5aa22816ce860e"), recipient_pub_key: decode_hex_into_vec("04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0"), recipient_priv_key: decode_hex("f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2"), encapsulated_key: decode_hex_into_vec("04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4"), plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), associated_data: decode_hex("436f756e742d30"), ciphertext: decode_hex("5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434"), exporter_context: decode_hex("54657374436f6e74657874"), exported_value: decode_hex("d8f1ea7942adbba7412c6d431c62d01371ea476b823eb697e1f6e6cae1dab85a"), } } #[test] fn all_algorithms() { let kems = vec![Kem::X25519HkdfSha256, Kem::P256HkdfSha256]; 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()); } } } } #[test] fn kem_public_from_private() { let kems = vec![Kem::X25519HkdfSha256, Kem::P256HkdfSha256]; for kem in &kems { let (pub_key, priv_key) = kem.generate_keypair(); assert_eq!(kem.public_from_private(&priv_key), Some(pub_key)); assert_eq!(kem.public_from_private(b"invalid"), None); } } fn new_sender_context_for_testing( params: &Params, recipient_pub_key: &[u8], info: &[u8], seed_for_testing: &[u8], ) -> (SenderContext, Vec) { 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(), p256_hkdf_sha256_hkdf_sha256_aes_128_gcm(), ] { 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(), p256_hkdf_sha256_hkdf_sha256_aes_128_gcm(), ] { 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 export_with_vector() { for test in vec![ x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), p256_hkdf_sha256_hkdf_sha256_aes_128_gcm(), ] { let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); let (mut sender_ctx, _encapsulated_key) = new_sender_context_for_testing( ¶ms, &test.recipient_pub_key, &test.info, &test.seed_for_testing, ); assert_eq!( test.exported_value.as_ref(), sender_ctx.export(&test.exporter_context, test.exported_value.len()) ); let mut recipient_ctx = RecipientContext::new( ¶ms, &test.recipient_priv_key, &test.encapsulated_key, &test.info, ) .unwrap(); assert_eq!( test.exported_value.as_ref(), recipient_ctx.export(&test.exporter_context, test.exported_value.len()) ); } } #[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()); } }