Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TS: Rely on new web-stream-tools types, fix SignOptions #1502

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 22 additions & 24 deletions openpgp.d.ts
Expand Up @@ -7,6 +7,8 @@
* - Errietta Kostala <https://github.com/errietta>
*/

import type { WebStream as GenericWebStream, NodeStream as GenericNodeStream } from '@openpgp/web-stream-tools';

/* ############## v5 KEY #################### */
// The Key and PublicKey types can be used interchangably since TS cannot detect the difference, as they have the same class properties.
// The declared readKey(s) return type is Key instead of a PublicKey since it seems more obvious that a Key can be cast to a PrivateKey.
Expand Down Expand Up @@ -171,15 +173,9 @@ export class CleartextMessage {

/* ############## v5 MSG #################### */
export function generateSessionKey(options: { encryptionKeys: MaybeArray<PublicKey>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig }): Promise<SessionKey>;
export function encryptSessionKey(options: SessionKey & {
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, format?: 'armored', wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
}) : Promise<string>;
export function encryptSessionKey(options: SessionKey & {
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, format: 'binary', wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
}) : Promise<Uint8Array>;
export function encryptSessionKey(options: SessionKey & {
encryptionKeys?: MaybeArray<PublicKey>, passwords?: MaybeArray<string>, format: 'object', wildcard?: boolean, encryptionKeyIDs?: MaybeArray<KeyID>, date?: Date, encryptionUserIDs?: MaybeArray<UserID>, config?: PartialConfig
}) : Promise<Message<Data>>;
export function encryptSessionKey(options: EncryptSessionKeyOptions & { format?: 'armored' }): Promise<string>;
export function encryptSessionKey(options: EncryptSessionKeyOptions & { format: 'binary' }): Promise<Uint8Array>;
export function encryptSessionKey(options: EncryptSessionKeyOptions & { format: 'object' }): Promise<Message<Data>>;
export function decryptSessionKeys<T extends MaybeStream<Data>>(options: { message: Message<T>, decryptionKeys?: MaybeArray<PrivateKey>, passwords?: MaybeArray<string>, date?: Date, config?: PartialConfig }): Promise<SessionKey[]>;

export function readMessage<T extends MaybeStream<string>>(options: { armoredMessage: T, config?: PartialConfig }): Promise<Message<T>>;
Expand Down Expand Up @@ -271,15 +267,15 @@ export class Message<T extends MaybeStream<Data>> {

/** Get literal data that is the body of the message
*/
public getLiteralData(): MaybeStream<Uint8Array> | null;
public getLiteralData(): (T extends Stream<Data> ? WebStream<Uint8Array> : Uint8Array) | null;

/** Returns the key IDs of the keys that signed the message
*/
public getSigningKeyIDs(): KeyID[];

/** Get literal data as text
*/
public getText(): MaybeStream<string> | null;
public getText(): (T extends Stream<Data> ? WebStream<string> : string) | null;

public getFilename(): string | null;

Expand Down Expand Up @@ -548,18 +544,10 @@ export class PacketList<T extends AnyPacket> extends Array<T> {
/* ############## v5 STREAM #################### */

type Data = Uint8Array | string;
interface BaseStream<T extends Data> extends AsyncIterable<T> { }
interface WebStream<T extends Data> extends BaseStream<T> { // copied+simplified version of ReadableStream from lib.dom.d.ts
readonly locked: boolean; getReader: Function; pipeThrough: Function; pipeTo: Function; tee: Function;
cancel(reason?: any): Promise<void>;
}
interface NodeStream<T extends Data> extends BaseStream<T> { // copied+simplified version of ReadableStream from @types/node/index.d.ts
readable: boolean; pipe: Function; unpipe: Function; wrap: Function;
read(size?: number): string | Uint8Array; setEncoding(encoding: string): this; pause(): this; resume(): this;
isPaused(): boolean; unshift(chunk: string | Uint8Array): void;
}
type Stream<T extends Data> = WebStream<T> | NodeStream<T>;
type MaybeStream<T extends Data> = T | Stream<T>;
export interface WebStream<T extends Data> extends GenericWebStream<T> {}
export interface NodeStream<T extends Data> extends GenericNodeStream<T> {}
export type Stream<T extends Data> = WebStream<T> | NodeStream<T>;
export type MaybeStream<T extends Data> = T | Stream<T>;

/* ############## v5 GENERAL #################### */
type MaybeArray<T> = T | Array<T>;
Expand Down Expand Up @@ -627,7 +615,7 @@ interface DecryptOptions {

interface SignOptions {
message: CleartextMessage | Message<MaybeStream<Data>>;
signingKeys?: MaybeArray<PrivateKey>;
signingKeys: MaybeArray<PrivateKey>;
format?: 'armored' | 'binary' | 'object';
detached?: boolean;
signingKeyIDs?: MaybeArray<KeyID>;
Expand All @@ -652,6 +640,16 @@ interface VerifyOptions {
config?: PartialConfig;
}

interface EncryptSessionKeyOptions extends SessionKey {
encryptionKeys?: MaybeArray<PublicKey>,
passwords?: MaybeArray<string>,
format?: 'armored' | 'binary' | 'object',
date?: Date,
wildcard?: boolean,
encryptionKeyIDs?: MaybeArray<KeyID>,
encryptionUserIDs?: MaybeArray<UserID>,
config?: PartialConfig
}

interface SerializedKeyPair<T extends string|Uint8Array> {
privateKey: T;
Expand Down
20 changes: 14 additions & 6 deletions package-lock.json

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

5 changes: 3 additions & 2 deletions package.json
Expand Up @@ -59,7 +59,7 @@
"@openpgp/pako": "^1.0.12",
"@openpgp/seek-bzip": "^1.0.5-git",
"@openpgp/tweetnacl": "^1.0.3",
"@openpgp/web-stream-tools": "0.0.9",
"@openpgp/web-stream-tools": "^0.0.10",
"@rollup/plugin-commonjs": "^11.1.0",
"@rollup/plugin-node-resolve": "^7.1.3",
"@rollup/plugin-replace": "^2.3.2",
Expand Down Expand Up @@ -87,7 +87,8 @@
"rollup": "^2.38.5",
"rollup-plugin-terser": "^7.0.2",
"sinon": "^4.3.0",
"typescript": "^4.1.2"
"typescript": "^4.1.2",
"web-streams-polyfill": "^3.2.0"
},
"dependencies": {
"asn1.js": "^5.0.0"
Expand Down
52 changes: 36 additions & 16 deletions test/typescript/definitions.ts
Expand Up @@ -5,14 +5,17 @@
* - if it fails to build, edit the file to match type definitions
* - if it fails to run, edit this file to match the actual library API, then edit the definitions file (openpgp.d.ts) accordingly.
*/
import { ReadableStream as WebReadableStream } from 'web-streams-polyfill';
import { createReadStream } from 'fs';

import { expect } from 'chai';
import {
generateKey, readKey, readKeys, readPrivateKey, PrivateKey, Key, PublicKey, revokeKey,
readMessage, createMessage, Message, createCleartextMessage,
encrypt, decrypt, sign, verify, config, enums,
generateSessionKey, encryptSessionKey, decryptSessionKeys,
LiteralDataPacket, PacketList, CompressedDataPacket, PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket, CleartextMessage
LiteralDataPacket, PacketList, CompressedDataPacket, PublicKeyPacket, PublicSubkeyPacket, SecretKeyPacket, SecretSubkeyPacket, CleartextMessage,
WebStream, NodeStream,
} from '../..';

(async () => {
Expand Down Expand Up @@ -100,8 +103,10 @@ import {
// Get session keys from encrypted message
const sessionKeys = await decryptSessionKeys({ message: await readMessage({ binaryMessage: encryptedBinary }), decryptionKeys: privateKeys });
expect(sessionKeys).to.have.length(1);
const encryptedSessionKeys: string = await encryptSessionKey({ ...sessionKeys[0], passwords: 'pass', algorithm: 'aes128', aeadAlgorithm: 'eax' });
expect(encryptedSessionKeys).to.include('-----BEGIN PGP MESSAGE-----');
const armoredEncryptedSessionKeys: string = await encryptSessionKey({ ...sessionKeys[0], passwords: 'pass', algorithm: 'aes128', aeadAlgorithm: 'eax' });
expect(armoredEncryptedSessionKeys).to.include('-----BEGIN PGP MESSAGE-----');
const encryptedSessionKeys: Message<any> = await encryptSessionKey({ ...sessionKeys[0], passwords: 'pass', algorithm: 'aes128', aeadAlgorithm: 'eax', format: 'object' });
expect(encryptedSessionKeys).to.be.instanceOf(Message);
const newSessionKey = await generateSessionKey({ encryptionKeys: privateKey.toPublic() });
expect(newSessionKey.data).to.exist;
expect(newSessionKey.algorithm).to.exist;
Expand Down Expand Up @@ -180,19 +185,34 @@ import {
// const signed = await sign({ privateKeys, message, detached: true, format: 'binary' });
// console.log(signed); // Uint8Array

// // Streaming - encrypt text message on Node.js (armored)
// const data = fs.createReadStream(filename, { encoding: 'utf8' });
// const message = await createMessage({ text: data });
// const encrypted = await encrypt({ publicKeys, message });
// encrypted.on('data', chunk => {
// console.log(chunk); // String
// });

// // Streaming - encrypt binary message on Node.js (unarmored)
// const data = fs.createReadStream(filename);
// const message = await createMessage({ binary: data });
// const encrypted = await encrypt({ publicKeys, message, format: 'binary' });
// encrypted.pipe(targetStream);
// @ts-expect-error for passing text stream as binary data
await createMessage({ binary: new WebReadableStream<string>() });
// @ts-expect-error for passing binary stream as text data
await createMessage({ text: new WebReadableStream<Uint8Array>() });

// Streaming - encrypt text message (armored output)
try {
const nodeTextStream = createReadStream('non-existent-file', { encoding: 'utf8' });
const messageFromNodeTextStream = await createMessage({ text: nodeTextStream });
(await encrypt({ message: messageFromNodeTextStream, passwords: 'password', format: 'armored' })) as NodeStream<string>;
} catch (err) {}
const webTextStream = new WebReadableStream<string>();
const messageFromWebTextStream = await createMessage({ text: webTextStream });
(await encrypt({ message: messageFromWebTextStream, passwords: 'password', format: 'armored' })) as WebStream<string>;
messageFromWebTextStream.getText() as WebStream<string>;
messageFromWebTextStream.getLiteralData() as WebStream<Uint8Array>;

// Streaming - encrypt binary message (binary output)
try {
const nodeBinaryStream = createReadStream('non-existent-file');
const messageFromNodeBinaryStream = await createMessage({ binary: nodeBinaryStream });
(await encrypt({ message: messageFromNodeBinaryStream, passwords: 'password', format: 'binary' })) as NodeStream<Uint8Array>;
} catch (err) {}
const webBinaryStream = new WebReadableStream<Uint8Array>();
const messageFromWebBinaryStream = await createMessage({ binary: webBinaryStream });
(await encrypt({ message: messageFromWebBinaryStream, passwords: 'password', format: 'binary' })) as WebStream<Uint8Array>;
messageFromWebBinaryStream.getText() as WebStream<string>;
messageFromWebBinaryStream.getLiteralData() as WebStream<Uint8Array>;

console.log('TypeScript definitions are correct');
})().catch(e => {
Expand Down