Skip to main content

codec_multicodec/
lookup.rs

1// SPDX-FileCopyrightText: 2026 ReallyMe LLC
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use crate::table::{CodecTag, KeyLength, KeyMaterialKind, MULTICODEC_TABLE};
6
7/// Failure to strip a public-key multicodec prefix.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
9pub enum CodecPrefixError {
10    /// No supported prefix was found.
11    #[error("invalid multicodec prefix")]
12    InvalidPrefix,
13    /// The prefix identifies private, symmetric, or non-key material.
14    #[error("multicodec does not identify public key material")]
15    NonPublicKeyMaterial,
16    /// A recognized public-key prefix carries an invalid payload length.
17    #[error("invalid multicodec payload length")]
18    InvalidLength,
19}
20
21/// Codec metadata resolved from a multicodec prefix.
22#[derive(Debug, Clone)]
23pub struct CodecLookupResult {
24    /// Canonical multicodec name (e.g. `ed25519-pub`).
25    pub name: &'static str,
26    /// Multicodec table tag.
27    pub tag: CodecTag,
28    /// Key-material class for `key` codecs.
29    pub key_material: KeyMaterialKind,
30    /// Human-readable algorithm name (e.g. `Ed25519`).
31    pub alg: &'static str,
32    /// The multicodec varint prefix bytes.
33    pub codec: &'static [u8],
34    /// Expected raw public key length after the prefix.
35    pub key_length: KeyLength,
36}
37
38/// Lookup multicodec prefix → codec metadata
39pub fn lookup_codec_prefix(bytes: &[u8]) -> Option<CodecLookupResult> {
40    for (name, spec) in MULTICODEC_TABLE {
41        let prefix = spec.codec;
42        if bytes.len() >= prefix.len() && bytes.starts_with(prefix) {
43            return Some(CodecLookupResult {
44                name,
45                tag: spec.tag,
46                key_material: spec.key_material,
47                alg: spec.alg,
48                codec: spec.codec,
49                key_length: spec.key_length,
50            });
51        }
52    }
53    None
54}
55
56/// Strip a recognized public-key multicodec prefix from a complete key.
57///
58/// # Errors
59///
60/// Rejects unknown prefixes, non-public key classes, and invalid key lengths
61/// so callers cannot accidentally use prefixed or private bytes as raw keys.
62pub fn strip_codec_prefix(bytes: &[u8]) -> Result<&[u8], CodecPrefixError> {
63    let found = lookup_codec_prefix(bytes).ok_or(CodecPrefixError::InvalidPrefix)?;
64    if found.key_material != KeyMaterialKind::PublicKey {
65        return Err(CodecPrefixError::NonPublicKeyMaterial);
66    }
67    let remainder = bytes
68        .get(found.codec.len()..)
69        .ok_or(CodecPrefixError::InvalidLength)?;
70    let valid = match found.key_length {
71        KeyLength::Fixed(expected) => remainder.len() == expected,
72        KeyLength::Variable => !remainder.is_empty(),
73        KeyLength::NotApplicable => false,
74    };
75    if !valid {
76        return Err(CodecPrefixError::InvalidLength);
77    }
78    Ok(remainder)
79}