1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::hash::Hash;

use iref::{Iri, IriBuf, UriBuf};
use serde::{Deserialize, Serialize};
use ssi_claims_core::{MessageSignatureError, ProofValidationError};
use ssi_jwk::JWK;
use ssi_verification_methods_core::VerificationMethodSet;
use static_iref::iri;

use crate::{
    ExpectedType, GenericVerificationMethod, InvalidVerificationMethod, TypedVerificationMethod,
    VerificationMethod,
};

// pub const BLOCKCHAIN_VERIFICATION_METHOD_2021_IRI: &Iri =
//     iri!("https://w3id.org/security#BlockchainVerificationMethod2021");

pub const BLOCKCHAIN_VERIFICATION_METHOD_2021_TYPE: &str = "BlockchainVerificationMethod2021";

/// BlockchainVerificationMethod2021.
///
/// See: <https://w3id.org/security#BlockchainVerificationMethod2021>.
#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    Hash,
    Serialize,
    Deserialize,
    linked_data::Serialize,
    linked_data::Deserialize,
)]
#[serde(tag = "type", rename = "BlockchainVerificationMethod2021")]
#[ld(prefix("sec" = "https://w3id.org/security#"))]
#[ld(type = "sec:BlockchainVerificationMethod2021")]
pub struct BlockchainVerificationMethod2021 {
    /// Key identifier.
    #[ld(id)]
    pub id: IriBuf,

    /// Controller of the verification method.
    #[ld("sec:controller")]
    pub controller: UriBuf,

    /// Blockchain accound ID.
    #[serde(rename = "blockchainAccountId")]
    #[ld("sec:blockchainAccountId")]
    pub blockchain_account_id: ssi_caips::caip10::BlockchainAccountId,
}

impl BlockchainVerificationMethod2021 {
    pub const NAME: &'static str = BLOCKCHAIN_VERIFICATION_METHOD_2021_TYPE;
    pub const IRI: &'static Iri =
        iri!("https://w3id.org/security#BlockchainVerificationMethod2021");

    pub fn verify_bytes(
        &self,
        public_key_jwk: Option<&JWK>,
        message: &[u8],
        algorithm: ssi_jwk::Algorithm,
        signature: &[u8],
    ) -> Result<bool, ProofValidationError> {
        match public_key_jwk {
            Some(jwk) => match self.blockchain_account_id.verify(jwk) {
                Ok(()) => Ok(ssi_jws::verify_bytes(algorithm, message, jwk, signature).is_ok()),
                Err(_) => Ok(false),
            },
            None => Err(ProofValidationError::MissingPublicKey),
        }
    }

    pub fn sign_bytes(
        &self,
        key: &JWK,
        algorithm: ssi_jwk::Algorithm,
        bytes: &[u8],
    ) -> Result<Vec<u8>, MessageSignatureError> {
        ssi_jws::sign_bytes(algorithm, bytes, key).map_err(MessageSignatureError::signature_failed)
    }
}

impl VerificationMethod for BlockchainVerificationMethod2021 {
    fn id(&self) -> &Iri {
        self.id.as_iri()
    }

    fn controller(&self) -> Option<&Iri> {
        Some(self.controller.as_iri())
    }
}

impl VerificationMethodSet for BlockchainVerificationMethod2021 {
    type TypeSet = &'static str;

    fn type_set() -> Self::TypeSet {
        Self::NAME
    }
}

impl TypedVerificationMethod for BlockchainVerificationMethod2021 {
    fn expected_type() -> Option<ExpectedType> {
        Some(BLOCKCHAIN_VERIFICATION_METHOD_2021_TYPE.to_string().into())
    }

    fn type_match(ty: &str) -> bool {
        ty == BLOCKCHAIN_VERIFICATION_METHOD_2021_TYPE
    }

    fn type_(&self) -> &str {
        BLOCKCHAIN_VERIFICATION_METHOD_2021_TYPE
    }
}

impl TryFrom<GenericVerificationMethod> for BlockchainVerificationMethod2021 {
    type Error = InvalidVerificationMethod;

    fn try_from(m: GenericVerificationMethod) -> Result<Self, Self::Error> {
        Ok(Self {
            id: m.id,
            controller: m.controller,
            blockchain_account_id: m
                .properties
                .get("blockchainAccountId")
                .ok_or_else(|| InvalidVerificationMethod::missing_property("blockchainAccountId"))?
                .as_str()
                .ok_or_else(|| InvalidVerificationMethod::invalid_property("blockchainAccountId"))?
                .parse()
                .map_err(|_| InvalidVerificationMethod::invalid_property("blockchainAccountId"))?,
        })
    }
}