mirror of
https://github.com/the-djmaze/snappymail.git
synced 2025-09-21 20:44:25 +08:00
Split OpenPGP.js and GnuPG from PgpUserStore
This commit is contained in:
parent
76226f45ca
commit
76361a13da
6 changed files with 453 additions and 369 deletions
|
@ -1,4 +1,5 @@
|
|||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
import { SettingsUserStore } from 'Stores/User/Settings';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
@ -13,14 +14,14 @@ import { Settings } from 'Common/Globals';
|
|||
|
||||
export class OpenPgpUserSettings /*extends AbstractViewSettings*/ {
|
||||
constructor() {
|
||||
this.gnupgPublicKeys = PgpUserStore.gnupgPublicKeys;
|
||||
this.gnupgPrivateKeys = PgpUserStore.gnupgPrivateKeys;
|
||||
this.gnupgPublicKeys = GnuPGUserStore.publicKeys;
|
||||
this.gnupgPrivateKeys = GnuPGUserStore.privateKeys;
|
||||
|
||||
this.openpgpkeysPublic = PgpUserStore.openpgpPublicKeys;
|
||||
this.openpgpkeysPrivate = PgpUserStore.openpgpPrivateKeys;
|
||||
this.openpgpkeysPublic = OpenPGPUserStore.publicKeys;
|
||||
this.openpgpkeysPrivate = OpenPGPUserStore.privateKeys;
|
||||
|
||||
this.canOpenPGP = Settings.capa(Capa.OpenPGP);
|
||||
this.canGnuPG = Settings.capa(Capa.GnuPG);
|
||||
this.canGnuPG = GnuPGUserStore.isSupported();
|
||||
this.canMailvelope = !!window.mailvelope;
|
||||
|
||||
this.allowDraftAutosave = SettingsUserStore.allowDraftAutosave;
|
||||
|
|
154
dev/Stores/User/GnuPG.js
Normal file
154
dev/Stores/User/GnuPG.js
Normal file
|
@ -0,0 +1,154 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Capa } from 'Common/Enums';
|
||||
import { Settings } from 'Common/Globals';
|
||||
import { delegateRunOnDestroy } from 'Common/UtilsUser';
|
||||
|
||||
//import { showScreenPopup } from 'Knoin/Knoin';
|
||||
|
||||
//import { EmailModel } from 'Model/Email';
|
||||
//import { OpenPgpKeyModel } from 'Model/OpenPgpKey';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { OpenPgpKeyPopupView } from 'View/Popup/OpenPgpKey';
|
||||
|
||||
const
|
||||
findGnuPGKey = (keys, query, sign) =>
|
||||
keys.find(key =>
|
||||
key[sign ? 'can_sign' : 'can_decrypt']
|
||||
&& (key.emails.includes(query) || key.subkeys.find(key => query == key.keyid || query == key.fingerprint))
|
||||
);
|
||||
|
||||
export const GnuPGUserStore = new class {
|
||||
constructor() {
|
||||
/**
|
||||
* PECL gnupg / PEAR Crypt_GPG
|
||||
* [ {email, can_encrypt, can_sign}, ... ]
|
||||
*/
|
||||
this.keyring;
|
||||
this.publicKeys = ko.observableArray();
|
||||
this.privateKeys = ko.observableArray();
|
||||
}
|
||||
|
||||
loadKeyrings(/*identifier*/) {
|
||||
this.keyring = null;
|
||||
this.publicKeys([]);
|
||||
this.privateKeys([]);
|
||||
Remote.request('GnupgGetKeys',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
this.keyring = oData.Result;
|
||||
const initKey = (key, isPrivate) => {
|
||||
const aEmails = [];
|
||||
key.id = key.subkeys[0].keyid;
|
||||
key.fingerprint = key.subkeys[0].fingerprint;
|
||||
key.uids.forEach(uid => uid.email && aEmails.push(uid.email));
|
||||
key.emails = aEmails;
|
||||
key.askDelete = ko.observable(false);
|
||||
key.openForDeletion = ko.observable(null).askDeleteHelper();
|
||||
key.remove = () => {
|
||||
if (key.askDelete()) {
|
||||
Remote.request('GnupgDeleteKey',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
if (isPrivate) {
|
||||
this.privateKeys.remove(key);
|
||||
} else {
|
||||
this.publicKeys.remove(key);
|
||||
}
|
||||
delegateRunOnDestroy(key);
|
||||
}
|
||||
}, {
|
||||
KeyId: key.id,
|
||||
isPrivate: isPrivate
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
key.view = () => {
|
||||
let pass = isPrivate ? prompt('Passphrase') : true;
|
||||
if (pass) {
|
||||
Remote.request('GnupgExportKey',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
key.armor = oData.Result;
|
||||
showScreenPopup(OpenPgpKeyPopupView, [key]);
|
||||
}
|
||||
}, {
|
||||
KeyId: key.id,
|
||||
isPrivate: isPrivate,
|
||||
Passphrase: isPrivate ? pass : ''
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
return key;
|
||||
};
|
||||
this.publicKeys(oData.Result.public.map(key => initKey(key, 0)));
|
||||
this.privateKeys(oData.Result.private.map(key => initKey(key, 1)));
|
||||
console.log('gnupg ready');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSupported() {
|
||||
return Settings.capa(Capa.GnuPG);
|
||||
}
|
||||
|
||||
importKey(key, callback) {
|
||||
Remote.request('GnupgImportKey',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
// this.gnupgKeyring = oData.Result;
|
||||
}
|
||||
callback && callback(iError, oData);
|
||||
}, {
|
||||
Key: key
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
keyPair.privateKey
|
||||
keyPair.publicKey
|
||||
keyPair.revocationCertificate
|
||||
keyPair.onServer
|
||||
keyPair.inGnuPG
|
||||
*/
|
||||
storeKeyPair(keyPair, callback) {
|
||||
Remote.request('PgpStoreKeyPair',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
// this.gnupgKeyring = oData.Result;
|
||||
}
|
||||
callback && callback(iError, oData);
|
||||
}, keyPair
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if verifying/encrypting a message is possible with given email addresses.
|
||||
*/
|
||||
hasPublicKeyForEmails(recipients, all) {
|
||||
const count = recipients.length,
|
||||
length = count ? recipients.filter(email =>
|
||||
// (key.can_verify || key.can_encrypt) &&
|
||||
this.publicKeys.find(key => key.emails.includes(email))
|
||||
).length : 0;
|
||||
return length && (!all || length === count);
|
||||
}
|
||||
|
||||
getPrivateKeyFor(query, sign) {
|
||||
return findGnuPGKey(this.privateKeys, query, sign);
|
||||
}
|
||||
|
||||
getPublicKeyFor(query, sign) {
|
||||
return findGnuPGKey(this.publicKeys, query, sign);
|
||||
}
|
||||
};
|
268
dev/Stores/User/OpenPGP.js
Normal file
268
dev/Stores/User/OpenPGP.js
Normal file
|
@ -0,0 +1,268 @@
|
|||
/**
|
||||
* OpenPGP.js
|
||||
*/
|
||||
|
||||
import ko from 'ko';
|
||||
|
||||
import { isArray, arrayLength } from 'Common/Utils';
|
||||
import { delegateRunOnDestroy } from 'Common/UtilsUser';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { OpenPgpKeyPopupView } from 'View/Popup/OpenPgpKey';
|
||||
|
||||
const
|
||||
findKeyByHex = (keys, hash) =>
|
||||
keys.find(item => item && (hash === item.id || item.ids.includes(hash))),
|
||||
|
||||
findOpenPGPKey = (keys, query/*, sign*/) =>
|
||||
keys.find(key =>
|
||||
key.emails.includes(query) || query == key.id || query == key.fingerprint
|
||||
),
|
||||
|
||||
/**
|
||||
* OpenPGP.js v5 removed the localStorage (keyring)
|
||||
* This should be compatible with the old OpenPGP.js v2
|
||||
*/
|
||||
publicKeysItem = 'openpgp-public-keys',
|
||||
privateKeysItem = 'openpgp-private-keys',
|
||||
storage = window.localStorage,
|
||||
loadOpenPgpKeys = async itemname => {
|
||||
let keys = [], key,
|
||||
armoredKeys = JSON.parse(storage.getItem(itemname)),
|
||||
i = arrayLength(armoredKeys);
|
||||
while (i--) {
|
||||
key = await openpgp.readKey({armoredKey:armoredKeys[i]});
|
||||
if (!key.err) {
|
||||
keys.push(new OpenPgpKeyModel(armoredKeys[i], key));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
},
|
||||
storeOpenPgpKeys = (keys, section) => {
|
||||
let armoredKeys = keys.map(item => item.armor);
|
||||
if (armoredKeys.length) {
|
||||
storage.setItem(section, JSON.stringify(armoredKeys));
|
||||
} else {
|
||||
storage.removeItem(section);
|
||||
}
|
||||
};
|
||||
|
||||
class OpenPgpKeyModel {
|
||||
constructor(armor, key) {
|
||||
this.key = key;
|
||||
const aEmails = [];
|
||||
if (key.users) {
|
||||
key.users.forEach(user => user.userID.email && aEmails.push(user.userID.email));
|
||||
}
|
||||
this.id = key.getKeyID().toHex();
|
||||
this.fingerprint = key.getFingerprint();
|
||||
this.can_encrypt = !!key.getEncryptionKey();
|
||||
this.can_sign = !!key.getSigningKey();
|
||||
this.emails = aEmails;
|
||||
this.armor = armor;
|
||||
this.askDelete = ko.observable(false);
|
||||
this.openForDeletion = ko.observable(null).askDeleteHelper();
|
||||
// key.getUserIDs()
|
||||
// key.getPrimaryUser()
|
||||
}
|
||||
|
||||
view() {
|
||||
showScreenPopup(OpenPgpKeyPopupView, [this]);
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.askDelete()) {
|
||||
if (this.key.isPrivate()) {
|
||||
OpenPGPUserStore.privateKeys.remove(this);
|
||||
storeOpenPgpKeys(OpenPGPUserStore.privateKeys, privateKeysItem);
|
||||
} else {
|
||||
OpenPGPUserStore.publicKeys.remove(this);
|
||||
storeOpenPgpKeys(OpenPGPUserStore.publicKeys, publicKeysItem);
|
||||
}
|
||||
delegateRunOnDestroy(this);
|
||||
}
|
||||
}
|
||||
/*
|
||||
toJSON() {
|
||||
return this.armor;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
export const OpenPGPUserStore = new class {
|
||||
constructor() {
|
||||
this.publicKeys = ko.observableArray();
|
||||
this.privateKeys = ko.observableArray();
|
||||
}
|
||||
|
||||
loadKeyrings() {
|
||||
loadOpenPgpKeys(publicKeysItem).then(keys => {
|
||||
this.publicKeys(keys || []);
|
||||
console.log('openpgp.js public keys loaded');
|
||||
});
|
||||
loadOpenPgpKeys(privateKeysItem).then(keys => {
|
||||
this.privateKeys(keys || [])
|
||||
console.log('openpgp.js private keys loaded');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isSupported() {
|
||||
return !!window.openpgp;
|
||||
}
|
||||
|
||||
importKey(armoredKey) {
|
||||
openpgp.readKey({armoredKey:armoredKey}).then(key => {
|
||||
if (!key.err) {
|
||||
if (key.isPrivate()) {
|
||||
this.privateKeys.push(new OpenPgpKeyModel(armoredKey, key));
|
||||
storeOpenPgpKeys(this.privateKeys, privateKeysItem);
|
||||
} else {
|
||||
this.publicKeys.push(new OpenPgpKeyModel(armoredKey, key));
|
||||
storeOpenPgpKeys(this.publicKeys, publicKeysItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
keyPair.privateKey
|
||||
keyPair.publicKey
|
||||
keyPair.revocationCertificate
|
||||
keyPair.onServer
|
||||
keyPair.inGnuPG
|
||||
*/
|
||||
storeKeyPair(keyPair) {
|
||||
openpgp.readKey({armoredKey:keyPair.publicKey}).then(key => {
|
||||
this.publicKeys.push(new OpenPgpKeyModel(keyPair.publicKey, key));
|
||||
storeOpenPgpKeys(this.publicKeys, publicKeysItem);
|
||||
});
|
||||
openpgp.readKey({armoredKey:keyPair.privateKey}).then(key => {
|
||||
this.privateKeys.push(new OpenPgpKeyModel(keyPair.privateKey, key));
|
||||
storeOpenPgpKeys(this.privateKeys, privateKeysItem);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if verifying/encrypting a message is possible with given email addresses.
|
||||
*/
|
||||
hasPublicKeyForEmails(recipients, all) {
|
||||
const count = recipients.length,
|
||||
length = count ? recipients.filter(email =>
|
||||
this.publicKeys().find(key => key.emails.includes(email))
|
||||
).length : 0;
|
||||
return length && (!all || length === count);
|
||||
}
|
||||
|
||||
getPrivateKeyFor(query/*, sign*/) {
|
||||
return findOpenPGPKey(this.privateKeys, query/*, sign*/);
|
||||
}
|
||||
|
||||
getPublicKeyFor(query/*, sign*/) {
|
||||
return findOpenPGPKey(this.publicKeys, query/*, sign*/);
|
||||
}
|
||||
|
||||
/*
|
||||
decryptMessage(message, recipients, fCallback) {
|
||||
message = store.openpgp.message.readArmored(armoredMessage);
|
||||
try {
|
||||
message = store.openpgp.message.readArmored(armoredMessage);
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
if (message && message.getText && message.verify && message.decrypt) {
|
||||
if (message && message.getEncryptionKeyIds) {
|
||||
// findPrivateKeysByEncryptionKeyIds
|
||||
const encryptionKeyIds = message.getEncryptionKeyIds();
|
||||
let privateKeys = isArray(encryptionKeyIds)
|
||||
? encryptionKeyIds.map(id => {
|
||||
// openpgpKeyring.publicKeys.getForId(id.toHex())
|
||||
// openpgpKeyring.privateKeys.getForId(id.toHex())
|
||||
const key = id && id.toHex ? findKeyByHex(this.privateKeys, id.toHex()) : null;
|
||||
return key ? [key] : [null];
|
||||
}).flat().filter(v => v)
|
||||
: [];
|
||||
if (!privateKeys.length && arrayLength(recipients)) {
|
||||
privateKeys = recipients.map(sEmail =>
|
||||
(sEmail
|
||||
? this.privateKeys.filter(item => item && item.emails.includes(sEmail)) : 0)
|
||||
|| [null]
|
||||
).flat().validUnique(key => key.id);
|
||||
}
|
||||
|
||||
if (privateKeys && privateKeys.length) {
|
||||
showScreenPopup(OpenPgpSelectorPopupView, [
|
||||
(decryptedKey) => {
|
||||
if (decryptedKey) {
|
||||
message.decrypt(decryptedKey).then(
|
||||
(decryptedMessage) => {
|
||||
let privateKey = null;
|
||||
if (decryptedMessage) {
|
||||
privateKey = findKeyByHex(this.privateKeys, 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) {
|
||||
// findPublicKeysBySigningKeyIds
|
||||
const publicKeys = signingKeyIds.map(id => {
|
||||
const key = id && id.toHex ? findKeyByHex(this.publicKeys, id.toHex()) : null;
|
||||
return key ? key.key : [null];
|
||||
}).flat().filter(v => v);
|
||||
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(findKeyByHex(this.publicKeys, valid.keyid.toHex()));
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
fCallback(null, signingKeyIds);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fCallback(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
|
@ -1,121 +1,17 @@
|
|||
import ko from 'ko';
|
||||
|
||||
import { Capa } from 'Common/Enums';
|
||||
import { doc, createElement, Settings } from 'Common/Globals';
|
||||
import { staticLink } from 'Common/Links';
|
||||
import { isArray, arrayLength } from 'Common/Utils';
|
||||
import { delegateRunOnDestroy } from 'Common/UtilsUser';
|
||||
|
||||
//import { showScreenPopup } from 'Knoin/Knoin';
|
||||
|
||||
//import { EmailModel } from 'Model/Email';
|
||||
//import { OpenPgpKeyModel } from 'Model/OpenPgpKey';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
||||
import { showScreenPopup } from 'Knoin/Knoin';
|
||||
import { OpenPgpKeyPopupView } from 'View/Popup/OpenPgpKey';
|
||||
|
||||
const
|
||||
findKeyByHex = (keys, hash) =>
|
||||
keys.find(item => item && (hash === item.id || item.ids.includes(hash))),
|
||||
|
||||
findGnuPGKey = (keys, query, sign) =>
|
||||
keys.find(key =>
|
||||
key[sign ? 'can_sign' : 'can_decrypt']
|
||||
&& (key.emails.includes(query) || key.subkeys.find(key => query == key.keyid || query == key.fingerprint))
|
||||
),
|
||||
|
||||
findOpenPGPKey = (keys, query/*, sign*/) =>
|
||||
keys.find(key =>
|
||||
key.emails.includes(query) || query == key.id || query == key.fingerprint
|
||||
);
|
||||
|
||||
/**
|
||||
* OpenPGP.js v5 removed the localStorage (keyring)
|
||||
* This should be compatible with the old OpenPGP.js v2
|
||||
*/
|
||||
const
|
||||
publicKeysItem = 'openpgp-public-keys',
|
||||
privateKeysItem = 'openpgp-private-keys',
|
||||
storage = window.localStorage,
|
||||
loadOpenPgpKeys = async itemname => {
|
||||
let keys = [], key,
|
||||
armoredKeys = JSON.parse(storage.getItem(itemname)),
|
||||
i = arrayLength(armoredKeys);
|
||||
while (i--) {
|
||||
key = await openpgp.readKey({armoredKey:armoredKeys[i]});
|
||||
if (!key.err) {
|
||||
keys.push(new OpenPgpKeyModel(armoredKeys[i], key));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
},
|
||||
storeOpenPgpKeys = (keys, section) => {
|
||||
let armoredKeys = keys.map(item => item.armor);
|
||||
if (armoredKeys.length) {
|
||||
storage.setItem(section, JSON.stringify(armoredKeys));
|
||||
} else {
|
||||
storage.removeItem(section);
|
||||
}
|
||||
};
|
||||
|
||||
class OpenPgpKeyModel {
|
||||
constructor(armor, key) {
|
||||
this.key = key;
|
||||
const aEmails = [];
|
||||
if (key.users) {
|
||||
key.users.forEach(user => user.userID.email && aEmails.push(user.userID.email));
|
||||
}
|
||||
this.id = key.getKeyID().toHex();
|
||||
this.fingerprint = key.getFingerprint();
|
||||
this.can_encrypt = !!key.getEncryptionKey();
|
||||
this.can_sign = !!key.getSigningKey();
|
||||
this.emails = aEmails;
|
||||
this.armor = armor;
|
||||
this.askDelete = ko.observable(false);
|
||||
this.openForDeletion = ko.observable(null).askDeleteHelper();
|
||||
// key.getUserIDs()
|
||||
// key.getPrimaryUser()
|
||||
}
|
||||
|
||||
view() {
|
||||
showScreenPopup(OpenPgpKeyPopupView, [this]);
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.askDelete()) {
|
||||
if (this.key.isPrivate()) {
|
||||
PgpUserStore.openpgpPrivateKeys.remove(this);
|
||||
storeOpenPgpKeys(PgpUserStore.openpgpPrivateKeys, privateKeysItem);
|
||||
} else {
|
||||
PgpUserStore.openpgpPublicKeys.remove(this);
|
||||
storeOpenPgpKeys(PgpUserStore.openpgpPublicKeys, publicKeysItem);
|
||||
}
|
||||
delegateRunOnDestroy(this);
|
||||
}
|
||||
}
|
||||
/*
|
||||
toJSON() {
|
||||
return this.armor;
|
||||
}
|
||||
*/
|
||||
}
|
||||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
|
||||
export const PgpUserStore = new class {
|
||||
constructor() {
|
||||
/**
|
||||
* PECL gnupg / PEAR Crypt_GPG
|
||||
* [ {email, can_encrypt, can_sign}, ... ]
|
||||
*/
|
||||
this.gnupgKeyring;
|
||||
this.gnupgPublicKeys = ko.observableArray();
|
||||
this.gnupgPrivateKeys = ko.observableArray();
|
||||
|
||||
// OpenPGP.js
|
||||
this.openpgpPublicKeys = ko.observableArray();
|
||||
this.openpgpPrivateKeys = ko.observableArray();
|
||||
|
||||
// https://mailvelope.github.io/mailvelope/Keyring.html
|
||||
this.mailvelopeKeyring = null;
|
||||
}
|
||||
|
@ -155,77 +51,12 @@ export const PgpUserStore = new class {
|
|||
addEventListener('mailvelope', () => this.loadKeyrings(identifier));
|
||||
}
|
||||
|
||||
if (openpgp) {
|
||||
loadOpenPgpKeys(publicKeysItem).then(keys => {
|
||||
this.openpgpPublicKeys(keys || []);
|
||||
console.log('openpgp.js public keys loaded');
|
||||
});
|
||||
loadOpenPgpKeys(privateKeysItem).then(keys => {
|
||||
this.openpgpPrivateKeys(keys || [])
|
||||
console.log('openpgp.js private keys loaded');
|
||||
});
|
||||
if (OpenPGPUserStore.isSupported()) {
|
||||
OpenPGPUserStore.loadKeyrings(identifier);
|
||||
}
|
||||
|
||||
if (Settings.capa(Capa.GnuPG)) {
|
||||
this.gnupgKeyring = null;
|
||||
this.gnupgPublicKeys([]);
|
||||
this.gnupgPrivateKeys([]);
|
||||
Remote.request('GnupgGetKeys',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
this.gnupgKeyring = oData.Result;
|
||||
const initKey = (key, isPrivate) => {
|
||||
const aEmails = [];
|
||||
key.id = key.subkeys[0].keyid;
|
||||
key.fingerprint = key.subkeys[0].fingerprint;
|
||||
key.uids.forEach(uid => uid.email && aEmails.push(uid.email));
|
||||
key.emails = aEmails;
|
||||
key.askDelete = ko.observable(false);
|
||||
key.openForDeletion = ko.observable(null).askDeleteHelper();
|
||||
key.remove = () => {
|
||||
if (key.askDelete()) {
|
||||
Remote.request('GnupgDeleteKey',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
if (isPrivate) {
|
||||
PgpUserStore.gnupgPrivateKeys.remove(key);
|
||||
} else {
|
||||
PgpUserStore.gnupgPublicKeys.remove(key);
|
||||
}
|
||||
delegateRunOnDestroy(key);
|
||||
}
|
||||
}, {
|
||||
KeyId: key.id,
|
||||
isPrivate: isPrivate
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
key.view = () => {
|
||||
let pass = isPrivate ? prompt('Passphrase') : true;
|
||||
if (pass) {
|
||||
Remote.request('GnupgExportKey',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
key.armor = oData.Result;
|
||||
showScreenPopup(OpenPgpKeyPopupView, [key]);
|
||||
}
|
||||
}, {
|
||||
KeyId: key.id,
|
||||
isPrivate: isPrivate,
|
||||
Passphrase: isPrivate ? pass : ''
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
return key;
|
||||
};
|
||||
this.gnupgPublicKeys(oData.Result.public.map(key => initKey(key, 0)));
|
||||
this.gnupgPrivateKeys(oData.Result.private.map(key => initKey(key, 1)));
|
||||
console.log('gnupg ready');
|
||||
}
|
||||
}
|
||||
);
|
||||
GnuPGUserStore.loadKeyrings(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -233,36 +64,7 @@ export const PgpUserStore = new class {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
isSupported() {
|
||||
return !!(window.openpgp || window.mailvelope || Settings.capa(Capa.GnuPG));
|
||||
}
|
||||
|
||||
openpgpImportKey(armoredKey) {
|
||||
openpgp && openpgp.readKey({armoredKey:armoredKey}).then(key => {
|
||||
if (!key.err) {
|
||||
if (key.isPrivate()) {
|
||||
this.openpgpPrivateKeys.push(new OpenPgpKeyModel(armoredKey, key));
|
||||
storeOpenPgpKeys(this.openpgpPrivateKeys, privateKeysItem);
|
||||
} else {
|
||||
this.openpgpPublicKeys.push(new OpenPgpKeyModel(armoredKey, key));
|
||||
storeOpenPgpKeys(PgpUserStore.openpgpPublicKeys, publicKeysItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
gnupgImportKey(key, callback) {
|
||||
if (Settings.capa(Capa.GnuPG)) {
|
||||
Remote.request('GnupgImportKey',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
// this.gnupgKeyring = oData.Result;
|
||||
}
|
||||
callback && callback(iError, oData);
|
||||
}, {
|
||||
Key: key
|
||||
}
|
||||
);
|
||||
}
|
||||
return !!(OpenPGPUserStore.isSupported() || GnuPGUserStore.isSupported() || window.mailvelope);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,23 +75,9 @@ export const PgpUserStore = new class {
|
|||
keyPair.inGnuPG
|
||||
*/
|
||||
storeKeyPair(keyPair, callback) {
|
||||
openpgp.readKey({armoredKey:keyPair.publicKey}).then(key => {
|
||||
PgpUserStore.openpgpPublicKeys.push(new OpenPgpKeyModel(keyPair.publicKey, key));
|
||||
storeOpenPgpKeys(PgpUserStore.openpgpPublicKeys, publicKeysItem);
|
||||
});
|
||||
openpgp.readKey({armoredKey:keyPair.privateKey}).then(key => {
|
||||
PgpUserStore.openpgpPrivateKeys.push(new OpenPgpKeyModel(keyPair.privateKey, key));
|
||||
storeOpenPgpKeys(PgpUserStore.openpgpPrivateKeys, privateKeysItem);
|
||||
});
|
||||
OpenPGPUserStore.isSupported() && OpenPGPUserStore.storeKeyPair(keyPair);
|
||||
// if (Settings.capa(Capa.GnuPG)) {
|
||||
Remote.request('PgpStoreKeyPair',
|
||||
(iError, oData) => {
|
||||
if (oData && oData.Result) {
|
||||
// this.gnupgKeyring = oData.Result;
|
||||
}
|
||||
callback && callback(iError, oData);
|
||||
}, keyPair
|
||||
);
|
||||
GnuPGUserStore.storeKeyPair(keyPair, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -299,18 +87,11 @@ export const PgpUserStore = new class {
|
|||
async hasPublicKeyForEmails(recipients, all) {
|
||||
const count = recipients.length;
|
||||
if (count) {
|
||||
let length = this.gnupgKeyring && recipients.filter(email =>
|
||||
// (key.can_verify || key.can_encrypt) &&
|
||||
this.gnupgPublicKeys.find(key => key.emails.includes(email))
|
||||
).length;
|
||||
if (length && (!all || length === count)) {
|
||||
if (GnuPGUserStore.hasPublicKeyForEmails(recipients, all)) {
|
||||
return 'gnupg';
|
||||
}
|
||||
|
||||
length = recipients.filter(email =>
|
||||
this.openpgpPublicKeys().find(key => key.emails.includes(email))
|
||||
).length;
|
||||
if (openpgp && (!all || openpgp === count)) {
|
||||
if (OpenPGPUserStore.hasPublicKeyForEmails(recipients, all)) {
|
||||
return 'openpgp';
|
||||
}
|
||||
|
||||
|
@ -326,28 +107,14 @@ export const PgpUserStore = new class {
|
|||
}
|
||||
|
||||
getGnuPGPrivateKeyFor(query, sign) {
|
||||
let key = findGnuPGKey(this.gnupgPrivateKeys, query, sign);
|
||||
if (key) {
|
||||
return ['gnupg', key];
|
||||
}
|
||||
}
|
||||
|
||||
getGnuPGPublicKeyFor(query, sign) {
|
||||
let key = findGnuPGKey(this.gnupgPublicKeys, query, sign);
|
||||
let key = GnuPGUserStore.getPrivateKeyFor(query, sign);
|
||||
if (key) {
|
||||
return ['gnupg', key];
|
||||
}
|
||||
}
|
||||
|
||||
getOpenPGPPrivateKeyFor(query/*, sign*/) {
|
||||
let key = findOpenPGPKey(this.openpgpPrivateKeys, query/*, sign*/);
|
||||
if (key) {
|
||||
return ['openpgp', key];
|
||||
}
|
||||
}
|
||||
|
||||
getOpenPGPPublicKeyFor(query/*, sign*/) {
|
||||
let key = findOpenPGPKey(this.openpgpPublicKeys, query/*, sign*/);
|
||||
let key = OpenPGPUserStore.getPrivateKeyFor(query/*, sign*/);
|
||||
if (key) {
|
||||
return ['openpgp', key];
|
||||
}
|
||||
|
@ -400,6 +167,7 @@ export const PgpUserStore = new class {
|
|||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
i = ids.length;
|
||||
while (i--) {
|
||||
key = this.getOpenPGPPrivateKeyFor(ids[i]);
|
||||
|
@ -409,104 +177,6 @@ export const PgpUserStore = new class {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenPGP.js
|
||||
*/
|
||||
|
||||
/*
|
||||
decryptMessage(message, recipients, fCallback) {
|
||||
if (message && message.getEncryptionKeyIds) {
|
||||
// findPrivateKeysByEncryptionKeyIds
|
||||
const encryptionKeyIds = message.getEncryptionKeyIds();
|
||||
let privateKeys = isArray(encryptionKeyIds)
|
||||
? encryptionKeyIds.map(id => {
|
||||
// openpgpKeyring.publicKeys.getForId(id.toHex())
|
||||
// openpgpKeyring.privateKeys.getForId(id.toHex())
|
||||
const key = id && id.toHex ? findKeyByHex(this.openpgpPrivateKeys, id.toHex()) : null;
|
||||
return key ? [key] : [null];
|
||||
}).flat().filter(v => v)
|
||||
: [];
|
||||
if (!privateKeys.length && arrayLength(recipients)) {
|
||||
privateKeys = recipients.map(sEmail =>
|
||||
(sEmail
|
||||
? this.openpgpPrivateKeys.filter(item => item && item.emails.includes(sEmail)) : 0)
|
||||
|| [null]
|
||||
).flat().validUnique(key => key.id);
|
||||
}
|
||||
|
||||
if (privateKeys && privateKeys.length) {
|
||||
showScreenPopup(OpenPgpSelectorPopupView, [
|
||||
(decryptedKey) => {
|
||||
if (decryptedKey) {
|
||||
message.decrypt(decryptedKey).then(
|
||||
(decryptedMessage) => {
|
||||
let privateKey = null;
|
||||
if (decryptedMessage) {
|
||||
privateKey = findKeyByHex(this.openpgpPrivateKeys, 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) {
|
||||
// findPublicKeysBySigningKeyIds
|
||||
const publicKeys = signingKeyIds.map(id => {
|
||||
const key = id && id.toHex ? findKeyByHex(this.openpgpPublicKeys, id.toHex()) : null;
|
||||
return key ? key.key : [null];
|
||||
}).flat().filter(v => v);
|
||||
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(findKeyByHex(this.openpgpPublicKeys, valid.keyid.toHex()));
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
fCallback(null, signingKeyIds);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fCallback(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an iframe with an editor for a new encrypted mail.
|
||||
* The iframe will be injected into the container identified by selector.
|
||||
|
|
|
@ -29,6 +29,7 @@ import { IdentityUserStore } from 'Stores/User/Identity';
|
|||
import { AccountUserStore } from 'Stores/User/Account';
|
||||
import { FolderUserStore } from 'Stores/User/Folder';
|
||||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
import { MessageUserStore } from 'Stores/User/Message';
|
||||
|
||||
import Remote from 'Remote/User/Fetch';
|
||||
|
@ -458,10 +459,10 @@ class ComposePopupView extends AbstractViewPopup {
|
|||
if ('openpgp' == sign) {
|
||||
let privateKey;
|
||||
try {
|
||||
const keys = PgpUserStore.getOpenPGPPrivateKeyFor(this.currentIdentity().email());
|
||||
if (keys[0]) {
|
||||
keys[0].decrypt(window.prompt('Passphrase'));
|
||||
cfg.privateKey = privateKey = keys[0];
|
||||
const key = OpenPGPUserStore.getPrivateKeyFor(this.currentIdentity().email());
|
||||
if (key) {
|
||||
key.decrypt(window.prompt('Passphrase'));
|
||||
cfg.privateKey = privateKey = key;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
|
@ -477,7 +478,7 @@ class ComposePopupView extends AbstractViewPopup {
|
|||
// error 'sign and encrypt must be same engine';
|
||||
} else if ('openpgp' == encrypt) {
|
||||
this.allRecipients().forEach(recEmail => {
|
||||
cfg.publicKeys = cfg.publicKeys.concat(PgpUserStore.getOpenPGPPublicKeyFor(recEmail));
|
||||
cfg.publicKeys = cfg.publicKeys.concat(OpenPGPUserStore.getPublicKeyFor(recEmail));
|
||||
});
|
||||
pgpPromise = openpgp.encrypt(cfg);
|
||||
} else if ('openpgp' == sign) {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { PgpUserStore } from 'Stores/User/Pgp';
|
||||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||
|
||||
import { AbstractViewPopup } from 'Knoin/AbstractViews';
|
||||
|
||||
import { Capa } from 'Common/Enums';
|
||||
import { Settings } from 'Common/Globals';
|
||||
|
||||
export class OpenPgpImportPopupView extends AbstractViewPopup {
|
||||
constructor() {
|
||||
super('OpenPgpImport');
|
||||
|
@ -18,7 +16,7 @@ export class OpenPgpImportPopupView extends AbstractViewPopup {
|
|||
saveServer: true
|
||||
});
|
||||
|
||||
this.canGnuPG = Settings.capa(Capa.GnuPG);
|
||||
this.canGnuPG = GnuPGUserStore.isSupported();
|
||||
|
||||
this.key.subscribe(() => {
|
||||
this.keyError(false);
|
||||
|
@ -50,16 +48,8 @@ export class OpenPgpImportPopupView extends AbstractViewPopup {
|
|||
match = reg.exec(keyTrimmed);
|
||||
if (match && 0 < count) {
|
||||
if (match[0] && match[1] && match[2] && match[1] === match[2]) {
|
||||
let err = null;
|
||||
if (this.saveGnuPG()) {
|
||||
PgpUserStore.gnupgImportKey(this.key());
|
||||
}
|
||||
PgpUserStore.openpgpImportKey(this.key());
|
||||
if (err) {
|
||||
this.keyError(true);
|
||||
this.keyErrorMessage(err && err[0] ? '' + err[0] : '');
|
||||
console.log(err);
|
||||
}
|
||||
this.saveGnuPG() && GnuPGUserStore.isSupported() && GnuPGUserStore.importKey(this.key());
|
||||
OpenPGPUserStore.isSupported() && OpenPGPUserStore.importKey(this.key());
|
||||
}
|
||||
|
||||
--count;
|
||||
|
|
Loading…
Add table
Reference in a new issue