From 6c954e19c09e365f8a6741f27c7a5b86cd2af14d Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 10 Feb 2016 20:32:28 +0700 Subject: [PATCH] Update README.md with v2.x api examples Minor cleanup in public api, fix typos in docs --- README.md | 180 ++++++++++++++++++++++++++++--------------------- src/openpgp.js | 10 +-- 2 files changed, 108 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index dbdb9f4f..b341ad6f 100644 --- a/README.md +++ b/README.md @@ -5,110 +5,136 @@ OpenPGP.js [![Build Status](https://travis-ci.org/openpgpjs/openpgpjs.svg?branch [![Saucelabs Test Status](https://saucelabs.com/browser-matrix/openpgpjs.svg)](https://saucelabs.com/u/openpgpjs) -### Node support -For server side use, install via npm: +### Platform support + +* OpenPGP.js supports node.js v0.12+ and browsers that implement [window.crypto.getRandomValues](http://caniuse.com/#feat=getrandomvalues). + +* The api uses ES6 promises which are available in [most modern browsers](http://caniuse.com/#feat=promises). If you need to support browsers that do not support Promises, fear not! There is a [polyfill](https://github.com/jakearchibald/es6-promise), which is included in our build. So no action required on your part! + +* For the OpenPGP HTTP Key Server (HKP) client the new [fetch api](http://caniuse.com/#feat=fetch) is used. There is a polyfill for both [browsers](https://github.com/github/fetch) and [node.js](https://github.com/bitinn/node-fetch) runtimes. These are not bundled in the library however and you must add these yourself. See the unit tests for examples of how to integrate them. + + +### Performance + +* 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. + +* If the user's browser supports [native WebCrypto](http://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. This can be deactivated by setting `openpgp.config.useNative = false`. + +* For environments that don't provide native crypto, the library falls back to [asm.js](http://caniuse.com/#feat=asmjs) implementations of AES-CFB, SHA-1, and SHA-256. We use [Rusha](https://github.com/srijs/rusha) and [asmCrypto Lite](https://www.npmjs.com/package/asmcrypto-lite) (a minimal subset of asmCrypto.js built specifically for OpenPGP.js). + + +### Getting started + +#### Npm npm install --save openpgp - -### Browser support - -For use in browser, install via bower: +#### Bower bower install --save openpgp -Or Fetch a minified build under [dist](https://github.com/openpgpjs/openpgpjs/tree/master/dist). - -The library can be loaded as a CommonJS module, an AMD module or accessed globally via `window.openpgp`. - - -### Dependencies - -OpenPGP.js only supports browsers that implement `window.crypto.getRandomValues`. Also, if the browsers support [native WebCrypto](http://www.w3.org/TR/WebCryptoAPI/) via the `window.crypto.subtle` api, this will be used. Though this can be deactivated by setting `config.useWebCrypto = false`. In this case the library will fall back to Web Worker operations if the `initWorker(workerPath)` is set. - -OpenPGP.js uses ES6 promises which are available in [most modern browsers](http://caniuse.com/#feat=promises). If you need to support browsers that do not support Promises, fear not! There is a [polyfill](https://github.com/jakearchibald/es6-promise), which is included in the build step. So no action required on the developer's part for promises! - -For the OpenPGP HTTP Key Server (HKP) client the new [fetch api](https://fetch.spec.whatwg.org) is used. There is a polyfill for both [browsers](https://github.com/github/fetch) and [node.js](https://github.com/bitinn/node-fetch) runtimes. These are not bundled in the library however and users must add these themselves. See the unit tests for examples of how to integrate them. +Or just fetch a minified build under [dist](https://github.com/openpgpjs/openpgpjs/tree/master/dist). ### Examples -#### Generate new keypair -```js -var openpgp = require('openpgp'); +Here are some examples of how to use the v2.x api. If you're upgrading from v1.x it might help to check out the [documentation](https://github.com/openpgpjs/openpgpjs#documentation). -var options = { - numBits: 2048, - userId: 'Jon Smith ', - passphrase: 'super long and hard to guess secret' +#### Set up +```js +import openpgp from 'openpgp.js'; // use as ES6, CommonJS, AMD module or via window.openpgp + +openpgp.initWorker({ path:'openpgp.worker.js' }) // the relative Web Worker path +``` + +#### Encrypt with password +```js +let options = { + data: 'Hello, World!', // input as String + passwords: ['secret stuff'] // multiple passwords possible }; -openpgp.generateKeyPair(options).then(function(keypair) { - // success - var privkey = keypair.privateKeyArmored; - var pubkey = keypair.publicKeyArmored; -}).catch(function(error) { - // failure +openpgp.encrypt(options).then(ciphertext => { ... }); +``` + +#### Decrypt with password +```js +const ciphertext = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'; + +let options = { + message: openpgp.message.readArmored(ciphertext), // the encrypted message + password: 'secret stuff', // single password + format : 'utf8' // output as String +}; + +openpgp.decrypt(options).then(plaintext => { ... }) +``` + +#### Encrypt with PGP keys +```js +const pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; +const privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; + +let options = { + data: new Uint8Array([0x01, 0x01, 0x01]), // input as Uint8Array + publicKeys: openpgp.key.readArmored(pubkey).keys, // for encryption + privateKeys: openpgp.key.readArmored(privkey).keys // for signing (optional) +}; + +openpgp.encrypt(options).then(ciphertext => { ... }); +``` + +#### Decrypt with PGP keys +```js +const ciphertext = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'; +const pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; +const privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; + +let options = { + message: openpgp.message.readArmored(ciphertext), // the encrypted message + publicKeys: openpgp.key.readArmored(pubkey).keys, // for verification (optional) + privateKey: openpgp.key.readArmored(privkey).keys[0], // for decryption + format: 'binary' // output as Uint8Array +}; + +openpgp.decrypt(options).then(plaintext => { ... }); +``` + +#### Generate new key pair +```js +let options = { + userIds: [{ name:'Jon Smith', email:'jon@example.com' }], // multiple user IDs + numBits: 4096, // RSA key size + passphrase: 'super long and hard to guess secret' // protects the private key +}; + +openpgp.generateKey(options).then(key => { + let privkey = key.privateKeyArmored; + let pubkey = key.publicKeyArmored; }); ``` #### Lookup public key on HKP server ```js -var openpgp = require('openpgp'); -var hkp = new openpgp.HKP('https://pgp.mit.edu'); +let hkp = new openpgp.HKP('https://pgp.mit.edu'); -hkp.lookup({ +let options = { query: 'alice@example.com' -}).then(function(key) { - var publicKey = openpgp.key.readArmored(key); +}; + +hkp.lookup(options).then(key => { + let pubkey = openpgp.key.readArmored(key); }); ``` #### Upload public key to HKP server ```js -var openpgp = require('openpgp'); -var hkp = new openpgp.HKP('https://pgp.mit.edu'); +let hkp = new openpgp.HKP('https://pgp.mit.edu'); -var key = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; +const pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; -hkp.upload(key).then(function() { - // success -}).catch(function(error) { - // failure -}); -``` - -#### Encryption -```js -var openpgp = require('openpgp'); - -var key = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; -var publicKey = openpgp.key.readArmored(key); - -openpgp.encryptMessage(publicKey.keys, 'Hello, World!').then(function(pgpMessage) { - // success -}).catch(function(error) { - // failure -}); -``` - -#### Decryption -```js -var openpgp = require('openpgp'); - -var key = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; -var privateKey = openpgp.key.readArmored(key).keys[0]; -privateKey.decrypt('passphrase'); - -var pgpMessage = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'; -pgpMessage = openpgp.message.readArmored(pgpMessage); - -openpgp.decryptMessage(privateKey, pgpMessage).then(function(plaintext) { - // success -}).catch(function(error) { - // failure -}); +hkp.upload(pubkey).then(() => { ... }); ``` ### Documentation @@ -121,7 +147,7 @@ To date the OpenPGP.js code base has undergone two complete security audits from ### Security recommendations -It 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)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) to protect users against [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting). This [blogpost](http://tonyarcieri.com/whats-wrong-with-webcrypto) explains the trust model of the web quite well. +It 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)](http://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. It is also recommended to set a strong passphrase that protects the user's private key on disk. diff --git a/src/openpgp.js b/src/openpgp.js index 9e69ebf5..56457203 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -88,8 +88,7 @@ export function destroyWorker() { * @static */ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false } = {}) { - const options = { userIds, passphrase, numBits, unlocked }; - formatUserIds(options); + const options = formatUserIds({ userIds, passphrase, numBits, unlocked }); if (!util.getWebCrypto() && asyncProxy) { // use web worker if web crypto apis are not supported return asyncProxy.delegate('generateKey', options); @@ -155,7 +154,7 @@ export function decryptKey({ privateKey, passphrase }) { * @param {String|Array} passwords (optional) array of passwords or a single password to encrypt the message * @param {String} filename (optional) a filename for the literal data packet * @param {Boolean} armor (optional) if the return value should be ascii armored or the message object - * @return {Promise} encrypted ASCII armored message, or Message if 'armor' is true + * @return {Promise} encrypted ASCII armored message, or the full Message object if 'armor' is false * @static */ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true }) { @@ -377,7 +376,7 @@ function checkCleartextMessage(message) { */ function formatUserIds(options) { if (!options.userIds) { - return; + return options; } options.userIds = toArray(options.userIds); // normalize to array options.userIds = options.userIds.map(id => { @@ -387,7 +386,7 @@ function formatUserIds(options) { if (util.isUserId(id)) { return id; // user id is already in correct format... no conversion necessary } - // name and email address can be empty but must be of type the correct type + // name and email address can be empty but must be of the correct type id.name = id.name || ''; id.email = id.email || ''; if (!util.isString(id.name) || (id.email && !util.isEmailAddress(id.email))) { @@ -395,6 +394,7 @@ function formatUserIds(options) { } return id.name + ' <' + id.email + '>'; }); + return options; } /**