Unverified Commit 25179608 authored by Steven Fackler's avatar Steven Fackler Committed by GitHub
Browse files

Merge pull request #1498 from tomleavy/hkdf-derive

Added HKDF derivation functionality
parents 14d93244 37407d97
Loading
Loading
Loading
Loading
+124 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ pub const EVP_PKEY_X448: c_int = NID_X448;
pub const EVP_PKEY_ED448: c_int = NID_ED448;
pub const EVP_PKEY_HMAC: c_int = NID_hmac;
pub const EVP_PKEY_CMAC: c_int = NID_cmac;
#[cfg(ossl110)]
pub const EVP_PKEY_HKDF: c_int = NID_hkdf;

pub const EVP_CTRL_GCM_SET_IVLEN: c_int = 0x9;
pub const EVP_CTRL_GCM_GET_TAG: c_int = 0x10;
@@ -561,6 +563,7 @@ cfg_if! {
        pub const EVP_PKEY_OP_VERIFYCTX: c_int = 1 << 8;
        pub const EVP_PKEY_OP_ENCRYPT: c_int = 1 << 9;
        pub const EVP_PKEY_OP_DECRYPT: c_int = 1 << 10;
        pub const EVP_PKEY_OP_DERIVE: c_int = 1 << 11;
    } else {
        pub const EVP_PKEY_OP_SIGN: c_int = 1 << 3;
        pub const EVP_PKEY_OP_VERIFY: c_int = 1 << 4;
@@ -569,6 +572,7 @@ cfg_if! {
        pub const EVP_PKEY_OP_VERIFYCTX: c_int = 1 << 7;
        pub const EVP_PKEY_OP_ENCRYPT: c_int = 1 << 8;
        pub const EVP_PKEY_OP_DECRYPT: c_int = 1 << 9;
        pub const EVP_PKEY_OP_DERIVE: c_int = 1 << 10;
    }
}

@@ -586,6 +590,30 @@ pub const EVP_PKEY_CTRL_CIPHER: c_int = 12;

pub const EVP_PKEY_ALG_CTRL: c_int = 0x1000;

#[cfg(ossl111)]
pub const EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND: c_int = 0;

#[cfg(ossl111)]
pub const EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY: c_int = 1;

#[cfg(ossl111)]
pub const EVP_PKEY_HKDEF_MODE_EXPAND_ONLY: c_int = 2;

#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_HKDF_MD: c_int = EVP_PKEY_ALG_CTRL + 3;

#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_HKDF_SALT: c_int = EVP_PKEY_ALG_CTRL + 4;

#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_HKDF_KEY: c_int = EVP_PKEY_ALG_CTRL + 5;

#[cfg(ossl110)]
pub const EVP_PKEY_CTRL_HKDF_INFO: c_int = EVP_PKEY_ALG_CTRL + 6;

#[cfg(ossl111)]
pub const EVP_PKEY_CTRL_HKDF_MODE: c_int = EVP_PKEY_ALG_CTRL + 7;

extern "C" {
    pub fn EVP_PKEY_CTX_new(k: *mut EVP_PKEY, e: *mut ENGINE) -> *mut EVP_PKEY_CTX;
    pub fn EVP_PKEY_CTX_new_id(id: c_int, e: *mut ENGINE) -> *mut EVP_PKEY_CTX;
@@ -639,6 +667,102 @@ extern "C" {
    ) -> c_int;
}

