summaryrefslogtreecommitdiffstats
path: root/comm/mail/extensions/openpgp/content/modules/armor.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/extensions/openpgp/content/modules/armor.jsm')
-rw-r--r--comm/mail/extensions/openpgp/content/modules/armor.jsm367
1 files changed, 367 insertions, 0 deletions
diff --git a/comm/mail/extensions/openpgp/content/modules/armor.jsm b/comm/mail/extensions/openpgp/content/modules/armor.jsm
new file mode 100644
index 0000000000..023a68158c
--- /dev/null
+++ b/comm/mail/extensions/openpgp/content/modules/armor.jsm
@@ -0,0 +1,367 @@
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["EnigmailArmor"];
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const lazy = {};
+
+XPCOMUtils.defineLazyModuleGetters(lazy, {
+ EnigmailConstants: "chrome://openpgp/content/modules/constants.jsm",
+ EnigmailLog: "chrome://openpgp/content/modules/log.jsm",
+});
+
+// Locates STRing in TEXT occurring only at the beginning of a line
+function indexOfArmorDelimiter(text, str, offset) {
+ let currentOffset = offset;
+
+ while (currentOffset < text.length) {
+ let loc = text.indexOf(str, currentOffset);
+
+ if (loc === -1 || loc === 0 || text.charAt(loc - 1) == "\n") {
+ return loc;
+ }
+
+ currentOffset = loc + str.length;
+ }
+
+ return -1;
+}
+
+function searchBlankLine(str, then) {
+ var offset = str.search(/\n\s*\r?\n/);
+ if (offset === -1) {
+ return "";
+ }
+ return then(offset);
+}
+
+function indexOfNewline(str, off, then) {
+ var offset = str.indexOf("\n", off);
+ if (offset === -1) {
+ return "";
+ }
+ return then(offset);
+}
+
+var EnigmailArmor = {
+ /**
+ * Locates offsets bracketing PGP armored block in text,
+ * starting from given offset, and returns block type string.
+ *
+ * @param text: String - ASCII armored text
+ * @param offset: Number - offset to start looking for block
+ * @param indentStr: String - prefix that is used for all lines (such as "> ")
+ * @param beginIndexObj: Object - o.value will contain offset of first character of block
+ * @param endIndexObj: Object - o.value will contain offset of last character of block (newline)
+ * @param indentStrObj: Object - o.value will contain indent of 1st line
+ *
+ * @returns String - type of block found (e.g. MESSAGE, PUBLIC KEY)
+ * If no block is found, an empty String is returned;
+ */
+ locateArmoredBlock(
+ text,
+ offset,
+ indentStr,
+ beginIndexObj,
+ endIndexObj,
+ indentStrObj
+ ) {
+ lazy.EnigmailLog.DEBUG(
+ "armor.jsm: Enigmail.locateArmoredBlock: " +
+ offset +
+ ", '" +
+ indentStr +
+ "'\n"
+ );
+
+ beginIndexObj.value = -1;
+ endIndexObj.value = -1;
+
+ var beginIndex = indexOfArmorDelimiter(
+ text,
+ indentStr + "-----BEGIN PGP ",
+ offset
+ );
+
+ if (beginIndex == -1) {
+ var blockStart = text.indexOf("-----BEGIN PGP ");
+ if (blockStart >= 0) {
+ var indentStart = text.search(/\n.*-----BEGIN PGP /) + 1;
+ indentStrObj.value = text.substring(indentStart, blockStart);
+ indentStr = indentStrObj.value;
+ beginIndex = indexOfArmorDelimiter(
+ text,
+ indentStr + "-----BEGIN PGP ",
+ offset
+ );
+ }
+ }
+
+ if (beginIndex == -1) {
+ return "";
+ }
+
+ // Locate newline at end of armor header
+ offset = text.indexOf("\n", beginIndex);
+
+ if (offset == -1) {
+ return "";
+ }
+
+ var endIndex = indexOfArmorDelimiter(
+ text,
+ indentStr + "-----END PGP ",
+ offset
+ );
+
+ if (endIndex == -1) {
+ return "";
+ }
+
+ // Locate newline at end of PGP block
+ endIndex = text.indexOf("\n", endIndex);
+
+ if (endIndex == -1) {
+ // No terminating newline
+ endIndex = text.length - 1;
+ }
+
+ var blockHeader = text.substr(beginIndex, offset - beginIndex + 1);
+
+ let escapedIndentStr = indentStr.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
+ var blockRegex = new RegExp(
+ "^" + escapedIndentStr + "-----BEGIN PGP (.{1,30})-----\\s*\\r?\\n"
+ );
+
+ var matches = blockHeader.match(blockRegex);
+
+ var blockType = "";
+ if (matches && matches.length > 1) {
+ blockType = matches[1];
+ lazy.EnigmailLog.DEBUG(
+ "armor.jsm: Enigmail.locateArmoredBlock: blockType=" + blockType + "\n"
+ );
+ }
+
+ if (blockType == "UNVERIFIED MESSAGE") {
+ // Skip any unverified message block
+ return EnigmailArmor.locateArmoredBlock(
+ text,
+ endIndex + 1,
+ indentStr,
+ beginIndexObj,
+ endIndexObj,
+ indentStrObj
+ );
+ }
+
+ beginIndexObj.value = beginIndex;
+ endIndexObj.value = endIndex;
+
+ return blockType;
+ },
+
+ /**
+ * locateArmoredBlocks returns an array of ASCII Armor block positions
+ *
+ * @param text: String - text containing ASCII armored block(s)
+ *
+ * @returns Array of objects with the following structure:
+ * obj.begin: Number
+ * obj.end: Number
+ * obj.indent: String
+ * obj.blocktype: String
+ *
+ * if no block was found, an empty array is returned
+ */
+ locateArmoredBlocks(text) {
+ var beginObj = {};
+ var endObj = {};
+ var indentStrObj = {};
+ var blocks = [];
+ var i = 0;
+ var b;
+
+ while (
+ (b = EnigmailArmor.locateArmoredBlock(
+ text,
+ i,
+ "",
+ beginObj,
+ endObj,
+ indentStrObj
+ )) !== ""
+ ) {
+ blocks.push({
+ begin: beginObj.value,
+ end: endObj.value,
+ indent: indentStrObj.value ? indentStrObj.value : "",
+ blocktype: b,
+ });
+
+ i = endObj.value;
+ }
+
+ lazy.EnigmailLog.DEBUG(
+ "armor.jsm: locateArmorBlocks: Found " + blocks.length + " Blocks\n"
+ );
+ return blocks;
+ },
+
+ extractSignaturePart(signatureBlock, part) {
+ lazy.EnigmailLog.DEBUG(
+ "armor.jsm: Enigmail.extractSignaturePart: part=" + part + "\n"
+ );
+
+ return searchBlankLine(signatureBlock, function (offset) {
+ return indexOfNewline(signatureBlock, offset + 1, function (offset) {
+ var beginIndex = signatureBlock.indexOf(
+ "-----BEGIN PGP SIGNATURE-----",
+ offset + 1
+ );
+ if (beginIndex == -1) {
+ return "";
+ }
+
+ if (part === lazy.EnigmailConstants.SIGNATURE_TEXT) {
+ return signatureBlock
+ .substr(offset + 1, beginIndex - offset - 1)
+ .replace(/^- -/, "-")
+ .replace(/\n- -/g, "\n-")
+ .replace(/\r- -/g, "\r-");
+ }
+
+ return indexOfNewline(signatureBlock, beginIndex, function (offset) {
+ var endIndex = signatureBlock.indexOf(
+ "-----END PGP SIGNATURE-----",
+ offset
+ );
+ if (endIndex == -1) {
+ return "";
+ }
+
+ var signBlock = signatureBlock.substr(offset, endIndex - offset);
+
+ return searchBlankLine(signBlock, function (armorIndex) {
+ if (part == lazy.EnigmailConstants.SIGNATURE_HEADERS) {
+ return signBlock.substr(1, armorIndex);
+ }
+
+ return indexOfNewline(
+ signBlock,
+ armorIndex + 1,
+ function (armorIndex) {
+ if (part == lazy.EnigmailConstants.SIGNATURE_ARMOR) {
+ return signBlock
+ .substr(armorIndex, endIndex - armorIndex)
+ .replace(/\s*/g, "");
+ }
+ return "";
+ }
+ );
+ });
+ });
+ });
+ });
+ },
+
+ /**
+ * Remove all headers from an OpenPGP Armored message and replace them
+ * with a set of new headers.
+ *
+ * @param armorText: String - ASCII armored message
+ * @param headers: Object - key/value pairs of new headers to insert
+ *
+ * @returns String - new armored message
+ */
+ replaceArmorHeaders(armorText, headers) {
+ let text = armorText.replace(/\r\n/g, "\n");
+ let i = text.search(/\n/);
+
+ if (i < 0) {
+ return armorText;
+ }
+ let m = text.substr(0, i + 1);
+
+ for (let j in headers) {
+ m += j + ": " + headers[j] + "\n";
+ }
+
+ i = text.search(/\n\n/);
+ if (i < 0) {
+ return armorText;
+ }
+ m += text.substr(i + 1);
+
+ return m;
+ },
+
+ /**
+ * Get a list of all headers found in an armor message
+ *
+ * @param text String - ASCII armored message
+ *
+ * @returns Object: key/value pairs of headers. All keys are in lowercase.
+ */
+ getArmorHeaders(text) {
+ let headers = {};
+ let b = this.locateArmoredBlocks(text);
+
+ if (b.length === 0) {
+ return headers;
+ }
+
+ let msg = text.substr(b[0].begin);
+
+ // Escape regex chars.
+ let indent = b[0].indent.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&");
+ let lx = new RegExp("\\n" + indent + "\\r?\\n");
+ let hdrEnd = msg.search(lx);
+ if (hdrEnd < 0) {
+ return headers;
+ }
+
+ let lines = msg.substr(0, hdrEnd).split(/\r?\n/);
+
+ let rx = new RegExp("^" + b[0].indent + "([^: ]+)(: )(.*)");
+ // skip 1st line (ARMOR-line)
+ for (let i = 1; i < lines.length; i++) {
+ let m = lines[i].match(rx);
+ if (m && m.length >= 4) {
+ headers[m[1].toLowerCase()] = m[3];
+ }
+ }
+
+ return headers;
+ },
+
+ /**
+ * Split armored blocks into an array of strings
+ */
+ splitArmoredBlocks(keyBlockStr) {
+ let myRe = /-----BEGIN PGP (PUBLIC|PRIVATE) KEY BLOCK-----/g;
+ let myArray;
+ let retArr = [];
+ let startIndex = -1;
+ while ((myArray = myRe.exec(keyBlockStr)) !== null) {
+ if (startIndex >= 0) {
+ let s = keyBlockStr.substring(startIndex, myArray.index);
+ retArr.push(s);
+ }
+ startIndex = myArray.index;
+ }
+
+ retArr.push(keyBlockStr.substring(startIndex));
+
+ return retArr;
+ },
+};