Merge pull request #259 from openpgpjs/promises

Refactor public api to use ES6 Promises
This commit is contained in:
Tankred Hase 2014-10-20 16:12:13 +02:00
commit 2362cce642
22 changed files with 1221 additions and 1004 deletions

39
.jshintrc Normal file
View File

@ -0,0 +1,39 @@
{
"indent": 2,
"strict": true,
"globalstrict": true,
"node": true,
"browser": true,
"nonew": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"newcap": true,
"regexp": true,
"evil": true,
"eqnull": true,
"expr": true,
"trailing": true,
"undef": true,
"unused": true,
"predef": [
"console",
"Promise",
"importScripts",
"process",
"Event",
"self",
"describe",
"it",
"sinon",
"mocha",
"before",
"beforeEach",
"after",
"afterEach"
],
"globals": {
}
}

View File

@ -38,7 +38,7 @@ module.exports = function(grunt) {
'test/lib/unittests-bundle.js': [ './test/unittests.js' ] 'test/lib/unittests-bundle.js': [ './test/unittests.js' ]
}, },
options: { options: {
external: [ 'openpgp', 'crypto', 'node-localstorage' ] external: [ 'openpgp', 'crypto', 'node-localstorage']
} }
} }
}, },

View File

@ -23,6 +23,13 @@ Or Fetch a minified build under [releases](https://github.com/openpgpjs/openpgpj
The library can be loaded via AMD/require.js or accessed globally via `window.openpgp`. The library can be loaded via AMD/require.js 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!
### Examples ### Examples
#### Encryption #### Encryption
@ -30,7 +37,11 @@ The library can be loaded via AMD/require.js or accessed globally via `window.o
var openpgp = require('openpgp'); var openpgp = require('openpgp');
var key = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; var key = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----';
var publicKey = openpgp.key.readArmored(key); var publicKey = openpgp.key.readArmored(key);
var pgpMessage = openpgp.encryptMessage(publicKey.keys, 'Hello, World!'); openpgp.encryptMessage(publicKey.keys, 'Hello, World!').then(function(pgpMessage) {
// success
}).catch(function(error) {
// failure
});
#### Decryption #### Decryption
@ -40,12 +51,13 @@ The library can be loaded via AMD/require.js or accessed globally via `window.o
privateKey.decrypt('passphrase'); privateKey.decrypt('passphrase');
var pgpMessage = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'; var pgpMessage = '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----';
pgpMessage = openpgp.message.readArmored(pgpMessage); pgpMessage = openpgp.message.readArmored(pgpMessage);
var plaintext = openpgp.decryptMessage(privateKey, pgpMessage); openpgp.decryptMessage(privateKey, pgpMessage).then(function(plaintext) {
// success
}).catch(function(error) {
// failure
});
OpenPGP.js currently only fully supports browsers that implement `window.crypto.getRandomValues`. If you can help us support more browsers and runtimes, please chip in!
### Security recommendations ### 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/Publishing/Packaged_apps) or [Chrome](http://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](http://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/Publishing/Packaged_apps) or [Chrome](http://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](http://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.

View File

@ -1,7 +1,7 @@
{ {
"name": "openpgp", "name": "openpgp",
"description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.", "description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.",
"version": "0.7.2", "version": "0.8.0",
"homepage": "http://openpgpjs.org/", "homepage": "http://openpgpjs.org/",
"engines": { "engines": {
"node": ">=0.8" "node": ">=0.8"
@ -46,6 +46,7 @@
"phantomjs": "~1.9.2-5" "phantomjs": "~1.9.2-5"
}, },
"dependencies": { "dependencies": {
"es6-promise": "^1.0.0",
"node-localstorage": "~0.3.4" "node-localstorage": "~0.3.4"
}, },
"repository": { "repository": {

View File

@ -23,6 +23,8 @@
* @module cleartext * @module cleartext
*/ */
'use strict';
var config = require('./config'), var config = require('./config'),
packet = require('./packet'), packet = require('./packet'),
enums = require('./enums.js'), enums = require('./enums.js'),

View File

@ -179,14 +179,13 @@ module.exports = {
}, },
generateMpi: function(algo, bits) { generateMpi: function(algo, bits) {
var result = (function() {
switch (algo) { switch (algo) {
case 'rsa_encrypt': case 'rsa_encrypt':
case 'rsa_encrypt_sign': case 'rsa_encrypt_sign':
case 'rsa_sign': case 'rsa_sign':
//remember "publicKey" refers to the crypto/public_key dir //remember "publicKey" refers to the crypto/public_key dir
var rsa = new publicKey.rsa(); var rsa = new publicKey.rsa();
var keyObject = rsa.generate(bits, "10001"); return rsa.generate(bits, "10001").then(function(keyObject) {
var output = []; var output = [];
output.push(keyObject.n); output.push(keyObject.n);
output.push(keyObject.ee); output.push(keyObject.ee);
@ -194,17 +193,19 @@ module.exports = {
output.push(keyObject.p); output.push(keyObject.p);
output.push(keyObject.q); output.push(keyObject.q);
output.push(keyObject.u); output.push(keyObject.u);
return output; return mapResult(output);
});
default: default:
throw new Error('Unsupported algorithm for key generation.'); throw new Error('Unsupported algorithm for key generation.');
} }
})();
function mapResult(result) {
return result.map(function(bn) { return result.map(function(bn) {
var mpi = new type_mpi(); var mpi = new type_mpi();
mpi.fromBigInteger(bn); mpi.fromBigInteger(bn);
return mpi; return mpi;
}); });
}
}, },

View File

@ -134,11 +134,64 @@ function RSA() {
// Generate a new random private key B bits long, using public expt E // Generate a new random private key B bits long, using public expt E
function generate(B, E) { function generate(B, E) {
var webCrypto = util.getWebCrypto();
var promise;
//
// Native RSA keygen using Web Crypto
//
if (webCrypto) {
var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent
var Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent
var keyGenOpt = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: B, // the specified keysize in bits
publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537)
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
promise = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
return promise.then(exportKey).then(decodeKey);
}
function exportKey(key) {
// export the generated keys as JsonWebKey (JWK)
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
return webCrypto.exportKey('jwk', key.privateKey);
}
function decodeKey(jwk) {
// map JWK parameters to local BigInteger type system
var key = new keyObject();
key.n = toBigInteger(jwk.n);
key.ee = new BigInteger(E, 16);
key.d = toBigInteger(jwk.d);
key.p = toBigInteger(jwk.p);
key.q = toBigInteger(jwk.q);
key.u = key.p.modInverse(key.q);
function toBigInteger(base64url) {
var base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/');
var hex = util.hexstrdump(atob(base64));
return new BigInteger(hex, 16);
}
return key;
}
//
// JS code
//
promise = new Promise(function(resolve) {
var key = new keyObject(); var key = new keyObject();
var rng = new SecureRandom(); var rng = new SecureRandom();
var qs = B >> 1; var qs = B >> 1;
key.e = parseInt(E, 16); key.e = parseInt(E, 16);
key.ee = new BigInteger(E, 16); key.ee = new BigInteger(E, 16);
for (;;) { for (;;) {
for (;;) { for (;;) {
key.p = new BigInteger(B - qs, 1, rng); key.p = new BigInteger(B - qs, 1, rng);
@ -167,7 +220,11 @@ function RSA() {
break; break;
} }
} }
return key;
resolve(key);
});
return promise;
} }
this.encrypt = encrypt; this.encrypt = encrypt;

View File

@ -1,3 +1,5 @@
'use strict';
/** /**
* @module enums * @module enums
*/ */

View File

@ -1,3 +1,4 @@
'use strict';
module.exports = require('./openpgp.js'); module.exports = require('./openpgp.js');
/** /**

View File

@ -23,6 +23,8 @@
* @module key * @module key
*/ */
'use strict';
var packet = require('./packet'), var packet = require('./packet'),
enums = require('./enums.js'), enums = require('./enums.js'),
armor = require('./encoding/armor.js'), armor = require('./encoding/armor.js'),
@ -911,6 +913,8 @@ function readArmored(armoredText) {
* @static * @static
*/ */
function generate(options) { function generate(options) {
var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket;
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
// RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { if (options.keyType !== enums.publicKey.rsa_encrypt_sign) {
@ -921,22 +925,39 @@ function generate(options) {
options.unlocked = true; options.unlocked = true;
} }
var packetlist = new packet.List(); // generate
var genSecretKey = generateSecretKey();
var genSecretSubkey = generateSecretSubkey();
return Promise.all([genSecretKey, genSecretSubkey]).then(wrapKeyObject);
var secretKeyPacket = new packet.SecretKey(); function generateSecretKey() {
secretKeyPacket = new packet.SecretKey();
secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
secretKeyPacket.generate(options.numBits); return secretKeyPacket.generate(options.numBits);
if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase);
} }
var userIdPacket = new packet.Userid(); function generateSecretSubkey() {
secretSubkeyPacket = new packet.SecretSubkey();
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
return secretSubkeyPacket.generate(options.numBits);
}
function wrapKeyObject() {
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase);
secretSubkeyPacket.encrypt(options.passphrase);
}
packetlist = new packet.List();
userIdPacket = new packet.Userid();
userIdPacket.read(options.userId); userIdPacket.read(options.userId);
var dataToSign = {}; dataToSign = {};
dataToSign.userid = userIdPacket; dataToSign.userid = userIdPacket;
dataToSign.key = secretKeyPacket; dataToSign.key = secretKeyPacket;
var signaturePacket = new packet.Signature(); signaturePacket = new packet.Signature();
signaturePacket.signatureType = enums.signature.cert_generic; signaturePacket.signatureType = enums.signature.cert_generic;
signaturePacket.publicKeyAlgorithm = options.keyType; signaturePacket.publicKeyAlgorithm = options.keyType;
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
@ -960,17 +981,10 @@ function generate(options) {
} }
signaturePacket.sign(secretKeyPacket, dataToSign); signaturePacket.sign(secretKeyPacket, dataToSign);
var secretSubkeyPacket = new packet.SecretSubkey();
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
secretSubkeyPacket.generate(options.numBits);
if (options.passphrase) {
secretSubkeyPacket.encrypt(options.passphrase);
}
dataToSign = {}; dataToSign = {};
dataToSign.key = secretKeyPacket; dataToSign.key = secretKeyPacket;
dataToSign.bind = secretSubkeyPacket; dataToSign.bind = secretSubkeyPacket;
var subkeySignaturePacket = new packet.Signature(); subkeySignaturePacket = new packet.Signature();
subkeySignaturePacket.signatureType = enums.signature.subkey_binding; subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
subkeySignaturePacket.publicKeyAlgorithm = options.keyType; subkeySignaturePacket.publicKeyAlgorithm = options.keyType;
subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm; subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
@ -989,6 +1003,7 @@ function generate(options) {
} }
return new Key(packetlist); return new Key(packetlist);
}
} }
/** /**

View File

@ -24,6 +24,8 @@
* @module message * @module message
*/ */
'use strict';
var packet = require('./packet'), var packet = require('./packet'),
enums = require('./enums.js'), enums = require('./enums.js'),
armor = require('./encoding/armor.js'), armor = require('./encoding/armor.js'),

View File

@ -31,15 +31,21 @@
* @module openpgp * @module openpgp
*/ */
'use strict';
var armor = require('./encoding/armor.js'), var armor = require('./encoding/armor.js'),
packet = require('./packet'),
enums = require('./enums.js'), enums = require('./enums.js'),
config = require('./config'),
message = require('./message.js'), message = require('./message.js'),
cleartext = require('./cleartext.js'), cleartext = require('./cleartext.js'),
key = require('./key.js'), key = require('./key.js'),
util = require('./util'),
AsyncProxy = require('./worker/async_proxy.js'); AsyncProxy = require('./worker/async_proxy.js');
if (typeof Promise === 'undefined') {
// load ES6 Promises polyfill
require('es6-promise').polyfill();
}
var asyncProxy; // instance of the asyncproxy var asyncProxy; // instance of the asyncproxy
/** /**
@ -54,18 +60,16 @@ function initWorker(path) {
* Encrypts message text with keys * Encrypts message text with keys
* @param {(Array<module:key~Key>|module:key~Key)} keys array of keys or single key, used to encrypt the message * @param {(Array<module:key~Key>|module:key~Key)} keys array of keys or single key, used to encrypt the message
* @param {String} text message as native JavaScript string * @param {String} text message as native JavaScript string
* @param {function} callback (optional) callback(error, result) for async style
* @return {String} encrypted ASCII armored message * @return {String} encrypted ASCII armored message
* @static * @static
*/ */
function encryptMessage(keys, text, callback) { function encryptMessage(keys, text) {
if (!keys.length) { if (!keys.length) {
keys = [keys]; keys = [keys];
} }
if (useWorker(callback)) { if (useWorker()) {
asyncProxy.encryptMessage(keys, text, callback); return asyncProxy.encryptMessage(keys, text);
return;
} }
return execute(function() { return execute(function() {
@ -74,7 +78,8 @@ function encryptMessage(keys, text, callback) {
msg = msg.encrypt(keys); msg = msg.encrypt(keys);
armored = armor.encode(enums.armor.message, msg.packets.write()); armored = armor.encode(enums.armor.message, msg.packets.write());
return armored; return armored;
}, callback);
}, 'Error encrypting message!');
} }
/** /**
@ -82,18 +87,16 @@ function encryptMessage(keys, text, callback) {
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, used to encrypt the message * @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, used to encrypt the message
* @param {module:key~Key} privateKey private key with decrypted secret key data for signing * @param {module:key~Key} privateKey private key with decrypted secret key data for signing
* @param {String} text message as native JavaScript string * @param {String} text message as native JavaScript string
* @param {function} callback (optional) callback(error, result) for async style
* @return {String} encrypted ASCII armored message * @return {String} encrypted ASCII armored message
* @static * @static
*/ */
function signAndEncryptMessage(publicKeys, privateKey, text, callback) { function signAndEncryptMessage(publicKeys, privateKey, text) {
if (!publicKeys.length) { if (!publicKeys.length) {
publicKeys = [publicKeys]; publicKeys = [publicKeys];
} }
if (useWorker(callback)) { if (useWorker()) {
asyncProxy.signAndEncryptMessage(publicKeys, privateKey, text, callback); return asyncProxy.signAndEncryptMessage(publicKeys, privateKey, text);
return;
} }
return execute(function() { return execute(function() {
@ -103,28 +106,28 @@ function signAndEncryptMessage(publicKeys, privateKey, text, callback) {
msg = msg.encrypt(publicKeys); msg = msg.encrypt(publicKeys);
armored = armor.encode(enums.armor.message, msg.packets.write()); armored = armor.encode(enums.armor.message, msg.packets.write());
return armored; return armored;
}, callback);
}, 'Error signing and encrypting message!');
} }
/** /**
* Decrypts message * Decrypts message
* @param {module:key~Key} privateKey private key with decrypted secret key data * @param {module:key~Key} privateKey private key with decrypted secret key data
* @param {module:message~Message} msg the message object with the encrypted data * @param {module:message~Message} msg the message object with the encrypted data
* @param {function} callback (optional) callback(error, result) for async style
* @return {(String|null)} decrypted message as as native JavaScript string * @return {(String|null)} decrypted message as as native JavaScript string
* or null if no literal data found * or null if no literal data found
* @static * @static
*/ */
function decryptMessage(privateKey, msg, callback) { function decryptMessage(privateKey, msg) {
if (useWorker(callback)) { if (useWorker()) {
asyncProxy.decryptMessage(privateKey, msg, callback); return asyncProxy.decryptMessage(privateKey, msg);
return;
} }
return execute(function() { return execute(function() {
msg = msg.decrypt(privateKey); msg = msg.decrypt(privateKey);
return msg.getText(); return msg.getText();
}, callback);
}, 'Error decrypting message!');
} }
/** /**
@ -132,20 +135,18 @@ function decryptMessage(privateKey, msg, callback) {
* @param {module:key~Key} privateKey private key with decrypted secret key data * @param {module:key~Key} privateKey private key with decrypted secret key data
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures * @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures
* @param {module:message~Message} msg the message object with signed and encrypted data * @param {module:message~Message} msg the message object with signed and encrypted data
* @param {function} callback (optional) callback(error, result) for async style
* @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}} * @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}}
* decrypted message as as native JavaScript string * decrypted message as as native JavaScript string
* with verified signatures or null if no literal data found * with verified signatures or null if no literal data found
* @static * @static
*/ */
function decryptAndVerifyMessage(privateKey, publicKeys, msg, callback) { function decryptAndVerifyMessage(privateKey, publicKeys, msg) {
if (!publicKeys.length) { if (!publicKeys.length) {
publicKeys = [publicKeys]; publicKeys = [publicKeys];
} }
if (useWorker(callback)) { if (useWorker()) {
asyncProxy.decryptAndVerifyMessage(privateKey, publicKeys, msg, callback); return asyncProxy.decryptAndVerifyMessage(privateKey, publicKeys, msg);
return;
} }
return execute(function() { return execute(function() {
@ -157,51 +158,49 @@ function decryptAndVerifyMessage(privateKey, publicKeys, msg, callback) {
return result; return result;
} }
return null; return null;
}, callback);
}, 'Error decrypting and verifying message!');
} }
/** /**
* Signs a cleartext message * Signs a cleartext message
* @param {(Array<module:key~Key>|module:key~Key)} privateKeys array of keys or single key with decrypted secret key data to sign cleartext * @param {(Array<module:key~Key>|module:key~Key)} privateKeys array of keys or single key with decrypted secret key data to sign cleartext
* @param {String} text cleartext * @param {String} text cleartext
* @param {function} callback (optional) callback(error, result) for async style
* @return {String} ASCII armored message * @return {String} ASCII armored message
* @static * @static
*/ */
function signClearMessage(privateKeys, text, callback) { function signClearMessage(privateKeys, text) {
if (!privateKeys.length) { if (!privateKeys.length) {
privateKeys = [privateKeys]; privateKeys = [privateKeys];
} }
if (useWorker(callback)) { if (useWorker()) {
asyncProxy.signClearMessage(privateKeys, text, callback); return asyncProxy.signClearMessage(privateKeys, text);
return;
} }
return execute(function() { return execute(function() {
var cleartextMessage = new cleartext.CleartextMessage(text); var cleartextMessage = new cleartext.CleartextMessage(text);
cleartextMessage.sign(privateKeys); cleartextMessage.sign(privateKeys);
return cleartextMessage.armor(); return cleartextMessage.armor();
}, callback);
}, 'Error signing cleartext message!');
} }
/** /**
* Verifies signatures of cleartext signed message * Verifies signatures of cleartext signed message
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures * @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures
* @param {module:cleartext~CleartextMessage} msg cleartext message object with signatures * @param {module:cleartext~CleartextMessage} msg cleartext message object with signatures
* @param {function} callback (optional) callback(error, result) for async style
* @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}} * @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}}
* cleartext with status of verified signatures * cleartext with status of verified signatures
* @static * @static
*/ */
function verifyClearSignedMessage(publicKeys, msg, callback) { function verifyClearSignedMessage(publicKeys, msg) {
if (!publicKeys.length) { if (!publicKeys.length) {
publicKeys = [publicKeys]; publicKeys = [publicKeys];
} }
if (useWorker(callback)) { if (useWorker()) {
asyncProxy.verifyClearSignedMessage(publicKeys, msg, callback); return asyncProxy.verifyClearSignedMessage(publicKeys, msg);
return;
} }
return execute(function() { return execute(function() {
@ -212,7 +211,8 @@ function verifyClearSignedMessage(publicKeys, msg, callback) {
result.text = msg.getText(); result.text = msg.getText();
result.signatures = msg.verify(publicKeys); result.signatures = msg.verify(publicKeys);
return result; return result;
}, callback);
}, 'Error verifying cleartext signed message!');
} }
/** /**
@ -224,24 +224,23 @@ function verifyClearSignedMessage(publicKeys, msg, callback) {
* @param {String} options.userId assumes already in form of "User Name <username@email.com>" * @param {String} options.userId assumes already in form of "User Name <username@email.com>"
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key * @param {String} options.passphrase The passphrase used to encrypt the resulting private key
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked * @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
* @param {function} callback (optional) callback(error, result) for async style
* @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String} * @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String}
* @static * @static
*/ */
function generateKeyPair(options, callback) { function generateKeyPair(options) {
if (useWorker(callback)) { // use web worker if web crypto apis are not supported
asyncProxy.generateKeyPair(options, callback); if (!util.getWebCrypto() && useWorker()) {
return; return asyncProxy.generateKeyPair(options);
} }
return execute(function() { return key.generate(options).then(function(newKey) {
var result = {}; var result = {};
var newKey = key.generate(options);
result.key = newKey; result.key = newKey;
result.privateKeyArmored = newKey.armor(); result.privateKeyArmored = newKey.armor();
result.publicKeyArmored = newKey.toPublic().armor(); result.publicKeyArmored = newKey.toPublic().armor();
return result; return result;
}, callback);
}, 'Error generating keypair!');
} }
// //
@ -251,41 +250,50 @@ function generateKeyPair(options, callback) {
/** /**
* Are we in a browser and do we support worker? * Are we in a browser and do we support worker?
*/ */
function useWorker(callback) { function useWorker() {
if (typeof window === 'undefined' || !window.Worker || typeof callback !== 'function') { if (typeof window === 'undefined' || !window.Worker) {
return false; return false;
} }
if (!asyncProxy) { if (!asyncProxy) {
throw new Error('You need to set the worker path!'); console.log('You need to set the worker path!');
return false;
} }
return true; return true;
} }
/** /**
* Command pattern that handles async calls gracefully * Command pattern that wraps synchronous code into a promise
* @param {function} cmd The synchronous function with a return value
* to be wrapped in a promise
* @param {String} errMsg A human readable error Message
* @return {Promise} The promise wrapped around cmd
*/ */
function execute(cmd, callback) { function execute(cmd, errMsg) {
var result; // wrap the sync cmd in a promise
var promise = new Promise(function(resolve) {
var result = cmd();
resolve(result);
});
try { // handler error globally
result = cmd(); promise.catch(onError.bind(null, errMsg));
} catch (err) {
if (callback) {
callback(err);
return;
}
throw err; return promise;
} }
if (callback) { /**
callback(null, result); * Global error handler that logs the stack trace and
return; * rethrows a high lvl error message
} * @param {String} message A human readable high level error Message
* @param {Error} error The internal error that caused the failure
return result; */
function onError(message, error) {
// log the stack trace
console.error(error.stack);
// rethrow new high level error for api users
throw new Error(message);
} }
exports.initWorker = initWorker; exports.initWorker = initWorker;

View File

@ -271,8 +271,12 @@ SecretKey.prototype.decrypt = function (passphrase) {
}; };
SecretKey.prototype.generate = function (bits) { SecretKey.prototype.generate = function (bits) {
this.mpi = crypto.generateMpi(this.algorithm, bits); var self = this;
this.isDecrypted = true;
return crypto.generateMpi(self.algorithm, bits).then(function(mpi) {
self.mpi = mpi;
self.isDecrypted = true;
});
}; };
/** /**

View File

@ -21,6 +21,8 @@
* @module util * @module util
*/ */
'use strict';
var config = require('./config'); var config = require('./config');
module.exports = { module.exports = {
@ -305,5 +307,21 @@ module.exports = {
return "SHA224"; return "SHA224";
} }
return "unknown"; return "unknown";
},
/**
* Get native Web Cryptography api. The default configuration is to use
* the api when available. But it can also be deactivated with config.useWebCrypto
* @return {Object} The SubtleCrypto api or 'undefined'
*/
getWebCrypto: function() {
if (config.useWebCrypto === false) {
// make web crypto optional
return;
}
if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) {
return window.crypto.subtle;
}
} }
}; };

