Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Implement correct `did:key` format
Alexis Sellier committed 3 years ago
commit 240825387d260b8415838af8987ec5870298403e
parent a2fc976ed6c290e4dc4f3283d0d7a382bf286a06
3 files changed +52 -11
modified radicle/src/crypto.rs
@@ -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);
+
    }
}
modified radicle/src/identity.rs
@@ -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();
+
    }
}
modified radicle/src/ssh.rs
@@ -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)