use crate::error::ErrorStack;
use native_ossl_sys as sys;
use std::ffi::CStr;
pub struct KdfAlg {
ptr: *mut sys::EVP_KDF,
}
impl KdfAlg {
pub fn fetch(name: &CStr) -> Result<Self, ErrorStack> {
let ptr =
unsafe { sys::EVP_KDF_fetch(std::ptr::null_mut(), name.as_ptr(), std::ptr::null()) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(KdfAlg { ptr })
}
fn as_ptr(&self) -> *mut sys::EVP_KDF {
self.ptr
}
}
impl Drop for KdfAlg {
fn drop(&mut self) {
unsafe { sys::EVP_KDF_free(self.ptr) };
}
}
unsafe impl Send for KdfAlg {}
unsafe impl Sync for KdfAlg {}
pub struct KdfCtx {
ptr: *mut sys::EVP_KDF_CTX,
}
impl KdfCtx {
pub fn new(alg: &KdfAlg) -> Result<Self, ErrorStack> {
let ptr = unsafe { sys::EVP_KDF_CTX_new(alg.as_ptr()) };
if ptr.is_null() {
return Err(ErrorStack::drain());
}
Ok(KdfCtx { ptr })
}
pub fn derive(
&mut self,
out: &mut [u8],
params: &crate::params::Params<'_>,
) -> Result<(), ErrorStack> {
crate::ossl_call!(sys::EVP_KDF_derive(
self.ptr,
out.as_mut_ptr(),
out.len(),
params.as_ptr()
))
}
}
impl Drop for KdfCtx {
fn drop(&mut self) {
unsafe { sys::EVP_KDF_CTX_free(self.ptr) };
}
}
unsafe impl Send for KdfCtx {}
#[derive(Default, Clone, Copy, PartialEq, Eq)]
pub enum HkdfMode {
#[default]
ExtractAndExpand,
ExtractOnly,
ExpandOnly,
}
impl HkdfMode {
fn as_uint(self) -> u32 {
match self {
HkdfMode::ExtractAndExpand => 0,
HkdfMode::ExtractOnly => 1,
HkdfMode::ExpandOnly => 2,
}
}
}
pub struct HkdfBuilder<'a> {
digest: &'a crate::digest::DigestAlg,
key: Option<&'a [u8]>,
salt: Option<&'a [u8]>,
info: Option<&'a [u8]>,
mode: HkdfMode,
}
impl<'a> HkdfBuilder<'a> {
#[must_use]
pub fn new(digest: &'a crate::digest::DigestAlg) -> Self {
HkdfBuilder {
digest,
key: None,
salt: None,
info: None,
mode: HkdfMode::default(),
}
}
#[must_use]
pub fn key(mut self, key: &'a [u8]) -> Self {
self.key = Some(key);
self
}
#[must_use]
pub fn salt(mut self, salt: &'a [u8]) -> Self {
self.salt = Some(salt);
self
}
#[must_use]
pub fn info(mut self, info: &'a [u8]) -> Self {
self.info = Some(info);
self
}
#[must_use]
pub fn mode(mut self, mode: HkdfMode) -> Self {
self.mode = mode;
self
}
pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
if name_ptr.is_null() {
return Err(ErrorStack::drain());
}
let name = unsafe { CStr::from_ptr(name_ptr) };
let mut builder = crate::params::ParamBuilder::new()?
.push_utf8_string(c"digest", name)?
.push_uint(c"mode", self.mode.as_uint())?;
if let Some(k) = self.key {
builder = builder.push_octet_slice(c"key", k)?;
}
if let Some(s) = self.salt {
builder = builder.push_octet_slice(c"salt", s)?;
}
if let Some(i) = self.info {
builder = builder.push_octet_slice(c"info", i)?;
}
let params = builder.build()?;
let alg = KdfAlg::fetch(c"HKDF")?;
KdfCtx::new(&alg)?.derive(out, ¶ms)
}
pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
let mut out = vec![0u8; len];
self.derive(&mut out)?;
Ok(out)
}
}
pub struct Pbkdf2Builder<'a> {
digest: &'a crate::digest::DigestAlg,
password: &'a [u8],
salt: &'a [u8],
iterations: u32,
}
impl<'a> Pbkdf2Builder<'a> {
#[must_use]
pub fn new(digest: &'a crate::digest::DigestAlg, password: &'a [u8], salt: &'a [u8]) -> Self {
Pbkdf2Builder {
digest,
password,
salt,
iterations: 600_000,
}
}
#[must_use]
pub fn iterations(mut self, n: u32) -> Self {
self.iterations = n;
self
}
pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
if name_ptr.is_null() {
return Err(ErrorStack::drain());
}
let name = unsafe { CStr::from_ptr(name_ptr) };
let params = crate::params::ParamBuilder::new()?
.push_octet_slice(c"pass", self.password)?
.push_octet_slice(c"salt", self.salt)?
.push_uint(c"iter", self.iterations)?
.push_utf8_string(c"digest", name)?
.build()?;
let alg = KdfAlg::fetch(c"PBKDF2")?;
KdfCtx::new(&alg)?.derive(out, ¶ms)
}
pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
let mut out = vec![0u8; len];
self.derive(&mut out)?;
Ok(out)
}
}
pub struct ScryptParams {
pub n: u64,
pub r: u32,
pub p: u32,
}
impl Default for ScryptParams {
fn default() -> Self {
ScryptParams {
n: 16_384,
r: 8,
p: 1,
}
}
}
pub struct ScryptBuilder<'a> {
password: &'a [u8],
salt: &'a [u8],
params: ScryptParams,
}
impl<'a> ScryptBuilder<'a> {
#[must_use]
pub fn new(password: &'a [u8], salt: &'a [u8]) -> Self {
ScryptBuilder {
password,
salt,
params: ScryptParams::default(),
}
}
#[must_use]
pub fn params(mut self, params: ScryptParams) -> Self {
self.params = params;
self
}
pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
let params = crate::params::ParamBuilder::new()?
.push_octet_slice(c"pass", self.password)?
.push_octet_slice(c"salt", self.salt)?
.push_uint64(c"n", self.params.n)?
.push_uint(c"r", self.params.r)?
.push_uint(c"p", self.params.p)?
.build()?;
let alg = KdfAlg::fetch(c"SCRYPT")?;
KdfCtx::new(&alg)?.derive(out, ¶ms)
}
pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
let mut out = vec![0u8; len];
self.derive(&mut out)?;
Ok(out)
}
}
#[cfg(ossl350)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SshkdfKeyType {
InitialIvClientToServer,
InitialIvServerToClient,
EncryptionKeyClientToServer,
EncryptionKeyServerToClient,
IntegrityKeyClientToServer,
IntegrityKeyServerToClient,
}
#[cfg(ossl350)]
impl SshkdfKeyType {
fn as_cstr(self) -> &'static CStr {
match self {
Self::InitialIvClientToServer => c"A",
Self::InitialIvServerToClient => c"B",
Self::EncryptionKeyClientToServer => c"C",
Self::EncryptionKeyServerToClient => c"D",
Self::IntegrityKeyClientToServer => c"E",
Self::IntegrityKeyServerToClient => c"F",
}
}
}
#[cfg(ossl350)]
pub struct SshkdfBuilder<'a> {
digest: &'a crate::digest::DigestAlg,
key: &'a [u8],
xcghash: &'a [u8],
session_id: &'a [u8],
key_type: SshkdfKeyType,
}
#[cfg(ossl350)]
impl<'a> SshkdfBuilder<'a> {
#[must_use]
pub fn new(
digest: &'a crate::digest::DigestAlg,
key: &'a [u8],
xcghash: &'a [u8],
session_id: &'a [u8],
key_type: SshkdfKeyType,
) -> Self {
SshkdfBuilder {
digest,
key,
xcghash,
session_id,
key_type,
}
}
pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
let name_ptr = unsafe { sys::OBJ_nid2sn(self.digest.nid()) };
if name_ptr.is_null() {
return Err(ErrorStack::drain());
}
let name = unsafe { CStr::from_ptr(name_ptr) };
let params = crate::params::ParamBuilder::new()?
.push_utf8_string(c"digest", name)?
.push_octet_slice(c"key", self.key)?
.push_octet_slice(c"xcghash", self.xcghash)?
.push_octet_slice(c"session-id", self.session_id)?
.push_utf8_string(c"type", self.key_type.as_cstr())?
.build()?;
let alg = KdfAlg::fetch(c"SSHKDF")?;
KdfCtx::new(&alg)?.derive(out, ¶ms)
}
pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
let mut out = vec![0u8; len];
self.derive(&mut out)?;
Ok(out)
}
}
#[cfg(ossl350)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KbkdfMode {
Counter,
Feedback,
}
#[cfg(ossl350)]
impl KbkdfMode {
fn as_cstr(self) -> &'static CStr {
match self {
KbkdfMode::Counter => c"counter",
KbkdfMode::Feedback => c"feedback",
}
}
}
#[cfg(ossl350)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(ossl350, derive(Default))]
pub enum KbkdfCounterLen {
Bits8 = 8,
Bits16 = 16,
Bits24 = 24,
#[cfg_attr(ossl350, default)]
Bits32 = 32,
}
#[cfg(ossl350)]
pub struct KbkdfBuilder<'a> {
mode: KbkdfMode,
mac: &'a crate::mac::MacAlg,
digest: Option<&'a crate::digest::DigestAlg>,
key: &'a [u8],
label: Option<&'a [u8]>,
context: Option<&'a [u8]>,
salt: Option<&'a [u8]>,
counter_len: KbkdfCounterLen,
use_l: Option<bool>,
use_separator: Option<bool>,
}
#[cfg(ossl350)]
impl<'a> KbkdfBuilder<'a> {
#[must_use]
pub fn new(mode: KbkdfMode, mac: &'a crate::mac::MacAlg, key: &'a [u8]) -> Self {
KbkdfBuilder {
mode,
mac,
digest: None,
key,
label: None,
context: None,
salt: None,
counter_len: KbkdfCounterLen::default(),
use_l: None,
use_separator: None,
}
}
#[must_use]
pub fn digest(mut self, digest: &'a crate::digest::DigestAlg) -> Self {
self.digest = Some(digest);
self
}
#[must_use]
pub fn label(mut self, label: &'a [u8]) -> Self {
self.label = Some(label);
self
}
#[must_use]
pub fn context(mut self, context: &'a [u8]) -> Self {
self.context = Some(context);
self
}
#[must_use]
pub fn salt(mut self, salt: &'a [u8]) -> Self {
self.salt = Some(salt);
self
}
#[must_use]
pub fn counter_len(mut self, len: KbkdfCounterLen) -> Self {
self.counter_len = len;
self
}
#[must_use]
pub fn use_l(mut self, enabled: bool) -> Self {
self.use_l = Some(enabled);
self
}
#[must_use]
pub fn use_separator(mut self, enabled: bool) -> Self {
self.use_separator = Some(enabled);
self
}
pub fn derive(self, out: &mut [u8]) -> Result<(), ErrorStack> {
let mut builder = crate::params::ParamBuilder::new()?
.push_utf8_string(c"mode", self.mode.as_cstr())?
.push_utf8_string(c"mac", self.mac.name())?
.push_octet_slice(c"key", self.key)?
.push_uint(c"r", self.counter_len as u32)?;
if let Some(d) = self.digest {
let name_ptr = unsafe { sys::OBJ_nid2sn(d.nid()) };
if name_ptr.is_null() {
return Err(ErrorStack::drain());
}
let name = unsafe { CStr::from_ptr(name_ptr) };
builder = builder.push_utf8_string(c"digest", name)?;
}
if let Some(l) = self.label {
builder = builder.push_octet_slice(c"label", l)?;
}
if let Some(c) = self.context {
builder = builder.push_octet_slice(c"data", c)?;
}
if let Some(s) = self.salt {
builder = builder.push_octet_slice(c"salt", s)?;
}
if let Some(v) = self.use_l {
builder = builder.push_int(c"use-l", i32::from(v))?;
}
if let Some(v) = self.use_separator {
builder = builder.push_int(c"use-separator", i32::from(v))?;
}
let params = builder.build()?;
let alg = KdfAlg::fetch(c"KBKDF")?;
KdfCtx::new(&alg)?.derive(out, ¶ms)
}
pub fn derive_to_vec(self, len: usize) -> Result<Vec<u8>, ErrorStack> {
let mut out = vec![0u8; len];
self.derive(&mut out)?;
Ok(out)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Pkcs12KdfId {
Key = 1,
Iv = 2,
Mac = 3,
}
pub struct Pkcs12KdfBuilder<'a> {
md: &'a crate::digest::DigestAlg,
password: &'a [u8],
salt: &'a [u8],
id: Pkcs12KdfId,
iter: u32,
}
impl<'a> Pkcs12KdfBuilder<'a> {
#[must_use]
pub fn new(
md: &'a crate::digest::DigestAlg,
password: &'a [u8],
salt: &'a [u8],
id: Pkcs12KdfId,
) -> Self {
Self {
md,
password,
salt,
id,
iter: 2048,
}
}
#[must_use]
pub fn iterations(mut self, n: u32) -> Self {
self.iter = n;
self
}
pub fn derive(&self, out: &mut [u8]) -> Result<(), ErrorStack> {
let rc = unsafe {
sys::PKCS12_key_gen_utf8(
self.password.as_ptr().cast(),
i32::try_from(self.password.len()).expect("password too long"),
self.salt.as_ptr().cast_mut(),
i32::try_from(self.salt.len()).expect("salt too long"),
self.id as std::ffi::c_int,
self.iter.cast_signed(),
i32::try_from(out.len()).expect("output too long"),
out.as_mut_ptr(),
self.md.as_ptr(),
)
};
if rc != 1 {
return Err(ErrorStack::drain());
}
Ok(())
}
pub fn derive_to_vec(&self, len: usize) -> Result<Vec<u8>, ErrorStack> {
let mut out = vec![0u8; len];
self.derive(&mut out)?;
Ok(out)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::digest::DigestAlg;
#[test]
fn hkdf_sha256_rfc5869_tc1() {
let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
let ikm = [0x0b_u8; 22];
let salt = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c_u8,
];
let info = [
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9_u8,
];
let okm = HkdfBuilder::new(&digest)
.key(&ikm)
.salt(&salt)
.info(&info)
.derive_to_vec(42)
.unwrap();
assert_eq!(
hex::encode(&okm),
"3cb25f25faacd57a90434f64d0362f2a\
2d2d0a90cf1a5a4c5db02d56ecc4c5bf\
34007208d5b887185865"
);
}
#[test]
fn hkdf_sha256_no_salt_no_info() {
let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
let ikm = [0x0b_u8; 22];
let okm = HkdfBuilder::new(&digest)
.key(&ikm)
.derive_to_vec(42)
.unwrap();
assert_eq!(
hex::encode(&okm),
"8da4e775a563c18f715f802a063c5a31\
b8a11f5c5ee1879ec3454e5f3c738d2d\
9d201395faa4b61a96c8"
);
}
#[test]
fn pbkdf2_sha256_known_answer() {
let digest = DigestAlg::fetch(c"SHA2-256", None).unwrap();
let dk = Pbkdf2Builder::new(&digest, b"password", b"salt")
.iterations(1)
.derive_to_vec(32)
.unwrap();
assert_eq!(
hex::encode(&dk),
"120fb6cffcf8b32c43e7225256c4f837\
a86548c92ccc35480805987cb70be17b"
);
}
#[test]
fn scrypt_derives_nonzero_output() {
let dk = ScryptBuilder::new(b"password", b"salt")
.params(ScryptParams { n: 32, r: 1, p: 1 })
.derive_to_vec(32)
.unwrap();
assert_eq!(dk.len(), 32);
assert_ne!(dk, vec![0u8; 32]);
}
#[test]
fn scrypt_different_passwords_differ() {
let p = ScryptParams { n: 32, r: 1, p: 1 };
let dk1 = ScryptBuilder::new(b"pass1", b"salt")
.params(ScryptParams {
n: p.n,
r: p.r,
p: p.p,
})
.derive_to_vec(32)
.unwrap();
let dk2 = ScryptBuilder::new(b"pass2", b"salt")
.params(ScryptParams {
n: p.n,
r: p.r,
p: p.p,
})
.derive_to_vec(32)
.unwrap();
assert_ne!(dk1, dk2);
}
#[test]
fn pkcs12_kdf_basic() {
let sha1 = DigestAlg::fetch(c"SHA1", None).unwrap();
let salt = b"saltsalt";
let key = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Key)
.iterations(2048)
.derive_to_vec(24)
.unwrap();
assert_eq!(key.len(), 24);
assert_ne!(key, vec![0u8; 24]);
let key2 = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Key)
.iterations(2048)
.derive_to_vec(24)
.unwrap();
assert_eq!(key, key2);
let iv = Pkcs12KdfBuilder::new(&sha1, b"password", salt, Pkcs12KdfId::Iv)
.iterations(2048)
.derive_to_vec(8)
.unwrap();
assert_ne!(key[..8], iv[..]);
}
#[test]
fn pkcs12_kdf_different_passwords_differ() {
let sha1 = DigestAlg::fetch(c"SHA1", None).unwrap();
let salt = b"saltsalt";
let k1 = Pkcs12KdfBuilder::new(&sha1, b"pass1", salt, Pkcs12KdfId::Key)
.derive_to_vec(24)
.unwrap();
let k2 = Pkcs12KdfBuilder::new(&sha1, b"pass2", salt, Pkcs12KdfId::Key)
.derive_to_vec(24)
.unwrap();
assert_ne!(k1, k2);
}
}