use core::fmt;
use std::sync::{Arc, PoisonError, RwLock};
use super::{Identifier, Identity, IdentityError, IdentityKind};
use crate::did::{DidMethod, DidUrl};
use crate::jose::{Jwk, JwsAlgorithm, KeyType, SignError, Signer};
use crate::keys::{KeyId, KeySigner, KeySpec, KeyStore, KeyStoreError};
use crate::resolver::DidProducer;
pub struct KeyStoreIdentity {
store: Arc<dyn KeyStore>,
producers: Vec<Arc<dyn DidProducer<Params = ()>>>,
generate_missing: bool,
bindings: RwLock<Vec<Binding>>,
}
#[derive(Clone)]
struct Binding {
kind: IdentityKind,
signer: KeySigner<Arc<dyn KeyStore>>,
identifier: Identifier,
kid: Option<String>,
}
impl KeyStoreIdentity {
pub fn new(store: Arc<dyn KeyStore>) -> Self {
Self {
store,
producers: Vec::new(),
generate_missing: false,
bindings: RwLock::new(Vec::new()),
}
}
#[must_use]
pub fn with_producer(mut self, producer: impl DidProducer<Params = ()> + 'static) -> Self {
self.producers
.retain(|registered| registered.method() != producer.method());
self.producers.push(Arc::new(producer));
self
}
#[must_use]
pub fn generate_missing(mut self, enabled: bool) -> Self {
self.generate_missing = enabled;
self
}
pub async fn bind(
&self,
kind: IdentityKind,
alg: JwsAlgorithm,
key: KeyId,
) -> Result<Identifier, IdentityError> {
let binding = self.derive(kind, alg, key).await?;
let identifier = binding.identifier.clone();
self.insert(binding, true);
Ok(identifier)
}
pub async fn bind_did_url(
&self,
kind: IdentityKind,
alg: JwsAlgorithm,
key: KeyId,
url: DidUrl,
) -> Result<Identifier, IdentityError> {
match &kind {
IdentityKind::Did(method) if method.matches(url.did()) => {}
_ => return Err(IdentityError::UnsupportedKind(kind)),
}
let signer = self.signer_for(alg, key).await?;
let identifier = Identifier::Did(url.did().clone());
self.insert(
Binding {
kind,
signer,
identifier: identifier.clone(),
kid: Some(url.to_string()),
},
true,
);
Ok(identifier)
}
pub fn bindings(&self) -> Vec<(IdentityKind, JwsAlgorithm, KeyId)> {
self.read()
.iter()
.map(|binding| {
(
binding.kind.clone(),
binding.signer.alg(),
binding.signer.key_id().clone(),
)
})
.collect()
}
async fn signer_for(
&self,
alg: JwsAlgorithm,
key: KeyId,
) -> Result<KeySigner<Arc<dyn KeyStore>>, IdentityError> {
KeySigner::new(Arc::clone(&self.store), key, alg)
.await
.map_err(|error| match error {
KeyStoreError::UnsupportedAlgorithm(alg) => {
IdentityError::UnsupportedAlgorithm(alg)
}
error => IdentityError::KeyStore(error),
})
}
#[expect(
clippy::similar_names,
reason = "`kind` and `kid` are the terms of the API"
)]
async fn derive(
&self,
kind: IdentityKind,
alg: JwsAlgorithm,
key: KeyId,
) -> Result<Binding, IdentityError> {
let producer = match &kind {
IdentityKind::Did(method) => Some(
self.producer(method)
.ok_or_else(|| IdentityError::UnsupportedKind(kind.clone()))?,
),
IdentityKind::JwkThumbprint => None,
};
let signer = self.signer_for(alg, key).await?;
let public = signer.public_jwk().public();
let (identifier, kid) = match (&kind, producer) {
(IdentityKind::Did(method), Some(producer)) => {
let document = producer.produce(&[public], ()).await?;
if !method.matches(&document.id) {
return Err(IdentityError::UnsupportedKind(kind));
}
let kid = document
.methods(None)
.next()
.map(|method| method.id.to_string())
.ok_or_else(|| IdentityError::NoVerificationMethod(document.id.clone()))?;
(Identifier::Did(document.id), Some(kid))
}
_ => (Identifier::JwkThumbprint(public.thumbprint_sha256()?), None),
};
Ok(Binding {
kind,
signer,
identifier,
kid,
})
}
fn producer(&self, method: &DidMethod) -> Option<&Arc<dyn DidProducer<Params = ()>>> {
self.producers
.iter()
.find(|producer| producer.method() == method.name())
}
async fn binding(
&self,
kind: &IdentityKind,
alg: JwsAlgorithm,
) -> Result<Binding, IdentityError> {
if let Some(binding) = self.find(kind, alg) {
return Ok(binding);
}
if !self.generate_missing {
return Err(if self.read().iter().any(|binding| binding.kind == *kind) {
IdentityError::UnsupportedAlgorithm(alg)
} else {
IdentityError::UnsupportedKind(kind.clone())
});
}
if let IdentityKind::Did(method) = kind
&& self.producer(method).is_none()
{
return Err(IdentityError::UnsupportedKind(kind.clone()));
}
let capabilities = self.store.capabilities();
let Some(key_type) = key_type_for(alg).filter(|_| capabilities.supports(alg)) else {
return Err(IdentityError::UnsupportedAlgorithm(alg));
};
if !capabilities.can_generate {
return Err(KeyStoreError::Unsupported("generate").into());
}
let key = self.store.generate(KeySpec::new(key_type, alg)).await?;
let binding = match self.derive(kind.clone(), alg, key.clone()).await {
Ok(binding) => binding,
Err(error) => {
self.discard(&key).await;
return Err(error);
}
};
let kept = self.insert(binding, false);
if kept.signer.key_id() != &key {
self.discard(&key).await;
}
Ok(kept)
}
async fn discard(&self, key: &KeyId) {
if self.store.capabilities().can_delete {
let _ = self.store.delete(key).await;
}
}
fn find(&self, kind: &IdentityKind, alg: JwsAlgorithm) -> Option<Binding> {
self.read()
.iter()
.find(|binding| binding.kind == *kind && binding.signer.alg() == alg)
.cloned()
}
fn insert(&self, binding: Binding, replace: bool) -> Binding {
let mut bindings = self
.bindings
.write()
.unwrap_or_else(PoisonError::into_inner);
let existing = bindings
.iter_mut()
.find(|bound| bound.kind == binding.kind && bound.signer.alg() == binding.signer.alg());
match existing {
Some(bound) if replace => {
*bound = binding.clone();
binding
}
Some(bound) => bound.clone(),
None => {
bindings.push(binding.clone());
binding
}
}
}
fn read(&self) -> std::sync::RwLockReadGuard<'_, Vec<Binding>> {
self.bindings.read().unwrap_or_else(PoisonError::into_inner)
}
}
fn key_type_for(alg: JwsAlgorithm) -> Option<KeyType> {
match alg {
JwsAlgorithm::EdDSA => Some(KeyType::Ed25519),
JwsAlgorithm::ES256 => Some(KeyType::P256),
JwsAlgorithm::ES256K => Some(KeyType::Secp256k1),
JwsAlgorithm::RS256 | JwsAlgorithm::PS256 => Some(KeyType::Rsa2048),
JwsAlgorithm::ES384 => None,
}
}
impl fmt::Debug for KeyStoreIdentity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let producers: Vec<&str> = self
.producers
.iter()
.map(|producer| producer.method())
.collect();
f.debug_struct("KeyStoreIdentity")
.field("producers", &producers)
.field("generate_missing", &self.generate_missing)
.field("bindings", &self.bindings())
.finish_non_exhaustive()
}
}
#[async_trait::async_trait]
impl Identity for KeyStoreIdentity {
fn capabilities(&self) -> Vec<(IdentityKind, JwsAlgorithm)> {
let mut pairs: Vec<(IdentityKind, JwsAlgorithm)> = self
.read()
.iter()
.map(|binding| (binding.kind.clone(), binding.signer.alg()))
.collect();
let capabilities = self.store.capabilities();
if self.generate_missing && capabilities.can_generate {
let kinds = self
.producers
.iter()
.filter_map(|producer| DidMethod::new(producer.method()).ok())
.map(IdentityKind::Did)
.chain([IdentityKind::JwkThumbprint]);
for kind in kinds {
for &alg in &capabilities.algorithms {
if key_type_for(alg).is_some() && !pairs.contains(&(kind.clone(), alg)) {
pairs.push((kind.clone(), alg));
}
}
}
}
pairs
}
async fn identifier(
&self,
kind: &IdentityKind,
alg: JwsAlgorithm,
) -> Result<Identifier, IdentityError> {
Ok(self.binding(kind, alg).await?.identifier)
}
async fn public_jwk(
&self,
kind: &IdentityKind,
alg: JwsAlgorithm,
) -> Result<Jwk, IdentityError> {
Ok(self.binding(kind, alg).await?.signer.public_jwk().public())
}
async fn signer(
&self,
kind: &IdentityKind,
alg: JwsAlgorithm,
) -> Result<Box<dyn Signer + '_>, IdentityError> {
let binding = self.binding(kind, alg).await?;
Ok(Box::new(BoundSigner {
signer: binding.signer,
kid: binding.kid,
}))
}
}
struct BoundSigner {
signer: KeySigner<Arc<dyn KeyStore>>,
kid: Option<String>,
}
#[async_trait::async_trait]
impl Signer for BoundSigner {
fn alg(&self) -> JwsAlgorithm {
self.signer.alg()
}
fn kid(&self) -> Option<&str> {
self.kid.as_deref()
}
async fn sign(&self, signing_input: &[u8]) -> Result<Vec<u8>, SignError> {
self.signer.sign(signing_input).await
}
}