Skip to main content

async_snmp/v3/crypto/
rustcrypto.rs

1use super::{CryptoError, CryptoProvider, CryptoResult};
2use crate::v3::{AuthProtocol, PrivProtocol};
3
4/// Default crypto provider backed by the RustCrypto crate ecosystem.
5///
6/// This is a stateless unit struct that dispatches to the appropriate
7/// RustCrypto implementations based on the protocol enum values.
8pub struct RustCryptoProvider;
9
10// --- Dispatch macro for auth protocol -> concrete hash type ---
11
12macro_rules! dispatch_auth {
13    ($protocol:expr, $fn:ident, $($arg:expr),*) => {
14        match $protocol {
15            AuthProtocol::Md5 => $fn::<md5::Md5>($($arg),*),
16            AuthProtocol::Sha1 => $fn::<sha1::Sha1>($($arg),*),
17            AuthProtocol::Sha224 => $fn::<sha2::Sha224>($($arg),*),
18            AuthProtocol::Sha256 => $fn::<sha2::Sha256>($($arg),*),
19            AuthProtocol::Sha384 => $fn::<sha2::Sha384>($($arg),*),
20            AuthProtocol::Sha512 => $fn::<sha2::Sha512>($($arg),*),
21        }
22    };
23}
24
25impl CryptoProvider for RustCryptoProvider {
26    fn hash(&self, protocol: AuthProtocol, data: &[u8]) -> CryptoResult<Vec<u8>> {
27        Ok(dispatch_auth!(protocol, hash_impl, data))
28    }
29
30    fn password_to_key(&self, protocol: AuthProtocol, password: &[u8]) -> CryptoResult<Vec<u8>> {
31        const EXPANSION_SIZE: usize = 1_048_576; // 1MB
32        Ok(dispatch_auth!(
33            protocol,
34            password_to_key_impl,
35            password,
36            EXPANSION_SIZE
37        ))
38    }
39
40    fn localize_key(
41        &self,
42        protocol: AuthProtocol,
43        master_key: &[u8],
44        engine_id: &[u8],
45    ) -> CryptoResult<Vec<u8>> {
46        Ok(dispatch_auth!(
47            protocol,
48            localize_key_impl,
49            master_key,
50            engine_id
51        ))
52    }
53
54    fn compute_hmac(
55        &self,
56        protocol: AuthProtocol,
57        key: &[u8],
58        slices: &[&[u8]],
59        truncate_len: usize,
60    ) -> CryptoResult<Vec<u8>> {
61        Ok(dispatch_auth!(
62            protocol,
63            compute_hmac_impl,
64            key,
65            slices,
66            truncate_len
67        ))
68    }
69
70    fn encrypt(
71        &self,
72        protocol: PrivProtocol,
73        key: &[u8],
74        iv: &[u8],
75        data: &mut [u8],
76    ) -> CryptoResult<()> {
77        match protocol {
78            PrivProtocol::Des => encrypt_des_cbc(key, iv, data),
79            PrivProtocol::Des3 => encrypt_des3_cbc(key, iv, data),
80            PrivProtocol::Aes128 | PrivProtocol::Aes192 | PrivProtocol::Aes256 => {
81                encrypt_aes_cfb(key, iv, data)
82            }
83        }
84    }
85
86    fn decrypt(
87        &self,
88        protocol: PrivProtocol,
89        key: &[u8],
90        iv: &[u8],
91        data: &mut [u8],
92    ) -> CryptoResult<()> {
93        match protocol {
94            PrivProtocol::Des => decrypt_des_cbc(key, iv, data),
95            PrivProtocol::Des3 => decrypt_des3_cbc(key, iv, data),
96            PrivProtocol::Aes128 | PrivProtocol::Aes192 | PrivProtocol::Aes256 => {
97                decrypt_aes_cfb(key, iv, data)
98            }
99        }
100    }
101}
102
103// --- Auth primitive implementations ---
104
105use digest::core_api::BlockSizeUser;
106use digest::{Digest, KeyInit, Mac, OutputSizeUser};
107
108fn hash_impl<D>(data: &[u8]) -> Vec<u8>
109where
110    D: Digest + Default,
111{
112    let mut hasher = D::new();
113    hasher.update(data);
114    hasher.finalize().to_vec()
115}
116
117fn password_to_key_impl<D>(password: &[u8], expansion_size: usize) -> Vec<u8>
118where
119    D: Digest + Default,
120{
121    if password.is_empty() {
122        return vec![0u8; <D as OutputSizeUser>::output_size()];
123    }
124
125    let mut hasher = D::new();
126
127    let mut buf = [0u8; 64];
128    let password_len = password.len();
129    let mut password_index = 0;
130    let mut count = 0;
131
132    while count < expansion_size {
133        for byte in &mut buf {
134            *byte = password[password_index];
135            password_index = (password_index + 1) % password_len;
136        }
137        hasher.update(buf);
138        count += 64;
139    }
140
141    hasher.finalize().to_vec()
142}
143
144fn localize_key_impl<D>(master_key: &[u8], engine_id: &[u8]) -> Vec<u8>
145where
146    D: Digest + Default,
147{
148    let mut hasher = D::new();
149    hasher.update(master_key);
150    hasher.update(engine_id);
151    hasher.update(master_key);
152    hasher.finalize().to_vec()
153}
154
155fn compute_hmac_impl<D>(key: &[u8], slices: &[&[u8]], truncate_len: usize) -> Vec<u8>
156where
157    D: Digest + BlockSizeUser + Clone,
158{
159    use hmac::SimpleHmac;
160
161    let mut mac =
162        <SimpleHmac<D> as KeyInit>::new_from_slice(key).expect("HMAC can take key of any size");
163    for slice in slices {
164        Mac::update(&mut mac, slice);
165    }
166    let result = mac.finalize().into_bytes();
167    result[..truncate_len].to_vec()
168}
169
170// --- Privacy primitive implementations ---
171
172fn encrypt_des_cbc(key: &[u8], iv: &[u8], data: &mut [u8]) -> CryptoResult<()> {
173    use cbc::cipher::{BlockEncryptMut, KeyIvInit};
174    type DesCbc = cbc::Encryptor<des::Des>;
175
176    let cipher = DesCbc::new_from_slices(key, iv).map_err(|_| {
177        tracing::debug!(target: "async_snmp::crypto", "DES encryption failed: invalid key length");
178        CryptoError::InvalidKeyLength
179    })?;
180    let len = data.len();
181    cipher
182        .encrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(data, len)
183        .map_err(|_| {
184            tracing::debug!(target: "async_snmp::crypto", "DES encryption failed: cipher error");
185            CryptoError::CipherError
186        })?;
187    Ok(())
188}
189
190fn decrypt_des_cbc(key: &[u8], iv: &[u8], data: &mut [u8]) -> CryptoResult<()> {
191    use cbc::cipher::{BlockDecryptMut, KeyIvInit};
192    type DesCbc = cbc::Decryptor<des::Des>;
193
194    let cipher = DesCbc::new_from_slices(key, iv).map_err(|_| {
195        tracing::debug!(target: "async_snmp::crypto", "DES decryption failed: invalid key length");
196        CryptoError::InvalidKeyLength
197    })?;
198    cipher
199        .decrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(data)
200        .map_err(|_| {
201            tracing::debug!(target: "async_snmp::crypto", "DES decryption failed: cipher error");
202            CryptoError::CipherError
203        })?;
204    Ok(())
205}
206
207fn encrypt_des3_cbc(key: &[u8], iv: &[u8], data: &mut [u8]) -> CryptoResult<()> {
208    use cbc::cipher::{BlockEncryptMut, KeyIvInit};
209    type Des3Cbc = cbc::Encryptor<des::TdesEde3>;
210
211    let cipher = Des3Cbc::new_from_slices(key, iv).map_err(|_| {
212        tracing::debug!(target: "async_snmp::crypto", "3DES encryption failed: invalid key length");
213        CryptoError::InvalidKeyLength
214    })?;
215    let len = data.len();
216    cipher
217        .encrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(data, len)
218        .map_err(|_| {
219            tracing::debug!(target: "async_snmp::crypto", "3DES encryption failed: cipher error");
220            CryptoError::CipherError
221        })?;
222    Ok(())
223}
224
225fn decrypt_des3_cbc(key: &[u8], iv: &[u8], data: &mut [u8]) -> CryptoResult<()> {
226    use cbc::cipher::{BlockDecryptMut, KeyIvInit};
227    type Des3Cbc = cbc::Decryptor<des::TdesEde3>;
228
229    let cipher = Des3Cbc::new_from_slices(key, iv).map_err(|_| {
230        tracing::debug!(target: "async_snmp::crypto", "3DES decryption failed: invalid key length");
231        CryptoError::InvalidKeyLength
232    })?;
233    cipher
234        .decrypt_padded_mut::<cbc::cipher::block_padding::NoPadding>(data)
235        .map_err(|_| {
236            tracing::debug!(target: "async_snmp::crypto", "3DES decryption failed: cipher error");
237            CryptoError::CipherError
238        })?;
239    Ok(())
240}
241
242fn encrypt_aes_cfb(key: &[u8], iv: &[u8], data: &mut [u8]) -> CryptoResult<()> {
243    use aes::{Aes128, Aes192, Aes256};
244    use cfb_mode::cipher::{AsyncStreamCipher, KeyIvInit};
245
246    match key.len() {
247        16 => {
248            type Aes128Cfb = cfb_mode::Encryptor<Aes128>;
249            let cipher = Aes128Cfb::new_from_slices(key, iv).map_err(|_| {
250                tracing::debug!(target: "async_snmp::crypto", "AES-128 encryption failed: invalid key length");
251                CryptoError::InvalidKeyLength
252            })?;
253            cipher.encrypt(data);
254        }
255        24 => {
256            type Aes192Cfb = cfb_mode::Encryptor<Aes192>;
257            let cipher = Aes192Cfb::new_from_slices(key, iv).map_err(|_| {
258                tracing::debug!(target: "async_snmp::crypto", "AES-192 encryption failed: invalid key length");
259                CryptoError::InvalidKeyLength
260            })?;
261            cipher.encrypt(data);
262        }
263        32 => {
264            type Aes256Cfb = cfb_mode::Encryptor<Aes256>;
265            let cipher = Aes256Cfb::new_from_slices(key, iv).map_err(|_| {
266                tracing::debug!(target: "async_snmp::crypto", "AES-256 encryption failed: invalid key length");
267                CryptoError::InvalidKeyLength
268            })?;
269            cipher.encrypt(data);
270        }
271        key_len => {
272            tracing::debug!(target: "async_snmp::crypto", { key_len }, "AES encryption failed: unsupported key length");
273            return Err(CryptoError::InvalidKeyLength);
274        }
275    }
276    Ok(())
277}
278
279fn decrypt_aes_cfb(key: &[u8], iv: &[u8], data: &mut [u8]) -> CryptoResult<()> {
280    use aes::{Aes128, Aes192, Aes256};
281    use cfb_mode::cipher::{AsyncStreamCipher, KeyIvInit};
282
283    match key.len() {
284        16 => {
285            type Aes128Cfb = cfb_mode::Decryptor<Aes128>;
286            let cipher = Aes128Cfb::new_from_slices(key, iv).map_err(|_| {
287                tracing::debug!(target: "async_snmp::crypto", "AES-128 decryption failed: invalid key length");
288                CryptoError::InvalidKeyLength
289            })?;
290            cipher.decrypt(data);
291        }
292        24 => {
293            type Aes192Cfb = cfb_mode::Decryptor<Aes192>;
294            let cipher = Aes192Cfb::new_from_slices(key, iv).map_err(|_| {
295                tracing::debug!(target: "async_snmp::crypto", "AES-192 decryption failed: invalid key length");
296                CryptoError::InvalidKeyLength
297            })?;
298            cipher.decrypt(data);
299        }
300        32 => {
301            type Aes256Cfb = cfb_mode::Decryptor<Aes256>;
302            let cipher = Aes256Cfb::new_from_slices(key, iv).map_err(|_| {
303                tracing::debug!(target: "async_snmp::crypto", "AES-256 decryption failed: invalid key length");
304                CryptoError::InvalidKeyLength
305            })?;
306            cipher.decrypt(data);
307        }
308        key_len => {
309            tracing::debug!(target: "async_snmp::crypto", { key_len }, "AES decryption failed: unsupported key length");
310            return Err(CryptoError::InvalidKeyLength);
311        }
312    }
313    Ok(())
314}