ssi_verification_methods/methods/unspecified/
eip712_method_2021.rs1use std::hash::Hash;
2
3use iref::{Iri, IriBuf, UriBuf};
4use serde::{Deserialize, Serialize};
5use ssi_caips::caip10::BlockchainAccountIdVerifyError;
6use ssi_claims_core::{InvalidProof, MessageSignatureError, ProofValidationError, ProofValidity};
7use ssi_jwk::JWK;
8use ssi_verification_methods_core::VerificationMethodSet;
9use static_iref::iri;
10
11use crate::{
12 ExpectedType, GenericVerificationMethod, InvalidVerificationMethod, SigningMethod,
13 TypedVerificationMethod, VerificationMethod,
14};
15
16pub const EIP712_METHOD_2021_TYPE: &str = "Eip712Method2021";
22
23#[derive(
25 Debug,
26 Clone,
27 PartialEq,
28 Eq,
29 Hash,
30 Serialize,
31 Deserialize,
32 linked_data::Serialize,
33 linked_data::Deserialize,
34)]
35#[serde(tag = "type", rename = "Eip712Method2021")]
36#[ld(prefix("sec" = "https://w3id.org/security#"))]
37#[ld(type = "sec:Eip712Method2021")]
38pub struct Eip712Method2021 {
39 #[ld(id)]
41 pub id: IriBuf,
42
43 #[ld("sec:controller")]
45 pub controller: UriBuf,
46
47 #[serde(rename = "blockchainAccountId")]
49 #[ld("sec:blockchainAccountId")]
50 pub blockchain_account_id: ssi_caips::caip10::BlockchainAccountId,
51}
52
53impl Eip712Method2021 {
54 pub const NAME: &'static str = EIP712_METHOD_2021_TYPE;
55 pub const IRI: &'static Iri = iri!("https://w3id.org/security#Eip712Method2021");
56
57 pub fn sign_bytes(
58 &self,
59 secret_key: &JWK,
60 data: &[u8],
61 ) -> Result<Vec<u8>, MessageSignatureError> {
62 use sha3::Digest;
63 use ssi_jwk::Params;
64 let ec_params = match &secret_key.params {
65 Params::EC(ec) => ec,
66 _ => return Err(MessageSignatureError::InvalidSecretKey),
67 };
68
69 let secret_key = k256::SecretKey::try_from(ec_params)
70 .map_err(|_| MessageSignatureError::InvalidSecretKey)?;
71 let signing_key = k256::ecdsa::SigningKey::from(secret_key);
72 let (sig, rec_id) = signing_key
73 .sign_digest_recoverable(sha3::Keccak256::new_with_prefix(data))
74 .map_err(MessageSignatureError::signature_failed)?;
75
76 let mut result = sig.to_bytes().to_vec();
81 result.push(rec_id.to_byte());
82
83 Ok(result)
84 }
85
86 pub fn verify_bytes(
87 &self,
88 data: &[u8],
89 signature_bytes: &[u8],
90 ) -> Result<ProofValidity, ProofValidationError> {
91 use sha3::Digest;
92 if signature_bytes.len() != 65 {
93 return Err(ProofValidationError::InvalidSignature);
94 }
95
96 let signature = k256::ecdsa::Signature::try_from(&signature_bytes[..64])
98 .map_err(|_| ProofValidationError::InvalidSignature)?;
99
100 let rec_id = k256::ecdsa::RecoveryId::try_from(signature_bytes[64])
102 .map_err(|_| ProofValidationError::InvalidSignature)?;
103
104 let recovered_key: k256::ecdsa::VerifyingKey =
105 k256::ecdsa::VerifyingKey::recover_from_digest(
106 sha3::Keccak256::new_with_prefix(data),
107 &signature,
108 rec_id,
109 )
110 .map_err(|_| ProofValidationError::InvalidSignature)?;
111
112 let jwk = JWK {
114 params: ssi_jwk::Params::EC(ssi_jwk::ECParams::from(
115 &k256::PublicKey::from_sec1_bytes(&recovered_key.to_sec1_bytes()).unwrap(),
116 )),
117 public_key_use: None,
118 key_operations: None,
119 algorithm: None,
120 key_id: None,
121 x509_url: None,
122 x509_certificate_chain: None,
123 x509_thumbprint_sha1: None,
124 x509_thumbprint_sha256: None,
125 };
126
127 match self.blockchain_account_id.verify(&jwk) {
128 Ok(()) => Ok(Ok(())),
129 Err(BlockchainAccountIdVerifyError::KeyMismatch(_, _)) => {
130 Ok(Err(InvalidProof::KeyMismatch))
131 }
132 Err(_) => Err(ProofValidationError::InvalidKey),
133 }
134 }
135}
136
137impl VerificationMethod for Eip712Method2021 {
138 fn id(&self) -> &Iri {
139 self.id.as_iri()
140 }
141
142 fn controller(&self) -> Option<&Iri> {
143 Some(self.controller.as_iri())
144 }
145}
146
147impl VerificationMethodSet for Eip712Method2021 {
148 type TypeSet = &'static str;
149
150 fn type_set() -> Self::TypeSet {
151 Self::NAME
152 }
153}
154
155impl TypedVerificationMethod for Eip712Method2021 {
156 fn expected_type() -> Option<ExpectedType> {
157 Some(EIP712_METHOD_2021_TYPE.to_string().into())
158 }
159
160 fn type_match(ty: &str) -> bool {
161 ty == EIP712_METHOD_2021_TYPE
162 }
163
164 fn type_(&self) -> &str {
165 EIP712_METHOD_2021_TYPE
166 }
167}
168
169impl TryFrom<GenericVerificationMethod> for Eip712Method2021 {
170 type Error = InvalidVerificationMethod;
171
172 fn try_from(m: GenericVerificationMethod) -> Result<Self, Self::Error> {
173 Ok(Self {
174 id: m.id,
175 controller: m.controller,
176 blockchain_account_id: m
177 .properties
178 .get("blockchainAccountId")
179 .ok_or_else(|| InvalidVerificationMethod::missing_property("blockchainAccountId"))?
180 .as_str()
181 .ok_or_else(|| InvalidVerificationMethod::invalid_property("blockchainAccountId"))?
182 .parse()
183 .map_err(|_| InvalidVerificationMethod::invalid_property("blockchainAccountId"))?,
184 })
185 }
186}
187
188impl SigningMethod<JWK, ssi_crypto::algorithm::ESKeccakKR> for Eip712Method2021 {
189 fn sign_bytes(
190 &self,
191 key: &JWK,
192 _algorithm: ssi_crypto::algorithm::ESKeccakKR,
193 bytes: &[u8],
194 ) -> Result<Vec<u8>, MessageSignatureError> {
195 self.sign_bytes(key, bytes)
196 }
197}