did_ion/sidetree/operation/
update.rs

1use serde::{Deserialize, Serialize};
2
3use crate::sidetree::{json_canonicalization_scheme, DIDSuffix, Delta, PublicKeyJwk, Sidetree};
4
5use super::{jws_decode_verify_inner, PartialVerificationError, SidetreeOperation};
6
7/// Sidetree DID Update operation
8///
9/// ### References
10/// - [Sidetree §11.2 Update](https://identity.foundation/sidetree/spec/v1.0.0/#update)
11/// - [Sidetree REST API §1.2.2 Update](https://identity.foundation/sidetree/api/#update)
12#[derive(Debug, Serialize, Deserialize, Clone)]
13#[serde(rename_all = "camelCase")]
14#[serde(deny_unknown_fields)]
15pub struct UpdateOperation {
16    pub did_suffix: DIDSuffix,
17    /// Output of [Sidetree::reveal_value]
18    pub reveal_value: String,
19    pub delta: Delta,
20    /// Compact JWS (RFC 7515) of [UpdateClaims]
21    ///
22    /// <https://identity.foundation/sidetree/spec/v1.0.0/#update-signed-data-object>
23    pub signed_data: String,
24}
25
26/// Partially verified DID Create operation
27///
28/// Converted from [UpdateOperation].
29#[derive(Debug, Clone)]
30pub struct PartiallyVerifiedUpdateOperation {
31    pub reveal_value: String,
32    pub signed_delta: Delta,
33    pub signed_update_key: PublicKeyJwk,
34}
35
36impl SidetreeOperation for UpdateOperation {
37    type PartiallyVerifiedForm = PartiallyVerifiedUpdateOperation;
38
39    /// Partially verify an [UpdateOperation]
40    ///
41    /// Specifically, the following is done:
42    /// - The operation's [signed data](UpdateOperation::signed_data) is verified against the
43    ///   revealed [public key](UpdateClaims::update_key) that it must contain;
44    /// - the revealed public key is verified against the operation's
45    ///   [reveal value](UpdateOperation::reveal_value); and
46    /// - the operation's [delta object](UpdateOperation::delta) is verified against the
47    ///   [delta hash](UpdateClaims::update_key) in the signed data payload.
48    ///
49    /// The [DID Suffix](UpdateOperation::did_suffix) is **not** verified
50    /// by this function. The correspondence of the reveal value's hash to the previous update
51    /// commitment is not checked either, since that is not known from this function.
52    fn partial_verify<S: Sidetree>(
53        self,
54    ) -> Result<PartiallyVerifiedUpdateOperation, PartialVerificationError> {
55        // Verify JWS against public key in payload.
56        // Then check public key against its hash (reveal value).
57        let (header, claims) =
58            jws_decode_verify_inner(&self.signed_data, |claims: &UpdateClaims| {
59                &claims.update_key
60            })?;
61
62        if header.algorithm != S::SIGNATURE_ALGORITHM {
63            return Err(PartialVerificationError::InvalidSignatureAlgorithm);
64        }
65
66        let canonicalized_public_key = json_canonicalization_scheme(&claims.update_key).unwrap();
67        let computed_reveal_value = S::reveal_value(canonicalized_public_key.as_bytes());
68        if self.reveal_value != computed_reveal_value {
69            return Err(PartialVerificationError::RevealValueMismatch {
70                computed: computed_reveal_value,
71                found: self.reveal_value,
72            });
73        }
74        let delta_string = json_canonicalization_scheme(&self.delta).unwrap();
75        let delta_hash = S::hash(delta_string.as_bytes());
76        if claims.delta_hash != delta_hash {
77            return Err(PartialVerificationError::DeltaHashMismatch);
78        }
79        // Note: did_suffix is dropped, since it's not signed over.
80        Ok(PartiallyVerifiedUpdateOperation {
81            reveal_value: self.reveal_value,
82            signed_delta: self.delta,
83            signed_update_key: claims.update_key,
84        })
85    }
86}
87
88/// Payload object for JWS in [UpdateOperation]
89#[derive(Debug, Serialize, Deserialize, Clone)]
90#[serde(rename_all = "camelCase")]
91pub struct UpdateClaims {
92    /// Key matching previous Update Commitment
93    pub update_key: PublicKeyJwk,
94
95    /// [Hash](Sidetree::hash) of canonicalized [Update Operation Delta Object](Delta).
96    pub delta_hash: String,
97}