use super::JwkStorageDocumentError as Error;
use crate::key_id_storage::MethodDigest;
use crate::try_undo_key_generation;
use crate::JwkGenOutput;
use crate::JwkStorageBbsPlusExt;
use crate::KeyIdStorage;
use crate::KeyType;
use crate::Storage;
use crate::StorageResult;
use async_trait::async_trait;
use identity_core::common::Object;
use identity_core::convert::ToJson;
use identity_credential::credential::Credential;
use identity_credential::credential::Jpt;
use identity_credential::credential::JwpCredentialOptions;
use identity_credential::presentation::JwpPresentationOptions;
use identity_credential::presentation::SelectiveDisclosurePresentation;
use identity_did::DIDUrl;
use identity_document::document::CoreDocument;
use identity_verification::MethodData;
use identity_verification::MethodScope;
use identity_verification::VerificationMethod;
use jsonprooftoken::encoding::SerializationType;
use jsonprooftoken::jpa::algs::ProofAlgorithm;
use jsonprooftoken::jpt::claims::JptClaims;
use jsonprooftoken::jwk::key::Jwk;
use jsonprooftoken::jwp::header::IssuerProtectedHeader;
use jsonprooftoken::jwp::header::PresentationProtectedHeader;
use jsonprooftoken::jwp::issued::JwpIssuedBuilder;
use serde::de::DeserializeOwned;
use serde::Serialize;
#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
#[cfg_attr(feature = "send-sync-storage", async_trait)]
pub trait JwpDocumentExt {
async fn generate_method_jwp<K, I>(
&mut self,
storage: &Storage<K, I>,
key_type: KeyType,
alg: ProofAlgorithm,
fragment: Option<&str>,
scope: MethodScope,
) -> StorageResult<String>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage;
async fn create_issued_jwp<K, I>(
&self,
storage: &Storage<K, I>,
fragment: &str,
jpt_claims: &JptClaims,
options: &JwpCredentialOptions,
) -> StorageResult<String>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage;
async fn create_presented_jwp(
&self,
presentation: &mut SelectiveDisclosurePresentation,
method_id: &str,
options: &JwpPresentationOptions,
) -> StorageResult<String>;
async fn create_credential_jpt<K, I, T>(
&self,
credential: &Credential<T>,
storage: &Storage<K, I>,
fragment: &str,
options: &JwpCredentialOptions,
custom_claims: Option<Object>,
) -> StorageResult<Jpt>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync;
async fn create_presentation_jpt(
&self,
presentation: &mut SelectiveDisclosurePresentation,
method_id: &str,
options: &JwpPresentationOptions,
) -> StorageResult<Jpt>;
}
generate_method_for_document_type!(
CoreDocument,
ProofAlgorithm,
JwkStorageBbsPlusExt,
JwkStorageBbsPlusExt::generate_bbs,
generate_method_core_document
);
#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
#[cfg_attr(feature = "send-sync-storage", async_trait)]
impl JwpDocumentExt for CoreDocument {
async fn generate_method_jwp<K, I>(
&mut self,
storage: &Storage<K, I>,
key_type: KeyType,
alg: ProofAlgorithm,
fragment: Option<&str>,
scope: MethodScope,
) -> StorageResult<String>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
{
generate_method_core_document(self, storage, key_type, alg, fragment, scope).await
}
async fn create_issued_jwp<K, I>(
&self,
storage: &Storage<K, I>,
fragment: &str,
jpt_claims: &JptClaims,
options: &JwpCredentialOptions,
) -> StorageResult<String>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
{
let method: &VerificationMethod = self.resolve_method(fragment, None).ok_or(Error::MethodNotFound)?;
let MethodData::PublicKeyJwk(ref jwk) = method.data() else {
return Err(Error::NotPublicKeyJwk);
};
let alg: ProofAlgorithm = jwk
.alg()
.unwrap_or("")
.parse()
.map_err(|_| Error::InvalidJwpAlgorithm)?;
let typ = "JPT".to_string();
let kid = if let Some(ref kid) = options.kid {
kid.clone()
} else {
method.id().to_string()
};
let mut issuer_header = IssuerProtectedHeader::new(alg);
issuer_header.set_typ(Some(typ));
issuer_header.set_kid(Some(kid));
let method_digest: MethodDigest = MethodDigest::new(method).map_err(Error::MethodDigestConstructionError)?;
let key_id = <I as KeyIdStorage>::get_key_id(storage.key_id_storage(), &method_digest)
.await
.map_err(Error::KeyIdStorageError)?;
let jwp_builder = JwpIssuedBuilder::new(issuer_header, jpt_claims.clone());
let header = jwp_builder.get_issuer_protected_header().map_or_else(
|| Err(Error::JwpBuildingError),
|h| h.to_json_vec().map_err(|_| Error::JwpBuildingError),
)?;
let data = jwp_builder.get_payloads().map_or_else(
|| Err(Error::JwpBuildingError),
|p| p.to_bytes().map_err(|_| Error::JwpBuildingError),
)?;
let signature = <K as JwkStorageBbsPlusExt>::sign_bbs(storage.key_storage(), &key_id, &data, &header, jwk)
.await
.map_err(Error::KeyStorageError)?;
jwp_builder
.build_with_proof(signature)
.map_err(|_| Error::JwpBuildingError)?
.encode(SerializationType::COMPACT)
.map_err(|err| Error::EncodingError(Box::new(err)))
}
async fn create_presented_jwp(
&self,
presentation: &mut SelectiveDisclosurePresentation,
method_id: &str,
options: &JwpPresentationOptions,
) -> StorageResult<String> {
let method: &VerificationMethod = self.resolve_method(method_id, None).ok_or(Error::MethodNotFound)?;
let MethodData::PublicKeyJwk(ref jwk) = method.data() else {
return Err(Error::NotPublicKeyJwk);
};
let alg: ProofAlgorithm = jwk
.alg()
.unwrap_or("")
.parse()
.map_err(|_| Error::InvalidJwpAlgorithm)?;
let public_key: Jwk = jwk.try_into().map_err(|_| Error::NotPublicKeyJwk)?;
let mut presentation_header = PresentationProtectedHeader::new(alg.into());
presentation_header.set_nonce(options.nonce.clone());
presentation_header.set_aud(options.audience.as_ref().map(|u| u.to_string()));
presentation.set_presentation_header(presentation_header);
let jwp_builder = presentation.builder();
let presented_jwp = jwp_builder.build(&public_key).map_err(|_| Error::JwpBuildingError)?;
Ok(
presented_jwp
.encode(SerializationType::COMPACT)
.map_err(|e| Error::EncodingError(Box::new(e)))?,
)
}
async fn create_credential_jpt<K, I, T>(
&self,
credential: &Credential<T>,
storage: &Storage<K, I>,
fragment: &str,
options: &JwpCredentialOptions,
custom_claims: Option<Object>,
) -> StorageResult<Jpt>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync,
{
let jpt_claims = credential
.serialize_jpt(custom_claims)
.map_err(Error::ClaimsSerializationError)?;
self
.create_issued_jwp(storage, fragment, &jpt_claims, options)
.await
.map(Jpt::new)
}
async fn create_presentation_jpt(
&self,
presentation: &mut SelectiveDisclosurePresentation,
method_id: &str,
options: &JwpPresentationOptions,
) -> StorageResult<Jpt> {
self
.create_presented_jwp(presentation, method_id, options)
.await
.map(Jpt::new)
}
}
#[cfg(feature = "iota-document")]
mod iota_document {
use super::*;
use identity_iota_core::IotaDocument;
generate_method_for_document_type!(
IotaDocument,
ProofAlgorithm,
JwkStorageBbsPlusExt,
JwkStorageBbsPlusExt::generate_bbs,
generate_method_iota_document
);
#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
#[cfg_attr(feature = "send-sync-storage", async_trait)]
impl JwpDocumentExt for IotaDocument {
async fn generate_method_jwp<K, I>(
&mut self,
storage: &Storage<K, I>,
key_type: KeyType,
alg: ProofAlgorithm,
fragment: Option<&str>,
scope: MethodScope,
) -> StorageResult<String>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
{
generate_method_iota_document(self, storage, key_type, alg, fragment, scope).await
}
async fn create_issued_jwp<K, I>(
&self,
storage: &Storage<K, I>,
fragment: &str,
jpt_claims: &JptClaims,
options: &JwpCredentialOptions,
) -> StorageResult<String>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
{
self
.core_document()
.create_issued_jwp(storage, fragment, jpt_claims, options)
.await
}
async fn create_presented_jwp(
&self,
presentation: &mut SelectiveDisclosurePresentation,
method_id: &str,
options: &JwpPresentationOptions,
) -> StorageResult<String> {
self
.core_document()
.create_presented_jwp(presentation, method_id, options)
.await
}
async fn create_credential_jpt<K, I, T>(
&self,
credential: &Credential<T>,
storage: &Storage<K, I>,
fragment: &str,
options: &JwpCredentialOptions,
custom_claims: Option<Object>,
) -> StorageResult<Jpt>
where
K: JwkStorageBbsPlusExt,
I: KeyIdStorage,
T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync,
{
self
.core_document()
.create_credential_jpt(credential, storage, fragment, options, custom_claims)
.await
}
async fn create_presentation_jpt(
&self,
presentation: &mut SelectiveDisclosurePresentation,
method_id: &str,
options: &JwpPresentationOptions,
) -> StorageResult<Jpt> {
self
.core_document()
.create_presentation_jpt(presentation, method_id, options)
.await
}
}
}