extern crate aes_gcm;
use self::aes_gcm::Aes256Gcm;
use self::aes_gcm::aead::{Aead, AeadInPlace, NewAead, generic_array::GenericArray, Payload};
use crate::secure::{base64, rand, Key};
use crate::{Cookie, CookieJar};
use self::rand::RngCore;
pub(crate) const NONCE_LEN: usize = 12;
pub(crate) const TAG_LEN: usize = 16;
pub(crate) const KEY_LEN: usize = 32;
#[cfg_attr(all(doc, not(doctest)), doc(cfg(feature = "private")))]
pub struct PrivateJar<'a> {
parent: &'a mut CookieJar,
key: [u8; KEY_LEN]
}
impl<'a> PrivateJar<'a> {
pub(crate) fn new(parent: &'a mut CookieJar, key: &Key) -> PrivateJar<'a> {
PrivateJar { parent, key: key.encryption }
}
fn unseal(&self, name: &str, value: &str) -> Result<String, &'static str> {
let data = base64::decode(value).map_err(|_| "bad base64 value")?;
if data.len() <= NONCE_LEN {
return Err("length of decoded data is <= NONCE_LEN");
}
let (nonce, cipher) = data.split_at(NONCE_LEN);
let payload = Payload { msg: cipher, aad: name.as_bytes() };
let aead = Aes256Gcm::new(GenericArray::from_slice(&self.key));
aead.decrypt(GenericArray::from_slice(nonce), payload)
.map_err(|_| "invalid key/nonce/value: bad seal")
.and_then(|s| String::from_utf8(s).map_err(|_| "bad unsealed utf8"))
}
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
if let Some(cookie_ref) = self.parent.get(name) {
let mut cookie = cookie_ref.clone();
if let Ok(value) = self.unseal(name, cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
}
None
}
pub fn add(&mut self, mut cookie: Cookie<'static>) {
self.encrypt_cookie(&mut cookie);
self.parent.add(cookie);
}
pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
self.encrypt_cookie(&mut cookie);
self.parent.add_original(cookie);
}
fn encrypt_cookie(&self, cookie: &mut Cookie) {
let cookie_val = cookie.value().as_bytes();
let mut data = vec![0; NONCE_LEN + cookie_val.len() + TAG_LEN];
let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
let (in_out, tag) = in_out.split_at_mut(cookie_val.len());
in_out.copy_from_slice(cookie_val);
let mut rng = self::rand::thread_rng();
rng.try_fill_bytes(nonce).expect("couldn't random fill nonce");
let nonce = GenericArray::from_slice(nonce);
let aad = cookie.name().as_bytes();
let aead = Aes256Gcm::new(GenericArray::from_slice(&self.key));
let aad_tag = aead.encrypt_in_place_detached(&nonce, aad, in_out)
.expect("encryption failure!");
tag.copy_from_slice(&aad_tag);
cookie.set_value(base64::encode(&data));
}
pub fn remove(&mut self, cookie: Cookie<'static>) {
self.parent.remove(cookie);
}
}
#[cfg(test)]
mod test {
use crate::{CookieJar, Cookie, Key};
#[test]
fn simple() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_simple_behaviour!(jar, jar.private(&key));
}
#[test]
fn secure() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.private(&key));
}
#[test]
fn roundtrip() {
let key = Key::from(&[89, 202, 200, 125, 230, 90, 197, 245, 166, 249,
34, 169, 135, 31, 20, 197, 94, 154, 254, 79, 60, 26, 8, 143, 254,
24, 116, 138, 92, 225, 159, 60, 157, 41, 135, 129, 31, 226, 196, 16,
198, 168, 134, 4, 42, 1, 196, 24, 57, 103, 241, 147, 201, 185, 233,
10, 180, 170, 187, 89, 252, 137, 110, 107]);
let mut jar = CookieJar::new();
jar.add(Cookie::new("encrypted_with_ring014",
"lObeZJorGVyeSWUA8khTO/8UCzFVBY9g0MGU6/J3NN1R5x11dn2JIA=="));
jar.add(Cookie::new("encrypted_with_ring016",
"SU1ujceILyMBg3fReqRmA9HUtAIoSPZceOM/CUpObROHEujXIjonkA=="));
let private = jar.private(&key);
assert_eq!(private.get("encrypted_with_ring014").unwrap().value(), "Tamper-proof");
assert_eq!(private.get("encrypted_with_ring016").unwrap().value(), "Tamper-proof");
}
}