source: node_modules/nodemailer/lib/dkim/sign.js

main
Last change on this file was 81bc7da, checked in by Klimentina Efremova <klimentina08642@…>, 3 months ago

Initial commit

  • Property mode set to 100644
File size: 3.6 KB
Line 
1'use strict';
2
3const punycode = require('../punycode');
4const mimeFuncs = require('../mime-funcs');
5const crypto = require('crypto');
6
7/**
8 * Returns DKIM signature header line
9 *
10 * @param {Object} headers Parsed headers object from MessageParser
11 * @param {String} bodyHash Base64 encoded hash of the message
12 * @param {Object} options DKIM options
13 * @param {String} options.domainName Domain name to be signed for
14 * @param {String} options.keySelector DKIM key selector to use
15 * @param {String} options.privateKey DKIM private key to use
16 * @return {String} Complete header line
17 */
18
19module.exports = (headers, hashAlgo, bodyHash, options) => {
20 options = options || {};
21
22 // all listed fields from RFC4871 #5.5
23 let defaultFieldNames =
24 'From:Sender:Reply-To:Subject:Date:Message-ID:To:' +
25 'Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:' +
26 'Content-Description:Resent-Date:Resent-From:Resent-Sender:' +
27 'Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:' +
28 'List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:' +
29 'List-Owner:List-Archive';
30
31 let fieldNames = options.headerFieldNames || defaultFieldNames;
32
33 let canonicalizedHeaderData = relaxedHeaders(headers, fieldNames, options.skipFields);
34 let dkimHeader = generateDKIMHeader(options.domainName, options.keySelector, canonicalizedHeaderData.fieldNames, hashAlgo, bodyHash);
35
36 let signer, signature;
37
38 canonicalizedHeaderData.headers += 'dkim-signature:' + relaxedHeaderLine(dkimHeader);
39
40 signer = crypto.createSign(('rsa-' + hashAlgo).toUpperCase());
41 signer.update(canonicalizedHeaderData.headers);
42 try {
43 signature = signer.sign(options.privateKey, 'base64');
44 } catch (E) {
45 return false;
46 }
47
48 return dkimHeader + signature.replace(/(^.{73}|.{75}(?!\r?\n|\r))/g, '$&\r\n ').trim();
49};
50
51module.exports.relaxedHeaders = relaxedHeaders;
52
53function generateDKIMHeader(domainName, keySelector, fieldNames, hashAlgo, bodyHash) {
54 let dkim = [
55 'v=1',
56 'a=rsa-' + hashAlgo,
57 'c=relaxed/relaxed',
58 'd=' + punycode.toASCII(domainName),
59 'q=dns/txt',
60 's=' + keySelector,
61 'bh=' + bodyHash,
62 'h=' + fieldNames
63 ].join('; ');
64
65 return mimeFuncs.foldLines('DKIM-Signature: ' + dkim, 76) + ';\r\n b=';
66}
67
68function relaxedHeaders(headers, fieldNames, skipFields) {
69 let includedFields = new Set();
70 let skip = new Set();
71 let headerFields = new Map();
72
73 (skipFields || '')
74 .toLowerCase()
75 .split(':')
76 .forEach(field => {
77 skip.add(field.trim());
78 });
79
80 (fieldNames || '')
81 .toLowerCase()
82 .split(':')
83 .filter(field => !skip.has(field.trim()))
84 .forEach(field => {
85 includedFields.add(field.trim());
86 });
87
88 for (let i = headers.length - 1; i >= 0; i--) {
89 let line = headers[i];
90 // only include the first value from bottom to top
91 if (includedFields.has(line.key) && !headerFields.has(line.key)) {
92 headerFields.set(line.key, relaxedHeaderLine(line.line));
93 }
94 }
95
96 let headersList = [];
97 let fields = [];
98 includedFields.forEach(field => {
99 if (headerFields.has(field)) {
100 fields.push(field);
101 headersList.push(field + ':' + headerFields.get(field));
102 }
103 });
104
105 return {
106 headers: headersList.join('\r\n') + '\r\n',
107 fieldNames: fields.join(':')
108 };
109}
110
111function relaxedHeaderLine(line) {
112 return line
113 .substr(line.indexOf(':') + 1)
114 .replace(/\r?\n/g, '')
115 .replace(/\s+/g, ' ')
116 .trim();
117}
Note: See TracBrowser for help on using the repository browser.