1use super::JwkStorageDocumentError as Error;
5use crate::key_id_storage::MethodDigest;
6use crate::try_undo_key_generation;
7use crate::JwkGenOutput;
8use crate::JwkStorageBbsPlusExt;
9use crate::KeyIdStorage;
10use crate::KeyType;
11use crate::Storage;
12use crate::StorageResult;
13use async_trait::async_trait;
14use identity_core::common::Object;
15use identity_core::convert::ToJson;
16use identity_credential::credential::Credential;
17use identity_credential::credential::Jpt;
18use identity_credential::credential::JwpCredentialOptions;
19use identity_credential::presentation::JwpPresentationOptions;
20use identity_credential::presentation::SelectiveDisclosurePresentation;
21use identity_did::DIDUrl;
22use identity_document::document::CoreDocument;
23use identity_verification::MethodData;
24use identity_verification::MethodScope;
25use identity_verification::VerificationMethod;
26use jsonprooftoken::encoding::SerializationType;
27use jsonprooftoken::jpa::algs::ProofAlgorithm;
28use jsonprooftoken::jpt::claims::JptClaims;
29use jsonprooftoken::jwk::key::Jwk;
30use jsonprooftoken::jwp::header::IssuerProtectedHeader;
31use jsonprooftoken::jwp::header::PresentationProtectedHeader;
32use jsonprooftoken::jwp::issued::JwpIssuedBuilder;
33use serde::de::DeserializeOwned;
34use serde::Serialize;
35
36#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
38#[cfg_attr(feature = "send-sync-storage", async_trait)]
39pub trait JwpDocumentExt {
40 async fn generate_method_jwp<K, I>(
43 &mut self,
44 storage: &Storage<K, I>,
45 key_type: KeyType,
46 alg: ProofAlgorithm,
47 fragment: Option<&str>,
48 scope: MethodScope,
49 ) -> StorageResult<String>
50 where
51 K: JwkStorageBbsPlusExt,
52 I: KeyIdStorage;
53
54 async fn create_issued_jwp<K, I>(
57 &self,
58 storage: &Storage<K, I>,
59 fragment: &str,
60 jpt_claims: &JptClaims,
61 options: &JwpCredentialOptions,
62 ) -> StorageResult<String>
63 where
64 K: JwkStorageBbsPlusExt,
65 I: KeyIdStorage;
66
67 async fn create_presented_jwp(
70 &self,
71 presentation: &mut SelectiveDisclosurePresentation,
72 method_id: &str,
73 options: &JwpPresentationOptions,
74 ) -> StorageResult<String>;
75
76 async fn create_credential_jpt<K, I, T>(
78 &self,
79 credential: &Credential<T>,
80 storage: &Storage<K, I>,
81 fragment: &str,
82 options: &JwpCredentialOptions,
83 custom_claims: Option<Object>,
84 ) -> StorageResult<Jpt>
85 where
86 K: JwkStorageBbsPlusExt,
87 I: KeyIdStorage,
88 T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync;
89
90 async fn create_presentation_jpt(
92 &self,
93 presentation: &mut SelectiveDisclosurePresentation,
94 method_id: &str,
95 options: &JwpPresentationOptions,
96 ) -> StorageResult<Jpt>;
97}
98
99generate_method_for_document_type!(
104 CoreDocument,
105 ProofAlgorithm,
106 JwkStorageBbsPlusExt,
107 JwkStorageBbsPlusExt::generate_bbs,
108 generate_method_core_document
109);
110
111#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
112#[cfg_attr(feature = "send-sync-storage", async_trait)]
113impl JwpDocumentExt for CoreDocument {
114 async fn generate_method_jwp<K, I>(
115 &mut self,
116 storage: &Storage<K, I>,
117 key_type: KeyType,
118 alg: ProofAlgorithm,
119 fragment: Option<&str>,
120 scope: MethodScope,
121 ) -> StorageResult<String>
122 where
123 K: JwkStorageBbsPlusExt,
124 I: KeyIdStorage,
125 {
126 generate_method_core_document(self, storage, key_type, alg, fragment, scope).await
127 }
128
129 async fn create_issued_jwp<K, I>(
130 &self,
131 storage: &Storage<K, I>,
132 fragment: &str,
133 jpt_claims: &JptClaims,
134 options: &JwpCredentialOptions,
135 ) -> StorageResult<String>
136 where
137 K: JwkStorageBbsPlusExt,
138 I: KeyIdStorage,
139 {
140 let method: &VerificationMethod = self.resolve_method(fragment, None).ok_or(Error::MethodNotFound)?;
142 let MethodData::PublicKeyJwk(ref jwk) = method.data() else {
143 return Err(Error::NotPublicKeyJwk);
144 };
145
146 let alg: ProofAlgorithm = jwk
148 .alg()
149 .unwrap_or("")
150 .parse()
151 .map_err(|_| Error::InvalidJwpAlgorithm)?;
152
153 let typ = "JPT".to_string();
154
155 let kid = if let Some(ref kid) = options.kid {
156 kid.clone()
157 } else {
158 method.id().to_string()
159 };
160
161 let mut issuer_header = IssuerProtectedHeader::new(alg);
162 issuer_header.set_typ(Some(typ));
163 issuer_header.set_kid(Some(kid));
164
165 let method_digest: MethodDigest = MethodDigest::new(method).map_err(Error::MethodDigestConstructionError)?;
167 let key_id = <I as KeyIdStorage>::get_key_id(storage.key_id_storage(), &method_digest)
168 .await
169 .map_err(Error::KeyIdStorageError)?;
170
171 let jwp_builder = JwpIssuedBuilder::new(issuer_header, jpt_claims.clone());
172
173 let header = jwp_builder.get_issuer_protected_header().map_or_else(
174 || Err(Error::JwpBuildingError),
175 |h| h.to_json_vec().map_err(|_| Error::JwpBuildingError),
176 )?;
177
178 let data = jwp_builder.get_payloads().map_or_else(
179 || Err(Error::JwpBuildingError),
180 |p| p.to_bytes().map_err(|_| Error::JwpBuildingError),
181 )?;
182
183 let signature = <K as JwkStorageBbsPlusExt>::sign_bbs(storage.key_storage(), &key_id, &data, &header, jwk)
184 .await
185 .map_err(Error::KeyStorageError)?;
186
187 jwp_builder
188 .build_with_proof(signature)
189 .map_err(|_| Error::JwpBuildingError)?
190 .encode(SerializationType::COMPACT)
191 .map_err(|err| Error::EncodingError(Box::new(err)))
192 }
193
194 async fn create_presented_jwp(
195 &self,
196 presentation: &mut SelectiveDisclosurePresentation,
197 method_id: &str,
198 options: &JwpPresentationOptions,
199 ) -> StorageResult<String> {
200 let method: &VerificationMethod = self.resolve_method(method_id, None).ok_or(Error::MethodNotFound)?;
202 let MethodData::PublicKeyJwk(ref jwk) = method.data() else {
203 return Err(Error::NotPublicKeyJwk);
204 };
205
206 let alg: ProofAlgorithm = jwk
208 .alg()
209 .unwrap_or("")
210 .parse()
211 .map_err(|_| Error::InvalidJwpAlgorithm)?;
212
213 let public_key: Jwk = jwk.try_into().map_err(|_| Error::NotPublicKeyJwk)?;
214
215 let mut presentation_header = PresentationProtectedHeader::new(alg.into());
216 presentation_header.set_nonce(options.nonce.clone());
217 presentation_header.set_aud(options.audience.as_ref().map(|u| u.to_string()));
218
219 presentation.set_presentation_header(presentation_header);
220
221 let jwp_builder = presentation.builder();
222
223 let presented_jwp = jwp_builder.build(&public_key).map_err(|_| Error::JwpBuildingError)?;
224
225 Ok(
226 presented_jwp
227 .encode(SerializationType::COMPACT)
228 .map_err(|e| Error::EncodingError(Box::new(e)))?,
229 )
230 }
231
232 async fn create_credential_jpt<K, I, T>(
233 &self,
234 credential: &Credential<T>,
235 storage: &Storage<K, I>,
236 fragment: &str,
237 options: &JwpCredentialOptions,
238 custom_claims: Option<Object>,
239 ) -> StorageResult<Jpt>
240 where
241 K: JwkStorageBbsPlusExt,
242 I: KeyIdStorage,
243 T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync,
244 {
245 let jpt_claims = credential
246 .serialize_jpt(custom_claims)
247 .map_err(Error::ClaimsSerializationError)?;
248
249 self
250 .create_issued_jwp(storage, fragment, &jpt_claims, options)
251 .await
252 .map(Jpt::new)
253 }
254
255 async fn create_presentation_jpt(
256 &self,
257 presentation: &mut SelectiveDisclosurePresentation,
258 method_id: &str,
259 options: &JwpPresentationOptions,
260 ) -> StorageResult<Jpt> {
261 self
262 .create_presented_jwp(presentation, method_id, options)
263 .await
264 .map(Jpt::new)
265 }
266}
267
268#[cfg(feature = "iota-document")]
272mod iota_document {
273 use super::*;
274 use identity_iota_core::IotaDocument;
275
276 generate_method_for_document_type!(
277 IotaDocument,
278 ProofAlgorithm,
279 JwkStorageBbsPlusExt,
280 JwkStorageBbsPlusExt::generate_bbs,
281 generate_method_iota_document
282 );
283
284 #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
285 #[cfg_attr(feature = "send-sync-storage", async_trait)]
286 impl JwpDocumentExt for IotaDocument {
287 async fn generate_method_jwp<K, I>(
288 &mut self,
289 storage: &Storage<K, I>,
290 key_type: KeyType,
291 alg: ProofAlgorithm,
292 fragment: Option<&str>,
293 scope: MethodScope,
294 ) -> StorageResult<String>
295 where
296 K: JwkStorageBbsPlusExt,
297 I: KeyIdStorage,
298 {
299 generate_method_iota_document(self, storage, key_type, alg, fragment, scope).await
300 }
301
302 async fn create_issued_jwp<K, I>(
303 &self,
304 storage: &Storage<K, I>,
305 fragment: &str,
306 jpt_claims: &JptClaims,
307 options: &JwpCredentialOptions,
308 ) -> StorageResult<String>
309 where
310 K: JwkStorageBbsPlusExt,
311 I: KeyIdStorage,
312 {
313 self
314 .core_document()
315 .create_issued_jwp(storage, fragment, jpt_claims, options)
316 .await
317 }
318
319 async fn create_presented_jwp(
320 &self,
321 presentation: &mut SelectiveDisclosurePresentation,
322 method_id: &str,
323 options: &JwpPresentationOptions,
324 ) -> StorageResult<String> {
325 self
326 .core_document()
327 .create_presented_jwp(presentation, method_id, options)
328 .await
329 }
330
331 async fn create_credential_jpt<K, I, T>(
332 &self,
333 credential: &Credential<T>,
334 storage: &Storage<K, I>,
335 fragment: &str,
336 options: &JwpCredentialOptions,
337 custom_claims: Option<Object>,
338 ) -> StorageResult<Jpt>
339 where
340 K: JwkStorageBbsPlusExt,
341 I: KeyIdStorage,
342 T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync,
343 {
344 self
345 .core_document()
346 .create_credential_jpt(credential, storage, fragment, options, custom_claims)
347 .await
348 }
349
350 async fn create_presentation_jpt(
351 &self,
352 presentation: &mut SelectiveDisclosurePresentation,
353 method_id: &str,
354 options: &JwpPresentationOptions,
355 ) -> StorageResult<Jpt> {
356 self
357 .core_document()
358 .create_presentation_jpt(presentation, method_id, options)
359 .await
360 }
361 }
362}