mailauth 4.6.4 → 4.6.5

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.6.5](https://github.com/postalsys/mailauth/compare/v4.6.4...v4.6.5) (2024-02-12)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **dkim:** Added new output property mimeStructureStart ([8f25353](https://github.com/postalsys/mailauth/commit/8f25353fa6a67ba3e1f0c5091325007b2434a29d))
9
+
3
10
  ## [4.6.4](https://github.com/postalsys/mailauth/compare/v4.6.3...v4.6.4) (2024-02-05)
4
11
 
5
12
 
@@ -3,6 +3,7 @@
3
3
  'use strict';
4
4
 
5
5
  const crypto = require('crypto');
6
+ const { MimeStructureStartFinder } = require('../mime-structure-start-finder');
6
7
 
7
8
  const CHAR_CR = 0x0d;
8
9
  const CHAR_LF = 0x0a;
@@ -39,9 +40,20 @@ class RelaxedHash {
39
40
  this.maxSizeReached = maxBodyLength === 0;
40
41
 
41
42
  this.emptyLinesQueue = [];
43
+
44
+ this.mimeStructureStartFinder = new MimeStructureStartFinder();
45
+ }
46
+
47
+ setContentType(contentTypeObj) {
48
+ if (/^multipart\//i.test(contentTypeObj.value) && contentTypeObj.params.boundary) {
49
+ this.mimeStructureStartFinder.setBoundary(contentTypeObj.params.boundary);
50
+ }
42
51
  }
43
52
 
44
53
  _updateBodyHash(chunk) {
54
+ // serach through the entire document, not just signed part
55
+ this.mimeStructureStartFinder.update(chunk);
56
+
45
57
  this.canonicalizedLength += chunk.length;
46
58
 
47
59
  if (this.maxSizeReached) {
@@ -270,6 +282,10 @@ class RelaxedHash {
270
282
  // finalize
271
283
  return this.bodyHash.digest(encoding);
272
284
  }
285
+
286
+ getMimeStructureStart() {
287
+ return this.mimeStructureStartFinder.getMimeStructureStart();
288
+ }
273
289
  }
274
290
 
275
291
  module.exports = { RelaxedHash };
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const crypto = require('crypto');
4
+ const { MimeStructureStartFinder } = require('../mime-structure-start-finder');
4
5
 
5
6
  /**
6
7
  * Class for calculating body hash of an email message body stream
@@ -30,9 +31,20 @@ class SimpleHash {
30
31
  this.maxSizeReached = maxBodyLength === 0;
31
32
 
32
33
  this.lastNewline = false;
34
+
35
+ this.mimeStructureStartFinder = new MimeStructureStartFinder();
36
+ }
37
+
38
+ setContentType(contentTypeObj) {
39
+ if (/^multipart\//i.test(contentTypeObj.value) && contentTypeObj.params.boundary) {
40
+ this.mimeStructureStartFinder.setBoundary(contentTypeObj.params.boundary);
41
+ }
33
42
  }
34
43
 
35
44
  _updateBodyHash(chunk) {
45
+ // serach through the entire document, not just signed part
46
+ this.mimeStructureStartFinder.update(chunk);
47
+
36
48
  this.canonicalizedLength += chunk.length;
37
49
 
38
50
  if (this.maxSizeReached) {
@@ -115,6 +127,10 @@ class SimpleHash {
115
127
 
116
128
  return this.bodyHash.digest(encoding);
117
129
  }
130
+
131
+ getMimeStructureStart() {
132
+ return this.mimeStructureStartFinder.getMimeStructureStart();
133
+ }
118
134
  }
119
135
 
120
136
  module.exports = { SimpleHash };
@@ -8,6 +8,7 @@ const { getARChain } = require('../arc');
8
8
  const addressparser = require('nodemailer/lib/addressparser');
9
9
  const crypto = require('crypto');
10
10
  const { v4: uuidv4 } = require('uuid');
11
+ const libmime = require('libmime');
11
12
 
12
13
  class DkimVerifier extends MessageParser {
13
14
  constructor(options) {
@@ -147,6 +148,23 @@ class DkimVerifier extends MessageParser {
147
148
  if (!this.bodyHashes.has(signatureHeader.bodyHashKey)) {
148
149
  this.bodyHashes.set(signatureHeader.bodyHashKey, dkimBody(signatureHeader.bodyCanon, signatureHeader.hashAlgo, signatureHeader.maxBodyLength));
149
150
  }
151
+
152
+ let headersArray = this.headers.parsed;
153
+ const findLastMethod = typeof headersArray.findLast === 'function' ? headersArray.findLast : headersArray.find;
154
+ if (typeof headersArray.findLast !== 'function') {
155
+ headersArray = [].concat(headersArray).reverse();
156
+ }
157
+ const contentTypeHeader = findLastMethod.call(headersArray, header => header.key === 'content-type');
158
+ if (contentTypeHeader) {
159
+ let line = contentTypeHeader.line.toString();
160
+ if (line.indexOf(':') >= 0) {
161
+ line = line.substring(line.indexOf(':') + 1).trim();
162
+ }
163
+ const parsedContentType = libmime.parseHeaderValue(line);
164
+ for (let hasher of this.bodyHashes.values()) {
165
+ hasher.setContentType(parsedContentType);
166
+ }
167
+ }
150
168
  }
151
169
  }
152
170
 
@@ -165,6 +183,7 @@ class DkimVerifier extends MessageParser {
165
183
  // convert bodyHashes from hash objects to base64 strings
166
184
  for (let [key, bodyHash] of this.bodyHashes.entries()) {
167
185
  this.bodyHashes.get(key).hash = bodyHash.digest('base64');
186
+ this.bodyHashes.get(key).mimeStructureStart = bodyHash.getMimeStructureStart();
168
187
  }
169
188
 
170
189
  for (let signatureHeader of this.signatureHeaders) {
@@ -210,7 +229,9 @@ class DkimVerifier extends MessageParser {
210
229
  : false;
211
230
  }
212
231
 
213
- let bodyHash = this.bodyHashes.get(signatureHeader.bodyHashKey)?.hash;
232
+ const bodyHash = this.bodyHashes.get(signatureHeader.bodyHashKey)?.hash;
233
+ const mimeStructureStart = this.bodyHashes.get(signatureHeader.bodyHashKey)?.mimeStructureStart;
234
+
214
235
  if (signatureHeader.parsed?.bh?.value !== bodyHash) {
215
236
  status.result = 'neutral';
216
237
  status.comment = `body hash did not verify`;
@@ -344,6 +365,10 @@ class DkimVerifier extends MessageParser {
344
365
  result.canonBodyLengthLimited = false;
345
366
  }
346
367
 
368
+ if (typeof mimeStructureStart === 'number') {
369
+ result.mimeStructureStart = mimeStructureStart;
370
+ }
371
+
347
372
  if (publicKey) {
348
373
  result.publicKey = publicKey.toString();
349
374
  }
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ class MimeStructureStartFinder {
4
+ constructor() {
5
+ this.byteCache = [];
6
+
7
+ this.matchFound = false;
8
+ this.noMatch = false;
9
+ this.lineStart = -1;
10
+
11
+ this.prevChunks = 0;
12
+
13
+ this.mimeStructureStart = -1;
14
+ }
15
+
16
+ setBoundary(boundary) {
17
+ this.boundary = (boundary || '').toString().trim();
18
+
19
+ this.boundaryBuf = Array.from(Buffer.from(`--${this.boundary}`));
20
+ this.boundaryBufLen = this.boundaryBuf.length;
21
+ }
22
+
23
+ update(chunk) {
24
+ if (this.matchFound || !this.boundary) {
25
+ return;
26
+ }
27
+
28
+ for (let i = 0, bufLen = chunk.length; i < bufLen; i++) {
29
+ let c = chunk[i];
30
+
31
+ // check ending
32
+ if (c === 0x0a || c === 0x0d) {
33
+ if (!this.noMatch && this.byteCache.length === this.boundaryBufLen) {
34
+ // match found
35
+ this.matchFound = true;
36
+ this.mimeStructureStart = this.lineStart;
37
+ break;
38
+ }
39
+ // reset counter
40
+ this.lineStart = -1;
41
+ this.noMatch = false;
42
+ this.byteCache = [];
43
+ continue;
44
+ }
45
+
46
+ if (this.noMatch) {
47
+ // no need to look
48
+ continue;
49
+ }
50
+
51
+ if (this.lineStart < 0) {
52
+ this.lineStart = this.prevChunks + i;
53
+ }
54
+
55
+ if (this.byteCache.length >= this.boundaryBufLen) {
56
+ this.noMatch = true;
57
+ continue;
58
+ }
59
+
60
+ const expectingByte = this.boundaryBuf[this.byteCache.length];
61
+ if (expectingByte !== c) {
62
+ this.noMatch = true;
63
+ continue;
64
+ }
65
+ this.byteCache[this.byteCache.length] = c;
66
+ }
67
+
68
+ this.prevChunks += chunk.length;
69
+ }
70
+
71
+ getMimeStructureStart() {
72
+ if (!this.boundary) {
73
+ return 0;
74
+ }
75
+
76
+ if (!this.matchFound && !this.noMatch && this.byteCache.length === this.boundaryBufLen) {
77
+ this.matchFound = true;
78
+ this.mimeStructureStart = this.lineStart;
79
+ }
80
+
81
+ return this.mimeStructureStart;
82
+ }
83
+ }
84
+
85
+ module.exports = { MimeStructureStartFinder };
package/man/mailauth.1 CHANGED
@@ -1,4 +1,4 @@
1
- .TH "MAILAUTH" "1" "February 2024" "v4.6.4" "Mailauth Help"
1
+ .TH "MAILAUTH" "1" "February 2024" "v4.6.5" "Mailauth Help"
2
2
  .SH "NAME"
3
3
  \fBmailauth\fR
4
4
  .QP
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailauth",
3
- "version": "4.6.4",
3
+ "version": "4.6.5",
4
4
  "description": "Email authentication library for Node.js",
5
5
  "main": "lib/mailauth.js",
6
6
  "scripts": {