// Keep these in sync, and keep the key len synced with the `signed` docs as // well as the `KEYS_INFO` const in secure::Key. pub(crate) const BASE64_DIGEST_LEN: usize = 44; pub(crate) 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 provided /// 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. #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))] pubstruct SignedJar<J> {
parent: J,
key: [u8; KEY_LEN],
}
impl<J> SignedJar<J> { /// Creates a new child `SignedJar` with parent `parent` and key `key`. This /// method is typically called indirectly via the `signed{_mut}` methods of /// `CookieJar`. pub(crate) fn new(parent: J, key: &Key) -> SignedJar<J> {
SignedJar { parent, key: key.signing().try_into().expect("sign key len") }
}
/// Signs the cookie's value providing integrity and authenticity. fn sign_cookie(&self, cookie: &mut Cookie) { // Compute HMAC-SHA256 of the cookie's value. letmut mac = Hmac::<Sha256>::new_from_slice(&self.key).expect("good key");
mac.update(cookie.value().as_bytes());
// Cookie's new value is [MAC | original-value]. letmut new_value = base64::encode(&mac.finalize().into_bytes());
new_value.push_str(cookie.value());
cookie.set_value(new_value);
}
/// 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, &='color:blue'>'static str> { if !cookie_value.is_char_boundary(BASE64_DIGEST_LEN) { return Err("missing or invalid digest");
}
// Split [MAC | original-value] into its two parts. let (digest_str, value) = cookie_value.split_at(BASE64_DIGEST_LEN); let digest = base64::decode(digest_str).map_err(|_| "bad base64 digest")?;
// Perform the verification. letmut mac = Hmac::<Sha256>::new_from_slice(&self.key).expect("good key");
mac.update(value.as_bytes());
mac.verify_slice(&digest)
.map(|_| value.to_string())
.map_err(|_| "value did not verify")
}
/// Verifies the authenticity and integrity of `cookie`, returning the /// plaintext version if verification succeeds or `None` otherwise. /// Verification _always_ succeeds if `cookie` was generated by a /// `SignedJar` with the same key as `self`. /// /// # Example /// /// ```rust /// use cookie::{CookieJar, Cookie, Key}; /// /// let key = Key::generate(); /// let mut jar = CookieJar::new(); /// assert!(jar.signed(&key).get("name").is_none()); /// /// jar.signed_mut(&key).add(Cookie::new("name", "value")); /// assert_eq!(jar.signed(&key).get("name").unwrap().value(), "value"); /// /// let plain = jar.get("name").cloned().unwrap(); /// assert_ne!(plain.value(), "value"); /// let verified = jar.signed(&key).verify(plain).unwrap(); /// assert_eq!(verified.value(), "value"); /// /// let plain = Cookie::new("plaintext", "hello"); /// assert!(jar.signed(&key).verify(plain).is_none()); /// ``` pubfn verify(&self, mut cookie: Cookie<'static>) -> Option<Cookie<'static>> { iflet Ok(value) = self._verify(cookie.value()) {
cookie.set_value(value); return Some(cookie);
}
None
}
}
impl<J: Borrow<CookieJar>> SignedJar<J> { /// 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 jar = CookieJar::new(); /// assert!(jar.signed(&key).get("name").is_none()); /// /// let mut jar = jar; /// let mut signed_jar = jar.signed_mut(&key); /// signed_jar.add(Cookie::new("name", "value")); /// assert_eq!(signed_jar.get("name").unwrap().value(), "value"); /// ``` pubfn get(&self, name: &str) -> Option<Cookie<'static>> { self.parent.borrow().get(name).and_then(|c| self.verify(c.clone()))
}
}
impl<J: BorrowMut<CookieJar>> SignedJar<J> { /// 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_mut(&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"); /// ``` pubfn add(&mutself, mut cookie: Cookie<'static>) { self.sign_cookie(&mut cookie); self.parent.borrow_mut().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()`] 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_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.sign_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 signed_jar = jar.signed_mut(&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()); /// ``` 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.signed_mut(&key));
}
#[test] fn private() { let key = Key::generate(); letmut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.signed_mut(&key));
}
letmut jar = CookieJar::new();
jar.add(Cookie::new("signed_with_ring014", "3tdHXEQ2kf6fxC7dWzBGmpSLMtJenXLKrZ9cHkSsl1w=Tamper-proof"));
jar.add(Cookie::new("signed_with_ring016", "3tdHXEQ2kf6fxC7dWzBGmpSLMtJenXLKrZ9cHkSsl1w=Tamper-proof"));
let signed = jar.signed(&key);
assert_eq!(signed.get("signed_with_ring014").unwrap().value(), "Tamper-proof");
assert_eq!(signed.get("signed_with_ring016").unwrap().value(), "Tamper-proof");
}
#[test] fn issue_178() { let data = "x=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy£"; let c = Cookie::parse(data).expect("failed to parse cookie"); let key = Key::from(&[0u8; 64]); letmut jar = CookieJar::new(); let signed = jar.signed_mut(&key);
assert!(signed.verify(c).is_none());
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet am 2026-06-19)
¤
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.