1use std::collections::{BTreeMap, BTreeSet, HashMap};
2use std::error::Error;
3use std::fmt::{self, Debug, Display};
4use std::sync::Arc;
5use std::time::Duration;
6
7use chrono::{DateTime, NaiveDateTime, Utc};
8use http::header::{HeaderName, HeaderValue, AUTHORIZATION, DATE};
9use sha2::{Digest, Sha256, Sha512};
10use subtle::ConstantTimeEq;
11
12use crate::algorithm::{HttpDigest, HttpSignatureVerify};
13use crate::canonicalize::{CanonicalizeConfig, CanonicalizeExt};
14use crate::header::{Header, PseudoHeader};
15use crate::{DefaultDigestAlgorithm, RequestLike, DATE_FORMAT};
16
17#[derive(Debug)]
20#[non_exhaustive]
21pub struct VerifyingError<Remnant> {
22 remnant: Remnant,
23}
24
25impl<Remnant> VerifyingError<Remnant> {
26 pub fn into_remnant(self) -> Remnant {
30 self.remnant
31 }
32}
33
34impl<Remnant: Debug> Error for VerifyingError<Remnant> {}
35
36impl<Remnant> Display for VerifyingError<Remnant> {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 f.write_str("VerifyingError")
39 }
40}
41
42pub trait KeyProvider: Debug + Sync + 'static {
48 fn provide_keys(&self, key_id: &str) -> Vec<Arc<dyn HttpSignatureVerify>>;
52}
53
54#[derive(Debug, Default, Clone)]
60pub struct SimpleKeyProvider {
61 keys: HashMap<String, Vec<Arc<dyn HttpSignatureVerify>>>,
62}
63
64impl SimpleKeyProvider {
65 pub fn new<I, S, K>(key_iter: I) -> Self
68 where
69 I: IntoIterator<Item = (S, K)>,
70 S: Into<String>,
71 K: Into<Arc<dyn HttpSignatureVerify>>,
72 {
73 let mut keys: HashMap<String, Vec<_>> = HashMap::new();
74 for (key_id, key) in key_iter.into_iter() {
75 keys.entry(key_id.into()).or_default().push(key.into());
76 }
77 Self { keys }
78 }
79
80 pub fn add(&mut self, key_id: &str, key: Arc<dyn HttpSignatureVerify>) {
82 self.keys.entry(key_id.into()).or_default().push(key);
83 }
84 pub fn clear(&mut self) {
86 self.keys.clear();
87 }
88 pub fn remove_all(&mut self, key_id: &str) {
90 self.keys.remove(key_id);
91 }
92}
93
94impl KeyProvider for SimpleKeyProvider {
95 fn provide_keys(&self, key_id: &str) -> Vec<Arc<dyn HttpSignatureVerify>> {
96 self.keys.get(key_id).unwrap_or(&Vec::new()).to_vec()
97 }
98}
99
100pub trait DigestProvider: Debug + Sync + 'static {
105 fn provide_digest(&self, name: &str) -> Option<Box<dyn HttpDigest>>;
108}
109
110#[derive(Debug, Default, Copy, Clone)]
112pub struct DefaultDigestProvider;
113
114impl DigestProvider for DefaultDigestProvider {
115 fn provide_digest(&self, name: &str) -> Option<Box<dyn HttpDigest>> {
116 let name = name.to_ascii_uppercase();
117 match name.as_str() {
118 "SHA-256" => Some(Box::new(Sha256::new())),
119 "SHA-512" => Some(Box::new(Sha512::new())),
120 _ => None,
121 }
122 }
123}
124
125#[derive(Debug)]
127pub struct VerifyingConfig {
128 key_provider: Arc<dyn KeyProvider>,
129 digest_provider: Arc<dyn DigestProvider>,
130 required_headers: BTreeSet<Header>,
131 require_digest: bool,
132 validate_digest: bool,
133 validate_date: bool,
134 date_leeway: Duration,
135}
136
137impl VerifyingConfig {
138 pub fn new<KP: KeyProvider>(key_provider: KP) -> Self {
140 VerifyingConfig {
141 key_provider: Arc::new(key_provider),
142 digest_provider: Arc::new(DefaultDigestProvider),
143 required_headers: [
144 Header::Pseudo(PseudoHeader::RequestTarget),
145 Header::Normal(DATE),
146 ]
147 .iter()
148 .cloned()
149 .collect(),
150 require_digest: true,
151 validate_digest: true,
152 validate_date: true,
153 date_leeway: Duration::from_secs(30),
154 }
155 }
156
157 pub fn key_provider(&self) -> &dyn KeyProvider {
159 &*self.key_provider
160 }
161 pub fn digest_provider(&self) -> &dyn DigestProvider {
163 &*self.digest_provider
164 }
165 fn set_digest_provider<DP: DigestProvider>(&mut self, digest_provider: DP) -> &mut Self {
167 self.digest_provider = Arc::new(digest_provider);
168 self
169 }
170 pub fn with_digest<DP: DigestProvider>(mut self, digest_provider: DP) -> Self {
172 self.set_digest_provider(digest_provider);
173 self
174 }
175 pub fn require_digest(&self) -> bool {
180 self.require_digest
181 }
182 pub fn set_require_digest(&mut self, require_digest: bool) -> &mut Self {
187 self.require_digest = require_digest;
188 self
189 }
190 pub fn with_require_digest(mut self, require_digest: bool) -> Self {
195 self.set_require_digest(require_digest);
196 self
197 }
198 pub fn validate_digest(&self) -> bool {
203 self.validate_digest
204 }
205 pub fn set_validate_digest(&mut self, validate_digest: bool) -> &mut Self {
210 self.validate_digest = validate_digest;
211 self
212 }
213 pub fn with_validate_digest(mut self, validate_digest: bool) -> Self {
218 self.set_validate_digest(validate_digest);
219 self
220 }
221 pub fn validate_date(&self) -> bool {
226 self.validate_date
227 }
228 pub fn set_validate_date(&mut self, validate_date: bool) -> &mut Self {
233 self.validate_date = validate_date;
234 self
235 }
236 pub fn with_validate_date(mut self, validate_date: bool) -> Self {
241 self.set_validate_date(validate_date);
242 self
243 }
244 pub fn date_leeway(&self) -> Duration {
249 self.date_leeway
250 }
251 pub fn set_date_leeway(&mut self, date_leeway: Duration) -> &mut Self {
256 self.date_leeway = date_leeway;
257 self
258 }
259 pub fn with_date_leeway(mut self, date_leeway: Duration) -> Self {
264 self.set_date_leeway(date_leeway);
265 self
266 }
267 pub fn required_headers(&self) -> impl IntoIterator<Item = &Header> {
273 &self.required_headers
274 }
275 pub fn set_required_headers(&mut self, required_headers: &[Header]) -> &mut Self {
281 self.required_headers = required_headers.iter().cloned().collect();
282 self
283 }
284 pub fn with_required_headers(mut self, required_headers: &[Header]) -> Self {
290 self.set_required_headers(required_headers);
291 self
292 }
293}
294
295pub trait ServerRequestLike: RequestLike {
302 type Remnant;
306
307 fn complete_with_digest(self, digest: &dyn HttpDigest) -> (Option<String>, Self::Remnant);
315
316 fn complete(self) -> Self::Remnant;
318}
319
320#[derive(Debug)]
322pub struct VerificationDetails {
323 key_id: String,
324}
325
326impl VerificationDetails {
327 pub fn key_id(&self) -> &str {
329 &self.key_id
330 }
331}
332
333pub trait VerifyingExt {
336 type Remnant;
340
341 fn verify(
343 self,
344 config: &VerifyingConfig,
345 ) -> Result<(Self::Remnant, VerificationDetails), VerifyingError<Self::Remnant>>;
346}
347
348fn verify_signature_only<T: ServerRequestLike>(
349 req: &T,
350 config: &VerifyingConfig,
351) -> Option<(BTreeMap<Header, HeaderValue>, VerificationDetails)> {
352 let auth_header = req.header(&AUTHORIZATION.into()).or_else(|| {
353 info!("Verification Failed: No 'Authorization' header");
354 None
355 })?;
356 let mut auth_header = auth_header
357 .to_str()
358 .ok()
359 .or_else(|| {
360 info!("Verification Failed: Non-ascii 'Authorization' header");
361 None
362 })?
363 .splitn(2, ' ');
364
365 let auth_scheme = auth_header.next().or_else(|| {
366 info!("Verification Failed: Malformed 'Authorization' header");
367 None
368 })?;
369 let auth_args = auth_header.next().or_else(|| {
370 info!("Verification Failed: Malformed 'Authorization' header");
371 None
372 })?;
373
374 if !auth_scheme.eq_ignore_ascii_case("Signature") {
376 info!("Verification Failed: Not using Signature auth");
377 return None;
378 }
379
380 let auth_args = auth_args
382 .split(',')
383 .map(|part: &str| {
384 let mut kv = part.splitn(2, '=');
385 let k = kv.next()?.trim();
386 let v = kv.next()?.trim().trim_matches('"');
387 Some((k, v))
388 })
389 .collect::<Option<BTreeMap<_, _>>>()
390 .or_else(|| {
391 info!("Verification Failed: Unable to parse 'Authorization' header");
392 None
393 })?;
394
395 let key_id = *auth_args.get("keyId").or_else(|| {
396 info!("Verification Failed: Missing required 'keyId' in 'Authorization' header");
397 None
398 })?;
399 let provided_signature = auth_args.get("signature").or_else(|| {
400 info!("Verification Failed: Missing required 'signature' in 'Authorization' header");
401 None
402 })?;
403 let algorithm_name = auth_args.get("algorithm").copied();
404 let verification_details = VerificationDetails {
405 key_id: key_id.into(),
406 };
407
408 let algorithms = config.key_provider.provide_keys(key_id);
410 if algorithms.is_empty() {
411 info!(
412 "Verification Failed: Unknown key (keyId={:?}, algorithm={:?})",
413 key_id,
414 algorithm_name.unwrap_or_default()
415 );
416 return None;
417 }
418
419 let mut canonicalize_config = CanonicalizeConfig::new();
421 if let Some(headers) = auth_args.get("headers") {
422 canonicalize_config.set_headers(
423 headers
424 .split(' ')
425 .map(str::to_ascii_lowercase)
426 .map(|header| {
427 header.parse::<Header>().ok().or_else(|| {
428 info!("Verification Failed: Invalid header name {:?}", header);
429 None
430 })
431 })
432 .collect::<Option<_>>()?,
433 );
434 }
435 if let Some(created) = auth_args.get("created") {
436 canonicalize_config.set_signature_created(created.parse::<HeaderValue>().ok().or_else(
437 || {
438 info!(
439 "Verification Failed: Invalid signature creation date {:?}",
440 created
441 );
442 None
443 },
444 )?);
445 }
446 if let Some(expires) = auth_args.get("expires") {
447 canonicalize_config.set_signature_expires(expires.parse::<HeaderValue>().ok().or_else(
448 || {
449 info!(
450 "Verification Failed: Invalid signature expires date {:?}",
451 expires
452 );
453 None
454 },
455 )?);
456 }
457
458 let content = req
460 .canonicalize(&canonicalize_config)
461 .map_err(|e| {
462 info!("Canonicalization Failed: {}", e);
463 })
464 .ok()?;
465
466 for algorithm in &algorithms {
468 if algorithm.http_verify(content.as_bytes(), provided_signature) {
469 return Some((content.headers.into_iter().collect(), verification_details));
470 }
471 }
472
473 if algorithms.is_empty() {
474 info!("Verification Failed: No keys found for this keyId");
475 } else {
476 info!("Verification Failed: Invalid signature provided");
477 }
478 None
479}
480
481fn verify_except_digest<T: ServerRequestLike>(
482 req: &T,
483 config: &VerifyingConfig,
484) -> Option<(BTreeMap<Header, HeaderValue>, VerificationDetails)> {
485 let (headers, verification_details) = verify_signature_only(req, config)?;
486
487 for header in &config.required_headers {
489 if !headers.contains_key(header) {
490 info!(
491 "Verification Failed: Missing header '{}' required by configuration",
492 header.as_str()
493 );
494 return None;
495 }
496 }
497
498 if config.validate_date {
500 if let Some(date_value) = headers.get(&DATE.into()) {
502 let date_value = date_value.to_str().ok().or_else(|| {
504 info!("Verification Failed: Non-ascii value for 'date' header");
505 None
506 })?;
507
508 let provided_date = DateTime::<Utc>::from_naive_utc_and_offset(
510 NaiveDateTime::parse_from_str(date_value, DATE_FORMAT)
511 .ok()
512 .or_else(|| {
513 info!("Verification Failed: Failed to parse 'date' header");
514 None
515 })?,
516 Utc,
517 );
518
519 let chrono_delta = provided_date.signed_duration_since(Utc::now());
522 let delta = chrono_delta
523 .to_std()
524 .or_else(|_| (-chrono_delta).to_std())
525 .expect("Should only fail on negative values");
526
527 if delta > config.date_leeway {
528 info!(
529 "Verification Failed: Date skew of '{}' is outside allowed range",
530 chrono_delta
531 );
532 return None;
533 }
534 }
535 }
536
537 Some((headers, verification_details))
538}
539
540impl<T: ServerRequestLike> VerifyingExt for T {
541 type Remnant = T::Remnant;
542
543 fn verify(
544 self,
545 config: &VerifyingConfig,
546 ) -> Result<(Self::Remnant, VerificationDetails), VerifyingError<Self::Remnant>> {
547 let digest_header: Header = HeaderName::from_static("digest").into();
548
549 let (headers, verification_details) = if let Some(res) = verify_except_digest(&self, config)
552 {
553 res
554 } else {
555 return Err(VerifyingError {
556 remnant: self.complete(),
557 });
558 };
559
560 if let Some(digest_value) = headers.get(&digest_header) {
562 if config.validate_digest {
564 let digest_value = match digest_value.to_str() {
566 Ok(v) => v,
567 Err(_) => {
568 info!("Verification Failed: Non-ascii value for 'digest' header");
569 return Err(VerifyingError {
570 remnant: self.complete(),
571 });
572 }
573 };
574
575 if let Some((digest_alg, provided_digest)) = digest_value
577 .split(',')
578 .filter_map(|part| {
579 let mut kv = part.splitn(2, '=');
580 let k = kv.next()?.trim();
581 let v = kv.next()?.trim();
582
583 let digest = config.digest_provider.provide_digest(k)?;
584 Some((digest, v))
585 })
586 .next()
587 {
588 let (maybe_digest, remnant) = self.complete_with_digest(&*digest_alg);
590
591 match maybe_digest {
593 Some(expected_digest)
594 if provided_digest
595 .as_bytes()
596 .ct_eq(expected_digest.as_bytes())
597 .into() =>
598 {
599 Ok((remnant, verification_details))
600 }
601 None => {
602 info!("Verification Failed: Unable to compute digest for comparison");
603 Err(VerifyingError { remnant })
604 }
605 _ => {
606 info!("Verification Failed: Computed digest did not match the 'digest' header");
607 Err(VerifyingError { remnant })
608 }
609 }
610 } else {
611 info!("Verification Failed: No supported digest algorithms were used");
613 Err(VerifyingError {
614 remnant: self.complete(),
615 })
616 }
617 } else {
618 Ok((self.complete(), verification_details))
620 }
621 } else if config.require_digest {
622 let (maybe_digest, remnant) = self.complete_with_digest(&DefaultDigestAlgorithm::new());
625
626 if maybe_digest.is_some() {
628 info!("Verification Failed: 'digest' header was not included in signature, but is required by configuration");
630 Err(VerifyingError { remnant })
631 } else {
632 Ok((remnant, verification_details))
634 }
635 } else {
636 Ok((self.complete(), verification_details))
638 }
639 }
640}