// HKDF Functions
cfg_if! {
    if #[cfg(ossl300)] {
        extern "C" {
            pub fn EVP_PKEY_CTX_set_hkdf_mode(ctx: *mut EVP_PKEY_CTX, mode: c_int) -> c_int;
            pub fn EVP_PKEY_CTX_set_hkdf_md(ctx: *mut EVP_PKEY_CTX, md: *const EVP_MD) -> c_int;
            pub fn EVP_PKEY_CTX_set1_hkdf_salt(
                ctx: *mut EVP_PKEY_CTX,
                salt: *const u8,
                saltlen: c_int,
            ) -> c_int;
            pub fn EVP_PKEY_CTX_set1_hkdf_key(
                ctx: *mut EVP_PKEY_CTX,
                key: *const u8,
                keylen: c_int,
            ) -> c_int;
            pub fn EVP_PKEY_CTX_add1_hkdf_info(
                ctx: *mut EVP_PKEY_CTX,
                info: *const u8,
                infolen: c_int,
            ) -> c_int;
        }
    } else {
        #[cfg(ossl111)]
        pub unsafe fn EVP_PKEY_CTX_set_hkdf_mode(ctx: *mut EVP_PKEY_CTX, mode: c_int) -> c_int {
            EVP_PKEY_CTX_ctrl(
                ctx,
                -1,
                EVP_PKEY_OP_DERIVE,
                EVP_PKEY_CTRL_HKDF_MODE,
                mode, std::ptr::null_mut(),
            )
        }

        #[cfg(ossl110)]
        pub unsafe fn EVP_PKEY_CTX_set_hkdf_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_HKDF_MD,
                0,
                md as *mut c_void,
            )
        }

        #[cfg(ossl110)]
        pub unsafe fn EVP_PKEY_CTX_set1_hkdf_salt(
            ctx: *mut EVP_PKEY_CTX,
            salt: *const u8,
            saltlen: c_int,
        ) -> c_int {
            EVP_PKEY_CTX_ctrl(
                ctx,
                -1,
                EVP_PKEY_OP_DERIVE,
                EVP_PKEY_CTRL_HKDF_SALT,
                saltlen,
                salt as *mut c_void,
            )
        }

        #[cfg(ossl110)]
        pub unsafe fn EVP_PKEY_CTX_set1_hkdf_key(
            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_HKDF_KEY,
                keylen,
                key as *mut c_void,
            )
        }

        #[cfg(ossl110)]
        pub unsafe fn EVP_PKEY_CTX_add1_hkdf_info(
            ctx: *mut EVP_PKEY_CTX,
            info: *const u8,
            infolen: c_int,
        ) -> c_int {
            EVP_PKEY_CTX_ctrl(
                ctx,
                -1,
                EVP_PKEY_OP_DERIVE,
                EVP_PKEY_CTRL_HKDF_INFO,
                infolen,
                info as *mut c_void,
            )
        }
    }
}

