mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-07-13 03:27:39 +03:00
Cache S/MIME passphrases when "remember" is checked
This commit is contained in:
parent
9941ff61f7
commit
453e73f71a
5 changed files with 46 additions and 32 deletions
|
|
@ -1 +1,8 @@
|
||||||
export const Passphrases = new Map();
|
import { AskPopupView } from 'View/Popup/Ask';
|
||||||
|
|
||||||
|
export const Passphrases = new WeakMap();
|
||||||
|
|
||||||
|
Passphrases.ask = async (key, sAskDesc, btnText) =>
|
||||||
|
Passphrases.has(key)
|
||||||
|
? {password:Passphrases.get(key)/*, remember:false*/}
|
||||||
|
: await AskPopupView.password(sAskDesc, btnText);
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,10 @@ 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';
|
||||||
import { AskPopupView } from 'View/Popup/Ask';
|
|
||||||
|
|
||||||
import { Passphrases } from 'Storage/Passphrases';
|
import { Passphrases } from 'Storage/Passphrases';
|
||||||
|
|
||||||
const
|
const
|
||||||
askPassphrase = async (privateKey, btnTxt = 'SIGN') => {
|
|
||||||
const key = privateKey.id,
|
|
||||||
pass = Passphrases.has(key)
|
|
||||||
? {password:Passphrases.get(key), remember:false}
|
|
||||||
: await AskPopupView.password('GnuPG key<br>' + key + ' ' + privateKey.emails[0], 'CRYPTO/'+btnTxt);
|
|
||||||
pass && pass.remember && Passphrases.set(key, pass.password);
|
|
||||||
return pass?.password;
|
|
||||||
},
|
|
||||||
|
|
||||||
findGnuPGKey = (keys, query/*, sign*/) =>
|
findGnuPGKey = (keys, query/*, sign*/) =>
|
||||||
keys.find(key =>
|
keys.find(key =>
|
||||||
// key[sign ? 'can_sign' : 'can_decrypt']
|
// key[sign ? 'can_sign' : 'can_decrypt']
|
||||||
|
|
@ -77,14 +67,22 @@ export const GnuPGUserStore = new class {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if (isPrivate) {
|
||||||
|
key.password = async (btnTxt = 'SIGN') => {
|
||||||
|
const pass = await Passphrases.ask(
|
||||||
|
key,
|
||||||
|
'GnuPG key<br>' + key.id + ' ' + key.emails[0],
|
||||||
|
'CRYPTO/'+btnTxt
|
||||||
|
);
|
||||||
|
pass && pass.remember && Passphrases.set(key, pass.password);
|
||||||
|
return pass?.password;
|
||||||
|
};
|
||||||
|
}
|
||||||
key.fetch = async callback => {
|
key.fetch = async callback => {
|
||||||
if (key.armor) {
|
if (key.armor) {
|
||||||
callback && callback();
|
callback && callback();
|
||||||
} else {
|
} else {
|
||||||
let pass = '';
|
let pass = isPrivate ? await key.password('POPUP_VIEW_TITLE') : '';
|
||||||
if (isPrivate) {
|
|
||||||
pass = await askPassphrase(key, 'POPUP_VIEW_TITLE');
|
|
||||||
}
|
|
||||||
if (null != pass) {
|
if (null != pass) {
|
||||||
const result = await Remote.post('GnupgExportKey', null, {
|
const result = await Remote.post('GnupgExportKey', null, {
|
||||||
keyId: key.id,
|
keyId: key.id,
|
||||||
|
|
@ -95,7 +93,7 @@ export const GnuPGUserStore = new class {
|
||||||
key.armor = result.Result;
|
key.armor = result.Result;
|
||||||
callback && callback();
|
callback && callback();
|
||||||
} else {
|
} else {
|
||||||
Passphrases.delete(key.id);
|
Passphrases.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -180,7 +178,7 @@ export const GnuPGUserStore = new class {
|
||||||
uid: message.uid,
|
uid: message.uid,
|
||||||
partId: pgpInfo.partId,
|
partId: pgpInfo.partId,
|
||||||
keyId: key.id,
|
keyId: key.id,
|
||||||
passphrase: await askPassphrase(key, 'DECRYPT'),
|
passphrase: await key.password('DECRYPT'),
|
||||||
data: '' // message.plain() optional
|
data: '' // message.plain() optional
|
||||||
}
|
}
|
||||||
if (null !== params.passphrase) {
|
if (null !== params.passphrase) {
|
||||||
|
|
@ -188,7 +186,7 @@ export const GnuPGUserStore = new class {
|
||||||
if (result?.Result && false !== result.Result.data) {
|
if (result?.Result && false !== result.Result.data) {
|
||||||
return result.Result;
|
return result.Result;
|
||||||
}
|
}
|
||||||
Passphrases.delete(key.id);
|
Passphrases.delete(key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -217,7 +215,7 @@ export const GnuPGUserStore = new class {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sign(privateKey) {
|
async sign(privateKey) {
|
||||||
return await askPassphrase(privateKey);
|
return await privateKey.password();
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ 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';
|
||||||
import { AskPopupView } from 'View/Popup/Ask';
|
|
||||||
|
|
||||||
import { Passphrases } from 'Storage/Passphrases';
|
import { Passphrases } from 'Storage/Passphrases';
|
||||||
|
|
||||||
|
|
@ -25,12 +24,11 @@ const
|
||||||
return privateKey.key;
|
return privateKey.key;
|
||||||
}
|
}
|
||||||
const key = privateKey.id,
|
const key = privateKey.id,
|
||||||
pass = Passphrases.has(key)
|
pass = await Passphrases.ask(
|
||||||
? {password:Passphrases.get(key), remember:false}
|
key,
|
||||||
: await AskPopupView.password(
|
'OpenPGP.js key<br>' + key + ' ' + privateKey.emails[0],
|
||||||
'OpenPGP.js key<br>' + key + ' ' + privateKey.emails[0],
|
'CRYPTO/'+btnTxt
|
||||||
'CRYPTO/'+btnTxt
|
);
|
||||||
);
|
|
||||||
if (pass) {
|
if (pass) {
|
||||||
const passphrase = pass.password,
|
const passphrase = pass.password,
|
||||||
result = await openpgp.decryptKey({
|
result = await openpgp.decryptKey({
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||||
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||||
//import { OpenPgpImportPopupView } from 'View/Popup/OpenPgpImport';
|
//import { OpenPgpImportPopupView } from 'View/Popup/OpenPgpImport';
|
||||||
import { SMimeUserStore } from 'Stores/User/SMime';
|
import { SMimeUserStore } from 'Stores/User/SMime';
|
||||||
|
import { Passphrases } from 'Storage/Passphrases';
|
||||||
|
|
||||||
import { MessageUserStore } from 'Stores/User/Message';
|
import { MessageUserStore } from 'Stores/User/Message';
|
||||||
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
import { MessagelistUserStore } from 'Stores/User/Messagelist';
|
||||||
|
|
@ -470,7 +471,8 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||||
}
|
}
|
||||||
|
|
||||||
sendCommand() {
|
sendCommand() {
|
||||||
let sSentFolder = this.currentIdentity()?.sentFolder?.() || FolderUserStore.sentFolder();
|
const identity = this.currentIdentity();
|
||||||
|
let sSentFolder = identity?.sentFolder?.() || FolderUserStore.sentFolder();
|
||||||
|
|
||||||
this.attachmentsInProcessError(false);
|
this.attachmentsInProcessError(false);
|
||||||
this.attachmentsInErrorError(false);
|
this.attachmentsInErrorError(false);
|
||||||
|
|
@ -520,6 +522,7 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||||
}
|
}
|
||||||
this.savedErrorDesc(msg);
|
this.savedErrorDesc(msg);
|
||||||
} else {
|
} else {
|
||||||
|
params.signPassphrase && Passphrases.delete(identity);
|
||||||
this.sendError(true);
|
this.sendError(true);
|
||||||
this.sendErrorDesc(getNotification(iError, data?.ErrorMessage)
|
this.sendErrorDesc(getNotification(iError, data?.ErrorMessage)
|
||||||
|| getNotification(Notifications.CantSendMessage));
|
|| getNotification(Notifications.CantSendMessage));
|
||||||
|
|
@ -1539,8 +1542,13 @@ export class ComposePopupView extends AbstractViewPopup {
|
||||||
params.signCertificate = identity.smimeCertificate();
|
params.signCertificate = identity.smimeCertificate();
|
||||||
params.signPrivateKey = identity.smimeKey();
|
params.signPrivateKey = identity.smimeKey();
|
||||||
if (identity.smimeKeyEncrypted()) {
|
if (identity.smimeKeyEncrypted()) {
|
||||||
const pass = await AskPopupView.password('S/MIME private key', 'CRYPTO/SIGN');
|
const pass = await Passphrases.ask(
|
||||||
|
params.signPrivateKey,
|
||||||
|
'S/MIME private key ' + identity.email(),
|
||||||
|
'CRYPTO/DECRYPT'
|
||||||
|
);
|
||||||
params.signPassphrase = pass?.password;
|
params.signPassphrase = pass?.password;
|
||||||
|
// pass && pass.remember && Passphrases.set(identity, pass.password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ import { GnuPGUserStore } from 'Stores/User/GnuPG';
|
||||||
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
import { OpenPGPUserStore } from 'Stores/User/OpenPGP';
|
||||||
import { IdentityUserStore } from 'Stores/User/Identity';
|
import { IdentityUserStore } from 'Stores/User/Identity';
|
||||||
|
|
||||||
import { AskPopupView } from 'View/Popup/Ask';
|
import { Passphrases } from 'Storage/Passphrases';
|
||||||
|
|
||||||
const
|
const
|
||||||
oMessageScrollerDom = () => elementById('messageItem') || {},
|
oMessageScrollerDom = () => elementById('messageItem') || {},
|
||||||
|
|
@ -624,7 +624,7 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
|
|
||||||
async smimeDecrypt() {
|
async smimeDecrypt() {
|
||||||
const message = currentMessage();
|
const message = currentMessage();
|
||||||
let data = message.smimeEncrypted(); // { partId: "1" }
|
let pass, data = message.smimeEncrypted(); // { partId: "1" }
|
||||||
const addresses = message.from.concat(message.to, message.cc, message.bcc).map(item => item.email),
|
const addresses = message.from.concat(message.to, message.cc, message.bcc).map(item => item.email),
|
||||||
identity = IdentityUserStore.find(item => addresses.includes(item.email()));
|
identity = IdentityUserStore.find(item => addresses.includes(item.email()));
|
||||||
if (data && identity) {
|
if (data && identity) {
|
||||||
|
|
@ -635,15 +635,18 @@ export class MailMessageView extends AbstractViewRight {
|
||||||
data.certificate = identity.smimeCertificate();
|
data.certificate = identity.smimeCertificate();
|
||||||
data.privateKey = identity.smimeKey();
|
data.privateKey = identity.smimeKey();
|
||||||
if (identity.smimeKeyEncrypted()) {
|
if (identity.smimeKeyEncrypted()) {
|
||||||
const pass = await AskPopupView.password('S/MIME private key', 'CRYPTO/DECRYPT');
|
pass = await Passphrases.ask(identity, 'S/MIME private key ' + identity.email(), 'CRYPTO/DECRYPT');
|
||||||
|
if (!pass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
data.passphrase = pass?.password;
|
data.passphrase = pass?.password;
|
||||||
}
|
}
|
||||||
|
|
||||||
Remote.post('SMimeDecryptMessage', null, data).then(response => {
|
Remote.post('SMimeDecryptMessage', null, data).then(response => {
|
||||||
if (response?.Result) {
|
if (response?.Result) {
|
||||||
message.smimeDecrypted(true);
|
message.smimeDecrypted(true);
|
||||||
MimeToMessage(response.Result, message);
|
MimeToMessage(response.Result, message);
|
||||||
message.html() ? message.viewHtml() : message.viewPlain();
|
message.html() ? message.viewHtml() : message.viewPlain();
|
||||||
|
pass && pass.remember && Passphrases.set(identity, pass.password);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue