extern crate base64;
use crate::truncate_iv;
use crate::{AesCcm128, AesCcm192, AesCcm256};
use crate::{SjclBlock, SjclError, SjclParams};
use ccm::aead::{generic_array::GenericArray, Aead, NewAead, Payload};
use ccm::consts::{U16, U24, U32};
use password_hash::{PasswordHasher, SaltString};
use pbkdf2::{Params, Pbkdf2};
pub fn encrypt(
plaintext: Vec<u8>,
params: SjclParams,
key: String,
) -> Result<SjclBlock, SjclError> {
let iv_b64 = base64::encode(params.iv.clone());
let adata_b64 = base64::encode(params.adata.clone());
let salt_b64 = base64::encode(params.salt.clone());
match params.cipher.as_str() {
"aes" => match params.mode.as_str() {
"ccm" => {
if params.v != 1 {
return Err(SjclError::EncryptionError {
message: "Only version 1 is currently supported".to_string(),
});
}
let salt = match SaltString::b64_encode(¶ms.salt) {
Ok(s) => s,
Err(_) => {
return Err(SjclError::EncryptionError {
message: "Failed to generate salt string".to_string(),
})
}
};
let password_hash = Pbkdf2.hash_password(
key.as_bytes(),
None,
None,
Params {
rounds: params.iter,
output_length: params.ks / 8,
},
salt.as_salt(),
);
let password_hash = match password_hash {
Ok(pwh) => pwh,
Err(_) => {
return Err(SjclError::EncryptionError {
message: "Failed to generate password hash".to_string(),
})
}
};
let password_hash = password_hash.hash.unwrap();
let iv = truncate_iv(params.iv, plaintext.len() * 8, params.ts);
let nonce = GenericArray::from_slice(iv.as_slice());
let ciphertext = match params.ks {
256 => {
let key: &GenericArray<u8, U32> =
GenericArray::from_slice(password_hash.as_bytes());
let cipher = AesCcm256::new(key);
match cipher.encrypt(
nonce,
Payload {
aad: ¶ms.adata,
msg: &plaintext,
},
) {
Ok(c) => c,
Err(_) => {
return Err(SjclError::EncryptionError {
message: "Failed to encrypt ciphertext".to_string(),
});
}
}
}
192 => {
let key: &GenericArray<u8, U24> =
GenericArray::from_slice(password_hash.as_bytes());
let cipher = AesCcm192::new(key);
match cipher.encrypt(
nonce,
Payload {
aad: ¶ms.adata,
msg: &plaintext,
},
) {
Ok(c) => c,
Err(_) => {
return Err(SjclError::EncryptionError {
message: "Failed to encrypt ciphertext".to_string(),
});
}
}
}
128 => {
let key: &GenericArray<u8, U16> =
GenericArray::from_slice(password_hash.as_bytes());
let cipher = AesCcm128::new(key);
match cipher.encrypt(
nonce,
Payload {
aad: ¶ms.adata,
msg: &plaintext,
},
) {
Ok(c) => c,
Err(_) => {
return Err(SjclError::EncryptionError {
message: "Failed to encrypt ciphertext".to_string(),
});
}
}
}
_ => {
return Err(SjclError::NotImplementedError);
}
};
Ok(SjclBlock::new(
iv_b64,
params.v,
params.iter,
params.ks,
params.ts,
params.mode,
adata_b64,
params.cipher,
salt_b64,
base64::encode(ciphertext),
))
}
_ => Err(SjclError::NotImplementedError),
},
_ => Err(SjclError::NotImplementedError),
}
}