use crate::did::{
Context, DEFAULT_MAX_LEN, Did, DidDocument, DidError, DocumentError, MethodType,
ResolutionError, ResolutionOptions, ResolutionResult, VerificationMaterial, VerificationMethod,
VerificationRelationship,
};
use crate::jose::Jwk;
use crate::resolver::{DidProducer, DidResolver, Hosting, MethodCapabilities, ProduceError};
use super::check_accept;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DidJwk;
impl DidJwk {
pub const METHOD: &'static str = "jwk";
pub const FRAGMENT: &'static str = "0";
pub const MAX_DID_LEN: usize = DEFAULT_MAX_LEN - 1 - Self::FRAGMENT.len();
pub const CONTEXT: [&'static str; 2] = [
Context::DID_V1,
"https://w3id.org/security/suites/jws-2020/v1",
];
pub fn did(jwk: &Jwk) -> Result<Did, DidError> {
let json = serde_json::to_vec(&jwk.public()).expect("a JWK serializes");
let id = crate::jose::base64url::encode(&json);
Did::parse_with_max_len(&format!("did:jwk:{id}"), Self::MAX_DID_LEN)
}
pub fn document(did: &Did) -> Result<DidDocument, ResolutionError> {
if did.method() != Self::METHOD {
return Err(ResolutionError::MethodNotSupported);
}
if did.as_str().len() > Self::MAX_DID_LEN {
return Err(ResolutionError::InvalidDid);
}
let json = crate::jose::base64url::decode(did.method_specific_id())
.map_err(|_| ResolutionError::InvalidDid)?;
let jwk: Jwk = serde_json::from_slice(&json).map_err(|_| ResolutionError::InvalidDid)?;
if jwk.is_private() {
return Err(ResolutionError::InvalidDid);
}
build(did, jwk).map_err(|_| ResolutionError::InvalidDid)
}
}
fn build(did: &Did, jwk: Jwk) -> Result<DidDocument, DocumentError> {
let relationships: &[VerificationRelationship] = match jwk.key_use() {
Some("sig") => &[
VerificationRelationship::Authentication,
VerificationRelationship::AssertionMethod,
VerificationRelationship::CapabilityInvocation,
VerificationRelationship::CapabilityDelegation,
],
Some("enc") => &[VerificationRelationship::KeyAgreement],
_ => VerificationRelationship::ALL,
};
let method = VerificationMethod::new(
did.to_url().with_fragment(DidJwk::FRAGMENT),
MethodType::JSON_WEB_KEY_2020,
did.clone(),
VerificationMaterial::PublicKeyJwk(Box::new(jwk)),
);
DidDocument::builder(did.clone())
.context(Context::uris(DidJwk::CONTEXT))
.verification_method(method, relationships.iter().copied())
.build()
}
#[async_trait::async_trait]
impl DidResolver for DidJwk {
fn method(&self) -> &'static str {
Self::METHOD
}
async fn resolve(
&self,
did: &Did,
options: &ResolutionOptions,
) -> Result<ResolutionResult, ResolutionError> {
check_accept(options)?;
Self::document(did).map(ResolutionResult::new)
}
}
#[async_trait::async_trait]
impl DidProducer for DidJwk {
type Params = ();
fn method(&self) -> &'static str {
Self::METHOD
}
fn capabilities(&self) -> MethodCapabilities {
MethodCapabilities::new(Hosting::Embedded)
.with_single_key(true)
.with_fixed_fragment(Self::FRAGMENT)
}
async fn produce(&self, keys: &[Jwk], _: ()) -> Result<DidDocument, ProduceError> {
let jwk = super::single_key(keys)?;
let did = Self::did(jwk).map_err(|error| ProduceError::UnsupportedKey {
index: 0,
reason: error.to_string(),
})?;
Ok(build(&did, jwk.public())?)
}
}