Skip to main content

mail_auth/dkim2/
error.rs

1/*
2 * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
3 *
4 * SPDX-License-Identifier: Apache-2.0 OR MIT
5 */
6
7use super::Dkim2Hash;
8use std::fmt::{Display, Formatter};
9
10/// Reason a DKIM2 header field failed to parse, a DKIM2 chain failed
11/// verification or a message could not be signed.
12///
13/// Carried as [`Error::Dkim2`](crate::Error::Dkim2) inside a
14/// [`Dkim2Result`](super::Dkim2Result) or returned by the parsing and signing
15/// functions. The `i` payloads are the `i=` of the signature involved and the
16/// `m` payloads the `m=` of the instance involved. The
17/// [`Display`] text is the human-readable string of §14 without the
18/// `PERMERROR:`/`FAIL:` prefix, which [`Dkim2Result`](super::Dkim2Result)
19/// conveys.
20#[derive(Debug, PartialEq, Eq, Clone)]
21pub enum Dkim2Error {
22    /// No `Message-Instance` with this `m=`, or the instance numbering has a
23    /// gap.
24    InstanceMissing(u32),
25    /// The `Message-Instance` has a syntax error or a repeated tag.
26    InstanceSyntax(u32),
27    /// A required `Message-Instance` tag is missing.
28    InstanceTagMissing {
29        /// Revision number (`m=`) of the instance.
30        m: u32,
31        /// Name of the missing tag.
32        tag: &'static str,
33    },
34    /// The `Message-Instance` has a higher `m=` than any signature.
35    InstanceNotSigned(u32),
36    /// The `Message-Instance` lists the same hash algorithm twice.
37    InstanceDuplicateHash(u32),
38    /// The recipe (`r=` tag) of the `Message-Instance` is not valid JSON or
39    /// does not follow the recipe schema (§5).
40    InvalidJson(u32),
41    /// The `Message-Instance` has no hash with a supported algorithm.
42    NoValidHash(u32),
43    /// No `DKIM2-Signature` with this `i=`, for example a message with a
44    /// `Message-Instance` but no signature.
45    SignatureMissing(u32),
46    /// The `DKIM2-Signature` has a syntax error, a repeated tag, an
47    /// undecodable value, an invalid nonce, an address without angle
48    /// brackets, or an `m=` lower than that of the previous signature.
49    SignatureSyntax(u32),
50    /// A required `DKIM2-Signature` tag is missing.
51    SignatureTagMissing {
52        /// Sequence number (`i=`) of the signature.
53        i: u32,
54        /// Name of the missing tag.
55        tag: &'static str,
56    },
57    /// A `DKIM2-Signature` tag is not allowed in this position, such as
58    /// `nd=` together with `mf=`/`rt=`, `nd=` on the most recent signature,
59    /// or the `exploded` flag (`f=`) on the first signature.
60    SignatureTagUnexpected {
61        /// Sequence number (`i=`) of the signature.
62        i: u32,
63        /// Name of the unexpected tag.
64        tag: &'static str,
65    },
66    /// The `s=` tag names the same selector twice.
67    DuplicateSelector(u32),
68    /// The `s=` tag has more than two entries with the same algorithm.
69    TooManySelectors(u32),
70    /// The signature sequence numbering does not run from 1 without gaps;
71    /// carries the first missing `i=`.
72    SequenceGap(u32),
73    /// A new signature or instance number would exceed `u32::MAX`.
74    SequenceOverflow,
75    /// The message has more than 50 `DKIM2-Signature` or `Message-Instance`
76    /// header fields.
77    ChainTooLong,
78    /// The signature timestamp (`t=`) is more than 14 days old.
79    SignatureExpired(u32),
80    /// The signature timestamp (`t=`) is more than one day in the future.
81    SignatureFromFuture(u32),
82    /// The SMTP `MAIL FROM` does not match `mf=` of the most recent
83    /// signature.
84    MailFromMismatch {
85        /// Sequence number (`i=`) of the signature.
86        i: u32,
87        /// The SMTP `MAIL FROM` value.
88        value: Box<str>,
89    },
90    /// An SMTP `RCPT TO` address is not listed in `rt=` of the most recent
91    /// signature.
92    RcptToMismatch {
93        /// Sequence number (`i=`) of the signature.
94        i: u32,
95        /// The SMTP `RCPT TO` value.
96        value: Box<str>,
97    },
98    /// The `mf=` domain is neither `d=` nor a subdomain of it (§10.8).
99    MailFromDomainMismatch(u32),
100    /// `d=` differs from the `nd=` of the previous signature.
101    NextDomainMismatch(u32),
102    /// The `mf=` domain does not relaxed-match any `rt=` recipient of the
103    /// previous signature (§7.1).
104    CustodyMailFromMismatch {
105        /// Sequence number (`i=`) of the signature.
106        i: u32,
107        /// Sequence number (`i=`) of the previous signature.
108        previous: u32,
109    },
110    /// No `rt=` recipient of the previous signature relaxed-matches `d=`,
111    /// for a signature with a null `mf=` or with `nd=` (§7.1).
112    CustodyDomainMismatch {
113        /// Sequence number (`i=`) of the signature.
114        i: u32,
115        /// Sequence number (`i=`) of the previous signature.
116        previous: u32,
117    },
118    /// The public key lookup failed with a temporary DNS error.
119    PublicKeyFetch {
120        /// Sequence number (`i=`) of the signature.
121        i: u32,
122        /// Selector of the key.
123        selector: Box<str>,
124    },
125    /// The public key record does not exist.
126    PublicKeyMissing {
127        /// Sequence number (`i=`) of the signature.
128        i: u32,
129        /// Selector of the key.
130        selector: Box<str>,
131    },
132    /// The selector has more than one TXT record.
133    PublicKeyMultiple {
134        /// Sequence number (`i=`) of the signature.
135        i: u32,
136        /// Selector of the key.
137        selector: Box<str>,
138    },
139    /// The public key record or the key itself is malformed, or an RSA key
140    /// is shorter than 1024 bits.
141    PublicKeySyntax {
142        /// Sequence number (`i=`) of the signature.
143        i: u32,
144        /// Selector of the key.
145        selector: Box<str>,
146    },
147    /// The public key type (`k=`) does not match the `s=` algorithm.
148    PublicKeyAlgorithmMismatch {
149        /// Sequence number (`i=`) of the signature.
150        i: u32,
151        /// Selector of the key.
152        selector: Box<str>,
153    },
154    /// The public key record has an empty `p=` tag (revoked key).
155    PublicKeyRevoked {
156        /// Sequence number (`i=`) of the signature.
157        i: u32,
158        /// Selector of the key.
159        selector: Box<str>,
160    },
161    /// No `s=` entry of the signature verified.
162    IncorrectSignature {
163        /// Sequence number (`i=`) of the signature.
164        i: u32,
165        /// Selector of the first entry that failed.
166        selector: Box<str>,
167    },
168    /// Some `s=` entries of the signature verified and others did not.
169    PartialSignatureFailure {
170        /// Sequence number (`i=`) of the signature.
171        i: u32,
172        /// Selector of the first entry that verified.
173        passed: Box<str>,
174        /// Selector of the first entry that failed.
175        failed: Box<str>,
176    },
177    /// The signature has no `s=` entry with a supported algorithm.
178    NoValidAlgorithm(u32),
179    /// The header fields hash does not match the `Message-Instance`.
180    HeaderHashMismatch {
181        /// Revision number (`m=`) of the instance.
182        m: u32,
183        /// Hash algorithm that did not match.
184        hash: Dkim2Hash,
185    },
186    /// The body hash does not match the `Message-Instance`.
187    BodyHashMismatch {
188        /// Revision number (`m=`) of the instance.
189        m: u32,
190        /// Hash algorithm that did not match.
191        hash: Dkim2Hash,
192    },
193    /// A `From` header field is not Authentic (§11): its domain does not
194    /// relaxed-match `d=` of the signature that covers it, or it has no
195    /// single address with a domain name.
196    FromNotAuthentic(u32),
197    /// [`Recipe::diff`](super::Recipe::diff) found a changed header field
198    /// value with a bare CR or LF outside folding, which a recipe cannot
199    /// recreate.
200    HeaderNotRepresentable,
201    /// [`Recipe::apply`](super::Recipe::apply) found a body recipe that
202    /// declares the previous body unreconstructable (`"b": null`).
203    BodyUnreconstructable,
204    /// A recipe could not be serialized to or parsed from JSON, or does not
205    /// follow the recipe schema (§5).
206    RecipeSyntax,
207    /// The message to sign already has DKIM2 header fields but no passing
208    /// full verification of it was supplied, and the signer has no
209    /// out-of-band arrangement (§12).
210    NotValidlySigned,
211}
212
213impl Display for Dkim2Error {
214    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
215        match self {
216            Dkim2Error::InstanceMissing(m) => write!(f, "Message-Instance m={m} missing"),
217            Dkim2Error::InstanceSyntax(m) => write!(f, "Message-Instance m={m} syntax error"),
218            Dkim2Error::InstanceTagMissing { m, tag } => {
219                write!(f, "Message-Instance m={m} tag={tag} missing")
220            }
221            Dkim2Error::InstanceNotSigned(m) => write!(f, "Message-Instance m={m} is not signed"),
222            Dkim2Error::InstanceDuplicateHash(m) => {
223                write!(f, "Message-Instance m={m} has a duplicate hash algorithm")
224            }
225            Dkim2Error::InvalidJson(m) => {
226                write!(f, "Message-Instance m={m} contains invalid JSON")
227            }
228            Dkim2Error::NoValidHash(m) => {
229                write!(f, "Message-Instance m={m} has no valid hash algorithms")
230            }
231            Dkim2Error::SignatureMissing(i) => write!(f, "DKIM2-Signature i={i} missing"),
232            Dkim2Error::SignatureSyntax(i) => write!(f, "DKIM2-Signature i={i} syntax error"),
233            Dkim2Error::SignatureTagMissing { i, tag } => {
234                write!(f, "DKIM2-Signature i={i} tag={tag} missing")
235            }
236            Dkim2Error::SignatureTagUnexpected { i, tag } => {
237                write!(f, "DKIM2-Signature i={i} tag={tag} was unexpected")
238            }
239            Dkim2Error::DuplicateSelector(i) => {
240                write!(f, "DKIM2-Signature i={i} has a duplicate selector")
241            }
242            Dkim2Error::TooManySelectors(i) => {
243                write!(f, "DKIM2-Signature i={i} has more selectors than allowed")
244            }
245            Dkim2Error::SequenceGap(i) => write!(f, "DKIM2-Signature i={i} missing"),
246            Dkim2Error::SequenceOverflow => {
247                write!(f, "DKIM2 sequence numbering would overflow")
248            }
249            Dkim2Error::ChainTooLong => write!(f, "Too many DKIM2 header fields"),
250            Dkim2Error::SignatureExpired(i) => write!(f, "DKIM2-Signature i={i} signature expired"),
251            Dkim2Error::SignatureFromFuture(i) => {
252                write!(f, "DKIM2-Signature i={i} signature is from the future")
253            }
254            Dkim2Error::MailFromMismatch { i, value } => {
255                write!(f, "DKIM2-Signature i={i} MAIL FROM {value} did not match")
256            }
257            Dkim2Error::RcptToMismatch { i, value } => {
258                write!(f, "DKIM2-Signature i={i} RCPT TO {value} did not match")
259            }
260            Dkim2Error::MailFromDomainMismatch(i) => {
261                write!(f, "DKIM2-Signature i={i} MAIL FROM and d= do not match")
262            }
263            Dkim2Error::NextDomainMismatch(i) => {
264                write!(f, "DKIM2-Signature i={i} nd= does not match")
265            }
266            Dkim2Error::CustodyMailFromMismatch { i, previous } => {
267                write!(
268                    f,
269                    "DKIM2-Signature i={i} mf= does not match the rt= of i={previous}"
270                )
271            }
272            Dkim2Error::CustodyDomainMismatch { i, previous } => {
273                write!(
274                    f,
275                    "DKIM2-Signature i={i} d= does not match the rt= of i={previous}"
276                )
277            }
278            Dkim2Error::PublicKeyFetch { i, selector } => {
279                write!(
280                    f,
281                    "DKIM2-Signature i={i} public key {selector} could not be fetched"
282                )
283            }
284            Dkim2Error::PublicKeyMissing { i, selector } => {
285                write!(
286                    f,
287                    "DKIM2-Signature i={i} public key {selector} does not exist"
288                )
289            }
290            Dkim2Error::PublicKeyMultiple { i, selector } => {
291                write!(
292                    f,
293                    "DKIM2-Signature i={i} public key {selector} has multiple records"
294                )
295            }
296            Dkim2Error::PublicKeySyntax { i, selector } => {
297                write!(
298                    f,
299                    "DKIM2-Signature i={i} public key {selector} has a syntax error"
300                )
301            }
302            Dkim2Error::PublicKeyAlgorithmMismatch { i, selector } => {
303                write!(
304                    f,
305                    "DKIM2-Signature i={i} public key {selector} algorithm mismatch"
306                )
307            }
308            Dkim2Error::PublicKeyRevoked { i, selector } => {
309                write!(
310                    f,
311                    "DKIM2-Signature i={i} public key {selector} has been revoked"
312                )
313            }
314            Dkim2Error::IncorrectSignature { i, selector } => {
315                write!(
316                    f,
317                    "DKIM2-Signature i={i} selector {selector} incorrect signature"
318                )
319            }
320            Dkim2Error::PartialSignatureFailure { i, passed, failed } => {
321                write!(
322                    f,
323                    "DKIM2-Signature i={i} signature {passed} signature {failed} failed"
324                )
325            }
326            Dkim2Error::NoValidAlgorithm(i) => {
327                write!(f, "DKIM2-Signature i={i} has no valid signature algorithms")
328            }
329            Dkim2Error::HeaderHashMismatch { m, hash } => {
330                write!(
331                    f,
332                    "Message-Instance m={m} header hash {} mismatch",
333                    hash.name()
334                )
335            }
336            Dkim2Error::BodyHashMismatch { m, hash } => {
337                write!(
338                    f,
339                    "Message-Instance m={m} body hash {} mismatch",
340                    hash.name()
341                )
342            }
343            Dkim2Error::FromNotAuthentic(i) => {
344                write!(f, "DKIM2-Signature i={i} matching From is not authentic")
345            }
346            Dkim2Error::HeaderNotRepresentable => {
347                write!(
348                    f,
349                    "Changed header field has a bare CR or LF and cannot be recorded in a recipe"
350                )
351            }
352            Dkim2Error::BodyUnreconstructable => {
353                write!(f, "Message body cannot be reconstructed (null body recipe)")
354            }
355            Dkim2Error::RecipeSyntax => write!(f, "Message-Instance recipe syntax error"),
356            Dkim2Error::NotValidlySigned => {
357                write!(f, "Message is not a Validly Signed DKIM2 Message")
358            }
359        }
360    }
361}