#![deny(warnings, clippy::pedantic)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "argon")]
mod argon;
#[cfg(feature = "argon")]
pub use argon::decrypt as decrypt_with_password;
#[cfg(feature = "argon")]
pub use argon::encrypt as encrypt_with_password;
#[cfg(feature = "stream")]
pub mod stream;
#[cfg(feature = "ffi")]
mod ffi;
#[cfg(feature = "wasm")]
mod wasm;
mod sizes {
use aes_gcm_siv::aead::generic_array::typenum::Unsigned;
use aes_gcm_siv::{AeadCore, Aes256GcmSiv};
pub(crate) const TAG_LEN: usize = <Aes256GcmSiv as AeadCore>::TagSize::USIZE;
pub(crate) const NONCE_LEN: usize = <Aes256GcmSiv as AeadCore>::NonceSize::USIZE;
#[cfg(feature = "argon")]
pub(crate) const SALT_LEN: usize = argon2::RECOMMENDED_SALT_LEN;
}
pub type Key = [u8; <<aes_gcm_siv::Aes256GcmSiv as aes_gcm_siv::aead::KeySizeUser>::KeySize as aes_gcm_siv::aead::generic_array::typenum::Unsigned>::USIZE];
pub fn encrypt<'k, Key, Payload>(key: Key, payload: Payload) -> Option<Vec<u8>>
where
Key: Into<&'k aes_gcm_siv::Key<aes_gcm_siv::Aes256GcmSiv>>,
Payload: AsRef<[u8]>,
{
encrypt_inner(key, payload, 0)
}
pub fn decrypt<'k, Key, Payload>(key: Key, payload: Payload) -> Option<Vec<u8>>
where
Key: Into<&'k aes_gcm_siv::Key<aes_gcm_siv::Aes256GcmSiv>>,
Payload: AsRef<[u8]>,
{
decrypt_inner(key, payload)
}
fn encrypt_inner<'k, Key, Payload>(
key: Key,
payload: Payload,
extra_capacity: usize,
) -> Option<Vec<u8>>
where
Key: Into<&'k aes_gcm_siv::Key<aes_gcm_siv::Aes256GcmSiv>>,
Payload: AsRef<[u8]>,
{
use aes_gcm_siv::aead::AeadMutInPlace;
use aes_gcm_siv::aead::KeyInit;
let key = key.into();
let payload = payload.as_ref();
let nonce = <aes_gcm_siv::Aes256GcmSiv as aes_gcm_siv::aead::AeadCore>::generate_nonce(
aes_gcm_siv::aead::rand_core::OsRng,
);
let mut cipher = aes_gcm_siv::Aes256GcmSiv::new(key);
let mut buffer =
Vec::with_capacity(payload.len() + sizes::NONCE_LEN + sizes::TAG_LEN + extra_capacity);
buffer.extend_from_slice(payload);
let tag = cipher
.encrypt_in_place_detached(&nonce, &[], &mut buffer)
.ok()?;
buffer.extend_from_slice(&nonce);
buffer.extend_from_slice(&tag);
Some(buffer)
}
fn decrypt_inner<'k, Key, Payload>(key: Key, payload: Payload) -> Option<Vec<u8>>
where
Key: Into<&'k aes_gcm_siv::Key<aes_gcm_siv::Aes256GcmSiv>>,
Payload: AsRef<[u8]>,
{
use aes_gcm_siv::aead::AeadInPlace;
use aes_gcm_siv::aead::KeyInit;
let key = key.into();
let mut payload = payload.as_ref();
if payload.len() < sizes::TAG_LEN + sizes::NONCE_LEN {
return None;
}
let tag = aes_gcm_siv::Tag::from_slice(payload.split_off(payload.len() - sizes::TAG_LEN..)?);
let nonce =
aes_gcm_siv::Nonce::from_slice(payload.split_off(payload.len() - sizes::NONCE_LEN..)?);
let cipher = aes_gcm_siv::Aes256GcmSiv::new(key);
let mut buffer = Vec::from(payload);
if cipher
.decrypt_in_place_detached(nonce, &[], &mut buffer, tag)
.is_ok()
{
Some(buffer)
} else {
None
}
}
#[cfg(test)]
mod test {
use super::*;
fn make_key() -> aes_gcm_siv::Key<aes_gcm_siv::Aes256GcmSiv> {
<aes_gcm_siv::Aes256GcmSiv as aes_gcm_siv::KeyInit>::generate_key(aes_gcm_siv::aead::OsRng)
}
#[test]
fn round_trip() {
let key = make_key();
let payload = "super secret payload";
let encrypted = encrypt(&key, payload).unwrap();
let decrypted = decrypt(&key, encrypted).unwrap();
let recovered = String::from_utf8(decrypted).unwrap();
assert_eq!(recovered, payload);
}
#[test]
fn corrupted_byte() {
let key = make_key();
let payload = "super secret payload";
let encrypted = encrypt(&key, payload).unwrap();
for i in 0..encrypted.len() {
let mut corrupted = encrypted.clone();
corrupted[i] = !corrupted[i];
assert_eq!(decrypt(&key, corrupted), None);
}
}
}