View File

@ -24,11 +24,12 @@
* @module async_proxy * @module async_proxy
*/ */
'use strict';
var crypto = require('../crypto'), var crypto = require('../crypto'),
packet = require('../packet'), packet = require('../packet'),
key = require('../key.js'), key = require('../key.js'),
type_keyid = require('../type/keyid.js'), type_keyid = require('../type/keyid.js');
enums = require('../enums.js');
var INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker var INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker
RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request
@ -49,6 +50,24 @@ function AsyncProxy(path) {
this.tasks = []; this.tasks = [];
} }
/**
* Command pattern that wraps synchronous code into a promise
* @param {Object} self The current this
* @param {function} cmd The synchronous function with a return value
* to be wrapped in a promise
* @return {Promise} The promise wrapped around cmd
*/
AsyncProxy.prototype.execute = function(cmd) {
var self = this;
var promise = new Promise(function(resolve, reject) {
cmd();
self.tasks.push({ resolve:resolve, reject:reject });
});
return promise;
};
/** /**
* Message handling * Message handling
*/ */
@ -56,7 +75,13 @@ AsyncProxy.prototype.onMessage = function(event) {
var msg = event.data; var msg = event.data;
switch (msg.event) { switch (msg.event) {
case 'method-return': case 'method-return':
this.tasks.shift()(msg.err ? new Error(msg.err) : null, msg.data); if (msg.err) {
// fail
this.tasks.shift().reject(new Error(msg.err));
} else {
// success
this.tasks.shift().resolve(msg.data);
}
break; break;
case 'request-seed': case 'request-seed':
this.seedRandom(RANDOM_SEED_REQUEST); this.seedRandom(RANDOM_SEED_REQUEST);
@ -98,21 +123,23 @@ AsyncProxy.prototype.terminate = function() {
* Encrypts message text with keys * Encrypts message text with keys
* @param {(Array<module:key~Key>|module:key~Key)} keys array of keys or single key, used to encrypt the message * @param {(Array<module:key~Key>|module:key~Key)} keys array of keys or single key, used to encrypt the message
* @param {String} text message as native JavaScript string * @param {String} text message as native JavaScript string
* @param {Function} callback receives encrypted ASCII armored message
*/ */
AsyncProxy.prototype.encryptMessage = function(keys, text, callback) { AsyncProxy.prototype.encryptMessage = function(keys, text) {
var self = this;
return self.execute(function() {
if (!keys.length) { if (!keys.length) {
keys = [keys]; keys = [keys];
} }
keys = keys.map(function(key) { keys = keys.map(function(key) {
return key.toPacketlist(); return key.toPacketlist();
}); });
this.worker.postMessage({ self.worker.postMessage({
event: 'encrypt-message', event: 'encrypt-message',
keys: keys, keys: keys,
text: text text: text
}); });
this.tasks.push(callback); });
}; };
/** /**
@ -120,9 +147,11 @@ AsyncProxy.prototype.encryptMessage = function(keys, text, callback) {
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, used to encrypt the message * @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, used to encrypt the message
* @param {module:key~Key} privateKey private key with decrypted secret key data for signing * @param {module:key~Key} privateKey private key with decrypted secret key data for signing
* @param {String} text message as native JavaScript string * @param {String} text message as native JavaScript string
* @param {Function} callback receives encrypted ASCII armored message
*/ */
AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text, callback) { AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text) {
var self = this;
return self.execute(function() {
if (!publicKeys.length) { if (!publicKeys.length) {
publicKeys = [publicKeys]; publicKeys = [publicKeys];
} }
@ -130,30 +159,31 @@ AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, te
return key.toPacketlist(); return key.toPacketlist();
}); });
privateKey = privateKey.toPacketlist(); privateKey = privateKey.toPacketlist();
this.worker.postMessage({ self.worker.postMessage({
event: 'sign-and-encrypt-message', event: 'sign-and-encrypt-message',
publicKeys: publicKeys, publicKeys: publicKeys,
privateKey: privateKey, privateKey: privateKey,
text: text text: text
}); });
this.tasks.push(callback); });
}; };
/** /**
* Decrypts message * Decrypts message
* @param {module:key~Key} privateKey private key with decrypted secret key data * @param {module:key~Key} privateKey private key with decrypted secret key data
* @param {module:message~Message} message the message object with the encrypted data * @param {module:message~Message} message the message object with the encrypted data
* @param {Function} callback receives decrypted message as as native JavaScript string
* or null if no literal data found
*/ */
AsyncProxy.prototype.decryptMessage = function(privateKey, message, callback) { AsyncProxy.prototype.decryptMessage = function(privateKey, message) {
var self = this;
return self.execute(function() {
privateKey = privateKey.toPacketlist(); privateKey = privateKey.toPacketlist();
this.worker.postMessage({ self.worker.postMessage({
event: 'decrypt-message', event: 'decrypt-message',
privateKey: privateKey, privateKey: privateKey,
message: message message: message
}); });
this.tasks.push(callback); });
}; };
/** /**
@ -161,10 +191,11 @@ AsyncProxy.prototype.decryptMessage = function(privateKey, message, callback) {
* @param {module:key~Key} privateKey private key with decrypted secret key data * @param {module:key~Key} privateKey private key with decrypted secret key data
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key to verify signatures * @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key to verify signatures
* @param {module:message~Message} message the message object with signed and encrypted data * @param {module:message~Message} message the message object with signed and encrypted data
* @param {Function} callback receives decrypted message as as native JavaScript string
* with verified signatures or null if no literal data found
*/ */
AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys, message, callback) { AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys, message) {
var self = this;
var promise = new Promise(function(resolve, reject) {
privateKey = privateKey.toPacketlist(); privateKey = privateKey.toPacketlist();
if (!publicKeys.length) { if (!publicKeys.length) {
publicKeys = [publicKeys]; publicKeys = [publicKeys];
@ -172,71 +203,79 @@ AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys,
publicKeys = publicKeys.map(function(key) { publicKeys = publicKeys.map(function(key) {
return key.toPacketlist(); return key.toPacketlist();
}); });
this.worker.postMessage({ self.worker.postMessage({
event: 'decrypt-and-verify-message', event: 'decrypt-and-verify-message',
privateKey: privateKey, privateKey: privateKey,
publicKeys: publicKeys, publicKeys: publicKeys,
message: message message: message
}); });
this.tasks.push(function(err, data) {
if (data) { self.tasks.push({ resolve:function(data) {
data.signatures = data.signatures.map(function(sig) { data.signatures = data.signatures.map(function(sig) {
sig.keyid = type_keyid.fromClone(sig.keyid); sig.keyid = type_keyid.fromClone(sig.keyid);
return sig; return sig;
}); });
} resolve(data);
callback(err, data); }, reject:reject });
}); });
return promise;
}; };
/** /**
* Signs a cleartext message * Signs a cleartext message
* @param {(Array<module:key~Key>|module:key~Key)} privateKeys array of keys or single key, with decrypted secret key data to sign cleartext * @param {(Array<module:key~Key>|module:key~Key)} privateKeys array of keys or single key, with decrypted secret key data to sign cleartext
* @param {String} text cleartext * @param {String} text cleartext
* @param {Function} callback receives ASCII armored message
*/ */
AsyncProxy.prototype.signClearMessage = function(privateKeys, text, callback) { AsyncProxy.prototype.signClearMessage = function(privateKeys, text) {
var self = this;
return self.execute(function() {
if (!privateKeys.length) { if (!privateKeys.length) {
privateKeys = [privateKeys]; privateKeys = [privateKeys];
} }
privateKeys = privateKeys.map(function(key) { privateKeys = privateKeys.map(function(key) {
return key.toPacketlist(); return key.toPacketlist();
}); });
this.worker.postMessage({ self.worker.postMessage({
event: 'sign-clear-message', event: 'sign-clear-message',
privateKeys: privateKeys, privateKeys: privateKeys,
text: text text: text
}); });
this.tasks.push(callback); });
}; };
/** /**
* Verifies signatures of cleartext signed message * Verifies signatures of cleartext signed message
* @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures * @param {(Array<module:key~Key>|module:key~Key)} publicKeys array of keys or single key, to verify signatures
* @param {module:cleartext~CleartextMessage} message cleartext message object with signatures * @param {module:cleartext~CleartextMessage} message cleartext message object with signatures
* @param {Function} callback receives cleartext with status of verified signatures
*/ */
AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message, callback) { AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message) {
var self = this;
var promise = new Promise(function(resolve, reject) {
if (!publicKeys.length) { if (!publicKeys.length) {
publicKeys = [publicKeys]; publicKeys = [publicKeys];
} }
publicKeys = publicKeys.map(function(key) { publicKeys = publicKeys.map(function(key) {
return key.toPacketlist(); return key.toPacketlist();
}); });
this.worker.postMessage({ self.worker.postMessage({
event: 'verify-clear-signed-message', event: 'verify-clear-signed-message',
publicKeys: publicKeys, publicKeys: publicKeys,
message: message message: message
}); });
this.tasks.push(function(err, data) {
if (data) { self.tasks.push({ resolve:function(data) {
data.signatures = data.signatures.map(function(sig) { data.signatures = data.signatures.map(function(sig) {
sig.keyid = type_keyid.fromClone(sig.keyid); sig.keyid = type_keyid.fromClone(sig.keyid);
return sig; return sig;
}); });
} resolve(data);
callback(err, data); }, reject:reject });
}); });
return promise;
}; };
/** /**
@ -247,42 +286,50 @@ AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message, ca
* @param {Integer} numBits number of bits for the key creation. (should be 1024+, generally) * @param {Integer} numBits number of bits for the key creation. (should be 1024+, generally)
* @param {String} userId assumes already in form of "User Name <username@email.com>" * @param {String} userId assumes already in form of "User Name <username@email.com>"
* @param {String} passphrase The passphrase used to encrypt the resulting private key * @param {String} passphrase The passphrase used to encrypt the resulting private key
* @param {Function} callback receives object with key and public and private armored texts
*/ */
AsyncProxy.prototype.generateKeyPair = function(options, callback) { AsyncProxy.prototype.generateKeyPair = function(options) {
this.worker.postMessage({ var self = this;
var promise = new Promise(function(resolve, reject) {
self.worker.postMessage({
event: 'generate-key-pair', event: 'generate-key-pair',
options: options options: options
}); });
this.tasks.push(function(err, data) {
if (data) { self.tasks.push({ resolve:function(data) {
var packetlist = packet.List.fromStructuredClone(data.key); var packetlist = packet.List.fromStructuredClone(data.key);
data.key = new key.Key(packetlist); data.key = new key.Key(packetlist);
} resolve(data);
callback(err, data); }, reject:reject });
}); });
return promise;
}; };
/** /**
* Decrypts secret part of all secret key packets of key. * Decrypts secret part of all secret key packets of key.
* @param {module:key~Key} privateKey private key with encrypted secret key data * @param {module:key~Key} privateKey private key with encrypted secret key data
* @param {String} password password to unlock the key * @param {String} password password to unlock the key
* @param {Function} callback receives decrypted key
*/ */
AsyncProxy.prototype.decryptKey = function(privateKey, password, callback) { AsyncProxy.prototype.decryptKey = function(privateKey, password) {
var self = this;
var promise = new Promise(function(resolve, reject) {
privateKey = privateKey.toPacketlist(); privateKey = privateKey.toPacketlist();
this.worker.postMessage({ self.worker.postMessage({
event: 'decrypt-key', event: 'decrypt-key',
privateKey: privateKey, privateKey: privateKey,
password: password password: password
}); });
this.tasks.push(function(err, data) {
if (data) { self.tasks.push({ resolve:function(data) {
var packetlist = packet.List.fromStructuredClone(data); var packetlist = packet.List.fromStructuredClone(data);
data = new key.Key(packetlist); data = new key.Key(packetlist);
} resolve(data);
callback(err, data); }, reject:reject });
}); });
return promise;
}; };
/** /**
@ -290,23 +337,27 @@ AsyncProxy.prototype.decryptKey = function(privateKey, password, callback) {
* @param {module:key~Key} privateKey private key with encrypted secret key data * @param {module:key~Key} privateKey private key with encrypted secret key data
* @param {Array<module:type/keyid>} keyIds * @param {Array<module:type/keyid>} keyIds
* @param {String} password password to unlock the key * @param {String} password password to unlock the key
* @param {Function} callback receives decrypted key
*/ */
AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password, callback) { AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password) {
var self = this;
var promise = new Promise(function(resolve, reject) {
privateKey = privateKey.toPacketlist(); privateKey = privateKey.toPacketlist();
this.worker.postMessage({ self.worker.postMessage({
event: 'decrypt-key-packet', event: 'decrypt-key-packet',
privateKey: privateKey, privateKey: privateKey,
keyIds: keyIds, keyIds: keyIds,
password: password password: password
}); });
this.tasks.push(function(err, data) {
if (data) { self.tasks.push({ resolve:function(data) {
var packetlist = packet.List.fromStructuredClone(data); var packetlist = packet.List.fromStructuredClone(data);
data = new key.Key(packetlist); data = new key.Key(packetlist);
} resolve(data);
callback(err, data); }, reject:reject });
}); });
return promise;
}; };
module.exports = AsyncProxy; module.exports = AsyncProxy;

