mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-11 00:14:50 +03:00
Now it does not fetch the PGP signature, because validation was broken anyway. Instead it validates multipart/signed according to RFC 3156 section 5 and returns details for the signed part: * BodyPartId * SigPartId * MicAlg So in the future several implementations (GnuPG, OpenPGP.js, etc.) can use the correct data for verification.
213 lines
5.5 KiB
JavaScript
213 lines
5.5 KiB
JavaScript
import ko from 'ko';
|
|
|
|
import { isArray, arrayLength, pString, addComputablesTo } from 'Common/Utils';
|
|
|
|
import { AccountUserStore } from 'Stores/User/Account';
|
|
|
|
import { showScreenPopup } from 'Knoin/Knoin';
|
|
|
|
import { MessageOpenPgpPopupView } from 'View/Popup/MessageOpenPgp';
|
|
|
|
export const PgpUserStore = new class {
|
|
constructor() {
|
|
this.openpgp = null;
|
|
|
|
this.openpgpkeys = ko.observableArray();
|
|
this.openpgpKeyring = null;
|
|
|
|
addComputablesTo(this, {
|
|
openpgpkeysPublic: () => this.openpgpkeys.filter(item => item && !item.isPrivate),
|
|
openpgpkeysPrivate: () => this.openpgpkeys.filter(item => item && item.isPrivate)
|
|
});
|
|
}
|
|
|
|
/**
|
|
* @returns {boolean}
|
|
*/
|
|
isSupported() {
|
|
return !!this.openpgp;
|
|
}
|
|
|
|
findKeyByHex(keys, hash) {
|
|
return keys.find(item => hash && item && (hash === item.id || item.ids.includes(hash)));
|
|
}
|
|
|
|
findPublicKeyByHex(hash) {
|
|
return this.findKeyByHex(this.openpgpkeysPublic(), hash);
|
|
}
|
|
|
|
findPrivateKeyByHex(hash) {
|
|
return this.findKeyByHex(this.openpgpkeysPrivate(), hash);
|
|
}
|
|
|
|
findPublicKeysByEmail(email) {
|
|
return this.openpgpkeysPublic().map(item => {
|
|
const key = item && item.emails.includes(email) ? item : null;
|
|
return key ? key.getNativeKeys() : [null];
|
|
}).flat().filter(v => v);
|
|
}
|
|
|
|
findPublicKeysBySigningKeyIds(signingKeyIds) {
|
|
return signingKeyIds.map(id => {
|
|
const key = id && id.toHex ? this.findPublicKeyByHex(id.toHex()) : null;
|
|
return key ? key.getNativeKeys() : [null];
|
|
}).flat().filter(v => v);
|
|
}
|
|
|
|
findPrivateKeysByEncryptionKeyIds(encryptionKeyIds, recipients, returnWrapKeys) {
|
|
let result = isArray(encryptionKeyIds)
|
|
? encryptionKeyIds.map(id => {
|
|
const key = id && id.toHex ? this.findPrivateKeyByHex(id.toHex()) : null;
|
|
return key ? (returnWrapKeys ? [key] : key.getNativeKeys()) : [null];
|
|
}).flat().filter(v => v)
|
|
: [];
|
|
|
|
if (!result.length && arrayLength(recipients)) {
|
|
result = recipients.map(sEmail => {
|
|
const keys = sEmail ? this.findAllPrivateKeysByEmailNotNative(sEmail) : null;
|
|
return keys
|
|
? returnWrapKeys
|
|
? keys
|
|
: keys.map(key => key.getNativeKeys()).flat()
|
|
: [null];
|
|
}).flat().validUnique(key => key.id);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @param {string} email
|
|
* @returns {?}
|
|
*/
|
|
findPublicKeyByEmailNotNative(email) {
|
|
return this.openpgpkeysPublic().find(item => item && item.emails.includes(email)) || null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} email
|
|
* @returns {?}
|
|
*/
|
|
findPrivateKeyByEmailNotNative(email) {
|
|
return this.openpgpkeysPrivate().find(item => item && item.emails.includes(email)) || null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} email
|
|
* @returns {?}
|
|
*/
|
|
findAllPublicKeysByEmailNotNative(email) {
|
|
return this.openpgpkeysPublic().filter(item => item && item.emails.includes(email)) || null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} email
|
|
* @returns {?}
|
|
*/
|
|
findAllPrivateKeysByEmailNotNative(email) {
|
|
return this.openpgpkeysPrivate().filter(item => item && item.emails.includes(email)) || null;
|
|
}
|
|
|
|
/**
|
|
* @param {string} email
|
|
* @param {string=} password
|
|
* @returns {?}
|
|
*/
|
|
findPrivateKeyByEmail(email, password) {
|
|
let privateKey = null;
|
|
const key = this.openpgpkeysPrivate().find(item => item && item.emails.includes(email));
|
|
|
|
if (key) {
|
|
try {
|
|
privateKey = key.getNativeKeys()[0] || null;
|
|
if (privateKey) {
|
|
privateKey.decrypt(pString(password));
|
|
}
|
|
} catch (e) {
|
|
privateKey = null;
|
|
}
|
|
}
|
|
|
|
return privateKey;
|
|
}
|
|
|
|
/**
|
|
* @param {string=} password
|
|
* @returns {?}
|
|
*/
|
|
findSelfPrivateKey(password) {
|
|
return this.findPrivateKeyByEmail(AccountUserStore.email(), password);
|
|
}
|
|
|
|
decryptMessage(message, recipients, fCallback) {
|
|
if (message && message.getEncryptionKeyIds) {
|
|
const privateKeys = this.findPrivateKeysByEncryptionKeyIds(message.getEncryptionKeyIds(), recipients, true);
|
|
if (privateKeys && privateKeys.length) {
|
|
showScreenPopup(MessageOpenPgpPopupView, [
|
|
(decryptedKey) => {
|
|
if (decryptedKey) {
|
|
message.decrypt(decryptedKey).then(
|
|
(decryptedMessage) => {
|
|
let privateKey = null;
|
|
if (decryptedMessage) {
|
|
privateKey = this.findPrivateKeyByHex(decryptedKey.primaryKey.keyid.toHex());
|
|
if (privateKey) {
|
|
this.verifyMessage(decryptedMessage, (oValidKey, aSigningKeyIds) => {
|
|
fCallback(privateKey, decryptedMessage, oValidKey || null, aSigningKeyIds || null);
|
|
});
|
|
} else {
|
|
fCallback(privateKey, decryptedMessage);
|
|
}
|
|
} else {
|
|
fCallback(privateKey, decryptedMessage);
|
|
}
|
|
},
|
|
() => {
|
|
fCallback(null, null);
|
|
}
|
|
);
|
|
} else {
|
|
fCallback(null, null);
|
|
}
|
|
},
|
|
privateKeys
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fCallback(null, null);
|
|
|
|
return false;
|
|
}
|
|
|
|
verifyMessage(message, fCallback) {
|
|
if (message && message.getSigningKeyIds) {
|
|
const signingKeyIds = message.getSigningKeyIds();
|
|
if (signingKeyIds && signingKeyIds.length) {
|
|
const publicKeys = this.findPublicKeysBySigningKeyIds(signingKeyIds);
|
|
if (publicKeys && publicKeys.length) {
|
|
try {
|
|
const result = message.verify(publicKeys),
|
|
valid = (isArray(result) ? result : []).find(item => item && item.valid && item.keyid);
|
|
|
|
if (valid && valid.keyid && valid.keyid && valid.keyid.toHex) {
|
|
fCallback(this.findPublicKeyByHex(valid.keyid.toHex()));
|
|
return true;
|
|
}
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
}
|
|
|
|
fCallback(null, signingKeyIds);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
fCallback(null);
|
|
return false;
|
|
}
|
|
|
|
};
|