aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorAdam Langley <agl@chromium.org>2024-02-22 11:49:41 -0800
committerBoringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>2024-03-06 00:46:22 +0000
commit4e8a84758d65656f3db91a609f6eaa0219999bbb (patch)
treec23a8dfde27d548a821448c1b45d83a4e349e57f /rust
parent2fb5cdb6c44506442fce110c2d3903a880888dfb (diff)
downloadboringssl-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.rs490
-rw-r--r--rust/bssl-crypto/src/scoped.rs22
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(&params, &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(&params, &recipient_pub_key, &info).unwrap();
+//! // On the receiving side...
+//! let mut recipient_ctx = hpke::RecipientContext::new(
+//! &params,
+//! &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(
//! &params,
-//! &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(&params, &vec.recipient_pub_key, &vec.info).unwrap();
-
- let mut recipient_ctx = RecipientContext::new(
- &params,
- &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(&params, &pub_key, info).unwrap();
+ let mut recv_ctx =
+ RecipientContext::new(&params, &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(
- &params,
- &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(
+ &params,
+ &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(
- &params,
- &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(
+ &params,
+ &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) }
}
}