#![forbid(unsafe_code)]
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
pub const DEFAULT_KID_PREFIX_BYTES: usize = 12;
pub fn kid_from_bytes(bytes: &[u8]) -> String {
kid_from_bytes_with_prefix(bytes, DEFAULT_KID_PREFIX_BYTES)
}
pub fn kid_from_bytes_with_prefix(bytes: &[u8], prefix_bytes: usize) -> String {
assert!(
(1..=blake3::OUT_LEN).contains(&prefix_bytes),
"prefix_bytes must be in 1..={} (got {prefix_bytes})",
blake3::OUT_LEN
);
let digest = blake3::hash(bytes);
URL_SAFE_NO_PAD.encode(&digest.as_bytes()[..prefix_bytes])
}
#[cfg(test)]
mod tests {
use base64::Engine as _;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use super::{DEFAULT_KID_PREFIX_BYTES, kid_from_bytes, kid_from_bytes_with_prefix};
#[test]
fn kid_is_deterministic() {
let a = kid_from_bytes(b"fixture-public-key");
let b = kid_from_bytes(b"fixture-public-key");
assert_eq!(a, b);
}
#[test]
fn kid_changes_when_input_changes() {
let a = kid_from_bytes(b"fixture-public-key-a");
let b = kid_from_bytes(b"fixture-public-key-b");
assert_ne!(a, b);
}
#[test]
fn default_kid_decodes_to_96_bits() {
let kid = kid_from_bytes(b"fixture-public-key");
let decoded = URL_SAFE_NO_PAD
.decode(kid.as_bytes())
.expect("kid should be valid base64url");
assert_eq!(decoded.len(), DEFAULT_KID_PREFIX_BYTES);
}
#[test]
fn configurable_prefix_length_is_respected() {
let kid = kid_from_bytes_with_prefix(b"fixture-public-key", 8);
let decoded = URL_SAFE_NO_PAD
.decode(kid.as_bytes())
.expect("kid should be valid base64url");
assert_eq!(decoded.len(), 8);
}
#[test]
#[should_panic(expected = "prefix_bytes must be in 1..=32")]
fn prefix_length_must_be_non_zero() {
let _ = kid_from_bytes_with_prefix(b"fixture-public-key", 0);
}
#[test]
#[should_panic(expected = "prefix_bytes must be in 1..=32")]
fn prefix_length_above_32_panics() {
let _ = kid_from_bytes_with_prefix(b"fixture-public-key", 33);
}
#[test]
fn prefix_length_max_32_is_valid() {
let kid = kid_from_bytes_with_prefix(b"fixture-public-key", 32);
let decoded = URL_SAFE_NO_PAD
.decode(kid.as_bytes())
.expect("should be valid base64url");
assert_eq!(decoded.len(), 32);
}
#[test]
fn prefix_length_min_1_is_valid() {
let kid = kid_from_bytes_with_prefix(b"fixture-public-key", 1);
let decoded = URL_SAFE_NO_PAD
.decode(kid.as_bytes())
.expect("should be valid base64url");
assert_eq!(decoded.len(), 1);
}
#[test]
fn kid_from_empty_input() {
let kid = kid_from_bytes(b"");
assert!(!kid.is_empty(), "even empty input should produce a kid");
}
#[test]
fn default_kid_prefix_bytes_is_12() {
assert_eq!(DEFAULT_KID_PREFIX_BYTES, 12);
}
#[test]
fn kid_is_url_safe() {
let kid = kid_from_bytes(b"any-public-key-material");
assert!(
kid.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_'),
"kid should be URL-safe: {kid}"
);
}
}