huskarl_core/crypto/verifier/
multi.rs1use std::sync::Arc;
2
3use futures_util::future::join_all;
4
5use crate::{
6 crypto::{
7 KeyMatchStrength,
8 verifier::{CreateVerifierError, JwsVerifier, JwsVerifierPlatform, KeyMatch, VerifyError},
9 },
10 error::{Error, ErrorKind},
11 jwk::PublicJwks,
12 platform::MaybeSendBoxFuture,
13};
14
15#[derive(Debug)]
30pub struct MultiKeyVerifier {
31 verifiers: Vec<Arc<dyn JwsVerifier>>,
32 try_all_on_ambiguous_match: bool,
33}
34
35enum GetVerifierResult {
36 ByKeyId(Arc<dyn JwsVerifier>),
37 ByAlgorithm(Vec<Arc<dyn JwsVerifier>>),
38 None,
39}
40
41impl GetVerifierResult {
42 fn key_match_strength(&self) -> Option<KeyMatchStrength> {
43 match self {
44 Self::ByKeyId(_) => Some(KeyMatchStrength::ByKeyId),
45 Self::ByAlgorithm(_) => Some(KeyMatchStrength::ByAlgorithm),
46 Self::None => None,
47 }
48 }
49}
50
51impl MultiKeyVerifier {
52 #[must_use]
54 pub fn new(verifiers: Vec<Arc<dyn JwsVerifier>>) -> Self {
55 Self {
56 verifiers,
57 try_all_on_ambiguous_match: false,
58 }
59 }
60
61 pub async fn from_jwks(
75 jwks: &PublicJwks,
76 platform: &dyn JwsVerifierPlatform,
77 ) -> Result<Self, Error> {
78 let verifiers: Vec<Arc<dyn JwsVerifier>> = join_all(
79 jwks.keys
80 .iter()
81 .map(|jwk| platform.create_verifier_from_jwk(jwk.clone())),
82 )
83 .await
84 .into_iter()
85 .filter_map(|result| match result {
86 Ok(v) => Some(Ok(v)),
87 Err(CreateVerifierError::UnsupportedKey) => None,
88 Err(e) => Some(Err(e)),
89 })
90 .collect::<Result<_, _>>()
91 .map_err(|source| {
92 Error::new(ErrorKind::Crypto, source).with_context("creating verifier from JWK")
93 })?;
94
95 Ok(Self {
96 verifiers,
97 try_all_on_ambiguous_match: false,
98 })
99 }
100
101 #[must_use]
108 pub fn try_all_on_ambiguous_match(mut self, value: bool) -> Self {
109 self.try_all_on_ambiguous_match = value;
110 self
111 }
112
113 async fn dispatch_verify(
114 &self,
115 input: &[u8],
116 signature: &[u8],
117 key_match: &KeyMatch<'_>,
118 ) -> Result<(), VerifyError> {
119 let by_algorithm_verifiers = match self.get_verifier(key_match) {
120 GetVerifierResult::ByKeyId(verifier) => {
121 return verifier.verify(input, signature, key_match).await;
122 }
123 GetVerifierResult::ByAlgorithm(verifiers) => verifiers,
124 GetVerifierResult::None => return Err(VerifyError::NoMatchingKey),
125 };
126
127 if by_algorithm_verifiers.len() > 1 && !self.try_all_on_ambiguous_match {
128 return Err(VerifyError::AmbiguousKeyMatch);
129 }
130
131 let mut last_retryable = None;
132 let mut last_non_retryable = None;
133 for verifier in by_algorithm_verifiers {
134 match verifier.verify(input, signature, key_match).await {
135 Ok(()) => return Ok(()),
136 Err(VerifyError::NoMatchingKey) => {}
139 Err(e) => {
140 if e.is_retryable() {
141 last_retryable = Some(e);
142 } else {
143 last_non_retryable = Some(e);
144 }
145 }
146 }
147 }
148
149 Err(last_non_retryable
150 .or(last_retryable)
151 .unwrap_or(VerifyError::NoMatchingKey))
152 }
153
154 fn get_verifier(&self, key_match: &KeyMatch) -> GetVerifierResult {
155 let mut by_algorithm_verifiers: Vec<Arc<dyn JwsVerifier>> = Vec::new();
156
157 for verifier in &self.verifiers {
158 match verifier.key_match(key_match) {
159 Some(KeyMatchStrength::ByKeyId) => {
160 return GetVerifierResult::ByKeyId(verifier.clone());
161 }
162 Some(KeyMatchStrength::ByAlgorithm) => {
163 by_algorithm_verifiers.push(verifier.clone());
164 }
165 None => {}
166 }
167 }
168
169 if by_algorithm_verifiers.is_empty() {
170 GetVerifierResult::None
171 } else {
172 GetVerifierResult::ByAlgorithm(by_algorithm_verifiers)
173 }
174 }
175}
176
177impl JwsVerifier for MultiKeyVerifier {
178 fn key_match(&self, key_match: &KeyMatch<'_>) -> Option<KeyMatchStrength> {
179 self.get_verifier(key_match).key_match_strength()
180 }
181
182 fn verify<'a>(
183 &'a self,
184 input: &'a [u8],
185 signature: &'a [u8],
186 key_match: &'a KeyMatch<'a>,
187 ) -> MaybeSendBoxFuture<'a, Result<(), VerifyError>> {
188 Box::pin(self.dispatch_verify(input, signature, key_match))
189 }
190
191 fn try_refresh(&self) -> MaybeSendBoxFuture<'_, bool> {
192 Box::pin(async move {
193 join_all(self.verifiers.iter().map(JwsVerifier::try_refresh))
194 .await
195 .into_iter()
196 .any(|b| b)
197 })
198 }
199}