1use codec_multibase::multibase_to_bytes;
6use codec_multicodec::{lookup_codec_prefix, KeyMaterialKind, VARIABLE_KEY_LENGTH};
7
8use crate::encode::MAX_RSA_PUBLIC_KEY_DER_LEN;
9use crate::error::MultikeyError;
10
11pub struct ParsedMultikey {
13 pub codec_name: &'static str,
15 pub alg: &'static str,
17 pub public_key: Vec<u8>,
19 pub key_length: usize,
21}
22
23pub fn parse_multikey(multibase_key: &str) -> Result<ParsedMultikey, MultikeyError> {
28 if multibase_key.len() < 2 {
29 return Err(MultikeyError::InvalidMultibase);
30 }
31 if !multibase_key.starts_with('z') {
35 return Err(MultikeyError::InvalidMultibase);
36 }
37
38 let raw = multibase_to_bytes(multibase_key).map_err(|_| MultikeyError::InvalidMultibase)?;
40
41 if raw.len() < 2 {
42 return Err(MultikeyError::DecodedTooShort(raw.len()));
43 }
44
45 let found = lookup_codec_prefix(&raw).ok_or(MultikeyError::UnknownCodecPrefix)?;
47
48 if found.key_material != KeyMaterialKind::PublicKey {
49 return Err(MultikeyError::UnknownCodecPrefix);
50 }
51
52 let public_key = raw[found.codec.len()..].to_vec();
53
54 if found.key_length == VARIABLE_KEY_LENGTH && public_key.is_empty() {
56 return Err(MultikeyError::KeyLengthMismatch {
57 codec_name: found.name,
58 expected: found.key_length,
59 actual: public_key.len(),
60 });
61 }
62
63 if found.name == "rsa-pub" && public_key.len() > MAX_RSA_PUBLIC_KEY_DER_LEN {
64 return Err(MultikeyError::KeyTooLarge {
65 codec_name: found.name,
66 max: MAX_RSA_PUBLIC_KEY_DER_LEN,
67 actual: public_key.len(),
68 });
69 }
70
71 if found.key_length != VARIABLE_KEY_LENGTH && public_key.len() != found.key_length {
72 return Err(MultikeyError::KeyLengthMismatch {
73 codec_name: found.name,
74 expected: found.key_length,
75 actual: public_key.len(),
76 });
77 }
78
79 Ok(ParsedMultikey {
80 codec_name: found.name,
81 alg: found.alg,
82 public_key,
83 key_length: found.key_length,
84 })
85}