summaryrefslogtreecommitdiffstats
path: root/third_party/rust/hawk/src/mac.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/hawk/src/mac.rs')
-rw-r--r--third_party/rust/hawk/src/mac.rs200
1 files changed, 200 insertions, 0 deletions
diff --git a/third_party/rust/hawk/src/mac.rs b/third_party/rust/hawk/src/mac.rs
new file mode 100644
index 0000000000..d56e755c86
--- /dev/null
+++ b/third_party/rust/hawk/src/mac.rs
@@ -0,0 +1,200 @@
+use crate::credentials::Key;
+use crate::error::*;
+use base64::{display::Base64Display, STANDARD};
+use std::io::Write;
+use std::ops::Deref;
+use std::time::{SystemTime, UNIX_EPOCH};
+
+/// The kind of MAC calcuation (corresponding to the first line of the message)
+pub enum MacType {
+ Header,
+ Response,
+ Bewit,
+}
+
+/// Mac represents a message authentication code, the signature in a Hawk transaction.
+///
+/// This class supports creating Macs using the Hawk specification, and comparing Macs
+/// using a cosntant-time comparison (thus preventing timing side-channel attacks).
+#[derive(Debug, Clone)]
+pub struct Mac(Vec<u8>);
+
+impl Mac {
+ pub fn new(
+ mac_type: MacType,
+ key: &Key,
+ ts: SystemTime,
+ nonce: &str,
+ method: &str,
+ host: &str,
+ port: u16,
+ path: &str,
+ hash: Option<&[u8]>,
+ ext: Option<&str>,
+ ) -> Result<Mac> {
+ // Note: there's a \n after each item.
+ let mut buffer: Vec<u8> = Vec::with_capacity(
+ 15 + 1 + // mac_type (worst case since it doesn't really matter)
+ 10 + 1 + // ts (in practice this will be 10 bytes)
+ nonce.len() + 1 +
+ host.len() + 1 +
+ 6 + 1 + // Longer than 6 bytes of port seems very unlikely
+ path.len() + 1 +
+ hash.map_or(0, |h| h.len() * 4 / 3) + 1 +
+ ext.map_or(0, str::len) + 1,
+ );
+
+ writeln!(
+ buffer,
+ "{mac_type}\n{ts}\n{nonce}\n{method}\n{path}\n{host}\n{port}",
+ mac_type = match mac_type {
+ MacType::Header => "hawk.1.header",
+ MacType::Response => "hawk.1.response",
+ MacType::Bewit => "hawk.1.bewit",
+ },
+ ts = ts.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs(),
+ nonce = nonce,
+ method = method,
+ path = path,
+ host = host,
+ port = port,
+ )?;
+
+ if let Some(h) = hash {
+ writeln!(buffer, "{}", Base64Display::with_config(h, STANDARD))?;
+ } else {
+ writeln!(buffer)?;
+ }
+ writeln!(buffer, "{}", ext.unwrap_or_default())?;
+
+ Ok(Mac(key.sign(buffer.as_ref())?))
+ }
+}
+
+impl AsRef<[u8]> for Mac {
+ fn as_ref(&self) -> &[u8] {
+ &self.0[..]
+ }
+}
+
+impl From<Vec<u8>> for Mac {
+ fn from(original: Vec<u8>) -> Self {
+ Mac(original)
+ }
+}
+
+impl Deref for Mac {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl PartialEq for Mac {
+ fn eq(&self, other: &Mac) -> bool {
+ crate::crypto::constant_time_compare(&self.0, &other.0)
+ }
+}
+
+#[cfg(all(test, any(feature = "use_ring", feature = "use_openssl")))]
+mod test {
+ use super::{Mac, MacType};
+ use crate::credentials::Key;
+ use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+ fn key() -> 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::SHA256,
+ )
+ .unwrap()
+ }
+
+ fn sys_time(secs: u64, ns: u32) -> SystemTime {
+ UNIX_EPOCH + Duration::new(secs, ns)
+ }
+
+ #[test]
+ fn test_make_mac() {
+ let key = key();
+ let mac = Mac::new(
+ MacType::Header,
+ &key,
+ sys_time(1000, 100),
+ "nonny",
+ "POST",
+ "mysite.com",
+ 443,
+ "/v1/api",
+ None,
+ None,
+ )
+ .unwrap();
+ println!("got {:?}", mac);
+ assert!(
+ mac.0
+ == vec![
+ 192, 227, 235, 121, 157, 185, 197, 79, 189, 214, 235, 139, 9, 232, 99, 55, 67,
+ 30, 68, 0, 150, 187, 192, 238, 21, 200, 209, 107, 245, 159, 243, 178
+ ]
+ );
+ }
+
+ #[test]
+ fn test_make_mac_hash() {
+ let key = key();
+ let hash = vec![1, 2, 3, 4, 5];
+ let mac = Mac::new(
+ MacType::Header,
+ &key,
+ sys_time(1000, 100),
+ "nonny",
+ "POST",
+ "mysite.com",
+ 443,
+ "/v1/api",
+ Some(&hash),
+ None,
+ )
+ .unwrap();
+ println!("got {:?}", mac);
+ assert!(
+ mac.0
+ == vec![
+ 61, 128, 208, 253, 88, 135, 190, 196, 1, 69, 153, 193, 124, 4, 195, 87, 38, 96,
+ 181, 34, 65, 234, 58, 157, 175, 175, 145, 151, 61, 0, 57, 5
+ ]
+ );
+ }
+
+ #[test]
+ fn test_make_mac_ext() {
+ let key = key();
+ let ext = "ext-data".to_string();
+ let mac = Mac::new(
+ MacType::Header,
+ &key,
+ sys_time(1000, 100),
+ "nonny",
+ "POST",
+ "mysite.com",
+ 443,
+ "/v1/api",
+ None,
+ Some(&ext),
+ )
+ .unwrap();
+ println!("got {:?}", mac);
+ assert!(
+ mac.0
+ == vec![
+ 187, 104, 238, 100, 168, 112, 37, 68, 187, 141, 168, 155, 177, 193, 113, 0, 50,
+ 105, 127, 36, 24, 117, 200, 251, 138, 199, 108, 14, 105, 123, 234, 119
+ ]
+ );
+ }
+}