use base64::Engine;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use hkdf::Hkdf;
use hmac::{Hmac, Mac};
use sha2::{Digest, Sha256};
pub const DAEMON_HMAC_INFO: &[u8] = b"auths-daemon-hmac-v1";
pub const LOOKUP_PATH: &str = "/v1/pairing/sessions/lookup";
pub fn canonical_string(
context: &[u8],
method: &str,
path: &str,
body: &[u8],
ts: i64,
nonce: &[u8],
) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(body);
let body_hash_hex = hex::encode(hasher.finalize());
let nonce_b64 = URL_SAFE_NO_PAD.encode(nonce);
let mut out = Vec::with_capacity(context.len() + method.len() + path.len() + 200);
out.extend_from_slice(context);
out.push(b'\n');
out.extend_from_slice(method.as_bytes());
out.push(b'\n');
out.extend_from_slice(path.as_bytes());
out.push(b'\n');
out.extend_from_slice(body_hash_hex.as_bytes());
out.push(b'\n');
out.extend_from_slice(ts.to_string().as_bytes());
out.push(b'\n');
out.extend_from_slice(nonce_b64.as_bytes());
out
}
pub fn derive_hmac_key(short_code: &str) -> [u8; 32] {
let hk = Hkdf::<Sha256>::new(None, short_code.as_bytes());
let mut key = [0u8; 32];
let _ = hk.expand(DAEMON_HMAC_INFO, &mut key);
key
}
pub fn derive_hmac_kid(short_code: &str) -> [u8; 16] {
let mut h = Sha256::new();
h.update(short_code.as_bytes());
let full = h.finalize();
let mut out = [0u8; 16];
out.copy_from_slice(&full[..16]);
out
}
pub fn build_lookup_authorization(short_code: &str, ts: i64, nonce: &[u8]) -> String {
let kid = derive_hmac_kid(short_code);
let key = derive_hmac_key(short_code);
let canonical = canonical_string(DAEMON_HMAC_INFO, "GET", LOOKUP_PATH, b"", ts, nonce);
let Ok(mut mac) = Hmac::<Sha256>::new_from_slice(&key) else {
unreachable!("HMAC-SHA256 accepts a key of any length")
};
mac.update(&canonical);
let sig = mac.finalize().into_bytes();
format!(
"Auths-HMAC kid={},ts={},nonce={},sig={}",
URL_SAFE_NO_PAD.encode(kid),
ts,
URL_SAFE_NO_PAD.encode(nonce),
URL_SAFE_NO_PAD.encode(sig),
)
}