mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
#89 revamp code for better decryption and get it working for GnuPG
This commit is contained in:
parent
4eb70c3f06
commit
d9e81d6123
6 changed files with 152 additions and 152 deletions
|
|
@ -151,4 +151,40 @@ export const GnuPGUserStore = new class {
|
||||||
getPublicKeyFor(query, sign) {
|
getPublicKeyFor(query, sign) {
|
||||||
return findGnuPGKey(this.publicKeys, 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*/) {
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -162,28 +162,47 @@ export const OpenPGPUserStore = new class {
|
||||||
/**
|
/**
|
||||||
* https://docs.openpgpjs.org/#encrypt-and-decrypt-string-data-with-pgp-keys
|
* 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');
|
const message = await openpgp.readMessage({ armoredMessage: armoredText }),
|
||||||
if (!passphrase) {
|
privateKeys = this.privateKeys(),
|
||||||
return;
|
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) {
|
async verify(message, detachedSignature, publicKey) {
|
||||||
|
// message.getSigningKeyIDs();
|
||||||
return await openpgp.verify({
|
return await openpgp.verify({
|
||||||
message,
|
message,
|
||||||
verificationKeys: publicKey,
|
verificationKeys: publicKey,
|
||||||
|
|
|
||||||
|
|
@ -93,20 +93,6 @@ export const PgpUserStore = new class {
|
||||||
return false;
|
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*/) {
|
async getMailvelopePrivateKeyFor(email/*, sign*/) {
|
||||||
let keyring = this.mailvelopeKeyring;
|
let keyring = this.mailvelopeKeyring;
|
||||||
if (keyring && await keyring.hasPrivateKey({email:email})) {
|
if (keyring && await keyring.hasPrivateKey({email:email})) {
|
||||||
|
|
@ -120,54 +106,73 @@ export const PgpUserStore = new class {
|
||||||
* Returns the first library that can.
|
* Returns the first library that can.
|
||||||
*/
|
*/
|
||||||
async getKeyForSigning(email) {
|
async getKeyForSigning(email) {
|
||||||
return this.getGnuPGPrivateKeyFor(email, 1)
|
let key = GnuPGUserStore.getPrivateKeyFor(email, 1);
|
||||||
|| this.getOpenPGPPrivateKeyFor(email, 1)
|
if (key) {
|
||||||
|| await this.getMailvelopePrivateKeyFor(email, 1);
|
return ['gnupg', key];
|
||||||
|
}
|
||||||
|
|
||||||
|
key = OpenPGPUserStore.getPrivateKeyFor(email, 1);
|
||||||
|
if (key) {
|
||||||
|
return ['openpgp', key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.getMailvelopePrivateKeyFor(email, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async decrypt(message) {
|
||||||
* Checks if decrypting a message is possible with given keyIds or email address.
|
const sender = message.from[0].email,
|
||||||
* Returns the first library that can.
|
armoredText = message.plain();
|
||||||
*/
|
|
||||||
async getKeyForDecryption(ids, email) {
|
|
||||||
ids = [email].concat(ids);
|
|
||||||
let i = ids.length, key;
|
|
||||||
|
|
||||||
|
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 {
|
try {
|
||||||
key = await this.getMailvelopePrivateKeyFor(email);
|
let key = await this.getMailvelopePrivateKeyFor(message.to[0].email);
|
||||||
if (key) {
|
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) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
/* Not working, needs full fingerprint
|
|
||||||
while (i--) {
|
// Now try GnuPG
|
||||||
key = await this.getMailvelopePrivateKeyFor(ids[i]);
|
return GnuPGUserStore.decrypt(message);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -673,80 +673,13 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
}
|
}
|
||||||
|
|
||||||
pgpDecrypt(self) {
|
pgpDecrypt(self) {
|
||||||
const message = self.message(),
|
const message = self.message();
|
||||||
sender = message && message.from[0].email,
|
message && PgpUserStore.decrypt(message).then(result => {
|
||||||
pgpInfo = message && message.pgpEncrypted();
|
console.dir({decrypt_result:result});
|
||||||
if (pgpInfo) {
|
if (result) {
|
||||||
// Also check message.from[0].email
|
mimeToMessage(result.data, message);
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pgpVerify(self) {
|
pgpVerify(self) {
|
||||||
|
|
|
||||||
|
|
@ -64,16 +64,20 @@ trait Pgp
|
||||||
|
|
||||||
$sData = $this->GetActionParam('Data', '');
|
$sData = $this->GetActionParam('Data', '');
|
||||||
$oPart = null;
|
$oPart = null;
|
||||||
|
$result = [
|
||||||
|
'data' => '',
|
||||||
|
'signatures' => []
|
||||||
|
];
|
||||||
if ($sData) {
|
if ($sData) {
|
||||||
$result = $GPG->decrypt($sData);
|
$result = $GPG->decrypt($sData);
|
||||||
$oPart = \MailSo\Mime\Part::FromString($result);
|
// $oPart = \MailSo\Mime\Part::FromString($result);
|
||||||
} else {
|
} else {
|
||||||
$this->initMailClientConnection();
|
$this->initMailClientConnection();
|
||||||
$this->MailClient()->MessageMimeStream(
|
$this->MailClient()->MessageMimeStream(
|
||||||
function ($rResource) use ($GPG, $oPart) {
|
function ($rResource) use ($GPG, &$result, &$oPart) {
|
||||||
if (\is_resource($rResource)) {
|
if (\is_resource($rResource)) {
|
||||||
$result = $GPG->decryptStream($rResource);
|
$result['data'] = $GPG->decryptStream($rResource);
|
||||||
$oPart = \MailSo\Mime\Part::FromString($result);
|
// $oPart = \MailSo\Mime\Part::FromString($result);
|
||||||
// $GPG->decryptStream($rResource, $rStreamHandle);
|
// $GPG->decryptStream($rResource, $rStreamHandle);
|
||||||
// $oPart = \MailSo\Mime\Part::FromStream($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));
|
// $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
|
public function DoGnupgGetKeys() : array
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,9 @@ class GnuPG
|
||||||
if (!$fp || !\is_resource($fp)) {
|
if (!$fp || !\is_resource($fp)) {
|
||||||
throw new \Exception('Invalid stream resource');
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue