identity_storage/storage/
timeframe_revocation_ext.rs1use super::JwkStorageDocumentError as Error;
5use crate::JwkStorageBbsPlusExt;
6use crate::KeyIdStorage;
7use crate::MethodDigest;
8use crate::Storage;
9use crate::StorageResult;
10use async_trait::async_trait;
11use identity_core::common::Duration;
12use identity_core::common::Timestamp;
13use identity_credential::credential::Jpt;
14use identity_credential::revocation::RevocationTimeframeStatus;
15use identity_document::document::CoreDocument;
16use identity_verification::MethodData;
17use identity_verification::VerificationMethod;
18use jsonprooftoken::encoding::SerializationType;
19use jsonprooftoken::jpt::payloads::Payloads;
20use jsonprooftoken::jwp::issued::JwpIssued;
21use serde_json::Value;
22use zkryptium::bbsplus::signature::BBSplusSignature;
23
24pub struct ProofUpdateCtx {
26 pub old_start_validity_timeframe: Vec<u8>,
28 pub new_start_validity_timeframe: Vec<u8>,
30 pub old_end_validity_timeframe: Vec<u8>,
32 pub new_end_validity_timeframe: Vec<u8>,
34 pub index_start_validity_timeframe: usize,
36 pub index_end_validity_timeframe: usize,
38 pub number_of_signed_messages: usize,
40}
41
42#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
44#[cfg_attr(feature = "send-sync-storage", async_trait)]
45pub trait TimeframeRevocationExtension {
46 async fn update<K, I>(
48 &self,
49 storage: &Storage<K, I>,
50 fragment: &str,
51 start_validity: Option<Timestamp>,
52 duration: Duration,
53 credential_jwp: &mut JwpIssued,
54 ) -> StorageResult<Jpt>
55 where
56 K: JwkStorageBbsPlusExt,
57 I: KeyIdStorage;
58}
59
60#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
65#[cfg_attr(feature = "send-sync-storage", async_trait)]
66impl TimeframeRevocationExtension for CoreDocument {
67 async fn update<K, I>(
68 &self,
69 storage: &Storage<K, I>,
70 fragment: &str,
71 start_validity: Option<Timestamp>,
72 duration: Duration,
73 credential_jwp: &mut JwpIssued,
74 ) -> StorageResult<Jpt>
75 where
76 K: JwkStorageBbsPlusExt,
77 I: KeyIdStorage,
78 {
79 let method: &VerificationMethod = self.resolve_method(fragment, None).ok_or(Error::MethodNotFound)?;
81 let MethodData::PublicKeyJwk(ref jwk) = method.data() else {
82 return Err(Error::NotPublicKeyJwk);
83 };
84
85 let method_digest: MethodDigest = MethodDigest::new(method).map_err(Error::MethodDigestConstructionError)?;
87 let key_id = <I as KeyIdStorage>::get_key_id(storage.key_id_storage(), &method_digest)
88 .await
89 .map_err(Error::KeyIdStorageError)?;
90
91 let new_start_validity_timeframe = start_validity.unwrap_or(Timestamp::now_utc());
92 let new_end_validity_timeframe = new_start_validity_timeframe
93 .checked_add(duration)
94 .ok_or(Error::ProofUpdateError("Invalid granularity".to_owned()))?;
95 let new_start_validity_timeframe = new_start_validity_timeframe.to_rfc3339();
96 let new_end_validity_timeframe = new_end_validity_timeframe.to_rfc3339();
97
98 let proof = credential_jwp.get_proof();
99 let claims = credential_jwp
100 .get_claims()
101 .ok_or(Error::ProofUpdateError("Should not happen".to_owned()))?;
102 let mut payloads: Payloads = credential_jwp.get_payloads().clone();
103
104 let index_start_validity_timeframe = claims
105 .get_claim_index(format!(
106 "vc.credentialStatus.{}",
107 RevocationTimeframeStatus::START_TIMEFRAME_PROPERTY
108 ))
109 .ok_or(Error::ProofUpdateError(
110 "'startValidityTimeframe' property NOT found".to_owned(),
111 ))?;
112 let index_end_validity_timeframe = claims
113 .get_claim_index(format!(
114 "vc.credentialStatus.{}",
115 RevocationTimeframeStatus::END_TIMEFRAME_PROPERTY
116 ))
117 .ok_or(Error::ProofUpdateError(
118 "'endValidityTimeframe' property NOT found".to_owned(),
119 ))?;
120
121 let old_start_validity_timeframe = payloads
122 .replace_payload_at_index(
123 index_start_validity_timeframe,
124 Value::String(new_start_validity_timeframe.clone()),
125 )
126 .map(serde_json::from_value::<String>)
127 .map_err(|_| Error::ProofUpdateError("'startValidityTimeframe' value NOT found".to_owned()))?
128 .map_err(|_| Error::ProofUpdateError("'startValidityTimeframe' value NOT a JSON String".to_owned()))?;
129
130 let old_end_validity_timeframe = payloads
131 .replace_payload_at_index(
132 index_end_validity_timeframe,
133 Value::String(new_end_validity_timeframe.clone()),
134 )
135 .map(serde_json::from_value::<String>)
136 .map_err(|_| Error::ProofUpdateError("'endValidityTimeframe' value NOT found".to_owned()))?
137 .map_err(|_| Error::ProofUpdateError("'endValidityTimeframe' value NOT a JSON String".to_owned()))?;
138
139 let proof: [u8; BBSplusSignature::BYTES] = proof
140 .try_into()
141 .map_err(|_| Error::ProofUpdateError("Invalid bytes length of JWP proof".to_owned()))?;
142
143 let proof_update_ctx = ProofUpdateCtx {
144 old_start_validity_timeframe: serde_json::to_vec(&old_start_validity_timeframe).unwrap(),
145 new_start_validity_timeframe: serde_json::to_vec(&new_start_validity_timeframe).unwrap(),
146 old_end_validity_timeframe: serde_json::to_vec(&old_end_validity_timeframe).unwrap(),
147 new_end_validity_timeframe: serde_json::to_vec(&new_end_validity_timeframe).unwrap(),
148 index_start_validity_timeframe,
149 index_end_validity_timeframe,
150 number_of_signed_messages: payloads.0.len(),
151 };
152
153 let new_proof =
154 <K as JwkStorageBbsPlusExt>::update_signature(storage.key_storage(), &key_id, jwk, &proof, proof_update_ctx)
155 .await
156 .map_err(Error::KeyStorageError)?;
157
158 credential_jwp.set_proof(&new_proof);
159 credential_jwp.set_payloads(payloads);
160
161 let jpt = credential_jwp
162 .encode(SerializationType::COMPACT)
163 .map_err(|e| Error::EncodingError(Box::new(e)))?;
164
165 Ok(Jpt::new(jpt))
166 }
167}
168
169#[cfg(feature = "iota-document")]
173mod iota_document {
174 use super::*;
175 use identity_iota_core::IotaDocument;
176
177 #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
178 #[cfg_attr(feature = "send-sync-storage", async_trait)]
179 impl TimeframeRevocationExtension for IotaDocument {
180 async fn update<K, I>(
181 &self,
182 storage: &Storage<K, I>,
183 fragment: &str,
184 start_validity: Option<Timestamp>,
185 duration: Duration,
186 credential_jwp: &mut JwpIssued,
187 ) -> StorageResult<Jpt>
188 where
189 K: JwkStorageBbsPlusExt,
190 I: KeyIdStorage,
191 {
192 self
193 .core_document()
194 .update(storage, fragment, start_validity, duration, credential_jwp)
195 .await
196 }
197 }
198}