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 Salt(&'a [u8]),
81 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;