Verify a certificate against the delegator's public key and return the typed view. Checks, in order: 1. the document is an `AgentDelegation`; 2. content hash recomputes, the proof verifies, and its `verificationMethod` belongs to `delegator_public_key` ([`proof::verify_value`]); 3. the `delegator` DID is that same key — otherwise a certificate signed by one key could name another as the delegato
(document: &Value, delegator_public_key: &PublicKey)
| 144 | /// 5. `@id` recomputes from the body, so the identifier cannot be swapped for |
| 145 | /// one belonging to a different (perhaps revoked) certificate. |
| 146 | pub fn verify(document: &Value, delegator_public_key: &PublicKey) -> Result<Delegation> { |
| 147 | expect_type(document, TYPE_DELEGATION)?; |
| 148 | proof::verify_value(document, delegator_public_key)?; |
| 149 | |
| 150 | let parsed = parse(document)?; |
| 151 | |
| 152 | // 3. The signer must be the delegator the document names. |
| 153 | let delegator_id = IdentityId::from_did(&parsed.delegator) |
| 154 | .map_err(|e| CanonicalError::Verification(format!("delegator DID is malformed: {e}")))?; |
| 155 | if !delegator_id.matches_public_key(delegator_public_key) { |
| 156 | return Err(CanonicalError::Verification( |
| 157 | "delegator DID does not match the verifying key".into(), |
| 158 | )); |
| 159 | } |
| 160 | |
| 161 | // 3b. The delegator's own two renderings must agree too, so a |
| 162 | // self-contained verifier that starts from `delegatorKey` reaches the |
| 163 | // same conclusion as one that starts from a key it already trusts. |
| 164 | let stated_delegator_key = self::delegator_public_key(&parsed)?; |
| 165 | if &stated_delegator_key != delegator_public_key { |
| 166 | return Err(CanonicalError::Verification( |
| 167 | "delegatorKey does not match the verifying key".into(), |
| 168 | )); |
| 169 | } |
| 170 | |
| 171 | // 4. The two renderings of the delegate's key must agree. Without this a |
| 172 | // certificate could name agent A in `delegate` while handing out agent |
| 173 | // B's key in `delegateKey`. |
| 174 | let delegate_key = delegate_public_key(&parsed)?; |
| 175 | let delegate_id = IdentityId::from_did(&parsed.delegate) |
| 176 | .map_err(|e| CanonicalError::Verification(format!("delegate DID is malformed: {e}")))?; |
| 177 | if !delegate_id.matches_public_key(&delegate_key) { |
| 178 | return Err(CanonicalError::Verification( |
| 179 | "delegateKey does not match the delegate DID".into(), |
| 180 | )); |
| 181 | } |
| 182 | |
| 183 | // 5. The id is a claim like any other; recompute it. |
| 184 | if !parsed.id_matches(&delegator_id, &delegate_id) { |
| 185 | return Err(CanonicalError::Verification( |
| 186 | "delegation @id does not match its delegator, delegate and issue time".into(), |
| 187 | )); |
| 188 | } |
| 189 | |
| 190 | Ok(parsed) |
| 191 | } |
| 192 | |
| 193 | /// Parse a certificate into its typed view **without** verifying the proof. |
| 194 | /// |