Skip to main content

codec_multikey/
parse.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use codec_multibase::multibase_to_bytes;
6use codec_multicodec::{lookup_codec_prefix, KeyMaterialKind, VARIABLE_KEY_LENGTH};
7
8use crate::error::MultikeyError;
9
10/// A multikey decoded into its codec metadata and raw public key bytes.
11pub struct ParsedMultikey {
12    /// Canonical multicodec name of the key type (e.g. `ed25519-pub`).
13    pub codec_name: &'static str,
14    /// Human-readable algorithm name implied by the codec (e.g. `Ed25519`).
15    pub alg: &'static str,
16    /// Raw public key bytes with the multicodec prefix stripped.
17    pub public_key: Vec<u8>,
18    /// Expected public key length for the codec.
19    pub key_length: usize,
20}
21
22/// Parses a multibase-encoded multikey string into its codec and key bytes.
23///
24/// Fails closed: returns an error on invalid multibase, unknown codec prefix,
25/// or a key length that does not match the codec.
26pub fn parse_multikey(multibase_key: &str) -> Result<ParsedMultikey, MultikeyError> {
27    if multibase_key.len() < 2 {
28        return Err(MultikeyError::InvalidMultibase);
29    }
30
31    // 1) multibase decode
32    let raw = multibase_to_bytes(multibase_key).map_err(|_| MultikeyError::InvalidMultibase)?;
33
34    if raw.len() < 2 {
35        return Err(MultikeyError::DecodedTooShort(raw.len()));
36    }
37
38    // 2) multicodec prefix lookup
39    let found = lookup_codec_prefix(&raw).ok_or(MultikeyError::UnknownCodecPrefix)?;
40
41    if found.key_material != KeyMaterialKind::PublicKey {
42        return Err(MultikeyError::UnknownCodecPrefix);
43    }
44
45    let public_key = raw[found.codec.len()..].to_vec();
46
47    // 3) key length validation
48    if found.key_length == VARIABLE_KEY_LENGTH && public_key.is_empty() {
49        return Err(MultikeyError::KeyLengthMismatch {
50            codec_name: found.name,
51            expected: found.key_length,
52            actual: public_key.len(),
53        });
54    }
55
56    if found.key_length != VARIABLE_KEY_LENGTH && public_key.len() != found.key_length {
57        return Err(MultikeyError::KeyLengthMismatch {
58            codec_name: found.name,
59            expected: found.key_length,
60            actual: public_key.len(),
61        });
62    }
63
64    Ok(ParsedMultikey {
65        codec_name: found.name,
66        alg: found.alg,
67        public_key,
68        key_length: found.key_length,
69    })
70}