trustchain_ion/
controller.rs

1//! Implementation of `Controller` API for ION DID method.
2use crate::attestor::IONAttestor;
3use crate::ion::IONTest as ION;
4use crate::TrustchainIONError;
5use did_ion::sidetree::{DIDStatePatch, PublicKeyJwk, ServiceEndpointEntry, Sidetree};
6use serde_json::{Map, Value};
7use ssi::did::ServiceEndpoint;
8use ssi::did_resolve::{DocumentMetadata, Metadata};
9use ssi::jwk::JWK;
10use std::convert::TryFrom;
11use trustchain_core::attestor::Attestor;
12use trustchain_core::controller::Controller;
13use trustchain_core::key_manager::{ControllerKeyManager, KeyManager, KeyManagerError, KeyType};
14use trustchain_core::subject::Subject;
15use trustchain_core::utils::generate_key;
16use trustchain_core::{TRUSTCHAIN_PROOF_SERVICE_ID_VALUE, TRUSTCHAIN_PROOF_SERVICE_TYPE_VALUE};
17impl KeyManager for IONController {}
18impl ControllerKeyManager for IONController {}
19
20/// Type for holding controller data.
21pub struct ControllerData {
22    did: String,
23    controlled_did: String,
24    update_key: JWK,
25    recovery_key: JWK,
26}
27
28impl ControllerData {
29    pub fn new(did: String, controlled_did: String, update_key: JWK, recovery_key: JWK) -> Self {
30        ControllerData {
31            did,
32            controlled_did,
33            update_key,
34            recovery_key,
35        }
36    }
37}
38
39impl TryFrom<ControllerData> for IONController {
40    type Error = Box<dyn std::error::Error>;
41    fn try_from(data: ControllerData) -> Result<Self, Self::Error> {
42        let controller = IONController {
43            did: data.did,
44            controlled_did: data.controlled_did,
45        };
46        // Attempt to save the update key, but do not overwrite existing key data.
47        controller.save_key(
48            controller.controlled_did_suffix(),
49            KeyType::UpdateKey,
50            &data.update_key,
51            false,
52        )?;
53        // Attempt to save the recovery key, but do not overwrite existing key data.
54        controller.save_key(
55            controller.controlled_did_suffix(),
56            KeyType::RecoveryKey,
57            &data.recovery_key,
58            false,
59        )?;
60        Ok(controller)
61    }
62}
63
64/// Struct for IONController.
65pub struct IONController {
66    did: String,
67    controlled_did: String,
68}
69
70impl IONController {
71    /// Constructs a new IONController instance from existing Subject and Controller DIDs.
72    pub fn new(did: &str, controlled_did: &str) -> Result<Self, Box<dyn std::error::Error>> {
73        Ok(Self {
74            did: did.to_owned(),
75            controlled_did: controlled_did.to_owned(),
76        })
77    }
78
79    // TODO: consider moving the create operation into this struct.
80    // fn create(doc: DocumentState) -> IONController {
81    //     todo!()
82    // }
83}
84
85impl Subject for IONController {
86    fn did(&self) -> &str {
87        &self.did
88    }
89}
90
91impl Controller for IONController {
92    fn controlled_did(&self) -> &str {
93        &self.controlled_did
94    }
95
96    fn update_key(&self) -> Result<JWK, KeyManagerError> {
97        let update_key = self.read_update_key(self.controlled_did_suffix())?;
98        Ok(update_key)
99    }
100
101    fn next_update_key(&self) -> Result<Option<JWK>, KeyManagerError> {
102        let next_update_key = self.read_next_update_key(self.controlled_did_suffix())?;
103        Ok(Some(next_update_key))
104    }
105
106    fn generate_next_update_key(&self) -> Result<(), KeyManagerError> {
107        let key = generate_key();
108        self.save_key(
109            self.controlled_did_suffix(),
110            KeyType::NextUpdateKey,
111            &key,
112            false,
113        )?;
114        Ok(())
115    }
116
117    fn recovery_key(&self) -> Result<JWK, KeyManagerError> {
118        let recovery_key = self.read_recovery_key(self.controlled_did_suffix())?;
119        Ok(recovery_key)
120    }
121
122    fn to_attestor(&self) -> Box<dyn Attestor> {
123        Box::new(IONAttestor::new(&self.did))
124    }
125}
126
127impl IONController {
128    /// Checks whether there is a proof field in document metadata.
129    pub fn is_proof_in_doc_meta(&self, doc_meta: &DocumentMetadata) -> bool {
130        if let Some(property_set) = doc_meta.property_set.as_ref() {
131            property_set.contains_key("proof")
132        } else {
133            false
134        }
135    }
136
137    /// Returns a patch for adding a proof service.
138    pub fn add_proof_service(&self, did: &str, proof: &str) -> DIDStatePatch {
139        let mut obj: Map<String, Value> = Map::new();
140        obj.insert("controller".to_string(), Value::from(did));
141        obj.insert("proofValue".to_string(), Value::from(proof.to_owned()));
142
143        DIDStatePatch::AddServices {
144            services: vec![ServiceEndpointEntry {
145                id: TRUSTCHAIN_PROOF_SERVICE_ID_VALUE.to_string(),
146                r#type: TRUSTCHAIN_PROOF_SERVICE_TYPE_VALUE.to_string(),
147                service_endpoint: ServiceEndpoint::Map(serde_json::Value::Object(obj.clone())),
148            }],
149        }
150    }
151
152    /// Confirms whether a given key is the `commitment` in document metadata
153    pub fn is_commitment_key(
154        &self,
155        doc_meta: &DocumentMetadata,
156        key: &JWK,
157        key_type: KeyType,
158    ) -> bool {
159        if let Ok(expected_commitment) = self.key_to_commitment(key) {
160            if let Ok(actual_commitment) = self.extract_commitment(doc_meta, key_type) {
161                actual_commitment == expected_commitment
162            } else {
163                eprintln!("Unable to extract a commitment from document metadata.");
164                // Return false in this case as the key can't be a commitment
165                false
166            }
167        } else {
168            eprintln!("Unable to convert key to commitment.");
169            // Return false as no comparison possible
170            false
171        }
172    }
173
174    /// Extracts commitment of passed key type from document metadata.s
175    fn extract_commitment(
176        &self,
177        doc_meta: &DocumentMetadata,
178        key_type: KeyType,
179    ) -> Result<String, TrustchainIONError> {
180        if let Some(property_set) = doc_meta.property_set.as_ref() {
181            if let Some(Metadata::Map(method)) = property_set.get("method") {
182                let k = match key_type {
183                    KeyType::UpdateKey => "updateCommitment",
184                    KeyType::NextUpdateKey => "updateCommitment",
185                    KeyType::RecoveryKey => "recoveryCommitment",
186                    _ => return Err(TrustchainIONError::IncorrectKeyType),
187                };
188                if let Some(Metadata::String(s)) = method.get(k) {
189                    Ok(s.to_owned())
190                } else {
191                    Err(TrustchainIONError::FailedToExtractCommitment)
192                }
193            } else {
194                Err(TrustchainIONError::FailedToExtractCommitment)
195            }
196        } else {
197            Err(TrustchainIONError::FailedToExtractCommitment)
198        }
199    }
200
201    /// Converts a given JWK into a commitment.
202    fn key_to_commitment(&self, next_update_key: &JWK) -> Result<String, TrustchainIONError> {
203        match &PublicKeyJwk::try_from(next_update_key.to_public()) {
204            Ok(pk_jwk) => match ION::commitment_scheme(pk_jwk) {
205                Ok(commitment) => Ok(commitment),
206                Err(_) => Err(TrustchainIONError::FailedToConvertToCommitment),
207            },
208            Err(_) => Err(TrustchainIONError::FailedToConvertToCommitment),
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use trustchain_core::data::{
217        TEST_RECOVERY_KEY, TEST_SIDETREE_DOCUMENT_METADATA, TEST_TRUSTCHAIN_DOCUMENT_METADATA,
218        TEST_UPDATE_KEY,
219    };
220    use trustchain_core::utils::init;
221
222    // Make an IONController using this test function
223    fn test_controller(
224        did: &str,
225        controlled_did: &str,
226    ) -> Result<IONController, Box<dyn std::error::Error>> {
227        let update_key: JWK = serde_json::from_str(TEST_UPDATE_KEY)?;
228        let recovery_key: JWK = serde_json::from_str(TEST_RECOVERY_KEY)?;
229        IONController::try_from(ControllerData::new(
230            did.to_string(),
231            controlled_did.to_string(),
232            update_key,
233            recovery_key,
234        ))
235    }
236
237    #[test]
238    fn test_try_from() -> Result<(), Box<dyn std::error::Error>> {
239        init();
240        let update_key: JWK = serde_json::from_str(TEST_UPDATE_KEY)?;
241        let recovery_key: JWK = serde_json::from_str(TEST_RECOVERY_KEY)?;
242        let did = "did:example:did_try_from";
243        let controlled_did = "did:example:controlled_did_try_from";
244        let controlled_did_suffix = "controlled_did_try_from";
245
246        // Make controller using try_from()
247        let target = test_controller(did, controlled_did)?;
248
249        assert_eq!(target.controlled_did_suffix(), controlled_did_suffix);
250
251        let loaded_update_key = target.update_key()?;
252        assert_eq!(loaded_update_key, update_key);
253
254        let loaded_recovery_key = target.recovery_key()?;
255        assert_eq!(loaded_recovery_key, recovery_key);
256
257        Ok(())
258    }
259
260    #[test]
261    fn test_to_attestor() -> Result<(), Box<dyn std::error::Error>> {
262        init();
263        let did = "did:example:did_to_attestor";
264        let controlled_did = "did:example:controlled_did_to_attestor";
265        let did_suffix = "did_to_attestor";
266        let controlled_did_suffix = "controlled_did_to_attestor";
267        let target = test_controller(did, controlled_did)?;
268        assert_eq!(target.did(), did);
269        assert_ne!(target.did(), controlled_did);
270        assert_eq!(target.did_suffix(), did_suffix);
271        assert_ne!(target.did_suffix(), controlled_did_suffix);
272
273        let result = target.to_attestor();
274        assert_eq!(result.did(), did);
275        assert_ne!(result.did(), controlled_did);
276        assert_eq!(result.did_suffix(), did_suffix);
277        assert_ne!(result.did_suffix(), controlled_did_suffix);
278        Ok(())
279    }
280
281    #[test]
282    fn test_is_proof_in_doc_meta() -> Result<(), Box<dyn std::error::Error>> {
283        init();
284        let did = "did:example:did_is_proof_in_doc_meta";
285        let controlled_did = "did:example:controlled_is_proof_in_doc_meta";
286        let controller = test_controller(did, controlled_did)?;
287
288        let tc_doc_meta: DocumentMetadata =
289            serde_json::from_str(TEST_TRUSTCHAIN_DOCUMENT_METADATA)?;
290        assert!(controller.is_proof_in_doc_meta(&tc_doc_meta));
291
292        let sidetree_doc_meta: DocumentMetadata =
293            serde_json::from_str(TEST_SIDETREE_DOCUMENT_METADATA)?;
294        assert!(!controller.is_proof_in_doc_meta(&sidetree_doc_meta));
295
296        Ok(())
297    }
298
299    #[test]
300    fn test_extract_commitment() -> Result<(), Box<dyn std::error::Error>> {
301        init();
302        let did = "did:example:did_extract_commitment";
303        let controlled_did = "did:example:controlled_extract_commitment";
304        let controller = test_controller(did, controlled_did)?;
305        let expected_recovery_commitment = "EiDZpHjQ5x7aRRqv6aUtmOdHsxWktAm1kU1IZl1w7iexsw";
306        let expected_update_commitment = "EiBWPR1JNdAQ4j3ZMqurb4rt10NA7s17lztFF9OIcEO3ew";
307        let doc_meta: DocumentMetadata = serde_json::from_str(TEST_TRUSTCHAIN_DOCUMENT_METADATA)?;
308
309        let update_commiment = controller.extract_commitment(&doc_meta, KeyType::UpdateKey)?;
310        assert_eq!(expected_update_commitment, update_commiment.as_str());
311
312        let next_update_commiment =
313            controller.extract_commitment(&doc_meta, KeyType::NextUpdateKey)?;
314        assert_eq!(expected_update_commitment, next_update_commiment.as_str());
315
316        let recovery_commiment = controller.extract_commitment(&doc_meta, KeyType::RecoveryKey)?;
317        assert_eq!(expected_recovery_commitment, recovery_commiment.as_str());
318        Ok(())
319    }
320
321    #[test]
322    fn test_key_to_commitment() -> Result<(), Box<dyn std::error::Error>> {
323        init();
324        let did = "did:example:did_key_to_commitment";
325        let controlled_did = "did:example:controlled_key_to_commitment";
326        let update_key: JWK = serde_json::from_str(TEST_UPDATE_KEY)?;
327        let recovery_key: JWK = serde_json::from_str(TEST_RECOVERY_KEY)?;
328
329        let controller = test_controller(did, controlled_did)?;
330
331        let expected_recovery_commitment = "EiDZpHjQ5x7aRRqv6aUtmOdHsxWktAm1kU1IZl1w7iexsw";
332        let expected_update_commitment = "EiBWPR1JNdAQ4j3ZMqurb4rt10NA7s17lztFF9OIcEO3ew";
333
334        let update_commitment = controller.key_to_commitment(&update_key)?;
335        let recovery_commitment = controller.key_to_commitment(&recovery_key)?;
336
337        assert_eq!(expected_update_commitment, update_commitment);
338        assert_eq!(expected_recovery_commitment, recovery_commitment);
339
340        Ok(())
341    }
342
343    #[test]
344    fn test_is_commitment_key() -> Result<(), Box<dyn std::error::Error>> {
345        init();
346        let did = "did:example:did_is_commitment_key";
347        let controlled_did = "did:example:controlled_is_commitment_key";
348        let update_key: JWK = serde_json::from_str(TEST_UPDATE_KEY)?;
349        let recovery_key: JWK = serde_json::from_str(TEST_RECOVERY_KEY)?;
350        let controller = test_controller(did, controlled_did)?;
351        let doc_meta: DocumentMetadata = serde_json::from_str(TEST_TRUSTCHAIN_DOCUMENT_METADATA)?;
352
353        assert!(controller.is_commitment_key(&doc_meta, &update_key, KeyType::UpdateKey));
354        assert!(controller.is_commitment_key(&doc_meta, &recovery_key, KeyType::RecoveryKey));
355        Ok(())
356    }
357
358    #[test]
359    fn test_add_proof_service() -> Result<(), Box<dyn std::error::Error>> {
360        init();
361        let did = "did:example:did_add_proof_service";
362        let controlled_did = "did:example:controlled_add_proof_service";
363        let controller = test_controller(did, controlled_did)?;
364        let proof = "test_proof_information";
365        let _ = controller.add_proof_service(controlled_did, proof);
366        Ok(())
367    }
368}