Skip to content

Commit

Permalink
Expose TLS PRF key derivation
Browse files Browse the repository at this point in the history
  • Loading branch information
tofay committed Nov 14, 2024
1 parent 96607c6 commit 861dead
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 0 deletions.
53 changes: 53 additions & 0 deletions openssl-sys/src/evp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub const EVP_PKEY_CMAC: c_int = NID_cmac;
pub const EVP_PKEY_POLY1305: c_int = NID_poly1305;
#[cfg(any(ossl110, libressl360))]
pub const EVP_PKEY_HKDF: c_int = NID_hkdf;
#[cfg(ossl110)]
pub const EVP_PKEY_TLS1_PRF: c_int = NID_tls1_prf;

#[cfg(ossl102)]
pub const EVP_CIPHER_CTX_FLAG_WRAP_ALLOW: c_int = 0x1;
Expand Down Expand Up @@ -241,6 +243,13 @@ pub const EVP_PKEY_CTRL_HKDF_INFO: c_int = EVP_PKEY_ALG_CTRL + 6;
#[cfg(any(ossl111, libressl360))]
pub const EVP_PKEY_CTRL_HKDF_MODE: c_int = EVP_PKEY_ALG_CTRL + 7;

#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_TLS_MD: c_int = EVP_PKEY_ALG_CTRL;
#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_TLS_SECRET: c_int = EVP_PKEY_ALG_CTRL + 1;
#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_TLS_SEED: c_int = EVP_PKEY_ALG_CTRL + 2;

#[cfg(any(all(ossl111, not(ossl300)), libressl360))]
pub unsafe fn EVP_PKEY_CTX_set_hkdf_mode(ctx: *mut EVP_PKEY_CTX, mode: c_int) -> c_int {
EVP_PKEY_CTX_ctrl(
Expand Down Expand Up @@ -340,3 +349,47 @@ pub unsafe fn EVP_PKEY_assign_DH(pkey: *mut EVP_PKEY, dh: *mut DH) -> c_int {
pub unsafe fn EVP_PKEY_assign_EC_KEY(pkey: *mut EVP_PKEY, ec_key: *mut EC_KEY) -> c_int {
EVP_PKEY_assign(pkey, EVP_PKEY_EC, ec_key as *mut c_void)
}

#[cfg(all(ossl110, not(ossl300)))]
pub unsafe fn EVP_PKEY_CTX_set_tls1_prf_md(ctx: *mut EVP_PKEY_CTX, md: *const EVP_MD) -> c_int {
EVP_PKEY_CTX_ctrl(
ctx,
-1,
EVP_PKEY_OP_DERIVE,
EVP_PKEY_CTRL_TLS_MD,
0,
md as *mut c_void,
)
}

#[cfg(all(ossl110, not(ossl300)))]
pub unsafe fn EVP_PKEY_CTX_set1_tls1_prf_secret(
ctx: *mut EVP_PKEY_CTX,
key: *const u8,
keylen: c_int,
) -> c_int {
EVP_PKEY_CTX_ctrl(
ctx,
-1,
EVP_PKEY_OP_DERIVE,
EVP_PKEY_CTRL_TLS_SECRET,
keylen,
key as *mut c_void,
)
}

#[cfg(all(ossl110, not(ossl300)))]
pub unsafe fn EVP_PKEY_CTX_add1_tls1_prf_seed(
ctx: *mut EVP_PKEY_CTX,
seed: *const u8,
seedlen: c_int,
) -> c_int {
EVP_PKEY_CTX_ctrl(
ctx,
-1,
EVP_PKEY_OP_DERIVE,
EVP_PKEY_CTRL_TLS_SEED,
seedlen,
seed as *mut c_void,
)
}
11 changes: 11 additions & 0 deletions openssl-sys/src/handwritten/kdf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ cfg_if! {
pub fn EVP_KDF_derive(ctx: *mut EVP_KDF_CTX, key: *mut u8, keylen: size_t, params: *const OSSL_PARAM) -> c_int;
pub fn EVP_KDF_fetch(ctx: *mut OSSL_LIB_CTX, algorithm: *const c_char, properties: *const c_char) -> *mut EVP_KDF;
pub fn EVP_KDF_free(kdf: *mut EVP_KDF);
pub fn EVP_PKEY_CTX_set_tls1_prf_md(ctx: *mut EVP_PKEY_CTX, md: *const EVP_MD) -> c_int;
pub fn EVP_PKEY_CTX_set1_tls1_prf_secret(
ctx: *mut EVP_PKEY_CTX,
secret: *const u8,
secretlen: c_int,
) -> c_int;
pub fn EVP_PKEY_CTX_add1_tls1_prf_seed(
ctx: *mut EVP_PKEY_CTX,
seed: *const u8,
seedlen: c_int,
) -> c_int;
}

}
Expand Down
2 changes: 2 additions & 0 deletions openssl-sys/src/obj_mac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1021,3 +1021,5 @@ cfg_if! {
pub const NID_ac_auditEntity: c_int = 287;
}
}
#[cfg(ossl110)]
pub const NID_tls1_prf: c_int = 1021;
3 changes: 3 additions & 0 deletions openssl/src/pkey.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ impl Id {
#[cfg(any(ossl110, boringssl, libressl360))]
pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF);

#[cfg(ossl110)]
pub const TLS1_PRF: Id = Id(ffi::EVP_PKEY_TLS1_PRF);

#[cfg(any(ossl111, boringssl, libressl370))]
pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
#[cfg(ossl111)]
Expand Down
107 changes: 107 additions & 0 deletions openssl/src/pkey_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,67 @@ impl<T> PkeyCtxRef<T> {
Ok(())
}

/// Sets the digest used for TLS1 PRF derivation.
///
/// Requires OpenSSL 1.1.0 or newer.
#[corresponds(EVP_PKEY_CTX_set_tls1_prf_md)]
#[cfg(ossl110)]
#[inline]
pub fn set_tls1_prf_md(&mut self, digest: &MdRef) -> Result<(), ErrorStack> {
unsafe {
cvt(ffi::EVP_PKEY_CTX_set_tls1_prf_md(
self.as_ptr(),
digest.as_ptr(),
))?;
}

Ok(())
}

/// Sets the secret value for TLS PRF derivation.
///
/// Any existing secret value is replaced and any seed is reset.
///
/// Requires OpenSSL 1.1.0 or newer.
#[corresponds(EVP_PKEY_CTX_set1_tls1_prf_secret)]
#[cfg(ossl110)]
#[inline]
pub fn set_tls1_prf_secret(&mut self, secret: &[u8]) -> Result<(), ErrorStack> {
let len = c_int::try_from(secret.len()).unwrap();

unsafe {
cvt(ffi::EVP_PKEY_CTX_set1_tls1_prf_secret(
self.as_ptr(),
secret.as_ptr(),
len,
))?;
}

Ok(())
}

/// Adds a seed for TLS PRF derivation.
///
/// If a seed is already set, the new seed is appended to the existing seed.
///
/// Requires OpenSSL 1.1.0 or newer.
#[corresponds(EVP_PKEY_CTX_add1_tls1_prf_seed)]
#[cfg(ossl110)]
#[inline]
pub fn add_tls1_prf_seed(&mut self, seed: &[u8]) -> Result<(), ErrorStack> {
let len = c_int::try_from(seed.len()).unwrap();

unsafe {
cvt(ffi::EVP_PKEY_CTX_add1_tls1_prf_seed(
self.as_ptr(),
seed.as_ptr(),
len,
))?;
}

Ok(())
}

/// Derives a shared secret between two keys.
///
/// If `buf` is set to `None`, an upper bound on the number of bytes required for the buffer will be returned.
Expand Down Expand Up @@ -1107,4 +1168,50 @@ n9Q=
assert_eq!(output, expected_output);
assert!(ErrorStack::get().errors().is_empty());
}

