mirror of
https://github.com/the-djmaze/snappymail.git
synced 2024-09-21 16:06:01 +08:00
8dcd0cf833
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.
214 lines
5.5 KiB
JavaScript
214 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;
|
|
}
|
|
|
|
};
|