For now, only did-web.
H3V4EM4IBB7YSJV63CXX33B4FJFXNGH2QYQSBKDC7HLMDPBQ2H5QC
// Copyright © 2023 Kim Altintop <kim@eagain.io>
// SPDX-License-Identifier: GPL-2.0-only
use core::fmt;
use std::{
collections::HashMap,
iter,
ops::Deref,
str::FromStr,
};
use base64::{
engine::general_purpose::URL_SAFE_NO_PAD,
Engine as _,
};
use derive_more::From;
use ed25519_compact as ed25519;
use itertools::Itertools as _;
use pgp::Deserializable as _;
use url::Url;
#[cfg(test)]
mod tests;
pub mod error {
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DID {
#[error("not a DID")]
NotADid,
#[error("unsupported method")]
UnsupportedMethod,
#[error("unexpected eof")]
UnexpectedEof,
#[error("malformed method-specific identifier: `{0}`")]
MalformedIdentifier(String),
#[error("invalid method-specific identifier")]
InvalidIdentifier(#[from] url::ParseError),
}
#[derive(Debug, Error)]
pub enum DIDUrl {
#[error("not a DID URL")]
NotADidUrl,
#[error("not a valid URL")]
InvalidUrl(#[from] url::ParseError),
}
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Document {
pub id: DID,
#[serde(deserialize_with = "deserialize_verification_methods")]
pub verification_method: Vec<VerificationMethod>,
#[serde(default)]
pub also_known_as: Vec<Url>,
}
#[derive(Debug)]
pub struct VerificationMethod {
pub id: String,
pub controller: DID,
pub public_key: PublicKey,
}
#[derive(Debug, From)]
pub enum PublicKey {
Ed25519(ed25519::PublicKey),
Pgp(pgp::SignedPublicKey),
}
fn deserialize_verification_methods<'de, D>(
deserializer: D,
) -> Result<Vec<VerificationMethod>, D::Error>
where
D: serde::Deserializer<'de>,
{
struct Visitor;
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Vec<VerificationMethod>;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a list of verification methods")
}
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
where
S: serde::de::SeqAccess<'de>,
{
let mut out = seq.size_hint().map(Vec::with_capacity).unwrap_or_default();
while let Some(val) = seq.next_element::<VerificationMethodMap>()? {
let key = if let Some(key) = val.public_key_jwk {
key.0.into()
} else if let Some(key) = val.public_key_base58 {
key.0.into()
} else if let Some(key) = val.public_key_multibase {
key.0.into()
} else if let Some(key) = val.public_key_pgp {
key.0.into()
} else {
return Err(serde::de::Error::missing_field("public_key"));
};
out.push(VerificationMethod {
id: val.id,
controller: val.controller,
public_key: key,
})
}
Ok(out)
}
}
deserializer.deserialize_seq(Visitor)
}
/// A [decentralized identifier] (DID).
///
/// The only currently supported [method] is [did-web].
///
/// # Examples
///
/// ```text
/// did:web:example.com
/// did:web:example.com:u:bob
/// ```
///
/// [decentralized identifier]: https://www.w3.org/TR/did-core/#dfn-decentralized-identifiers
/// [method]: https://www.w3.org/TR/did-core/#dfn-did-methods
/// [did-web]: https://w3c-ccg.github.io/did-method-web/
#[derive(Debug, serde::Deserialize)]
#[serde(try_from = "String")]
pub struct DID(String);
impl DID {
pub fn parse<S>(s: S) -> Result<Self, error::DID>
where
S: AsRef<str> + Into<String>,
{
let uri = Url::parse(s.as_ref())?;
if uri.scheme() != "did" {
return Err(error::DID::NotADid);
}
let mut iter = uri.path().split(':');
let method = iter.next().ok_or(error::DID::UnexpectedEof)?;
if method != "web" {
return Err(error::DID::UnsupportedMethod);
}
if iter.any(|segment| segment.contains('/')) {
return Err(error::DID::MalformedIdentifier(
uri.path().strip_prefix("web:").unwrap().to_owned(),
));
}
let iter = uri.path().split(':').skip(1);
let suf = iter::once("https://").chain(iter).join("/");
let _ = Url::parse(&suf)?;
Ok(Self(s.into()))
}
pub fn id(&self) -> &str {
self
}
pub async fn resolve(&self) -> anyhow::Result<Document> {
let mut url = Url::parse(
&iter::once("https://")
.chain(self.split(':').skip(2))
.join("/"),
)?;
let is_empty_path = url.path() == "/";
{
let mut path = url.path_segments_mut().unwrap();
if is_empty_path {
path.push("/.well-known");
}
path.push("/did.json");
}
Ok(reqwest::get(url).await?.json().await?)
}
}
impl fmt::Display for DID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self)
}
}
impl FromStr for DID {
type Err = error::DID;
fn from_str(s: &str) -> Result<Self, Self::Err> {
DID::parse(s)
}
}
impl TryFrom<String> for DID {
type Error = error::DID;
fn try_from(s: String) -> Result<Self, Self::Error> {
DID::parse(s)
}
}
impl Deref for DID {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for DID {
fn as_ref(&self) -> &str {
self
}
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct VerificationMethodMap {
id: String,
controller: DID,
public_key_jwk: Option<PublicKeyJwk>,
public_key_base58: Option<PublicKeyBase58>,
public_key_multibase: Option<PublicKeyMultibase>,
public_key_pgp: Option<PublicKeyPgp>,
}
#[derive(Debug)]
struct PublicKeyJwk(ed25519::PublicKey);
impl<'de> serde::Deserialize<'de> for PublicKeyJwk {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let map: HashMap<String, String> = serde::Deserialize::deserialize(deserializer)?;
match map.get("kty").map(|s| s.as_str()) {
Some("OKP") => Ok(()),
Some(x) => Err(serde::de::Error::custom(format!("unsupported kty: {x}"))),
None => Err(serde::de::Error::custom("missing kty")),
}?;
match map.get("crv").map(|s| s.as_str()) {
Some("Ed25519") => Ok(()),
Some(x) => Err(serde::de::Error::custom(format!("unsupported crv: {x}"))),
None => Err(serde::de::Error::custom("missing crv")),
}?;
let b64: &str = map.get("x").ok_or(serde::de::Error::custom("missing x"))?;
let bytes = URL_SAFE_NO_PAD
.decode(b64)
.map_err(serde::de::Error::custom)?;
let x = ed25519::PublicKey::from_slice(&bytes).map_err(serde::de::Error::custom)?;
Ok(Self(x))
}
}
#[derive(Debug)]
struct PublicKeyBase58(ed25519::PublicKey);
impl<'de> serde::Deserialize<'de> for PublicKeyBase58 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let b58: &str = serde::Deserialize::deserialize(deserializer)?;
let bytes = bs58::decode(b58)
.into_vec()
.map_err(serde::de::Error::custom)?;
let x = ed25519::PublicKey::from_slice(&bytes).map_err(serde::de::Error::custom)?;
Ok(Self(x))
}
}
#[derive(Debug)]
struct PublicKeyMultibase(ed25519::PublicKey);
impl<'de> serde::Deserialize<'de> for PublicKeyMultibase {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mb: &str = serde::Deserialize::deserialize(deserializer)?;
let (_base, bytes) = multibase::decode(mb).map_err(serde::de::Error::custom)?;
let x = ed25519::PublicKey::from_slice(&bytes[2..]).map_err(serde::de::Error::custom)?;
Ok(Self(x))
}
}
#[derive(Debug)]
struct PublicKeyPgp(pgp::SignedPublicKey);
impl<'de> serde::Deserialize<'de> for PublicKeyPgp {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let ascii: String = serde::Deserialize::deserialize(deserializer)?;
let (key, _) =
pgp::SignedPublicKey::from_string(&ascii).map_err(serde::de::Error::custom)?;
Ok(Self(key))
}
}
// Copyright © 2023 Kim Altintop <kim@eagain.io>
// SPDX-License-Identifier: GPL-2.0-only
use super::*;
#[test]
fn valid_did() {
for ex in [
"did:web:example.com",
"did:web:example.com:u:bob",
"did:web:eagain.io:kim",
] {
let did: DID = ex.parse().unwrap();
assert_eq!(did.id(), ex);
assert_eq!(&did.to_string(), ex);
}
}
#[test]
fn invalid_did_scheme() {
for ex in ["d1d:web:example.com", "https://example.com"] {
match ex.parse::<DID>() {
Ok(did) => unreachable!("did scheme accepted: {ex} -> {did}"),
Err(e) => {
if !matches!(e, error::DID::NotADid) {
unreachable!("unexpected error: {e}")
}
},
}
}
}
#[test]
fn invalid_did_method() {
for ex in ["did:plc:deafbeef", "did:example:123456789abcdefghi"] {
match ex.parse::<DID>() {
Ok(did) => unreachable!("did method accepted: {ex} -> {did}"),
Err(e) => {
if !matches!(e, error::DID::UnsupportedMethod) {
unreachable!("unexpected error: {e}")
}
},
}
}
}
#[test]
fn invalid_method_specific_identifier() {
for ex in ["did:web:eagain.io/kim"] {
match ex.parse::<DID>() {
Ok(did) => unreachable!("did accepted: {ex} -> {did}"),
Err(e) => {
if !matches!(e, error::DID::MalformedIdentifier(..)) {
unreachable!("unexpected error: {e}")
}
},
}
}
}
#[test]
fn verification_methods() {
let input = [
r#"
{
"id": "jwk",
"type": "JsonWebKey2020",
"controller": "did:web:example.com",
"publicKeyJwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "CV-aGlld3nVdgnhoZK0D36Wk-9aIMlZjZOK2XhPMnkQ"
}
}"#,
r#"
{
"id": "ed2018",
"type": "Ed25519VerificationKey2018",
"controller": "did:web:example.com",
"publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
}"#,
r#"
{
"id": "ed2020",
"type": "Ed25519VerificationKey2020",
"controller": "did:web::example.com:u:bob",
"publicKeyMultibase": "z6Mkf5rGMoatrSj1f4CyvuHBeXJELe9RPdzo2PKGNCKVtZxP"
}"#,
r#"
{
"id": "pgp",
"type": "PgpVerificationKey2021",
"controller": "did:web:example.com",
"publicKeyPgp": "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n\r\n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\r\n=JzF5\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n"
}"#,
];
for js in input {
serde_json::from_str::<VerificationMethodMap>(js).unwrap_or_else(|e| {
panic!("failed to decode {js}:\n{e:#?}");
});
}
}
#[test]
fn resolve() {
let did = DID::parse("did:web:eagain.io:kim").unwrap();
let doc = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(did.resolve())
.unwrap();
eprintln!("{doc:#?}");
}
[[package]]
name = "data-encoding-macro"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c904b33cc60130e1aeea4956ab803d08a3f4a0ca82d64ed757afac3891f2bb99"
dependencies = [
"data-encoding",
"data-encoding-macro-internal",
]
[[package]]
name = "data-encoding-macro-internal"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fdf3fce3ce863539ec1d7fd1b6dcc3c645663376b43ed376bbf887733e4f772"
dependencies = [
"data-encoding",
"syn 1.0.109",
]