1use codec_multibase::bytes_to_multibase58btc;
6use codec_multicodec::{KeyMaterialKind, MULTICODEC_TABLE, VARIABLE_KEY_LENGTH};
7
8use crate::error::{CodecNameReason, MultikeyError};
9
10pub const MAX_RSA_PUBLIC_KEY_DER_LEN: usize = 4 * 1024;
16
17pub fn encode_multikey(codec_name: &str, public_key: &[u8]) -> Result<String, MultikeyError> {
22 let (canonical_codec_name, spec) = MULTICODEC_TABLE
23 .iter()
24 .find(|(name, _)| *name == codec_name)
25 .ok_or(MultikeyError::UnknownCodecName {
26 reason: CodecNameReason::Unsupported,
27 })?;
28
29 if spec.key_material != KeyMaterialKind::PublicKey {
30 return Err(MultikeyError::UnknownCodecName {
31 reason: CodecNameReason::Unsupported,
32 });
33 }
34
35 if spec.key_length == VARIABLE_KEY_LENGTH && public_key.is_empty() {
36 return Err(MultikeyError::KeyLengthMismatch {
37 codec_name: canonical_codec_name,
38 expected: spec.key_length,
39 actual: public_key.len(),
40 });
41 }
42
43 if *canonical_codec_name == "rsa-pub" && public_key.len() > MAX_RSA_PUBLIC_KEY_DER_LEN {
44 return Err(MultikeyError::KeyTooLarge {
45 codec_name: canonical_codec_name,
46 max: MAX_RSA_PUBLIC_KEY_DER_LEN,
47 actual: public_key.len(),
48 });
49 }
50
51 if spec.key_length != VARIABLE_KEY_LENGTH && public_key.len() != spec.key_length {
52 return Err(MultikeyError::KeyLengthMismatch {
53 codec_name: canonical_codec_name,
54 expected: spec.key_length,
55 actual: public_key.len(),
56 });
57 }
58
59 let capacity =
60 spec.codec
61 .len()
62 .checked_add(public_key.len())
63 .ok_or(MultikeyError::KeyLengthMismatch {
64 codec_name: canonical_codec_name,
65 expected: spec.key_length,
66 actual: public_key.len(),
67 })?;
68 let mut payload = Vec::with_capacity(capacity);
69 payload.extend_from_slice(spec.codec);
70 payload.extend_from_slice(public_key);
71
72 bytes_to_multibase58btc(&payload).map_err(|_| MultikeyError::EncodedPayloadTooLarge)
73}