Bug 1603782 - Implement OpenPGP message creation that uses separate MIME layers for signature and encryption. r=PatrickBrunschwig DONTBUILD

Differential Revision: https://phabricator.services.mozilla.com/D83698

--HG--
extra : amend_source : 3bdec5bb91f5ba3add4a7d20d80dbfb943f2d44c
This commit is contained in:
Kai Engert 2020-07-11 13:46:33 +02:00
Родитель 11ea2ce6c2
Коммит 1f516189db
8 изменённых файлов: 399 добавлений и 480 удалений

Просмотреть файл

@ -32,6 +32,8 @@ pref("mail.openpgp.allow_external_gnupg", false);
pref("mail.openpgp.alternative_gpg_path", "");
// The hexadecimal OpenPGP key ID used for an identity.
pref("mail.identity.default.openpgp_key_id", "");
// If true, then openpgp_key_id is managed externally by GnuPG
pref("mail.identity.default.is_gnupg_key_id", false);
// When sending, encrypt to this additional key. Not available in release channel builds.
pref("mail.openpgp.debug.extra_encryption_key", "");

Просмотреть файл

@ -8,6 +8,9 @@ var { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm");
var { GPGMELibLoader } = ChromeUtils.import(
"chrome://openpgp/content/modules/GPGMELib.jsm"
);
var { EnigmailConstants } = ChromeUtils.import(
"chrome://openpgp/content/modules/constants.jsm"
);
var GPGMELib;
@ -110,4 +113,73 @@ var GPGME = {
return result;
},
async signDetached(plaintext, args, resultStatus) {
resultStatus.exitCode = -1;
resultStatus.statusFlags = 0;
resultStatus.statusMsg = "";
resultStatus.errorMsg = "";
if (args.encrypt || !args.sign || !args.sigTypeDetached) {
throw new Error("invalid parameters, neither encrypt nor sign");
}
let result = null;
//args.sender must be keyId
let keyId = args.sender.replace(/^0x/, "").toUpperCase();
let ctx = new GPGMELib.gpgme_ctx_t();
if (GPGMELib.gpgme_new(ctx.address())) {
throw new Error("gpgme_new failed");
}
GPGMELib.gpgme_set_armor(ctx, 1);
GPGMELib.gpgme_set_textmode(ctx, 1);
let keyHandle = new GPGMELib.gpgme_key_t();
if (!GPGMELib.gpgme_get_key(ctx, keyId, keyHandle.address(), 1)) {
if (!GPGMELib.gpgme_signers_add(ctx, keyHandle)) {
var tmp_array = ctypes.char.array()(plaintext);
let data_plaintext = new GPGMELib.gpgme_data_t();
if (
!GPGMELib.gpgme_data_new_from_mem(
data_plaintext.address(),
tmp_array,
tmp_array.length,
0
)
) {
let data_signed = new GPGMELib.gpgme_data_t();
if (!GPGMELib.gpgme_data_new(data_signed.address())) {
let exitCode = GPGMELib.gpgme_op_sign(
ctx,
data_plaintext,
data_signed,
GPGMELib.GPGME_SIG_MODE_DETACH
);
if (exitCode != GPGMELib.GPG_ERR_NO_ERROR) {
GPGMELib.gpgme_data_release(data_signed);
} else {
let result_len = new ctypes.size_t();
let result_buf = GPGMELib.gpgme_data_release_and_get_mem(
data_signed,
result_len.address()
);
if (!result_buf.isNull()) {
let unwrapped = ctypes.cast(
result_buf,
ctypes.char.array(result_len.value).ptr
).contents;
result = unwrapped.readString();
resultStatus.exitCode = 0;
resultStatus.statusFlags |= EnigmailConstants.SIG_CREATED;
GPGMELib.gpgme_free(result_buf);
}
}
}
}
}
GPGMELib.gpgme_key_release(keyHandle);
}
GPGMELib.gpgme_release(ctx);
return result;
},
};

Просмотреть файл

