// key_resolver.mbt — Key resolution (RFC 9421 §3.1.1).
//
// A `keyid` in the signature parameters identifies the key material. The
// library never fetches keys over the network, never parses `file://` URLs,
// never resolves JWKS/DID pointers, and never treats a `keyid` as a trusted
// identity: it is only a lookup identifier into a caller-supplied resolver.
///|
/// A resolved key record.
pub(all) struct KeyRecord {
/// The key identifier (must match the Signature-Input `keyid`).
keyid : String
/// The algorithm name the key is valid for.
algorithm : String
/// The key material.
material : KeyMaterial
}
///|
/// The resolver interface: maps a `keyid` to a `KeyRecord`.
pub(open) trait KeyResolver {
/// Resolves `keyid` to a key record, or fails with `KeyNotFound`.
fn resolve(Self, String) -> Result[KeyRecord, HsError]
}
///|
/// An in-memory key resolver backed by an ordered list of records.
pub struct InMemoryKeyResolver {
keys : Array[KeyRecord]
}
///|
/// Constructs an empty in-memory resolver.
pub fn InMemoryKeyResolver::new() -> InMemoryKeyResolver {
{ keys: Array::new() }
}
///|
/// Adds or replaces a key record.
///
/// A keyid longer than `limits.max_keyid_bytes` is rejected.
pub fn InMemoryKeyResolver::add(
self : InMemoryKeyResolver,
record : KeyRecord,
limits : Limits,
) -> Result[Unit, HsError] {
try {
limits.check_keyid_length(record.keyid)
let mut found = false
for i, k in self.keys {
if k.keyid == record.keyid {
self.keys[i] = record
found = true
break
}
}
if !found {
self.keys.push(record)
}
Ok(())
} catch {
e => Err(e)
}
}
///|
/// Removes a key by keyid.
pub fn InMemoryKeyResolver::remove(
self : InMemoryKeyResolver,
keyid : String,
) -> Unit {
let mut i = 0
while i < self.keys.length() {
if self.keys[i].keyid == keyid {
let _ = self.keys.remove(i)
} else {
i = i + 1
}
}
}
///|
/// Returns the key record for a keyid, or `None`.
pub fn InMemoryKeyResolver::get(
self : InMemoryKeyResolver,
keyid : String,
) -> KeyRecord? {
for k in self.keys {
if k.keyid == keyid {
return Some({
keyid: k.keyid,
algorithm: k.algorithm,
material: k.material,
})
}
}
None
}
///|
/// Returns the number of stored keys.
pub fn InMemoryKeyResolver::length(self : InMemoryKeyResolver) -> Int {
self.keys.length()
}
///|
/// Implements `KeyResolver` for `InMemoryKeyResolver`.
pub impl KeyResolver for InMemoryKeyResolver with fn resolve(self, keyid) {
match self.get(keyid) {
Some(record) => Ok(record)
None => Err(hs_error(KeyResolution, KeyNotFound, "key not found: " + keyid))
}
}
///|
/// Validates that the algorithm implied by a key matches the requested
/// algorithm, raising `AlgorithmMismatch` otherwise.
pub fn check_key_algorithm(
record : KeyRecord,
requested_algorithm : String?,
) -> Result[Unit, HsError] {
match requested_algorithm {
Some(alg) =>
if record.algorithm != alg {
Err(
hs_error(
KeyResolution,
AlgorithmMismatch,
"key algorithm " +
record.algorithm +
" does not match requested " +
alg,
),
)
} else {
Ok(())
}
None =>
if record.algorithm.is_empty() {
Err(hs_error(KeyResolution, AlgorithmMissing, "key has no algorithm"))
} else {
Ok(())
}
}
}