Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 0 additions & 8 deletions extension/js/common/core/crypto/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,6 @@ export class KeyUtil {
return (await KeyUtil.parseMany(text))[0];
}

public static dearmor = async (text: string): Promise<{ type: number, data: Uint8Array }> => {
const decoded = await opgp.armor.decode(text);
let buffer = new Uint8Array();
const ws = new WritableStream<Uint8Array>({ write: chunk => { buffer = new Uint8Array([...buffer, ...chunk]); } });
await decoded.data.pipeTo(ws);
return { type: decoded.type, data: buffer };
}

public static parseMany = async (text: string): Promise<Key[]> => {
const keyType = KeyUtil.getKeyType(text);
if (keyType === 'openpgp') {
Expand Down
10 changes: 10 additions & 0 deletions extension/js/common/core/crypto/pgp/pgp-armor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Buf } from '../../buf.js';
import { ReplaceableMsgBlockType } from '../../msg-block.js';
import { Str } from '../../common.js';
import { opgp } from './openpgpjs-custom.js';
import { Stream } from '../../stream.js';

export type PreparedForDecrypt = { isArmored: boolean, isCleartext: true, message: OpenPGP.cleartext.CleartextMessage | OpenPGP.message.Message }
| { isArmored: boolean, isCleartext: false, message: OpenPGP.message.Message };
Expand Down Expand Up @@ -94,4 +95,13 @@ export class PgpArmor {
throw new Error('Message does not have armor headers');
}

public static dearmor = async (text: string): Promise<{ type: OpenPGP.enums.armor, data: Uint8Array }> => {
const decoded = await opgp.armor.decode(text);
const data = await Stream.readToEnd(decoded.data);
return { type: decoded.type, data };
}

public static armor = (messagetype: OpenPGP.enums.armor, body: object, partindex?: number, parttotal?: number, customComment?: string): string => {
return opgp.armor.encode(messagetype, body, partindex, parttotal, customComment);
}
}
10 changes: 10 additions & 0 deletions extension/js/common/core/stream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */

export class Stream {
public static readToEnd = async (data: ReadableStream<Uint8Array>) => {
let buffer = new Uint8Array();
const ws = new WritableStream<Uint8Array>({ write: chunk => { buffer = new Uint8Array([...buffer, ...chunk]); } });
await data.pipeTo(ws);
return buffer;
}
}
4 changes: 2 additions & 2 deletions extension/js/common/core/types/openpgp.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,13 +506,13 @@ declare namespace OpenPGP {
* @param partindex
* @param parttotal
*/
function encode(messagetype: enums.armor, body: object, partindex: number, parttotal: number): string;
function encode(messagetype: enums.armor, body: object, partindex?: number, parttotal?: number, customComment?: string): string;

/** DeArmor an OpenPGP armored message; verify the checksum and return the encoded bytes
*
* @param text OpenPGP armored message
*/
function decode(text: string): Promise<{ type: number, data: ReadableStream<Uint8Array> }>;
function decode(text: string): Promise<{ type: enums.armor, data: ReadableStream<Uint8Array> }>;
}

