massa_cipher/
encrypt.rs

1// Copyright (c) 2022 MASSA LABS <info@massa.net>
2
3//! massa-cipher encryption module.
4//!
5//! Read `lib.rs` module documentation for more information.
6
7use aes_gcm::aead::Aead;
8use aes_gcm::{Aes256Gcm, KeyInit, Nonce};
9use pbkdf2::password_hash::{Salt, SaltString};
10use pbkdf2::{password_hash::PasswordHasher, Pbkdf2};
11use rand::{thread_rng, RngCore};
12
13use crate::constants::{HASH_PARAMS, NONCE_SIZE, SALT_SIZE};
14use crate::error::CipherError;
15
16pub struct CipherData {
17    pub salt: [u8; SALT_SIZE],
18    pub nonce: [u8; NONCE_SIZE],
19    pub encrypted_bytes: Vec<u8>,
20}
21
22/// Encryption function using AES-GCM cipher.
23///
24/// Read `lib.rs` module documentation for more information.
25pub fn encrypt(password: &str, data: &[u8]) -> Result<CipherData, CipherError> {
26    // generate the PBKDF2 salt
27    // Re-implementation of the SaltString::generate function (allowing to control the SALT_SIZE here)
28    let mut rng = thread_rng();
29    let mut raw_salt = [0u8; SALT_SIZE];
30    rng.fill_bytes(&mut raw_salt);
31    let salt = SaltString::encode_b64(&raw_salt)
32        .map_err(|e| CipherError::EncryptionError(format!("Failed to encode salt: {e:?}")))?;
33
34    // compute PBKDF2 password hash
35    let password_hash = Pbkdf2
36        .hash_password_customized(
37            password.as_bytes(),
38            None,
39            None,
40            HASH_PARAMS,
41            Salt::from(&salt),
42        )
43        .map_err(|e| CipherError::EncryptionError(e.to_string()))?
44        .hash
45        .expect("content is missing after a successful hash");
46
47    // generate the AES-GCM nonce
48    let mut nonce_bytes = [0u8; NONCE_SIZE];
49    thread_rng().fill_bytes(&mut nonce_bytes);
50    let nonce = Nonce::from_slice(&nonce_bytes);
51
52    // encrypt the data
53    let cipher = Aes256Gcm::new_from_slice(password_hash.as_bytes()).expect("invalid key length");
54    let encrypted_bytes = cipher
55        .encrypt(nonce, data.as_ref())
56        .map_err(|e| CipherError::EncryptionError(e.to_string()))?;
57
58    // build the encryption result
59    let result = CipherData {
60        salt: raw_salt,
61        nonce: nonce_bytes,
62        encrypted_bytes,
63    };
64    Ok(result)
65}