chiark / gitweb /
server: get token, wip hmac work
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 9 Aug 2021 02:03:59 +0000 (03:03 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 9 Aug 2021 02:03:59 +0000 (03:03 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/bin/client.rs
src/bin/server.rs
src/prelude.rs
src/utils.rs

index 322482a5c04de56604f5ed6105fce8d50eb9f9d7..2578f7aabc6c5421b42be7ff254b3ca12d7fd19a 100644 (file)
@@ -45,10 +45,7 @@ fn submit_request<'r, 'c:'r, C:HCC>(
     .saturating_add(Duration::from_nanos(999_999_999))
     .as_secs();
 
-  let time_t = SystemTime::now()
-    .duration_since(UNIX_EPOCH)
-    .unwrap_or_else(|_| Duration::default()) // clock is being weird
-    .as_secs();
+  let time_t = time_t_now();
   let time_t = format!("{:x}", time_t);
   let hmac = token_hmac(c.ic.secret.0.as_bytes(), time_t.as_bytes());
   let mut token = time_t;
index 18785122831681b708ddc082b599d6d978645351..bc56fe7fd5af97090b438de873cc1501930832d9 100644 (file)
@@ -27,7 +27,7 @@ struct WebRequest {
 }
 
 async fn handle(
-  _all_clients: Arc<AllClients>,
+  all_clients: Arc<AllClients>,
   req: hyper::Request<hyper::Body>
 ) -> Result<hyper::Response<hyper::Body>, Void> {
   if req.method() == Method::GET {
@@ -91,12 +91,49 @@ async fn handle(
     let mut meta = MetadataFieldIterator::new(comp.payload_start);
 
     let client: ClientName = meta.need_parse().context("client addr")?;
-    let token: &str = meta.need_next().context("token")?;
-    
-// let client = all_clients.get(&client).ok_or_else(|| anyhow!(BAD_CLIENT))?;
 
+    let (client_time, hmac_got) = (||{
+      let token: &str = meta.need_next().context(r#"find in "m""#)?;
+      let (time_t, hmac_got) = token.split_once(' ')
+        .ok_or_else(|| anyhow!("split"))?;
+      let time_t: u64 = time_t.parse().context("parse time_t")?;
+      Ok::<_,AE>((time_t, hmac_got))
+    })().context("token")?;
+
+    let client = all_clients.get(&client);
+
+    // We attempt to hide whether the client exists we don't try to
+    // hide the hash lookup computationgs, but we do try to hide the
+    // HMAC computation by always doing it.  We hope that the compiler
+    // doesn't produce a specialised implementation for the dummy
+    // secret value.
+    let client_exists = subtle::Choice::from(client.is_some() as u8);
+    let secret = client.map(|c| c.ic.secret.0.as_bytes());
+    let secret = secret.unwrap_or(&[0x55; HMAC_B][..]);
+    let client_time_s = format!("{:x}", client_time);
+    let hmac_exp = token_hmac(secret, client_time_s.as_bytes());
+    // We also definitely want a consttime memeq for the hmac value
+    let hmac_ok = hmac_got.as_bytes().ct_eq(&hmac_exp);
+    if ! bool::from(hmac_ok & client_exists) {
+      throw!(anyhow!("xxx should be a 403 error"));
+    }
+
+    let client = client.unwrap();
+    let now = time_t_now();
+    let chk_skew = |a: u64, b: u64, c_ahead_behind| {
+      if let Some(a_ahead) = a.checked_sub(b) {
+        if a_ahead > client.ic.max_clock_skew.as_secs() {
+          throw!(anyhow!("too much clock skew (client {} by {})",
+                         c_ahead_behind, a_ahead));
+        }
+      }
+      Ok::<_,AE>(())
+    };
+    chk_skew(client_time, now, "ahead")?;
+    chk_skew(now, client_time, "behind")?;
+    
     eprintln!("boundary={:?} start={} name={:?} client={}",
-              boundary, start, &comp.name, client);
+              boundary, start, &comp.name, &client.ic);
 
     Ok::<_,AE>(())
   }.await {
@@ -115,6 +152,7 @@ async fn handle(
 async fn run_client(_ic: Arc<InstanceConfig>, _web: mpsc::Receiver<WebRequest>)
                     -> Result<Void, AE>
 {
+  tokio::time::sleep(Duration::from_secs(1_000_000_000)).await;
   Err(anyhow!("xxx"))
 }
 
index 931a9fa15d3155144a84e7d7d91e587f3203cd30..078dac1a9beb979fbed546031f8905caadc9bb09 100644 (file)
@@ -39,6 +39,7 @@ pub use lazy_static::lazy_static;
 pub use log::{trace, debug, info, warn, error};
 pub use memchr::memmem;
 pub use structopt::StructOpt;
+pub use subtle::ConstantTimeEq;
 pub use thiserror::Error;
 pub use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
 pub use tokio::pin;
index a254b7b60e51acf0c7084817e78f7eaec94b3242..e8508b5a6909030fcca62c32884d0b4365029445 100644 (file)
@@ -55,11 +55,18 @@ where S: futures::Stream<Item=Result<hyper::body::Bytes,hyper::Error>>
   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_B: usize = 64;
-const HMAC_L: usize = 32;
+pub type HmacH = sha2::Sha256;
+pub const HMAC_B: usize = 64;
+pub const HMAC_L: usize = 32;
 
 pub fn token_hmac(key: &[u8], message: &[u8]) -> [u8; HMAC_L] {
   let key = {