// Keep these in sync, and keep the key len synced with the `private` docs as // well as the `KEYS_INFO` const in secure::Key. pub(crate) const NONCE_LEN: usize = 12; pub(crate) const TAG_LEN: usize = 16; pub(crate) const KEY_LEN: usize = 32;
/// A child cookie jar that provides authenticated encryption for its cookies. /// /// A _private_ child jar signs and encrypts all the cookies added to it and /// verifies and decrypts cookies retrieved from it. Any cookies stored in a /// `PrivateJar` are simultaneously assured confidentiality, integrity, and /// authenticity. In other words, clients cannot discover nor tamper with the /// contents of a cookie, nor can they fabricate cookie data. #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))] pubstruct PrivateJar<J> {
parent: J,
key: [u8; KEY_LEN]
}
impl<J> PrivateJar<J> { /// Creates a new child `PrivateJar` with parent `parent` and key `key`. /// This method is typically called indirectly via the `signed` method of /// `CookieJar`. pub(crate) fn new(parent: J, key: &Key) -> PrivateJar<J> {
PrivateJar { parent, key: key.encryption().try_into().expect("enc key len") }
}
/// Encrypts the cookie's value with authenticated encryption providing /// confidentiality, integrity, and authenticity. fn encrypt_cookie(&self, cookie: &mut Cookie) { // Create a vec to hold the [nonce | cookie value | tag]. let cookie_val = cookie.value().as_bytes(); letmut data = vec![0; NONCE_LEN + cookie_val.len() + TAG_LEN];
// Split data into three: nonce, input/output, tag. Copy input. 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);
// Fill nonce piece with random data. letmut rng = self::rand::thread_rng();
rng.try_fill_bytes(nonce).expect("couldn't random fill nonce"); let nonce = GenericArray::clone_from_slice(nonce);
// Perform the actual sealing operation, using the cookie's name as // associated data to prevent value swapping. 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!");
// Copy the tag into the tag piece.
tag.copy_from_slice(&aad_tag);
/// Given a sealed value `str` and a key name `name`, where the nonce is /// prepended to the original value and then both are Base64 encoded, /// verifies and decrypts the sealed value and returns it. If there's a /// problem, returns an `Err` with a string describing the issue. fn unseal(&self, name: &str, value: &str) -> Result<String, &le='color:blue'>'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"))
}
/// Authenticates and decrypts `cookie`, returning the plaintext version if /// decryption succeeds or `None` otherwise. Authenticatation and decryption /// _always_ succeeds if `cookie` was generated by a `PrivateJar` with the /// same key as `self`. /// /// # Example /// /// ```rust /// use cookie::{CookieJar, Cookie, Key}; /// /// let key = Key::generate(); /// let mut jar = CookieJar::new(); /// assert!(jar.private(&key).get("name").is_none()); /// /// jar.private_mut(&key).add(Cookie::new("name", "value")); /// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value"); /// /// let plain = jar.get("name").cloned().unwrap(); /// assert_ne!(plain.value(), "value"); /// let decrypted = jar.private(&key).decrypt(plain).unwrap(); /// assert_eq!(decrypted.value(), "value"); /// /// let plain = Cookie::new("plaintext", "hello"); /// assert!(jar.private(&key).decrypt(plain).is_none()); /// ``` pubfn decrypt(&self, mut cookie: Cookie<'static>) -> Option<Cookie<'static>> { iflet Ok(value) = self.unseal(cookie.name(), cookie.value()) {
cookie.set_value(value); return Some(cookie);
}
None
}
}
impl<J: Borrow<CookieJar>> PrivateJar<J> { /// Returns a reference to the `Cookie` inside this jar with the name `name` /// and authenticates and decrypts the cookie's value, returning a `Cookie` /// with the decrypted value. If the cookie cannot be found, or the cookie /// fails to authenticate or decrypt, `None` is returned. /// /// # Example /// /// ```rust /// use cookie::{CookieJar, Cookie, Key}; /// /// let key = Key::generate(); /// let jar = CookieJar::new(); /// assert!(jar.private(&key).get("name").is_none()); /// /// let mut jar = jar; /// let mut private_jar = jar.private_mut(&key); /// private_jar.add(Cookie::new("name", "value")); /// assert_eq!(private_jar.get("name").unwrap().value(), "value"); /// ``` pubfn get(&self, name: &str) -> Option<Cookie<'static>> { self.parent.borrow().get(name).and_then(|c| self.decrypt(c.clone()))
}
}
impl<J: BorrowMut<CookieJar>> PrivateJar<J> { /// Adds `cookie` to the parent jar. The cookie's value is encrypted with /// authenticated encryption assuring confidentiality, integrity, and /// authenticity. /// /// # Example /// /// ```rust /// use cookie::{CookieJar, Cookie, Key}; /// /// let key = Key::generate(); /// let mut jar = CookieJar::new(); /// jar.private_mut(&key).add(Cookie::new("name", "value")); /// /// assert_ne!(jar.get("name").unwrap().value(), "value"); /// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value"); /// ``` pubfn add(&mutself, mut cookie: Cookie<'static>) { self.encrypt_cookie(&mut cookie); self.parent.borrow_mut().add(cookie);
}
/// Adds an "original" `cookie` to parent jar. The cookie's value is /// encrypted with authenticated encryption assuring confidentiality, /// integrity, and authenticity. Adding an original cookie does not affect /// the [`CookieJar::delta()`] computation. This method is intended to be /// used to seed the cookie jar with cookies received from a client's HTTP /// message. /// /// For accurate `delta` computations, this method should not be called /// after calling `remove`. /// /// # Example /// /// ```rust /// use cookie::{CookieJar, Cookie, Key}; /// /// let key = Key::generate(); /// let mut jar = CookieJar::new(); /// jar.private_mut(&key).add_original(Cookie::new("name", "value")); /// /// assert_eq!(jar.iter().count(), 1); /// assert_eq!(jar.delta().count(), 0); /// ``` pubfn add_original(&mutself, mut cookie: Cookie<'static>) { self.encrypt_cookie(&mut cookie); self.parent.borrow_mut().add_original(cookie);
}
/// Removes `cookie` from the parent jar. /// /// For correct removal, the passed in `cookie` must contain the same `path` /// and `domain` as the cookie that was initially set. /// /// This is identical to [`CookieJar::remove()`]. See the method's /// documentation for more details. /// /// # Example /// /// ```rust /// use cookie::{CookieJar, Cookie, Key}; /// /// let key = Key::generate(); /// let mut jar = CookieJar::new(); /// let mut private_jar = jar.private_mut(&key); /// /// private_jar.add(Cookie::new("name", "value")); /// assert!(private_jar.get("name").is_some()); /// /// private_jar.remove(Cookie::named("name")); /// assert!(private_jar.get("name").is_none()); /// ``` pubfn remove(&mutself, cookie: Cookie<'static>) { self.parent.borrow_mut().remove(cookie);
}
}
#[cfg(test)] mod test { usecrate::{CookieJar, Cookie, Key};
#[test] fn simple() { let key = Key::generate(); letmut jar = CookieJar::new();
assert_simple_behaviour!(jar, jar.private_mut(&key));
}
#[test] fn secure() { let key = Key::generate(); letmut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.private_mut(&key));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.