///|
/// Encode a UTF-8 string as bytes for cryptographic operations.
fn crypto_utf8(value : String) -> Bytes {
@utf8.encode(value)
}
///|
/// Return the lowercase SHA-256 digest of a UTF-8 string.
pub fn sha256_hex(message : String) -> String {
@crypto.bytes_to_hex_string(@crypto.sha256(crypto_utf8(message)))
}
///|
/// Return the lowercase HMAC-SHA256 digest of a UTF-8 payload.
pub fn hmac_sha256_hex(secret : String, message : String) -> String {
let key = crypto_utf8(secret)
let payload = crypto_utf8(message)
@crypto.bytes_to_hex_string(
@crypto.hmac(@crypto.SHA256::new(), key.view(), payload.view()),
)
}
///|
/// Compare signatures without returning early on the first different byte.
/// Length is folded into the accumulated difference to avoid prefix matches.
pub fn constant_time_equal(left : String, right : String) -> Bool {
let left_bytes = crypto_utf8(left)
let right_bytes = crypto_utf8(right)
let limit = if left_bytes.length() > right_bytes.length() {
left_bytes.length()
} else {
right_bytes.length()
}
let mut difference = left_bytes.length() ^ right_bytes.length()
for index in 0.. String {
"sha256=" + hmac_sha256_hex(secret, body)
}