identity_credential/revocation/revocation_bitmap_2022/
document_ext.rs1use super::RevocationBitmap;
5use identity_document::document::CoreDocument;
6use identity_document::service::Service;
7use identity_document::utils::DIDUrlQuery;
8use identity_document::utils::Queryable;
9
10use crate::revocation::RevocationError;
11use crate::revocation::RevocationResult;
12
13pub trait RevocationDocumentExt: private::Sealed {
16 fn revoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> RevocationResult<()>
19 where
20 Q: Into<DIDUrlQuery<'query>>;
21
22 fn unrevoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> RevocationResult<()>
25 where
26 Q: Into<DIDUrlQuery<'query>>;
27
28 fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> RevocationResult<RevocationBitmap>;
35}
36
37mod private {
38 use super::CoreDocument;
39
40 pub trait Sealed {}
41 impl Sealed for CoreDocument {}
42}
43
44impl RevocationDocumentExt for CoreDocument {
45 fn revoke_credentials<'query, 'me, Q>(&'me mut self, service_query: Q, indices: &[u32]) -> RevocationResult<()>
46 where
47 Q: Into<DIDUrlQuery<'query>>,
48 {
49 update_revocation_bitmap(self, service_query, |revocation_bitmap| {
50 for credential in indices {
51 revocation_bitmap.revoke(*credential);
52 }
53 })
54 }
55
56 fn unrevoke_credentials<'query, 'me, Q>(&mut self, service_query: Q, indices: &[u32]) -> RevocationResult<()>
57 where
58 Q: Into<DIDUrlQuery<'query>>,
59 {
60 update_revocation_bitmap(self, service_query, |revocation_bitmap| {
61 for credential in indices {
62 revocation_bitmap.unrevoke(*credential);
63 }
64 })
65 }
66
67 fn resolve_revocation_bitmap(&self, query: DIDUrlQuery<'_>) -> RevocationResult<RevocationBitmap> {
68 self
69 .resolve_service(query)
70 .ok_or(RevocationError::InvalidService("revocation bitmap service not found"))
71 .and_then(RevocationBitmap::try_from)
72 }
73}
74
75fn update_revocation_bitmap<'query, 'me, F, Q>(
76 document: &'me mut CoreDocument,
77 service_query: Q,
78 f: F,
79) -> RevocationResult<()>
80where
81 F: FnOnce(&mut RevocationBitmap),
82 Q: Into<DIDUrlQuery<'query>>,
83{
84 let service: &mut Service = document
85 .service_mut_unchecked()
86 .query_mut(service_query)
87 .ok_or(RevocationError::InvalidService("invalid id - service not found"))?;
88
89 let mut revocation_bitmap: RevocationBitmap = RevocationBitmap::try_from(&*service)?;
90 f(&mut revocation_bitmap);
91
92 std::mem::swap(service.service_endpoint_mut(), &mut revocation_bitmap.to_endpoint()?);
93
94 Ok(())
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use identity_core::convert::FromJson;
101 use identity_did::DID;
102
103 const START_DOCUMENT_JSON: &str = r#"{
104 "id": "did:example:1234",
105 "verificationMethod": [
106 {
107 "id": "did:example:1234#key-1",
108 "controller": "did:example:1234",
109 "type": "Ed25519VerificationKey2018",
110 "publicKeyMultibase": "zJdzr2UvC"
111 },
112 {
113 "id": "did:example:1234#key-2",
114 "controller": "did:example:1234",
115 "type": "Ed25519VerificationKey2018",
116 "publicKeyMultibase": "zJdzr2UvD"
117 },
118 {
119 "id": "did:example:1234#key-3",
120 "controller": "did:example:1234",
121 "type": "Ed25519VerificationKey2018",
122 "publicKeyMultibase": "zJdzr2UvE"
123 }
124 ],
125 "authentication": [
126 {
127 "id": "did:example:1234#auth-key",
128 "controller": "did:example:1234",
129 "type": "Ed25519VerificationKey2018",
130 "publicKeyMultibase": "zT7yhPEwJZL4G"
131 },
132 "did:example:1234#key-3"
133 ],
134 "keyAgreement": [
135 "did:example:1234#key-4"
136 ]
137 }
138 "#;
139
140 #[test]
141 fn test_revocation() {
142 let mut document: CoreDocument = CoreDocument::from_json(&START_DOCUMENT_JSON).unwrap();
143
144 let indices_1 = [3, 9, 254, 65536];
145 let indices_2 = [2, 15, 1337, 1000];
146
147 let service_id = document.id().to_url().join("#revocation-service").unwrap();
148
149 assert!(document.revoke_credentials(&service_id, &indices_2).is_err());
151 assert!(document.unrevoke_credentials(&service_id, &indices_2).is_err());
152
153 let mut bitmap: crate::revocation::RevocationBitmap = crate::revocation::RevocationBitmap::new();
155 for index in indices_1.iter() {
156 bitmap.revoke(*index);
157 }
158
159 assert!(document
160 .insert_service(bitmap.to_service(service_id.clone()).unwrap())
161 .is_ok());
162
163 document.revoke_credentials(&service_id, &indices_2).unwrap();
165 let service: &Service = document.resolve_service(&service_id).unwrap();
166 let decoded_bitmap: crate::revocation::RevocationBitmap = service.try_into().unwrap();
167
168 for index in indices_1.iter().chain(indices_2.iter()) {
170 assert!(decoded_bitmap.is_revoked(*index));
171 }
172
173 document.unrevoke_credentials(&service_id, &indices_1).unwrap();
175
176 let service: &Service = document.resolve_service(&service_id).unwrap();
177 let decoded_bitmap: crate::revocation::RevocationBitmap = service.try_into().unwrap();
178
179 for index in indices_2 {
181 assert!(decoded_bitmap.is_revoked(index));
182 }
183 for index in indices_1 {
184 assert!(!decoded_bitmap.is_revoked(index));
185 }
186 }
187}