#89 revamp code for better decryption and get it working for GnuPG

This commit is contained in:
the-djmaze 2022-02-01 11:45:20 +01:00
parent 4eb70c3f06
commit d9e81d6123
6 changed files with 152 additions and 152 deletions

View file

@ -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*/) {
}
}; };

View file

@ -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,

View file

@ -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;
}
}
} }
/** /**

View file

@ -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) {

View file

@ -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

View file

@ -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);
} }
/** /**