Skip to content

Commit

Permalink
Changes for #89
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
the-djmaze committed Jan 17, 2022
1 parent ba49d06 commit 8dcd0cf
Show file tree
Hide file tree
Showing 14 changed files with 496 additions and 338 deletions.
3 changes: 1 addition & 2 deletions dev/App/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ class AppUser extends AbstractApp {
}

reloadOpenPgpKeys() {
if (PgpUserStore.capaOpenPGP()) {
if (PgpUserStore.openpgp) {
const keys = [],
email = new EmailModel(),
openpgpKeyring = PgpUserStore.openpgpKeyring,
Expand Down Expand Up @@ -784,7 +784,6 @@ class AppUser extends AbstractApp {
}
}
PgpUserStore.openpgpKeyring = new openpgp.Keyring();
PgpUserStore.capaOpenPGP(true);
this.reloadOpenPgpKeys();
};
script.onerror = () => console.error(script.src);
Expand Down
12 changes: 4 additions & 8 deletions dev/Model/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import { AbstractModel } from 'Knoin/AbstractModel';

import PreviewHTML from 'Html/PreviewMessage.html';

import { PgpUserStore } from 'Stores/User/Pgp';

const
/*eslint-disable max-len*/
url = /(^|[\s\n]|\/?>)(https:\/\/[-A-Z0-9+\u0026\u2019#/%?=()~_|!:,.;]*[-A-Z0-9+\u0026#/%=~()_|])/gi,
Expand Down Expand Up @@ -102,7 +100,7 @@ export class MessageModel extends AbstractModel {
hasImages: false,
hasExternals: false,

isPgpSigned: false,
pgpSigned: null, // { BodyPartId: "1", SigPartId: "2", MicAlg: "pgp-sha256" }
isPgpEncrypted: false,
pgpSignedVerifyStatus: SignedVerifyStatus.None,
pgpSignedVerifyUser: '',
Expand Down Expand Up @@ -181,7 +179,7 @@ export class MessageModel extends AbstractModel {
this.hasExternals(false);
this.attachments(new AttachmentCollectionModel);

this.isPgpSigned(false);
this.pgpSigned(null);
this.isPgpEncrypted(false);
this.pgpSignedVerifyStatus(SignedVerifyStatus.None);
this.pgpSignedVerifyUser('');
Expand Down Expand Up @@ -404,7 +402,7 @@ export class MessageModel extends AbstractModel {
viewHtml() {
const body = this.body;
if (body && this.html()) {
let html = this.html().toString()
let html = this.html()
.replace(/font-size:\s*[0-9]px/g, 'font-size:11px')
// Strip utm_* tracking
.replace(/(\\?|&|&)utm_[a-z]+=[a-z0-9_-]*/si, '$1');
Expand Down Expand Up @@ -465,7 +463,7 @@ export class MessageModel extends AbstractModel {
if (body && this.plain()) {
body.classList.toggle('html', 0);
body.classList.toggle('plain', 1);
body.innerHTML = plainToHtml(this.plain().toString())
body.innerHTML = plainToHtml(this.plain())
// Strip utm_* tracking
.replace(/(\\?|&|&)utm_[a-z]+=[a-z0-9_-]*/si, '$1')
.replace(url, '$1<a href="$2" target="_blank">$2</a>')
Expand All @@ -479,8 +477,6 @@ export class MessageModel extends AbstractModel {
}

initView() {
PgpUserStore.initMessageBodyControls(this.body, this);

// init BlockquoteSwitcher
this.body.querySelectorAll('blockquote:not(.rl-bq-switcher)').forEach(node => {
if (node.textContent.trim() && !node.parentNode.closest('blockquote')) {
Expand Down
2 changes: 1 addition & 1 deletion dev/Stores/User/Message.js
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ export const MessageUserStore = new class {
message.hasImages(body.rlHasImages);
} else {
body = Element.fromHTML('<div id="' + id + '" hidden="" class="b-text-part '
+ (message.isPgpSigned() ? ' openpgp-signed' : '')
+ (message.pgpSigned() ? ' openpgp-signed' : '')
+ (message.isPgpEncrypted() ? ' openpgp-encrypted' : '')
+ '">'
+ '</div>');
Expand Down
149 changes: 0 additions & 149 deletions dev/Stores/User/Pgp.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import ko from 'ko';

import { i18n } from 'Common/Translator';
import { isArray, arrayLength, pString, addComputablesTo } from 'Common/Utils';

import { AccountUserStore } from 'Stores/User/Account';
Expand All @@ -9,132 +8,8 @@ import { showScreenPopup } from 'Knoin/Knoin';

import { MessageOpenPgpPopupView } from 'View/Popup/MessageOpenPgp';

function controlsHelper(dom, verControl, success, title, text)
{
dom.classList.toggle('error', !success);
dom.classList.toggle('success', success);
verControl.classList.toggle('error', !success);
verControl.classList.toggle('success', success);
dom.title = verControl.title = title;

if (undefined !== text) {
dom.textContent = text.trim();
}
}

function domControlEncryptedClickHelper(store, dom, armoredMessage, recipients) {
return function() {
let message = null;

if (this.classList.contains('success')) {
return false;
}

try {
message = store.openpgp.message.readArmored(armoredMessage);
} catch (e) {
console.log(e);
}

if (message && message.getText && message.verify && message.decrypt) {
store.decryptMessage(
message,
recipients,
(validPrivateKey, decryptedMessage, validPublicKey, signingKeyIds) => {
if (decryptedMessage) {
if (validPublicKey) {
controlsHelper(
dom,
this,
true,
i18n('PGP_NOTIFICATIONS/GOOD_SIGNATURE', {
USER: validPublicKey.user + ' (' + validPublicKey.id + ')'
}),
decryptedMessage.getText()
);
} else if (validPrivateKey) {
const keyIds = arrayLength(signingKeyIds) ? signingKeyIds : null,
additional = keyIds
? keyIds.map(item => (item && item.toHex ? item.toHex() : null)).filter(v => v).join(', ')
: '';

controlsHelper(
dom,
this,
false,
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : ''),
decryptedMessage.getText()
);
} else {
controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
}
} else {
controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
}
}
);

return false;
}

controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
return false;
};
}

function domControlSignedClickHelper(store, dom, armoredMessage) {
return function() {
let message = null;

if (this.classList.contains('success') || this.classList.contains('error')) {
return false;
}

try {
message = store.openpgp.cleartext.readArmored(armoredMessage);
} catch (e) {
console.log(e);
}

if (message && message.getText && message.verify) {
store.verifyMessage(message, (validKey, signingKeyIds) => {
if (validKey) {
controlsHelper(
dom,
this,
true,
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(', ')
: '';

controlsHelper(
dom,
this,
false,
i18n('PGP_NOTIFICATIONS/UNVERIFIRED_SIGNATURE') + (additional ? ' (' + additional + ')' : '')
);
}
});

return false;
}

controlsHelper(dom, this, false, i18n('PGP_NOTIFICATIONS/DECRYPTION_ERROR'));
return false;
};
}

export const PgpUserStore = new class {
constructor() {
this.capaOpenPGP = ko.observable(false);

this.openpgp = null;

this.openpgpkeys = ko.observableArray();
Expand Down Expand Up @@ -335,28 +210,4 @@ export const PgpUserStore = new class {
return false;
}

/**
* @param {*} dom
* @param {MessageModel} rainLoopMessage
*/
initMessageBodyControls(dom, rainLoopMessage) {
const cl = dom.classList,
signed = cl.contains('openpgp-signed'),
encrypted = cl.contains('openpgp-encrypted');
if (encrypted || signed) {
const
domText = dom.textContent,
recipients = rainLoopMessage ? rainLoopMessage.getEmails(['from', 'to', 'cc']) : [],
verControl = Element.fromHTML('<div class="b-openpgp-control"><i class="fontastic">🔒</i></div>');
if (encrypted) {
verControl.title = i18n('MESSAGE/PGP_ENCRYPTED_MESSAGE_DESC');
verControl.addEventListener('click', domControlEncryptedClickHelper(this, dom, domText, recipients));
} else {
verControl.title = i18n('MESSAGE/PGP_SIGNED_MESSAGE_DESC');
verControl.addEventListener('click', domControlSignedClickHelper(this, dom, domText));
}

dom.prepend(verControl);
}
}
};
35 changes: 18 additions & 17 deletions dev/Styles/User/MessageView.less
Original file line number Diff line number Diff line change
Expand Up @@ -429,27 +429,28 @@ html.rl-no-preview-pane {
}
}
*/
.b-openpgp-control {
}

color: #FA0;
cursor: pointer;
display: block;
opacity: 0.5;
margin: 15px;
.b-openpgp-control {

&:hover {
opacity: 1;
}
color: #FA0;
cursor: pointer;
display: block;
opacity: 0.5;
margin: 15px;

&.success {
color: green;
opacity: 1;
}
&:hover {
opacity: 1;
}

&.error {
color: red;
opacity: 1;
}
&.success {
color: green;
opacity: 1;
}

&.error {
color: red;
opacity: 1;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions dev/View/Popup/Compose.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class ComposePopupView extends AbstractViewPopup {

this.bSkipNextHide = false;

this.capaOpenPGP = PgpUserStore.capaOpenPGP;
this.capaOpenPGP = !!PgpUserStore.openpgp;

this.identities = IdentityUserStore;

Expand Down Expand Up @@ -552,7 +552,7 @@ class ComposePopupView extends AbstractViewPopup {
}

openOpenPgpPopup() {
if (PgpUserStore.capaOpenPGP() && !this.oEditor.isHtml()) {
if (PgpUserStore.openpgp && !this.oEditor.isHtml()) {
showScreenPopup(ComposeOpenPgpPopupView, [
result => this.editor(editor => editor.setPlain(result)),
this.oEditor.getData(false),
Expand Down
Loading

0 comments on commit 8dcd0cf

Please sign in to comment.