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}