aboutsummaryrefslogtreecommitdiffstats
path: root/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js')
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js217
1 files changed, 217 insertions, 0 deletions
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js
new file mode 100644
index 0000000..e3f1011
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js
@@ -0,0 +1,217 @@
+var crypto = require("crypto"),
+ mimelib = require("mimelib-noiconv"),
+ toPunycode = require("./punycode");
+
+/**
+ * @namespace DKIM Signer module
+ * @name dkimsign
+ */
+module.exports.DKIMSign = DKIMSign;
+module.exports.generateDKIMHeader = generateDKIMHeader;
+module.exports.sha256 = sha256;
+
+
+/**
+ * <p>Sign an email with provided DKIM key, uses RSA-SHA256.</p>
+ *
+ * @memberOf dkimsign
+ * @param {String} email Full e-mail source complete with headers and body to sign
+ * @param {Object} options DKIM options
+ * @param {String} [options.headerFieldNames="from:to:cc:subject"] Header fields to sign
+ * @param {String} options.privateKey DKMI private key
+ * @param {String} options.domainName Domain name to use for signing (ie: "domain.com")
+ * @param {String} options.keySelector Selector for the DKMI public key (ie. "dkim" if you have set up a TXT record for "dkim._domainkey.domain.com")
+ *
+ * @return {String} Signed DKIM-Signature header field for prepending
+ */
+function DKIMSign(email, options){
+ options = options || {};
+ email = (email || "").toString("utf-8");
+
+ var match = email.match(/^\r?\n|(?:\r?\n){2}/),
+ headers = match && email.substr(0, match.index) || "",
+ body = match && email.substr(match.index + match[0].length) || email;
+
+ // all listed fields from RFC4871 #5.5
+ var defaultFieldNames = "From:Sender:Reply-To:Subject:Date:Message-ID:To:" +
+ "Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:" +
+ "Content-Description:Resent-Date:Resent-From:Resent-Sender:" +
+ "Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:" +
+ "List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:" +
+ "List-Owner:List-Archive";
+
+ var dkim = generateDKIMHeader(options.domainName, options.keySelector, options.headerFieldNames || defaultFieldNames, headers, body),
+ canonicalizedHeaderData = DKIMCanonicalizer.relaxedHeaders(headers, options.headerFieldNames || defaultFieldNames),
+ canonicalizedDKIMHeader = DKIMCanonicalizer.relaxedHeaderLine(dkim),
+ signer, signature;
+
+ canonicalizedHeaderData.headers += canonicalizedDKIMHeader.key+":"+canonicalizedDKIMHeader.value;
+
+ signer = crypto.createSign("RSA-SHA256");
+ signer.update(canonicalizedHeaderData.headers);
+ signature = signer.sign(options.privateKey, 'base64');
+
+ return dkim + signature.replace(/(.{76}(?!\r?\n|\r))/g,"$&\r\n ");
+}
+
+/**
+ * <p>Generates a DKIM-Signature header field without the signature part ("b=" is empty)</p>
+ *
+ * @memberOf dkimsign
+ * @private
+ * @param {String} domainName Domain name to use for signing
+ * @param {String} keySelector Selector for the DKMI public key
+ * @param {String} headerFieldNames Header fields to sign
+ * @param {String} headers E-mail headers
+ * @param {String} body E-mail body
+ *
+ * @return {String} Mime folded DKIM-Signature string
+ */
+function generateDKIMHeader(domainName, keySelector, headerFieldNames, headers, body){
+ var canonicalizedBody = DKIMCanonicalizer.relaxedBody(body),
+ canonicalizedBodyHash = sha256(canonicalizedBody, "base64"),
+ canonicalizedHeaderData = DKIMCanonicalizer.relaxedHeaders(headers, headerFieldNames),
+ dkim;
+
+ if(hasUTFChars(domainName)){
+ domainName = toPunycode(domainName);
+ }
+
+ dkim = [
+ "v=1",
+ "a=rsa-sha256",
+ "c=relaxed/relaxed",
+ "d="+domainName,
+ "q=dns/txt",
+ "s="+keySelector,
+ "bh="+canonicalizedBodyHash,
+ "h="+canonicalizedHeaderData.fieldNames
+ ].join("; ");
+
+ return mimelib.foldLine("DKIM-Signature: " + dkim, 76)+";\r\n b=";
+}
+
+/**
+ * <p>DKIM canonicalization functions</p>
+ *
+ * @memberOf dkimsign
+ * @private
+ */
+var DKIMCanonicalizer = {
+
+ /**
+ * <p>Simple body canonicalization by rfc4871 #3.4.3</p>
+ *
+ * @param {String} body E-mail body part
+ * @return {String} Canonicalized body
+ */
+ simpleBody: function(body){
+ return (body || "").toString().replace(/(?:\r?\n|\r)*$/, "\r\n");
+ },
+
+ /**
+ * <p>Relaxed body canonicalization by rfc4871 #3.4.4</p>
+ *
+ * @param {String} body E-mail body part
+ * @return {String} Canonicalized body
+ */
+ relaxedBody: function(body){
+ return (body || "").toString().
+ replace(/\r?\n|\r/g, "\n").
+ split("\n").
+ map(function(line){
+ return line.replace(/\s*$/, ""). //rtrim
+ replace(/\s+/g, " "); // only single spaces
+ }).
+ join("\n").
+ replace(/\n*$/, "\n").
+ replace(/\n/g, "\r\n");
+ },
+
+ /**
+ * <p>Relaxed headers canonicalization by rfc4871 #3.4.2 with filtering</p>
+ *
+ * @param {String} body E-mail headers part
+ * @return {String} Canonicalized headers
+ */
+ relaxedHeaders: function(headers, fieldNames){
+ var includedFields = (fieldNames || "").toLowerCase().
+ split(":").
+ map(function(field){
+ return field.trim();
+ }),
+ headerFields = {},
+ headerLines = headers.split(/\r?\n|\r/),
+ line, i;
+
+ // join lines
+ for(i = headerLines.length-1; i>=0; i--){
+ if(i && headerLines[i].match(/^\s/)){
+ headerLines[i-1] += headerLines.splice(i,1);
+ }else{
+ line = DKIMCanonicalizer.relaxedHeaderLine(headerLines[i]);
+
+ // on multiple values, include only the first one (the one in the bottom of the list)
+ if(includedFields.indexOf(line.key) >= 0 && !(line.key in headerFields)){
+ headerFields[line.key] = line.value;
+ }
+ }
+ }
+
+ headers = [];
+ for(i = includedFields.length-1; i>=0; i--){
+ if(!headerFields[includedFields[i]]){
+ includedFields.splice(i,1);
+ }else{
+ headers.unshift(includedFields[i]+":"+headerFields[includedFields[i]]);
+ }
+ }
+
+ return {
+ headers: headers.join("\r\n")+"\r\n",
+ fieldNames: includedFields.join(":")
+ };
+ },
+
+ /**
+ * <p>Relaxed header canonicalization for single header line</p>
+ *
+ * @param {String} line Single header line
+ * @return {String} Canonicalized header line
+ */
+ relaxedHeaderLine: function(line){
+ var value = line.split(":"),
+ key = (value.shift() || "").toLowerCase().trim();
+
+ value = value.join(":").replace(/\s+/g, " ").trim();
+
+ return {key: key, value: value};
+ }
+};
+module.exports.DKIMCanonicalizer = DKIMCanonicalizer;
+
+/**
+ * <p>Generates a SHA-256 hash</p>
+ *
+ * @param {String} str String to be hashed
+ * @param {String} [encoding="hex"] Output encoding
+ * @return {String} SHA-256 hash in the selected output encoding
+ */
+function sha256(str, encoding){
+ var shasum = crypto.createHash('sha256');
+ shasum.update(str);
+ return shasum.digest(encoding || "hex");
+}
+
+
+
+/**
+ * <p>Detects if a string includes unicode symbols</p>
+ *
+ * @param {String} str String to be checked
+ * @return {String} true, if string contains non-ascii symbols
+ */
+function hasUTFChars(str){
+ var rforeign = /[^\u0000-\u007f]/;
+ return !!rforeign.test(str);
+} \ No newline at end of file