mailauth 7.1.1 → 7.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/.c8rc.json +11 -0
  2. package/.prettierignore +3 -0
  3. package/.release-please-manifest.json +1 -1
  4. package/CHANGELOG.md +68 -0
  5. package/README.md +131 -31
  6. package/bin/mailauth.js +62 -0
  7. package/cli.md +150 -8
  8. package/docs/arc.md +62 -54
  9. package/docs/bimi.md +153 -54
  10. package/docs/dkim.md +37 -36
  11. package/docs/dkim2.md +154 -0
  12. package/docs/dmarc.md +4 -1
  13. package/docs/mta-sts.md +12 -9
  14. package/docs/spf.md +14 -13
  15. package/index.d.ts +583 -42
  16. package/lib/arc/index.d.ts +6 -2
  17. package/lib/arc/index.js +45 -15
  18. package/lib/bimi/index.js +362 -102
  19. package/lib/bimi/validate-svg.js +493 -60
  20. package/lib/commands/dkim2-hash.js +25 -0
  21. package/lib/commands/dkim2-sign.js +77 -0
  22. package/lib/commands/dkim2-verify.js +59 -0
  23. package/lib/commands/report.js +10 -35
  24. package/lib/commands/resolver.js +51 -0
  25. package/lib/commands/seal.js +10 -38
  26. package/lib/commands/spf.js +4 -35
  27. package/lib/dkim/dkim-signer.js +50 -30
  28. package/lib/dkim/dkim-verifier.js +79 -27
  29. package/lib/dkim/header/simple.js +2 -2
  30. package/lib/dkim/sign.js +7 -2
  31. package/lib/dkim/syntax.js +11 -0
  32. package/lib/dkim/verify.js +1 -0
  33. package/lib/dkim2/dkim2-parser.js +52 -0
  34. package/lib/dkim2/dkim2-signer.js +334 -0
  35. package/lib/dkim2/dkim2-verifier.js +572 -0
  36. package/lib/dkim2/fields.js +570 -0
  37. package/lib/dkim2/hash.d.ts +3 -0
  38. package/lib/dkim2/hash.js +51 -0
  39. package/lib/dkim2/key.js +203 -0
  40. package/lib/dkim2/message-state.js +181 -0
  41. package/lib/dkim2/recipe.js +134 -0
  42. package/lib/dkim2/sign.d.ts +3 -0
  43. package/lib/dkim2/sign.js +38 -0
  44. package/lib/dkim2/verify.d.ts +3 -0
  45. package/lib/dkim2/verify.js +19 -0
  46. package/lib/dmarc/author-domain.js +270 -5
  47. package/lib/dmarc/get-dmarc-record.js +24 -11
  48. package/lib/dmarc/verify.js +7 -3
  49. package/lib/mailauth.js +57 -14
  50. package/lib/mta-sts.js +37 -11
  51. package/lib/parse-received.js +214 -25
  52. package/lib/spf/index.js +21 -4
  53. package/lib/spf/macro.js +5 -3
  54. package/lib/spf/spf-verify.js +14 -5
  55. package/lib/spf/syntax.js +5 -0
  56. package/lib/tools.js +160 -21
  57. package/package.json +15 -6
  58. package/stryker.config.json +16 -0
