ssi_verification_methods/methods/unspecified/tezos/
tezos_method_2021.rs

1use iref::{Iri, IriBuf, UriBuf};
2use serde::{Deserialize, Serialize};
3use ssi_claims_core::{InvalidProof, MessageSignatureError, ProofValidationError, ProofValidity};
4use ssi_crypto::algorithm::AnyBlake2b;
5use ssi_jwk::JWK;
6use ssi_verification_methods_core::VerificationMethodSet;
7use static_iref::iri;
8use std::{collections::BTreeMap, hash::Hash};
9
10use crate::{
11    ExpectedType, GenericVerificationMethod, InvalidVerificationMethod, SigningMethod,
12    TypedVerificationMethod, VerificationMethod,
13};
14
15pub const TEZOS_METHOD_2021_IRI: &Iri = iri!("https://w3id.org/security#TezosMethod2021");
16
17pub const TEZOS_METHOD_2021_TYPE: &str = "TezosMethod2021";
18
19/// `TezosMethod2021` Verification Method.
20///
21/// # Signature algorithm
22///
23/// The signature algorithm must be either:
24/// - EdBlake2b,
25/// - ESBlake2bK,
26/// - ESBlake2b
27///
28/// # Key format
29///
30/// The public key is either stored using the `publicKeyJwk` or
31/// `blockchainAccountId` properties. Because `blockchainAccountId` is just a
32/// hash of the key, the public key must be embedded in the proof and passed to
33/// the verification method (as its context).
34///
35/// In the proof, the public must be stored using the `publicKeyJwk` or
36/// `publicKeyMultibase` properties. Here `publicKeyMultibase` is used in a
37/// non-standard way, where the public key is encoded in base58 (`z` prefix) as
38/// a tezos key (so without multicodec, contrarily to the specification).
39#[derive(
40    Debug,
41    Clone,
42    PartialEq,
43    Eq,
44    Hash,
45    Serialize,
46    Deserialize,
47    linked_data::Serialize,
48    linked_data::Deserialize,
49)]
50#[serde(tag = "type", rename = "TezosMethod2021")]
51#[ld(prefix("sec" = "https://w3id.org/security#"))]
52pub struct TezosMethod2021 {
53    /// Key identifier.
54    #[ld(id)]
55    pub id: IriBuf,
56
57    /// Controller of the verification method.
58    #[ld("sec:controller")]
59    pub controller: UriBuf,
60
61    #[serde(flatten)]
62    #[ld(flatten)]
63    pub public_key: PublicKey,
64}
65
66impl TezosMethod2021 {
67    pub const NAME: &'static str = TEZOS_METHOD_2021_TYPE;
68    pub const IRI: &'static Iri = TEZOS_METHOD_2021_IRI;
69
70    pub fn public_key_jwk(&self) -> Option<&JWK> {
71        self.public_key.as_jwk()
72    }
73
74    pub fn verify_bytes(
75        &self,
76        public_key_jwk: Option<&JWK>,
77        message: &[u8],
78        algorithm: AnyBlake2b,
79        signature: &[u8],
80    ) -> Result<ProofValidity, ProofValidationError> {
81        self.public_key
82            .verify_bytes(public_key_jwk, message, algorithm, signature)
83    }
84}
85
86#[derive(
87    Debug,
88    Clone,
89    PartialEq,
90    Eq,
91    Hash,
92    Serialize,
93    Deserialize,
94    linked_data::Serialize,
95    linked_data::Deserialize,
96)]
97#[ld(prefix("sec" = "https://w3id.org/security#"))]
98pub enum PublicKey {
99    #[serde(rename = "publicKeyJwk")]
100    #[ld("sec:publicKeyJwk")]
101    Jwk(Box<JWK>),
102
103    #[serde(rename = "blockchainAccountId")]
104    #[ld("sec:blockchainAccountId")]
105    BlockchainAccountId(ssi_caips::caip10::BlockchainAccountId),
106}
107
108impl PublicKey {
109    pub fn as_jwk(&self) -> Option<&JWK> {
110        match self {
111            Self::Jwk(jwk) => Some(jwk),
112            Self::BlockchainAccountId(_) => None,
113        }
114    }
115
116    pub fn matches(&self, other: &JWK) -> Result<bool, ProofValidationError> {
117        use ssi_caips::caip10::BlockchainAccountIdVerifyError as VerifyError;
118        match self {
119            Self::Jwk(jwk) => Ok(jwk.equals_public(other)),
120            Self::BlockchainAccountId(id) => match id.verify(other) {
121                Err(VerifyError::UnknownChainId(_) | VerifyError::HashError(_)) => {
122                    Err(ProofValidationError::InvalidKey)
123                }
124                Err(VerifyError::KeyMismatch(_, _)) => Ok(false),
125                Ok(()) => Ok(true),
126            },
127        }
128    }
129
130    fn from_generic(
131        properties: &BTreeMap<String, serde_json::Value>,
132    ) -> Result<Self, InvalidVerificationMethod> {
133        match properties.get("publicKeyJwk") {
134            Some(value) => Ok(Self::Jwk(Box::new(
135                serde_json::from_value(value.clone())
136                    .map_err(|_| InvalidVerificationMethod::invalid_property("publicKeyJwk"))?,
137            ))),
138            None => match properties.get("blockchainAccountId") {
139                Some(serde_json::Value::String(value)) => {
140                    Ok(Self::BlockchainAccountId(value.parse().map_err(|_| {
141                        InvalidVerificationMethod::invalid_property("blockchainAccountId")
142                    })?))
143                }
144                Some(_) => Err(InvalidVerificationMethod::invalid_property(
145                    "blockchainAccountId",
146                )),
147                None => Err(InvalidVerificationMethod::missing_property("publicKeyJwk")),
148            },
149        }
150    }
151
152    pub fn verify_bytes(
153        &self,
154        public_key_jwk: Option<&JWK>,
155        message: &[u8],
156        algorithm: AnyBlake2b,
157        signature: &[u8],
158    ) -> Result<ProofValidity, ProofValidationError> {
159        match self {
160            Self::BlockchainAccountId(account_id) => match public_key_jwk {
161                Some(jwk) => match account_id.verify(jwk) {
162                    Ok(()) => Ok(
163                        ssi_jws::verify_bytes(algorithm.into(), message, jwk, signature)
164                            .map_err(|_| InvalidProof::Signature),
165                    ),
166                    Err(_) => Ok(Err(InvalidProof::KeyMismatch)),
167                },
168                None => Err(ProofValidationError::MissingPublicKey),
169            },
170            Self::Jwk(jwk) => Ok(
171                ssi_jws::verify_bytes(algorithm.into(), message, jwk, signature)
172                    .map_err(|_| InvalidProof::Signature),
173            ),
174        }
175    }
176
177    pub fn sign_bytes(
178        &self,
179        key: &JWK,
180        algorithm: ssi_crypto::algorithm::AnyBlake2b,
181        bytes: &[u8],
182    ) -> Result<Vec<u8>, MessageSignatureError> {
183        ssi_jws::sign_bytes(algorithm.into(), bytes, key)
184            .map_err(MessageSignatureError::signature_failed)
185    }
186}
187
188impl VerificationMethod for TezosMethod2021 {
189    fn id(&self) -> &Iri {
190        self.id.as_iri()
191    }
192
193    fn controller(&self) -> Option<&Iri> {
194        Some(self.controller.as_iri())
195    }
196}
197
198impl VerificationMethodSet for TezosMethod2021 {
199    type TypeSet = &'static str;
200
201    fn type_set() -> Self::TypeSet {
202        Self::NAME
203    }
204}
205
206impl TypedVerificationMethod for TezosMethod2021 {
207    fn expected_type() -> Option<ExpectedType> {
208        Some(TEZOS_METHOD_2021_TYPE.to_string().into())
209    }
210
211    fn type_match(ty: &str) -> bool {
212        ty == TEZOS_METHOD_2021_TYPE
213    }
214
215    fn type_(&self) -> &str {
216        TEZOS_METHOD_2021_TYPE
217    }
218}
219
220impl TryFrom<GenericVerificationMethod> for TezosMethod2021 {
221    type Error = InvalidVerificationMethod;
222
223    fn try_from(value: GenericVerificationMethod) -> Result<Self, Self::Error> {
224        if value.type_ == TEZOS_METHOD_2021_TYPE {
225            Ok(Self {
226                id: value.id,
227                controller: value.controller,
228                public_key: PublicKey::from_generic(&value.properties)?,
229            })
230        } else {
231            Err(InvalidVerificationMethod::invalid_type_name(
232                &value.type_,
233                TEZOS_METHOD_2021_TYPE,
234            ))
235        }
236    }
237}
238
239impl SigningMethod<JWK, ssi_crypto::algorithm::AnyBlake2b> for TezosMethod2021 {
240    fn sign_bytes(
241        &self,
242        key: &JWK,
243        algorithm: ssi_crypto::algorithm::AnyBlake2b,
244        bytes: &[u8],
245    ) -> Result<Vec<u8>, MessageSignatureError> {
246        ssi_jws::sign_bytes(algorithm.into(), bytes, key)
247            .map_err(MessageSignatureError::signature_failed)
248    }
249}