Implement correct `did:key` format
We weren't including the *multicodec* bytes previously. This is now tested against some test vectors from the spec. Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
a2fc976ed6
commit
240825387d
|
|
@ -131,6 +131,8 @@ pub enum PublicKeyError {
|
|||
InvalidLength(usize),
|
||||
#[error("invalid multibase string: {0}")]
|
||||
Multibase(#[from] multibase::Error),
|
||||
#[error("invalid multicodec prefix, expected {0:?}")]
|
||||
Multicodec([u8; 2]),
|
||||
#[error("invalid key: {0}")]
|
||||
InvalidKey(#[from] ed25519::Error),
|
||||
}
|
||||
|
|
@ -198,8 +200,21 @@ impl TryFrom<&[u8]> for PublicKey {
|
|||
}
|
||||
|
||||
impl PublicKey {
|
||||
/// Multicodec key type for Ed25519 keys.
|
||||
pub const MULTICODEC_TYPE: [u8; 2] = [0xED, 0x1];
|
||||
|
||||
/// Encode public key in human-readable format.
|
||||
///
|
||||
/// We use the format specified by the DID `key` method, which is described as:
|
||||
///
|
||||
/// `did:key:MULTIBASE(base58-btc, MULTICODEC(public-key-type, raw-public-key-bytes))`
|
||||
///
|
||||
pub fn to_human(&self) -> String {
|
||||
multibase::encode(multibase::Base::Base58Btc, self.0.deref())
|
||||
let mut buf = [0; 2 + ed25519::PublicKey::BYTES];
|
||||
buf[..2].copy_from_slice(&Self::MULTICODEC_TYPE);
|
||||
buf[2..].copy_from_slice(self.0.deref());
|
||||
|
||||
multibase::encode(multibase::Base::Base58Btc, &buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,12 +223,14 @@ impl FromStr for PublicKey {
|
|||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (_, bytes) = multibase::decode(s)?;
|
||||
let array: [u8; 32] = bytes
|
||||
.try_into()
|
||||
.map_err(|v: Vec<u8>| PublicKeyError::InvalidLength(v.len()))?;
|
||||
let key = ed25519::PublicKey::new(array);
|
||||
|
||||
Ok(Self(key))
|
||||
if let Some(bytes) = bytes.strip_prefix(&Self::MULTICODEC_TYPE) {
|
||||
let key = ed25519::PublicKey::from_slice(bytes)?;
|
||||
|
||||
Ok(Self(key))
|
||||
} else {
|
||||
Err(PublicKeyError::Multicodec(Self::MULTICODEC_TYPE))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,4 +263,12 @@ mod test {
|
|||
|
||||
assert_eq!(input, decoded);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_decode() {
|
||||
let input = "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
|
||||
let key = PublicKey::from_str(input).unwrap();
|
||||
|
||||
assert_eq!(key.to_string(), input);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,4 +117,19 @@ mod test {
|
|||
let json = serde_json::to_string(&did).unwrap();
|
||||
assert_eq!(format!("\"{}\"", did), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_did_encode_decode() {
|
||||
let input = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
|
||||
let Did(key) = Did::decode(input).unwrap();
|
||||
|
||||
assert_eq!(Did::from(key).encode(), input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_did_vectors() {
|
||||
Did::decode("did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp").unwrap();
|
||||
Did::decode("did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG").unwrap();
|
||||
Did::decode("did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf").unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ pub mod fmt {
|
|||
|
||||
#[test]
|
||||
fn test_key() {
|
||||
let pk = PublicKey::from_str("zF4VJZgNEeL1niWmKu1NtT1B4ZyGpjACyhs2VEZvtsws5").unwrap();
|
||||
let pk =
|
||||
PublicKey::from_str("z6MktWkM9vcfysWFq1c2aaLjJ6j4PYYg93TLPswR4qtuoAeT").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
key(&pk),
|
||||
|
|
@ -63,8 +64,8 @@ pub mod fmt {
|
|||
|
||||
#[test]
|
||||
fn test_fingerprint() {
|
||||
let pk = PublicKey::from_str("zF4VJZgNEeL1niWmKu1NtT1B4ZyGpjACyhs2VEZvtsws5").unwrap();
|
||||
|
||||
let pk =
|
||||
PublicKey::from_str("z6MktWkM9vcfysWFq1c2aaLjJ6j4PYYg93TLPswR4qtuoAeT").unwrap();
|
||||
assert_eq!(
|
||||
fingerprint(&pk),
|
||||
"SHA256:gE/Ty4fuXzww49lcnNe9/GI0L7xSEQdFp/v9tOjFwB4"
|
||||
|
|
@ -360,7 +361,7 @@ mod test {
|
|||
fn test_agent_encoding_remove() {
|
||||
use std::str::FromStr;
|
||||
|
||||
let pk = PublicKey::from_str("zF4VJZgNEeL1niWmKu1NtT1B4ZyGpjACyhs2VEZvtsws5").unwrap();
|
||||
let pk = PublicKey::from_str("z6MktWkM9vcfysWFq1c2aaLjJ6j4PYYg93TLPswR4qtuoAeT").unwrap();
|
||||
let expected = [
|
||||
0, 0, 0, 56, // Message length
|
||||
18, // Message type (remove identity)
|
||||
|
|
@ -387,7 +388,7 @@ mod test {
|
|||
fn test_agent_encoding_sign() {
|
||||
use std::str::FromStr;
|
||||
|
||||
let pk = PublicKey::from_str("zF4VJZgNEeL1niWmKu1NtT1B4ZyGpjACyhs2VEZvtsws5").unwrap();
|
||||
let pk = PublicKey::from_str("z6MktWkM9vcfysWFq1c2aaLjJ6j4PYYg93TLPswR4qtuoAeT").unwrap();
|
||||
let expected = [
|
||||
0, 0, 0, 73, // Message length
|
||||
13, // Message type (sign request)
|
||||
|
|
|
|||
Loading…
Reference in New Issue