diff --git a/openssl-sys/src/evp.rs b/openssl-sys/src/evp.rs index 5fad4b977..ecb5e1e1e 100644 --- a/openssl-sys/src/evp.rs +++ b/openssl-sys/src/evp.rs @@ -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; @@ -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( @@ -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, + ) +} diff --git a/openssl-sys/src/handwritten/kdf.rs b/openssl-sys/src/handwritten/kdf.rs index d34f27450..7fbf13b70 100644 --- a/openssl-sys/src/handwritten/kdf.rs +++ b/openssl-sys/src/handwritten/kdf.rs @@ -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; } } diff --git a/openssl-sys/src/obj_mac.rs b/openssl-sys/src/obj_mac.rs index 8dd720a7a..ec951cc03 100644 --- a/openssl-sys/src/obj_mac.rs +++ b/openssl-sys/src/obj_mac.rs @@ -1021,3 +1021,5 @@ cfg_if! { pub const NID_ac_auditEntity: c_int = 287; } } +#[cfg(ossl110)] +pub const NID_tls1_prf: c_int = 1021; diff --git a/openssl/src/pkey.rs b/openssl/src/pkey.rs index 8d69e1cdc..e2fe018ff 100644 --- a/openssl/src/pkey.rs +++ b/openssl/src/pkey.rs @@ -96,6 +96,9 @@ impl Id { #[cfg(any(ossl110, boringssl, libressl360, awslc))] 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, awslc))] pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519); #[cfg(ossl111)] diff --git a/openssl/src/pkey_ctx.rs b/openssl/src/pkey_ctx.rs index aa39a0f9c..a38fda468 100644 --- a/openssl/src/pkey_ctx.rs +++ b/openssl/src/pkey_ctx.rs @@ -696,6 +696,67 @@ impl PkeyCtxRef { 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. @@ -1107,4 +1168,50 @@ mxJ7imIrEg9nIQ== 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() + ); + } }