#[test]
#[cfg(ossl111)]
fn tls1_prf_sha256() {
// SHA256 PRF test vectors from https://mailarchive.ietf.org/arch/msg/tls/fzVCzk-z3FShgGJ6DOXqM1ydxms/
let mut ctx = PkeyCtx::new_id(Id::TLS1_PRF).unwrap();
ctx.derive_init().unwrap();
ctx.set_tls1_prf_md(Md::sha256()).unwrap();
ctx.set_tls1_prf_secret(&hex::decode("9bbe436ba940f017b17652849a71db35").unwrap())
.unwrap();
ctx.add_tls1_prf_seed(&hex::decode("74657374206c6162656c").unwrap())
.unwrap();
ctx.add_tls1_prf_seed(&hex::decode("a0ba9f936cda311827a6f796ffd5198c").unwrap())
.unwrap();
let mut out = [0u8; 100];
ctx.derive(Some(&mut out)).unwrap();

assert_eq!(
&out[..],
hex::decode("e3f229ba727be17b8d122620557cd453c2aab21d07c3d495329b52d4e61edb5a6b301791e90d35c9c9a46b4e14baf9af0fa022f7077def17abfd3797c0564bab4fbc91666e9def9b97fce34f796789baa48082d122ee42c5a72e5a5110fff70187347b66")
.unwrap()
);
}

#[test]
#[cfg(ossl111)]
fn tls1_prf_sha384() {
// SHA384 PRF test vectors from https://mailarchive.ietf.org/arch/msg/tls/fzVCzk-z3FShgGJ6DOXqM1ydxms/
let mut ctx = PkeyCtx::new_id(Id::TLS1_PRF).unwrap();
ctx.derive_init().unwrap();
ctx.set_tls1_prf_md(Md::sha384()).unwrap();
ctx.set_tls1_prf_secret(&hex::decode("b80b733d6ceefcdc71566ea48e5567df").unwrap())
.unwrap();
ctx.add_tls1_prf_seed(&hex::decode("74657374206c6162656c").unwrap())
.unwrap();
ctx.add_tls1_prf_seed(&hex::decode("cd665cf6a8447dd6ff8b27555edb7465").unwrap())
.unwrap();
let mut out = [0u8; 148];
ctx.derive(Some(&mut out)).unwrap();

assert_eq!(
&out[..],
hex::decode("7b0c18e9ced410ed1804f2cfa34a336a1c14dffb4900bb5fd7942107e81c83cde9ca0faa60be9fe34f82b1233c9146a0e534cb400fed2700884f9dc236f80edd8bfa961144c9e8d792eca722a7b32fc3d416d473ebc2c5fd4abfdad05d9184259b5bf8cd4d90fa0d31e2dec479e4f1a26066f2eea9a69236a3e52655c9e9aee691c8f3a26854308d5eaa3be85e0990703d73e56f")
.unwrap()
);
}
}

0 comments on commit 861dead

Please sign in to comment.