export namespace cleartext {
Expand Down
18 changes: 18 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
"devDependencies": {
"@types/chrome": "0.0.130",
"@types/chai": "4.2.15",
"@types/chai-as-promised": "^7.1.3",
"@types/dompurify": "2.2.1",
"@types/jquery": "3.5.5",
"@types/mailparser": "3.0.1",
"@types/puppeteer": "5.4.3",
"@types/mkdirp": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@types/request": "2.48.5",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"ava": "3.15.0",
"@typescript-eslint/parser": "^4.15.1",
"chai": "4.3.0",
"chai-as-promised": "^7.1.1",
"del": "6.0.0",
"eslint": "^7.20.0",
"eslint-plugin-header": "^3.1.1",
Expand Down Expand Up @@ -90,4 +91,4 @@
"url": "https://github.com/FlowCrypt/flowcrypt-browser/issues"
},
"homepage": "https://flowcrypt.com"
}
}
2 changes: 1 addition & 1 deletion test/source/buf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const UTF8 = `გამარჯობა.\nこんにちは。\nЗдравств
const UTF8_AS_BYTES = Buffer.from(UTF8);
const UTF8_AS_RAW_STRING = Buffer.from(UTF8).toString('binary');

const equals = (a: string | Uint8Array, b: string | Uint8Array) => {
export const equals = (a: string | Uint8Array, b: string | Uint8Array) => {
expect(typeof a).to.equal(typeof b, `types dont match`);
if (typeof a === 'string' && typeof b === 'string') {
expect(a).to.equal(b, 'string result mismatch');
Expand Down
20 changes: 10 additions & 10 deletions test/source/mock/wkd/wkd-endpoints.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */

import { KeyUtil } from '../../core/crypto/key.js';
import { PgpArmor } from '../../core/crypto/pgp/pgp-armor.js';
import { HandlersDefinition } from '../all-apis-mock';

const alice = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Expand Down Expand Up @@ -191,28 +191,28 @@ ctnWuBzRDeI0n6XDaPv5TpKpS7uqy/fTlJLGE9vZTFUKzeGkQFomBoXNVWs=

export const mockWkdEndpoints: HandlersDefinition = {
'/.well-known/openpgpkey/hu/ihyath4noz8dsckzjbuyqnh4kbup6h4i?l=john.doe': async () => {
return Buffer.from((await KeyUtil.dearmor(johnDoe1)).data); // direct for john.doe@localhost
return Buffer.from((await PgpArmor.dearmor(johnDoe1)).data); // direct for john.doe@localhost
},
'/.well-known/openpgpkey/hu/ihyath4noz8dsckzjbuyqnh4kbup6h4i?l=John.Doe': async () => {
return Buffer.from((await KeyUtil.dearmor(johnDoe1)).data); // direct for John.Doe@localhost
return Buffer.from((await PgpArmor.dearmor(johnDoe1)).data); // direct for John.Doe@localhost
},
'/.well-known/openpgpkey/hu/cb53pfqmbzc8mm3ecbjxyen65fdxos56?l=jack.advanced': async () => {
return Buffer.from((await KeyUtil.dearmor(jackAdvanced)).data); // direct for jack.advanced@localhost
return Buffer.from((await PgpArmor.dearmor(jackAdvanced)).data); // direct for jack.advanced@localhost
},
'/.well-known/openpgpkey/localhost/hu/ihyath4noz8dsckzjbuyqnh4kbup6h4i?l=john.doe': async () => {
return Buffer.from((await KeyUtil.dearmor(johnDoe)).data); // advanced for john.doe@localhost
return Buffer.from((await PgpArmor.dearmor(johnDoe)).data); // advanced for john.doe@localhost
},
'/.well-known/openpgpkey/localhost/hu/ihyath4noz8dsckzjbuyqnh4kbup6h4i?l=John.Doe': async () => {
return Buffer.from((await KeyUtil.dearmor(johnDoe)).data); // advanced for John.Doe@localhost
return Buffer.from((await PgpArmor.dearmor(johnDoe)).data); // advanced for John.Doe@localhost
},
'/.well-known/openpgpkey/localhost/hu/pob4adi8roqdsmtmxikx68pi6ij35oca?l=incorrect': async () => {
return Buffer.from((await KeyUtil.dearmor(alice)).data); // advanced for incorrect@localhost
return Buffer.from((await PgpArmor.dearmor(alice)).data); // advanced for incorrect@localhost
},
'/.well-known/openpgpkey/localhost/hu/66iu18j7mk6hod4wqzf6qd37u6wejx4y?l=some.revoked': async () => {
return Buffer.from([
...(await KeyUtil.dearmor(validAmongRevokedRevoked1)).data,
...(await KeyUtil.dearmor(validAmongRevokedValid)).data,
...(await KeyUtil.dearmor(validAmongRevokedRevoked2)).data,
...(await PgpArmor.dearmor(validAmongRevokedRevoked1)).data,
...(await PgpArmor.dearmor(validAmongRevokedValid)).data,
...(await PgpArmor.dearmor(validAmongRevokedRevoked2)).data,
]);
},
'/.well-known/openpgpkey/localhost/policy': async () => {
Expand Down
15 changes: 15 additions & 0 deletions test/source/tests/flaky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SettingsPageRecipe } from './page-recipe/settings-page-recipe';
import { SetupPageRecipe } from './page-recipe/setup-page-recipe';
import { TestWithBrowser } from './../test';
import { GoogleData } from './../mock/google/google-data';
import { Stream } from '../core/stream';

// tslint:disable:no-blank-lines-func

Expand Down Expand Up @@ -119,6 +120,20 @@ export const defineFlakyTests = (testVariant: TestVariant, testWithBrowser: Test
expect(fileText.toString()).to.equal(`small text file\nnot much here\nthis worked\n`);
}));

ava.default(`[unit][Stream.readToEnd] efficiently handles multiple chunks`, async t => {
const stream = new ReadableStream<Uint8Array>({
start(controller) {
for (let i = 0; i < 10; i++) {
controller.enqueue(Buffer.from('test'.repeat(1000000)));
}
controller.close();
}
});
const result = await Stream.readToEnd(stream);
expect(result.length).to.equal(40000000);
t.pass();
});

}

};
28 changes: 27 additions & 1 deletion test/source/tests/unit-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import { MsgBlock } from '../core/msg-block';
import { MsgBlockParser } from '../core/msg-block-parser';
import { PgpHash } from '../core/crypto/pgp/pgp-hash';
import { TestVariant } from '../util';
import { expect } from 'chai';
import chai = require('chai');
import chaiAsPromised = require('chai-as-promised');
import { KeyUtil, KeyInfoWithOptionalPp } from '../core/crypto/key';
import { UnreportableError } from '../platform/catch.js';
import { Buf } from '../core/buf';
Expand All @@ -17,7 +18,11 @@ import { Attachment } from '../core/attachment.js';
import { ContactStore } from '../platform/store/contact-store.js';
import { GoogleData, GmailParser, GmailMsg } from '../mock/google/google-data';
import { pubkey2864E326A5BE488A, rsa1024subkeyOnly, rsa1024subkeyOnlyEncrypted } from './tooling/consts';
import { PgpArmor } from '../core/crypto/pgp/pgp-armor';
import { equals } from '../buf.js';

chai.use(chaiAsPromised);
const expect = chai.expect;
// tslint:disable:no-blank-lines-func
/* eslint-disable max-len */
// tslint:disable:no-unused-expression
Expand Down Expand Up @@ -1526,5 +1531,26 @@ kBXo
expect(key.usableForSigningButExpired).to.equal(false);
t.pass();
});

ava.default(`[unit][PgpArmor.dearmor] throws on incorrect sequence`, async t => {
await expect(PgpArmor.dearmor(`-----BEGIN PGP MESSAGE-----

AAAAAAAAAAAAAAAAzzzzzzzzzzzzzzzzzzzzzzzzzzzz.....`)).to.eventually.be.rejectedWith('Misformed armored text');
t.pass();
});

ava.default(`[unit][PgpArmor.dearmor] correctly handles long string`, async t => {
const source = Buffer.from('The test string concatenated many times to produce large output'.repeat(100000));
const type = 3;
const armored = PgpArmor.armor(type, source);
const dearmored = await PgpArmor.dearmor(armored);
expect(dearmored.type).to.equal(type);
equals(
dearmored.data,
source
);
t.pass();
});

}
};