View File

@ -17,6 +17,28 @@
window = {}; // to make UMD bundles work window = {}; // to make UMD bundles work
// Mozilla bind polyfill because phantomjs is stupid
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
FNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof FNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments)));
};
FNOP.prototype = this.prototype;
fBound.prototype = new FNOP();
return fBound;
};
}
importScripts('openpgp.js'); importScripts('openpgp.js');
var MIN_SIZE_RANDOM_BUFFER = 40000; var MIN_SIZE_RANDOM_BUFFER = 40000;
@ -24,11 +46,12 @@ var MAX_SIZE_RANDOM_BUFFER = 60000;
window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER);
onmessage = function (event) { self.onmessage = function (event) {
var data = null, var data = null,
err = null, err = null,
msg = event.data, msg = event.data,
correct = false; correct = false;
switch (msg.event) { switch (msg.event) {
case 'seed-random': case 'seed-random':
if (!(msg.buf instanceof Uint8Array)) { if (!(msg.buf instanceof Uint8Array)) {
@ -37,85 +60,78 @@ onmessage = function (event) {
window.openpgp.crypto.random.randomBuffer.set(msg.buf); window.openpgp.crypto.random.randomBuffer.set(msg.buf);
break; break;
case 'encrypt-message': case 'encrypt-message':
try {
if (!msg.keys.length) { if (!msg.keys.length) {
msg.keys = [msg.keys]; msg.keys = [msg.keys];
} }
msg.keys = msg.keys.map(packetlistCloneToKey); msg.keys = msg.keys.map(packetlistCloneToKey);
data = window.openpgp.encryptMessage(msg.keys, msg.text); window.openpgp.encryptMessage(msg.keys, msg.text).then(function(data) {
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'sign-and-encrypt-message': case 'sign-and-encrypt-message':
try {
if (!msg.publicKeys.length) { if (!msg.publicKeys.length) {
msg.publicKeys = [msg.publicKeys]; msg.publicKeys = [msg.publicKeys];
} }
msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey);
msg.privateKey = packetlistCloneToKey(msg.privateKey); msg.privateKey = packetlistCloneToKey(msg.privateKey);
data = window.openpgp.signAndEncryptMessage(msg.publicKeys, msg.privateKey, msg.text); window.openpgp.signAndEncryptMessage(msg.publicKeys, msg.privateKey, msg.text).then(function(data) {
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'decrypt-message': case 'decrypt-message':
try {
msg.privateKey = packetlistCloneToKey(msg.privateKey); msg.privateKey = packetlistCloneToKey(msg.privateKey);
msg.message = packetlistCloneToMessage(msg.message.packets); msg.message = packetlistCloneToMessage(msg.message.packets);
data = window.openpgp.decryptMessage(msg.privateKey, msg.message); window.openpgp.decryptMessage(msg.privateKey, msg.message).then(function(data) {
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'decrypt-and-verify-message': case 'decrypt-and-verify-message':
try {
msg.privateKey = packetlistCloneToKey(msg.privateKey); msg.privateKey = packetlistCloneToKey(msg.privateKey);
if (!msg.publicKeys.length) { if (!msg.publicKeys.length) {
msg.publicKeys = [msg.publicKeys]; msg.publicKeys = [msg.publicKeys];
} }
msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey);
msg.message = packetlistCloneToMessage(msg.message.packets); msg.message = packetlistCloneToMessage(msg.message.packets);
data = window.openpgp.decryptAndVerifyMessage(msg.privateKey, msg.publicKeys, msg.message); window.openpgp.decryptAndVerifyMessage(msg.privateKey, msg.publicKeys, msg.message).then(function(data) {
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'sign-clear-message': case 'sign-clear-message':
try {
msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey); msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey);
data = window.openpgp.signClearMessage(msg.privateKeys, msg.text); window.openpgp.signClearMessage(msg.privateKeys, msg.text).then(function(data) {
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'verify-clear-signed-message': case 'verify-clear-signed-message':
try {
if (!msg.publicKeys.length) { if (!msg.publicKeys.length) {
msg.publicKeys = [msg.publicKeys]; msg.publicKeys = [msg.publicKeys];
} }
msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey);
var packetlist = window.openpgp.packet.List.fromStructuredClone(msg.message.packets); var packetlist = window.openpgp.packet.List.fromStructuredClone(msg.message.packets);
msg.message = new window.openpgp.cleartext.CleartextMessage(msg.message.text, packetlist); msg.message = new window.openpgp.cleartext.CleartextMessage(msg.message.text, packetlist);
data = window.openpgp.verifyClearSignedMessage(msg.publicKeys, msg.message); window.openpgp.verifyClearSignedMessage(msg.publicKeys, msg.message).then(function(data) {
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'generate-key-pair': case 'generate-key-pair':
try { window.openpgp.generateKeyPair(msg.options).then(function(data) {
data = window.openpgp.generateKeyPair(msg.options);
data.key = data.key.toPacketlist(); data.key = data.key.toPacketlist();
} catch (e) { response({event: 'method-return', data: data});
err = e.message; }).catch(function(e) {
} response({event: 'method-return', err: e.message});
response({event: 'method-return', data: data, err: err}); });
break; break;
case 'decrypt-key': case 'decrypt-key':
try { try {

View File

@ -8,15 +8,18 @@ var chai = require('chai'),
describe('Basic', function() { describe('Basic', function() {
describe("Key generation/encryption/decryption", function() { describe("Key generation/encryption/decryption", function() {
var testHelper = function(passphrase, userid, message) { var testHelper = function(passphrase, userid, message, done) {
var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); var opt = {numBits: 512, userId: userid, passphrase: passphrase};
var privKey;
var pubKey;
openpgp.generateKeyPair(opt).then(function(key) {
expect(key).to.exist; expect(key).to.exist;
expect(key.key).to.exist; expect(key.key).to.exist;
expect(key.privateKeyArmored).to.exist; expect(key.privateKeyArmored).to.exist;
expect(key.publicKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist;
var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message;
var privKeys = openpgp.key.readArmored(key.privateKeyArmored); var privKeys = openpgp.key.readArmored(key.privateKeyArmored);
var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); var publicKeys = openpgp.key.readArmored(key.publicKeyArmored);
@ -24,78 +27,95 @@ describe('Basic', function() {
expect(privKeys.err).to.not.exist; expect(privKeys.err).to.not.exist;
expect(privKeys.keys).to.have.length(1); expect(privKeys.keys).to.have.length(1);
var privKey = privKeys.keys[0]; privKey = privKeys.keys[0];
var pubKey = publicKeys.keys[0]; pubKey = publicKeys.keys[0];
expect(privKey).to.exist; expect(privKey).to.exist;
expect(pubKey).to.exist; expect(pubKey).to.exist;
var success = privKey.decrypt(passphrase); var success = privKey.decrypt(passphrase);
expect(success).to.be.true; expect(success).to.be.true;
var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); return openpgp.signAndEncryptMessage([pubKey], privKey, message);
}).then(function(encrypted) {
expect(encrypted).to.exist; expect(encrypted).to.exist;
var msg = openpgp.message.readArmored(encrypted); var msg = openpgp.message.readArmored(encrypted);
expect(msg).to.exist; expect(msg).to.exist;
var keyids = msg.getEncryptionKeyIds(); var keyids = msg.getEncryptionKeyIds();
expect(keyids).to.exist; expect(keyids).to.exist;
var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); return openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg);
}).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.text).to.equal(message); expect(decrypted.text).to.equal(message);
done();
});
}; };
it('ASCII Text', function (done) { it('ASCII Text', function (done) {
testHelper('password', 'Test McTestington <test@example.com>', 'hello world'); testHelper('password', 'Test McTestington <test@example.com>', 'hello world', done);
done();
}); });
it('Unicode Text', function (done) { it('Unicode Text', function (done) {
testHelper('●●●●', '♔♔♔♔ <test@example.com>', 'łäóć'); testHelper('●●●●', '♔♔♔♔ <test@example.com>', 'łäóć', done);
done();
}); });
it('should fail to verify signature for wrong public key', function (done) { it('should fail to verify signature for wrong public key', function (done) {
var userid = 'Test McTestington <test@example.com>'; var userid = 'Test McTestington <test@example.com>';
var passphrase = 'password'; var passphrase = 'password';
var message = 'hello world'; var message = 'hello world';
var privKey;
var pubKey;
var msg;
var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); var opt = {numBits: 512, userId: userid, passphrase: passphrase};
openpgp.generateKeyPair(opt).then(function(key) {
var privKeys = openpgp.key.readArmored(key.privateKeyArmored); var privKeys = openpgp.key.readArmored(key.privateKeyArmored);
var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); var publicKeys = openpgp.key.readArmored(key.publicKeyArmored);
var privKey = privKeys.keys[0]; privKey = privKeys.keys[0];
var pubKey = publicKeys.keys[0]; pubKey = publicKeys.keys[0];
var success = privKey.decrypt(passphrase); var success = privKey.decrypt(passphrase);
expect(success).to.be.true;
var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); return openpgp.signAndEncryptMessage([pubKey], privKey, message);
var msg = openpgp.message.readArmored(encrypted); }).then(function(encrypted) {
msg = openpgp.message.readArmored(encrypted);
expect(msg).to.exist; expect(msg).to.exist;
var anotherKey = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); return openpgp.generateKeyPair(opt);
}).then(function(anotherKey) {
var anotherPubKey = openpgp.key.readArmored(anotherKey.publicKeyArmored).keys[0]; var anotherPubKey = openpgp.key.readArmored(anotherKey.publicKeyArmored).keys[0];
var decrypted = openpgp.decryptAndVerifyMessage(privKey, [anotherPubKey], msg); return openpgp.decryptAndVerifyMessage(privKey, [anotherPubKey], msg);
}).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted.signatures[0].valid).to.be.null; expect(decrypted.signatures[0].valid).to.be.null;
expect(decrypted.text).to.equal(message); expect(decrypted.text).to.equal(message);
done(); done();
});
}); });
it('Performance test', function (done) { it('Performance test', function (done) {
// init test data // init test data
function randomString(length, chars) { function randomString(length, chars) {
var result = ''; var result = '';
for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))]; for (var i = length; i > 0; --i) {
result += chars[Math.round(Math.random() * (chars.length - 1))];
}
return result; return result;
} }
var message = randomString(1024*1024*3, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); var message = randomString(1024*1024*3, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
@ -103,9 +123,8 @@ describe('Basic', function() {
var userid = 'Test McTestington <test@example.com>'; var userid = 'Test McTestington <test@example.com>';
var passphrase = 'password'; var passphrase = 'password';
var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); var opt = {numBits: 512, userId: userid, passphrase: passphrase};
openpgp.generateKeyPair(opt).then(function(key) {
var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message;
var privKeys = openpgp.key.readArmored(key.privateKeyArmored); var privKeys = openpgp.key.readArmored(key.privateKeyArmored);
var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); var publicKeys = openpgp.key.readArmored(key.publicKeyArmored);
@ -114,6 +133,7 @@ describe('Basic', function() {
var pubKey = publicKeys.keys[0]; var pubKey = publicKeys.keys[0];
var success = privKey.decrypt(passphrase); var success = privKey.decrypt(passphrase);
expect(success).to.be.true;
if (console.profile) { if (console.profile) {
console.profile("encrypt/sign/verify/decrypt"); console.profile("encrypt/sign/verify/decrypt");
@ -136,13 +156,16 @@ describe('Basic', function() {
expect(keyids).to.exist; expect(keyids).to.exist;
var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); return openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg);
}).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.text).to.equal(message); expect(decrypted.text).to.equal(message);
done(); done();
});
}); });
}); });
@ -227,7 +250,7 @@ describe('Basic', function() {
expect(pubKey).to.exist; expect(pubKey).to.exist;
var encrypted = openpgp.encryptMessage([pubKey], plaintext); openpgp.encryptMessage([pubKey], plaintext).then(function(encrypted) {
expect(encrypted).to.exist; expect(encrypted).to.exist;
@ -251,6 +274,9 @@ describe('Basic', function() {
expect(keyids).to.exist; expect(keyids).to.exist;
expect(keyids).to.have.length(1); expect(keyids).to.have.length(1);
done(); done();
});
}); });
it('Decrypting key packet with wrong password returns false', function (done) { it('Decrypting key packet with wrong password returns false', function (done) {
@ -261,16 +287,12 @@ describe('Basic', function() {
done(); done();
}); });
var decrypted, error;
it('Calling decryptMessage with not decrypted key packet leads to exception', function (done) { it('Calling decryptMessage with not decrypted key packet leads to exception', function (done) {
function exceptionTest() { openpgp.decryptMessage(privKey, message).catch(function(error) {
decrypted = openpgp.decryptMessage(privKey, message); expect(error).to.exist;
}
expect(exceptionTest).to.throw(Error);
done(); done();
}); });
});
it('Decrypting key packet with correct password returns true', function (done) { it('Decrypting key packet with correct password returns true', function (done) {
var success = privKey.decryptKeyPacket(keyids, 'hello world'); var success = privKey.decryptKeyPacket(keyids, 'hello world');
@ -280,16 +302,23 @@ describe('Basic', function() {
}); });
it('Encrypt plain text and afterwards decrypt leads to same result', function (done) { it('Encrypt plain text and afterwards decrypt leads to same result', function (done) {
decrypted = openpgp.decryptMessage(privKey, message); openpgp.decryptMessage(privKey, message).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted).to.equal(plaintext); expect(decrypted).to.equal(plaintext);
done(); done();
}); });
});
it('Decrypt message 2x', function() { it('Decrypt message 2x', function(done) {
decrypted = openpgp.decryptMessage(privKey, message); var decrypted1;
var decrypted2 = openpgp.decryptMessage(privKey, message);
expect(decrypted).to.equal(decrypted2); openpgp.decryptMessage(privKey, message).then(function(decrypted) {
decrypted1 = decrypted;
return openpgp.decryptMessage(privKey, message);
}).then(function(decrypted2) {
expect(decrypted1).to.equal(decrypted2);
done();
});
}); });
}); });
@ -347,16 +376,18 @@ describe('Basic', function() {
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n'); '-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
it('Decrypt message', function (done) { it('Decrypt message', function (done) {
var privKey, message, decrypted; var privKey, message;
privKey = openpgp.key.readArmored(priv_key).keys[0]; privKey = openpgp.key.readArmored(priv_key).keys[0];
privKey.decrypt('1234'); privKey.decrypt('1234');
message = openpgp.message.readArmored(pgp_msg); message = openpgp.message.readArmored(pgp_msg);
decrypted = openpgp.decryptMessage(privKey, message);
openpgp.decryptMessage(privKey, message).then(function(decrypted) {
expect(decrypted).to.equal('hello 3des\n'); expect(decrypted).to.equal('hello 3des\n');
done(); done();
}); });
});
}); });
describe("Misc.", function() { describe("Misc.", function() {

View File

@ -618,7 +618,7 @@ var pgp_desktop_priv =
expect(prefAlgo).to.equal(openpgp.config.encryption_cipher); expect(prefAlgo).to.equal(openpgp.config.encryption_cipher);
}); });
it('Preferences of generated key', function() { it('Preferences of generated key', function(done) {
var testPref = function(key) { var testPref = function(key) {
// key flags // key flags
var keyFlags = openpgp.enums.keyFlags; var keyFlags = openpgp.enums.keyFlags;
@ -633,10 +633,13 @@ var pgp_desktop_priv =
var compr = openpgp.enums.compression; var compr = openpgp.enums.compression;
expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zlib, compr.zip]); expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zlib, compr.zip]);
expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection
} };
var key = openpgp.generateKeyPair({numBits: 512, userId: 'test', passphrase: 'hello'}); var opt = {numBits: 512, userId: 'test', passphrase: 'hello'};
openpgp.generateKeyPair(opt).then(function(key) {
testPref(key.key); testPref(key.key);
testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]); testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]);
done();
});
}); });
it('User attribute packet read & write', function() { it('User attribute packet read & write', function() {
@ -653,11 +656,14 @@ var pgp_desktop_priv =
expect(primUser.selfCertificate).to.be.an.instanceof(openpgp.packet.Signature); expect(primUser.selfCertificate).to.be.an.instanceof(openpgp.packet.Signature);
}); });
it('Generated key is not unlocked by default', function() { it('Generated key is not unlocked by default', function(done) {
var key = openpgp.generateKeyPair({numBits: 512, userId: 'test', passphrase: '123'}); var opt = {numBits: 512, userId: 'test', passphrase: '123'};
openpgp.generateKeyPair(opt).then(function(key) {
var msg = openpgp.message.fromText('hello').encrypt([key.key]); var msg = openpgp.message.fromText('hello').encrypt([key.key]);
msg = msg.decrypt.bind(msg, key.key); msg = msg.decrypt.bind(msg, key.key);
expect(msg).to.throw('Private key is not decrypted.'); expect(msg).to.throw('Private key is not decrypted.');
done();
});
}); });
}); });

