diff options
author | Adam Langley <agl@chromium.org> | 2024-02-22 11:49:41 -0800 |
---|---|---|
committer | Boringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2024-03-06 00:46:22 +0000 |
commit | 4e8a84758d65656f3db91a609f6eaa0219999bbb (patch) | |
tree | c23a8dfde27d548a821448c1b45d83a4e349e57f /rust | |
parent | 2fb5cdb6c44506442fce110c2d3903a880888dfb (diff) | |
download | boringssl-4e8a84758d65656f3db91a609f6eaa0219999bbb.zip boringssl-4e8a84758d65656f3db91a609f6eaa0219999bbb.tar.gz boringssl-4e8a84758d65656f3db91a609f6eaa0219999bbb.tar.bz2 |
Tidy up Rust HPKE binding.
The doc test didn't compile and needs support for generating keys in
order to be a good example. Also bind all the AEADs that we support, and
have the encapsulated key be a second return value when creating a
sender, rather than carrying it in memory for the whole lifetime.
Change-Id: I1533560a925d4e239eedbfa0f4213d9e79085b77
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/66527
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Diffstat (limited to 'rust')
-rw-r--r-- | rust/bssl-crypto/src/hpke.rs | 490 | ||||
-rw-r--r-- | rust/bssl-crypto/src/scoped.rs | 22 |
2 files changed, 300 insertions, 212 deletions
diff --git a/rust/bssl-crypto/src/hpke.rs b/rust/bssl-crypto/src/hpke.rs index bd4e8fd..2725b3f 100644 --- a/rust/bssl-crypto/src/hpke.rs +++ b/rust/bssl-crypto/src/hpke.rs @@ -15,61 +15,168 @@ //! Hybrid Public Key Encryption //! -//! HPKE provides a variant of public key encryption of arbitrary-sized plaintexts -//! for a recipient public key. It works for any combination of an asymmetric key -//! encapsulation mechanism (KEM), key derivation function (KDF), and authenticated -//! encryption with additional data (AEAD) function. +//! 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. //! -//! Note that key generation is currently not supported. -//! //! ``` -//! use bssl_crypto::hpke::{Params, RecipientContext, SenderContext}; +//! 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); //! -//! let params = Params::new_from_rfc_ids(32, 1, 1).unwrap(); -//! let recipient_pub_key = ...; -//! let info = ...; -//! let mut sender_ctx = -//! SenderContext::new(¶ms, &recipient_pub_key, &info).unwrap(); +//! // On the receiving side... +//! let mut recipient_ctx = hpke::RecipientContext::new( +//! ¶ms, +//! &priv_key, +//! &encapsulated_key, +//! info, +//! ).unwrap(); //! -//! let pt = b"plaintext"; -//! let ad = b"associated_data"; -//! let ct = sender_ctx.seal(pt, ad); +//! 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); //! -//! let recipient_priv_key = ...; -//! let mut recipient_ctx = RecipientContext::new( +//! // Messages must be processed in order, so trying to `open` the second +//! // message first will fail. +//! let mut recipient_ctx = hpke::RecipientContext::new( //! ¶ms, -//! &recipient_priv_key, -//! &sender_ctx.encapsulated_key(), -//! &info, +//! &priv_key, +//! &encapsulated_key, +//! info, //! ).unwrap(); //! -//! let got_pt = recipient_ctx.open(&ct, ad); +//! 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(PartialEq)] -#[allow(missing_docs)] +#[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(PartialEq)] -#[allow(missing_docs)] +#[derive(Clone, Copy)] pub enum Kdf { + #[allow(missing_docs)] HkdfSha256 = 1, } /// Supported AEAD algorithms with values detailed in RFC 9180. -#[derive(PartialEq)] +#[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. @@ -83,63 +190,44 @@ pub struct Params { } impl Params { - /// New Params from KEM, KDF, and AEAD enums. - pub fn new(kem: Kem, kdf: Kdf, aead: Aead) -> Option<Self> { - if kem != Kem::X25519HkdfSha256 || kdf != Kdf::HkdfSha256 || aead != Aead::Aes128Gcm { - return None; - } - // Safety: EVP_hpke_x25519_hkdf_sha256, EVP_hpke_hkdf_sha256, and EVP_hpke_aes_128_gcm - // initialize structs containing constants and cannot return an error. + /// 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 { - Some(Self { - kem: bssl_sys::EVP_hpke_x25519_hkdf_sha256() as *const bssl_sys::EVP_HPKE_KEM, - kdf: bssl_sys::EVP_hpke_hkdf_sha256() as *const bssl_sys::EVP_HPKE_KDF, - aead: bssl_sys::EVP_hpke_aes_128_gcm() as *const bssl_sys::EVP_HPKE_AEAD, - }) + 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: u16, kdf: u16, aead: u16) -> Option<Self> { - if kem != Kem::X25519HkdfSha256 as u16 - || kdf != Kdf::HkdfSha256 as u16 - || aead != Aead::Aes128Gcm as u16 - { + /// 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; } - // Safety: EVP_hpke_x25519_hkdf_sha256, EVP_hpke_hkdf_sha256, and EVP_hpke_aes_128_gcm - // initialize structs containing constants and cannot return an error. - unsafe { - Some(Self { - kem: bssl_sys::EVP_hpke_x25519_hkdf_sha256() as *const bssl_sys::EVP_HPKE_KEM, - kdf: bssl_sys::EVP_hpke_hkdf_sha256() as *const bssl_sys::EVP_HPKE_KDF, - aead: bssl_sys::EVP_hpke_aes_128_gcm() as *const bssl_sys::EVP_HPKE_AEAD, - }) - } + Some(Self::new(kem, kdf, aead)) } } -/// HPKE recipient context. Callers may use `open()` to decrypt messages from the sender. -pub struct RecipientContext { - ctx: scoped::EvpHpkeCtx, -} - /// HPKE sender context. Callers may use `seal()` to encrypt messages for the recipient. -pub struct SenderContext { - ctx: RecipientContext, - encapsulated_key: Vec<u8>, -} +pub struct SenderContext(scoped::EvpHpkeCtx); impl SenderContext { - /// New implements the SetupBaseS HPKE operation, which encapsulates a shared secret for - /// `recipient_pub_key` and sets up a sender context. These are stored and returned in the - /// newly created SenderContext. + /// Performs the SetupBaseS HPKE operation and returns a sender context + /// plus an encapsulated shared secret for `recipient_pub_key`. /// - /// Note that `recipient_pub_key` may be invalid, in which case this function will return an - /// error. + /// 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> { + 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| { @@ -148,7 +236,7 @@ impl SenderContext { // - 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( + let ret = bssl_sys::EVP_HPKE_CTX_setup_sender( ctx.as_mut_ffi_ptr(), enc_key_buf, &mut enc_key_len, @@ -161,36 +249,56 @@ impl SenderContext { info.as_ffi_ptr(), info.len(), ); - if result == 1 { + if ret == 1 { Some(enc_key_len) } else { None } }) } - .map(|enc_key| Self { - ctx: RecipientContext { ctx }, - encapsulated_key: enc_key, - }) + .map(|enc_key| (Self(ctx), enc_key)) } - /// Seal encrypts `pt` and returns the resulting ciphertext, which is authenticated with `aad`. + /// 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 `pt` length and bssl_sys::EVP_HPKE_CTX_max_overhead - /// overflows. - pub fn seal(&mut self, pt: &[u8], aad: &[u8]) -> Vec<u8> { - self.ctx.seal(pt, aad) - } - - #[allow(missing_docs)] - pub fn encapsulated_key(&self) -> &[u8] { - &self.encapsulated_key + /// 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 @@ -241,54 +349,18 @@ impl RecipientContext { ) }; if result == 1 { - Some(Self { ctx }) + Some(Self(ctx)) } else { None } } - /// Seal encrypts `pt` and returns the resulting ciphertext, which is authenticated with `aad`. + /// 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. - /// - /// This function panics if adding the `pt` length and bssl_sys::EVP_HPKE_CTX_max_overhead - /// overflows. - pub fn seal(&mut self, pt: &[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 = pt - .len() - .checked_add(unsafe { bssl_sys::EVP_HPKE_CTX_max_overhead(self.ctx.as_mut_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.ctx.as_mut_ffi_ptr(), - out_buf, - &mut out_len, - max_out_len, - pt.as_ffi_ptr(), - pt.len(), - aad.as_ffi_ptr(), - aad.len(), - ); - assert_eq!(result, 1); - out_len - }) - } - } - - /// Open authenticates `aad` and decrypts `ct`. 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, ct: &[u8], aad: &[u8]) -> Option<Vec<u8>> { - let max_out_len = ct.len(); + 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; @@ -296,12 +368,12 @@ impl RecipientContext { // - 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.ctx.as_mut_ffi_ptr(), + self.0.as_mut_ffi_ptr(), out_buf, &mut out_len, max_out_len, - ct.as_ffi_ptr(), - ct.len(), + ciphertext.as_ffi_ptr(), + ciphertext.len(), aad.as_ffi_ptr(), aad.len(), ); @@ -351,32 +423,58 @@ mod test { } } - #[test] - fn seal_and_open() { - 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(); - - let mut sender_ctx = - SenderContext::new(¶ms, &vec.recipient_pub_key, &vec.info).unwrap(); - - let mut recipient_ctx = RecipientContext::new( - ¶ms, - &vec.recipient_priv_key, - &sender_ctx.encapsulated_key(), - &vec.info, - ) - .unwrap(); - - let pt = b"plaintext"; - let ad = b"associated_data"; - let mut prev_ct: Vec<u8> = Vec::new(); - for _ in 0..10 { - let ct = sender_ctx.seal(pt, ad); - assert_ne!(ct, prev_ct); - prev_ct = ct.clone(); + // 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"), + } + } - let got_pt = recipient_ctx.open(&ct, ad).unwrap(); - assert_eq!(got_pt, pt); + #[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()); + } + } } } @@ -385,10 +483,10 @@ mod test { recipient_pub_key: &[u8], info: &[u8], seed_for_testing: &[u8], - ) -> Option<SenderContext> { + ) -> (SenderContext, Vec<u8>) { let mut ctx = scoped::EvpHpkeCtx::new(); - unsafe { + 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 @@ -417,57 +515,51 @@ mod test { } }) } - .map(|enc_key| SenderContext { - ctx: RecipientContext { ctx }, - encapsulated_key: enc_key, - }) + .unwrap(); + (SenderContext(ctx), encapsulated_key) } #[test] fn seal_with_vector() { - 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(); - - let mut ctx = new_sender_context_for_testing( - ¶ms, - &vec.recipient_pub_key, - &vec.info, - &vec.seed_for_testing, - ) - .unwrap(); - - assert_eq!(ctx.encapsulated_key, vec.encapsulated_key.to_vec()); - - let ciphertext = ctx.seal(&vec.plaintext, &vec.associated_data); - assert_eq!(ciphertext, vec.ciphertext.to_vec()); + 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() { - 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(); - - let mut ctx = RecipientContext::new( - ¶ms, - &vec.recipient_priv_key, - &vec.encapsulated_key, - &vec.info, - ) - .unwrap(); - - let plaintext = ctx.open(&vec.ciphertext, &vec.associated_data).unwrap(); - assert_eq!(plaintext, vec.plaintext.to_vec()); - } - - #[test] - fn params_new() { - assert!(Params::new(Kem::X25519HkdfSha256, Kdf::HkdfSha256, Aead::Aes128Gcm).is_some()); - } + 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(); - #[test] - fn params_new_from_rfc_ids() { - let vec: TestVector = x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(); - assert!(Params::new_from_rfc_ids(vec.kem_id, vec.kdf_id, vec.aead_id).is_some()); + let plaintext = ctx.open(&test.ciphertext, &test.associated_data).unwrap(); + assert_eq!(&plaintext, test.plaintext.as_ref()); + } } #[test] @@ -477,12 +569,6 @@ mod test { 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()); - assert!(Params::new_from_rfc_ids( - vec.kem_id, - vec.kdf_id, - bssl_sys::EVP_HPKE_AES_256_GCM as u16 - ) - .is_none()); } #[test] diff --git a/rust/bssl-crypto/src/scoped.rs b/rust/bssl-crypto/src/scoped.rs index 17d331a..8c6b21b 100644 --- a/rust/bssl-crypto/src/scoped.rs +++ b/rust/bssl-crypto/src/scoped.rs @@ -79,6 +79,10 @@ impl EvpHpkeCtx { EvpHpkeCtx(ptr) } + pub fn as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_CTX { + self.0 + } + pub fn as_mut_ffi_ptr(&mut self) -> *mut bssl_sys::EVP_HPKE_CTX { self.0 } @@ -91,29 +95,27 @@ impl Drop for EvpHpkeCtx { } /// A scoped `EVP_HPKE_KEY`. -pub struct EvpHpkeKey(*mut bssl_sys::EVP_HPKE_KEY); +pub struct EvpHpkeKey(bssl_sys::EVP_HPKE_KEY); impl EvpHpkeKey { pub fn new() -> Self { - let ptr = unsafe { bssl_sys::EVP_HPKE_KEY_new() }; - // `ptr` is only NULL if we're out of memory, which this crate - // doesn't handle. - assert!(!ptr.is_null()); - EvpHpkeKey(ptr) + EvpHpkeKey(unsafe { initialized_struct(|ptr| bssl_sys::EVP_HPKE_KEY_zero(ptr)) }) } - pub fn as_ffi_ptr(&mut self) -> *const bssl_sys::EVP_HPKE_KEY { - self.0 + pub fn as_ffi_ptr(&self) -> *const bssl_sys::EVP_HPKE_KEY { + &self.0 } pub fn as_mut_ffi_ptr(&mut self) -> *mut bssl_sys::EVP_HPKE_KEY { - self.0 + &mut self.0 } } impl Drop for EvpHpkeKey { fn drop(&mut self) { - unsafe { bssl_sys::EVP_HPKE_KEY_free(self.0) } + // unsafe: the only way to create a `EvpHpkeKey` is via `new` and that + // ensures that this structure is initialized. + unsafe { bssl_sys::EVP_HPKE_KEY_cleanup(&mut self.0) } } } |