diff --git a/src/hkdf.rs b/src/hkdf.rs
new file mode 100644
index 0000000..cc7e5b3
--- /dev/null
+++ b/src/hkdf.rs
@@ -0,0 +1,89 @@
+use crate::cvt;
+use crate::error::ErrorStack;
+use crate::md::MdRef;
+use foreign_types::ForeignTypeRef;
+use openssl_macros::corresponds;
+
+/// Computes HKDF (as specified by RFC 5869).
+///
+/// HKDF is an Extract-and-Expand algorithm. It does not do any key stretching,
+/// and as such, is not suited to be used alone to generate a key from a
+/// password.
+#[corresponds(HKDF)]
+#[inline]
+pub fn hkdf(
+    out_key: &mut [u8],
+    md: &MdRef,
+    secret: &[u8],
+    salt: &[u8],
+    info: &[u8],
+) -> Result<(), ErrorStack> {
+    unsafe {
+        cvt(ffi::HKDF(
+            out_key.as_mut_ptr(),
+            out_key.len(),
+            md.as_ptr(),
+            secret.as_ptr(),
+            secret.len(),
+            salt.as_ptr(),
+            salt.len(),
+            info.as_ptr(),
+            info.len(),
+        ))?;
+    }
+
+    Ok(())
+}
+
+/// Computes a HKDF PRK (as specified by RFC 5869).
+///
+/// WARNING: This function orders the inputs differently from RFC 5869
+/// specification. Double-check which parameter is the secret/IKM and which is
+/// the salt when using.
+#[corresponds(HKDF_extract)]
+#[inline]
+pub fn hkdf_extract<'a>(
+    out_key: &'a mut [u8],
+    md: &MdRef,
+    secret: &[u8],
+    salt: &[u8],
+) -> Result<&'a [u8], ErrorStack> {
+    let mut out_len = out_key.len();
+    unsafe {
+        cvt(ffi::HKDF_extract(
+            out_key.as_mut_ptr(),
+            &mut out_len,
+            md.as_ptr(),
+            secret.as_ptr(),
+            secret.len(),
+            salt.as_ptr(),
+            salt.len(),
+        ))?;
+    }
+
+    Ok(&out_key[..out_len])
+}
+
+/// Computes a HKDF OKM (as specified by RFC 5869).
+#[corresponds(HKDF_expand)]
+#[inline]
+pub fn hkdf_expand(
+    out_key: &mut [u8],
+    md: &MdRef,
+    prk: &[u8],
+    info: &[u8],
+) -> Result<(), ErrorStack> {
+    unsafe {
+        cvt(ffi::HKDF_expand(
+            out_key.as_mut_ptr(),
+            out_key.len(),
+            md.as_ptr(),
+            prk.as_ptr(),
+            prk.len(),
+            info.as_ptr(),
+            info.len(),
+        ))?;
+    }
+
+    Ok(())
+}
