TypeScript: rely on new web-stream-tools
types, fix SignOptions
(#1502)
The updated stream types improve type inference and checks, in particular when using ReadableStreams. Also: - add `EncryptSessionKeyOptions` to make it easier to declare wrapper functions of `encryptSessionKey`; - tighter output type inference in `Message.getText()` and `.getLiteralData()`.
This commit is contained in:
parent
a1ef5f509f
commit
d89cc48bf3
46
openpgp.d.ts
vendored
46
openpgp.d.ts
vendored
|
@ -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.
|
||||
|
@ -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>>;
|
||||
|
@ -271,7 +267,7 @@ 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
|
||||
*/
|
||||
|
@ -279,7 +275,7 @@ export class Message<T extends MaybeStream<Data>> {
|
|||
|
||||
/** Get literal data as text
|
||||
*/
|
||||
public getText(): MaybeStream<string> | null;
|
||||
public getText(): (T extends Stream<Data> ? WebStream<string> : string) | null;
|
||||
|
||||
public getFilename(): string | null;
|
||||
|
||||
|
@ -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>;
|
||||
|
@ -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>;
|
||||
|
@ -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;
|
||||
|
|
20
package-lock.json
generated
20
package-lock.json
generated
|
@ -267,13 +267,21 @@
|
|||
"dev": true
|
||||
},
|
||||
"@openpgp/web-stream-tools": {
|
||||
"version": "0.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@openpgp/web-stream-tools/-/web-stream-tools-0.0.9.tgz",
|
||||
"integrity": "sha512-GEKuXpQRshUqgKH4sMcwYbHolxaUSHIowcIMd02EsnLv4q5acP0z9pRUy3kjV0ZyRPgyD0vXAy60Rf0MPKoCMw==",
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@openpgp/web-stream-tools/-/web-stream-tools-0.0.10.tgz",
|
||||
"integrity": "sha512-1ONZADML0fb0RJR5UiGYPnRf9VaYBYUBc1gF9jyq57sHkr58cp5/BQHS+ivrqbRw21Sb70FKTssmJbRe71V+kw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@mattiasbuelens/web-streams-adapter": "~0.1.0",
|
||||
"web-streams-polyfill": "~3.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"web-streams-polyfill": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz",
|
||||
"integrity": "sha512-d2H/t0eqRNM4w2WvmTdoeIvzAUSpK7JmATB8Nr2lb7nQ9BTIJVjbQ/TRFVEh2gUH1HwclPdoPtfMoFfetXaZnA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-commonjs": {
|
||||
|
@ -5401,9 +5409,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"web-streams-polyfill": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.0.3.tgz",
|
||||
"integrity": "sha512-d2H/t0eqRNM4w2WvmTdoeIvzAUSpK7JmATB8Nr2lb7nQ9BTIJVjbQ/TRFVEh2gUH1HwclPdoPtfMoFfetXaZnA==",
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz",
|
||||
"integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==",
|
||||
"dev": true
|
||||
},
|
||||
"which": {
|
||||
|
|
|
@ -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",
|
||||
|
@ -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"
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* - 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 {
|
||||
|
@ -12,7 +14,8 @@ import {
|
|||
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 () => {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
// });
|
||||
// @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 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);
|
||||
// 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 => {
|
||||
|
|
Loading…
Reference in New Issue
Block a user