use bytes::Bytes;
use ring::{
aead, hmac,
rand::{SecureRandom, SystemRandom},
};
const NONCE_LENGTH: usize = 12;
static ENCRYPTION_KEY_GENERATION_STRING: &[u8] = b"ilp_store_redis_encryption_key";
use core::sync::atomic;
use secrecy::{DebugSecret, Secret, SecretBytes};
use std::ptr;
use zeroize::Zeroize;
#[derive(Debug)]
pub struct EncryptionKey(pub(crate) aead::LessSafeKey);
#[derive(Debug)]
pub struct DecryptionKey(pub(crate) aead::LessSafeKey);
#[derive(Debug)]
pub struct GenerationKey(pub(crate) hmac::Key);
impl DebugSecret for EncryptionKey {}
impl DebugSecret for DecryptionKey {}
impl DebugSecret for GenerationKey {}
impl Zeroize for EncryptionKey {
fn zeroize(&mut self) {
let empty_key = EncryptionKey(aead::LessSafeKey::new(
aead::UnboundKey::new(&aead::AES_256_GCM, &[0; 32]).unwrap(),
));
volatile_write(self, empty_key);
atomic_fence();
}
}
impl Drop for EncryptionKey {
fn drop(&mut self) {
self.zeroize()
}
}
impl Zeroize for DecryptionKey {
fn zeroize(&mut self) {
let empty_key = DecryptionKey(aead::LessSafeKey::new(
aead::UnboundKey::new(&aead::AES_256_GCM, &[0; 32]).unwrap(),
));
volatile_write(self, empty_key);
atomic_fence();
}
}
impl Drop for DecryptionKey {
fn drop(&mut self) {
self.zeroize()
}
}
impl Zeroize for GenerationKey {
fn zeroize(&mut self) {
let empty_key = GenerationKey(hmac::Key::new(hmac::HMAC_SHA256, &[0; 32]));
volatile_write(self, empty_key);
atomic_fence();
}
}
impl Drop for GenerationKey {
fn drop(&mut self) {
self.zeroize()
}
}
#[inline]
fn volatile_write<T>(dst: &mut T, src: T) {
unsafe { ptr::write_volatile(dst, src) }
}
#[inline]
fn atomic_fence() {
atomic::compiler_fence(atomic::Ordering::SeqCst);
}
pub fn generate_keys(server_secret: &[u8]) -> (Secret<EncryptionKey>, Secret<DecryptionKey>) {
let generation_key = GenerationKey(hmac::Key::new(hmac::HMAC_SHA256, server_secret));
let encryption_key = Secret::new(EncryptionKey(aead::LessSafeKey::new(
aead::UnboundKey::new(
&aead::AES_256_GCM,
hmac::sign(&generation_key.0, ENCRYPTION_KEY_GENERATION_STRING).as_ref(),
)
.unwrap(),
)));
let decryption_key = Secret::new(DecryptionKey(aead::LessSafeKey::new(
aead::UnboundKey::new(
&aead::AES_256_GCM,
hmac::sign(&generation_key.0, ENCRYPTION_KEY_GENERATION_STRING).as_ref(),
)
.unwrap(),
)));
(encryption_key, decryption_key)
}
pub fn encrypt_token(encryption_key: &aead::LessSafeKey, token: &[u8]) -> Bytes {
let mut token = token.to_vec();
let mut nonce: [u8; NONCE_LENGTH] = [0; NONCE_LENGTH];
SystemRandom::new()
.fill(&mut nonce)
.expect("Unable to get sufficient entropy for nonce");
let nonce_copy = nonce;
let nonce = aead::Nonce::assume_unique_for_key(nonce);
match encryption_key.seal_in_place_append_tag(nonce, aead::Aad::from(&[]), &mut token) {
Ok(_) => {
token.append(&mut nonce_copy.as_ref().to_vec());
Bytes::from(token)
}
_ => panic!("Unable to encrypt token"),
}
}
pub fn decrypt_token(
decryption_key: &aead::LessSafeKey,
encrypted: &[u8],
) -> Result<SecretBytes, ()> {
if encrypted.len() < aead::MAX_TAG_LEN {
return Err(());
}
let mut encrypted = encrypted.to_vec();
let nonce_bytes = encrypted.split_off(encrypted.len() - NONCE_LENGTH);
let mut nonce: [u8; NONCE_LENGTH] = [0; NONCE_LENGTH];
nonce.copy_from_slice(nonce_bytes.as_ref());
let nonce = aead::Nonce::assume_unique_for_key(nonce);
if let Ok(token) = decryption_key.open_in_place(nonce, aead::Aad::empty(), &mut encrypted) {
Ok(SecretBytes::new(token.to_vec()))
} else {
Err(())
}
}
#[cfg(test)]
mod encryption {
use super::*;
use secrecy::ExposeSecret;
use std::str;
#[test]
fn encrypts_and_decrypts() {
let (encryption_key, decryption_key) = generate_keys(&[9; 32]);
let encrypted = encrypt_token(&encryption_key.expose_secret().0, b"test test");
let decrypted = decrypt_token(&decryption_key.expose_secret().0, encrypted.as_ref());
assert_eq!(
str::from_utf8(decrypted.unwrap().expose_secret().as_ref()).unwrap(),
"test test"
);
}
}