diff --git a/dev/Stores/User/GnuPG.js b/dev/Stores/User/GnuPG.js index ffab9007b..92d65cbdd 100644 --- a/dev/Stores/User/GnuPG.js +++ b/dev/Stores/User/GnuPG.js @@ -151,4 +151,40 @@ export const GnuPGUserStore = new class { getPublicKeyFor(query, sign) { return findGnuPGKey(this.publicKeys, query, sign); } + + async decrypt(message) { + const + pgpInfo = message.pgpEncrypted(); + if (pgpInfo) { + let ids = [message.to[0].email].concat(pgpInfo.KeyIds), + i = ids.length, key; + while (i--) { + key = findGnuPGKey(this.privateKeys, ids[i]); + if (key) { + break; + } + } + if (key) { + // Also check message.from[0].email + let params = { + Folder: message.folder, + Uid: message.uid, + PartId: pgpInfo.PartId, + KeyId: key.id, + Passphrase: prompt('GnuPG Passphrase for ' + key.id + ' ' + key.uids[0].uid), + Data: '' // message.plain() optional + } + if (null !== params.Passphrase) { + const result = await Remote.post('GnupgDecrypt', null, params); + if (result && result.Result) { + return result.Result; + } + } + } + } + } + + verify(/*message, fCallback*/) { + } + }; diff --git a/dev/Stores/User/OpenPGP.js b/dev/Stores/User/OpenPGP.js index 9f5db818f..07e809830 100644 --- a/dev/Stores/User/OpenPGP.js +++ b/dev/Stores/User/OpenPGP.js @@ -162,28 +162,47 @@ export const OpenPGPUserStore = new class { /** * https://docs.openpgpjs.org/#encrypt-and-decrypt-string-data-with-pgp-keys */ - async decrypt(armoredText, privateKey, publicKey) + async decrypt(armoredText, sender) { - const passphrase = prompt('Passphrase'); - if (!passphrase) { - return; + const message = await openpgp.readMessage({ armoredMessage: armoredText }), + privateKeys = this.privateKeys(), + msgEncryptionKeyIDs = message.getEncryptionKeyIDs().map(key => key.bytes); + // Find private key that can decrypt message + let i = privateKeys.length, privateKey; + while (i--) { + if ((await privateKeys[i].key.getDecryptionKeys()).find( + key => msgEncryptionKeyIDs.includes(key.getKeyID().bytes) + )) { + privateKey = privateKeys[i]; + break; + } + } + if (privateKey) try { + // Ask passphrase of private key + const passphrase = prompt('OpenPGP.js Passphrase for ' + privateKey.id + ' ' + privateKey.emails[0]); + if (null !== passphrase) { + const + decryptedKey = await openpgp.decryptKey({ + privateKey: privateKey.key, + passphrase + }); + + return await openpgp.decrypt({ + message, + verificationKeys: this.getPublicKeyFor(sender), +// expectSigned: true, +// signature: '', // Detached signature + decryptionKeys: decryptedKey + }); + } + } catch (err) { + alert(err); + console.error(err); } - const message = await openpgp.readMessage({ armoredMessage: armoredText }); - const decryptedKey = await openpgp.decryptKey({ -// privateKey: await openpgp.readPrivateKey({ armoredKey: armoredPrivateKey }), - privateKey: privateKey, - passphrase - }); - return await openpgp.decrypt({ - message, - verificationKeys: publicKey, -// expectSigned: true, -// signature: '', // Detached signature - decryptionKeys: decryptedKey - }); } async verify(message, detachedSignature, publicKey) { +// message.getSigningKeyIDs(); return await openpgp.verify({ message, verificationKeys: publicKey, diff --git a/dev/Stores/User/Pgp.js b/dev/Stores/User/Pgp.js index 5538e4826..8b9e43c06 100644 --- a/dev/Stores/User/Pgp.js +++ b/dev/Stores/User/Pgp.js @@ -93,20 +93,6 @@ export const PgpUserStore = new class { return false; } - getGnuPGPrivateKeyFor(query, sign) { - let key = GnuPGUserStore.getPrivateKeyFor(query, sign); - if (key) { - return ['gnupg', key]; - } - } - - getOpenPGPPrivateKeyFor(query/*, sign*/) { - let key = OpenPGPUserStore.getPrivateKeyFor(query/*, sign*/); - if (key) { - return ['openpgp', key]; - } - } - async getMailvelopePrivateKeyFor(email/*, sign*/) { let keyring = this.mailvelopeKeyring; if (keyring && await keyring.hasPrivateKey({email:email})) { @@ -120,54 +106,73 @@ export const PgpUserStore = new class { * Returns the first library that can. */ async getKeyForSigning(email) { - return this.getGnuPGPrivateKeyFor(email, 1) - || this.getOpenPGPPrivateKeyFor(email, 1) - || await this.getMailvelopePrivateKeyFor(email, 1); + let key = GnuPGUserStore.getPrivateKeyFor(email, 1); + if (key) { + return ['gnupg', key]; + } + + key = OpenPGPUserStore.getPrivateKeyFor(email, 1); + if (key) { + return ['openpgp', key]; + } + + return await this.getMailvelopePrivateKeyFor(email, 1); } - /** - * Checks if decrypting a message is possible with given keyIds or email address. - * Returns the first library that can. - */ - async getKeyForDecryption(ids, email) { - ids = [email].concat(ids); - let i = ids.length, key; + async decrypt(message) { + const sender = message.from[0].email, + armoredText = message.plain(); + if (!armoredText.includes('-----BEGIN PGP MESSAGE-----')) { + return; + } + + // Try OpenPGP.js + let result = await OpenPGPUserStore.decrypt(armoredText, sender); + if (result) { + return result; + } + + // Try Mailvelope (does not support inline images) try { - key = await this.getMailvelopePrivateKeyFor(email); + let key = await this.getMailvelopePrivateKeyFor(message.to[0].email); if (key) { - return key; + /** + * https://mailvelope.github.io/mailvelope/Mailvelope.html#createEncryptedFormContainer + * Creates an iframe to display an encrypted form + */ +// mailvelope.createEncryptedFormContainer('#mailvelope-form'); + /** + * https://mailvelope.github.io/mailvelope/Mailvelope.html#createDisplayContainer + * Creates an iframe to display the decrypted content of the encrypted mail. + */ + const body = message.body; + body.textContent = ''; + result = await mailvelope.createDisplayContainer( + '#'+body.id, + armoredText, + this.mailvelopeKeyring, + { + senderAddress: sender + } + ); + if (result) { + if (result.error && result.error.message) { + if ('PWD_DIALOG_CANCEL' !== result.error.code) { + alert(result.error.code + ': ' + result.error.message); + } + } else { + body.classList.add('mailvelope'); + return; + } + } } } catch (err) { console.error(err); } -/* Not working, needs full fingerprint - while (i--) { - key = await this.getMailvelopePrivateKeyFor(ids[i]); - if (key) { - return key; - } - if (await keyring.hasPrivateKey(ids[i])) { - return ['mailvelope', ids[i]]; - } - } - i = ids.length; -*/ -/* - while (i--) { - key = this.getGnuPGPrivateKeyFor(ids[i]); - if (key) { - return key; - } - } -*/ - i = ids.length; - while (i--) { - key = this.getOpenPGPPrivateKeyFor(ids[i]); - if (key) { - return key; - } - } + + // Now try GnuPG + return GnuPGUserStore.decrypt(message); } /** diff --git a/dev/View/User/MailBox/MessageView.js b/dev/View/User/MailBox/MessageView.js index 5c7dd7802..be6b011ed 100644 --- a/dev/View/User/MailBox/MessageView.js +++ b/dev/View/User/MailBox/MessageView.js @@ -673,80 +673,13 @@ export class MailMessageView extends AbstractViewRight { } pgpDecrypt(self) { - const message = self.message(), - sender = message && message.from[0].email, - pgpInfo = message && message.pgpEncrypted(); - if (pgpInfo) { - // Also check message.from[0].email - PgpUserStore.getKeyForDecryption(pgpInfo.KeyIds, message.to[0].email).then(result => { - if (!result) { - // TODO: show error - alert('No decrypt key found for ids:\n\n' + pgpInfo.KeyIds.join('\n')); - return; - } - if ('mailvelope' === result[0]) { - /** - * https://mailvelope.github.io/mailvelope/Mailvelope.html#createEncryptedFormContainer - * Creates an iframe to display an encrypted form - */ -// mailvelope.createEncryptedFormContainer('#mailvelope-form'); - /** - * https://mailvelope.github.io/mailvelope/Mailvelope.html#createDisplayContainer - * Creates an iframe to display the decrypted content of the encrypted mail. - */ - let body = message.body; - body.textContent = ''; - mailvelope.createDisplayContainer( - '#'+body.id, - message.plain(), - PgpUserStore.mailvelopeKeyring, - { - senderAddress: sender - } - ).then(status => { - if (status.error && status.error.message) { - alert(status.error.code + ': ' + status.error.message); - } else { - body.classList.add('mailvelope'); - } - }, error => { - console.error(error); - }); - } - else if ('openpgp' === result[0]) { - const publicKey = OpenPGPUserStore.getPublicKeyFor(sender); -// OpenPGPUserStore.decrypt(message.plain(), result[1].armor - OpenPGPUserStore.decrypt(message.plain(), result[1].key, publicKey ? publicKey.key : null).then(result => { - if (result) { - mimeToMessage(result.data, message); - } else { -// controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR')); - } - }); - } - else if ('gnupg' === result[0]) { - let params = { - Folder: message.folder, - Uid: message.uid, - PartId: pgpInfo.PartId, - KeyId: result[1].id, - Passphrase: prompt('Passphrase for ' + result[1].id + ' ' + result[1].uids[0].uid), - Data: '' // optional - } - if (params.Passphrase) { - rl.app.Remote.post('GnupgDecrypt', null, params) - .then(data => { - // TODO mimeToMessage(data, message) - console.dir(data); - }) - .catch(error => { - // TODO - console.dir(error); - }); - } - } - }); - } + const message = self.message(); + message && PgpUserStore.decrypt(message).then(result => { +console.dir({decrypt_result:result}); + if (result) { + mimeToMessage(result.data, message); + } + }); } pgpVerify(self) { diff --git a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php index e2808274a..2e9ff6037 100644 --- a/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php +++ b/snappymail/v/0.0.0/app/libraries/RainLoop/Actions/Pgp.php @@ -64,16 +64,20 @@ trait Pgp $sData = $this->GetActionParam('Data', ''); $oPart = null; + $result = [ + 'data' => '', + 'signatures' => [] + ]; if ($sData) { $result = $GPG->decrypt($sData); - $oPart = \MailSo\Mime\Part::FromString($result); +// $oPart = \MailSo\Mime\Part::FromString($result); } else { $this->initMailClientConnection(); $this->MailClient()->MessageMimeStream( - function ($rResource) use ($GPG, $oPart) { + function ($rResource) use ($GPG, &$result, &$oPart) { if (\is_resource($rResource)) { - $result = $GPG->decryptStream($rResource); - $oPart = \MailSo\Mime\Part::FromString($result); + $result['data'] = $GPG->decryptStream($rResource); +// $oPart = \MailSo\Mime\Part::FromString($result); // $GPG->decryptStream($rResource, $rStreamHandle); // $oPart = \MailSo\Mime\Part::FromStream($rStreamHandle); } @@ -84,12 +88,13 @@ trait Pgp ); } - if ($oPart->IsPgpSigned()) { + if ($oPart && $oPart->IsPgpSigned()) { // $GPG->verifyStream($oPart->SubParts[0]->Body, \stream_get_contents($oPart->SubParts[1]->Body)); - $oPart = $oPart->SubParts[0]; +// $result['signatures'] = $oPart->SubParts[0]; } - return $this->DefaultResponse(__FUNCTION__, $oPart); +// return $this->DefaultResponse(__FUNCTION__, $oPart); + return $this->DefaultResponse(__FUNCTION__, $result); } public function DoGnupgGetKeys() : array diff --git a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php index b58dd6cc7..f1bb25a62 100644 --- a/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php +++ b/snappymail/v/0.0.0/app/libraries/snappymail/pgp/gnupg.php @@ -143,7 +143,9 @@ class GnuPG if (!$fp || !\is_resource($fp)) { throw new \Exception('Invalid stream resource'); } - return $this->getGPG()->decryptStream($fp, $output); + return $this->GnuPG + ? $this->GnuPG->decrypt(\stream_get_contents($fp)) + : $this->GPG->decryptStream($fp, $output); } /**