Add HPKE secret export and implement Send for EvpHpkeCtx.
Change-Id: I929b31f996c8b67b77286fe9f8eb1af73d2bbc72
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/68307
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/rust/bssl-crypto/src/hpke.rs b/rust/bssl-crypto/src/hpke.rs
index 2725b3f..e3a61bc 100644
--- a/rust/bssl-crypto/src/hpke.rs
+++ b/rust/bssl-crypto/src/hpke.rs
@@ -65,6 +65,12 @@
//!
//! 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};
@@ -294,6 +300,28 @@
})
}
}
+
+ /// 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<u8> {
+ 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.
@@ -385,6 +413,28 @@
})
}
}
+
+ /// 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<u8> {
+ 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)]
@@ -404,6 +454,8 @@
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
@@ -420,6 +472,8 @@
plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"),
associated_data: decode_hex("436f756e742d30"),
ciphertext: decode_hex("f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a"),
+ exporter_context: decode_hex("54657374436f6e74657874"),
+ exported_value: decode_hex("e9e43065102c3836401bed8c3c3c75ae46be1639869391d62c61f1ec7af54931"),
}
}
@@ -437,6 +491,8 @@
plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"),
associated_data: decode_hex("436f756e742d30"),
ciphertext: decode_hex("1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28"),
+ exporter_context: decode_hex("54657374436f6e74657874"),
+ exported_value: decode_hex("5acb09211139c43b3090489a9da433e8a30ee7188ba8b0a9a1ccf0c229283e53"),
}
}
@@ -563,6 +619,38 @@
}
#[test]
+ fn export_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 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();
diff --git a/rust/bssl-crypto/src/scoped.rs b/rust/bssl-crypto/src/scoped.rs
index 8c6b21b..67bf301 100644
--- a/rust/bssl-crypto/src/scoped.rs
+++ b/rust/bssl-crypto/src/scoped.rs
@@ -69,6 +69,9 @@
/// A scoped `EVP_HPKE_CTX`.
pub struct EvpHpkeCtx(*mut bssl_sys::EVP_HPKE_CTX);
+// bssl_sys::EVP_HPKE_CTX is heap-allocated and safe to transfer
+// between threads.
+unsafe impl Send for EvpHpkeCtx {}
impl EvpHpkeCtx {
pub fn new() -> Self {