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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#[derive(Debug, thiserror::Error)]
pub enum ProofPreparationError {
    #[error("claims processing failed: {0}")]
    Claims(String),

    #[error("proof processing failed: {0}")]
    Proof(String),

    #[error("{0}")]
    Other(String),
}

#[derive(Debug, thiserror::Error)]
pub enum ProofValidationError {
    /// Input data could not be understood.
    #[error("invalid input data: {0}")]
    InvalidInputData(String),

    #[error(transparent)]
    Preparation(#[from] ProofPreparationError),

    /// Proof could not be understood.
    #[error("invalid proof")]
    InvalidProof,

    #[error("invalid proof options")]
    InvalidProofOptions,

    /// Key not found.
    #[error("unknown key")]
    UnknownKey,

    /// Invalid key.
    #[error("invalid key")]
    InvalidKey,

    /// Missing public key.
    #[error("missing public key")]
    MissingPublicKey,

    /// More than one public key is provided.
    #[error("ambiguous public key")]
    AmbiguousPublicKey,

    /// Unsupported controller scheme.
    #[error("unsupported key controller `{0}`")]
    UnsupportedKeyController(String),

    /// Key controller was not found.
    #[error("key controller `{0}` not found")]
    KeyControllerNotFound(String),

    /// Key controller is invalid.
    #[error("invalid key controller")]
    InvalidKeyController,

    /// Cryptographic key is not used correctly.
    #[error("invalid use of key")]
    InvalidKeyUse,

    #[error("missing signature algorithm")]
    MissingAlgorithm,

    #[error("missing signature")]
    MissingSignature,

    #[error("invalid signature")]
    InvalidSignature,

    #[error("invalid verification method: {0}")]
    InvalidVerificationMethod(String),

    #[error("{0}")]
    Other(String),
}

impl ProofValidationError {
    pub fn other(e: impl ToString) -> Self {
        Self::Other(e.to_string())
    }
}

#[derive(Debug, thiserror::Error, PartialEq)]
pub enum InvalidProof {
    /// Proof is missing.
    #[error("missing proof")]
    Missing,

    #[error("invalid signature")]
    Signature,

    #[error("key mismatch")]
    KeyMismatch,

    #[error("algorithm mismatch")]
    AlgorithmMismatch,

    #[error("{0}")]
    Other(String),
}

impl From<std::convert::Infallible> for ProofValidationError {
    fn from(_value: std::convert::Infallible) -> Self {
        unreachable!()
    }
}

pub type ProofValidity = Result<(), InvalidProof>;

/// Proof that can be validated against `T` claims with a verifier of type `V`.
pub trait ValidateProof<V, T> {
    /// Validates the input claim's proof using the given verifier.
    ///
    /// The returned value is a nested `Result`.
    /// The outer `Result` describes whether or not the proof could be verified.
    /// A proof may be valid even if the outer value is `Err`.
    /// The inner `Result` describes the validity of the proof itself.
    /// A proof is surely valid if the inner value is `Ok`.
    #[allow(async_fn_in_trait)]
    async fn validate_proof<'a>(
        &'a self,
        verifier: &'a V,
        claims: &'a T,
    ) -> Result<ProofValidity, ProofValidationError>;
}

impl<V, T, P: ValidateProof<V, T>> ValidateProof<V, T> for Vec<P> {
    async fn validate_proof<'a>(
        &'a self,
        verifier: &'a V,
        claims: &'a T,
    ) -> Result<ProofValidity, ProofValidationError> {
        if self.is_empty() {
            // No proof.
            Ok(Err(InvalidProof::Missing))
        } else {
            for p in self {
                if let Err(e) = p.validate_proof(verifier, claims).await? {
                    return Ok(Err(e));
                }
            }

            Ok(Ok(()))
        }
    }
}