1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use thiserror::Error;

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum KdfError {
    #[error("OpenSSL error: {0:?}")]
    OpenSSL(#[from] openssl::error::ErrorStack),
    #[error("Unsupported option for current backend: {0}")]
    UnsupportedOption(String),
    #[error("Unimplemented option for current backend: {0}")]
    Unimplemented(&'static str),
    #[error("Required option not specified: {0}")]
    MissingArgument(&'static str),
    #[error("Invalid option provided: {0}")]
    InvalidOption(&'static str),
}

#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum KdfKbMode {
    Counter,
    Feedback,
}

#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum KdfMacType {
    Hmac(openssl::hash::MessageDigest),
    Cmac(openssl::symm::Cipher),
}

impl KdfMacType {
    #[allow(unused)]
    fn has_md(&self) -> bool {
        matches!(self, KdfMacType::Hmac(_))
    }

    #[allow(unused)]
    fn get_md(&self) -> Option<&openssl::hash::MessageDigest> {
        match self {
            KdfMacType::Hmac(md) => Some(md),
            KdfMacType::Cmac(_) => None,
        }
    }

    #[allow(unused)]
    fn has_cipher(&self) -> bool {
        matches!(self, KdfMacType::Cmac(_))
    }

    #[allow(unused)]
    fn get_cipher(&self) -> Option<&openssl::symm::Cipher> {
        match self {
            KdfMacType::Cmac(cipher) => Some(cipher),
            KdfMacType::Hmac(_) => None,
        }
    }
}

impl std::fmt::Debug for KdfMacType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            KdfMacType::Hmac(md) => write!(f, "Hmac({:?})", md.type_().long_name()),
            KdfMacType::Cmac(cipher) => write!(f, "Cmac({:?})", cipher.nid().long_name()),
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum KdfType {
    KeyBased,
}

#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum KdfArgument<'a> {
    Key(&'a [u8]),
    // Called "Label" in SP800-108
    Salt(&'a [u8]),
    // Called "Context" in SP800-108
    KbInfo(&'a [u8]),

    KbSeed(&'a [u8]),

    R(u8),
    UseSeparator(bool),
    UseL(bool),
    LBits(u8),

    Mac(KdfMacType),
    KbMode(KdfKbMode),
}

pub fn perform_kdf<'a>(
    type_: KdfType,
    args: &[&'a KdfArgument],
    length: usize,
) -> Result<Vec<u8>, KdfError> {
    let mut last_result = None;
    for implementation in AVAILABLE_IMPLEMENTATIONS {
        last_result = Some(implementation(type_, args, length));
        match last_result {
            Some(Err(KdfError::Unimplemented(_))) => continue,
            Some(Err(KdfError::UnsupportedOption(_))) => continue,
            Some(_) => break,
            None => unreachable!(),
        }
    }

    if let Some(result) = last_result {
        result
    } else {
        Err(KdfError::Unimplemented("No implementation available"))
    }
}

#[cfg(implementation = "custom")]
mod custom;
#[cfg(implementation = "ossl11")]
mod ossl11;
#[cfg(implementation = "ossl3")]
mod ossl3;

type ImplFunc = dyn Fn(KdfType, &[&KdfArgument], usize) -> Result<Vec<u8>, KdfError>;

const AVAILABLE_IMPLEMENTATIONS: &[&ImplFunc] = &[
    #[cfg(implementation = "ossl11")]
    &ossl11::perform,
    #[cfg(implementation = "ossl3")]
    &ossl3::perform,
    #[cfg(implementation = "custom")]
    &custom::perform,
];

#[cfg(test)]
mod test;