use jsonwebtoken::jwk::Jwk;
use serde::Serialize;
use crate::nuts_auth::holder::{Did, HolderKey};
pub const MEDIA_TYPE: &str = "application/did+ld+json";
pub const DID_CONTEXT: &str = "https://www.w3.org/ns/did/v1";
pub const JWS_2020_CONTEXT: &str = "https://w3id.org/security/suites/jws-2020/v1";
pub const JSON_WEB_KEY_2020: &str = "JsonWebKey2020";
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum DocumentError {
#[error("the DID document names no key")]
NoKey,
#[error("the key {kid} is not a key of {did}")]
OtherDid {
kid: String,
did: String,
},
#[error("two different keys are named {kid}")]
SameKeyId {
kid: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct VerificationMethod {
id: String,
#[serde(rename = "type")]
kind: &'static str,
controller: String,
#[serde(rename = "publicKeyJwk")]
public_key_jwk: Jwk,
}
impl VerificationMethod {
#[must_use]
pub fn id(&self) -> &str {
&self.id
}
#[must_use]
pub fn public_key_jwk(&self) -> &Jwk {
&self.public_key_jwk
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct DidDocument {
#[serde(rename = "@context")]
context: [&'static str; 2],
id: String,
#[serde(rename = "verificationMethod")]
verification_method: Vec<VerificationMethod>,
authentication: Vec<String>,
#[serde(rename = "assertionMethod")]
assertion_method: Vec<String>,
}
impl DidDocument {
pub fn new<'a>(
did: &Did,
keys: impl IntoIterator<Item = &'a HolderKey>,
) -> Result<Self, DocumentError> {
let mut methods: Vec<VerificationMethod> = Vec::new();
for key in keys {
let of_did = key
.kid()
.strip_prefix(did.as_str())
.is_some_and(|rest| rest.starts_with('#'));
if !of_did {
return Err(DocumentError::OtherDid {
kid: key.kid().to_owned(),
did: did.as_str().to_owned(),
});
}
match methods.iter().find(|method| method.id == key.kid()) {
Some(known) if known.public_key_jwk == *key.public() => {}
Some(_) => {
return Err(DocumentError::SameKeyId {
kid: key.kid().to_owned(),
});
}
None => methods.push(VerificationMethod {
id: key.kid().to_owned(),
kind: JSON_WEB_KEY_2020,
controller: did.as_str().to_owned(),
public_key_jwk: key.public().clone(),
}),
}
}
if methods.is_empty() {
return Err(DocumentError::NoKey);
}
let referenced: Vec<String> = methods.iter().map(|method| method.id.clone()).collect();
Ok(Self {
context: [DID_CONTEXT, JWS_2020_CONTEXT],
id: did.as_str().to_owned(),
verification_method: methods,
authentication: referenced.clone(),
assertion_method: referenced,
})
}
#[must_use]
pub fn id(&self) -> &str {
&self.id
}
#[must_use]
pub fn verification_methods(&self) -> &[VerificationMethod] {
&self.verification_method
}
}