use crate::table::{CodecTag, KeyLength, KeyMaterialKind, MULTICODEC_TABLE};
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum CodecPrefixError {
#[error("invalid multicodec prefix")]
InvalidPrefix,
#[error("multicodec does not identify public key material")]
NonPublicKeyMaterial,
#[error("invalid multicodec payload length")]
InvalidLength,
}
#[derive(Debug, Clone)]
pub struct CodecLookupResult {
pub name: &'static str,
pub tag: CodecTag,
pub key_material: KeyMaterialKind,
pub alg: &'static str,
pub codec: &'static [u8],
pub key_length: KeyLength,
}
pub fn lookup_codec_prefix(bytes: &[u8]) -> Option<CodecLookupResult> {
for (name, spec) in MULTICODEC_TABLE {
let prefix = spec.codec;
if bytes.len() >= prefix.len() && bytes.starts_with(prefix) {
return Some(CodecLookupResult {
name,
tag: spec.tag,
key_material: spec.key_material,
alg: spec.alg,
codec: spec.codec,
key_length: spec.key_length,
});
}
}
None
}
pub fn strip_codec_prefix(bytes: &[u8]) -> Result<&[u8], CodecPrefixError> {
let found = lookup_codec_prefix(bytes).ok_or(CodecPrefixError::InvalidPrefix)?;
if found.key_material != KeyMaterialKind::PublicKey {
return Err(CodecPrefixError::NonPublicKeyMaterial);
}
let remainder = bytes
.get(found.codec.len()..)
.ok_or(CodecPrefixError::InvalidLength)?;
let valid = match found.key_length {
KeyLength::Fixed(expected) => remainder.len() == expected,
KeyLength::Variable => !remainder.is_empty(),
KeyLength::NotApplicable => false,
};
if !valid {
return Err(CodecPrefixError::InvalidLength);
}
Ok(remainder)
}