summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cookie/src/secure
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/cookie/src/secure')
-rw-r--r--third_party/rust/cookie/src/secure/key.rs174
-rw-r--r--third_party/rust/cookie/src/secure/macros.rs41
-rw-r--r--third_party/rust/cookie/src/secure/mod.rs12
-rw-r--r--third_party/rust/cookie/src/secure/private.rs221
-rw-r--r--third_party/rust/cookie/src/secure/signed.rs182
5 files changed, 630 insertions, 0 deletions
diff --git a/third_party/rust/cookie/src/secure/key.rs b/third_party/rust/cookie/src/secure/key.rs
new file mode 100644
index 0000000000..77997ae90d
--- /dev/null
+++ b/third_party/rust/cookie/src/secure/key.rs
@@ -0,0 +1,174 @@
+use secure::ring::hkdf::expand;
+use secure::ring::digest::{SHA256, Algorithm};
+use secure::ring::hmac::SigningKey;
+use secure::ring::rand::{SecureRandom, SystemRandom};
+
+use secure::private::KEY_LEN as PRIVATE_KEY_LEN;
+use secure::signed::KEY_LEN as SIGNED_KEY_LEN;
+
+static HKDF_DIGEST: &'static Algorithm = &SHA256;
+const KEYS_INFO: &'static str = "COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM";
+
+/// A cryptographic master key for use with `Signed` and/or `Private` jars.
+///
+/// This structure encapsulates secure, cryptographic keys for use with both
+/// [PrivateJar](struct.PrivateJar.html) and [SignedJar](struct.SignedJar.html).
+/// It can be derived from a single master key via
+/// [from_master](#method.from_master) or generated from a secure random source
+/// via [generate](#method.generate). A single instance of `Key` can be used for
+/// both a `PrivateJar` and a `SignedJar`.
+///
+/// This type is only available when the `secure` feature is enabled.
+#[derive(Clone)]
+pub struct Key {
+ signing_key: [u8; SIGNED_KEY_LEN],
+ encryption_key: [u8; PRIVATE_KEY_LEN]
+}
+
+impl Key {
+ /// Derives new signing/encryption keys from a master key.
+ ///
+ /// The master key must be at least 256-bits (32 bytes). For security, the
+ /// master key _must_ be cryptographically random. The keys are derived
+ /// deterministically from the master key.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `key` is less than 32 bytes in length.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::Key;
+ ///
+ /// # /*
+ /// let master_key = { /* a cryptographically random key >= 32 bytes */ };
+ /// # */
+ /// # let master_key: &Vec<u8> = &(0..32).collect();
+ ///
+ /// let key = Key::from_master(master_key);
+ /// ```
+ pub fn from_master(key: &[u8]) -> Key {
+ if key.len() < 32 {
+ panic!("bad master key length: expected at least 32 bytes, found {}", key.len());
+ }
+
+ // Expand the user's key into two.
+ let prk = SigningKey::new(HKDF_DIGEST, key);
+ let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
+ expand(&prk, KEYS_INFO.as_bytes(), &mut both_keys);
+
+ // Copy the keys into their respective arrays.
+ let mut signing_key = [0; SIGNED_KEY_LEN];
+ let mut encryption_key = [0; PRIVATE_KEY_LEN];
+ signing_key.copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
+ encryption_key.copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
+
+ Key {
+ signing_key: signing_key,
+ encryption_key: encryption_key
+ }
+ }
+
+ /// Generates signing/encryption keys from a secure, random source. Keys are
+ /// generated nondeterministically.
+ ///
+ /// # Panics
+ ///
+ /// Panics if randomness cannot be retrieved from the operating system. See
+ /// [try_generate](#method.try_generate) for a non-panicking version.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::Key;
+ ///
+ /// let key = Key::generate();
+ /// ```
+ pub fn generate() -> Key {
+ Self::try_generate().expect("failed to generate `Key` from randomness")
+ }
+
+ /// Attempts to generate signing/encryption keys from a secure, random
+ /// source. Keys are generated nondeterministically. If randomness cannot be
+ /// retrieved from the underlying operating system, returns `None`.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::Key;
+ ///
+ /// let key = Key::try_generate();
+ /// ```
+ pub fn try_generate() -> Option<Key> {
+ let mut sign_key = [0; SIGNED_KEY_LEN];
+ let mut enc_key = [0; PRIVATE_KEY_LEN];
+
+ let rng = SystemRandom::new();
+ if rng.fill(&mut sign_key).is_err() || rng.fill(&mut enc_key).is_err() {
+ return None
+ }
+
+ Some(Key { signing_key: sign_key, encryption_key: enc_key })
+ }
+
+ /// Returns the raw bytes of a key suitable for signing cookies.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::Key;
+ ///
+ /// let key = Key::generate();
+ /// let signing_key = key.signing();
+ /// ```
+ pub fn signing(&self) -> &[u8] {
+ &self.signing_key[..]
+ }
+
+ /// Returns the raw bytes of a key suitable for encrypting cookies.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::Key;
+ ///
+ /// let key = Key::generate();
+ /// let encryption_key = key.encryption();
+ /// ```
+ pub fn encryption(&self) -> &[u8] {
+ &self.encryption_key[..]
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::Key;
+
+ #[test]
+ fn deterministic_from_master() {
+ let master_key: Vec<u8> = (0..32).collect();
+
+ let key_a = Key::from_master(&master_key);
+ let key_b = Key::from_master(&master_key);
+
+ assert_eq!(key_a.signing(), key_b.signing());
+ assert_eq!(key_a.encryption(), key_b.encryption());
+ assert_ne!(key_a.encryption(), key_a.signing());
+
+ let master_key_2: Vec<u8> = (32..64).collect();
+ let key_2 = Key::from_master(&master_key_2);
+
+ assert_ne!(key_2.signing(), key_a.signing());
+ assert_ne!(key_2.encryption(), key_a.encryption());
+ }
+
+ #[test]
+ fn non_deterministic_generate() {
+ let key_a = Key::generate();
+ let key_b = Key::generate();
+
+ assert_ne!(key_a.signing(), key_b.signing());
+ assert_ne!(key_a.encryption(), key_b.encryption());
+ }
+}
diff --git a/third_party/rust/cookie/src/secure/macros.rs b/third_party/rust/cookie/src/secure/macros.rs
new file mode 100644
index 0000000000..ed3cc36c73
--- /dev/null
+++ b/third_party/rust/cookie/src/secure/macros.rs
@@ -0,0 +1,41 @@
+#[cfg(test)]
+macro_rules! assert_simple_behaviour {
+ ($clear:expr, $secure:expr) => ({
+ assert_eq!($clear.iter().count(), 0);
+
+ $secure.add(Cookie::new("name", "val"));
+ assert_eq!($clear.iter().count(), 1);
+ assert_eq!($secure.get("name").unwrap().value(), "val");
+ assert_ne!($clear.get("name").unwrap().value(), "val");
+
+ $secure.add(Cookie::new("another", "two"));
+ assert_eq!($clear.iter().count(), 2);
+
+ $clear.remove(Cookie::named("another"));
+ assert_eq!($clear.iter().count(), 1);
+
+ $secure.remove(Cookie::named("name"));
+ assert_eq!($clear.iter().count(), 0);
+ })
+}
+
+#[cfg(test)]
+macro_rules! assert_secure_behaviour {
+ ($clear:expr, $secure:expr) => ({
+ $secure.add(Cookie::new("secure", "secure"));
+ assert!($clear.get("secure").unwrap().value() != "secure");
+ assert!($secure.get("secure").unwrap().value() == "secure");
+
+ let mut cookie = $clear.get("secure").unwrap().clone();
+ let new_val = format!("{}l", cookie.value());
+ cookie.set_value(new_val);
+ $clear.add(cookie);
+ assert!($secure.get("secure").is_none());
+
+ let mut cookie = $clear.get("secure").unwrap().clone();
+ cookie.set_value("foobar");
+ $clear.add(cookie);
+ assert!($secure.get("secure").is_none());
+ })
+}
+
diff --git a/third_party/rust/cookie/src/secure/mod.rs b/third_party/rust/cookie/src/secure/mod.rs
new file mode 100644
index 0000000000..e3c41c8c24
--- /dev/null
+++ b/third_party/rust/cookie/src/secure/mod.rs
@@ -0,0 +1,12 @@
+extern crate ring;
+extern crate base64;
+
+#[macro_use]
+mod macros;
+mod private;
+mod signed;
+mod key;
+
+pub use self::private::*;
+pub use self::signed::*;
+pub use self::key::*;
diff --git a/third_party/rust/cookie/src/secure/private.rs b/third_party/rust/cookie/src/secure/private.rs
new file mode 100644
index 0000000000..0817a6c870
--- /dev/null
+++ b/third_party/rust/cookie/src/secure/private.rs
@@ -0,0 +1,221 @@
+use secure::ring::aead::{seal_in_place, open_in_place, Aad, Algorithm, Nonce, AES_256_GCM};
+use secure::ring::aead::{OpeningKey, SealingKey};
+use secure::ring::rand::{SecureRandom, SystemRandom};
+use secure::{base64, Key};
+
+use {Cookie, CookieJar};
+
+// Keep these in sync, and keep the key len synced with the `private` docs as
+// well as the `KEYS_INFO` const in secure::Key.
+static ALGO: &'static Algorithm = &AES_256_GCM;
+const NONCE_LEN: usize = 12;
+pub 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.
+///
+/// This type is only available when the `secure` feature is enabled.
+pub struct PrivateJar<'a> {
+ parent: &'a mut CookieJar,
+ key: [u8; KEY_LEN]
+}
+
+impl<'a> PrivateJar<'a> {
+ /// Creates a new child `PrivateJar` with parent `parent` and key `key`.
+ /// This method is typically called indirectly via the `signed` method of
+ /// `CookieJar`.
+ #[doc(hidden)]
+ pub fn new(parent: &'a mut CookieJar, key: &Key) -> PrivateJar<'a> {
+ let mut key_array = [0u8; KEY_LEN];
+ key_array.copy_from_slice(key.encryption());
+ PrivateJar { parent: parent, key: key_array }
+ }
+
+ /// 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, &'static str> {
+ let mut data = base64::decode(value).map_err(|_| "bad base64 value")?;
+ if data.len() <= NONCE_LEN {
+ return Err("length of decoded data is <= NONCE_LEN");
+ }
+
+ let ad = Aad::from(name.as_bytes());
+ let key = OpeningKey::new(ALGO, &self.key).expect("opening key");
+ let (nonce, sealed) = data.split_at_mut(NONCE_LEN);
+ let nonce = Nonce::try_assume_unique_for_key(nonce)
+ .expect("invalid length of `nonce`");
+ let unsealed = open_in_place(&key, nonce, ad, 0, sealed)
+ .map_err(|_| "invalid key/nonce/value: bad seal")?;
+
+ ::std::str::from_utf8(unsealed)
+ .map(|s| s.to_string())
+ .map_err(|_| "bad unsealed utf8")
+ }
+
+ /// 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 mut jar = CookieJar::new();
+ /// let mut private_jar = jar.private(&key);
+ /// assert!(private_jar.get("name").is_none());
+ ///
+ /// private_jar.add(Cookie::new("name", "value"));
+ /// assert_eq!(private_jar.get("name").unwrap().value(), "value");
+ /// ```
+ 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
+ }
+
+ /// 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(&key).add(Cookie::new("name", "value"));
+ ///
+ /// assert_ne!(jar.get("name").unwrap().value(), "value");
+ /// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value");
+ /// ```
+ pub fn add(&mut self, mut cookie: Cookie<'static>) {
+ self.encrypt_cookie(&mut cookie);
+
+ // Add the sealed cookie to the parent.
+ self.parent.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()`](struct.CookieJar.html#method.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(&key).add_original(Cookie::new("name", "value"));
+ ///
+ /// assert_eq!(jar.iter().count(), 1);
+ /// assert_eq!(jar.delta().count(), 0);
+ /// ```
+ pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
+ self.encrypt_cookie(&mut cookie);
+
+ // Add the sealed cookie to the parent.
+ self.parent.add_original(cookie);
+ }
+
+ /// Encrypts the cookie's value with
+ /// authenticated encryption assuring confidentiality, integrity, and authenticity.
+ fn encrypt_cookie(&self, cookie: &mut Cookie) {
+ let mut data;
+ let output_len = {
+ // Create the `SealingKey` structure.
+ let key = SealingKey::new(ALGO, &self.key).expect("sealing key creation");
+
+ // Create a vec to hold the [nonce | cookie value | overhead].
+ let overhead = ALGO.tag_len();
+ let cookie_val = cookie.value().as_bytes();
+ data = vec![0; NONCE_LEN + cookie_val.len() + overhead];
+
+ // Randomly generate the nonce, then copy the cookie value as input.
+ let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
+ SystemRandom::new().fill(nonce).expect("couldn't random fill nonce");
+ in_out[..cookie_val.len()].copy_from_slice(cookie_val);
+ let nonce = Nonce::try_assume_unique_for_key(nonce)
+ .expect("invalid length of `nonce`");
+
+ // Use cookie's name as associated data to prevent value swapping.
+ let ad = Aad::from(cookie.name().as_bytes());
+
+ // Perform the actual sealing operation and get the output length.
+ seal_in_place(&key, nonce, ad, in_out, overhead).expect("in-place seal")
+ };
+
+ // Base64 encode the nonce and encrypted value.
+ let sealed_value = base64::encode(&data[..(NONCE_LEN + output_len)]);
+ cookie.set_value(sealed_value);
+ }
+
+ /// 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.
+ ///
+ /// See [CookieJar::remove](struct.CookieJar.html#method.remove) 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(&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());
+ /// ```
+ pub fn remove(&mut self, cookie: Cookie<'static>) {
+ self.parent.remove(cookie);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use {CookieJar, Cookie, Key};
+
+ #[test]
+ fn simple() {
+ let key = Key::generate();
+ let mut jar = CookieJar::new();
+ assert_simple_behaviour!(jar, jar.private(&key));
+ }
+
+ #[test]
+ fn private() {
+ let key = Key::generate();
+ let mut jar = CookieJar::new();
+ assert_secure_behaviour!(jar, jar.private(&key));
+ }
+}
diff --git a/third_party/rust/cookie/src/secure/signed.rs b/third_party/rust/cookie/src/secure/signed.rs
new file mode 100644
index 0000000000..132fd746ae
--- /dev/null
+++ b/third_party/rust/cookie/src/secure/signed.rs
@@ -0,0 +1,182 @@
+use secure::ring::digest::{SHA256, Algorithm};
+use secure::ring::hmac::{SigningKey, sign, verify_with_own_key as verify};
+use secure::{base64, Key};
+
+use {Cookie, CookieJar};
+
+// Keep these in sync, and keep the key len synced with the `signed` docs as
+// well as the `KEYS_INFO` const in secure::Key.
+static HMAC_DIGEST: &'static Algorithm = &SHA256;
+const BASE64_DIGEST_LEN: usize = 44;
+pub const KEY_LEN: usize = 32;
+
+/// A child cookie jar that authenticates its cookies.
+///
+/// A _signed_ child jar signs all the cookies added to it and verifies cookies
+/// retrieved from it. Any cookies stored in a `SignedJar` are assured integrity
+/// and authenticity. In other words, clients cannot tamper with the contents of
+/// a cookie nor can they fabricate cookie values, but the data is visible in
+/// plaintext.
+///
+/// This type is only available when the `secure` feature is enabled.
+pub struct SignedJar<'a> {
+ parent: &'a mut CookieJar,
+ key: SigningKey
+}
+
+impl<'a> SignedJar<'a> {
+ /// Creates a new child `SignedJar` with parent `parent` and key `key`. This
+ /// method is typically called indirectly via the `signed` method of
+ /// `CookieJar`.
+ #[doc(hidden)]
+ pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> {
+ SignedJar { parent: parent, key: SigningKey::new(HMAC_DIGEST, key.signing()) }
+ }
+
+ /// Given a signed value `str` where the signature is prepended to `value`,
+ /// verifies the signed value and returns it. If there's a problem, returns
+ /// an `Err` with a string describing the issue.
+ fn verify(&self, cookie_value: &str) -> Result<String, &'static str> {
+ if cookie_value.len() < BASE64_DIGEST_LEN {
+ return Err("length of value is <= BASE64_DIGEST_LEN");
+ }
+
+ let (digest_str, value) = cookie_value.split_at(BASE64_DIGEST_LEN);
+ let sig = base64::decode(digest_str).map_err(|_| "bad base64 digest")?;
+
+ verify(&self.key, value.as_bytes(), &sig)
+ .map(|_| value.to_string())
+ .map_err(|_| "value did not verify")
+ }
+
+ /// Returns a reference to the `Cookie` inside this jar with the name `name`
+ /// and verifies the authenticity and integrity of the cookie's value,
+ /// returning a `Cookie` with the authenticated value. If the cookie cannot
+ /// be found, or the cookie fails to verify, `None` is returned.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::{CookieJar, Cookie, Key};
+ ///
+ /// let key = Key::generate();
+ /// let mut jar = CookieJar::new();
+ /// let mut signed_jar = jar.signed(&key);
+ /// assert!(signed_jar.get("name").is_none());
+ ///
+ /// signed_jar.add(Cookie::new("name", "value"));
+ /// assert_eq!(signed_jar.get("name").unwrap().value(), "value");
+ /// ```
+ 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.verify(cookie.value()) {
+ cookie.set_value(value);
+ return Some(cookie);
+ }
+ }
+
+ None
+ }
+
+ /// Adds `cookie` to the parent jar. The cookie's value is signed assuring
+ /// integrity and authenticity.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::{CookieJar, Cookie, Key};
+ ///
+ /// let key = Key::generate();
+ /// let mut jar = CookieJar::new();
+ /// jar.signed(&key).add(Cookie::new("name", "value"));
+ ///
+ /// assert_ne!(jar.get("name").unwrap().value(), "value");
+ /// assert!(jar.get("name").unwrap().value().contains("value"));
+ /// assert_eq!(jar.signed(&key).get("name").unwrap().value(), "value");
+ /// ```
+ pub fn add(&mut self, mut cookie: Cookie<'static>) {
+ self.sign_cookie(&mut cookie);
+ self.parent.add(cookie);
+ }
+
+ /// Adds an "original" `cookie` to this jar. The cookie's value is signed
+ /// assuring integrity and authenticity. Adding an original cookie does not
+ /// affect the [`CookieJar::delta()`](struct.CookieJar.html#method.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.signed(&key).add_original(Cookie::new("name", "value"));
+ ///
+ /// assert_eq!(jar.iter().count(), 1);
+ /// assert_eq!(jar.delta().count(), 0);
+ /// ```
+ pub fn add_original(&mut self, mut cookie: Cookie<'static>) {
+ self.sign_cookie(&mut cookie);
+ self.parent.add_original(cookie);
+ }
+
+ /// Signs the cookie's value assuring integrity and authenticity.
+ fn sign_cookie(&self, cookie: &mut Cookie) {
+ let digest = sign(&self.key, cookie.value().as_bytes());
+ let mut new_value = base64::encode(digest.as_ref());
+ new_value.push_str(cookie.value());
+ cookie.set_value(new_value);
+ }
+
+ /// 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.
+ ///
+ /// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
+ /// details.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use cookie::{CookieJar, Cookie, Key};
+ ///
+ /// let key = Key::generate();
+ /// let mut jar = CookieJar::new();
+ /// let mut signed_jar = jar.signed(&key);
+ ///
+ /// signed_jar.add(Cookie::new("name", "value"));
+ /// assert!(signed_jar.get("name").is_some());
+ ///
+ /// signed_jar.remove(Cookie::named("name"));
+ /// assert!(signed_jar.get("name").is_none());
+ /// ```
+ pub fn remove(&mut self, cookie: Cookie<'static>) {
+ self.parent.remove(cookie);
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use {CookieJar, Cookie, Key};
+
+ #[test]
+ fn simple() {
+ let key = Key::generate();
+ let mut jar = CookieJar::new();
+ assert_simple_behaviour!(jar, jar.signed(&key));
+ }
+
+ #[test]
+ fn private() {
+ let key = Key::generate();
+ let mut jar = CookieJar::new();
+ assert_secure_behaviour!(jar, jar.signed(&key));
+ }
+}