Skip to main content

actpub_httpsig/
error.rs

1//! Error types for [`actpub-httpsig`](crate).
2
3use thiserror::Error;
4
5/// Enumeration of every failure mode that this crate can surface.
6///
7/// The enum is non-exhaustive so that additional signature schemes or
8/// cryptographic algorithms can be added in minor releases without
9/// breaking downstream code.
10#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum Error {
13    /// The provided PEM document could not be parsed.
14    #[error("invalid PEM document: {0}")]
15    InvalidPem(String),
16
17    /// The PEM document had an unexpected `-----BEGIN <LABEL>-----` line.
18    #[error("unexpected PEM label `{0}`, expected one of: {1}")]
19    UnexpectedPemLabel(String, &'static str),
20
21    /// A PKCS#8 DER blob could not be decoded.
22    #[error("invalid PKCS#8 DER: {0}")]
23    InvalidPkcs8(String),
24
25    /// The key's algorithm identifier was not supported.
26    #[error("unsupported key algorithm: {0}")]
27    UnsupportedAlgorithm(String),
28
29    /// The RSA key size was outside the supported range.
30    ///
31    /// The bounds mirror `aws-lc-rs`'s
32    /// `RSA_PKCS1_2048_8192_SHA256` verification profile: any modulus
33    /// width between 2048 and 8192 bits (inclusive) whose byte count is
34    /// whole (i.e. divisible by 8) is accepted.
35    #[error(
36        "unsupported RSA key size {0} bits; only {min}-{max} supported",
37        min = 2048,
38        max = 8192
39    )]
40    UnsupportedRsaSize(u32),
41
42    /// An underlying `aws-lc-rs` primitive failed.
43    #[error("cryptographic operation failed: {0}")]
44    Crypto(&'static str),
45
46    /// Generation of a new key failed at the RNG layer.
47    #[error("key generation failed: {0}")]
48    KeyGeneration(&'static str),
49
50    /// A signature's Base64 encoding was malformed.
51    #[error("invalid Base64 in signature: {0}")]
52    InvalidBase64(#[from] base64ct::Error),
53
54    /// Multibase decoding of a FEP-521a `publicKeyMultibase` failed.
55    #[error("invalid multibase: {0}")]
56    InvalidMultibase(#[from] multibase::Error),
57
58    /// The multicodec prefix on a Multikey was unrecognised or truncated.
59    #[error("invalid multikey codec prefix")]
60    InvalidMultikeyPrefix,
61
62    /// The raw key material following the multicodec prefix had the wrong length.
63    #[error("invalid multikey body length: expected {expected}, got {actual}")]
64    InvalidMultikeyLength {
65        /// Expected number of key bytes.
66        expected: usize,
67        /// Actual number of key bytes.
68        actual: usize,
69    },
70
71    /// A required HTTP header is missing.
72    #[error("missing HTTP header `{0}`")]
73    MissingHeader(&'static str),
74
75    /// An HTTP header's value was not valid UTF-8 or otherwise unparseable.
76    #[error("invalid HTTP header `{name}`: {reason}")]
77    InvalidHeader {
78        /// Header name that could not be parsed.
79        name: &'static str,
80        /// Human-readable reason.
81        reason: String,
82    },
83
84    /// The `Signature` header's parameter list was malformed.
85    #[error("malformed Signature header: {0}")]
86    MalformedSignatureHeader(String),
87
88    /// The signature did not verify against the provided key.
89    #[error("signature verification failed")]
90    VerificationFailed,
91
92    /// The resolver closure returned an error while fetching the signer's key.
93    #[error("key resolution failed: {0}")]
94    KeyResolution(String),
95
96    /// The `Digest` / `Content-Digest` header did not match the body.
97    #[error("digest mismatch: body SHA-256 did not match `Digest` header")]
98    DigestMismatch,
99
100    /// The requested digest algorithm is not supported.
101    #[error("unsupported digest algorithm `{0}`")]
102    UnsupportedDigestAlgorithm(String),
103
104    /// The signature-base string includes a header that the request does not carry.
105    #[error("cannot build signature base: required header `{0}` is absent from the request")]
106    RequiredHeaderAbsent(String),
107
108    /// A signature parameter required by the standard is missing.
109    #[error("required signature parameter `{0}` is missing")]
110    MissingSignatureParameter(&'static str),
111
112    /// The signature carried no `created` parameter and no `Date`
113    /// header, and the active [`VerifyPolicy`](crate::VerifyPolicy)
114    /// requires one.
115    #[error("no `created` parameter or `Date` header on a signature that requires freshness")]
116    TimestampMissing,
117
118    /// The signature is older than the policy's `max_age`.
119    #[error("signature is too old: timestamp {timestamp}, now {now}")]
120    TimestampTooOld {
121        /// The signed timestamp, either from `created` or the `Date` header.
122        timestamp: chrono::DateTime<chrono::Utc>,
123        /// The verifier's current wall-clock time.
124        now: chrono::DateTime<chrono::Utc>,
125    },
126
127    /// The signature claims to have been produced further in the future
128    /// than the policy's `max_clock_skew_future` tolerance allows.
129    #[error("signature claims a future timestamp: timestamp {timestamp}, now {now}")]
130    TimestampInFuture {
131        /// The signed timestamp.
132        timestamp: chrono::DateTime<chrono::Utc>,
133        /// The verifier's current wall-clock time.
134        now: chrono::DateTime<chrono::Utc>,
135    },
136
137    /// The signature's `expires` parameter indicates it has lapsed.
138    #[error("signature expired at {expires}, now {now}")]
139    TimestampExpired {
140        /// The `expires` parameter interpreted as a UTC timestamp.
141        expires: chrono::DateTime<chrono::Utc>,
142        /// The verifier's current wall-clock time.
143        now: chrono::DateTime<chrono::Utc>,
144    },
145}