use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine as _;
use ring::hmac;
use ring::signature::{Ed25519KeyPair, UnparsedPublicKey, ED25519};
use crate::remote::util::{hex, sha256_hex, unhex};
pub const H_TIMESTAMP: &str = "x-rk-timestamp";
pub const H_NONCE: &str = "x-rk-nonce";
pub const H_SIGNATURE: &str = "x-rk-signature";
pub const H_PROVIDER_SIGNATURE: &str = "x-provider-signature";
pub const H_PROVIDER_TIMESTAMP: &str = "x-provider-timestamp";
pub fn canonical_request(
method: &str,
path_and_query: &str,
timestamp: u64,
nonce: &str,
body: &[u8],
) -> String {
format!(
"{}\n{}\n{}\n{}\n{}",
method.to_ascii_uppercase(),
path_and_query,
timestamp,
nonce,
sha256_hex(body)
)
}
pub fn webhook_signature(secret: &[u8], timestamp: u64, body: &[u8]) -> String {
let key = hmac::Key::new(hmac::HMAC_SHA256, secret);
let mut ctx = hmac::Context::with_key(&key);
ctx.update(timestamp.to_string().as_bytes());
ctx.update(b".");
ctx.update(body);
hex(ctx.sign().as_ref())
}
pub fn sign_request(
key: &Ed25519KeyPair,
method: &str,
path_and_query: &str,
timestamp: u64,
nonce: &str,
body: &[u8],
) -> String {
let msg = canonical_request(method, path_and_query, timestamp, nonce, body);
URL_SAFE_NO_PAD.encode(key.sign(msg.as_bytes()).as_ref())
}
pub fn verify_request(
public_key: &[u8],
method: &str,
path_and_query: &str,
timestamp: u64,
nonce: &str,
body: &[u8],
signature: &[u8],
) -> bool {
let msg = canonical_request(method, path_and_query, timestamp, nonce, body);
UnparsedPublicKey::new(&ED25519, public_key)
.verify(msg.as_bytes(), signature)
.is_ok()
}
pub fn parse_digest_key(key_hex: &str) -> Option<[u8; 32]> {
unhex(key_hex).and_then(|k| k.try_into().ok())
}
pub fn chunk_digest(key: &[u8; 32], data: &[u8]) -> String {
blake3::keyed_hash(key, data).to_hex().to_string()
}
pub fn object_hasher(key: &[u8; 32]) -> blake3::Hasher {
blake3::Hasher::new_keyed(key)
}
pub fn object_digest(hasher: &blake3::Hasher) -> String {
hasher.finalize().to_hex().to_string()
}