@ -6,6 +6,7 @@ const EXPORTED_SYMBOLS = ["GPGMELibLoader"];
var { ctypes } = ChromeUtils.import("resource://gre/modules/ctypes.jsm");
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var systemOS = Services.appinfo.OS.toLowerCase();
var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
var abi = ctypes.default_abi;
@ -38,7 +39,7 @@ function tryLoadGPGME(name, suffix) {
} catch (e) {}
}
if (!libgpgme && Services.appinfo.OS !== "WINNT") {
if (!libgpgme && systemOS !== "winnt") {
// try specific additional directories
for (let tryPath of ADDITIONAL_LIB_PATHS) {
@ -56,7 +57,7 @@ function tryLoadGPGME(name, suffix) {
if (libgpgme) {
console.debug(
"Successfully loaded OpenPGP library " +
"Successfully loaded optional OpenPGP library " +
filename +
" from " +
loadFromInfo
@ -66,14 +67,25 @@ function tryLoadGPGME(name, suffix) {
function loadExternalGPGMELib() {
if (!libgpgme) {
// Try loading libgpgme.so, libgpgme.dylib, or gpgme.dll first
if (systemOS === "winnt") {
tryLoadGPGME("libgpgme-11", "");
let gpgmeLibName = "gpgme";
if (Services.appinfo.OS === "WINNT") {
gpgmeLibName = "libgpgme-11";
if (!libgpgme) {
tryLoadGPGME("gpgme-11", "");
}
}
if (!libgpgme) {
tryLoadGPGME("gpgme", "");
}
if (!libgpgme) {
tryLoadGPGME("gpgme", ".11");
}
if (!libgpgme) {
tryLoadGPGME("gpgme.11");
}
tryLoadGPGME(gpgmeLibName, "");
}
return !!libgpgme;
@ -102,8 +114,7 @@ const gpgme_sig_notation_flags_t = ctypes.unsigned_int;
const gpgme_export_mode_t = ctypes.unsigned_int;
const gpgme_decrypt_flags_t = ctypes.unsigned_int;
const gpgme_data_encoding_t = ctypes.unsigned_int;
gpgme_data_t;
const gpgme_sig_mode_t = ctypes.int; // it's an enum, risk of wrong type.
let _gpgme_subkey = ctypes.StructType("_gpgme_subkey");
_gpgme_subkey.define([
@ -474,6 +485,42 @@ function enableGPGMELibJS() {
gpgme_data_encoding_t
),
gpgme_op_sign: libgpgme.declare(
"gpgme_op_sign",
abi,
gpgme_error_t,
gpgme_ctx_t,
gpgme_data_t,
gpgme_data_t,
gpgme_sig_mode_t
),
gpgme_signers_add: libgpgme.declare(
"gpgme_signers_add",
abi,
gpgme_error_t,
gpgme_ctx_t,
gpgme_key_t
),
gpgme_get_key: libgpgme.declare(
"gpgme_get_key",
abi,
gpgme_error_t,
gpgme_ctx_t,
ctypes.char.ptr,
gpgme_key_t.ptr,
ctypes.int
),
gpgme_set_textmode: libgpgme.declare(
"gpgme_set_textmode",
abi,
ctypes.void_t,
gpgme_ctx_t,
ctypes.int
),
gpgme_error_t,
gpgme_ctx_t,
gpgme_data_t,
@ -499,5 +546,6 @@ function enableGPGMELibJS() {
GPGME_EXPORT_MODE_SECRET: 16,
GPGME_DECRYPT_UNWRAP: 128,
GPGME_DATA_ENCODING_ARMOR: 3,
GPGME_SIG_MODE_DETACH: 1,
};
}

Просмотреть файл

@ -1822,6 +1822,25 @@ var RNP = {
},
async encryptAndOrSign(plaintext, args, resultStatus) {
if (args.sign && args.senderKeyIsExternal) {
if (!GPGME.allDependenciesLoaded()) {
throw new Error(
"invalid configuration, request to use external GnuPG key, but GPGME isn't working"
);
}
if (args.encrypt) {
throw new Error(
"internal error, unexpected request to sign and encrypt in a single step with external GnuPG key configuration"
);
}
if (!args.sigTypeDetached || args.sigTypeClear) {
throw new Error(
"unexpected signing request with external GnuPG key configuration"
);
}
return GPGME.signDetached(plaintext, args, resultStatus);
}
resultStatus.exitCode = -1;
resultStatus.statusFlags = 0;
resultStatus.statusMsg = "";
@ -1895,21 +1914,26 @@ var RNP = {
if (!senderKey || senderKey.isNull()) {
return null;
}
let isPersonal = false;
let senderKeySecretAvailable = this.getSecretAvailableFromHandle(
senderKey
);
if (senderKeySecretAvailable) {
let senderFpr = this.getFingerprintFromHandle(senderKey);
isPersonal = await PgpSqliteDb2.isAcceptedAsPersonalKey(senderFpr);
}
if (!isPersonal) {
throw new Error(
"configured sender key " +
args.sender +
" isn't accepted as a personal key"
// Manually configured external key overrides the check for
// a valid personal key.
if (!args.senderKeyIsExternal) {
let isPersonal = false;
let senderKeySecretAvailable = this.getSecretAvailableFromHandle(
senderKey
);
if (senderKeySecretAvailable) {
let senderFpr = this.getFingerprintFromHandle(senderKey);
isPersonal = await PgpSqliteDb2.isAcceptedAsPersonalKey(senderFpr);
}
if (!isPersonal) {
throw new Error(
"configured sender key " +
args.sender +
" isn't accepted as a personal key"
);
}
}
if (args.encryptToSender) {
this.addSuitableEncryptKey(senderKey, op);
}

Просмотреть файл

@ -78,6 +78,8 @@ var EnigmailConstants = {
SEND_ATTACHMENT: 0x0800, // 2048
ENCRYPT_HEADERS: 0x1000, // 4096
SEND_VERBATIM: 0x2000, // 8192
SEND_TWO_MIME_LAYERS: 0x4000, // 16384
SEND_SENDER_KEY_EXTERNAL: 0x8000, // 32768
/* Status flags */
GOOD_SIGNATURE: 0x00000001,

Просмотреть файл

@ -20,21 +20,12 @@ const { EnigmailLog } = ChromeUtils.import(
const { EnigmailPrefs } = ChromeUtils.import(
"chrome://openpgp/content/modules/prefs.jsm"
);
const { EnigmailApp } = ChromeUtils.import(
"chrome://openpgp/content/modules/app.jsm"
);
const { EnigmailDialog } = ChromeUtils.import(
"chrome://openpgp/content/modules/dialog.jsm"
);
const { EnigmailGpg } = ChromeUtils.import(
"chrome://openpgp/content/modules/gpg.jsm"
);
const { EnigmailErrorHandling } = ChromeUtils.import(
"chrome://openpgp/content/modules/errorHandling.jsm"
);
const { EnigmailFiles } = ChromeUtils.import(
"chrome://openpgp/content/modules/files.jsm"
);
const { EnigmailFuncs } = ChromeUtils.import(
"chrome://openpgp/content/modules/funcs.jsm"
);
@ -65,160 +56,8 @@ const gMimeHashAlgorithms = [
const ENC_TYPE_MSG = 0;
const ENC_TYPE_ATTACH_BINARY = 1;
const ENC_TYPE_ATTACH_ASCII = 2;
const GPG_COMMENT_OPT =
"Using GnuPG with %s - https://doesnotexist-openpgp-integration.thunderbird/";
var EnigmailEncryption = {
getEncryptCommand(
fromMailAddr,
toMailAddr,
bccMailAddr,
hashAlgorithm,
sendFlags,
isAscii,
errorMsgObj,
logFileObj
) {
EnigmailLog.DEBUG(
"encryption.jsm: getEncryptCommand: hashAlgorithm=" + hashAlgorithm + "\n"
);
try {
fromMailAddr = EnigmailFuncs.stripEmail(fromMailAddr);
toMailAddr = EnigmailFuncs.stripEmail(toMailAddr);
bccMailAddr = EnigmailFuncs.stripEmail(bccMailAddr);
} catch (ex) {
errorMsgObj.value = l10n.formatValueSync("invalid-email");
return null;
}
var signMsg = sendFlags & EnigmailConstants.SEND_SIGNED;
var encryptMsg = sendFlags & EnigmailConstants.SEND_ENCRYPTED;
var usePgpMime = sendFlags & EnigmailConstants.SEND_PGP_MIME;
var useDefaultComment = false;
try {
useDefaultComment = EnigmailPrefs.getPref("useDefaultComment");
} catch (ex) {}
var hushMailSupport = false;
try {
hushMailSupport = EnigmailPrefs.getPref("hushMailSupport");
} catch (ex) {}
var detachedSig =
(usePgpMime || sendFlags & EnigmailConstants.SEND_ATTACHMENT) &&
signMsg &&
!encryptMsg;
var toAddrList = toMailAddr.split(/\s*,\s*/);
var bccAddrList = bccMailAddr.split(/\s*,\s*/);
var k;
var encryptArgs = EnigmailGpg.getStandardArgs(true);
if (!useDefaultComment) {
encryptArgs = encryptArgs.concat([
"--comment",
GPG_COMMENT_OPT.replace(/%s/, EnigmailApp.getName()),
]);
}
var angledFromMailAddr =
fromMailAddr.search(/^0x/) === 0 || hushMailSupport
? fromMailAddr
: "<" + fromMailAddr + ">";
angledFromMailAddr = angledFromMailAddr.replace(/(["'`])/g, "\\$1");
if (signMsg && hashAlgorithm) {
encryptArgs = encryptArgs.concat(["--digest-algo", hashAlgorithm]);
}
if (logFileObj) {
logFileObj.value = EnigmailErrorHandling.getTempLogFile();
encryptArgs.push("--log-file");
encryptArgs.push(
EnigmailFiles.getEscapedFilename(
EnigmailFiles.getFilePath(logFileObj.value)
)
);
}
if (encryptMsg) {
switch (isAscii) {
case ENC_TYPE_MSG:
encryptArgs.push("-a");
encryptArgs.push("-t");
break;
case ENC_TYPE_ATTACH_ASCII:
encryptArgs.push("-a");
}
encryptArgs.push("--encrypt");
if (signMsg) {
encryptArgs.push("--sign");
}
if (sendFlags & EnigmailConstants.SEND_ALWAYS_TRUST) {
encryptArgs.push("--trust-model");
encryptArgs.push("always");
}
if (sendFlags & EnigmailConstants.SEND_ENCRYPT_TO_SELF && fromMailAddr) {
encryptArgs = encryptArgs.concat(["--encrypt-to", angledFromMailAddr]);
}
for (k = 0; k < toAddrList.length; k++) {
toAddrList[k] = toAddrList[k].replace(/'/g, "\\'");
if (toAddrList[k].length > 0) {
encryptArgs.push("-r");
if (toAddrList[k].search(/^GROUP:/) === 0) {
// groups from gpg.conf file
encryptArgs.push(toAddrList[k].substr(6));
} else {
encryptArgs.push(
hushMailSupport || toAddrList[k].search(/^0x/) === 0
? toAddrList[k]
: "<" + toAddrList[k] + ">"
);
}
}
}
for (k = 0; k < bccAddrList.length; k++) {
bccAddrList[k] = bccAddrList[k].replace(/'/g, "\\'");
if (bccAddrList[k].length > 0) {
encryptArgs.push("--hidden-recipient");
encryptArgs.push(
hushMailSupport || bccAddrList[k].search(/^0x/) === 0
? bccAddrList[k]
: "<" + bccAddrList[k] + ">"
);
}
}
} else if (detachedSig) {
encryptArgs = encryptArgs.concat(["-s", "-b"]);
switch (isAscii) {
case ENC_TYPE_MSG:
encryptArgs = encryptArgs.concat(["-a", "-t"]);
break;
case ENC_TYPE_ATTACH_ASCII:
encryptArgs.push("-a");
}
} else if (signMsg) {
encryptArgs = encryptArgs.concat(["-t", "--clearsign"]);
}
if (fromMailAddr) {
encryptArgs = encryptArgs.concat(["-u", angledFromMailAddr]);
}
return encryptArgs;
},
getCryptParams(
fromMailAddr,
toMailAddr,
@ -238,6 +77,7 @@ var EnigmailEncryption = {
result.encrypt = false;
result.encryptToSender = false;
result.armor = false;
result.senderKeyIsExternal = false;
EnigmailLog.DEBUG(
"encryption.jsm: getCryptParams: hashAlgorithm=" + hashAlgorithm + "\n"
@ -262,6 +102,10 @@ var EnigmailEncryption = {
var encryptMsg = sendFlags & EnigmailConstants.SEND_ENCRYPTED;
var usePgpMime = sendFlags & EnigmailConstants.SEND_PGP_MIME;
if (sendFlags & EnigmailConstants.SEND_SENDER_KEY_EXTERNAL) {
result.senderKeyIsExternal = true;
}
var detachedSig =
(usePgpMime || sendFlags & EnigmailConstants.SEND_ATTACHMENT) &&
signMsg &&
@ -346,7 +190,7 @@ var EnigmailEncryption = {
* - keyId: String - the found key ID, or null if fromMailAddr is not valid
* - errorMsg: String - the erorr message if key not valid, or null if key is valid
*/
async determineOwnKeyUsability(sendFlags, fromKeyId) {
async determineOwnKeyUsability(sendFlags, fromKeyId, isExternalGnuPG) {
EnigmailLog.DEBUG(
"encryption.jsm: determineOwnKeyUsability: sendFlags=" +
sendFlags +
@ -372,12 +216,13 @@ var EnigmailEncryption = {
foundKey = EnigmailKeyRing.getKeyById(fromKeyId);
}
// even for isExternalGnuPG we require that the public key is available
if (!foundKey) {
ret.errorMsg = EnigmailErrorHandling.determineInvSignReason(fromKeyId);
return ret;
}
if (foundKey.secretAvailable) {
if (!isExternalGnuPG && foundKey.secretAvailable) {
let isPersonal = await PgpSqliteDb2.isAcceptedAsPersonalKey(foundKey.fpr);
if (!isPersonal) {
ret.errorMsg = l10n.formatValueSync(
@ -392,7 +237,10 @@ var EnigmailEncryption = {
let canSign = false;
let canEncrypt = false;
if (sign) {
if (isExternalGnuPG) {
canSign = true;
} else if (sign) {
if (foundKey && foundKey.getSigningValidity().keyValid) {
canSign = true;
}
@ -458,7 +306,7 @@ var EnigmailEncryption = {
"encryption.jsm: encryptMessageStart: NO ENCRYPTION!\n"
);
errorMsgObj.value = l10n.formatValueSync("not-required");
return null;
return 0;
}
if (!EnigmailCore.getService(win)) {
@ -469,8 +317,6 @@ var EnigmailEncryption = {
let logFileObj = {};
// GnuPG
// let encryptArgs = EnigmailEncryption.getEncryptCommand(fromMailAddr, toMailAddr, bccMailAddr, hashAlgo, sendFlags, ENC_TYPE_MSG, errorMsgObj, logFileObj);
let encryptArgs = EnigmailEncryption.getCryptParams(
fromMailAddr,
toMailAddr,
@ -483,29 +329,18 @@ var EnigmailEncryption = {
);
if (!encryptArgs) {
return null;
return 0;
}
if (!listener) {
listener = {};
throw new Error("unexpected no listener");
}
if ("done" in listener) {
listener.outerDone = listener.done;
}
listener.done = function(exitCode) {
EnigmailErrorHandling.appendLogFileToDebug(logFileObj.value);
if (this.outerDone) {
this.outerDone(exitCode);
}
};
let resultStatus = {};
const cApi = EnigmailCryptoAPI();
console.debug("listener: %o", listener);
let encrypted = cApi.sync(
cApi.encryptAndOrSign(
listener.getInputForEncryption(),
listener.getInputForCrypto(),
encryptArgs,
resultStatus
)
@ -516,107 +351,18 @@ var EnigmailEncryption = {
EnigmailDialog.alert(win, resultStatus.errorMsg);
}
} else if (encrypted) {
listener.addEncryptedOutput(encrypted);
listener.addCryptoOutput(encrypted);
}
listener.done(resultStatus.exitCode);
return null;
},
encryptMessageEnd(
fromMailAddr,
stderrStr,
exitCode,
uiFlags,
sendFlags,
outputLen,
retStatusObj
) {
EnigmailLog.DEBUG(
"encryption.jsm: encryptMessageEnd: uiFlags=" +
uiFlags +
", sendFlags=" +
EnigmailData.bytesToHex(EnigmailData.pack(sendFlags, 4)) +
", outputLen=" +
outputLen +
"\n"
EnigmailErrorHandling.appendLogFileToDebug(logFileObj.value);
console.debug(
"sendFlags=" + EnigmailData.bytesToHex(EnigmailData.pack(sendFlags, 4))
);
var signMsg = sendFlags & EnigmailConstants.SEND_SIGNED;
var encryptMsg = sendFlags & EnigmailConstants.SEND_ENCRYPTED;
retStatusObj.statusFlags = 0;
retStatusObj.errorMsg = "";
retStatusObj.blockSeparation = "";
if (!EnigmailCore.getService().initialized) {
throw new Error("encryption.jsm: encryptMessageEnd: not yet initialized");
if (resultStatus.exitCode === 0 && !listener.getCryptoOutputLength()) {
resultStatus.exitCode = -1;
}
//EnigmailErrorHandling.parseErrorOutput(stderrStr, retStatusObj);
//exitCode = EnigmailExecution.fixExitCode(exitCode, retStatusObj);
if (exitCode === 0 && !outputLen) {
exitCode = -1;
}
if (exitCode !== 0 && (signMsg || encryptMsg)) {
// GnuPG might return a non-zero exit code, even though the message was correctly
// signed or encryped -> try to fix the exit code
var correctedExitCode = 0;
if (signMsg) {
if (!(retStatusObj.statusFlags & EnigmailConstants.SIG_CREATED)) {
correctedExitCode = exitCode;
}
}
if (encryptMsg) {
if (!(retStatusObj.statusFlags & EnigmailConstants.END_ENCRYPTION)) {
correctedExitCode = exitCode;
}
}
exitCode = correctedExitCode;
}
EnigmailLog.DEBUG(
"encryption.jsm: encryptMessageEnd: command execution exit code: " +
exitCode +
"\n"
);
/*
if (retStatusObj.statusFlags & EnigmailConstants.DISPLAY_MESSAGE) {
if (retStatusObj.extendedStatus.search(/\bdisp:/) >= 0) {
retStatusObj.errorMsg = retStatusObj.statusMsg;
} else {
if (fromMailAddr.search(/^0x/) === 0) {
fromMailAddr = fromMailAddr.substr(2);
}
if (fromMailAddr.search(/^[A-F0-9]{8,40}$/i) === 0) {
fromMailAddr = "[A-F0-9]+" + fromMailAddr;
}
let s = new RegExp(
"^(\\[GNUPG:\\] )?INV_(RECP|SGNR) [0-9]+ (\\<|0x)?" +
fromMailAddr +
"\\>?",
"m"
);
if (retStatusObj.statusMsg.search(s) >= 0) {
retStatusObj.errorMsg +=
"\n\n" + EnigmailLocale.getString("keyError.resolutionAction");
} else if (retStatusObj.statusMsg.length > 0) {
retStatusObj.errorMsg = retStatusObj.statusMsg;
}
}
} else if (retStatusObj.statusFlags & EnigmailConstants.INVALID_RECIPIENT) {
retStatusObj.errorMsg = retStatusObj.statusMsg;
} else if (exitCode !== 0) {
retStatusObj.errorMsg = EnigmailLocale.getString("badCommand");
}
*/
return exitCode;
return resultStatus.exitCode;
},
encryptMessage(

Просмотреть файл

@ -20,9 +20,6 @@ const { EnigmailCompat } = ChromeUtils.import(
const { EnigmailFuncs } = ChromeUtils.import(
"chrome://openpgp/content/modules/funcs.jsm"
);
const { EnigmailDialog } = ChromeUtils.import(
"chrome://openpgp/content/modules/dialog.jsm"
);
const { EnigmailLog } = ChromeUtils.import(
"chrome://openpgp/content/modules/log.jsm"
);
@ -49,8 +46,9 @@ const PGPMIME_ENCRYPT_CID = Components.ID(
const PGPMIME_ENCRYPT_CONTRACTID = "@enigmail.net/compose/mimeencrypt;1";
const maxBufferLen = 102400;
const MIME_SIGNED = 1;
const MIME_ENCRYPTED = 2;
const MIME_SIGNED = 1; // only one MIME layer
const MIME_ENCRYPTED = 2; // only one MIME layer, combined enc/sig data
const MIME_OUTER_ENC_INNER_SIG = 3; // use two MIME layers
var gDebugLogLevel = 1;
@ -105,21 +103,19 @@ PgpMimeEncrypt.prototype = {
// 1: processing body
// 2: skipping header
inputMode: 0,
dataLength: 0,
headerData: "",
encapsulate: null,
encHeader: null,
cryptoBoundary: null,
outerBoundary: null,
innerBoundary: null,
win: null,
pipe: null,
proc: null,
statusStr: "",
encryptedData: "",
hashAlgorithm: null,
pipeQueue: "",
outQueue: "",
closePipe: false,
cryptoMode: 0,
//statusStr: "",
cryptoOutputLength: 0,
cryptoOutput: "",
hashAlgorithm: "SHA256", // TODO: coordinate with RNP.jsm
cryptoInputBuffer: "",
outgoingMessageBuffer: "",
mimeStructure: 0,
exitCode: -1,
inspector: null,
checkSMime: true,
@ -222,16 +218,20 @@ PgpMimeEncrypt.prototype = {
if (this.sendFlags & EnigmailConstants.SEND_PGP_MIME) {
if (this.sendFlags & EnigmailConstants.SEND_ENCRYPTED) {
// applies to encrypted and signed & encrypted
this.cryptoMode = MIME_ENCRYPTED;
if (this.sendFlags & EnigmailConstants.SEND_TWO_MIME_LAYERS) {
this.mimeStructure = MIME_OUTER_ENC_INNER_SIG;
this.innerBoundary = EnigmailMime.createBoundary();
} else {
this.mimeStructure = MIME_ENCRYPTED;
}
} else if (this.sendFlags & EnigmailConstants.SEND_SIGNED) {
this.cryptoMode = MIME_SIGNED;
this.hashAlgorithm = "SHA256"; // TODO: coordinate with RNP.jsm
this.mimeStructure = MIME_SIGNED;
}
} else {
throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED);
}
this.cryptoBoundary = EnigmailMime.createBoundary();
this.outerBoundary = EnigmailMime.createBoundary();
this.startCryptoHeaders();
} catch (ex) {
console.debug(ex);
@ -245,11 +245,14 @@ PgpMimeEncrypt.prototype = {
startCryptoHeaders() {
EnigmailLog.DEBUG("mimeEncrypt.js: startCryptoHeaders\n");
if (this.cryptoMode == MIME_SIGNED) {
this.signedHeaders1(false);
}
if (this.cryptoMode == MIME_ENCRYPTED) {
this.encryptedHeaders();
switch (this.mimeStructure) {
case MIME_SIGNED:
this.signedHeaders1(false);
break;
case MIME_ENCRYPTED:
case MIME_OUTER_ENC_INNER_SIG:
this.encryptedHeaders();
break;
}
this.writeSecureHeaders();
@ -315,7 +318,8 @@ PgpMimeEncrypt.prototype = {
}
if (
this.cryptoMode == MIME_ENCRYPTED &&
(this.mimeStructure == MIME_ENCRYPTED ||
this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) &&
this.originalSubject &&
this.originalSubject.length > 0
) {
@ -355,17 +359,18 @@ PgpMimeEncrypt.prototype = {
}
w += this.getAutocryptGossip() + `\r\n--${this.encHeader}\r\n`;
this.writeToPipe(w);
this.appendToCryptoInput(w);
if (this.cryptoMode == MIME_SIGNED) {
this.writeOut(w);
if (this.mimeStructure == MIME_SIGNED) {
this.appendToMessage(w);
}
},
getAutocryptGossip() {
let gossip = "";
if (
this.cryptoMode == MIME_ENCRYPTED &&
(this.mimeStructure == MIME_ENCRYPTED ||
this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) &&
this.msgCompFields.hasHeader("autocrypt") &&
this.keyMap &&
EnigmailFuncs.getNumberOfRecipients(this.msgCompFields) > 1
@ -392,7 +397,7 @@ PgpMimeEncrypt.prototype = {
return gossip;
},
encryptedHeaders(isEightBit) {
encryptedHeaders(isEightBit = false) {
EnigmailLog.DEBUG("mimeEncrypt.js: encryptedHeaders\n");
let subj = "";
@ -403,18 +408,17 @@ PgpMimeEncrypt.prototype = {
{}
);
}
this.writeOut(
this.appendToMessage(
subj +
"Content-Type: multipart/encrypted;\r\n" +
' protocol="application/pgp-encrypted";\r\n' +
' boundary="' +
this.cryptoBoundary +
this.outerBoundary +
'"\r\n' +
"\r\n" +
"This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n" +
"--" +
this.cryptoBoundary +
this.outerBoundary +
"\r\n" +
"Content-Type: application/pgp-encrypted\r\n" +
"Content-Description: PGP/MIME version identification\r\n" +
@ -422,7 +426,7 @@ PgpMimeEncrypt.prototype = {
"Version: 1\r\n" +
"\r\n" +
"--" +
this.cryptoBoundary +
this.outerBoundary +
"\r\n" +
'Content-Type: application/octet-stream; name="encrypted.asc"\r\n' +
"Content-Description: OpenPGP encrypted message\r\n" +
@ -431,41 +435,60 @@ PgpMimeEncrypt.prototype = {
);
},
signedHeaders1(isEightBit) {
signedHeaders1(isEightBit = false) {
LOCAL_DEBUG("mimeEncrypt.js: signedHeaders1\n");
this.writeOut(
let boundary;
if (this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) {
boundary = this.innerBoundary;
} else {
boundary = this.outerBoundary;
}
let sigHeader =
"Content-Type: multipart/signed; micalg=pgp-" +
this.hashAlgorithm.toLowerCase() +
";\r\n" +
' protocol="application/pgp-signature";\r\n' +
' boundary="' +
this.cryptoBoundary +
'"\r\n' +
(isEightBit ? "Content-Transfer-Encoding: 8bit\r\n\r\n" : "\r\n") +
"This is an OpenPGP/MIME signed message (RFC 4880 and 3156)\r\n" +
"--" +
this.cryptoBoundary +
"\r\n"
);
this.hashAlgorithm.toLowerCase() +
";\r\n" +
' protocol="application/pgp-signature";\r\n' +
' boundary="' +
boundary +
'"\r\n' +
(isEightBit ? "Content-Transfer-Encoding: 8bit\r\n\r\n" : "\r\n") +
"This is an OpenPGP/MIME signed message (RFC 4880 and 3156)\r\n" +
"--" +
boundary +
"\r\n";
if (this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) {
this.appendToCryptoInput(sigHeader);
} else {
this.appendToMessage(sigHeader);
}
},
signedHeaders2() {
LOCAL_DEBUG("mimeEncrypt.js: signedHeaders2\n");
this.writeOut(
let boundary;
if (this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) {
boundary = this.innerBoundary;
} else {
boundary = this.outerBoundary;
}
let sigHeader =
"\r\n--" +
this.cryptoBoundary +
"\r\n" +
'Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"\r\n' +
"Content-Description: OpenPGP digital signature\r\n" +
'Content-Disposition: attachment; filename="OpenPGP_signature"\r\n\r\n'
);
boundary +
"\r\n" +
'Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"\r\n' +
"Content-Description: OpenPGP digital signature\r\n" +
'Content-Disposition: attachment; filename="OpenPGP_signature"\r\n\r\n';
if (this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) {
this.appendToCryptoInput(sigHeader);
} else {
this.appendToMessage(sigHeader);
}
},
finishCryptoHeaders() {
EnigmailLog.DEBUG("mimeEncrypt.js: finishCryptoHeaders\n");
this.writeOut("\r\n--" + this.cryptoBoundary + "--\r\n");
this.appendToMessage("\r\n--" + this.outerBoundary + "--\r\n");
},
finishCryptoEncapsulation(abort, sendReport) {
@ -486,48 +509,76 @@ PgpMimeEncrypt.prototype = {
}
if (this.encapsulate) {
this.writeToPipe("--" + this.encapsulate + "--\r\n");
this.appendToCryptoInput("--" + this.encapsulate + "--\r\n");
}
if (this.encHeader) {
this.writeToPipe("\r\n--" + this.encHeader + "--\r\n");
if (this.cryptoMode == MIME_SIGNED) {
this.writeOut("\r\n--" + this.encHeader + "--\r\n");
this.appendToCryptoInput("\r\n--" + this.encHeader + "--\r\n");
if (this.mimeStructure == MIME_SIGNED) {
this.appendToMessage("\r\n--" + this.encHeader + "--\r\n");
}
}
let statusFlagsObj = {};
let errorMsgObj = {};
EnigmailEncryption.encryptMessageStart(
this.win,
this.UIFlags,
this.senderEmailAddr,
this.recipients,
this.bccRecipients,
this.hashAlgorithm,
this.sendFlags,
this,
statusFlagsObj,
errorMsgObj
);
this.exitCode = 0;
//if (!proc) throw Cr.NS_ERROR_FAILURE;
if (this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) {
// prepare the inner crypto layer (the signature)
let sendFlagsWithoutEncrypt =
this.sendFlags & ~EnigmailConstants.SEND_ENCRYPTED;
this.exitCode = EnigmailEncryption.encryptMessageStart(
this.win,
this.UIFlags,
this.senderEmailAddr,
this.recipients,
this.bccRecipients,
this.hashAlgorithm,
sendFlagsWithoutEncrypt,
this,
statusFlagsObj,
errorMsgObj
);
if (!this.exitCode) {
// success
let innerSignedMessage = this.cryptoInputBuffer;
this.cryptoInputBuffer = "";
this.signedHeaders1(false);
this.appendToCryptoInput(innerSignedMessage);
this.signedHeaders2();
this.cryptoOutput = this.cryptoOutput
.replace(/\r/g, "")
.replace(/\n/g, "\r\n"); // force CRLF
this.appendToCryptoInput(this.cryptoOutput);
this.appendToCryptoInput("\r\n--" + this.innerBoundary + "--\r\n");
this.cryptoOutput = "";
}
}
if (!this.exitCode) {
// no failure yet
let encryptionFlags = this.sendFlags;
if (this.mimeStructure == MIME_OUTER_ENC_INNER_SIG) {
// remove signature flag, because we already signed
encryptionFlags = encryptionFlags & ~EnigmailConstants.SEND_SIGNED;
}
this.exitCode = EnigmailEncryption.encryptMessageStart(
this.win,
this.UIFlags,
this.senderEmailAddr,
this.recipients,
this.bccRecipients,
this.hashAlgorithm,
encryptionFlags,
this,
statusFlagsObj,
errorMsgObj
);
}
try {
this.flushInput();
/*
if (!this.pipe) {
this.closePipe = true;
}
else {
this.pipe.close();
}
*/
// wait here for proc to terminate
//proc.wait();
LOCAL_DEBUG(
"mimeEncrypt.js: finishCryptoEncapsulation: exitCode = " +
this.exitCode +
@ -537,14 +588,15 @@ PgpMimeEncrypt.prototype = {
throw new Error("failure in finishCryptoEncapsulation");
}
if (this.cryptoMode == MIME_SIGNED) {
if (this.mimeStructure == MIME_SIGNED) {
this.signedHeaders2();
}
this.encryptedData = this.encryptedData
this.cryptoOutput = this.cryptoOutput
.replace(/\r/g, "")
.replace(/\n/g, "\r\n"); // force CRLF
this.writeOut(this.encryptedData);
this.appendToMessage(this.cryptoOutput);
this.finishCryptoHeaders();
this.flushOutput();
} catch (ex) {
@ -586,7 +638,10 @@ PgpMimeEncrypt.prototype = {
if (line.replace(/[\r\n]/g, "").length === 0) {
this.inputMode = 1;
if (this.cryptoMode == MIME_ENCRYPTED) {
if (
this.mimeStructure == MIME_ENCRYPTED ||
this.mimeStructure == MIME_OUTER_ENC_INNER_SIG
) {
if (!this.encHeader) {
let ct = this.getHeader("content-type", false);
if (
@ -594,43 +649,43 @@ PgpMimeEncrypt.prototype = {
ct.search(/text\/html/i) === 0
) {
this.encapsulate = EnigmailMime.createBoundary();
this.writeToPipe(
this.appendToCryptoInput(
'Content-Type: multipart/mixed; boundary="' +
this.encapsulate +
'"\r\n\r\n'
);
this.writeToPipe("--" + this.encapsulate + "\r\n");
this.appendToCryptoInput("--" + this.encapsulate + "\r\n");
}
}
} else if (this.cryptoMode == MIME_SIGNED) {
} else if (this.mimeStructure == MIME_SIGNED) {
let ct = this.getHeader("content-type", true);
let hdr = EnigmailFuncs.getHeaderData(ct);
hdr.boundary = hdr.boundary || "";
hdr.boundary = hdr.boundary.replace(/['"]/g, "");
}
this.writeToPipe(this.headerData);
this.appendToCryptoInput(this.headerData);
if (
this.cryptoMode == MIME_SIGNED ||
this.mimeStructure == MIME_SIGNED ||
(this.sendFlags & EnigmailConstants.SEND_VERBATIM) !== 0
) {
this.writeOut(this.headerData);
this.appendToMessage(this.headerData);
}
}
} else if (this.inputMode == 1) {
if (this.cryptoMode == MIME_SIGNED) {
if (this.mimeStructure == MIME_SIGNED) {
// special treatments for various special cases with PGP/MIME signed messages
if (line.substr(0, 5) == "From ") {
LOCAL_DEBUG("mimeEncrypt.js: added >From\n");
this.writeToPipe(">");
this.appendToCryptoInput(">");
}
}
this.writeToPipe(line);
if (this.cryptoMode == MIME_SIGNED) {
this.writeOut(line);
this.appendToCryptoInput(line);
if (this.mimeStructure == MIME_SIGNED) {
this.appendToMessage(line);
} else if ((this.sendFlags & EnigmailConstants.SEND_VERBATIM) !== 0) {
this.writeOut(
this.appendToMessage(
EnigmailData.decodeQuotedPrintable(line.replace("=\r\n", ""))
);
}
@ -648,60 +703,49 @@ PgpMimeEncrypt.prototype = {
return null;
},
writeOut(str) {
appendToMessage(str) {
if (gDebugLogLevel > 4) {
LOCAL_DEBUG("mimeEncrypt.js: writeOut: " + str.length + "\n");
LOCAL_DEBUG("mimeEncrypt.js: appendToMessage: " + str.length + "\n");
}
this.outQueue += str;
this.outgoingMessageBuffer += str;
if (this.outQueue.length > maxBufferLen) {
if (this.outgoingMessageBuffer.length > maxBufferLen) {
this.flushOutput();
}
},
flushOutput() {
LOCAL_DEBUG("mimeEncrypt.js: flushOutput: " + this.outQueue.length + "\n");
LOCAL_DEBUG(
"mimeEncrypt.js: flushOutput: " + this.outgoingMessageBuffer.length + "\n"
);
this.outStringStream.setData(this.outQueue, this.outQueue.length);
this.outStringStream.setData(
this.outgoingMessageBuffer,
this.outgoingMessageBuffer.length
);
var writeCount = this.outStream.writeFrom(
this.outStringStream,
this.outQueue.length
this.outgoingMessageBuffer.length
);
if (writeCount < this.outQueue.length) {
if (writeCount < this.outgoingMessageBuffer.length) {
LOCAL_DEBUG(
"mimeEncrypt.js: flushOutput: wrote " +
writeCount +
" instead of " +
this.outQueue.length +
this.outgoingMessageBuffer.length +
" bytes\n"
);
}
this.outQueue = "";
this.outgoingMessageBuffer = "";
},
writeToPipe(str) {
appendToCryptoInput(str) {
if (gDebugLogLevel > 4) {
LOCAL_DEBUG("mimeEncrypt.js: writeToPipe: " + str.length + "\n");
LOCAL_DEBUG("mimeEncrypt.js: appendToCryptoInput: " + str.length + "\n");
}
if (this.pipe) {
this.pipeQueue += str;
if (this.pipeQueue.length > maxBufferLen) {
this.flushInput();
}
} else {
this.pipeQueue += str;
}
},
flushInput() {
LOCAL_DEBUG("mimeEncrypt.js: flushInput\n");
if (!this.pipe) {
return;
}
this.pipe.write(this.pipeQueue);
this.pipeQueue = "";
this.cryptoInputBuffer += str;
},
getHeader(hdrStr, fullHeader) {
@ -735,59 +779,28 @@ PgpMimeEncrypt.prototype = {
return res;
},
getInputForEncryption() {
return this.pipeQueue;
getInputForCrypto() {
return this.cryptoInputBuffer;
},
addEncryptedOutput(s) {
this.stdout(s);
addCryptoOutput(s) {
LOCAL_DEBUG("mimeEncrypt.js: addCryptoOutput:" + s.length + "\n");
this.cryptoOutput += s;
this.cryptoOutputLength += s.length;
},
getCryptoOutputLength() {
return this.cryptoOutputLength;
},
// API for decryptMessage Listener
stdin(pipe) {
LOCAL_DEBUG("mimeEncrypt.js: stdin\n");
if (this.pipeQueue.length > 0) {
pipe.write(this.pipeQueue);
this.pipeQueue = "";
}
if (this.closePipe) {
pipe.close();
} else {
this.pipe = pipe;
}
},
stdout(s) {
LOCAL_DEBUG("mimeEncrypt.js: stdout:" + s.length + "\n");
this.encryptedData += s;
this.dataLength += s.length;
throw new Error("unexpected");
},
stderr(s) {
LOCAL_DEBUG("mimeEncrypt.js: stderr\n");
this.statusStr += s;
},
done(exitCode) {
EnigmailLog.DEBUG("mimeEncrypt.js: done: " + exitCode + "\n");
let retStatusObj = {};
this.exitCode = EnigmailEncryption.encryptMessageEnd(
this.senderEmailAddr,
this.statusStr,
exitCode,
this.UIFlags,
this.sendFlags,
this.dataLength,
retStatusObj
);
if (this.exitCode !== 0) {
if (retStatusObj.errorMsg.length) {
EnigmailDialog.alert(this.win, retStatusObj.errorMsg);
}
}
throw new Error("unexpected");
//this.statusStr += s;
},
};

Просмотреть файл

@ -1866,6 +1866,10 @@ Enigmail.msg = {
f &= ~EnigmailConstants.SEND_SIGNED;
}
if (gSendEncrypted && gSendSigned) {
f |= EnigmailConstants.SEND_TWO_MIME_LAYERS;
}
return f;
},
@ -2200,10 +2204,18 @@ Enigmail.msg = {
return false;
}
let senderKeyIsGnuPG =
Services.prefs.getBoolPref("mail.openpgp.allow_external_gnupg") &&
this.identity.getBoolAttribute("is_gnupg_key_id");
if (senderKeyIsGnuPG) {
sendFlags |= EnigmailConstants.SEND_SENDER_KEY_EXTERNAL;
}
if ((gSendEncrypted || gSendSigned) && senderKeyId) {
let senderKeyUsable = await EnigmailEncryption.determineOwnKeyUsability(
sendFlags,
senderKeyId
senderKeyId,
senderKeyIsGnuPG
);
if (senderKeyUsable.errorMsg) {
let fullAlert = await document.l10n.formatValue(