From: Ian Jackson Date: Mon, 9 Aug 2021 02:03:59 +0000 (+0100) Subject: server: get token, wip hmac work X-Git-Tag: hippotat/1.0.0~189 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;ds=sidebyside;h=6fc2d0b1be19815ffefad50d4459931ea0ca8c5b;p=hippotat.git server: get token, wip hmac work Signed-off-by: Ian Jackson --- diff --git a/src/bin/client.rs b/src/bin/client.rs index 322482a..2578f7a 100644 --- a/src/bin/client.rs +++ b/src/bin/client.rs @@ -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; diff --git a/src/bin/server.rs b/src/bin/server.rs index 1878512..bc56fe7 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -27,7 +27,7 @@ struct WebRequest { } async fn handle( - _all_clients: Arc, + all_clients: Arc, req: hyper::Request ) -> Result, 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, _web: mpsc::Receiver) -> Result { + tokio::time::sleep(Duration::from_secs(1_000_000_000)).await; Err(anyhow!("xxx")) } diff --git a/src/prelude.rs b/src/prelude.rs index 931a9fa..078dac1 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -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; diff --git a/src/utils.rs b/src/utils.rs index a254b7b..e8508b5 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -55,11 +55,18 @@ where S: futures::Stream> 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 = {