use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
use sha2::{Digest, Sha256};
use crate::{hex_decode_exact, hex_encode};
pub const REQUEST_DOMAIN: &str = "weft-licence-v1/request";
pub const MAX_TS_MS: u64 = 4_102_444_800_000;
pub const CLOCK_FLOOR_SECS: u64 = 1_780_000_000;
pub const REQUEST_WINDOW_MS: u64 = 120_000;
pub const MAX_NONCES: usize = 4096;
pub fn signing_string(
method: &str,
target: &str,
node: &str,
audience: &str,
ts_ms: u64,
nonce: &str,
body: &[u8],
) -> String {
format!(
"{REQUEST_DOMAIN}\n{method}\n{target}\n{node}\n{audience}\n{ts_ms}\n{nonce}\n{}",
hex_encode(&Sha256::digest(body))
)
}
pub fn valid_nonce(n: &str) -> bool {
(16..=64).contains(&n.len()) && n.bytes().all(|b| b.is_ascii_alphanumeric())
}
pub fn valid_ts(ts_ms: u64) -> bool {
ts_ms <= MAX_TS_MS
}
pub fn within_window(ts_ms: u64, now_ms: u64, window_ms: u64) -> bool {
ts_ms.abs_diff(now_ms) <= window_ms
}
#[allow(clippy::too_many_arguments)]
pub fn sign(
key: &SigningKey,
node: &str,
audience: &str,
method: &str,
target: &str,
body: &[u8],
ts_ms: u64,
nonce: &str,
) -> String {
let s = signing_string(method, target, node, audience, ts_ms, nonce, body);
hex_encode(&key.sign(s.as_bytes()).to_bytes())
}
#[allow(clippy::too_many_arguments)]
pub fn verify_signature(
steward_pubkey: &[u8; 32],
node: &str,
audience: &str,
method: &str,
target: &str,
body: &[u8],
ts_ms: u64,
nonce: &str,
sig_hex: &str,
) -> bool {
let (Some(sig), Ok(vk)) =
(hex_decode_exact::<64>(sig_hex), VerifyingKey::from_bytes(steward_pubkey))
else {
return false;
};
let s = signing_string(method, target, node, audience, ts_ms, nonce, body);
vk.verify_strict(s.as_bytes(), &Signature::from_bytes(&sig)).is_ok()
}