package/cli.md CHANGED
@@ -14,6 +14,9 @@ mailauth provides a command-line utility for email authentication, complementing
14
14
  - [Available Commands](#available-commands)
15
15
  - [`report`](#report) — Validate SPF, DKIM, DMARC, ARC, and BIMI
16
16
  - [`sign`](#sign) — Sign an email with DKIM
17
+ - [`dkim2-sign`](#dkim2-sign) — Sign an email with DKIM2 (experimental)
18
+ - [`dkim2-verify`](#dkim2-verify) — Verify the DKIM2 signatures of an email (experimental)
19
+ - [`dkim2-hash`](#dkim2-hash) — Generate the DKIM2 header and body hashes for an email
17
20
  - [`seal`](#seal) — Seal an email with ARC
18
21
  - [`spf`](#spf) — Validate SPF for an IP address and email address
19
22
  - [`vmc`](#vmc) — Validate BIMI VMC logo files
@@ -56,13 +59,19 @@ mailauth spf --help
56
59
 
57
60
  The mailauth CLI offers several commands to perform different email authentication tasks:
58
61
 
59
- 1. [`report`](#report) — Validate SPF, DKIM, DMARC, ARC, and BIMI.
62
+ 1. [`report`](#report) — Validate SPF, DKIM, DMARC, ARC, and BIMI, and optionally DKIM2.
60
63
  2. [`sign`](#sign) — Sign an email with DKIM.
61
- 3. [`seal`](#seal) — Seal an email with ARC.
62
- 4. [`spf`](#spf) — Validate SPF for an IP address and email address.
63
- 5. [`vmc`](#vmc) — Validate BIMI VMC logo files.
64
- 6. [`bodyhash`](#bodyhash) — Generate the body hash value for an email.
65
- 7. [`license`](#license) — Display licenses for mailauth and included modules.
64
+ 3. [`dkim2-sign`](#dkim2-sign) — Sign an email with DKIM2 (experimental).
65
+ 4. [`dkim2-verify`](#dkim2-verify) — Verify the DKIM2 signatures of an email (experimental).
66
+ 5. [`dkim2-hash`](#dkim2-hash) — Generate the DKIM2 header and body hashes for an email.
67
+ 6. [`seal`](#seal) — Seal an email with ARC.
68
+ 7. [`spf`](#spf) — Validate SPF for an IP address and email address.
69
+ 8. [`vmc`](#vmc) — Validate BIMI VMC logo files.
70
+ 9. [`bodyhash`](#bodyhash) — Generate the body hash value for an email.
71
+ 10. [`license`](#license) — Display licenses for mailauth and included modules.
72
+
73
+ > [!WARNING]
74
+ > DKIM2 is not a published RFC yet. The DKIM2 commands are experimental and built against draft-ietf-dkim-dkim2-spec-06, draft-ietf-dkim-dkim2-dns-00 and draft-ietf-dkim-dkim2-bcp-01, see [DKIM2](README.md#dkim2).
66
75
 
67
76
  ### report
68
77
 
@@ -88,6 +97,8 @@ mailauth report [options] [email]
88
97
  - `--max-void-lookups number`, `-z number`: Sets the maximum number of void DNS lookups for SPF checks. Defaults to `2`.
89
98
  - `--strict`: Follows the RFCs exactly instead of the lenient defaults, for example an `rsa-sha1` DKIM signature is reported as `dkim=policy`. See [Strict mode](README.md#strict-mode).
90
99
  - `--reject-rsa-sha1`: Reports an `rsa-sha1` DKIM signature as `dkim=policy` and does not count it for DMARC, as `--strict` does, while every other check keeps the lenient default.
100
+ - `--dkim2`: Verifies DKIM2 header fields as well. The result is in the `dkim2` key of the report and a `dkim2=` entry is added to Authentication-Results. DKIM2 does not take part in DMARC.
101
+ - `--rcpt-to user@example.com`, `-r user@example.com`: RCPT TO address of the delivery, checked against the highest DKIM2-Signature together with `--sender`. Can be repeated. Without it the DKIM2 result has the `rcpt-to-not-checked` warning.
91
102
 
92
103
  #### Example
93
104
 
@@ -156,6 +167,137 @@ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;
156
167
  ...
157
168
  ```
158
169
 
170
+ ### dkim2-sign
171
+
172
+ The `dkim2-sign` command signs an email message with DKIM2. It adds a `DKIM2-Signature` header field, and a `Message-Instance` header field when the message has none yet or has changed since its last instance. Use it as the originator of a message, or as a forwarder that adds its own hop to a message that is already DKIM2 signed.
173
+
174
+ #### Usage
175
+
176
+ ```bash
177
+ mailauth dkim2-sign [options] [email]
178
+ ```
179
+
180
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
181
+
182
+ #### Options
183
+
184
+ - `--private-key /path/to/private.key`, `-k /path/to/private.key`: Path to a private key, RSA (at least 1024 bits) or Ed25519. Repeat it to sign with several keys, each with its own `--selector`.
185
+ - `--selector selector`, `-s selector`: Selector of the private key in the same position. Can be repeated. At most two keys can use the same algorithm.
186
+ - `--domain example.com`, `-d example.com`: Signing domain (`d=` tag). It has to be the MAIL FROM domain or a parent of it.
187
+ - `--mail-from user@example.com`, `-f user@example.com`: MAIL FROM address the message is sent with (`mf=` tag), `<>` for the null sender.
188
+ - `--rcpt-to user@example.net`, `-r user@example.net`: RCPT TO address the message is sent to (`rt=` tag). Can be repeated.
189
+ - `--next-domain example.net`: For a hop across a trust boundary, the domain of the next DKIM2 signature (`nd=` tag). Used instead of `--mail-from` and `--rcpt-to`.
190
+ - `--flag flag`: Flag to set (`f=` tag): `donotmodify`, `donotexplode`, `exploded`, `feedback` or `feedhere`. Can be repeated.
191
+ - `--nonce value`: A value for your own use (`n=` tag), at most 64 characters.
192
+ - `--hash algorithm`: Hash algorithm of a new `Message-Instance`, `sha256` or `sha512`. Can be repeated. Defaults to `sha256`.
193
+ - `--recipe /path/to/recipe.json`: For a forwarder that changed the message, a JSON file with the Recipe that recreates the previous instance (`r=` tag). It is checked before signing, and the command fails if it does not recreate the previous instance.
194
+ - `--time timestamp`, `-t timestamp`: Signing time as a Unix timestamp (`t=` tag). Defaults to the current time.
195
+ - `--headers-only`, `-o`: Outputs only the DKIM2 header fields without the message.
196
+
197
+ Either `--mail-from` and `--rcpt-to`, or `--next-domain`, is required.
198
+
199
+ #### Example
200
+
201
+ ```bash
202
+ mailauth dkim2-sign /path/to/message.eml \
203
+ -k /path/to/rsa.key -s rsa2026 -k /path/to/ed25519.key -s ed2026 \
204
+ -d example.com -f bounces@example.com -r user@example.net
205
+ ```
206
+
207
+ **Sample Output:**
208
+
209
+ ```
210
+ DKIM2-Signature: i=1; m=1; t=1791646747; d=example.com; mf=
211
+ PGJvdW5jZXNAZXhhbXBsZS5jb20+; rt=PHVzZXJAZXhhbXBsZS5uZXQ+; s=rsa2026:rsa-sha256:
212
+ MXp6X81Tuu/cwbedt/kFqdO1sE4yPkfYVnFzfBMO2ZjfAtoXuptS+G79SYEpEaAo
213
+ ...
214
+ Message-Instance: m=1; h=sha256:
215
+ sbKYVMaA7tqVLzGg5HTTU3o95q7ufdnBincKg/jBBQc=:
216
+ GjyEkbey2OupCW5AKJv4dzTPsPHSaZjRDMqUSmhpTyQ=;
217
+ From: ...
218
+ ```
219
+
220
+ A mailing list that replaced the `Subject` header field, added a `List-Id` header field and appended a footer after the first 3 body lines signs its revision with a Recipe:
221
+
222
+ ```json
223
+ { "h": { "subject": [{ "d": ["Original subject"] }], "list-id": [] }, "b": [{ "c": [1, 3] }] }
224
+ ```
225
+
226
+ ```bash
227
+ mailauth dkim2-sign revised.eml -k list.key -s list -d list.example.org \
228
+ -f bounce@list.example.org -r member@example.net --recipe recipe.json
229
+ ```
230
+
231
+ ### dkim2-verify
232
+
233
+ The `dkim2-verify` command verifies only the DKIM2 header fields of an email message and returns a JSON report. See [DKIM2 Result Reference](docs/dkim2.md) for the structure. Use `report --dkim2` to check DKIM2 together with everything else.
234
+
235
+ #### Usage
236
+
237
+ ```bash
238
+ mailauth dkim2-verify [options] [email]
239
+ ```
240
+
241
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
242
+
243
+ #### Options
244
+
245
+ - `--mail-from user@example.com`, `-f user@example.com`: MAIL FROM address the message was delivered with, `<>` for the null sender. Checked against the highest DKIM2-Signature.
246
+ - `--rcpt-to user@example.net`, `-r user@example.net`: RCPT TO address the message was delivered to, checked against the highest DKIM2-Signature. Can be repeated.
247
+ - `--dns-cache /path/to/dns.json`, `-n /path/to/dns.json`: Path to a DNS cache file. When provided, DNS queries use cached responses.
248
+ - `--time timestamp`, `-t timestamp`: Time to verify against as a Unix timestamp. Defaults to the current time.
249
+ - `--max-age seconds`: Seconds after which a signature expires, `0` to not check. Defaults to 14 days.
250
+ - `--max-future seconds`: Seconds a signature timestamp may be ahead of the current time, `0` to not check. Defaults to `300`.
251
+ - `--max-instances number`: The most `Message-Instance`, and the most `DKIM2-Signature`, header fields to process. Defaults to `20`.
252
+ - `--headers-only`, `-o`: Outputs only the Authentication-Results entry (`dkim2=...`) instead of the JSON report.
253
+ - `--verbose`, `-v`: Shows the DNS queries, and the parts of the envelope that were not checked.
254
+
255
+ The chain of custody against the delivery, which is what stops DKIM2 replay, is only checked for the parts of the envelope given with `--mail-from` and `--rcpt-to`. A missing part is reported in `status.warnings`.
256
+
257
+ #### Example
258
+
259
+ ```bash
260
+ mailauth dkim2-verify -f bounces@example.com -r user@example.net -o /path/to/message.eml
261
+ ```
262
+
263
+ **Sample Output:**
264
+
265
+ ```
266
+ dkim2=pass (i=1 example.com pass) header.d=example.com
267
+ ```
268
+
269
+ ### dkim2-hash
270
+
271
+ The `dkim2-hash` command computes the DKIM2 header and body hashes of an email message, in the `algorithm:header-hash:body-hash` form of the `h=` tag of a `Message-Instance` header field. Use it to check what a `Message-Instance` should contain, for example when writing a Recipe.
272
+
273
+ #### Usage
274
+
275
+ ```bash
276
+ mailauth dkim2-hash [options] [email]
277
+ ```
278
+
279
+ - **email**: (Optional) Path to the EML-formatted email message file. If omitted, the email is read from standard input.
280
+
281
+ #### Options
282
+
283
+ - `--algo algorithm`, `-a algorithm`: Hash algorithm, `sha256` or `sha512`. Can be repeated. Defaults to `sha256`.
284
+ - `--verbose`, `-v`: Also lists the header fields that go into the header hash. DKIM2 does not sign trace header fields, `X-` header fields, DKIM1, ARC and Authentication-Results header fields, or its own header fields.
285
+
286
+ #### Example
287
+
288
+ ```bash
289
+ mailauth dkim2-hash -v /path/to/message.eml
290
+ ```
291
+
292
+ **Sample Output:**
293
+
294
+ ```
295
+ Reading email message from /path/to/message.eml
296
+ Hashed header fields: content-type, date, from, message-id, mime-version, subject, to
297
+ --------
298
+ sha256:sbKYVMaA7tqVLzGg5HTTU3o95q7ufdnBincKg/jBBQc=:GjyEkbey2OupCW5AKJv4dzTPsPHSaZjRDMqUSmhpTyQ=
299
+ ```
300
+
159
301
  ### seal
160
302
 
161
303
  The `seal` command adds an ARC (Authenticated Received Chain) seal to an email message.
@@ -185,7 +327,7 @@ mailauth seal [options] [email]
185
327
 
186
328
  By default, `seal` authenticates the message (SPF, DKIM, DMARC, ARC) and embeds the computed results in the `ARC-Authentication-Results` header. If the message was already authenticated elsewhere (for example at an edge MTA) and modified afterwards, you can instead provide the original `Authentication-Results` value yourself. When one of the following options is set, no authentication checks or DNS lookups are performed:
187
329
 
188
- - `--auth-results "authserv-id; spf=pass ..."`: `Authentication-Results` value to embed in the `ARC-Authentication-Results` header (the part after `i=N;`). Used as is.
330
+ - `--auth-results "authserv-id; spf=pass ..."`: `Authentication-Results` value to embed in the `ARC-Authentication-Results` header (the part after `i=N;`). Used as is, except that line breaks are written as CRLF. A multi-line value must be folded: every line after the first starts with a space or a tab. A value with any other line break is refused, as it would start a new header field.
189
331
  - `--auth-results-file /path/to/value.txt`: Same as `--auth-results`, but the value is read from a file. Useful for long or multi-line values. Cannot be combined with `--auth-results`.
190
332
  - `--cv status`: Chain validation status for the `ARC-Seal` header (`cv=` tag): `none`, `pass`, or `fail`. Defaults to `none`.
191
333
  - `--instance number`: ARC instance number (`i=` tag). Defaults to the next instance number based on the existing ARC chain of the message, or `1`.
@@ -413,7 +555,7 @@ The `--dns-cache` option allows you to use a JSON-formatted DNS cache file for t
413
555
 
414
556
  The DNS cache file is a JSON object where:
415
557
 
416
- - **Keys**: Fully qualified domain names (e.g., `"example.com"`).
558
+ - **Keys**: Fully qualified domain names (e.g., `"example.com"`). DKIM and DKIM2 keys are looked up as `selector._domainkey.domain`.
417
559
  - **Values**: Objects with DNS record types as keys (e.g., `"TXT"`, `"MX"`) and their corresponding values.
418
560
 
419
561
  **Example:**
package/docs/arc.md CHANGED
@@ -31,13 +31,13 @@ With `strict: true` the validation and sealing follow RFC 8617 exactly, see [Str
31
31
 
32
32
  ## status Object
33
33
 
34
- | Field | Type | Presence | Description |
35
- | ------------ | --------- | ------------ | ---------------------------------------------------------------- |
36
- | `result` | `string` | Always | ARC result code (see below) |
37
- | `comment` | `string` | On pass/fail | Description or error message |
38
- | `shouldSeal` | `boolean` | On fail | Whether to continue sealing despite failure |
39
- | `policy` | `object` | Policy issue | Policy violation details (e.g., `{"dkim-rules": "weak-key"}`) |
40
- | `smtp` | `object` | Strict mode | `{"remote-ip": "192.0.2.1"}`, the client IP (RFC 8617 section 6) |
34
+ | Field | Type | Presence | Description |
35
+ | ------------ | --------- | ------------ | -------------------------------------------------------------------------------------------------------- |
36
+ | `result` | `string` | Always | ARC result code (see below) |
37
+ | `comment` | `string` | On pass/fail | Description or error message |
38
+ | `shouldSeal` | `boolean` | On fail | Whether the failed chain is sealed with `cv=fail`, false when its newest ARC-Seal already says `cv=fail` |
39
+ | `policy` | `object` | Policy issue | Policy violation details (e.g., `{"dkim-rules": "weak-key"}`) |
40
+ | `smtp` | `object` | Strict mode | `{"remote-ip": "192.0.2.1"}`, the client IP (RFC 8617 section 6) |
41
41
 
42
42
  ## authenticationResults Object
43
43
 
@@ -61,28 +61,29 @@ When ARC validation passes, the `authenticationResults` object contains parsed r
61
61
 
62
62
  ## Comment Values (on failure)
63
63
 
64
- | Comment Pattern | Description |
65
- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
66
- | `"i={n} seal signature validation failed"` | ARC-Seal cryptographic verification failed |
67
- | `"i={n} no valid signature"` | ARC-Message-Signature verification failed |
68
- | `"i={n} multiple {header} values"` | Duplicate ARC headers for same instance |
69
- | `"chain-length={n}"` | Chain exceeds 50 instances |
70
- | `"i={n} expected={m}"` | Missing or out-of-order instance numbers |
71
- | `"i={n} no {header} set"` | Missing required ARC header |
72
- | `"i=1 cv={value}"` | First instance must have `cv=none` |
73
- | `"i={n} cv={value}"` | Non-first instance must have `cv=pass`, and a newest seal with `cv=fail` fails the chain |
74
- | `"i={n} unexpected as h"` | ARC-Seal has an `h=` tag |
75
- | `"i={n} invalid ams h"` | ARC-Message-Signature signed arc-seal (forbidden) |
76
- | `"i={n} as {problem}"`, `"i={n} ams {problem}"` | Strict mode: a tag-list syntax error, a missing required tag or a malformed `t=`/`x=`/`l=` |
77
- | `"invalid as instance"`, `"invalid ams instance"`, `"invalid aar instance"` | Strict mode: an ARC header field whose `i=` is not 1 to 50 written as 1*2DIGIT (first in ARC-Authentication-Results) |
78
- | `"no key for {domain}"` | DNS key not found |
79
- | `"unknown key version for {domain}"` | Key record `v=` is not `DKIM1` |
80
- | `"unknown key type for {domain}"` | Unsupported key type |
81
- | `"invalid public key for {domain}"` | Malformed public key |
82
- | `"inappropriate hash algorithm for {domain}"` | Key record `h=` does not list sha256 |
83
- | `"key not for email for {domain}"` | Key record `s=` does not list email |
84
- | `"inappropriate key algorithm for {domain}"` | The key type does not match the seal's `a=` algorithm |
85
- | `"weak key for {domain}"` | RSA key too short |
64
+ | Comment Pattern | Description |
65
+ | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
66
+ | `"i={n} seal signature validation failed"` | ARC-Seal cryptographic verification failed |
67
+ | `"i={n} no valid signature"` | ARC-Message-Signature verification failed |
68
+ | `"i={n} multiple {header} values"` | Duplicate ARC headers for same instance |
69
+ | `"chain-length={n}"` | Chain exceeds 50 instances |
70
+ | `"i={n} expected={m}"` | Missing or out-of-order instance numbers |
71
+ | `"i={n} no {header} set"` | Missing required ARC header |
72
+ | `"i=1 cv={value}"` | First instance must have `cv=none` |
73
+ | `"i={n} cv={value}"` | Non-first instance must have `cv=pass`, and a newest seal with `cv=fail` fails the chain |
74
+ | `"i={n} unexpected as h"` | ARC-Seal has an `h=` tag |
75
+ | `"i={n} invalid ams h"` | ARC-Message-Signature signed arc-seal (forbidden) |
76
+ | `"i={n} as {problem}"`, `"i={n} ams {problem}"` | Strict mode: a tag-list syntax error, a missing required tag or a malformed `t=`/`x=`/`l=` |
77
+ | `"i={n} as invalid d="`, `"i={n} ams invalid d="` | Both modes: an ARC-Seal or the newest ARC-Message-Signature has a `d=` with a character that can not be in a host name |
78
+ | `"invalid as instance"`, `"invalid ams instance"`, `"invalid aar instance"` | Strict mode: an ARC header field whose `i=` is not 1 to 50 written as 1*2DIGIT (first in ARC-Authentication-Results) |
79
+ | `"no key for {domain}"` | DNS key not found |
80
+ | `"unknown key version for {domain}"` | Key record `v=` is not `DKIM1` |
81
+ | `"unknown key type for {domain}"` | Unsupported key type |
82
+ | `"invalid public key for {domain}"` | Malformed public key |
83
+ | `"inappropriate hash algorithm for {domain}"` | Key record `h=` does not list sha256 |
84
+ | `"key not for email for {domain}"` | Key record `s=` does not list email |
85
+ | `"inappropriate key algorithm for {domain}"` | The key type does not match the seal's `a=` algorithm |
86
+ | `"weak key for {domain}"` | RSA key too short |
86
87
 
87
88
  An ARC-Message-Signature that does not sign the From header field is not valid (RFC 6376 section 6.1.1), which fails the chain with `"i={n} no valid signature"` in both modes. An ARC-Seal `c=` tag is not defined by RFC 8617, so it is ignored like any other unknown tag.
88
89
 
@@ -94,6 +95,7 @@ These rules apply in both modes:
94
95
  - An Ed25519 seal signs the SHA-256 hash of the canonicalized data (RFC 8463).
95
96
  - The seal's `a=` algorithm has to match the key type, and the key record's `v=`, `h=` and `s=` tags are honored.
96
97
  - An ARC-Message-Signature without `c=` uses `simple/simple`, the DKIM-Signature default.
98
+ - A `d=` with a character that can not be in a host name (such as `/`, `?`, `#`, `@` or `:`) in any ARC-Seal or in the newest ARC-Message-Signature fails the chain, and its key is not looked up.
97
99
 
98
100
  Differences between the modes:
99
101
 
@@ -115,12 +117,12 @@ The position of `i=` in an ARC-Seal or ARC-Message-Signature is not checked in e
115
117
 
116
118
  In the default mode `warnings` lists what was accepted although strict mode would have rejected it. It is only present when there is something to report and the chain did not fail, and it is never written into the Authentication-Results text.
117
119
 
118
- | Warning | Meaning |
119
- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
120
- | `arc-tag-syntax` | An ARC-Seal or the newest ARC-Message-Signature has a tag-list syntax error or an upper case tag name |
121
- | `arc-instance-syntax` | An `i=` value is not valid RFC 8617 syntax, or an ARC header field was ignored because of its `i=` |
122
- | `ams-c-default` | The ARC-Message-Signature has no `c=` and only verified with `relaxed/relaxed` |
123
- | `key-syntax`, `key-v-syntax`, `key-type-inferred` | Key record leniencies, see the [DKIM result reference](dkim.md#warnings) |
120
+ | Warning | Meaning |
121
+ | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
122
+ | `arc-tag-syntax` | An ARC-Seal or the newest ARC-Message-Signature has a tag-list syntax error or an upper case tag name |
123
+ | `arc-instance-syntax` | An `i=` value is not valid RFC 8617 syntax, or an ARC header field was ignored because of its `i=` |
124
+ | `ams-c-default` | The ARC-Message-Signature has no `c=` and only verified with `relaxed/relaxed` |
125
+ | `key-syntax`, `key-v-syntax`, `key-type-inferred`, `key-ed25519-spki` | Key record leniencies, see the [DKIM result reference](dkim.md#warnings) |
124
126
 
125
127
  ## Sealing
126
128
 
@@ -131,31 +133,37 @@ In the default mode `warnings` lists what was accepted although strict mode woul
131
133
  | `headers` | `string[]` | ARC headers to prepend: `[ARC-Seal, ARC-Message-Signature, ARC-Authentication-Results]`, or empty |
132
134
  | `errors` | `array` | `{ err, type, selector, signingDomain }` entries saying why no set was created |
133
135
  | `warnings` | `string[]` | What the default mode sealed that strict mode would have refused |
136
+ | `instance` | `number` | The instance (`i=`) of the created set, not set when no set was created |
134
137
 
135
138
  Seal options:
136
139
 
137
- | Option | Type | Description |
138
- | --------------- | ------------------ | ---------------------------------------------------------------------------------------------------------- |
139
- | `signingDomain` | `string` | `d=` value |
140
- | `selector` | `string` | `s=` value |
141
- | `privateKey` | `string\|Buffer` | RSA or Ed25519 private key |
142
- | `algorithm` | `string` | `rsa-sha256` or `ed25519-sha256`, follows the key type when not set |
143
- | `authResults` | `string` | The Authentication-Results payload for the ARC-Authentication-Results header |
144
- | `cv` | `string` | `none`, `pass` or `fail`. Defaults to `none` for the first set, required for later ones |
145
- | `i` | `number` | Instance, defaults to one more than the highest instance on the message |
146
- | `headerList` | `string\|string[]` | Header fields for the ARC-Message-Signature. ARC header fields and Authentication-Results are never signed |
147
- | `signTime` | `Date` | Signing time |
148
- | `strict` | `boolean` | Follow the RFCs exactly |
140
+ | Option | Type | Description |
141
+ | --------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
142
+ | `signingDomain` | `string` | `d=` value |
143
+ | `selector` | `string` | `s=` value |
144
+ | `privateKey` | `string\|Buffer` | RSA or Ed25519 private key |
145
+ | `algorithm` | `string` | `rsa-sha256` or `ed25519-sha256`, follows the key type when not set |
146
+ | `authResults` | `string` | The Authentication-Results payload for the ARC-Authentication-Results header (required). Line breaks only as CRLF folding |
147
+ | `cv` | `string` | `none`, `pass` or `fail`. Defaults to `none` for the first set, required for later ones |
148
+ | `i` | `number` | Instance, defaults to one more than the highest instance on the message |
149
+ | `headerList` | `string\|string[]` | Header fields for the ARC-Message-Signature. ARC header fields and Authentication-Results are never signed |
150
+ | `signTime` | `Date` | Signing time |
151
+ | `strict` | `boolean` | Follow the RFCs exactly |
152
+
153
+ `authenticate()` always computes `authResults`, `cv` and `i` for the message it seals and ignores these values in its `seal` option. The seal options object is never modified, so one object can be shared by concurrent calls.
154
+
155
+ A name repeated in `headerList` is over-signed in the ARC-Message-Signature, as with `dkimSign`.
149
156
 
150
157
  No ARC set is created (in either mode) when:
151
158
 
152
- | Error code | Reason |
153
- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
154
- | `EARCCHAINFAILED` | The newest ARC-Seal on the message already has `cv=fail` (RFC 8617 section 5.1 step 2) |
155
- | `EINVALIDINSTANCE` | The instance is not 1 to 50, already exists on the message, or the chain already has 50 sets |
156
- | `EINVALIDCV` | `cv` is not `none`, `pass` or `fail`, is missing for an instance above 1, or is `pass` while the chain on the message could not be parsed |
157
- | `EINVALIDALGO` | The algorithm is not `rsa-sha256` or `ed25519-sha256` |
158
- | `EINVALIDTYPE` | The algorithm does not match the key type |
159
+ | Error code | Reason |
160
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
161
+ | `EARCCHAINFAILED` | The newest ARC-Seal on the message already has `cv=fail` (RFC 8617 section 5.1 step 2) |
162
+ | `EINVALIDINSTANCE` | The instance is not 1 to 50, already exists on the message, or the chain already has 50 sets |
163
+ | `EINVALIDCV` | `cv` is not `none`, `pass` or `fail`, is missing for an instance above 1, or is `pass` while the chain on the message could not be parsed |
164
+ | `EINVALIDALGO` | The algorithm is not `rsa-sha256` or `ed25519-sha256` |
165
+ | `EINVALIDAUTHRESULTS` | `authResults` is missing or empty, or has a line break that is not folding (CRLF followed by a space or tab and more text) |
166
+ | `EINVALIDTYPE` | The algorithm does not match the key type |
159
167
 
160
168
  What only strict mode refuses (the default mode seals and adds the warning):
161
169