From 5e5cde1624b1b4ffd00efa73935c48e547a5a8d3 Mon Sep 17 00:00:00 2001 From: Rutger Broekhoff Date: Sat, 30 Mar 2024 15:19:49 +0100 Subject: Restructure authorization flow Tried to write verify_claims such that the happy path is to the left as much as possible. --- common/src/lib.rs | 102 +++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'common/src') diff --git a/common/src/lib.rs b/common/src/lib.rs index c26150d..917f566 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -7,6 +7,27 @@ use std::{ }; use subtle::ConstantTimeEq; +#[repr(u8)] +enum AuthType { + BatchApi = 1, + Download = 2, +} + +#[derive(Debug, Copy, Clone)] +pub struct Claims<'a> { + pub specific_claims: SpecificClaims, + pub repo_path: &'a str, + pub expires_at: DateTime, +} + +#[derive(Debug, Copy, Clone)] +pub enum SpecificClaims { + BatchApi(Operation), + Download(Oid), +} + +pub type Oid = Digest<32>; + #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] #[repr(u8)] pub enum Operation { @@ -16,6 +37,29 @@ pub enum Operation { Upload = 2, } +/// Returns None if the claims are invalid. Repo path length may be no more than 100 bytes. +pub fn generate_tag(claims: Claims, key: impl AsRef<[u8]>) -> Option> { + if claims.repo_path.len() > 100 { + return None; + } + + let mut hmac = hmac_sha256::HMAC::new(key); + match claims.specific_claims { + SpecificClaims::BatchApi(operation) => { + hmac.update([AuthType::BatchApi as u8]); + hmac.update([operation as u8]); + } + SpecificClaims::Download(oid) => { + hmac.update([AuthType::Download as u8]); + hmac.update(oid.as_bytes()); + } + } + hmac.update([claims.repo_path.len() as u8]); + hmac.update(claims.repo_path.as_bytes()); + hmac.update(claims.expires_at.timestamp().to_be_bytes()); + Some(hmac.finalize().into()) +} + #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub struct ParseOperationError; @@ -37,12 +81,6 @@ impl FromStr for Operation { } } -#[repr(u8)] -enum AuthType { - BatchApi = 1, - Download = 2, -} - /// None means out of range. fn decode_nibble(c: u8) -> Option { if c.is_ascii_digit() { @@ -148,6 +186,13 @@ fn parse_hex_exact(value: &str, buf: &mut [u8]) -> Result<(), ParseHexError> { Ok(()) } +pub type Key = SafeByteArray<64>; + +pub fn load_key(path: &str) -> Result { + let key_str = std::fs::read_to_string(path).map_err(ReadHexError::Io)?; + key_str.trim().parse().map_err(ReadHexError::Format) +} + pub struct SafeByteArray { inner: [u8; N], } @@ -192,44 +237,6 @@ impl FromStr for SafeByteArray { } } -pub type Oid = Digest<32>; - -#[derive(Debug, Copy, Clone)] -pub enum SpecificClaims { - BatchApi(Operation), - Download(Oid), -} - -#[derive(Debug, Copy, Clone)] -pub struct Claims<'a> { - pub specific_claims: SpecificClaims, - pub repo_path: &'a str, - pub expires_at: DateTime, -} - -/// Returns None if the claims are invalid. Repo path length may be no more than 100 bytes. -pub fn generate_tag(claims: Claims, key: impl AsRef<[u8]>) -> Option> { - if claims.repo_path.len() > 100 { - return None; - } - - let mut hmac = hmac_sha256::HMAC::new(key); - match claims.specific_claims { - SpecificClaims::BatchApi(operation) => { - hmac.update([AuthType::BatchApi as u8]); - hmac.update([operation as u8]); - } - SpecificClaims::Download(oid) => { - hmac.update([AuthType::Download as u8]); - hmac.update(oid.as_bytes()); - } - } - hmac.update([claims.repo_path.len() as u8]); - hmac.update(claims.repo_path.as_bytes()); - hmac.update(claims.expires_at.timestamp().to_be_bytes()); - Some(hmac.finalize().into()) -} - pub struct HexFmt>(pub B); impl> fmt::Display for HexFmt { @@ -339,10 +346,3 @@ impl Serialize for Digest { serializer.serialize_str(&format!("{self}")) } } - -pub type Key = SafeByteArray<64>; - -pub fn load_key(path: &str) -> Result { - let key_str = std::fs::read_to_string(path).map_err(ReadHexError::Io)?; - key_str.trim().parse().map_err(ReadHexError::Format) -} -- cgit v1.2.3