summaryrefslogtreecommitdiffstats
path: root/third_party/rust/hawk/src/bewit.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/hawk/src/bewit.rs
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/hawk/src/bewit.rs')
-rw-r--r--third_party/rust/hawk/src/bewit.rs212
1 files changed, 212 insertions, 0 deletions
diff --git a/third_party/rust/hawk/src/bewit.rs b/third_party/rust/hawk/src/bewit.rs
new file mode 100644
index 0000000000..ea3db202a3
--- /dev/null
+++ b/third_party/rust/hawk/src/bewit.rs
@@ -0,0 +1,212 @@
+use crate::error::*;
+use crate::mac::Mac;
+use std::borrow::Cow;
+use std::str;
+use std::str::FromStr;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+/// A Bewit is a piece of data attached to a GET request that functions in place of a Hawk
+/// Authentication header. It contains an id, a timestamp, a MAC, and an optional `ext` value.
+/// These are available using accessor functions.
+#[derive(Clone, Debug, PartialEq)]
+pub struct Bewit<'a> {
+ id: Cow<'a, str>,
+ exp: SystemTime,
+ mac: Cow<'a, Mac>,
+ ext: Option<Cow<'a, str>>,
+}
+
+impl<'a> Bewit<'a> {
+ /// Create a new Bewit with the given values.
+ ///
+ /// See Request.make_bewit for an easier way to make a Bewit
+ pub fn new(id: &'a str, exp: SystemTime, mac: Mac, ext: Option<&'a str>) -> Bewit<'a> {
+ Bewit {
+ id: Cow::Borrowed(id),
+ exp,
+ mac: Cow::Owned(mac),
+ ext: match ext {
+ Some(s) => Some(Cow::Borrowed(s)),
+ None => None,
+ },
+ }
+ }
+
+ /// Generate the fully-encoded string for this Bewit
+ pub fn to_str(&self) -> String {
+ use base64::display::Base64Display;
+ let raw = format!(
+ "{}\\{}\\{}\\{}",
+ self.id,
+ self.exp
+ .duration_since(UNIX_EPOCH)
+ .unwrap_or_default()
+ .as_secs(),
+ Base64Display::with_config(self.mac.as_ref(), base64::STANDARD),
+ match self.ext {
+ Some(ref cow) => cow.as_ref(),
+ None => "",
+ }
+ );
+
+ base64::encode_config(&raw, base64::URL_SAFE_NO_PAD)
+ }
+
+ /// Get the Bewit's client identifier
+ pub fn id(&self) -> &str {
+ self.id.as_ref()
+ }
+
+ /// Get the expiration time of the bewit
+ pub fn exp(&self) -> SystemTime {
+ self.exp
+ }
+
+ /// Get the MAC included in the Bewit
+ pub fn mac(&self) -> &Mac {
+ self.mac.as_ref()
+ }
+
+ /// Get the Bewit's `ext` field.
+ pub fn ext(&self) -> Option<&str> {
+ match self.ext {
+ Some(ref cow) => Some(cow.as_ref()),
+ None => None,
+ }
+ }
+}
+
+const BACKSLASH: u8 = b'\\';
+
+impl<'a> FromStr for Bewit<'a> {
+ type Err = Error;
+ fn from_str(bewit: &str) -> Result<Bewit<'a>> {
+ let bewit = base64::decode(bewit)?;
+
+ let parts: Vec<&[u8]> = bewit.split(|c| *c == BACKSLASH).collect();
+ if parts.len() != 4 {
+ return Err(InvalidBewit::Format.into());
+ }
+
+ let id = String::from_utf8(parts[0].to_vec()).map_err(|_| InvalidBewit::Id)?;
+
+ let exp = str::from_utf8(parts[1]).map_err(|_| InvalidBewit::Exp)?;
+ let exp = u64::from_str(exp).map_err(|_| InvalidBewit::Exp)?;
+ let exp = UNIX_EPOCH + Duration::new(exp, 0);
+
+ let mac = str::from_utf8(parts[2]).map_err(|_| InvalidBewit::Mac)?;
+ let mac = Mac::from(base64::decode(mac).map_err(|_| InvalidBewit::Mac)?);
+
+ let ext = match parts[3].len() {
+ 0 => None,
+ _ => Some(Cow::Owned(
+ String::from_utf8(parts[3].to_vec()).map_err(|_| InvalidBewit::Ext)?,
+ )),
+ };
+
+ Ok(Bewit {
+ id: Cow::Owned(id),
+ exp,
+ mac: Cow::Owned(mac),
+ ext,
+ })
+ }
+}
+
+#[cfg(all(test, any(feature = "use_ring", feature = "use_openssl")))]
+mod test {
+ use super::*;
+ use crate::credentials::Key;
+ use crate::mac::{Mac, MacType};
+ use std::str::FromStr;
+
+ const BEWIT_STR: &str =
+ "bWVcMTM1MzgzMjgzNFxmaXk0ZTV3QmRhcEROeEhIZUExOE5yU3JVMVUzaVM2NmdtMFhqVEpwWXlVPVw";
+ const BEWIT_WITH_EXT_STR: &str =
+ "bWVcMTM1MzgzMjgzNFxmaXk0ZTV3QmRhcEROeEhIZUExOE5yU3JVMVUzaVM2NmdtMFhqVEpwWXlVPVxhYmNk";
+
+ fn make_mac() -> Mac {
+ let key = Key::new(
+ vec![
+ 11u8, 19, 228, 209, 79, 189, 200, 59, 166, 47, 86, 254, 235, 184, 120, 197, 75,
+ 152, 201, 79, 115, 61, 111, 242, 219, 187, 173, 14, 227, 108, 60, 232,
+ ],
+ crate::DigestAlgorithm::Sha256,
+ )
+ .unwrap();
+ Mac::new(
+ MacType::Header,
+ &key,
+ UNIX_EPOCH + Duration::new(1353832834, 100),
+ "nonny",
+ "POST",
+ "mysite.com",
+ 443,
+ "/v1/api",
+ None,
+ None,
+ )
+ .unwrap()
+ }
+
+ #[test]
+ fn test_to_str() {
+ let bewit = Bewit::new(
+ "me",
+ UNIX_EPOCH + Duration::new(1353832834, 0),
+ make_mac(),
+ None,
+ );
+ assert_eq!(bewit.to_str(), BEWIT_STR);
+ let bewit = Bewit::new(
+ "me",
+ UNIX_EPOCH + Duration::new(1353832834, 0),
+ make_mac(),
+ Some("abcd"),
+ );
+ assert_eq!(bewit.to_str(), BEWIT_WITH_EXT_STR);
+ }
+
+ #[test]
+ fn test_accessors() {
+ let bewit = Bewit::from_str(BEWIT_STR).unwrap();
+ assert_eq!(bewit.id(), "me");
+ assert_eq!(bewit.exp(), UNIX_EPOCH + Duration::new(1353832834, 0));
+ assert_eq!(bewit.mac(), &make_mac());
+ assert_eq!(bewit.ext(), None);
+ }
+
+ #[test]
+ fn test_from_str_invalid_base64() {
+ assert!(Bewit::from_str("!/==").is_err());
+ }
+
+ #[test]
+ fn test_from_str_invalid_too_many_parts() {
+ let bewit = base64::encode(&"a\\123\\abc\\ext\\WHUT?".as_bytes());
+ assert!(Bewit::from_str(&bewit).is_err());
+ }
+
+ #[test]
+ fn test_from_str_invalid_too_few_parts() {
+ let bewit = base64::encode(&"a\\123\\abc".as_bytes());
+ assert!(Bewit::from_str(&bewit).is_err());
+ }
+
+ #[test]
+ fn test_from_str_invalid_not_utf8() {
+ let a = 'a' as u8;
+ let one = '1' as u8;
+ let slash = '\\' as u8;
+ let invalid1 = 0u8;
+ let invalid2 = 159u8;
+ let bewit = base64::encode(&[invalid1, invalid2, slash, one, slash, a, slash, a]);
+ assert!(Bewit::from_str(&bewit).is_err());
+ let bewit = base64::encode(&[a, slash, invalid1, invalid2, slash, a, slash, a]);
+ assert!(Bewit::from_str(&bewit).is_err());
+ let bewit = base64::encode(&[a, slash, one, slash, invalid1, invalid2, slash, a]);
+ assert!(Bewit::from_str(&bewit).is_err());
+ let bewit = base64::encode(&[a, slash, one, slash, a, slash, invalid1, invalid2]);
+ assert!(Bewit::from_str(&bewit).is_err());
+ }
+}