chiark / gitweb /
server: get token, wip hmac work
[hippotat.git] / src / utils.rs
index 95498f95af7a33a2202124b4fe328f23bc911d3d..e8508b5a6909030fcca62c32884d0b4365029445 100644 (file)
@@ -1,5 +1,5 @@
 // Copyright 2021 Ian Jackson and contributors to Hippotat
-// SPDX-License-Identifier: AGPL-3.0-or-later
+// SPDX-License-Identifier: GPL-3.0-or-later
 // There is NO WARRANTY.
 
 use crate::prelude::*;
@@ -16,17 +16,64 @@ impl<T,E> Result<T,E> where AE: From<E> {
   }
 }
 
+#[derive(Error,Debug)]
+pub enum ReadLimitedError {
+  #[error("maximum size {limit} exceeded")]
+  Truncated { sofar: Box<[u8]>, limit: usize },
+
+  #[error("HTTP error {0}")]
+  Hyper(#[from] hyper::Error),
+}
+
+impl ReadLimitedError {
+  pub fn discard_data(&mut self) { match self {
+    ReadLimitedError::Truncated { sofar,.. } => { mem::take(sofar); },
+    _ => { },
+  } }
+}
+#[ext(pub)]
+impl<T> Result<T,ReadLimitedError> {
+  fn discard_data(self) -> Self {
+    self.map_err(|mut e| { e.discard_data(); e })
+  }
+}
+
+#[throws(ReadLimitedError)]
+pub async fn read_limited_bytes<S>(limit: usize, stream: &mut S) -> Box<[u8]>
+where S: futures::Stream<Item=Result<hyper::body::Bytes,hyper::Error>>
+         + Debug + Unpin,
+      // we also require that the Stream is cancellation-safe
+{
+  let mut accum = vec![];
+  while let Some(item) = stream.next().await {
+    let b = item?;
+    accum.extend(b);
+    if accum.len() > limit {
+      throw!(ReadLimitedError::Truncated { limit, sofar: accum.into() })
+    }
+  }
+  accum.into()
+}
+
+pub fn time_t_now() -> u64 {
+  SystemTime::now()
+    .duration_since(UNIX_EPOCH)
+    .unwrap_or_else(|_| Duration::default()) // clock is being weird
+    .as_secs()
+}
+
 use sha2::Digest as _;
 
-type HmacH = sha2::Sha256;
-const HMAC_L: usize = 32;
+pub type HmacH = sha2::Sha256;
+pub const HMAC_B: usize = 64;
+pub const HMAC_L: usize = 32;
 
-#[throws(AE)]
-fn token_hmac(key: &[u8], message: &[u8]) -> [u8; HMAC_L] {
+pub fn token_hmac(key: &[u8], message: &[u8]) -> [u8; HMAC_L] {
   let key = {
-    let mut padded = [0; HMAC_L];
-    if key.len() > HMAC_L {
-      padded = HmacH::digest(key).into();
+    let mut padded = [0; HMAC_B];
+    if key.len() > padded.len() {
+      let digest: [u8; HMAC_L] = HmacH::digest(key).into();
+      padded[0..HMAC_L].copy_from_slice(&digest);
     } else {
       padded[0.. key.len()].copy_from_slice(key);
     }
@@ -35,6 +82,8 @@ fn token_hmac(key: &[u8], message: &[u8]) -> [u8; HMAC_L] {
   let mut ikey = key;  for k in &mut ikey { *k ^= 0x36; }
   let mut okey = key;  for k in &mut okey { *k ^= 0x5C; }
 
+//dbg!(&key, &ikey, &okey);
+
   let h1 = HmacH::new()
     .chain(&ikey)
     .chain(message)
@@ -45,3 +94,120 @@ fn token_hmac(key: &[u8], message: &[u8]) -> [u8; HMAC_L] {
     .finalize();
   h2.into()
 }
+
+#[test]
+fn hmac_test_vectors(){
+  // C&P from RFC 4231
+  let vectors = r#"
+   Key =          0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
+                  0b0b0b0b                          (20 bytes)
+   Data =         4869205468657265                  ("Hi There")
+
+   HMAC-SHA-256 = b0344c61d8db38535ca8afceaf0bf12b
+                  881dc200c9833da726e9376c2e32cff7
+
+    
+   Key =          4a656665                          ("Jefe")
+   Data =         7768617420646f2079612077616e7420  ("what do ya want ")
+                  666f72206e6f7468696e673f          ("for nothing?")
+
+   HMAC-SHA-256 = 5bdcc146bf60754e6a042426089575c7
+                  5a003f089d2739839dec58b964ec3843
+
+
+   Key =          aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaa                          (20 bytes)
+   Data =         dddddddddddddddddddddddddddddddd
+                  dddddddddddddddddddddddddddddddd
+                  dddddddddddddddddddddddddddddddd
+                  dddd                              (50 bytes)
+
+   HMAC-SHA-256 = 773ea91e36800e46854db8ebd09181a7
+                  2959098b3ef8c122d9635514ced565fe
+
+
+   Key =          0102030405060708090a0b0c0d0e0f10
+                  111213141516171819                (25 bytes)
+   Data =         cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd
+                  cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd
+                  cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd
+                  cdcd                              (50 bytes)
+
+   HMAC-SHA-256 = 82558a389a443c0ea4cc819899f2083a
+                  85f0faa3e578f8077a2e3ff46729665b
+
+
+
+   Key =          aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaa                            (131 bytes)
+   Data =         54657374205573696e67204c61726765  ("Test Using Large")
+                  72205468616e20426c6f636b2d53697a  ("r Than Block-Siz")
+                  65204b6579202d2048617368204b6579  ("e Key - Hash Key")
+                  204669727374                      (" First")
+
+   HMAC-SHA-256 = 60e431591ee0b67f0d8a26aacbf5b77f
+                  8e0bc6213728c5140546040f0ee37f54
+
+   Key =          aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+                  aaaaaa                            (131 bytes)
+   Data =         54686973206973206120746573742075  ("This is a test u")
+                  73696e672061206c6172676572207468  ("sing a larger th")
+                  616e20626c6f636b2d73697a65206b65  ("an block-size ke")
+                  7920616e642061206c61726765722074  ("y and a larger t")
+                  68616e20626c6f636b2d73697a652064  ("han block-size d")
+                  6174612e20546865206b6579206e6565  ("ata. The key nee")
+                  647320746f2062652068617368656420  ("ds to be hashed ")
+                  6265666f7265206265696e6720757365  ("before being use")
+                  642062792074686520484d414320616c  ("d by the HMAC al")
+                  676f726974686d2e                  ("gorithm.")
+
+   HMAC-SHA-256 = 9b09ffa71b942fcb27635fbcd5b0e944
+                  bfdc63644f0713938a7f51535c3a35e2
+"#;
+  let vectors = regex_replace_all!{
+    r#"\(.*\)"#,
+    vectors.trim_end(),
+    |_| "",
+  };
+  let vectors = regex_replace_all!{
+    r#" *\n                  "#,
+    &vectors,
+    |_| "",
+  };
+  let vectors = regex_replace_all!{
+    r#"\s*\n"#,
+    &vectors,
+    |_| "\n",
+  };
+  let mut lines = vectors.split('\n');
+  assert_eq!( lines.next().unwrap(), "" );
+  let mut get = |prefix| {
+    let l = lines.next()?;
+    dbg!(l);
+    let b = l.strip_prefix(prefix).unwrap().as_bytes().chunks(2)
+      .map(|s| str::from_utf8(s).unwrap())
+      .map(|s| { assert_eq!(s.len(), 2); u8::from_str_radix(s,16).unwrap() })
+      .collect::<Vec<u8>>();
+    Some(b)
+  };
+  while let Some(key) = get("   Key =          ") {
+    let data = get("   Data =         ").unwrap();
+    let exp = get("   HMAC-SHA-256 = ").unwrap();
+    let got = token_hmac(&key, &data);
+    assert_eq!(&got[..], &exp);
+  }
+}