View File

@ -117,11 +117,11 @@ describe("Packet", function() {
}); });
it('Public key encrypted symmetric key packet', function(done) { it('Public key encrypted symmetric key packet', function(done) {
var rsa = new openpgp.crypto.publicKey.rsa(), var rsa = new openpgp.crypto.publicKey.rsa();
mpi = rsa.generate(512, "10001")
var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; rsa.generate(512, "10001").then(function(mpiGen) {
var mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
mpi = mpi.map(function(k) { mpi = mpi.map(function(k) {
var mpi = new openpgp.MPI(); var mpi = new openpgp.MPI();
mpi.fromBigInteger(k); mpi.fromBigInteger(k);
@ -148,6 +148,7 @@ describe("Packet", function() {
expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm);
done(); done();
}); });
});
it('Secret key packet (reading, unencrpted)', function(done) { it('Secret key packet (reading, unencrpted)', function(done) {
var armored_key = var armored_key =
@ -240,7 +241,6 @@ describe("Packet", function() {
'=iSaK\n' + '=iSaK\n' +
'-----END PGP MESSAGE-----'; '-----END PGP MESSAGE-----';
var key = new openpgp.packet.List(); var key = new openpgp.packet.List();
key.read(openpgp.armor.decode(armored_key).data); key.read(openpgp.armor.decode(armored_key).data);
key = key[3]; key = key[3];
@ -378,14 +378,12 @@ describe("Packet", function() {
it('Writing and encryption of a secret key packet.', function(done) { it('Writing and encryption of a secret key packet.', function(done) {
var key = new openpgp.packet.List(); var key = new openpgp.packet.List();
key.push(new openpgp.packet.SecretKey); key.push(new openpgp.packet.SecretKey());
var rsa = new openpgp.crypto.publicKey.rsa(), var rsa = new openpgp.crypto.publicKey.rsa();
mpi = rsa.generate(512, "10001")
var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u];
rsa.generate(512, "10001").then(function(mipGen) {
var mpi = [mipGen.n, mipGen.ee, mipGen.d, mipGen.p, mipGen.q, mipGen.u];
mpi = mpi.map(function(k) { mpi = mpi.map(function(k) {
var mpi = new openpgp.MPI(); var mpi = new openpgp.MPI();
mpi.fromBigInteger(k); mpi.fromBigInteger(k);
@ -405,15 +403,15 @@ describe("Packet", function() {
expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString()); expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString());
done(); done();
}); });
});
it('Writing and verification of a signature packet.', function(done) { it('Writing and verification of a signature packet.', function(done) {
var key = new openpgp.packet.SecretKey(); var key = new openpgp.packet.SecretKey();
var rsa = new openpgp.crypto.publicKey.rsa, var rsa = new openpgp.crypto.publicKey.rsa();
mpi = rsa.generate(512, "10001")
var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u];
rsa.generate(512, "10001").then(function(mpiGen) {
var mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
mpi = mpi.map(function(k) { mpi = mpi.map(function(k) {
var mpi = new openpgp.MPI(); var mpi = new openpgp.MPI();
mpi.fromBigInteger(k); mpi.fromBigInteger(k);
@ -447,4 +445,5 @@ describe("Packet", function() {
expect(verified).to.be.true; expect(verified).to.be.true;
done(); done();
}); });
});
}); });

View File

@ -262,11 +262,12 @@ describe("Signature", function() {
var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0]; var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0];
var msg = openpgp.message.readArmored(msg_arm1); var msg = openpgp.message.readArmored(msg_arm1);
priv_key.decrypt("abcd"); priv_key.decrypt("abcd");
var decrypted = openpgp.decryptAndVerifyMessage(priv_key, [pub_key], msg); openpgp.decryptAndVerifyMessage(priv_key, [pub_key], msg).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].valid).to.be.true;
done(); done();
}); });
});
it('Testing GnuPG stripped-key extensions', function(done) { it('Testing GnuPG stripped-key extensions', function(done) {
// exercises the GnuPG s2k type 1001 extension: // exercises the GnuPG s2k type 1001 extension:
@ -383,14 +384,14 @@ describe("Signature", function() {
var keyids = esMsg.getEncryptionKeyIds(); var keyids = esMsg.getEncryptionKeyIds();
privKey.decryptKeyPacket(keyids, 'hello world'); privKey.decryptKeyPacket(keyids, 'hello world');
var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], esMsg); openpgp.decryptAndVerifyMessage(privKey, [pubKey], esMsg).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted.text).to.equal(plaintext); expect(decrypted.text).to.equal(plaintext);
expect(decrypted.signatures).to.have.length(1); expect(decrypted.signatures).to.have.length(1);
expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].valid).to.be.true;
done(); done();
}); });
});
it('Verify signature of signed and encrypted message from PGP 10.3.0 with openpgp.decryptAndVerifyMessage', function(done) { it('Verify signature of signed and encrypted message from PGP 10.3.0 with openpgp.decryptAndVerifyMessage', function(done) {
var msg_armor = var msg_armor =
@ -419,8 +420,7 @@ describe("Signature", function() {
var keyids = esMsg.getEncryptionKeyIds(); var keyids = esMsg.getEncryptionKeyIds();
privKey.decryptKeyPacket(keyids, 'hello world'); privKey.decryptKeyPacket(keyids, 'hello world');
var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], esMsg); openpgp.decryptAndVerifyMessage(privKey, [pubKey], esMsg).then(function(decrypted) {
expect(decrypted).to.exist; expect(decrypted).to.exist;
expect(decrypted.text).to.equal(plaintext); expect(decrypted.text).to.equal(plaintext);
expect(decrypted.signatures).to.have.length(1); expect(decrypted.signatures).to.have.length(1);
@ -428,6 +428,8 @@ describe("Signature", function() {
done(); done();
}); });
});
it('Verify signed message with two one pass signatures', function(done) { it('Verify signed message with two one pass signatures', function(done) {
var msg_armor = var msg_armor =
[ '-----BEGIN PGP MESSAGE-----', [ '-----BEGIN PGP MESSAGE-----',
@ -504,8 +506,7 @@ describe("Signature", function() {
expect(pubKey2.getKeyPacket(keyids)).to.exist; expect(pubKey2.getKeyPacket(keyids)).to.exist;
expect(pubKey3.getKeyPacket(keyids)).to.exist; expect(pubKey3.getKeyPacket(keyids)).to.exist;
var cleartextSig = openpgp.verifyClearSignedMessage([pubKey2, pubKey3], csMsg); openpgp.verifyClearSignedMessage([pubKey2, pubKey3], csMsg).then(function(cleartextSig) {
expect(cleartextSig).to.exist; expect(cleartextSig).to.exist;
expect(cleartextSig.text).to.equal(plaintext); expect(cleartextSig.text).to.equal(plaintext);
expect(cleartextSig.signatures).to.have.length(2); expect(cleartextSig.signatures).to.have.length(2);
@ -513,6 +514,7 @@ describe("Signature", function() {
expect(cleartextSig.signatures[1].valid).to.be.true; expect(cleartextSig.signatures[1].valid).to.be.true;
done(); done();
}); });
});
it('Sign text with openpgp.signClearMessage and verify with openpgp.verifyClearSignedMessage leads to same cleartext and valid signatures', function(done) { it('Sign text with openpgp.signClearMessage and verify with openpgp.verifyClearSignedMessage leads to same cleartext and valid signatures', function(done) {
var plaintext = 'short message\nnext line\n한국어/조선말'; var plaintext = 'short message\nnext line\n한국어/조선말';
@ -520,12 +522,12 @@ describe("Signature", function() {
var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
privKey.getSigningKeyPacket().decrypt('hello world'); privKey.getSigningKeyPacket().decrypt('hello world');
var clearSignedArmor = openpgp.signClearMessage([privKey], plaintext); openpgp.signClearMessage([privKey], plaintext).then(function(clearSignedArmor) {
var csMsg = openpgp.cleartext.readArmored(clearSignedArmor); var csMsg = openpgp.cleartext.readArmored(clearSignedArmor);
return openpgp.verifyClearSignedMessage([pubKey], csMsg);
var cleartextSig = openpgp.verifyClearSignedMessage([pubKey], csMsg); }).then(function(cleartextSig) {
expect(cleartextSig).to.exist; expect(cleartextSig).to.exist;
expect(cleartextSig.text).to.equal(plaintext.replace(/\r/g,'')); expect(cleartextSig.text).to.equal(plaintext.replace(/\r/g,''));
expect(cleartextSig.signatures).to.have.length(1); expect(cleartextSig.signatures).to.have.length(1);
@ -533,6 +535,8 @@ describe("Signature", function() {
done(); done();
}); });
});
it('Verify primary key revocation signature', function(done) { it('Verify primary key revocation signature', function(done) {
var pubKey = openpgp.key.readArmored(pub_revoked).keys[0]; var pubKey = openpgp.key.readArmored(pub_revoked).keys[0];
@ -636,13 +640,17 @@ describe("Signature", function() {
expect(result[0].valid).to.be.true; expect(result[0].valid).to.be.true;
}); });
it('Sign message with key without password', function() { it('Sign message with key without password', function(done) {
var key = openpgp.generateKeyPair({numBits: 512, userId: 'ABC', passphrase: null}).key; var opt = {numBits: 512, userId: 'ABC', passphrase: null};
openpgp.generateKeyPair(opt).then(function(gen) {
var key = gen.key;
var message = openpgp.message.fromText('hello world'); var message = openpgp.message.fromText('hello world');
message = message.sign([key]); message = message.sign([key]);
expect(message).to.exist; expect(message).to.exist;
done();
});
}); });
}); });

View File

@ -9,12 +9,13 @@
<body> <body>
<div id="mocha"></div> <div id="mocha"></div>
<!--<script src="lib/jquery.min.js"></script>--> <!-- libs -->
<script src="../dist/openpgp.js"></script> <script src="../dist/openpgp.js"></script>
<script src="lib/chai.js"></script> <script src="lib/chai.js"></script>
<script src="lib/mocha.js"></script> <script src="lib/mocha.js"></script>
<script> <script>
mocha.setup('bdd'); mocha.setup('bdd');
mocha.timeout(20000);
</script> </script>
<script src="lib/unittests-bundle.js"></script> <script src="lib/unittests-bundle.js"></script>
<script> <script>

View File

@ -184,8 +184,7 @@ describe('High level API', function() {
describe('Encryption', function() { describe('Encryption', function() {
it('RSA: encryptMessage async', function (done) { it('RSA: encryptMessage async', function (done) {
openpgp.encryptMessage([pubKeyRSA], plaintext, function(err, data) { openpgp.encryptMessage([pubKeyRSA], plaintext).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.match(/^-----BEGIN PGP MESSAGE/); expect(data).to.match(/^-----BEGIN PGP MESSAGE/);
var msg = openpgp.message.readArmored(data); var msg = openpgp.message.readArmored(data);
@ -195,8 +194,7 @@ describe('High level API', function() {
}); });
it('RSA: encryptMessage one key async', function (done) { it('RSA: encryptMessage one key async', function (done) {
openpgp.encryptMessage(pubKeyRSA, plaintext, function(err, data) { openpgp.encryptMessage(pubKeyRSA, plaintext).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.match(/^-----BEGIN PGP MESSAGE/); expect(data).to.match(/^-----BEGIN PGP MESSAGE/);
var msg = openpgp.message.readArmored(data); var msg = openpgp.message.readArmored(data);
@ -205,25 +203,8 @@ describe('High level API', function() {
}); });
}); });
it('RSA: encryptMessage sync', function () {
var msg = openpgp.encryptMessage([pubKeyRSA], plaintext);
expect(msg).to.exist;
expect(msg).to.match(/^-----BEGIN PGP MESSAGE/);
msg = openpgp.message.readArmored(msg);
expect(msg).to.be.an.instanceof(openpgp.message.Message);
});
it('RSA: encryptMessage one key sync', function () {
var msg = openpgp.encryptMessage(pubKeyRSA, plaintext);
expect(msg).to.exist;
expect(msg).to.match(/^-----BEGIN PGP MESSAGE/);
msg = openpgp.message.readArmored(msg);
expect(msg).to.be.an.instanceof(openpgp.message.Message);
});
it('ELG: encryptMessage async', function (done) { it('ELG: encryptMessage async', function (done) {
openpgp.encryptMessage([pubKeyDE], plaintext, function(err, data) { openpgp.encryptMessage([pubKeyDE], plaintext).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.match(/^-----BEGIN PGP MESSAGE/); expect(data).to.match(/^-----BEGIN PGP MESSAGE/);
var msg = openpgp.message.readArmored(data); var msg = openpgp.message.readArmored(data);
@ -232,14 +213,6 @@ describe('High level API', function() {
}); });
}); });
it('ELG: encryptMessage sync', function () {
var msg = openpgp.encryptMessage([pubKeyDE], plaintext);
expect(msg).to.exist;
expect(msg).to.match(/^-----BEGIN PGP MESSAGE/);
msg = openpgp.message.readArmored(msg);
expect(msg).to.be.an.instanceof(openpgp.message.Message);
});
}); });
describe('Decryption', function() { describe('Decryption', function() {
@ -254,35 +227,21 @@ describe('High level API', function() {
}); });
it('RSA: decryptMessage async', function (done) { it('RSA: decryptMessage async', function (done) {
openpgp.decryptMessage(privKeyRSA, msgRSA, function(err, data) { openpgp.decryptMessage(privKeyRSA, msgRSA).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.equal(plaintext); expect(data).to.equal(plaintext);
done(); done();
}); });
}); });
it('RSA: decryptMessage sync', function () {
var text = openpgp.decryptMessage(privKeyRSA, msgRSA);
expect(text).to.exist;
expect(text).to.equal(plaintext);
});
it('ELG: decryptMessage async', function (done) { it('ELG: decryptMessage async', function (done) {
openpgp.decryptMessage(privKeyDE, msgDE, function(err, data) { openpgp.decryptMessage(privKeyDE, msgDE).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.equal(plaintext); expect(data).to.equal(plaintext);
done(); done();
}); });
}); });
it('ELG: decryptMessage sync', function () {
var text = openpgp.decryptMessage(privKeyDE, msgDE);
expect(text).to.exist;
expect(text).to.equal(plaintext);
});
}); });
function verifySignature(data, privKey) { function verifySignature(data, privKey) {
@ -304,8 +263,7 @@ describe('High level API', function() {
}); });
it('RSA: decryptAndVerifyMessage async', function (done) { it('RSA: decryptAndVerifyMessage async', function (done) {
openpgp.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA, function(err, data) { openpgp.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msgRSA).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
verifySignature(data, privKeyRSA); verifySignature(data, privKeyRSA);
done(); done();
@ -313,8 +271,7 @@ describe('High level API', function() {
}); });
it('ELG: decryptAndVerifyMessage async', function (done) { it('ELG: decryptAndVerifyMessage async', function (done) {
openpgp.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE, function(err, data) { openpgp.decryptAndVerifyMessage(privKeyDE, [pubKeyDE], msgDE).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
verifySignature(data, privKeyDE); verifySignature(data, privKeyDE);
done(); done();
@ -330,13 +287,13 @@ describe('High level API', function() {
}); });
it('RSA: signAndEncryptMessage async', function (done) { it('RSA: signAndEncryptMessage async', function (done) {
openpgp.signAndEncryptMessage([pubKeyRSA], privKeyRSA, plaintext, function(err, data) { openpgp.signAndEncryptMessage([pubKeyRSA], privKeyRSA, plaintext).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.match(/^-----BEGIN PGP MESSAGE/); expect(data).to.match(/^-----BEGIN PGP MESSAGE/);
var msg = openpgp.message.readArmored(data); var msg = openpgp.message.readArmored(data);
expect(msg).to.be.an.instanceof(openpgp.message.Message); expect(msg).to.be.an.instanceof(openpgp.message.Message);
var decrypted = openpgp.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msg); return openpgp.decryptAndVerifyMessage(privKeyRSA, [pubKeyRSA], msg);
}).then(function(decrypted) {
verifySignature(decrypted, privKeyRSA); verifySignature(decrypted, privKeyRSA);
done(); done();
}); });
@ -352,8 +309,7 @@ describe('High level API', function() {
}); });
it('RSA: signClearMessage async', function (done) { it('RSA: signClearMessage async', function (done) {
openpgp.signClearMessage([privKeyRSA], plaintext, function(err, data) { openpgp.signClearMessage([privKeyRSA], plaintext).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); expect(data).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/);
var msg = openpgp.message.readArmored(data); var msg = openpgp.message.readArmored(data);
@ -363,8 +319,7 @@ describe('High level API', function() {
}); });
it('DSA: signClearMessage async', function (done) { it('DSA: signClearMessage async', function (done) {
openpgp.signClearMessage([privKeyDE], plaintext, function(err, data) { openpgp.signClearMessage([privKeyDE], plaintext).then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/); expect(data).to.match(/-----BEGIN PGP SIGNED MESSAGE-----/);
var msg = openpgp.message.readArmored(data); var msg = openpgp.message.readArmored(data);
@ -374,10 +329,10 @@ describe('High level API', function() {
}); });
it('RSA: verifyClearSignedMessage async', function (done) { it('RSA: verifyClearSignedMessage async', function (done) {
var signed = openpgp.signClearMessage([privKeyRSA], plaintext); openpgp.signClearMessage([privKeyRSA], plaintext).then(function(signed) {
signed = openpgp.cleartext.readArmored(signed); signed = openpgp.cleartext.readArmored(signed);
openpgp.verifyClearSignedMessage([pubKeyRSA], signed, function(err, data) { return openpgp.verifyClearSignedMessage([pubKeyRSA], signed);
expect(err).to.not.exist; }).then(function(data) {
expect(data).to.exist; expect(data).to.exist;
verifySignature(data, privKeyRSA); verifySignature(data, privKeyRSA);
done(); done();
@ -391,10 +346,8 @@ describe('High level API', function() {
before(initKeys); before(initKeys);
it('Signing with not decrypted key gives error', function (done) { it('Signing with not decrypted key gives error', function (done) {
openpgp.signClearMessage([privKeyRSA], plaintext, function(err, data) { openpgp.signClearMessage([privKeyRSA], plaintext).catch(function(err) {
expect(data).to.not.exist;
expect(err).to.exist; expect(err).to.exist;
expect(err.message).to.equal('Private key is not decrypted.');
done(); done();
}); });
}); });
@ -404,10 +357,8 @@ describe('High level API', function() {
wProxy.worker = new Worker('../dist/openpgp.worker.js'); wProxy.worker = new Worker('../dist/openpgp.worker.js');
wProxy.worker.onmessage = wProxy.onMessage.bind(wProxy); wProxy.worker.onmessage = wProxy.onMessage.bind(wProxy);
wProxy.seedRandom(10); wProxy.seedRandom(10);
wProxy.encryptMessage([pubKeyRSA], plaintext, function(err, data) { wProxy.encryptMessage([pubKeyRSA], plaintext).catch(function(err) {
expect(data).to.not.exist;
expect(err).to.exist; expect(err).to.exist;
expect(err).to.eql(new Error('Random number buffer depleted'));
done(); done();
}); });
}); });
@ -417,8 +368,8 @@ describe('High level API', function() {
describe('Key generation', function() { describe('Key generation', function() {
it('Generate 1024-bit RSA/RSA key async', function (done) { it('Generate 1024-bit RSA/RSA key async', function (done) {
openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington <test@example.com>', passphrase: 'hello world'}, function(err, data) { var opt = {numBits: 1024, userId: 'Test McTestington <test@example.com>', passphrase: 'hello world'};
expect(err).to.not.exist; openpgp.generateKeyPair(opt).then(function(data) {
expect(data).to.exist; expect(data).to.exist;
expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/); expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/);
expect(data.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/); expect(data.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/);
@ -427,14 +378,6 @@ describe('High level API', function() {
}); });
}); });
it('Generate 1024-bit RSA/RSA key sync', function () {
var key = openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington <test@example.com>', passphrase: 'hello world'});
expect(key).to.exist;
expect(key.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/);
expect(key.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/);
expect(key.key).to.be.an.instanceof(openpgp.key.Key);
});
}); });
describe('Decrypt secret key', function() { describe('Decrypt secret key', function() {
@ -450,13 +393,13 @@ describe('High level API', function() {
it('Decrypt key', function (done) { it('Decrypt key', function (done) {
expect(privKeyRSA.primaryKey.isDecrypted).to.be.false; expect(privKeyRSA.primaryKey.isDecrypted).to.be.false;
expect(privKeyRSA.subKeys[0].subKey.isDecrypted).to.be.false; expect(privKeyRSA.subKeys[0].subKey.isDecrypted).to.be.false;
proxy.decryptKey(privKeyRSA, 'hello world', function(err, data) { proxy.decryptKey(privKeyRSA, 'hello world').then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.be.an.instanceof(openpgp.key.Key); expect(data).to.be.an.instanceof(openpgp.key.Key);
expect(data.primaryKey.isDecrypted).to.be.true; expect(data.primaryKey.isDecrypted).to.be.true;
expect(data.subKeys[0].subKey.isDecrypted).to.be.true; expect(data.subKeys[0].subKey.isDecrypted).to.be.true;
var text = openpgp.decryptMessage(data, msg); return openpgp.decryptMessage(data, msg);
}).then(function(text) {
expect(text).to.equal(plaintext); expect(text).to.equal(plaintext);
done(); done();
}); });
@ -466,20 +409,20 @@ describe('High level API', function() {
expect(privKeyRSA.primaryKey.isDecrypted).to.be.false; expect(privKeyRSA.primaryKey.isDecrypted).to.be.false;
expect(privKeyRSA.subKeys[0].subKey.isDecrypted).to.be.false; expect(privKeyRSA.subKeys[0].subKey.isDecrypted).to.be.false;
var keyid = privKeyRSA.subKeys[0].subKey.getKeyId(); var keyid = privKeyRSA.subKeys[0].subKey.getKeyId();
proxy.decryptKeyPacket(privKeyRSA, [keyid], 'hello world', function(err, data) { proxy.decryptKeyPacket(privKeyRSA, [keyid], 'hello world').then(function(data) {
expect(err).to.not.exist;
expect(data).to.exist; expect(data).to.exist;
expect(data).to.be.an.instanceof(openpgp.key.Key); expect(data).to.be.an.instanceof(openpgp.key.Key);
expect(data.primaryKey.isDecrypted).to.be.false; expect(data.primaryKey.isDecrypted).to.be.false;
expect(data.subKeys[0].subKey.isDecrypted).to.be.true; expect(data.subKeys[0].subKey.isDecrypted).to.be.true;
var text = openpgp.decryptMessage(data, msg); return openpgp.decryptMessage(data, msg);
}).then(function(text) {
expect(text).to.equal(plaintext); expect(text).to.equal(plaintext);
done(); done();
}); });
}); });
it('Error on wrong password decryptKey', function (done) { it('Error on wrong password decryptKey', function (done) {
proxy.decryptKey(privKeyRSA, 'what?', function(err, data) { proxy.decryptKey(privKeyRSA, 'what?').catch(function(err) {
expect(err).to.eql(new Error('Wrong password')); expect(err).to.eql(new Error('Wrong password'));
done(); done();
}); });
@ -487,7 +430,7 @@ describe('High level API', function() {
it('Error on wrong password decryptKeyPacket', function (done) { it('Error on wrong password decryptKeyPacket', function (done) {
var keyid = privKeyRSA.subKeys[0].subKey.getKeyId(); var keyid = privKeyRSA.subKeys[0].subKey.getKeyId();
proxy.decryptKeyPacket(privKeyRSA, [keyid], 'what?', function(err, data) { proxy.decryptKeyPacket(privKeyRSA, [keyid], 'what?').catch(function(err) {
expect(err).to.eql(new Error('Wrong password')); expect(err).to.eql(new Error('Wrong password'));
done(); done();
}); });