baseid_crypto/
multikey.rs1use baseid_core::error::CryptoError;
4use baseid_core::types::KeyType;
5
6use crate::key::PublicKey;
7
8const ED25519_PREFIX: [u8; 2] = [0xed, 0x01];
10const P256_PREFIX: [u8; 2] = [0x80, 0x24];
11const P384_PREFIX: [u8; 2] = [0x81, 0x24];
12const SECP256K1_PREFIX: [u8; 2] = [0xe7, 0x01];
13
14impl PublicKey {
15 pub fn to_multibase(&self) -> String {
19 let prefix = multicodec_prefix(self.key_type);
20 let mut data = Vec::with_capacity(prefix.len() + self.bytes.len());
21 data.extend_from_slice(&prefix);
22 data.extend_from_slice(&self.bytes);
23 multibase::encode(multibase::Base::Base58Btc, &data)
24 }
25
26 pub fn from_multibase(encoded: &str) -> baseid_core::Result<Self> {
31 let (_base, data) =
32 multibase::decode(encoded).map_err(|_| CryptoError::InvalidKeyMaterial)?;
33
34 if data.len() < 2 {
35 return Err(CryptoError::InvalidKeyMaterial.into());
36 }
37
38 let (key_type, prefix_len) = match (data[0], data[1]) {
39 (0xed, 0x01) => (KeyType::Ed25519, 2),
40 (0x80, 0x24) => (KeyType::P256, 2),
41 (0x81, 0x24) => (KeyType::P384, 2),
42 (0xe7, 0x01) => (KeyType::Secp256k1, 2),
43 _ => return Err(CryptoError::UnsupportedAlgorithm.into()),
44 };
45
46 let key_bytes = &data[prefix_len..];
47 PublicKey::from_bytes(key_type, key_bytes)
48 }
49}
50
51const BLS12381G2_PREFIX: [u8; 2] = [0xeb, 0x01];
53
54fn multicodec_prefix(key_type: KeyType) -> [u8; 2] {
55 match key_type {
56 KeyType::Ed25519 => ED25519_PREFIX,
57 KeyType::P256 => P256_PREFIX,
58 KeyType::P384 => P384_PREFIX,
59 KeyType::Secp256k1 => SECP256K1_PREFIX,
60 KeyType::Bls12381G2 => BLS12381G2_PREFIX,
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use crate::key::KeyPair;
68
69 fn roundtrip(key_type: KeyType) {
70 let kp = KeyPair::generate(key_type).unwrap();
71 let encoded = kp.public.to_multibase();
72 assert!(encoded.starts_with('z'));
74 let decoded = PublicKey::from_multibase(&encoded).unwrap();
75 assert_eq!(decoded.key_type, kp.public.key_type);
76 assert_eq!(decoded.bytes, kp.public.bytes);
77 }
78
79 #[test]
80 fn roundtrip_ed25519() {
81 roundtrip(KeyType::Ed25519);
82 }
83
84 #[test]
85 fn roundtrip_p256() {
86 roundtrip(KeyType::P256);
87 }
88
89 #[test]
90 fn roundtrip_p384() {
91 roundtrip(KeyType::P384);
92 }
93
94 #[test]
95 fn roundtrip_secp256k1() {
96 roundtrip(KeyType::Secp256k1);
97 }
98
99 #[test]
100 fn ed25519_prefix_format() {
101 let kp = KeyPair::generate(KeyType::Ed25519).unwrap();
102 let encoded = kp.public.to_multibase();
103 assert!(encoded.starts_with("z6Mk"), "got: {encoded}");
105 }
106
107 #[test]
108 fn reject_invalid_multibase() {
109 assert!(PublicKey::from_multibase("not-valid").is_err());
110 }
111
112 #[test]
113 fn reject_unknown_prefix() {
114 let mut data = vec![0xFF, 0xFF];
116 data.extend_from_slice(&[0u8; 32]);
117 let encoded = multibase::encode(multibase::Base::Base58Btc, &data);
118 assert!(PublicKey::from_multibase(&encoded).is_err());
119 }
120
121 #[test]
122 fn reject_too_short() {
123 let data = vec![0xed]; let encoded = multibase::encode(multibase::Base::Base58Btc, &data);
125 assert!(PublicKey::from_multibase(&encoded).is_err());
126 }
127}