const_ptr_api! {
    extern "C" {
        pub fn EVP_PKCS82PKEY(p8: #[const_ptr_if(any(ossl110, libressl280))] PKCS8_PRIV_KEY_INFO) -> *mut EVP_PKEY;
+2 −0
Original line number Diff line number Diff line
@@ -916,6 +916,8 @@ pub const NID_aes_256_cbc_hmac_sha1: c_int = 918;
pub const NID_X25519: c_int = 1034;
#[cfg(ossl111)]
pub const NID_X448: c_int = 1035;
#[cfg(ossl110)]
pub const NID_hkdf: c_int = 1036;
#[cfg(ossl111)]
pub const NID_ED25519: c_int = 1087;
#[cfg(ossl111)]
+3 −0
Original line number Diff line number Diff line
@@ -83,6 +83,9 @@ impl Id {
    pub const DH: Id = Id(ffi::EVP_PKEY_DH);
    pub const EC: Id = Id(ffi::EVP_PKEY_EC);

    #[cfg(ossl110)]
    pub const HKDF: Id = Id(ffi::EVP_PKEY_HKDF);

    #[cfg(ossl111)]
    pub const ED25519: Id = Id(ffi::EVP_PKEY_ED25519);
    #[cfg(ossl111)]
+210 −36
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@
//! use openssl::cipher::Cipher;
//!
//! let mut ctx = PkeyCtx::new_id(Id::CMAC).unwrap();
//! ctx.keygen_init();
//! ctx.keygen_init().unwrap();
//! ctx.set_keygen_cipher(Cipher::aes_128_cbc()).unwrap();
//! ctx.set_keygen_mac_key(b"0123456789abcdef").unwrap();
//! let cmac_key = ctx.keygen().unwrap();
@@ -45,6 +45,17 @@ use openssl_macros::corresponds;
use std::convert::TryFrom;
use std::ptr;

/// HKDF modes of operation.
#[cfg(ossl111)]
pub struct HkdfMode(c_int);

#[cfg(ossl111)]
impl HkdfMode {
    pub const EXTRACT_THEN_EXPAND: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND);
    pub const EXTRACT_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY);
    pub const EXPAND_ONLY: Self = HkdfMode(ffi::EVP_PKEY_HKDEF_MODE_EXPAND_ONLY);
}

generic_foreign_type_and_impl_send_sync! {
    type CType = ffi::EVP_PKEY_CTX;
    fn drop = ffi::EVP_PKEY_CTX_free;
@@ -141,17 +152,6 @@ where
        Ok(())
    }

    /// Prepares the context for shared secret derivation.
    #[corresponds(EVP_PKEY_derive_init)]
    #[inline]
    pub fn derive_init(&mut self) -> Result<(), ErrorStack> {
        unsafe {
            cvt(ffi::EVP_PKEY_derive_init(self.as_ptr()))?;
        }

        Ok(())
    }

    /// Sets the peer key used for secret derivation.
    #[corresponds(EVP_PKEY_derive_set_peer)]
    pub fn derive_set_peer<U>(&mut self, key: &PKeyRef<U>) -> Result<(), ErrorStack>
@@ -195,36 +195,20 @@ where
        out.truncate(base + len);
        Ok(len)
    }

    /// Derives a shared secrete between two keys.
    ///
    /// If `buf` is set to `None`, an upper bound on the number of bytes required for the buffer will be returned.
    #[corresponds(EVP_PKEY_derive)]
    pub fn derive(&mut self, buf: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
        let mut len = buf.as_ref().map_or(0, |b| b.len());
        unsafe {
            cvt(ffi::EVP_PKEY_derive(
                self.as_ptr(),
                buf.map_or(ptr::null_mut(), |b| b.as_mut_ptr()),
                &mut len,
            ))?;
}

        Ok(len)
impl<T> PkeyCtxRef<T> {
    /// Prepares the context for shared secret derivation.
    #[corresponds(EVP_PKEY_derive_init)]
    #[inline]
    pub fn derive_init(&mut self) -> Result<(), ErrorStack> {
        unsafe {
            cvt(ffi::EVP_PKEY_derive_init(self.as_ptr()))?;
        }

    /// Like [`Self::derive`] but appends the secret to a [`Vec`].
    pub fn derive_to_vec(&mut self, buf: &mut Vec<u8>) -> Result<usize, ErrorStack> {
        let base = buf.len();
        let len = self.derive(None)?;
        buf.resize(base + len, 0);
        let len = self.derive(Some(&mut buf[base..]))?;
        buf.truncate(base + len);
        Ok(len)
    }
        Ok(())
    }

impl<T> PkeyCtxRef<T> {
    /// Prepares the context for key generation.
    #[corresponds(EVP_PKEY_keygen_init)]
    pub fn keygen_init(&mut self) -> Result<(), ErrorStack> {
@@ -322,6 +306,7 @@ impl<T> PkeyCtxRef<T> {

    /// Sets the cipher used during key generation.
    #[corresponds(EVP_PKEY_CTX_ctrl)]
    #[inline]
    pub fn set_keygen_cipher(&mut self, cipher: &CipherRef) -> Result<(), ErrorStack> {
        unsafe {
            cvt(ffi::EVP_PKEY_CTX_ctrl(
@@ -339,6 +324,7 @@ impl<T> PkeyCtxRef<T> {

    /// Sets the key MAC key used during key generation.
    #[corresponds(EVP_PKEY_CTX_ctrl)]
    #[inline]
    pub fn set_keygen_mac_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> {
        let len = c_int::try_from(key.len()).unwrap();

@@ -356,8 +342,129 @@ impl<T> PkeyCtxRef<T> {
        Ok(())
    }

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

        Ok(())
    }

    /// Sets the HKDF mode of operation.
    ///
    /// Defaults to [`HkdfMode::EXTRACT_THEN_EXPAND`].
    ///
    /// Requires OpenSSL 1.1.1 or newer.
    #[corresponds(EVP_PKEY_CTX_set_hkdf_mode)]
    #[cfg(ossl111)]
    #[inline]
    pub fn set_hkdf_mode(&mut self, mode: HkdfMode) -> Result<(), ErrorStack> {
        unsafe {
            cvt(ffi::EVP_PKEY_CTX_set_hkdf_mode(self.as_ptr(), mode.0))?;
        }

        Ok(())
    }

    /// Sets the input keying material for HKDF generation.
    ///
    /// Requires OpenSSL 1.1.0 or newer.
    #[corresponds(EVP_PKEY_CTX_set1_hkdf_key)]
    #[cfg(ossl110)]
    #[inline]
    pub fn set_hkdf_key(&mut self, key: &[u8]) -> Result<(), ErrorStack> {
        let len = c_int::try_from(key.len()).unwrap();

        unsafe {
            cvt(ffi::EVP_PKEY_CTX_set1_hkdf_key(
                self.as_ptr(),
                key.as_ptr(),
                len,
            ))?;
        }

        Ok(())
    }

    /// Sets the salt value for HKDF generation.
    ///
    /// Requires OpenSSL 1.1.0 or newer.
    #[corresponds(EVP_PKEY_CTX_set1_hkdf_salt)]
    #[cfg(ossl110)]
    #[inline]
    pub fn set_hkdf_salt(&mut self, salt: &[u8]) -> Result<(), ErrorStack> {
        let len = c_int::try_from(salt.len()).unwrap();

        unsafe {
            cvt(ffi::EVP_PKEY_CTX_set1_hkdf_salt(
                self.as_ptr(),
                salt.as_ptr(),
                len,
            ))?;
        }

        Ok(())
    }

    /// Appends info bytes for HKDF generation.
    ///
    /// Requires OpenSSL 1.1.0 or newer.
    #[corresponds(EVP_PKEY_CTX_add1_hkdf_info)]
    #[cfg(ossl110)]
    #[inline]
    pub fn add_hkdf_info(&mut self, info: &[u8]) -> Result<(), ErrorStack> {
        let len = c_int::try_from(info.len()).unwrap();

        unsafe {
            cvt(ffi::EVP_PKEY_CTX_add1_hkdf_info(
                self.as_ptr(),
                info.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.
    #[corresponds(EVP_PKEY_derive)]
    pub fn derive(&mut self, buf: Option<&mut [u8]>) -> Result<usize, ErrorStack> {
        let mut len = buf.as_ref().map_or(0, |b| b.len());
        unsafe {
            cvt(ffi::EVP_PKEY_derive(
                self.as_ptr(),
                buf.map_or(ptr::null_mut(), |b| b.as_mut_ptr()),
                &mut len,
            ))?;
        }

        Ok(len)
    }

    /// Like [`Self::derive`] but appends the secret to a [`Vec`].
    pub fn derive_to_vec(&mut self, buf: &mut Vec<u8>) -> Result<usize, ErrorStack> {
        let base = buf.len();
        let len = self.derive(None)?;
        buf.resize(base + len, 0);
        let len = self.derive(Some(&mut buf[base..]))?;
        buf.truncate(base + len);
        Ok(len)
    }

    /// Generates a new public/private keypair.
    #[corresponds(EVP_PKEY_keygen)]
    #[inline]
    pub fn keygen(&mut self) -> Result<PKey<Private>, ErrorStack> {
        unsafe {
            let mut key = ptr::null_mut();
@@ -454,4 +561,71 @@ mod test {
            .unwrap();
        ctx.keygen().unwrap();
    }

    #[test]
    #[cfg(ossl110)]
    fn hkdf() {
        let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
        ctx.derive_init().unwrap();
        ctx.set_hkdf_md(Md::sha256()).unwrap();
        ctx.set_hkdf_key(&hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap())
            .unwrap();
        ctx.set_hkdf_salt(&hex::decode("000102030405060708090a0b0c").unwrap())
            .unwrap();
        ctx.add_hkdf_info(&hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap())
            .unwrap();
        let mut out = [0; 42];
        ctx.derive(Some(&mut out)).unwrap();

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

    #[test]
    #[cfg(ossl111)]
    fn hkdf_expand() {
        let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
        ctx.derive_init().unwrap();
        ctx.set_hkdf_mode(HkdfMode::EXPAND_ONLY).unwrap();
        ctx.set_hkdf_md(Md::sha256()).unwrap();
        ctx.set_hkdf_key(
            &hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5")
                .unwrap(),
        )
        .unwrap();
        ctx.add_hkdf_info(&hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap())
            .unwrap();
        let mut out = [0; 42];
        ctx.derive(Some(&mut out)).unwrap();

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

    #[test]
    #[cfg(ossl111)]
    fn hkdf_extract() {
        let mut ctx = PkeyCtx::new_id(Id::HKDF).unwrap();
        ctx.derive_init().unwrap();
        ctx.set_hkdf_mode(HkdfMode::EXTRACT_ONLY).unwrap();
        ctx.set_hkdf_md(Md::sha256()).unwrap();
        ctx.set_hkdf_key(&hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap())
            .unwrap();
        ctx.set_hkdf_salt(&hex::decode("000102030405060708090a0b0c").unwrap())
            .unwrap();
        let mut out = vec![];
        ctx.derive_to_vec(&mut out).unwrap();

        assert_eq!(
            &out[..],
            hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5")
                .unwrap()
        );
    }
}
+4 −1
Original line number Diff line number Diff line
@@ -63,8 +63,11 @@ fn main() {
        .header("openssl/evp.h")
        .header("openssl/x509_vfy.h");

    if openssl_version.is_some() {
    if let Some(version) = openssl_version {
        cfg.header("openssl/cms.h");
        if version >= 0x010100000 {
            cfg.header("openssl/kdf.h");
        }
    }

    #[allow(clippy::if_same_then_else)]