1 use secure::ring::digest::{SHA256, Algorithm}; 2 use secure::ring::hmac::{SigningKey, sign, verify_with_own_key as verify}; 3 use secure::{base64, Key}; 4 5 use {Cookie, CookieJar}; 6 7 // Keep these in sync, and keep the key len synced with the `signed` docs as 8 // well as the `KEYS_INFO` const in secure::Key. 9 static HMAC_DIGEST: &'static Algorithm = &SHA256; 10 const BASE64_DIGEST_LEN: usize = 44; 11 pub const KEY_LEN: usize = 32; 12 13 /// A child cookie jar that authenticates its cookies. 14 /// 15 /// A _signed_ child jar signs all the cookies added to it and verifies cookies 16 /// retrieved from it. Any cookies stored in a `SignedJar` are assured integrity 17 /// and authenticity. In other words, clients cannot tamper with the contents of 18 /// a cookie nor can they fabricate cookie values, but the data is visible in 19 /// plaintext. 20 /// 21 /// This type is only available when the `secure` feature is enabled. 22 pub struct SignedJar<'a> { 23 parent: &'a mut CookieJar, 24 key: SigningKey 25 } 26 27 impl<'a> SignedJar<'a> { 28 /// Creates a new child `SignedJar` with parent `parent` and key `key`. This 29 /// method is typically called indirectly via the `signed` method of 30 /// `CookieJar`. 31 #[doc(hidden)] new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a>32 pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> { 33 SignedJar { parent: parent, key: SigningKey::new(HMAC_DIGEST, key.signing()) } 34 } 35 36 /// Given a signed value `str` where the signature is prepended to `value`, 37 /// verifies the signed value and returns it. If there's a problem, returns 38 /// an `Err` with a string describing the issue. verify(&self, cookie_value: &str) -> Result<String, &'static str>39 fn verify(&self, cookie_value: &str) -> Result<String, &'static str> { 40 if cookie_value.len() < BASE64_DIGEST_LEN { 41 return Err("length of value is <= BASE64_DIGEST_LEN"); 42 } 43 44 let (digest_str, value) = cookie_value.split_at(BASE64_DIGEST_LEN); 45 let sig = base64::decode(digest_str).map_err(|_| "bad base64 digest")?; 46 47 verify(&self.key, value.as_bytes(), &sig) 48 .map(|_| value.to_string()) 49 .map_err(|_| "value did not verify") 50 } 51 52 /// Returns a reference to the `Cookie` inside this jar with the name `name` 53 /// and verifies the authenticity and integrity of the cookie's value, 54 /// returning a `Cookie` with the authenticated value. If the cookie cannot 55 /// be found, or the cookie fails to verify, `None` is returned. 56 /// 57 /// # Example 58 /// 59 /// ```rust 60 /// use cookie::{CookieJar, Cookie, Key}; 61 /// 62 /// let key = Key::generate(); 63 /// let mut jar = CookieJar::new(); 64 /// let mut signed_jar = jar.signed(&key); 65 /// assert!(signed_jar.get("name").is_none()); 66 /// 67 /// signed_jar.add(Cookie::new("name", "value")); 68 /// assert_eq!(signed_jar.get("name").unwrap().value(), "value"); 69 /// ``` get(&self, name: &str) -> Option<Cookie<'static>>70 pub fn get(&self, name: &str) -> Option<Cookie<'static>> { 71 if let Some(cookie_ref) = self.parent.get(name) { 72 let mut cookie = cookie_ref.clone(); 73 if let Ok(value) = self.verify(cookie.value()) { 74 cookie.set_value(value); 75 return Some(cookie); 76 } 77 } 78 79 None 80 } 81 82 /// Adds `cookie` to the parent jar. The cookie's value is signed assuring 83 /// integrity and authenticity. 84 /// 85 /// # Example 86 /// 87 /// ```rust 88 /// use cookie::{CookieJar, Cookie, Key}; 89 /// 90 /// let key = Key::generate(); 91 /// let mut jar = CookieJar::new(); 92 /// jar.signed(&key).add(Cookie::new("name", "value")); 93 /// 94 /// assert_ne!(jar.get("name").unwrap().value(), "value"); 95 /// assert!(jar.get("name").unwrap().value().contains("value")); 96 /// assert_eq!(jar.signed(&key).get("name").unwrap().value(), "value"); 97 /// ``` add(&mut self, mut cookie: Cookie<'static>)98 pub fn add(&mut self, mut cookie: Cookie<'static>) { 99 self.sign_cookie(&mut cookie); 100 self.parent.add(cookie); 101 } 102 103 /// Adds an "original" `cookie` to this jar. The cookie's value is signed 104 /// assuring integrity and authenticity. Adding an original cookie does not 105 /// affect the [`CookieJar::delta()`](struct.CookieJar.html#method.delta) 106 /// computation. This method is intended to be used to seed the cookie jar 107 /// with cookies received from a client's HTTP message. 108 /// 109 /// For accurate `delta` computations, this method should not be called 110 /// after calling `remove`. 111 /// 112 /// # Example 113 /// 114 /// ```rust 115 /// use cookie::{CookieJar, Cookie, Key}; 116 /// 117 /// let key = Key::generate(); 118 /// let mut jar = CookieJar::new(); 119 /// jar.signed(&key).add_original(Cookie::new("name", "value")); 120 /// 121 /// assert_eq!(jar.iter().count(), 1); 122 /// assert_eq!(jar.delta().count(), 0); 123 /// ``` add_original(&mut self, mut cookie: Cookie<'static>)124 pub fn add_original(&mut self, mut cookie: Cookie<'static>) { 125 self.sign_cookie(&mut cookie); 126 self.parent.add_original(cookie); 127 } 128 129 /// Signs the cookie's value assuring integrity and authenticity. sign_cookie(&self, cookie: &mut Cookie)130 fn sign_cookie(&self, cookie: &mut Cookie) { 131 let digest = sign(&self.key, cookie.value().as_bytes()); 132 let mut new_value = base64::encode(digest.as_ref()); 133 new_value.push_str(cookie.value()); 134 cookie.set_value(new_value); 135 } 136 137 /// Removes `cookie` from the parent jar. 138 /// 139 /// For correct removal, the passed in `cookie` must contain the same `path` 140 /// and `domain` as the cookie that was initially set. 141 /// 142 /// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more 143 /// details. 144 /// 145 /// # Example 146 /// 147 /// ```rust 148 /// use cookie::{CookieJar, Cookie, Key}; 149 /// 150 /// let key = Key::generate(); 151 /// let mut jar = CookieJar::new(); 152 /// let mut signed_jar = jar.signed(&key); 153 /// 154 /// signed_jar.add(Cookie::new("name", "value")); 155 /// assert!(signed_jar.get("name").is_some()); 156 /// 157 /// signed_jar.remove(Cookie::named("name")); 158 /// assert!(signed_jar.get("name").is_none()); 159 /// ``` remove(&mut self, cookie: Cookie<'static>)160 pub fn remove(&mut self, cookie: Cookie<'static>) { 161 self.parent.remove(cookie); 162 } 163 } 164 165 #[cfg(test)] 166 mod test { 167 use {CookieJar, Cookie, Key}; 168 169 #[test] simple()170 fn simple() { 171 let key = Key::generate(); 172 let mut jar = CookieJar::new(); 173 assert_simple_behaviour!(jar, jar.signed(&key)); 174 } 175 176 #[test] private()177 fn private() { 178 let key = Key::generate(); 179 let mut jar = CookieJar::new(); 180 assert_secure_behaviour!(jar, jar.signed(&key)); 181 } 182 } 183