http_sig/
verifying.rs

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/// This error indicates that we failed to verify the request. As a result
18/// the request should be ignored.
19#[derive(Debug)]
20#[non_exhaustive]
21pub struct VerifyingError<Remnant> {
22    remnant: Remnant,
23}
24
25impl<Remnant> VerifyingError<Remnant> {
26    /// For some request types, the verification process may be a destructive operation.
27    /// This method can be used to access information that would otherwise be lost as a
28    /// result of the failed verification.
29    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
42/// The verification process will use this trait to find the appropriate key and algorithm
43/// to use for verifying a request.
44///
45/// You do not need to implement this yourself: the `SimpleKeyProvider` type provides an
46/// key store that should be suitable for many situations.
47pub trait KeyProvider: Debug + Sync + 'static {
48    /// Given the name of an algorithm (eg. `hmac-sha256`) and the key ID, return a set
49    /// of possible keys and algorithms. Returns an empty Vec if no appropriate key/algorithm
50    /// combination could be found.
51    fn provide_keys(&self, key_id: &str) -> Vec<Arc<dyn HttpSignatureVerify>>;
52}
53
54/// Implementation of a simple key store.
55///
56/// Can store multiple keys with the same Key ID but different algorithms.
57/// If no algorithm is specified in the request, the first key added for
58/// that Key ID will be used.
59#[derive(Debug, Default, Clone)]
60pub struct SimpleKeyProvider {
61    keys: HashMap<String, Vec<Arc<dyn HttpSignatureVerify>>>,
62}
63
64impl SimpleKeyProvider {
65    /// Initializes the key store from a list of key IDs and signature
66    /// algorithms.
67    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    /// Adds a key to the key store
81    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    /// Clears all keys from the key store
85    pub fn clear(&mut self) {
86        self.keys.clear();
87    }
88    /// Removes all keys with the specified Key ID from the key store
89    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
100/// The verification process will use this trait to find the appropriate digest algorithm
101/// to use when verifying the body of a request.
102///
103/// Unless explicitly overridden, the `DefaultDigestProvider` will be used
104pub trait DigestProvider: Debug + Sync + 'static {
105    /// Returns a digest algorithm for the given name, or `None` if the algorithm is not
106    /// recognised by the provider.
107    fn provide_digest(&self, name: &str) -> Option<Box<dyn HttpDigest>>;
108}
109
110/// Supports the `SHA-256` and `SHA-512` digest algorithms.
111#[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/// The configuration used for verifying HTTP requests.
126#[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    /// Creates a new verifying configuration using the given key provider.
139    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    /// Returns the key provider.
158    pub fn key_provider(&self) -> &dyn KeyProvider {
159        &*self.key_provider
160    }
161    /// Returns the digest provider.
162    pub fn digest_provider(&self) -> &dyn DigestProvider {
163        &*self.digest_provider
164    }
165    /// Sets the digest provider (in-place).
166    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    /// Sets the digest provider.
171    pub fn with_digest<DP: DigestProvider>(mut self, digest_provider: DP) -> Self {
172        self.set_digest_provider(digest_provider);
173        self
174    }
175    /// Returns whether a digest header must be present and included in the signature for requests
176    /// with a body.
177    ///
178    /// This is set to `true` by default.
179    pub fn require_digest(&self) -> bool {
180        self.require_digest
181    }
182    /// Controls whether a digest header must be present and included in the signature for requests
183    /// with a body (in-place).
184    ///
185    /// This is set to `true` by default.
186    pub fn set_require_digest(&mut self, require_digest: bool) -> &mut Self {
187        self.require_digest = require_digest;
188        self
189    }
190    /// Controls whether a digest header must be present and included in the signature for requests
191    /// with a body.
192    ///
193    /// This is set to `true` by default.
194    pub fn with_require_digest(mut self, require_digest: bool) -> Self {
195        self.set_require_digest(require_digest);
196        self
197    }
198    /// Returns whether the request body will be checked against the digest for correctness if the
199    /// digest is included in the signature.
200    ///
201    /// This is set to `true` by default.
202    pub fn validate_digest(&self) -> bool {
203        self.validate_digest
204    }
205    /// Controls whether the request body will be checked against the digest for correctness if the
206    /// digest is included in the signature (in-place).
207    ///
208    /// This is set to `true` by default.
209    pub fn set_validate_digest(&mut self, validate_digest: bool) -> &mut Self {
210        self.validate_digest = validate_digest;
211        self
212    }
213    /// Controls whether the request body will be checked against the digest for correctness if the
214    /// digest is included in the signature.
215    ///
216    /// This is set to `true` by default.
217    pub fn with_validate_digest(mut self, validate_digest: bool) -> Self {
218        self.set_validate_digest(validate_digest);
219        self
220    }
221    /// Returns whether the date header will be compared against the current date and time if the
222    /// date header is included in the signature.
223    ///
224    /// This is set to `true` by default.
225    pub fn validate_date(&self) -> bool {
226        self.validate_date
227    }
228    /// Controls whether the date header will be compared against the current date and time if the
229    /// date header is included in the signature (in-place).
230    ///
231    /// This is set to `true` by default.
232    pub fn set_validate_date(&mut self, validate_date: bool) -> &mut Self {
233        self.validate_date = validate_date;
234        self
235    }
236    /// Controls whether the date header will be compared against the current date and time if the
237    /// date header is included in the signature.
238    ///
239    /// This is set to `true` by default.
240    pub fn with_validate_date(mut self, validate_date: bool) -> Self {
241        self.set_validate_date(validate_date);
242        self
243    }
244    /// Returns the amount of leeway allowed in either direction when comparing dates and times
245    /// from requests against the current date and time.
246    ///
247    /// This is set to 30 seconds by default.
248    pub fn date_leeway(&self) -> Duration {
249        self.date_leeway
250    }
251    /// Controls the amount of leeway allowed in either direction when comparing dates and times
252    /// from requests against the current date and time (in-place).
253    ///
254    /// This is set to 30 seconds by default.
255    pub fn set_date_leeway(&mut self, date_leeway: Duration) -> &mut Self {
256        self.date_leeway = date_leeway;
257        self
258    }
259    /// Controls the amount of leeway allowed in either direction when comparing dates and times
260    /// from requests against the current date and time.
261    ///
262    /// This is set to 30 seconds by default.
263    pub fn with_date_leeway(mut self, date_leeway: Duration) -> Self {
264        self.set_date_leeway(date_leeway);
265        self
266    }
267    /// Returns the list of headers that *must* be included in every request's signature. Do not
268    /// include the `digest` header here or requests without a body will be denied. Instead, rely
269    /// on the `validate_digest` option.
270    ///
271    /// This list contains `(request-target)` and `date` by default.
272    pub fn required_headers(&self) -> impl IntoIterator<Item = &Header> {
273        &self.required_headers
274    }
275    /// Controls the list of headers that *must* be included in every request's signature (in-place). Do not
276    /// include the `digest` header here or requests without a body will be denied. Instead, rely
277    /// on the `validate_digest` option.
278    ///
279    /// This list contains `(request-target)` and `date` by default.
280    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    /// Controls the list of headers that *must* be included in every request's signature. Do not
285    /// include the `digest` header here or requests without a body will be denied. Instead, rely
286    /// on the `validate_digest` option.
287    ///
288    /// This list contains `(request-target)` and `date` by default.
289    pub fn with_required_headers(mut self, required_headers: &[Header]) -> Self {
290        self.set_required_headers(required_headers);
291        self
292    }
293}
294
295/// This trait is to be implemented for types representing an incoming
296/// HTTP request. The HTTP verification extension methods are available on
297/// any type implementing this trait.
298///
299/// Typically this trait is implemented for references or mutable references to those
300/// request types rather than for the request type itself.
301pub trait ServerRequestLike: RequestLike {
302    /// For some request types, the verification process may be a destructive operation.
303    /// This associated type can be used to return information that might otherwise
304    /// be lost.
305    type Remnant;
306
307    /// Complete the verification process, indicating that we want to compute a digest of the
308    /// request body. This may require buffering the whole request body into memory.
309    ///
310    /// If a request body was present, its digest should be returned as the first element of
311    /// the tuple. Otherwise `None` should be returned. The second tuple element may contain
312    /// any information the implementation wants returned to the caller (for example the buffered
313    /// request body, if it had to be removed from the request).
314    fn complete_with_digest(self, digest: &dyn HttpDigest) -> (Option<String>, Self::Remnant);
315
316    /// Complete the verification process without attempting to compute a digest.
317    fn complete(self) -> Self::Remnant;
318}
319
320/// Contains information about a successfully validated request.
321#[derive(Debug)]
322pub struct VerificationDetails {
323    key_id: String,
324}
325
326impl VerificationDetails {
327    /// Returns the ID of the key used to validate this request's signature.
328    pub fn key_id(&self) -> &str {
329        &self.key_id
330    }
331}
332
333/// Import this trait to get access to access the `verify` method on all types implementing
334/// `ServerRequestLike`.
335pub trait VerifyingExt {
336    /// For some request types, the verification process may be a destructive operation.
337    /// This associated type can be used to return information that might otherwise
338    /// be lost.
339    type Remnant;
340
341    /// Verify the request using the given verification configuration.
342    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    // Check that we're using signature auth
375    if !auth_scheme.eq_ignore_ascii_case("Signature") {
376        info!("Verification Failed: Not using Signature auth");
377        return None;
378    }
379
380    // Parse the auth params
381    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    // Find the appropriate key
409    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    // Determine config for canonicalization
420    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    // Canonicalize the request
459    let content = req
460        .canonicalize(&canonicalize_config)
461        .map_err(|e| {
462            info!("Canonicalization Failed: {}", e);
463        })
464        .ok()?;
465
466    // Verify the signature of the content
467    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    // Check that all the required headers are set
488    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 we are expected to validate the date
499    if config.validate_date {
500        // If date was part of signature
501        if let Some(date_value) = headers.get(&DATE.into()) {
502            // First convert to a string
503            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            // Then parse into a datetime
509            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            // Finally, compute the absolute difference between the provided
520            // date and now.
521            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        // Check everything but the digest first, as that doesn't require consuming
550        // the request.
551        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 we got a digest header
561        if let Some(digest_value) = headers.get(&digest_header) {
562            // If we are expected to validate it
563            if config.validate_digest {
564                // First convert to a string
565                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                // Find the first digest which is using a supported algorithm
576                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                    // Tell the request to compute a digest as it completes
589                    let (maybe_digest, remnant) = self.complete_with_digest(&*digest_alg);
590
591                    // Check that the digest is correct in constant time
592                    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                    // No supported digest algorithm.
612                    info!("Verification Failed: No supported digest algorithms were used");
613                    Err(VerifyingError {
614                        remnant: self.complete(),
615                    })
616                }
617            } else {
618                // We are not expected to validate the digest
619                Ok((self.complete(), verification_details))
620            }
621        } else if config.require_digest {
622            // We require a digest for requests with a body, but we didn't get one. Either the request
623            // has no body, or we should reject it.
624            let (maybe_digest, remnant) = self.complete_with_digest(&DefaultDigestAlgorithm::new());
625
626            // If the request did have a body (because we were able to compute a digest).
627            if maybe_digest.is_some() {
628                // Then reject the request
629                info!("Verification Failed: 'digest' header was not included in signature, but is required by configuration");
630                Err(VerifyingError { remnant })
631            } else {
632                // No body, so request if fine.
633                Ok((remnant, verification_details))
634            }
635        } else {
636            // We do not require a digest, valid or otherwise.
637            Ok((self.complete(), verification_details))
638        }
639    }
640}