feat: impl safer export_keyring_material interface.

Prior to this commit the `export_keyring_material` function used
a mutable out buffer for writing exported key material, and returned an
empty Ok result when there was no error doing so.

This commit updates the function such that the ownership of the output
buffer passes through the export function and is returned as the Ok
result when there is no error.

Doing this makes for a safer interface for end users: the output buffer
will be dropped if `export_keyring_material` errors. Callers can only
access the buffer again using the OK result.

All credit due to davidv1992 for the implementation idea and initial
code that was extended in this commit.
This commit is contained in:
Daniel McCarney 2023-03-07 16:08:52 -05:00 committed by Dirkjan Ochtman
parent 861e76d599
commit 38fdd952be
2 changed files with 24 additions and 24 deletions

View File

@ -79,12 +79,12 @@ impl Connection {
/// Derives key material from the agreed connection secrets.
///
/// See [`ConnectionCommon::export_keying_material()`] for more information.
pub fn export_keying_material(
pub fn export_keying_material<T: AsMut<[u8]>>(
&self,
output: &mut [u8],
output: T,
label: &[u8],
context: Option<&[u8]>,
) -> Result<(), Error> {
) -> Result<T, Error> {
match self {
Self::Client(conn) => conn.export_keying_material(output, label, context),
Self::Server(conn) => conn.export_keying_material(output, label, context),
@ -738,7 +738,9 @@ impl<Data> ConnectionCommon<Data> {
///
/// This function fills in `output` with `output.len()` bytes of key
/// material derived from the master session secret using `label`
/// and `context` for diversification.
/// and `context` for diversification. Ownership of the buffer is taken
/// by the function and returned via the Ok result to ensure no key
/// material leaks if the function fails.
///
/// See RFC5705 for more details on what this does and is for.
///
@ -747,14 +749,16 @@ impl<Data> ConnectionCommon<Data> {
///
/// This function fails if called prior to the handshake completing;
/// check with [`CommonState::is_handshaking`] first.
pub fn export_keying_material(
pub fn export_keying_material<T: AsMut<[u8]>>(
&self,
output: &mut [u8],
mut output: T,
label: &[u8],
context: Option<&[u8]>,
) -> Result<(), Error> {
) -> Result<T, Error> {
match self.state.as_ref() {
Ok(st) => st.export_keying_material(output, label, context),
Ok(st) => st
.export_keying_material(output.as_mut(), label, context)
.map(|_| output),
Err(e) => Err(e.clone()),
}
}

View File

@ -2029,25 +2029,21 @@ fn do_exporter_test(client_config: ClientConfig, server_config: ServerConfig) {
);
do_handshake(&mut client, &mut server);
assert_debug_eq(
client.export_keying_material(&mut client_secret, b"label", Some(b"context")),
Ok(()),
);
assert_debug_eq(
server.export_keying_material(&mut server_secret, b"label", Some(b"context")),
Ok(()),
);
assert!(client
.export_keying_material(&mut client_secret, b"label", Some(b"context"))
.is_ok());
assert!(server
.export_keying_material(&mut server_secret, b"label", Some(b"context"))
.is_ok());
assert_eq!(client_secret.to_vec(), server_secret.to_vec());
assert_debug_eq(
client.export_keying_material(&mut client_secret, b"label", None),
Ok(()),
);
assert!(client
.export_keying_material(&mut client_secret, b"label", None)
.is_ok());
assert_ne!(client_secret.to_vec(), server_secret.to_vec());
assert_debug_eq(
server.export_keying_material(&mut server_secret, b"label", None),
Ok(()),
);
assert!(server
.export_keying_material(&mut server_secret, b"label", None)
.is_ok(),);
assert_eq!(client_secret.to_vec(), server_secret.to_vec());
}