mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
#89 verify PGP signatures using OpenPGP.js
This commit is contained in:
parent
93db6e6e0e
commit
da79feeaee
5 changed files with 93 additions and 72 deletions
|
|
@ -7,6 +7,8 @@ import ko from 'ko';
|
||||||
import { arrayLength } from 'Common/Utils';
|
import { arrayLength } from 'Common/Utils';
|
||||||
import { delegateRunOnDestroy } from 'Common/UtilsUser';
|
import { delegateRunOnDestroy } from 'Common/UtilsUser';
|
||||||
|
|
||||||
|
import Remote from 'Remote/User/Fetch';
|
||||||
|
|
||||||
import { showScreenPopup } from 'Knoin/Knoin';
|
import { showScreenPopup } from 'Knoin/Knoin';
|
||||||
import { OpenPgpKeyPopupView } from 'View/Popup/OpenPgpKey';
|
import { OpenPgpKeyPopupView } from 'View/Popup/OpenPgpKey';
|
||||||
|
|
||||||
|
|
@ -201,14 +203,37 @@ export const OpenPGPUserStore = new class {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async verify(message, detachedSignature, publicKey) {
|
/**
|
||||||
// message.getSigningKeyIDs();
|
* https://docs.openpgpjs.org/#sign-and-verify-cleartext-messages
|
||||||
return await openpgp.verify({
|
*/
|
||||||
message,
|
async verify(message) {
|
||||||
verificationKeys: publicKey,
|
const data = message.pgpSigned(), // { BodyPartId: "1", SigPartId: "2", MicAlg: "pgp-sha256" }
|
||||||
// expectSigned: true, // !!detachedSignature
|
publicKey = this.publicKeys().find(key => key.emails.includes(message.from[0].email));
|
||||||
signature: detachedSignature
|
if (data && publicKey) {
|
||||||
});
|
data.Folder = message.folder;
|
||||||
|
data.Uid = message.uid;
|
||||||
|
data.GnuPG = 0;
|
||||||
|
let response = await Remote.post('MessagePgpVerify', null, data);
|
||||||
|
if (response) {
|
||||||
|
const signature = response.Result.signature
|
||||||
|
? await openpgp.readSignature({ armoredSignature: response.Result.signature })
|
||||||
|
: null;
|
||||||
|
const signedMessage = signature
|
||||||
|
? await openpgp.createMessage({ text: response.Result.text })
|
||||||
|
: await openpgp.readCleartextMessage({ cleartextMessage: response.Result.text });
|
||||||
|
// (signature||signedMessage).getSigningKeyIDs();
|
||||||
|
let result = await openpgp.verify({
|
||||||
|
message: signedMessage,
|
||||||
|
verificationKeys: publicKey.key,
|
||||||
|
// expectSigned: true, // !!detachedSignature
|
||||||
|
signature: signature
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
fingerprint: publicKey.fingerprint,
|
||||||
|
success: result && !!result.signatures.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ export const PgpUserStore = new class {
|
||||||
if (/-----BEGIN PGP SIGNED MESSAGE-----/.test(plain) && /-----BEGIN PGP SIGNATURE-----/.test(plain)) {
|
if (/-----BEGIN PGP SIGNED MESSAGE-----/.test(plain) && /-----BEGIN PGP SIGNATURE-----/.test(plain)) {
|
||||||
let result = await OpenPGPUserStore.verify(plain);
|
let result = await OpenPGPUserStore.verify(plain);
|
||||||
console.dir(result);
|
console.dir(result);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (message.pgpSigned()) {
|
if (message.pgpSigned()) {
|
||||||
const sender = message.from[0].email;
|
const sender = message.from[0].email;
|
||||||
|
|
@ -188,34 +189,11 @@ export const PgpUserStore = new class {
|
||||||
return GnuPGUserStore.verify(message);
|
return GnuPGUserStore.verify(message);
|
||||||
}
|
}
|
||||||
if ('openpgp' === mode) {
|
if ('openpgp' === mode) {
|
||||||
const publicKey = OpenPGPUserStore.getPublicKeyFor(sender);
|
return OpenPGPUserStore.verify(message);
|
||||||
OpenPGPUserStore.verify(plain, null/*detachedSignature*/, publicKey).then(result => {
|
|
||||||
if (result) {
|
|
||||||
message.plain(result.data);
|
|
||||||
message.viewPlain();
|
|
||||||
console.dir({signatures:result.signatures});
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if (validKey) {
|
|
||||||
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
|
||||||
USER: validKey.user + ' (' + validKey.id + ')'
|
|
||||||
});
|
|
||||||
message.getText()
|
|
||||||
} else {
|
|
||||||
const keyIds = arrayLength(signingKeyIds) ? signingKeyIds : null,
|
|
||||||
additional = keyIds
|
|
||||||
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
|
|
||||||
: '';
|
|
||||||
|
|
||||||
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : '');
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an iframe with an editor for a new encrypted mail.
|
* Creates an iframe with an editor for a new encrypted mail.
|
||||||
* The iframe will be injected into the container identified by selector.
|
* The iframe will be injected into the container identified by selector.
|
||||||
|
|
|
||||||
|
|
@ -457,9 +457,9 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
data: params.Text,
|
data: params.Text,
|
||||||
};
|
};
|
||||||
if ('openpgp' == sign) {
|
if ('openpgp' == sign) {
|
||||||
let privateKey;
|
let privateKey, sender = this.currentIdentity().email();
|
||||||
try {
|
try {
|
||||||
const key = OpenPGPUserStore.getPrivateKeyFor(this.currentIdentity().email());
|
const key = OpenPGPUserStore.getPrivateKeyFor(sender);
|
||||||
if (key) {
|
if (key) {
|
||||||
key.decrypt(window.prompt('Passphrase'));
|
key.decrypt(window.prompt('Passphrase'));
|
||||||
cfg.privateKey = privateKey = key;
|
cfg.privateKey = privateKey = key;
|
||||||
|
|
@ -470,7 +470,7 @@ class ComposePopupView extends AbstractViewPopup {
|
||||||
}
|
}
|
||||||
if (!privateKey) {
|
if (!privateKey) {
|
||||||
this.sendError(true);
|
this.sendError(true);
|
||||||
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND'));
|
this.sendErrorDesc(i18n('PGP_NOTIFICATIONS/NO_PRIVATE_KEY_FOUND_FOR', { EMAIL: sender }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -641,10 +641,26 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
pgpVerify(/*self, event*/) {
|
pgpVerify(/*self, event*/) {
|
||||||
const oMessage = currentMessage()/*, ctrl = event.target.closest('.openpgp-control')*/;
|
const oMessage = currentMessage()/*, ctrl = event.target.closest('.openpgp-control')*/;
|
||||||
PgpUserStore.verify(oMessage).then(result => {
|
PgpUserStore.verify(oMessage).then(result => {
|
||||||
console.dir({result:result});
|
|
||||||
if (result) {
|
if (result) {
|
||||||
oMessage.pgpVerified(result);
|
oMessage.pgpVerified(result);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
if (result && result.success) {
|
||||||
|
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
|
||||||
|
USER: validKey.user + ' (' + validKey.id + ')'
|
||||||
|
});
|
||||||
|
message.getText()
|
||||||
|
} else {
|
||||||
|
const keyIds = arrayLength(signingKeyIds) ? signingKeyIds : null,
|
||||||
|
additional = keyIds
|
||||||
|
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
|
||||||
|
: '';
|
||||||
|
|
||||||
|
i18n('PGP_NOTIFICATIONS/PGP_ERROR', {
|
||||||
|
ERROR: 'message'
|
||||||
|
}) + (additional ? ' (' + additional + ')' : '');
|
||||||
|
}
|
||||||
|
*/
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -700,45 +700,47 @@ trait Messages
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$GPG = $this->GnuPG();
|
if ($this->GetActionParam('GnuPG', 1)) {
|
||||||
if ($GPG) {
|
$GPG = $this->GnuPG();
|
||||||
$info = $this->GnuPG()->verify($result['text'], $result['signature'])[0];
|
if ($GPG) {
|
||||||
// $info = $this->GnuPG()->verifyStream($fp, $result['signature'])[0];
|
$info = $this->GnuPG()->verify($result['text'], $result['signature'])[0];
|
||||||
|
// $info = $this->GnuPG()->verifyStream($fp, $result['signature'])[0];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://code.woboq.org/qt5/include/gpg-error.h.html
|
* https://code.woboq.org/qt5/include/gpg-error.h.html
|
||||||
* status:
|
* status:
|
||||||
0 = GPG_ERR_NO_ERROR
|
0 = GPG_ERR_NO_ERROR
|
||||||
9 = GPG_ERR_NO_PUBKEY
|
9 = GPG_ERR_NO_PUBKEY
|
||||||
117440513 = General error
|
117440513 = General error
|
||||||
117440520 = Bad signature
|
117440520 = Bad signature
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$summary = [
|
$summary = [
|
||||||
GNUPG_SIGSUM_VALID => 'The signature is fully valid.',
|
GNUPG_SIGSUM_VALID => 'The signature is fully valid.',
|
||||||
GNUPG_SIGSUM_GREEN => 'The signature is good but one might want to display some extra information. Check the other bits.',
|
GNUPG_SIGSUM_GREEN => 'The signature is good but one might want to display some extra information. Check the other bits.',
|
||||||
GNUPG_SIGSUM_RED => 'The signature is bad. It might be useful to check other bits and display more information, i.e. a revoked certificate might not render a signature invalid when the message was received prior to the cause for the revocation.',
|
GNUPG_SIGSUM_RED => 'The signature is bad. It might be useful to check other bits and display more information, i.e. a revoked certificate might not render a signature invalid when the message was received prior to the cause for the revocation.',
|
||||||
GNUPG_SIGSUM_KEY_REVOKED => 'The key or at least one certificate has been revoked.',
|
GNUPG_SIGSUM_KEY_REVOKED => 'The key or at least one certificate has been revoked.',
|
||||||
GNUPG_SIGSUM_KEY_EXPIRED => 'The key or one of the certificates has expired. It is probably a good idea to display the date of the expiration.',
|
GNUPG_SIGSUM_KEY_EXPIRED => 'The key or one of the certificates has expired. It is probably a good idea to display the date of the expiration.',
|
||||||
GNUPG_SIGSUM_SIG_EXPIRED => 'The signature has expired.',
|
GNUPG_SIGSUM_SIG_EXPIRED => 'The signature has expired.',
|
||||||
GNUPG_SIGSUM_KEY_MISSING => 'Can’t verify due to a missing key or certificate.',
|
GNUPG_SIGSUM_KEY_MISSING => 'Can’t verify due to a missing key or certificate.',
|
||||||
GNUPG_SIGSUM_CRL_MISSING => 'The CRL (or an equivalent mechanism) is not available.',
|
GNUPG_SIGSUM_CRL_MISSING => 'The CRL (or an equivalent mechanism) is not available.',
|
||||||
GNUPG_SIGSUM_CRL_TOO_OLD => 'Available CRL is too old.',
|
GNUPG_SIGSUM_CRL_TOO_OLD => 'Available CRL is too old.',
|
||||||
GNUPG_SIGSUM_BAD_POLICY => 'A policy requirement was not met.',
|
GNUPG_SIGSUM_BAD_POLICY => 'A policy requirement was not met.',
|
||||||
GNUPG_SIGSUM_SYS_ERROR => 'A system error occurred.',
|
GNUPG_SIGSUM_SYS_ERROR => 'A system error occurred.',
|
||||||
// GNUPG_SIGSUM_TOFU_CONFLICT = 'A TOFU conflict was detected.',
|
// GNUPG_SIGSUM_TOFU_CONFLICT = 'A TOFU conflict was detected.',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Verified, so no need to return $result['text'] and $result['signature']
|
// Verified, so no need to return $result['text'] and $result['signature']
|
||||||
$result = [
|
$result = [
|
||||||
'fingerprint' => $info['fingerprint'],
|
'fingerprint' => $info['fingerprint'],
|
||||||
'validity' => $info['validity'],
|
'validity' => $info['validity'],
|
||||||
'status' => $info['status'],
|
'status' => $info['status'],
|
||||||
'summary' => $info['summary'],
|
'summary' => $info['summary'],
|
||||||
'message' => \implode("\n", \array_filter($summary, function($k) use ($info) {
|
'message' => \implode("\n", \array_filter($summary, function($k) use ($info) {
|
||||||
return $info['summary'] & $k;
|
return $info['summary'] & $k;
|
||||||
}, ARRAY_FILTER_USE_KEY))
|
}, ARRAY_FILTER_USE_KEY))
|
||||||
];
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->DefaultResponse(__FUNCTION__, $result);
|
return $this->DefaultResponse(__FUNCTION__, $result);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue