Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
a99a9e7
wip issue #609
Oct 5, 2021
b41c79f
wip
Oct 5, 2021
ea83b54
wip
Oct 5, 2021
ca80ec8
Remove stuff related to keys.verificationContacts
Oct 6, 2021
12e4beb
wip
Oct 6, 2021
852b76a
add test stubs
Oct 6, 2021
c72883b
wip
Oct 7, 2021
6818f90
wip
Oct 7, 2021
947f15c
wip
Oct 7, 2021
3085fed
wip
Oct 7, 2021
a2b3ea6
wip
Oct 7, 2021
a89d924
wip
Oct 7, 2021
eefddc6
wip
Oct 7, 2021
da7915f
wip
Oct 7, 2021
832093f
Merge remote-tracking branch 'origin/master' into issue-609-verify-si…
Oct 9, 2021
1432ac8
Merge remote-tracking branch 'origin/master' into issue-609-verify-si…
Oct 10, 2021
69e314a
wip
Oct 10, 2021
7ef6f30
wip
Oct 10, 2021
0a8b541
wip
Oct 10, 2021
fc0ee3a
wip
Oct 10, 2021
f95eca4
Add encrypted+signed email
Oct 10, 2021
72d5109
wip
Oct 10, 2021
644c9b2
wip
Oct 10, 2021
0382533
wip
Oct 10, 2021
0008014
wip
Oct 10, 2021
534d7dc
wip
Oct 10, 2021
f623892
wip
Oct 10, 2021
eb81bb8
wip
Oct 10, 2021
9d3925c
wip
Oct 10, 2021
1f9ef13
wip
Oct 10, 2021
b9fa7bc
Added signed plaintext emails
Oct 10, 2021
fb048a6
wip
Oct 10, 2021
f69343f
wip
Oct 10, 2021
7c46aee
wip
Oct 10, 2021
2ea2cbc
wip
Oct 10, 2021
905e54a
wip
Oct 11, 2021
9e7ff9b
wip
Oct 11, 2021
e1100c2
wip
Oct 11, 2021
f9d32ff
wip
Oct 11, 2021
8fa81f5
wip
Oct 11, 2021
fd696b5
wip
Oct 11, 2021
53f26e5
wip
Oct 11, 2021
61ed274
wip
Oct 11, 2021
5531b9d
wip
Oct 11, 2021
6c85659
wip
Oct 11, 2021
d218252
Merge remote-tracking branch 'origin/master' into issue-609-verify-si…
Oct 11, 2021
634d631
wip
Oct 11, 2021
2ec9fcb
wip
Oct 11, 2021
eda1a70
wip
Oct 11, 2021
ed87c97
wip
Oct 11, 2021
6841e19
wip
Oct 11, 2021
702ad15
wip
Oct 11, 2021
95507a0
wip
Oct 11, 2021
334166e
wip
Oct 11, 2021
f59f4a8
wip
Oct 11, 2021
dafb06c
wip
Oct 11, 2021
24f427c
wip
Oct 11, 2021
e99d965
wip
Oct 11, 2021
1a8797f
wip
Oct 11, 2021
dd80785
wip
Oct 11, 2021
a399181
wip
Oct 11, 2021
c0594e0
wip
Oct 12, 2021
ea9a962
wip
Oct 12, 2021
a6f748e
wip
Oct 12, 2021
52dd244
wip
Oct 12, 2021
82c5972
wip
Oct 12, 2021
d0fa299
wip
Oct 12, 2021
a2f57b6
wip
Oct 12, 2021
9e3c07d
wip
Oct 12, 2021
2637397
wip
Oct 12, 2021
c113898
wip
Oct 12, 2021
e3078e3
wip
Oct 12, 2021
9f10bd8
wip
Oct 12, 2021
e7b157c
wip
Oct 12, 2021
20d8470
wip
Oct 12, 2021
a36f644
wip
Oct 12, 2021
b91faed
skip deteched signature test
Oct 12, 2021
dc97251
wip
Oct 12, 2021
a7e49ee
fixed detached test
Oct 12, 2021
488667c
enable all tests
Oct 12, 2021
0760e04
corrections after code review
Oct 13, 2021
28e75c4
remove unnecessary log
Oct 13, 2021
be5e1e0
Branch was auto-updated.
github-actions[bot] Oct 14, 2021
04cb08b
Branch was auto-updated.
github-actions[bot] Oct 14, 2021
0b12c85
Branch was auto-updated.
github-actions[bot] Oct 14, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Delivered-To: flowcrypt.compatibility@gmail.com
Return-Path: <flowcrypt.compatibility@gmail.com>
Openpgp: id=E76853E128A0D376CAE47C143A30F4CC0A9A8F10
From: flowcrypt.compatibility@gmail.com
MIME-Version: 1.0
Date: Thu, 2 Nov 2017 17:54:14 -0700
Message-ID: <CANzaQHU9A@mail.gmail.com>
Subject: mime email encrypted inline text signed
To: flowcrypt.compatibility@gmail.com
Content-Type: text/plain; charset="UTF-8"

-----BEGIN PGP MESSAGE-----
Version: FlowCrypt [BUILD_REPLACEABLE_VERSION] Gmail Encryption
Comment: Seamlessly send and receive encrypted email

wcBMAwurnAGLJl0iAQf/Unn+k8dkJ35DM+jJPCqFYMyP9WY8J/2g4Kf5yvlR
7jarIe89D+Uu6nGJwKZXzQ/aLn1FQT6Y3ty+RxCB2hSNzkcrydE9vO0hGJde
0uK3cljgtiBTW1qDAKJAAD8y3i6CRrs4+mJacJCJ7XoQsZqvHzN/ywJ0/XDW
FEWINXtqrt7IyfIy9LbP4LsqifUZOX2SMQX5i8JMTO/85xJrgFpH429XvreG
BAD2NdxAA0zTuUKuJtdJGmRBN+aU/rRZTdu5sF8PfDtVBGmPTYuhIiCSCoGe
KwkxejyfkAPmeUqjiWXSUX0Tlulmfuogo0ofgtizhF5kww3IbE3tiH6fzIxU
RtLAsgFhC00k2WTu1jzunu6BCoHkhAghVAN8M3mF112e6wmeiS4y3cGGTAPi
g+C4cd223NzpiRc/BvauONUaL6LLJIcIC65eErU5Ii25X8AEXio7Juc9JT0Z
pwvAnVqOuxxIV+WCAi0QHerEPDNz5pl9uyiGwPSd8ecnr50zgiqLoT9RjzFs
PJJjijjkBV1fH72NBlJD5XwRNbRJZKGFonV1swZbd01Ki2D2ENQvgM+NN+r0
Tdw/oauD+YfIxyc/mde3mF7ZDbBGmmATwvH1JyVURJKpXxT0SBKjkdeUYVL+
wIxo5zvz8RNMbfz3AB9gH/VnlNdwLirFGgKe3feoBIuOH60KxrIOOO/zJhMs
eHHCiaIT9+73ML1murgnMJi0rhzopQp4q2CdkzrUZce8+NubyhMWyur6cA4p
adZoSa5urtIYvoDnFreNYpUPu6VGV2RBTTj6NPJjoGoCw3rSRNpzbHY1KnBf
O2iCZQZx7IYfqBeoJlI=
=ADUd
-----END PGP MESSAGE-----

53 changes: 53 additions & 0 deletions Core/source/assets/compat/mime-email-plain-signed-detached.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Return-Path: <t@est.com>
X-Original-To: t@est.com
Delivered-To: t@est.com
Received: from [192.168.56.1] (unknown [192.168.56.1])
by est.com (Postfix) with ESMTPS id E08F2103ED0
for <t@est.com>; Tue, 12 Oct 2021 23:06:02 +0000 (UTC)
To: t@est.com
From: Test User <t@est.com>
Subject: mime email plain signed detached
Message-ID: <920dc165-7ba2-3f36-3177-a8e76bae4ddd@est.com>
Date: Wed, 13 Oct 2021 02:06:02 +0300
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101
Thunderbird/78.13.0
MIME-Version: 1.0
Content-Type: multipart/signed; micalg=pgp-sha256;
protocol="application/pgp-signature";
boundary="1C2MFssN1e2UeK7Q7Fok4Ko9VNn7xQryG"

This is an OpenPGP/MIME signed message (RFC 4880 and 3156)
--1C2MFssN1e2UeK7Q7Fok4Ko9VNn7xQryG
Content-Type: multipart/mixed; boundary="zB40xvQ1bEP0WaDoW1b6PiXYa0sf6aLHw";
protected-headers="v1"
From: Test User <t@est.com>
To: t@est.com
Message-ID: <920dc165-7ba2-3f36-3177-a8e76bae4ddd@est.com>
Subject: mime email plain signed detached

--zB40xvQ1bEP0WaDoW1b6PiXYa0sf6aLHw
Content-Type: text/plain; charset=utf-8; format=flowed
Content-Transfer-Encoding: base64
Content-Language: en-US

c29tZQrmsYkKdHh0Cgo=

--zB40xvQ1bEP0WaDoW1b6PiXYa0sf6aLHw--

--1C2MFssN1e2UeK7Q7Fok4Ko9VNn7xQryG
Content-Type: application/pgp-signature; name="OpenPGP_signature.asc"
Content-Description: OpenPGP digital signature
Content-Disposition: attachment; filename="OpenPGP_signature"

-----BEGIN PGP SIGNATURE-----

wsB5BAABCAAjFiEE52hT4Sig03bK5HwUOjD0zAqajxAFAmFmFNoFAwAAAAAACgkQOjD0zAqajxBM
dgf7BDgUTDQ4AvhUdyJ8xMtWpzRKCWz1tp2ErqciQCamXJxOkoBMlcY8mrohYgbNtOvabKguknWT
NUpzgcnU1DbtL/wB43tJNfAmquLhl3dw5TBPieg7lImZSNAyS5H0Uxtuxze0nVrdgCgVoyaPuC/7
PBoMMUM1LLrnP5LJM9d10Hku6BqsgYgI2GDx7MFiWkUIHWFcSLaZk+Ttc7Dpau40bpuJDxW5Cd1p
OrBkuuuSSlYVQkZB8ABZ2T/OT+H06kGuvN5SV9UDTuSHi+Uk355bzWJau5hEQHXoT+O4TskJlJAG
+VkotOZYUtGyPWdwKsun1/w8w2xc9GCMjnb2owFXdQ==
=bMPl
-----END PGP SIGNATURE-----

--1C2MFssN1e2UeK7Q7Fok4Ko9VNn7xQryG--
32 changes: 32 additions & 0 deletions Core/source/assets/compat/mime-email-plain-signed-edited.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Delivered-To: flowcrypt.compatibility@gmail.com
Return-Path: <flowcrypt.compatibility@gmail.com>
Openpgp: id=E76853E128A0D376CAE47C143A30F4CC0A9A8F10
From: flowcrypt.compatibility@gmail.com
MIME-Version: 1.0
Date: Thu, 2 Nov 2017 17:54:14 -0700
Message-ID: <CANzaQHU9A@mail.gmail.com>
Subject: mime email plain signed
To: flowcrypt.compatibility@gmail.com
Content-Type: text/plain; charset="UTF-8"


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

some
TXT
-----BEGIN PGP SIGNATURE-----
Version: FlowCrypt [BUILD_REPLACEABLE_VERSION] Gmail Encryption
Comment: Seamlessly send and receive encrypted email

wsBcBAEBCAAGBQJhY1x0AAoJEDow9MwKmo8QMR0H/iHkHFr1doJhqIVsSXyQ
bBwYpoaIRHEZlF3RQa3sbkLqXSTzHf81ySb7/oXgd+z5RYYw2vD9bebaInb3
Ca9FuBrM4ZlnmVVnNpv/nntZZdln+AVq+rEOaLDcHC4nFTC3Z7QwulZn31Hm
b0JcsGAuIDfUZ1mFfFpyCq7KB+XGbV3bc+bUBO0pjKfbsEISBqJxuCzJIr+0
pIwMSdluQXxtyyqT3aXnoH9jl75dBDDvMEPR3c/I2+k9SWWoYslwhn2TqB94
RFgwWpw/SH8+1b3lI1AWnkVpkkm8JOnteViHCz85ALd9xkACjq8UavPGLy0v
J6XRgsV1E2RTmydFB6vv/9w=
=quFc
-----END PGP SIGNATURE-----

32 changes: 32 additions & 0 deletions Core/source/assets/compat/mime-email-plain-signed.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Delivered-To: flowcrypt.compatibility@gmail.com
Return-Path: <flowcrypt.compatibility@gmail.com>
Openpgp: id=E76853E128A0D376CAE47C143A30F4CC0A9A8F10
From: flowcrypt.compatibility@gmail.com
MIME-Version: 1.0
Date: Thu, 2 Nov 2017 17:54:14 -0700
Message-ID: <CANzaQHU9A@mail.gmail.com>
Subject: mime email plain signed
To: flowcrypt.compatibility@gmail.com
Content-Type: text/plain; charset="UTF-8"


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

some
txt
-----BEGIN PGP SIGNATURE-----
Version: FlowCrypt [BUILD_REPLACEABLE_VERSION] Gmail Encryption
Comment: Seamlessly send and receive encrypted email

wsBcBAEBCAAGBQJhY1x0AAoJEDow9MwKmo8QMR0H/iHkHFr1doJhqIVsSXyQ
bBwYpoaIRHEZlF3RQa3sbkLqXSTzHf81ySb7/oXgd+z5RYYw2vD9bebaInb3
Ca9FuBrM4ZlnmVVnNpv/nntZZdln+AVq+rEOaLDcHC4nFTC3Z7QwulZn31Hm
b0JcsGAuIDfUZ1mFfFpyCq7KB+XGbV3bc+bUBO0pjKfbsEISBqJxuCzJIr+0
pIwMSdluQXxtyyqT3aXnoH9jl75dBDDvMEPR3c/I2+k9SWWoYslwhn2TqB94
RFgwWpw/SH8+1b3lI1AWnkVpkkm8JOnteViHCz85ALd9xkACjq8UavPGLy0v
J6XRgsV1E2RTmydFB6vv/9w=
=quFc
-----END PGP SIGNATURE-----

24 changes: 17 additions & 7 deletions Core/source/core/msg-block-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Catch } from '../platform/catch.js';
import { Mime } from './mime.js';
import { PgpArmor } from './pgp-armor.js';
import { PgpKey } from './pgp-key.js';
import { PgpMsg } from './pgp-msg.js';
import { PgpMsg, VerifyRes } from './pgp-msg.js';
import { Str } from './common.js';

type SanitizedBlocks = { blocks: MsgBlock[], subject: string | undefined, isRichText: boolean };
Expand Down Expand Up @@ -40,7 +40,7 @@ export class MsgBlockParser {
}
}

public static fmtDecryptedAsSanitizedHtmlBlocks = async (decryptedContent: Uint8Array, imgHandling: SanitizeImgHandling = 'IMG-TO-LINK'): Promise<SanitizedBlocks> => {
public static fmtDecryptedAsSanitizedHtmlBlocks = async (decryptedContent: Uint8Array, signature?: VerifyRes, imgHandling: SanitizeImgHandling = 'IMG-TO-LINK'): Promise<SanitizedBlocks> => {
const blocks: MsgBlock[] = [];
let isRichText = false;
if (!Mime.resemblesMsg(decryptedContent)) {
Expand All @@ -49,24 +49,34 @@ export class MsgBlockParser {
utf = PgpMsg.stripFcTeplyToken(utf);
const armoredPubKeys: string[] = [];
utf = PgpMsg.stripPublicKeys(utf, armoredPubKeys);
blocks.push(MsgBlock.fromContent('decryptedHtml', Str.asEscapedHtml(utf))); // escaped text as html
const block = MsgBlock.fromContent('decryptedHtml', Str.asEscapedHtml(utf));
block.verifyRes = signature;
blocks.push(block); // escaped text as html
await MsgBlockParser.pushArmoredPubkeysToBlocks(armoredPubKeys, blocks);
return { blocks, subject: undefined, isRichText };
}
const decoded = await Mime.decode(decryptedContent);
if (typeof decoded.html !== 'undefined') {
blocks.push(MsgBlock.fromContent('decryptedHtml', Xss.htmlSanitizeKeepBasicTags(decoded.html, imgHandling))); // sanitized html
const block = MsgBlock.fromContent('decryptedHtml', Xss.htmlSanitizeKeepBasicTags(decoded.html, imgHandling));
block.verifyRes = signature;
blocks.push(block); // sanitized html
isRichText = true;
} else if (typeof decoded.text !== 'undefined') {
blocks.push(MsgBlock.fromContent('decryptedHtml', Str.asEscapedHtml(decoded.text))); // escaped text as html
const block = MsgBlock.fromContent('decryptedHtml', Str.asEscapedHtml(decoded.text));
block.verifyRes = signature;
blocks.push(block); // escaped text as html
} else {
blocks.push(MsgBlock.fromContent('decryptedHtml', Str.asEscapedHtml(Buf.with(decryptedContent).toUtfStr()))); // escaped mime text as html
const block = MsgBlock.fromContent('decryptedHtml', Str.asEscapedHtml(Buf.with(decryptedContent).toUtfStr()));
block.verifyRes = signature;
blocks.push(); // escaped mime text as html
}
for (const att of decoded.atts) {
if (att.treatAs() === 'publicKey') {
await MsgBlockParser.pushArmoredPubkeysToBlocks([att.getData().toUtfStr()], blocks);
} else {
blocks.push(MsgBlock.fromAtt('decryptedAtt', '', { name: att.name, data: att.getData(), length: att.length, type: att.type }));
const block = MsgBlock.fromAtt('decryptedAtt', '', { name: att.name, data: att.getData(), length: att.length, type: att.type });
block.verifyRes = signature;
blocks.push(block);
}
}
return { blocks, subject: decoded.subject, isRichText };
Expand Down
71 changes: 45 additions & 26 deletions Core/source/core/pgp-msg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

'use strict';

import { Contact, KeyInfo, PgpKey, PrvKeyInfo } from './pgp-key.js';
import { KeyInfo, PgpKey, PrvKeyInfo } from './pgp-key.js';
import { MsgBlock, MsgBlockType } from './msg-block.js';
import { Str, Value } from './common.js';

Expand All @@ -19,9 +19,9 @@ export namespace PgpMsgMethod {
export namespace Arg {
export type Encrypt = { pubkeys: string[], signingPrv?: OpenPGP.key.Key, pwd?: string, data: Uint8Array, filename?: string, armor: boolean, date?: Date };
export type Type = { data: Uint8Array };
export type Decrypt = { kisWithPp: PrvKeyInfo[], encryptedData: Uint8Array, msgPwd?: string };
export type Decrypt = { kisWithPp: PrvKeyInfo[], encryptedData: Uint8Array, msgPwd?: string, verificationPubkeys?: string[] };
export type DiagnosePubkeys = { privateKis: KeyInfo[], message: Uint8Array };
export type VerifyDetached = { plaintext: Uint8Array, sigText: Uint8Array };
export type VerifyDetached = { plaintext: Uint8Array, sigText: Uint8Array, verificationPubkeys?: string[] };
}
export type DiagnosePubkeys = (arg: Arg.DiagnosePubkeys) => Promise<DiagnoseMsgPubkeysResult>;
export type VerifyDetached = (arg: Arg.VerifyDetached) => Promise<VerifyRes>;
Expand All @@ -31,7 +31,6 @@ export namespace PgpMsgMethod {
}

type SortedKeysForDecrypt = {
verificationContacts: Contact[];
forVerification: OpenPGP.key.Key[];
encryptedFor: string[];
signedBy: string[];
Expand All @@ -53,7 +52,13 @@ type PreparedForDecrypt = { isArmored: boolean, isCleartext: true, message: Open

type OpenpgpMsgOrCleartext = OpenPGP.message.Message | OpenPGP.cleartext.CleartextMessage;

export type VerifyRes = { signer?: string; contact?: Contact; match: boolean | null; error?: string; };
export type VerifyRes = {
signer?: string;
match: boolean | null;
error?: string;
mixed?: boolean;
partial?: boolean;
};
export type PgpMsgTypeResult = { armored: boolean, type: MsgBlockType } | undefined;
export type DecryptResult = DecryptSuccess | DecryptError;
export type DiagnoseMsgPubkeysResult = { found_match: boolean, receivers: number, };
Expand Down Expand Up @@ -123,8 +128,8 @@ export class PgpMsg {
return await openpgp.stream.readToEnd((signRes as OpenPGP.SignArmorResult).data);
}

public static verify = async (msgOrVerResults: OpenpgpMsgOrCleartext | OpenPGP.message.Verification[], pubs: OpenPGP.key.Key[], contact?: Contact): Promise<VerifyRes> => {
const sig: VerifyRes = { contact, match: null }; // tslint:disable-line:no-null-keyword
public static verify = async (msgOrVerResults: OpenpgpMsgOrCleartext | OpenPGP.message.Verification[], pubs: OpenPGP.key.Key[]): Promise<VerifyRes> => {
const sig: VerifyRes = { match: null }; // tslint:disable-line:no-null-keyword
try {
// While this looks like bad method API design, it's here to ensure execution order when 1) reading data, 2) verifying, 3) processing signatures
// Else it will hang trying to read a stream: https://github.com/openpgpjs/openpgpjs/issues/916#issuecomment-510620625
Expand Down Expand Up @@ -152,29 +157,34 @@ export class PgpMsg {
return sig;
}

public static verifyDetached: PgpMsgMethod.VerifyDetached = async ({ plaintext, sigText }) => {
public static verifyDetached: PgpMsgMethod.VerifyDetached = async ({ plaintext, sigText, verificationPubkeys }) => {
const message = openpgp.message.fromText(Buf.fromUint8(plaintext).toUtfStr());
await message.appendSignature(Buf.fromUint8(sigText).toUtfStr());
const keys = await PgpMsg.getSortedKeys([], message);
return await PgpMsg.verify(message, keys.forVerification, keys.verificationContacts[0]);
if (verificationPubkeys) {
for (const verificationPubkey of verificationPubkeys) {
keys.forVerification.push(...(await openpgp.key.readArmored(verificationPubkey)).keys);
}
}
return await PgpMsg.verify(message, keys.forVerification);
}

public static decrypt: PgpMsgMethod.Decrypt = async ({ kisWithPp, encryptedData, msgPwd }) => {
public static decrypt: PgpMsgMethod.Decrypt = async ({ kisWithPp, encryptedData, msgPwd, verificationPubkeys }) => {
let prepared: PreparedForDecrypt;
const longids: DecryptError$longids = { message: [], matching: [], chosen: [], needPassphrase: [] };
try {
prepared = await PgpArmor.cryptoMsgPrepareForDecrypt(encryptedData);
} catch (formatErr) {
return { success: false, error: { type: DecryptErrTypes.format, message: String(formatErr) }, longids };
}
const keys = await PgpMsg.getSortedKeys(kisWithPp, prepared.message);
const keys = await PgpMsg.getSortedKeys(kisWithPp, prepared.message, verificationPubkeys);
longids.message = keys.encryptedFor;
longids.matching = keys.prvForDecrypt.map(ki => ki.longid);
longids.chosen = keys.prvForDecryptDecrypted.map(ki => ki.longid);
longids.needPassphrase = keys.prvForDecryptWithoutPassphrases.map(ki => ki.longid);
const isEncrypted = !prepared.isCleartext;
if (!isEncrypted) {
const signature = await PgpMsg.verify(prepared.message, keys.forVerification, keys.verificationContacts[0]);
const signature = await PgpMsg.verify(prepared.message, keys.forVerification);
const text = await openpgp.stream.readToEnd(prepared.message.getText()!);
return { success: true, content: Buf.fromUtfStr(text), isEncrypted, signature };
}
Expand All @@ -194,10 +204,12 @@ export class PgpMsg {
const passwords = msgPwd ? [msgPwd] : undefined;
const privateKeys = keys.prvForDecryptDecrypted.map(ki => ki.decrypted!);
const decrypted = await (prepared.message as OpenPGP.message.Message).decrypt(privateKeys, passwords, undefined, false);
await PgpMsg.cryptoMsgGetSignedBy(decrypted, keys); // we can only figure out who signed the msg once it's decrypted
// we can only figure out who signed the msg once it's decrypted
await PgpMsg.cryptoMsgGetSignedBy(decrypted, keys);
await PgpMsg.populateKeysForVerification(keys, verificationPubkeys);
const verifyResults = keys.signedBy.length ? await decrypted.verify(keys.forVerification) : undefined; // verify first to prevent stream hang
const content = new Buf(await openpgp.stream.readToEnd(decrypted.getLiteralData()!)); // read content second to prevent stream hang
const signature = verifyResults ? await PgpMsg.verify(verifyResults, [], keys.verificationContacts[0]) : undefined; // evaluate verify results third to prevent stream hang
const signature = verifyResults ? await PgpMsg.verify(verifyResults, []) : undefined; // evaluate verify results third to prevent stream hang
if (!prepared.isCleartext && (prepared.message as OpenPGP.message.Message).packets.filterByTag(openpgp.enums.packet.symmetricallyEncrypted).length) {
const noMdc = 'Security threat!\n\nMessage is missing integrity checks (MDC). The sender should update their outdated software.\n\nDisplay the message at your own risk.';
return { success: false, content, error: { type: DecryptErrTypes.noMdc, message: noMdc }, message: prepared.message, longids, isEncrypted };
Expand Down Expand Up @@ -299,21 +311,23 @@ export class PgpMsg {
}

private static cryptoMsgGetSignedBy = async (msg: OpenpgpMsgOrCleartext, keys: SortedKeysForDecrypt) => {
keys.signedBy = Value.arr.unique(await PgpKey.longids(msg.getSigningKeyIds ? msg.getSigningKeyIds() : []));
if (keys.signedBy.length && typeof Store.dbContactGet === 'function') {
const verificationContacts = await Store.dbContactGet(undefined, keys.signedBy);
keys.verificationContacts = verificationContacts.filter(contact => contact && contact.pubkey) as Contact[];
keys.signedBy = Value.arr.unique(await PgpKey.longids(
msg.getSigningKeyIds ? msg.getSigningKeyIds() : []));
}

private static populateKeysForVerification = async (keys: SortedKeysForDecrypt,
verificationPubkeys?: string[]) => {
if (typeof verificationPubkeys !== 'undefined') {
keys.forVerification = [];
for (const contact of keys.verificationContacts) {
const { keys: keysForVerification } = await openpgp.key.readArmored(contact.pubkey!);
for (const verificationPubkey of verificationPubkeys) {
const { keys: keysForVerification } = await openpgp.key.readArmored(verificationPubkey);
keys.forVerification.push(...keysForVerification);
}
}
}

private static getSortedKeys = async (kiWithPp: PrvKeyInfo[], msg: OpenpgpMsgOrCleartext): Promise<SortedKeysForDecrypt> => {
private static getSortedKeys = async (kiWithPp: PrvKeyInfo[], msg: OpenpgpMsgOrCleartext, verificationPubkeys?: string[]): Promise<SortedKeysForDecrypt> => {
const keys: SortedKeysForDecrypt = {
verificationContacts: [],
forVerification: [],
encryptedFor: [],
signedBy: [],
Expand All @@ -322,16 +336,21 @@ export class PgpMsg {
prvForDecryptDecrypted: [],
prvForDecryptWithoutPassphrases: [],
};
const encryptedForKeyids = msg instanceof openpgp.message.Message ? (msg as OpenPGP.message.Message).getEncryptionKeyIds() : [];
const encryptedForKeyids = msg instanceof openpgp.message.Message
? (msg as OpenPGP.message.Message).getEncryptionKeyIds()
: [];
keys.encryptedFor = await PgpKey.longids(encryptedForKeyids);
await PgpMsg.cryptoMsgGetSignedBy(msg, keys);
await PgpMsg.populateKeysForVerification(keys, verificationPubkeys);
if (keys.encryptedFor.length) {
for (const ki of kiWithPp) {
ki.parsed = await PgpKey.read(ki.private); // todo
// this is inefficient because we are doing unnecessary parsing of all keys here
// better would be to compare to already stored KeyInfo, however KeyInfo currently only holds primary longid, not longids of subkeys
// while messages are typically encrypted for subkeys, thus we have to parse the key to get the info
// we are filtering here to avoid a significant performance issue of having to attempt decrypting with all keys simultaneously
// better would be to compare to already stored KeyInfo, however KeyInfo currently
// only holds primary longid, not longids of subkeys, while messages are typically
// encrypted for subkeys, thus we have to parse the key to get the info
// we are filtering here to avoid a significant performance issue of having
// to attempt decrypting with all keys simultaneously
for (const longid of await Promise.all(ki.parsed.getKeyIds().map(({ bytes }) => PgpKey.longid(bytes)))) {
if (keys.encryptedFor.includes(longid!)) {
keys.prvMatching.push(ki);
Expand Down
Loading