openssl_kdf/
lib.rs

1use thiserror::Error;
2
3#[derive(Debug, Error)]
4#[non_exhaustive]
5pub enum KdfError {
6    #[error("OpenSSL error: {0:?}")]
7    OpenSSL(#[from] openssl::error::ErrorStack),
8    #[error("Unsupported option for current backend: {0}")]
9    UnsupportedOption(String),
10    #[error("Unimplemented option for current backend: {0}")]
11    Unimplemented(&'static str),
12    #[error("Required option not specified: {0}")]
13    MissingArgument(&'static str),
14    #[error("Invalid option provided: {0}")]
15    InvalidOption(&'static str),
16}
17
18#[derive(Debug, Clone, Copy)]
19#[non_exhaustive]
20pub enum KdfKbMode {
21    Counter,
22    Feedback,
23}
24
25#[derive(Clone, Copy)]
26#[non_exhaustive]
27pub enum KdfMacType {
28    Hmac(openssl::hash::MessageDigest),
29    Cmac(openssl::symm::Cipher),
30}
31
32impl KdfMacType {
33    #[allow(unused)]
34    fn has_md(&self) -> bool {
35        matches!(self, KdfMacType::Hmac(_))
36    }
37
38    #[allow(unused)]
39    fn get_md(&self) -> Option<&openssl::hash::MessageDigest> {
40        match self {
41            KdfMacType::Hmac(md) => Some(md),
42            KdfMacType::Cmac(_) => None,
43        }
44    }
45
46    #[allow(unused)]
47    fn has_cipher(&self) -> bool {
48        matches!(self, KdfMacType::Cmac(_))
49    }
50
51    #[allow(unused)]
52    fn get_cipher(&self) -> Option<&openssl::symm::Cipher> {
53        match self {
54            KdfMacType::Cmac(cipher) => Some(cipher),
55            KdfMacType::Hmac(_) => None,
56        }
57    }
58}
59
60impl std::fmt::Debug for KdfMacType {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        match self {
63            KdfMacType::Hmac(md) => write!(f, "Hmac({:?})", md.type_().long_name()),
64            KdfMacType::Cmac(cipher) => write!(f, "Cmac({:?})", cipher.nid().long_name()),
65        }
66    }
67}
68
69#[derive(Debug, Clone, Copy)]
70#[non_exhaustive]
71pub enum KdfType {
72    KeyBased,
73}
74
75#[derive(Debug, Clone)]
76#[non_exhaustive]
77pub enum KdfArgument<'a> {
78    Key(&'a [u8]),
79    // Called "Label" in SP800-108
80    Salt(&'a [u8]),
81    // Called "Context" in SP800-108
82    KbInfo(&'a [u8]),
83
84    KbSeed(&'a [u8]),
85
86    R(u8),
87    UseSeparator(bool),
88    UseL(bool),
89    LBits(u8),
90
91    Mac(KdfMacType),
92    KbMode(KdfKbMode),
93}
94
95pub fn perform_kdf<'a>(
96    type_: KdfType,
97    args: &[&'a KdfArgument],
98    length: usize,
99) -> Result<Vec<u8>, KdfError> {
100    let mut last_result = None;
101    for implementation in AVAILABLE_IMPLEMENTATIONS {
102        last_result = Some((implementation.func)(type_, args, length));
103        match last_result {
104            Some(Err(KdfError::Unimplemented(_))) => continue,
105            Some(Err(KdfError::UnsupportedOption(_))) => continue,
106            Some(_) => break,
107            None => unreachable!(),
108        }
109    }
110
111    if let Some(result) = last_result {
112        result
113    } else {
114        Err(KdfError::Unimplemented("No implementation available"))
115    }
116}
117
118pub fn supports_args<'a>(args: &[&'a KdfArgument]) -> bool {
119    for implementation in AVAILABLE_IMPLEMENTATIONS {
120        if (implementation.supports_args)(args) {
121            return true;
122        }
123    }
124
125    false
126}
127
128struct Implementation {
129    supports_args: &'static dyn Fn(&[&KdfArgument]) -> bool,
130    func: &'static dyn Fn(KdfType, &[&KdfArgument], usize) -> Result<Vec<u8>, KdfError>,
131}
132
133#[cfg(implementation = "custom")]
134mod custom;
135#[cfg(implementation = "ossl11")]
136mod ossl11;
137#[cfg(implementation = "ossl3")]
138mod ossl3;
139
140const AVAILABLE_IMPLEMENTATIONS: &[&Implementation] = &[
141    #[cfg(implementation = "ossl11")]
142    &ossl11::IMPLEMENTATION,
143    #[cfg(implementation = "ossl3")]
144    &ossl3::IMPLEMENTATION,
145    #[cfg(implementation = "custom")]
146    &custom::IMPLEMENTATION,
147];
148
149#[cfg(test)]
150mod test;