Also fix casing of VerificationMethodMap
, and comply with weirdness of W3C-
endorsed multibase encoding.
4TPKSHZTLLJJZ2JUKCQIOKY3KM2VKN5DWZAUMOWMVZCX5MNZZHPAC
/// Create a JSON [DID document] from a [DID] and a public key.
///
/// [DID document]: https://www.w3.org/TR/did-core/#dfn-did-documents
/// [did-web]: https://w3c-ccg.github.io/did-method-web/
pub fn create(did: DID, key: ed25519::PublicKey) -> serde_json::Value {
let key = {
let mut buf = [0u8; ed25519::PublicKey::BYTES + 2];
buf[0] = 0xed;
buf[1] = 0x01;
buf[2..].copy_from_slice(&*key);
multibase::encode(multibase::Base::Base58Btc, buf)
};
json!({
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/ed25519-2020/v1",
],
"id": *did,
"verificationMethod": [{
"id": format!("{did}#key-0"),
"type": "Ed25519VerificationKey2020",
"controller": *did,
"publicKeyMultiBase": key,
}],
})
}
}
}
impl TryFrom<Url> for DID {
type Error = anyhow::Error;
fn try_from(url: Url) -> Result<Self, Self::Error> {
let host = match url.host().context("missing host")? {
url::Host::Domain(host) => host,
url::Host::Ipv4(_) | url::Host::Ipv6(_) => {
bail!("Host must be a domain name, not IP address")
},
};
let port = url.port().map(|p| format!("%3A{p}")).unwrap_or_default();
let mut path: Vec<&str> = url.path().split('/').filter(|s| !s.is_empty()).collect();
if let Some(&"did.json") = path.last() {
path.pop();
}
if let Some(&".well-known") = path.last() {
path.pop();
}
let path = path.join(":");
Ok(Self(format!("did:web:{host}{port}:{path}")))
if bytes.len() != 34 {
fn exp() -> impl serde::de::Expected {
"34"
}
return Err(serde::de::Error::invalid_length(bytes.len(), &exp()));
}
if bytes[0] != 0xed {
return Err(serde::de::Error::custom(format!(
"Expected first byte of multikey to be 0xed, got {}",
bytes[0]
)));
}
if bytes[1] != 0x01 {
return Err(serde::de::Error::custom(format!(
"Expected second byte of multikey to 0x01, got {}",
bytes[1]
)));
}
eprintln!("{doc:#?}");
#[test]
fn create_valid() {
let did = DID::parse("did:web:eagain.io:kim").unwrap();
let key = ed25519::KeyPair::generate().pk;
let doc = super::create(did, key);
let js = serde_json::to_string_pretty(&doc).unwrap();
let doc: PartialDocument = serde_json::from_str(&js).unwrap_or_else(|e| {
panic!("failed to decode\n{js}:\n{e:#?}");
});
assert!(doc.validate().is_some());