UNPKG

29.1 kBMarkdownView Raw
1OpenPGP.js [![BrowserStack Status](https://automate.browserstack.com/badge.svg?badge_key=N1l2eHFOanVBMU9wYWxJM3ZnWERnc1lidkt5UkRqa3BralV3SWVhOGpGTT0tLVljSjE4Z3dzVmdiQjl6RWgxb2c3T2c9PQ==--5864052cd523f751b6b907d547ac9c4c5f88c8a3)](https://automate.browserstack.com/public-build/N1l2eHFOanVBMU9wYWxJM3ZnWERnc1lidkt5UkRqa3BralV3SWVhOGpGTT0tLVljSjE4Z3dzVmdiQjl6RWgxb2c3T2c9PQ==--5864052cd523f751b6b907d547ac9c4c5f88c8a3) [![Join the chat on Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/openpgpjs/openpgpjs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
2==========
3
4[OpenPGP.js](https://openpgpjs.org/) is a JavaScript implementation of the OpenPGP protocol. It implements [RFC4880](https://tools.ietf.org/html/rfc4880) and parts of [RFC4880bis](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10).
5
6**Table of Contents**
7
8- [OpenPGP.js](#openpgpjs)
9 - [Platform Support](#platform-support)
10 - [Performance](#performance)
11 - [Getting started](#getting-started)
12 - [Node.js](#nodejs)
13 - [Deno (experimental)](#deno-experimental)
14 - [Browser (webpack)](#browser-webpack)
15 - [Browser (plain files)](#browser-plain-files)
16 - [Examples](#examples)
17 - [Encrypt and decrypt *Uint8Array* data with a password](#encrypt-and-decrypt-uint8array-data-with-a-password)
18 - [Encrypt and decrypt *String* data with PGP keys](#encrypt-and-decrypt-string-data-with-pgp-keys)
19 - [Encrypt symmetrically with compression](#encrypt-symmetrically-with-compression)
20 - [Streaming encrypt *Uint8Array* data with a password](#streaming-encrypt-uint8array-data-with-a-password)
21 - [Streaming encrypt and decrypt *String* data with PGP keys](#streaming-encrypt-and-decrypt-string-data-with-pgp-keys)
22 - [Generate new key pair](#generate-new-key-pair)
23 - [Revoke a key](#revoke-a-key)
24 - [Sign and verify cleartext messages](#sign-and-verify-cleartext-messages)
25 - [Create and verify *detached* signatures](#create-and-verify-detached-signatures)
26 - [Streaming sign and verify *Uint8Array* data](#streaming-sign-and-verify-uint8array-data)
27 - [Documentation](#documentation)
28 - [Security Audit](#security-audit)
29 - [Security recommendations](#security-recommendations)
30 - [Development](#development)
31 - [How do I get involved?](#how-do-i-get-involved)
32 - [License](#license)
33
34### Platform Support
35
36* The `dist/openpgp.min.js` bundle works well with recent versions of Chrome, Firefox, Safari and Edge.
37
38* The `dist/node/openpgp.min.js` bundle works well in Node.js. It is used by default when you `require('openpgp')` in Node.js.
39
40* Currently, Chrome, Safari and Edge have partial implementations of the
41[Streams specification](https://streams.spec.whatwg.org/), and Firefox
42has a partial implementation behind feature flags. Chrome is the only
43browser that implements `TransformStream`s, which we need, so we include
44a [polyfill](https://github.com/MattiasBuelens/web-streams-polyfill) for
45all other browsers. Please note that in those browsers, the global
46`ReadableStream` property gets overwritten with the polyfill version if
47it exists. In some edge cases, you might need to use the native
48`ReadableStream` (for example when using it to create a `Response`
49object), in which case you should store a reference to it before loading
50OpenPGP.js. There is also the
51[web-streams-adapter](https://github.com/MattiasBuelens/web-streams-adapter)
52library to convert back and forth between them.
53
54### Performance
55
56* Version 3.0.0 of the library introduces support for public-key cryptography using [elliptic curves](https://wiki.gnupg.org/ECC). We use native implementations on browsers and Node.js when available. Elliptic curve cryptography provides stronger security per bits of key, which allows for much faster operations. Currently the following curves are supported:
57
58 | Curve | Encryption | Signature | NodeCrypto | WebCrypto | Constant-Time |
59 |:---------------:|:----------:|:---------:|:----------:|:---------:|:-----------------:|
60 | curve25519 | ECDH | N/A | No | No | Algorithmically** |
61 | ed25519 | N/A | EdDSA | No | No | Algorithmically** |
62 | p256 | ECDH | ECDSA | Yes* | Yes* | If native*** |
63 | p384 | ECDH | ECDSA | Yes* | Yes* | If native*** |
64 | p521 | ECDH | ECDSA | Yes* | Yes* | If native*** |
65 | brainpoolP256r1 | ECDH | ECDSA | Yes* | No | If native*** |
66 | brainpoolP384r1 | ECDH | ECDSA | Yes* | No | If native*** |
67 | brainpoolP512r1 | ECDH | ECDSA | Yes* | No | If native*** |
68 | secp256k1 | ECDH | ECDSA | Yes* | No | If native*** |
69
70 \* when available
71 \** the curve25519 and ed25519 implementations are algorithmically constant-time, but may not be constant-time after optimizations of the JavaScript compiler
72 \*** these curves are only constant-time if the underlying native implementation is available and constant-time
73
74* Version 2.x of the library has been built from the ground up with Uint8Arrays. This allows for much better performance and memory usage than strings.
75
76* If the user's browser supports [native WebCrypto](https://caniuse.com/#feat=cryptography) via the `window.crypto.subtle` API, this will be used. Under Node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is used.
77
78* The library implements the [RFC4880bis proposal](https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10) for authenticated encryption using native AES-EAX, OCB, or GCM. This makes symmetric encryption up to 30x faster on supported platforms. Since the specification has not been finalized and other OpenPGP implementations haven't adopted it yet, the feature is currently behind a flag. **Note: activating this setting can break compatibility with other OpenPGP implementations, and also with future versions of OpenPGP.js. Don't use it with messages you want to store on disk or in a database.** You can enable it by setting `openpgp.config.aeadProtect = true`.
79
80 You can change the AEAD mode by setting one of the following options:
81
82 ```
83 openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax // Default, native
84 openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb // Non-native
85 openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM // **Non-standard**, fastest
86 ```
87
88* For environments that don't provide native crypto, the library falls back to [asm.js](https://caniuse.com/#feat=asmjs) implementations of AES, SHA-1, and SHA-256.
89
90
91### Getting started
92
93#### Node.js
94
95Install OpenPGP.js using npm and save it in your dependencies:
96
97```sh
98npm install --save openpgp
99```
100
101And import it as a CommonJS module:
102
103```js
104const openpgp = require('openpgp');
105```
106
107Or as an ES6 module, from an .mjs file:
108
109```js
110import * as openpgp from 'openpgp';
111```
112
113#### Deno (experimental)
114
115Import as an ES6 module, using /dist/openpgp.mjs.
116
117```js
118import * as openpgp from './openpgpjs/dist/openpgp.mjs';
119```
120
121#### Browser (webpack)
122
123Install OpenPGP.js using npm and save it in your devDependencies:
124
125```sh
126npm install --save-dev openpgp
127```
128
129And import it as an ES6 module:
130
131```js
132import * as openpgp from 'openpgp';
133```
134
135You can also only import the functions you need, as follows:
136
137```js
138import { readMessage, decrypt } from 'openpgp';
139```
140
141Or, if you want to use the lightweight build (which is smaller, and lazily loads non-default curves on demand):
142
143```js
144import * as openpgp from 'openpgp/lightweight';
145```
146
147To test whether the lazy loading works, try to generate a key with a non-standard curve:
148
149```js
150import { generateKey } from 'openpgp/lightweight';
151await generateKey({ curve: 'brainpoolP512r1', userIDs: [{ name: 'Test', email: 'test@test.com' }] });
152```
153
154For more examples of how to generate a key, see [Generate new key pair](#generate-new-key-pair). It is recommended to use `curve25519` instead of `brainpoolP512r1` by default.
155
156
157#### Browser (plain files)
158
159Grab `openpgp.min.js` from [unpkg.com/openpgp/dist](https://unpkg.com/openpgp/dist/), and load it in a script tag:
160
161```html
162<script src="openpgp.min.js"></script>
163```
164
165Or, to load OpenPGP.js as an ES6 module, grab `openpgp.min.mjs` from [unpkg.com/openpgp/dist](https://unpkg.com/openpgp/dist/), and import it as follows:
166
167```html
168<script type="module">
169import * as openpgp from './openpgp.min.mjs';
170</script>
171```
172
173To offload cryptographic operations off the main thread, you can implement a Web Worker in your application and load OpenPGP.js from there. For an example Worker implementation, see `test/worker/worker_example.js`.
174
175#### TypeScript
176
177Since TS is not fully integrated in the library, TS-only dependencies are currently listed as `devDependencies`, so to compile the project you’ll need to add `@openpgp/web-stream-tools` manually (NB: only versions below v0.12 are compatible with OpenPGP.js v5):
178
179```sh
180npm install --save-dev @openpgp/web-stream-tools@0.0.11-patch-0
181```
182
183If you notice missing or incorrect type definitions, feel free to open a PR.
184
185### Examples
186
187Here are some examples of how to use OpenPGP.js v5. For more elaborate examples and working code, please check out the [public API unit tests](https://github.com/openpgpjs/openpgpjs/blob/main/test/general/openpgp.js). If you're upgrading from v4 it might help to check out the [changelog](https://github.com/openpgpjs/openpgpjs/wiki/V5-Changelog) and [documentation](https://github.com/openpgpjs/openpgpjs#documentation).
188
189#### Encrypt and decrypt *Uint8Array* data with a password
190
191Encryption will use the algorithm specified in config.preferredSymmetricAlgorithm (defaults to aes256), and decryption will use the algorithm used for encryption.
192
193```js
194(async () => {
195 const message = await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x01, 0x01]) });
196 const encrypted = await openpgp.encrypt({
197 message, // input as Message object
198 passwords: ['secret stuff'], // multiple passwords possible
199 format: 'binary' // don't ASCII armor (for Uint8Array output)
200 });
201 console.log(encrypted); // Uint8Array
202
203 const encryptedMessage = await openpgp.readMessage({
204 binaryMessage: encrypted // parse encrypted bytes
205 });
206 const { data: decrypted } = await openpgp.decrypt({
207 message: encryptedMessage,
208 passwords: ['secret stuff'], // decrypt with password
209 format: 'binary' // output as Uint8Array
210 });
211 console.log(decrypted); // Uint8Array([0x01, 0x01, 0x01])
212})();
213```
214
215#### Encrypt and decrypt *String* data with PGP keys
216
217Encryption will use the algorithm preferred by the public (encryption) key (defaults to aes256 for keys generated in OpenPGP.js), and decryption will use the algorithm used for encryption.
218
219```js
220const openpgp = require('openpgp'); // use as CommonJS, AMD, ES6 module or via window.openpgp
221
222(async () => {
223 // put keys in backtick (``) to avoid errors caused by spaces or tabs
224 const publicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
225...
226-----END PGP PUBLIC KEY BLOCK-----`;
227 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
228...
229-----END PGP PRIVATE KEY BLOCK-----`; // encrypted private key
230 const passphrase = `yourPassphrase`; // what the private key is encrypted with
231
232 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
233
234 const privateKey = await openpgp.decryptKey({
235 privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
236 passphrase
237 });
238
239 const encrypted = await openpgp.encrypt({
240 message: await openpgp.createMessage({ text: 'Hello, World!' }), // input as Message object
241 encryptionKeys: publicKey,
242 signingKeys: privateKey // optional
243 });
244 console.log(encrypted); // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'
245
246 const message = await openpgp.readMessage({
247 armoredMessage: encrypted // parse armored message
248 });
249 const { data: decrypted, signatures } = await openpgp.decrypt({
250 message,
251 verificationKeys: publicKey, // optional
252 decryptionKeys: privateKey
253 });
254 console.log(decrypted); // 'Hello, World!'
255 // check signature validity (signed messages only)
256 try {
257 await signatures[0].verified; // throws on invalid signature
258 console.log('Signature is valid');
259 } catch (e) {
260 throw new Error('Signature could not be verified: ' + e.message);
261 }
262})();
263```
264
265Encrypt to multiple public keys:
266
267```js
268(async () => {
269 const publicKeysArmored = [
270 `-----BEGIN PGP PUBLIC KEY BLOCK-----
271...
272-----END PGP PUBLIC KEY BLOCK-----`,
273 `-----BEGIN PGP PUBLIC KEY BLOCK-----
274...
275-----END PGP PUBLIC KEY BLOCK-----`
276 ];
277 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
278...
279-----END PGP PRIVATE KEY BLOCK-----`; // encrypted private key
280 const passphrase = `yourPassphrase`; // what the private key is encrypted with
281 const plaintext = 'Hello, World!';
282
283 const publicKeys = await Promise.all(publicKeysArmored.map(armoredKey => openpgp.readKey({ armoredKey })));
284
285 const privateKey = await openpgp.decryptKey({
286 privateKey: await openpgp.readKey({ armoredKey: privateKeyArmored }),
287 passphrase
288 });
289
290 const message = await openpgp.createMessage({ text: plaintext });
291 const encrypted = await openpgp.encrypt({
292 message, // input as Message object
293 encryptionKeys: publicKeys,
294 signingKeys: privateKey // optional
295 });
296 console.log(encrypted); // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'
297})();
298```
299
300If you expect an encrypted message to be signed with one of the public keys you have, and do not want to trust the decrypted data otherwise, you can pass the decryption option `expectSigned = true`, so that the decryption operation will fail if no valid signature is found:
301```js
302(async () => {
303 // put keys in backtick (``) to avoid errors caused by spaces or tabs
304 const publicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
305...
306-----END PGP PUBLIC KEY BLOCK-----`;
307 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
308...
309-----END PGP PRIVATE KEY BLOCK-----`; // encrypted private key
310 const passphrase = `yourPassphrase`; // what the private key is encrypted with
311
312 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
313
314 const privateKey = await openpgp.decryptKey({
315 privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
316 passphrase
317 });
318
319 const encryptedAndSignedMessage = `-----BEGIN PGP MESSAGE-----
320...
321-----END PGP MESSAGE-----`;
322
323 const message = await openpgp.readMessage({
324 armoredMessage: encryptedAndSignedMessage // parse armored message
325 });
326 // decryption will fail if all signatures are invalid or missing
327 const { data: decrypted, signatures } = await openpgp.decrypt({
328 message,
329 decryptionKeys: privateKey,
330 expectSigned: true,
331 verificationKeys: publicKey, // mandatory with expectSigned=true
332 });
333 console.log(decrypted); // 'Hello, World!'
334})();
335```
336
337#### Encrypt symmetrically with compression
338
339By default, `encrypt` will not use any compression when encrypting symmetrically only (i.e. when no `encryptionKeys` are given).
340It's possible to change that behaviour by enabling compression through the config, either for the single encryption:
341
342```js
343(async () => {
344 const message = await openpgp.createMessage({ binary: new Uint8Array([0x01, 0x02, 0x03]) }); // or createMessage({ text: 'string' })
345 const encrypted = await openpgp.encrypt({
346 message,
347 passwords: ['secret stuff'], // multiple passwords possible
348 config: { preferredCompressionAlgorithm: openpgp.enums.compression.zlib } // compress the data with zlib
349 });
350})();
351```
352
353or by changing the default global configuration:
354```js
355openpgp.config.preferredCompressionAlgorithm = openpgp.enums.compression.zlib
356```
357
358Where the value can be any of:
359 * `openpgp.enums.compression.zip`
360 * `openpgp.enums.compression.zlib`
361 * `openpgp.enums.compression.uncompressed` (default)
362
363
364
365#### Streaming encrypt *Uint8Array* data with a password
366
367```js
368(async () => {
369 const readableStream = new ReadableStream({
370 start(controller) {
371 controller.enqueue(new Uint8Array([0x01, 0x02, 0x03]));
372 controller.close();
373 }
374 });
375
376 const message = await openpgp.createMessage({ binary: readableStream });
377 const encrypted = await openpgp.encrypt({
378 message, // input as Message object
379 passwords: ['secret stuff'], // multiple passwords possible
380 format: 'binary' // don't ASCII armor (for Uint8Array output)
381 });
382 console.log(encrypted); // raw encrypted packets as ReadableStream<Uint8Array>
383
384 // Either pipe the above stream somewhere, pass it to another function,
385 // or read it manually as follows:
386 for await (const chunk of encrypted) {
387 console.log('new chunk:', chunk); // Uint8Array
388 }
389})();
390```
391
392For more information on using ReadableStreams, see [the MDN Documentation on the
393Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API).
394
395You can also pass a [Node.js `Readable`
396stream](https://nodejs.org/api/stream.html#stream_class_stream_readable), in
397which case OpenPGP.js will return a Node.js `Readable` stream as well, which you
398can `.pipe()` to a `Writable` stream, for example.
399
400
401#### Streaming encrypt and decrypt *String* data with PGP keys
402
403```js
404(async () => {
405 const publicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
406...
407-----END PGP PUBLIC KEY BLOCK-----`; // Public key
408 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
409...
410-----END PGP PRIVATE KEY BLOCK-----`; // Encrypted private key
411 const passphrase = `yourPassphrase`; // Password that private key is encrypted with
412
413 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
414
415 const privateKey = await openpgp.decryptKey({
416 privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
417 passphrase
418 });
419
420 const readableStream = new ReadableStream({
421 start(controller) {
422 controller.enqueue('Hello, world!');
423 controller.close();
424 }
425 });
426
427 const encrypted = await openpgp.encrypt({
428 message: await openpgp.createMessage({ text: readableStream }), // input as Message object
429 encryptionKeys: publicKey,
430 signingKeys: privateKey // optional
431 });
432 console.log(encrypted); // ReadableStream containing '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'
433
434 const message = await openpgp.readMessage({
435 armoredMessage: encrypted // parse armored message
436 });
437 const decrypted = await openpgp.decrypt({
438 message,
439 verificationKeys: publicKey, // optional
440 decryptionKeys: privateKey
441 });
442 const chunks = [];
443 for await (const chunk of decrypted.data) {
444 chunks.push(chunk);
445 }
446 const plaintext = chunks.join('');
447 console.log(plaintext); // 'Hello, World!'
448})();
449```
450
451
452#### Generate new key pair
453
454ECC keys (smaller and faster to generate):
455
456Possible values for `curve` are: `curve25519`, `ed25519`, `p256`, `p384`, `p521`,
457`brainpoolP256r1`, `brainpoolP384r1`, `brainpoolP512r1`, and `secp256k1`.
458Note that both the `curve25519` and `ed25519` options generate a primary key for signing using Ed25519
459and a subkey for encryption using Curve25519.
460
461```js
462(async () => {
463 const { privateKey, publicKey, revocationCertificate } = await openpgp.generateKey({
464 type: 'ecc', // Type of the key, defaults to ECC
465 curve: 'curve25519', // ECC curve name, defaults to curve25519
466 userIDs: [{ name: 'Jon Smith', email: 'jon@example.com' }], // you can pass multiple user IDs
467 passphrase: 'super long and hard to guess secret', // protects the private key
468 format: 'armored' // output key format, defaults to 'armored' (other options: 'binary' or 'object')
469 });
470
471 console.log(privateKey); // '-----BEGIN PGP PRIVATE KEY BLOCK ... '
472 console.log(publicKey); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
473 console.log(revocationCertificate); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
474})();
475```
476
477RSA keys (increased compatibility):
478
479```js
480(async () => {
481 const { privateKey, publicKey } = await openpgp.generateKey({
482 type: 'rsa', // Type of the key
483 rsaBits: 4096, // RSA key size (defaults to 4096 bits)
484 userIDs: [{ name: 'Jon Smith', email: 'jon@example.com' }], // you can pass multiple user IDs
485 passphrase: 'super long and hard to guess secret' // protects the private key
486 });
487})();
488```
489
490#### Revoke a key
491
492Using a revocation certificate:
493```js
494(async () => {
495 const { publicKey: revokedKeyArmored } = await openpgp.revokeKey({
496 key: await openpgp.readKey({ armoredKey: publicKeyArmored }),
497 revocationCertificate,
498 format: 'armored' // output armored keys
499 });
500 console.log(revokedKeyArmored); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
501})();
502```
503
504Using the private key:
505```js
506(async () => {
507 const { publicKey: revokedKeyArmored } = await openpgp.revokeKey({
508 key: await openpgp.readKey({ armoredKey: privateKeyArmored }),
509 format: 'armored' // output armored keys
510 });
511 console.log(revokedKeyArmored); // '-----BEGIN PGP PUBLIC KEY BLOCK ... '
512})();
513```
514
515#### Sign and verify cleartext messages
516
517```js
518(async () => {
519 const publicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
520...
521-----END PGP PUBLIC KEY BLOCK-----`;
522 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
523...
524-----END PGP PRIVATE KEY BLOCK-----`; // encrypted private key
525 const passphrase = `yourPassphrase`; // what the private key is encrypted with
526
527 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
528
529 const privateKey = await openpgp.decryptKey({
530 privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
531 passphrase
532 });
533
534 const unsignedMessage = await openpgp.createCleartextMessage({ text: 'Hello, World!' });
535 const cleartextMessage = await openpgp.sign({
536 message: unsignedMessage, // CleartextMessage or Message object
537 signingKeys: privateKey
538 });
539 console.log(cleartextMessage); // '-----BEGIN PGP SIGNED MESSAGE ... END PGP SIGNATURE-----'
540
541 const signedMessage = await openpgp.readCleartextMessage({
542 cleartextMessage // parse armored message
543 });
544 const verificationResult = await openpgp.verify({
545 message: signedMessage,
546 verificationKeys: publicKey
547 });
548 const { verified, keyID } = verificationResult.signatures[0];
549 try {
550 await verified; // throws on invalid signature
551 console.log('Signed by key id ' + keyID.toHex());
552 } catch (e) {
553 throw new Error('Signature could not be verified: ' + e.message);
554 }
555})();
556```
557
558#### Create and verify *detached* signatures
559
560```js
561(async () => {
562 const publicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
563...
564-----END PGP PUBLIC KEY BLOCK-----`;
565 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
566...
567-----END PGP PRIVATE KEY BLOCK-----`; // encrypted private key
568 const passphrase = `yourPassphrase`; // what the private key is encrypted with
569
570 const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });
571
572 const privateKey = await openpgp.decryptKey({
573 privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
574 passphrase
575 });
576
577 const message = await openpgp.createMessage({ text: 'Hello, World!' });
578 const detachedSignature = await openpgp.sign({
579 message, // Message object
580 signingKeys: privateKey,
581 detached: true
582 });
583 console.log(detachedSignature);
584
585 const signature = await openpgp.readSignature({
586 armoredSignature: detachedSignature // parse detached signature
587 });
588 const verificationResult = await openpgp.verify({
589 message, // Message object
590 signature,
591 verificationKeys: publicKey
592 });
593 const { verified, keyID } = verificationResult.signatures[0];
594 try {
595 await verified; // throws on invalid signature
596 console.log('Signed by key id ' + keyID.toHex());
597 } catch (e) {
598 throw new Error('Signature could not be verified: ' + e.message);
599 }
600})();
601```
602
603#### Streaming sign and verify *Uint8Array* data
604
605```js
606(async () => {
607 var readableStream = new ReadableStream({
608 start(controller) {
609 controller.enqueue(new Uint8Array([0x01, 0x02, 0x03]));
610 controller.close();
611 }
612 });
613
614 const publicKeyArmored = `-----BEGIN PGP PUBLIC KEY BLOCK-----
615...
616-----END PGP PUBLIC KEY BLOCK-----`;
617 const privateKeyArmored = `-----BEGIN PGP PRIVATE KEY BLOCK-----
618...
619-----END PGP PRIVATE KEY BLOCK-----`; // encrypted private key
620 const passphrase = `yourPassphrase`; // what the private key is encrypted with
621
622 const privateKey = await openpgp.decryptKey({
623 privateKey: await openpgp.readPrivateKey({ armoredKey: privateKeyArmored }),
624 passphrase
625 });
626
627 const message = await openpgp.createMessage({ binary: readableStream }); // or createMessage({ text: ReadableStream<String> })
628 const signatureArmored = await openpgp.sign({
629 message,
630 signingKeys: privateKey
631 });
632 console.log(signatureArmored); // ReadableStream containing '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'
633
634 const verificationResult = await openpgp.verify({
635 message: await openpgp.readMessage({ armoredMessage: signatureArmored }), // parse armored signature
636 verificationKeys: await openpgp.readKey({ armoredKey: publicKeyArmored })
637 });
638
639 for await (const chunk of verificationResult.data) {}
640 // Note: you *have* to read `verificationResult.data` in some way or other,
641 // even if you don't need it, as that is what triggers the
642 // verification of the data.
643
644 try {
645 await verificationResult.signatures[0].verified; // throws on invalid signature
646 console.log('Signed by key id ' + verificationResult.signatures[0].keyID.toHex());
647 } catch (e) {
648 throw new Error('Signature could not be verified: ' + e.message);
649 }
650})();
651```
652
653### Documentation
654
655The full documentation is available at [openpgpjs.org](https://docs.openpgpjs.org/).
656
657### Security Audit
658
659To date the OpenPGP.js code base has undergone two complete security audits from [Cure53](https://cure53.de). The first audit's report has been published [here](https://github.com/openpgpjs/openpgpjs/wiki/Cure53-security-audit).
660
661### Security recommendations
662
663It should be noted that js crypto apps deployed via regular web hosting (a.k.a. [**host-based security**](https://www.schneier.com/blog/archives/2012/08/cryptocat.html)) provide users with less security than installable apps with auditable static versions. Installable apps can be deployed as a [Firefox](https://developer.mozilla.org/en-US/Marketplace/Options/Packaged_apps) or [Chrome](https://developer.chrome.com/apps/about_apps.html) packaged app. These apps are basically signed zip files and their runtimes typically enforce a strict [Content Security Policy (CSP)](https://www.html5rocks.com/en/tutorials/security/content-security-policy/) to protect users against [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting). This [blogpost](https://tankredhase.com/2014/04/13/heartbleed-and-javascript-crypto/) explains the trust model of the web quite well.
664
665It is also recommended to set a strong passphrase that protects the user's private key on disk.
666
667### Development
668
669To create your own build of the library, just run the following command after cloning the git repo. This will download all dependencies, run the tests and create a minified bundle under `dist/openpgp.min.js` to use in your project:
670
671 npm install && npm test
672
673For debugging browser errors, you can run `npm start` and open [`http://localhost:8080/test/unittests.html`](http://localhost:8080/test/unittests.html) in a browser, or run the following command:
674
675 npm run browsertest
676
677### How do I get involved?
678
679You want to help, great! It's probably best to send us a message on [Gitter](https://gitter.im/openpgpjs/openpgpjs) before you start your undertaking, to make sure nobody else is working on it, and so we can discuss the best course of action. Other than that, just go ahead and fork our repo, make your changes and send us a pull request! :)
680
681### License
682
683[GNU Lesser General Public License](https://www.gnu.org/licenses/lgpl-3.0.en.html) (3.0 or any later version). Please take a look at the [LICENSE](LICENSE) file for more information.