use codec_multibase::multibase_to_bytes;
use codec_multicodec::{lookup_codec_prefix, KeyMaterialKind, VARIABLE_KEY_LENGTH};
use crate::encode::MAX_RSA_PUBLIC_KEY_DER_LEN;
use crate::error::{classify_multikey_codec, MultikeyError};
pub struct ParsedMultikey {
pub codec_name: &'static str,
pub alg: &'static str,
pub public_key: Vec<u8>,
pub key_length: usize,
}
pub fn parse_multikey(multibase_key: &str) -> Result<ParsedMultikey, MultikeyError> {
if multibase_key.len() < 2 {
return Err(MultikeyError::InvalidMultibase);
}
if !multibase_key.starts_with('z') {
return Err(MultikeyError::InvalidMultibase);
}
let raw = multibase_to_bytes(multibase_key).map_err(|_| MultikeyError::InvalidMultibase)?;
if raw.len() < 2 {
return Err(MultikeyError::DecodedTooShort(raw.len()));
}
let found = lookup_codec_prefix(&raw).ok_or(MultikeyError::UnknownCodecPrefix)?;
if found.key_material != KeyMaterialKind::PublicKey {
return Err(MultikeyError::UnknownCodecPrefix);
}
let public_key = raw[found.codec.len()..].to_vec();
if found.key_length == VARIABLE_KEY_LENGTH && public_key.is_empty() {
return Err(MultikeyError::KeyLengthMismatch {
codec: classify_multikey_codec(found.name),
expected: found.key_length,
actual: public_key.len(),
});
}
if found.name == "rsa-pub" && public_key.len() > MAX_RSA_PUBLIC_KEY_DER_LEN {
return Err(MultikeyError::KeyTooLarge {
codec: classify_multikey_codec(found.name),
max: MAX_RSA_PUBLIC_KEY_DER_LEN,
actual: public_key.len(),
});
}
if found.key_length != VARIABLE_KEY_LENGTH && public_key.len() != found.key_length {
return Err(MultikeyError::KeyLengthMismatch {
codec: classify_multikey_codec(found.name),
expected: found.key_length,
actual: public_key.len(),
});
}
Ok(ParsedMultikey {
codec_name: found.name,
alg: found.alg,
public_key,
key_length: found.key_length,
})
}