aph_core/crypto/
did_key.rs1const ED25519_MULTICODEC: [u8; 2] = [0xed, 0x01];
16const P256_MULTICODEC: [u8; 2] = [0x80, 0x24];
18
19const DID_KEY_PREFIX: &str = "did:key:";
21
22#[derive(std::fmt::Debug, std::clone::Clone, std::cmp::PartialEq, std::cmp::Eq)]
24pub enum DecodedDidKey {
25 Ed25519(std::boxed::Box<ed25519_dalek::VerifyingKey>),
27 P256(std::vec::Vec<u8>),
30}
31
32pub fn encode_ed25519(key: &ed25519_dalek::VerifyingKey) -> String {
37 let mut payload = std::vec::Vec::with_capacity(2 + 32);
38 payload.extend_from_slice(&ED25519_MULTICODEC);
39 payload.extend_from_slice(key.as_bytes());
40 std::format!("{}{}", DID_KEY_PREFIX, super::multibase::base58btc_encode(&payload))
41}
42
43pub fn decode(did: &str) -> std::result::Result<DecodedDidKey, crate::errors::AphError> {
48 let encoded = match did.strip_prefix(DID_KEY_PREFIX) {
49 std::option::Option::Some(rest) => rest,
50 std::option::Option::None => {
51 return std::result::Result::Err(crate::errors::AphError::InvalidEnvelopeSignature);
52 }
53 };
54 decode_multibase_key(encoded)
55}
56
57pub fn decode_multibase_key(
63 encoded: &str,
64) -> std::result::Result<DecodedDidKey, crate::errors::AphError> {
65 let bytes = super::multibase::base58btc_decode(encoded)?;
66 if bytes.len() < 3 {
67 return std::result::Result::Err(crate::errors::AphError::InvalidEnvelopeSignature);
68 }
69 let (prefix, key_bytes) = bytes.split_at(2);
70
71 if prefix == ED25519_MULTICODEC {
72 let arr: [u8; 32] = match key_bytes.try_into() {
73 std::result::Result::Ok(a) => a,
74 std::result::Result::Err(_) => {
76 return std::result::Result::Err(crate::errors::AphError::InvalidEnvelopeSignature);
77 }
78 };
79 return match ed25519_dalek::VerifyingKey::from_bytes(&arr) {
80 std::result::Result::Ok(k) => {
81 std::result::Result::Ok(DecodedDidKey::Ed25519(std::boxed::Box::new(k)))
82 }
83 std::result::Result::Err(_) => {
85 std::result::Result::Err(crate::errors::AphError::InvalidEnvelopeSignature)
86 }
87 };
88 }
89
90 if prefix == P256_MULTICODEC {
91 if key_bytes.len() != 33 {
93 return std::result::Result::Err(crate::errors::AphError::InvalidEnvelopeSignature);
94 }
95 return std::result::Result::Ok(DecodedDidKey::P256(key_bytes.to_vec()));
96 }
97
98 std::result::Result::Err(crate::errors::AphError::unsupported_algorithm(std::format!(
100 "did:key multicodec 0x{:02x}{:02x}",
101 prefix[0],
102 prefix[1]
103 )))
104}
105
106#[cfg(test)]
107mod tests {
108 fn sample_key() -> ed25519_dalek::VerifyingKey {
109 let sk = ed25519_dalek::SigningKey::from_bytes(&[7u8; 32]);
110 sk.verifying_key()
111 }
112
113 #[test]
114 fn encode_decode_round_trip() {
115 let key = sample_key();
118 let did = super::encode_ed25519(&key);
119 match super::decode(&did).unwrap() {
120 super::DecodedDidKey::Ed25519(decoded) => std::assert_eq!(*decoded, key),
121 other => std::panic!("expected Ed25519, got {:?}", other),
122 }
123 }
124
125 #[test]
126 fn ed25519_dids_use_the_z6mk_prefix() {
127 let did = super::encode_ed25519(&sample_key());
131 std::assert!(did.starts_with("did:key:z6Mk"), "got {}", did);
132 }
133
134 #[test]
135 fn published_example_issuer_decodes() {
136 let did = "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSdoVTwBaPaeT1KhFmkV";
140 std::assert!(matches!(
141 super::decode(did).unwrap(),
142 super::DecodedDidKey::Ed25519(_)
143 ));
144 }
145
146 #[test]
147 fn non_did_key_input_is_rejected() {
148 std::assert!(super::decode("did:web:notary.example").is_err());
151 std::assert!(super::decode("z6MkfAkf").is_err());
152 }
153
154 #[test]
155 fn unknown_multicodec_reports_unsupported_algorithm() {
156 let payload = super::super::multibase::base58btc_encode(&[0x99u8, 0x01, 1, 2, 3]);
159 let err = super::decode(&std::format!("did:key:{}", payload)).unwrap_err();
160 std::assert_eq!(err.code(), "APH_E010");
161 }
162
163 #[test]
164 fn truncated_key_is_rejected_not_panicking() {
165 let payload = super::super::multibase::base58btc_encode(&[0xed, 0x01, 1, 2, 3]);
168 std::assert!(super::decode(&std::format!("did:key:{}", payload)).is_err());
169 }
170}