From 15edf09972f6f1d5682805cc16fe7a690b8ecb39 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 29 Sep 2014 22:00:29 +0200 Subject: [PATCH 01/19] Add RSA keygen example using WebCrypto Api --- src/crypto/public_key/rsa.js | 63 ++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 16908fff..56eb6d63 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -1,16 +1,16 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -134,6 +134,63 @@ function RSA() { // Generate a new random private key B bits long, using public expt E function generate(B, E) { + + // + // Web Crypto RSA keygen proposal example + // + + if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { + var keyGenOpt = { + name: 'RSASSA-PKCS1-v1_5', + modulusLength: B, // the specified keysize in bits + publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Equivalent to 65537, TODO: use provided argument E + hash: { + name: 'SHA-256' // not required for actual RSA keys, but for crypto api 'sign' and 'verifiy' + } + }; + + var extractable = true; // make generated key extractable + + window.crypto.subtle.generateKey(keyGenOpt, extractable, ['sign', 'verify']) + .then(onGenerated) + .then(onExported); + } + + function onGenerated(key) { + // export the generated keys as JsonWebKey (JWK) + // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 + var p1 = window.crypto.subtle.exportKey('jwk', key.privateKey); + var p2 = window.crypto.subtle.exportKey('jwk', key.publicKey); + + return window.Promise.all([p1, p2]); + } + + function onExported(exported) { + // Exported JWK has the following encoded parameters: n, p, q, qi, ... + + var privKey = exported[0]; + var pubKey = exported[1]; + + console.log('Exported private key: ', privKey); + console.log('Exported public key: ', pubKey); + + var d = privKey.d; + var dp = privKey.dp; + var dq = privKey.dq; + var e = privKey.e; + var n = privKey.n; + var p = privKey.p; + var q = privKey.q; + var qi = privKey.qi; + + // TODO: map JWK parameters to local BigInteger type system? + // TODO: add async style callback + } + + // + // JS code + // + var key = new keyObject(); var rng = new SecureRandom(); var qs = B >> 1; From d6963f2017a77ccdd9d67e94e2406123f3451927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Obernd=C3=B6rfer?= Date: Tue, 30 Sep 2014 12:19:06 +0200 Subject: [PATCH 02/19] map JWK parameters to local BigInteger --- src/crypto/public_key/rsa.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 56eb6d63..eb5de534 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -183,7 +183,19 @@ function RSA() { var q = privKey.q; var qi = privKey.qi; - // TODO: map JWK parameters to local BigInteger type system? + // map JWK parameters to local BigInteger type system + var key = new keyObject(); + key.n = new BigInteger(util.hexstrdump(base64(n)), 16); + key.ee = new BigInteger(E, 16); + key.d = new BigInteger(util.hexstrdump(base64(d)), 16); + key.p = new BigInteger(util.hexstrdump(base64(p)), 16); + key.q = new BigInteger(util.hexstrdump(base64(q)), 16); + key.u = key.p.modInverse(key.q); + + function base64(base64url) { + return base64url.replace(/-/g, '+').replace(/_/g, '/') + } + // TODO: add async style callback } From cbe4a17ccb035683207bcc17cb1d593fbe2be311 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 30 Sep 2014 15:38:02 +0200 Subject: [PATCH 03/19] Refactor key generation to use callback * TODO: reactive native web crypto in rsa.js:142 * TODO: generate publicExponent Uint8Array from argument E in rsa.js:148 * TODO: signing with generated web crypto key fails with "Could not find valid key packet for signing in key" --- package.json | 2 +- src/crypto/crypto.js | 62 ++-- src/crypto/public_key/rsa.js | 55 ++-- src/key.js | 194 ++++++----- src/openpgp.js | 26 +- src/packet/secret_key.js | 20 +- src/worker/worker.js | 22 +- test/general/basic.js | 163 ++++----- test/general/key.js | 30 +- test/general/packet.js | 621 ++++++++++++++++++----------------- test/general/signature.js | 30 +- test/worker/api.js | 8 - 12 files changed, 650 insertions(+), 583 deletions(-) diff --git a/package.json b/package.json index 10bab78b..8c31da29 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "openpgp", "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-dev", "homepage": "http://openpgpjs.org/", "engines": { "node": ">=0.8" diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 78d6454b..6ae3f64e 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -1,19 +1,19 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // The GPG4Browsers crypto interface @@ -32,12 +32,12 @@ var random = require('./random.js'), module.exports = { /** - * Encrypts data using the specified public key multiprecision integers + * Encrypts data using the specified public key multiprecision integers * and the specified algorithm. * @param {module:enums.publicKey} algo Algorithm to be used (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) * @param {Array} publicMPIs Algorithm dependent multiprecision integers * @param {module:type/mpi} data Data to be encrypted as MPI - * @return {Array} if RSA an module:type/mpi; + * @return {Array} if RSA an module:type/mpi; * if elgamal encryption an array of two module:type/mpi is returned; otherwise null */ publicKeyEncrypt: function(algo, publicMPIs, data) { @@ -76,9 +76,9 @@ module.exports = { * Decrypts data using the specified public key multiprecision integers of the private key, * the specified secretMPIs of the private key and the specified algorithm. * @param {module:enums.publicKey} algo Algorithm to be used (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) - * @param {Array} publicMPIs Algorithm dependent multiprecision integers + * @param {Array} publicMPIs Algorithm dependent multiprecision integers * of the public key part of the private key - * @param {Array} secretMPIs Algorithm dependent multiprecision integers + * @param {Array} secretMPIs Algorithm dependent multiprecision integers * of the private key used * @param {module:type/mpi} data Data to be encrypted as MPI * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null @@ -178,15 +178,19 @@ module.exports = { } }, - generateMpi: function(algo, bits) { - var result = (function() { - switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': - //remember "publicKey" refers to the crypto/public_key dir - var rsa = new publicKey.rsa(); - var keyObject = rsa.generate(bits, "10001"); + generateMpi: function(algo, bits, callback) { + switch (algo) { + case 'rsa_encrypt': + case 'rsa_encrypt_sign': + case 'rsa_sign': + //remember "publicKey" refers to the crypto/public_key dir + var rsa = new publicKey.rsa(); + rsa.generate(bits, "10001", function(err, keyObject) { + if (err) { + callback(err); + return; + } + var output = []; output.push(keyObject.n); output.push(keyObject.ee); @@ -194,17 +198,21 @@ module.exports = { output.push(keyObject.p); output.push(keyObject.q); output.push(keyObject.u); - return output; - default: - throw new Error('Unsupported algorithm for key generation.'); - } - })(); - return result.map(function(bn) { - var mpi = new type_mpi(); - mpi.fromBigInteger(bn); - return mpi; - }); + callback(null, mapResult(output)); + }); + break; + default: + callback(new Error('Unsupported algorithm for key generation.')); + } + + function mapResult(result) { + return result.map(function(bn) { + var mpi = new type_mpi(); + mpi.fromBigInteger(bn); + return mpi; + }); + } }, diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index eb5de534..f1222bd2 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -133,13 +133,15 @@ function RSA() { // Generate a new random private key B bits long, using public expt E - function generate(B, E) { + function generate(B, E, callback) { // - // Web Crypto RSA keygen proposal example + // Native RSA keygen using Web Crypto // - if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { + if (false && typeof window !== 'undefined' && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle)) { + var subtle = window.crypto.subtle || window.crypto.webkitSubtle; + var keyGenOpt = { name: 'RSASSA-PKCS1-v1_5', modulusLength: B, // the specified keysize in bits @@ -151,52 +153,34 @@ function RSA() { var extractable = true; // make generated key extractable - window.crypto.subtle.generateKey(keyGenOpt, extractable, ['sign', 'verify']) - .then(onGenerated) - .then(onExported); + subtle.generateKey(keyGenOpt, extractable, ['sign', 'verify']) + .then(onGenerated, callback) + .then(onExported, callback); + + return; } function onGenerated(key) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 - var p1 = window.crypto.subtle.exportKey('jwk', key.privateKey); - var p2 = window.crypto.subtle.exportKey('jwk', key.publicKey); - - return window.Promise.all([p1, p2]); + return subtle.exportKey('jwk', key.privateKey); } - function onExported(exported) { - // Exported JWK has the following encoded parameters: n, p, q, qi, ... - - var privKey = exported[0]; - var pubKey = exported[1]; - - console.log('Exported private key: ', privKey); - console.log('Exported public key: ', pubKey); - - var d = privKey.d; - var dp = privKey.dp; - var dq = privKey.dq; - var e = privKey.e; - var n = privKey.n; - var p = privKey.p; - var q = privKey.q; - var qi = privKey.qi; - + function onExported(jwk) { // map JWK parameters to local BigInteger type system var key = new keyObject(); - key.n = new BigInteger(util.hexstrdump(base64(n)), 16); + key.n = new BigInteger(util.hexstrdump(base64(jwk.n)), 16); key.ee = new BigInteger(E, 16); - key.d = new BigInteger(util.hexstrdump(base64(d)), 16); - key.p = new BigInteger(util.hexstrdump(base64(p)), 16); - key.q = new BigInteger(util.hexstrdump(base64(q)), 16); + key.d = new BigInteger(util.hexstrdump(base64(jwk.d)), 16); + key.p = new BigInteger(util.hexstrdump(base64(jwk.p)), 16); + key.q = new BigInteger(util.hexstrdump(base64(jwk.q)), 16); key.u = key.p.modInverse(key.q); function base64(base64url) { - return base64url.replace(/-/g, '+').replace(/_/g, '/') + return base64url.replace(/-/g, '+').replace(/_/g, '/'); } - // TODO: add async style callback + callback(null, key); } // @@ -236,7 +220,8 @@ function RSA() { break; } } - return key; + + callback(null, key); } this.encrypt = encrypt; diff --git a/src/key.js b/src/key.js index fbe1b6c7..2a730375 100644 --- a/src/key.js +++ b/src/key.js @@ -1,6 +1,6 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either @@ -10,7 +10,7 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -148,14 +148,14 @@ Key.prototype.toPacketlist = function() { if (this.subKeys) { for (i = 0; i < this.subKeys.length; i++) { packetlist.concat(this.subKeys[i].toPacketlist()); - } + } } return packetlist; }; -/** +/** * Returns all the private and public subkey packets - * @returns {Array<(module:packet/public_subkey|module:packet/secret_subkey)>} + * @returns {Array<(module:packet/public_subkey|module:packet/secret_subkey)>} */ Key.prototype.getSubkeyPackets = function() { var subKeys = []; @@ -167,17 +167,17 @@ Key.prototype.getSubkeyPackets = function() { return subKeys; }; -/** +/** * Returns all the private and public key and subkey packets - * @returns {Array<(module:packet/public_subkey|module:packet/secret_subkey|module:packet/secret_key|module:packet/public_key)>} + * @returns {Array<(module:packet/public_subkey|module:packet/secret_subkey|module:packet/secret_key|module:packet/public_key)>} */ Key.prototype.getAllKeyPackets = function() { return [this.primaryKey].concat(this.getSubkeyPackets()); }; -/** +/** * Returns key IDs of all key packets - * @returns {Array} + * @returns {Array} */ Key.prototype.getKeyIds = function() { var keyIds = []; @@ -197,7 +197,7 @@ Key.prototype.getKeyIds = function() { Key.prototype.getKeyPacket = function(keyIds) { var keys = this.getAllKeyPackets(); for (var i = 0; i < keys.length; i++) { - var keyId = keys[i].getKeyId(); + var keyId = keys[i].getKeyId(); for (var j = 0; j < keyIds.length; j++) { if (keyId.equals(keyIds[j])) { return keys[i]; @@ -284,7 +284,7 @@ Key.prototype.getSigningKeyPacket = function() { throw new Error('Need private key for signing'); } var primaryUser = this.getPrimaryUser(); - if (primaryUser && + if (primaryUser && isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate)) { return this.primaryKey; } @@ -342,7 +342,7 @@ Key.prototype.getEncryptionKeyPacket = function() { } // if no valid subkey for encryption, evaluate primary key var primaryUser = this.getPrimaryUser(); - if (primaryUser && + if (primaryUser && isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate)) { return this.primaryKey; } @@ -351,7 +351,7 @@ Key.prototype.getEncryptionKeyPacket = function() { /** * Decrypts all secret key and subkey packets - * @param {String} passphrase + * @param {String} passphrase * @return {Boolean} true if all key and subkey packets decrypted successfully */ Key.prototype.decrypt = function(passphrase) { @@ -370,14 +370,14 @@ Key.prototype.decrypt = function(passphrase) { /** * Decrypts specific key packets by key ID * @param {Array} keyIds - * @param {String} passphrase + * @param {String} passphrase * @return {Boolean} true if all key packets decrypted successfully */ Key.prototype.decryptKeyPacket = function(keyIds, passphrase) { if (this.isPrivate()) { var keys = this.getAllKeyPackets(); for (var i = 0; i < keys.length; i++) { - var keyId = keys[i].getKeyId(); + var keyId = keys[i].getKeyId(); for (var j = 0; j < keyIds.length; j++) { if (keyId.equals(keyIds[j])) { var success = keys[i].decrypt(passphrase); @@ -398,8 +398,8 @@ Key.prototype.decryptKeyPacket = function(keyIds, passphrase) { */ Key.prototype.verifyPrimaryKey = function() { // check revocation signature - if (this.revocationSignature && !this.revocationSignature.isExpired() && - (this.revocationSignature.verified || + if (this.revocationSignature && !this.revocationSignature.isExpired() && + (this.revocationSignature.verified || this.revocationSignature.verify(this.primaryKey, {key: this.primaryKey}))) { return enums.keyStatus.revoked; } @@ -641,8 +641,8 @@ User.prototype.isRevoked = function(certificate, primaryKey) { var that = this; return this.revocationCertifications.some(function(revCert) { return revCert.issuerKeyId.equals(certificate.issuerKeyId) && - !revCert.isExpired() && - (revCert.verified || + !revCert.isExpired() && + (revCert.verified || revCert.verify(primaryKey, {userid: that.userId || that.userAttribute, key: primaryKey})); }); } else { @@ -695,7 +695,7 @@ User.prototype.isValidSelfCertificate = function(primaryKey, selfCertificate) { * Verify User. Checks for existence of self signatures, revocation signatures * and validity of self signature * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet - * @return {module:enums.keyStatus} status of user + * @return {module:enums.keyStatus} status of user */ User.prototype.verify = function(primaryKey) { if (!this.selfCertifications) { @@ -707,7 +707,7 @@ User.prototype.verify = function(primaryKey) { status = enums.keyStatus.revoked; continue; } - if (!(this.selfCertifications[i].verified || + if (!(this.selfCertifications[i].verified || this.selfCertifications[i].verify(primaryKey, {userid: this.userId || this.userAttribute, key: primaryKey}))) { status = enums.keyStatus.invalid; continue; @@ -792,8 +792,8 @@ SubKey.prototype.isValidSigningKey = function(primaryKey) { */ SubKey.prototype.verify = function(primaryKey) { // check subkey revocation signature - if (this.revocationSignature && !this.revocationSignature.isExpired() && - (this.revocationSignature.verified || + if (this.revocationSignature && !this.revocationSignature.isExpired() && + (this.revocationSignature.verified || this.revocationSignature.verify(primaryKey, {key:primaryKey, bind: this.subKey}))) { return enums.keyStatus.revoked; } @@ -888,12 +888,12 @@ function readArmored(armoredText) { result.keys.push(newKey); } catch (e) { result.err = result.err || []; - result.err.push(e); + result.err.push(e); } } } catch (e) { result.err = result.err || []; - result.err.push(e); + result.err.push(e); } return result; } @@ -910,7 +910,9 @@ function readArmored(armoredText) { * @return {module:key~Key} * @static */ -function generate(options) { +function generate(options, callback) { + var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket; + options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { @@ -921,74 +923,90 @@ function generate(options) { options.unlocked = true; } - var packetlist = new packet.List(); + packetlist = new packet.List(); - var secretKeyPacket = new packet.SecretKey(); + secretKeyPacket = new packet.SecretKey(); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - secretKeyPacket.generate(options.numBits); - if (options.passphrase) { - secretKeyPacket.encrypt(options.passphrase); + secretKeyPacket.generate(options.numBits, onSecretKeyGenerated); + + function onSecretKeyGenerated(err) { + if (err) { + callback(err); + return; + } + + if (options.passphrase) { + secretKeyPacket.encrypt(options.passphrase); + } + + userIdPacket = new packet.Userid(); + userIdPacket.read(options.userId); + + dataToSign = {}; + dataToSign.userid = userIdPacket; + dataToSign.key = secretKeyPacket; + signaturePacket = new packet.Signature(); + signaturePacket.signatureType = enums.signature.cert_generic; + signaturePacket.publicKeyAlgorithm = options.keyType; + signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; + signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; + signaturePacket.preferredSymmetricAlgorithms = []; + signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256); + signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192); + signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128); + signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5); + signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes); + signaturePacket.preferredHashAlgorithms = []; + signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256); + signaturePacket.preferredHashAlgorithms.push(enums.hash.sha1); + signaturePacket.preferredHashAlgorithms.push(enums.hash.sha512); + signaturePacket.preferredCompressionAlgorithms = []; + signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zlib); + signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zip); + if (config.integrity_protect) { + signaturePacket.features = []; + signaturePacket.features.push(1); // Modification Detection + } + signaturePacket.sign(secretKeyPacket, dataToSign); + + secretSubkeyPacket = new packet.SecretSubkey(); + secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); + secretSubkeyPacket.generate(options.numBits, onSecretSubkeyGenerated); } - var userIdPacket = new packet.Userid(); - userIdPacket.read(options.userId); + function onSecretSubkeyGenerated(err) { + if (err) { + callback(err); + return; + } - var dataToSign = {}; - dataToSign.userid = userIdPacket; - dataToSign.key = secretKeyPacket; - var signaturePacket = new packet.Signature(); - signaturePacket.signatureType = enums.signature.cert_generic; - signaturePacket.publicKeyAlgorithm = options.keyType; - signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; - signaturePacket.preferredSymmetricAlgorithms = []; - signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256); - signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192); - signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128); - signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5); - signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes); - signaturePacket.preferredHashAlgorithms = []; - signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256); - signaturePacket.preferredHashAlgorithms.push(enums.hash.sha1); - signaturePacket.preferredHashAlgorithms.push(enums.hash.sha512); - signaturePacket.preferredCompressionAlgorithms = []; - signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zlib); - signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zip); - if (config.integrity_protect) { - signaturePacket.features = []; - signaturePacket.features.push(1); // Modification Detection + if (options.passphrase) { + secretSubkeyPacket.encrypt(options.passphrase); + } + + dataToSign = {}; + dataToSign.key = secretKeyPacket; + dataToSign.bind = secretSubkeyPacket; + subkeySignaturePacket = new packet.Signature(); + subkeySignaturePacket.signatureType = enums.signature.subkey_binding; + subkeySignaturePacket.publicKeyAlgorithm = options.keyType; + subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm; + subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage]; + subkeySignaturePacket.sign(secretKeyPacket, dataToSign); + + packetlist.push(secretKeyPacket); + packetlist.push(userIdPacket); + packetlist.push(signaturePacket); + packetlist.push(secretSubkeyPacket); + packetlist.push(subkeySignaturePacket); + + if (!options.unlocked) { + secretKeyPacket.clearPrivateMPIs(); + secretSubkeyPacket.clearPrivateMPIs(); + } + + callback(null, new Key(packetlist)); } - 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.key = secretKeyPacket; - dataToSign.bind = secretSubkeyPacket; - var subkeySignaturePacket = new packet.Signature(); - subkeySignaturePacket.signatureType = enums.signature.subkey_binding; - subkeySignaturePacket.publicKeyAlgorithm = options.keyType; - subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage]; - subkeySignaturePacket.sign(secretKeyPacket, dataToSign); - - packetlist.push(secretKeyPacket); - packetlist.push(userIdPacket); - packetlist.push(signaturePacket); - packetlist.push(secretSubkeyPacket); - packetlist.push(subkeySignaturePacket); - - if (!options.unlocked) { - secretKeyPacket.clearPrivateMPIs(); - secretSubkeyPacket.clearPrivateMPIs(); - } - - return new Key(packetlist); } /** diff --git a/src/openpgp.js b/src/openpgp.js index 8f96f96d..c2ecb783 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -224,24 +224,28 @@ function verifyClearSignedMessage(publicKeys, msg, callback) { * @param {String} options.userId assumes already in form of "User Name " * @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 {function} callback (optional) callback(error, result) for async style + * @param {function} callback(error, result) The required callback * @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String} * @static */ function generateKeyPair(options, callback) { - if (useWorker(callback)) { + if (!callback) { + throw new Error('Callback must be set for key generation!'); + } + + // use web worker if web crypto apis are not supported + if (!useWebCrypto() && useWorker(callback)) { asyncProxy.generateKeyPair(options, callback); return; } - return execute(function() { + key.generate(options, function(err, newKey) { var result = {}; - var newKey = key.generate(options); result.key = newKey; result.privateKeyArmored = newKey.armor(); result.publicKeyArmored = newKey.toPublic().armor(); - return result; - }, callback); + callback(null, result); + }); } // @@ -257,12 +261,20 @@ function useWorker(callback) { } 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; } +/** + * Check for WebCrypto support + */ +function useWebCrypto() { + return typeof window !== 'undefined' && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle); +} + /** * Command pattern that handles async calls gracefully */ diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index e2b1c0d6..21addf8f 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -1,6 +1,6 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either @@ -10,7 +10,7 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -270,9 +270,19 @@ SecretKey.prototype.decrypt = function (passphrase) { return true; }; -SecretKey.prototype.generate = function (bits) { - this.mpi = crypto.generateMpi(this.algorithm, bits); - this.isDecrypted = true; +SecretKey.prototype.generate = function (bits, callback) { + var self = this; + + crypto.generateMpi(self.algorithm, bits, function(err, mpi) { + if (err) { + callback(err); + return; + } + + self.mpi = mpi; + self.isDecrypted = true; + callback(); + }); }; /** diff --git a/src/worker/worker.js b/src/worker/worker.js index 336712d7..680eddf7 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -1,6 +1,6 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either @@ -10,7 +10,7 @@ // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -25,7 +25,7 @@ var MAX_SIZE_RANDOM_BUFFER = 60000; window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); onmessage = function (event) { - var data = null, + var data = null, err = null, msg = event.data, correct = false; @@ -101,7 +101,7 @@ onmessage = function (event) { } msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); 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); } catch (e) { err = e.message; @@ -110,12 +110,20 @@ onmessage = function (event) { break; case 'generate-key-pair': try { - data = window.openpgp.generateKeyPair(msg.options); - data.key = data.key.toPacketlist(); + window.openpgp.generateKeyPair(msg.options, function(error, data) { + if (error) { + err = error.message; + response({event: 'method-return', data: data, err: err}); + return; + } + + data.key = data.key.toPacketlist(); + response({event: 'method-return', data: data, err: err}); + }); } catch (e) { err = e.message; + response({event: 'method-return', data: data, err: err}); } - response({event: 'method-return', data: data, err: err}); break; case 'decrypt-key': try { diff --git a/test/general/basic.js b/test/general/basic.js index c954b42b..ffb26499 100644 --- a/test/general/basic.js +++ b/test/general/basic.js @@ -8,57 +8,61 @@ var chai = require('chai'), describe('Basic', function() { describe("Key generation/encryption/decryption", function() { - var testHelper = function(passphrase, userid, message) { - var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); - expect(key).to.exist; - expect(key.key).to.exist; - expect(key.privateKeyArmored).to.exist; - expect(key.publicKeyArmored).to.exist; + var testHelper = function(passphrase, userid, message, done) { + var opt = {numBits: 512, userId: userid, passphrase: passphrase}; + openpgp.generateKeyPair(opt, function(err, key) { + expect(err).to.not.exist; - var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message; + expect(key).to.exist; + expect(key.key).to.exist; + expect(key.privateKeyArmored).to.exist; + expect(key.publicKeyArmored).to.exist; - var privKeys = openpgp.key.readArmored(key.privateKeyArmored); - var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); + var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message; - expect(privKeys).to.exist; - expect(privKeys.err).to.not.exist; - expect(privKeys.keys).to.have.length(1); + var privKeys = openpgp.key.readArmored(key.privateKeyArmored); + var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + expect(privKeys).to.exist; + expect(privKeys.err).to.not.exist; + expect(privKeys.keys).to.have.length(1); - expect(privKey).to.exist; - expect(pubKey).to.exist; + var privKey = privKeys.keys[0]; + var pubKey = publicKeys.keys[0]; - var success = privKey.decrypt(passphrase); + expect(privKey).to.exist; + expect(pubKey).to.exist; - expect(success).to.be.true; + var success = privKey.decrypt(passphrase); - var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); + expect(success).to.be.true; - expect(encrypted).to.exist; + var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); - var msg = openpgp.message.readArmored(encrypted); + expect(encrypted).to.exist; - expect(msg).to.exist; + var msg = openpgp.message.readArmored(encrypted); - var keyids = msg.getEncryptionKeyIds(); + expect(msg).to.exist; - expect(keyids).to.exist; + var keyids = msg.getEncryptionKeyIds(); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); - expect(decrypted).to.exist; - expect(decrypted.signatures[0].valid).to.be.true; - expect(decrypted.text).to.equal(message); + expect(keyids).to.exist; + + var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + expect(decrypted).to.exist; + expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.text).to.equal(message); + + done(); + }); }; it('ASCII Text', function (done) { - testHelper('password', 'Test McTestington ', 'hello world'); - done(); + testHelper('password', 'Test McTestington ', 'hello world', done); }); it('Unicode Text', function (done) { - testHelper('●●●●', '♔♔♔♔ ', 'łäóć'); - done(); + testHelper('●●●●', '♔♔♔♔ ', 'łäóć', done); }); it('should fail to verify signature for wrong public key', function (done) { @@ -66,29 +70,35 @@ describe('Basic', function() { var passphrase = 'password'; var message = 'hello world'; - var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); + var opt = {numBits: 512, userId: userid, passphrase: passphrase}; + openpgp.generateKeyPair(opt, function(err, key) { + expect(err).to.not.exist; - var privKeys = openpgp.key.readArmored(key.privateKeyArmored); - var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); + var privKeys = openpgp.key.readArmored(key.privateKeyArmored); + var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + var privKey = privKeys.keys[0]; + var pubKey = publicKeys.keys[0]; - var success = privKey.decrypt(passphrase); + var success = privKey.decrypt(passphrase); - var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); + var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); - var msg = openpgp.message.readArmored(encrypted); - expect(msg).to.exist; + var msg = openpgp.message.readArmored(encrypted); + expect(msg).to.exist; - var anotherKey = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); - var anotherPubKey = openpgp.key.readArmored(anotherKey.publicKeyArmored).keys[0]; + openpgp.generateKeyPair(opt, function(err, anotherKey) { + expect(err).to.not.exist; - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [anotherPubKey], msg); - expect(decrypted).to.exist; - expect(decrypted.signatures[0].valid).to.be.null; - expect(decrypted.text).to.equal(message); - done(); + var anotherPubKey = openpgp.key.readArmored(anotherKey.publicKeyArmored).keys[0]; + + var decrypted = openpgp.decryptAndVerifyMessage(privKey, [anotherPubKey], msg); + expect(decrypted).to.exist; + expect(decrypted.signatures[0].valid).to.be.null; + expect(decrypted.text).to.equal(message); + done(); + }); + }); }); it('Performance test', function (done) { @@ -103,46 +113,49 @@ describe('Basic', function() { var userid = 'Test McTestington '; var passphrase = 'password'; - var key = openpgp.generateKeyPair({numBits: 512, userId: userid, passphrase: passphrase}); + var opt = {numBits: 512, userId: userid, passphrase: passphrase}; + openpgp.generateKeyPair(opt, function(err, key) { + expect(err).to.not.exist; - var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message; + var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message; - var privKeys = openpgp.key.readArmored(key.privateKeyArmored); - var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); + var privKeys = openpgp.key.readArmored(key.privateKeyArmored); + var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + var privKey = privKeys.keys[0]; + var pubKey = publicKeys.keys[0]; - var success = privKey.decrypt(passphrase); + var success = privKey.decrypt(passphrase); - if (console.profile) { - console.profile("encrypt/sign/verify/decrypt"); - } + if (console.profile) { + console.profile("encrypt/sign/verify/decrypt"); + } - // sign and encrypt - var msg, encrypted; - msg = openpgp.message.fromBinary(message); - msg = msg.sign([privKey]); - msg = msg.encrypt([pubKey]); - encrypted = openpgp.armor.encode(openpgp.enums.armor.message, msg.packets.write()); + // sign and encrypt + var msg, encrypted; + msg = openpgp.message.fromBinary(message); + msg = msg.sign([privKey]); + msg = msg.encrypt([pubKey]); + encrypted = openpgp.armor.encode(openpgp.enums.armor.message, msg.packets.write()); - if (console.profileEnd) { - console.profileEnd(); - } + if (console.profileEnd) { + console.profileEnd(); + } - msg = openpgp.message.readArmored(encrypted); + msg = openpgp.message.readArmored(encrypted); - var keyids = msg.getEncryptionKeyIds(); + var keyids = msg.getEncryptionKeyIds(); - expect(keyids).to.exist; + expect(keyids).to.exist; - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); - expect(decrypted).to.exist; - expect(decrypted.signatures[0].valid).to.be.true; - expect(decrypted.text).to.equal(message); + expect(decrypted).to.exist; + expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.text).to.equal(message); - done(); + done(); + }); }); }); diff --git a/test/general/key.js b/test/general/key.js index 6221ee35..f71b638d 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -137,7 +137,7 @@ describe('Key', function() { '=eoGb', '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); - var pub_sig_test = + var pub_sig_test = ['-----BEGIN PGP PUBLIC KEY BLOCK-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', @@ -618,7 +618,7 @@ var pgp_desktop_priv = 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) { // key flags var keyFlags = openpgp.enums.keyFlags; @@ -633,10 +633,14 @@ var pgp_desktop_priv = var compr = openpgp.enums.compression; 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 - } - var key = openpgp.generateKeyPair({numBits: 512, userId: 'test', passphrase: 'hello'}); - testPref(key.key); - testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]); + }; + var opt = {numBits: 512, userId: 'test', passphrase: 'hello'}; + openpgp.generateKeyPair(opt, function(err, key) { + expect(err).to.not.exist; + testPref(key.key); + testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]); + done(); + }); }); it('User attribute packet read & write', function() { @@ -653,11 +657,15 @@ var pgp_desktop_priv = expect(primUser.selfCertificate).to.be.an.instanceof(openpgp.packet.Signature); }); - it('Generated key is not unlocked by default', function() { - var key = openpgp.generateKeyPair({numBits: 512, userId: 'test', passphrase: '123'}); - var msg = openpgp.message.fromText('hello').encrypt([key.key]); - msg = msg.decrypt.bind(msg, key.key); - expect(msg).to.throw('Private key is not decrypted.'); + it('Generated key is not unlocked by default', function(done) { + var opt = {numBits: 512, userId: 'test', passphrase: '123'}; + openpgp.generateKeyPair(opt, function(err, key) { + expect(err).to.not.exist; + var msg = openpgp.message.fromText('hello').encrypt([key.key]); + msg = msg.decrypt.bind(msg, key.key); + expect(msg).to.throw('Private key is not decrypted.'); + done(); + }); }); }); diff --git a/test/general/packet.js b/test/general/packet.js index ecac10ff..4609370b 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -6,445 +6,452 @@ var chai = require('chai'), expect = chai.expect; describe("Packet", function() { - var armored_key = - '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - '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\n' + - 'YRlci72GHhlyos8YHA==\n' + - '=KXkj\n' + - '-----END PGP PRIVATE KEY BLOCK-----'; + var armored_key = + '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + '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\n' + + 'YRlci72GHhlyos8YHA==\n' + + '=KXkj\n' + + '-----END PGP PRIVATE KEY BLOCK-----'; it('Symmetrically encrypted packet', function(done) { - var message = new openpgp.packet.List(); + var message = new openpgp.packet.List(); - var literal = new openpgp.packet.Literal(); - literal.setText('Hello world'); - - var enc = new openpgp.packet.SymmetricallyEncrypted(); - message.push(enc); - enc.packets.push(literal); + var literal = new openpgp.packet.Literal(); + literal.setText('Hello world'); - var key = '12345678901234567890123456789012', - algo = 'aes256'; + var enc = new openpgp.packet.SymmetricallyEncrypted(); + message.push(enc); + enc.packets.push(literal); - enc.encrypt(algo, key); + var key = '12345678901234567890123456789012', + algo = 'aes256'; - var msg2 = new openpgp.packet.List(); - msg2.read(message.write()); + enc.encrypt(algo, key); - msg2[0].decrypt(algo, key); + var msg2 = new openpgp.packet.List(); + msg2.read(message.write()); + + msg2[0].decrypt(algo, key); expect(msg2[0].packets[0].data).to.equal(literal.data); done(); }); it('Sym. encrypted integrity protected packet', function(done) { - var key = '12345678901234567890123456789012', - algo = 'aes256'; + var key = '12345678901234567890123456789012', + algo = 'aes256'; - var literal = new openpgp.packet.Literal(), - enc = new openpgp.packet.SymEncryptedIntegrityProtected(), - msg = new openpgp.packet.List(); + var literal = new openpgp.packet.Literal(), + enc = new openpgp.packet.SymEncryptedIntegrityProtected(), + msg = new openpgp.packet.List(); - msg.push(enc); - literal.setText('Hello world!'); - enc.packets.push(literal); - enc.encrypt(algo, key); - - var msg2 = new openpgp.packet.List(); - msg2.read(msg.write()); + msg.push(enc); + literal.setText('Hello world!'); + enc.packets.push(literal); + enc.encrypt(algo, key); - msg2[0].decrypt(algo, key); + var msg2 = new openpgp.packet.List(); + msg2.read(msg.write()); + + msg2[0].decrypt(algo, key); expect(msg2[0].packets[0].data).to.equal(literal.data); done(); }); it('Sym encrypted session key with a compressed packet', function(done) { - var msg = - '-----BEGIN PGP MESSAGE-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - 'jA0ECQMCpo7I8WqsebTJ0koBmm6/oqdHXJU9aPe+Po+nk/k4/PZrLmlXwz2lhqBg\n' + - 'GAlY9rxVStLBrg0Hn+5gkhyHI9B85rM1BEYXQ8pP5CSFuTwbJ3O2s67dzQ==\n' + - '=VZ0/\n' + - '-----END PGP MESSAGE-----'; + var msg = + '-----BEGIN PGP MESSAGE-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'jA0ECQMCpo7I8WqsebTJ0koBmm6/oqdHXJU9aPe+Po+nk/k4/PZrLmlXwz2lhqBg\n' + + 'GAlY9rxVStLBrg0Hn+5gkhyHI9B85rM1BEYXQ8pP5CSFuTwbJ3O2s67dzQ==\n' + + '=VZ0/\n' + + '-----END PGP MESSAGE-----'; - var msgbytes = openpgp.armor.decode(msg).data; + var msgbytes = openpgp.armor.decode(msg).data; - var parsed = new openpgp.packet.List(); - parsed.read(msgbytes); + var parsed = new openpgp.packet.List(); + parsed.read(msgbytes); - parsed[0].decrypt('test'); + parsed[0].decrypt('test'); - var key = parsed[0].sessionKey; - parsed[1].decrypt(parsed[0].sessionKeyAlgorithm, key); - var compressed = parsed[1].packets[0]; + var key = parsed[0].sessionKey; + parsed[1].decrypt(parsed[0].sessionKeyAlgorithm, key); + var compressed = parsed[1].packets[0]; - var result = compressed.packets[0].data; + var result = compressed.packets[0].data; expect(result).to.equal('Hello world!\n'); done(); }); it('Public key encrypted symmetric key packet', function(done) { - var rsa = new openpgp.crypto.publicKey.rsa(), - mpi = rsa.generate(512, "10001") + var rsa = new openpgp.crypto.publicKey.rsa(); - var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; + rsa.generate(512, "10001", function(error, mpiGen) { + expect(error).to.not.exist; - mpi = mpi.map(function(k) { - var mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; - }); + var mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; - var enc = new openpgp.packet.PublicKeyEncryptedSessionKey(), - msg = new openpgp.packet.List(), - msg2 = new openpgp.packet.List(); + mpi = mpi.map(function(k) { + var mpi = new openpgp.MPI(); + mpi.fromBigInteger(k); + return mpi; + }); - enc.sessionKey = '12345678901234567890123456789012'; - enc.publicKeyAlgorithm = 'rsa_encrypt'; - enc.sessionKeyAlgorithm = 'aes256'; - enc.publicKeyId.bytes = '12345678'; - enc.encrypt({ mpi: mpi }); + var enc = new openpgp.packet.PublicKeyEncryptedSessionKey(), + msg = new openpgp.packet.List(), + msg2 = new openpgp.packet.List(); - msg.push(enc); + enc.sessionKey = '12345678901234567890123456789012'; + enc.publicKeyAlgorithm = 'rsa_encrypt'; + enc.sessionKeyAlgorithm = 'aes256'; + enc.publicKeyId.bytes = '12345678'; + enc.encrypt({ mpi: mpi }); - msg2.read(msg.write()); + msg.push(enc); - msg2[0].decrypt({ mpi: mpi }); + msg2.read(msg.write()); - expect(msg2[0].sessionKey).to.equal(enc.sessionKey); - expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); - done(); + msg2[0].decrypt({ mpi: mpi }); + + expect(msg2[0].sessionKey).to.equal(enc.sessionKey); + expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); + done(); + }); }); it('Secret key packet (reading, unencrpted)', function(done) { - var armored_key = - '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - 'lQHYBFF33iMBBAC9YfOYahJlWrVj2J1TjQiZLunWljI4G9e6ARTyD99nfOkV3swh\n' + - '0WaOse4Utj7BfTqdYcoezhCaQpuExUupKWZqmduBcwSmEBfNu1XyKcxlDQuuk0Vk\n' + - 'viGC3kFRce/cJaKVFSRU8V5zPgt6KQNv/wNz7ydEisaSoNbk51vQt5oGfwARAQAB\n' + - 'AAP5AVL8xWMuKgLj9g7/wftMH+jO7vhAxje2W3Y+8r8TnOSn0536lQvzl/eQyeLC\n' + - 'VK2k3+7+trgO7I4KuXCXZqgAbEi3niDYXDaCJ+8gdR9qvPM2gi9NM71TGXZvGE0w\n' + - 'X8gIZfqLTQWKm9TIS/3tdrth4nwhiye0ASychOboIiN6VIECAMbCQ4/noxGV6yTK\n' + - 'VezsGSz+iCMxz2lV270/Ac2C5WPk+OlxXloxUXeEkGIr6Xkmhhpceed2KL41UC8Y\n' + - 'w5ttGIECAPPsahniKGyqp9CHy6W0B83yhhcIbmLlaVG2ftKyUEDxIggzOlXuVrue\n' + - 'z9XRd6wFqwDd1QMFW0uUyHPDCIFPnv8CAJaDFSZutuWdWMt15NZXjfgRgfJuDrtv\n' + - 'E7yFY/p0el8lCihOT8WoHbTn1PbCYMzNBc0IhHaZKAtA2pjkE+wzz9ClP7QbR2Vv\n' + - 'cmdlIDxnZW9yZ2VAZXhhbXBsZS5jb20+iLkEEwECACMFAlF33iMCGwMHCwkIBwMC\n' + - 'AQYVCAIJCgsEFgIDAQIeAQIXgAAKCRBcqs36fwJCXRbvA/9LPiK6WFKcFoNBnLEJ\n' + - 'mS/CNkL8yTpkslpCP6+TwJMc8uXqwYl9/PW2+CwmzZjs6JsvTzMcR/ZbfZJuSW6Y\n' + - 'EsLNejsSpgcY9aiewGtE+53e5oKYnlmVMTWOPywciIgMvXlzdGhxcwqJ8u0hT+ug\n' + - '9CjcAfuX9yw85LwXtdGwNh7J8Q==\n' + - '=lKiS\n' + - '-----END PGP PRIVATE KEY BLOCK-----'; + var armored_key = + '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'lQHYBFF33iMBBAC9YfOYahJlWrVj2J1TjQiZLunWljI4G9e6ARTyD99nfOkV3swh\n' + + '0WaOse4Utj7BfTqdYcoezhCaQpuExUupKWZqmduBcwSmEBfNu1XyKcxlDQuuk0Vk\n' + + 'viGC3kFRce/cJaKVFSRU8V5zPgt6KQNv/wNz7ydEisaSoNbk51vQt5oGfwARAQAB\n' + + 'AAP5AVL8xWMuKgLj9g7/wftMH+jO7vhAxje2W3Y+8r8TnOSn0536lQvzl/eQyeLC\n' + + 'VK2k3+7+trgO7I4KuXCXZqgAbEi3niDYXDaCJ+8gdR9qvPM2gi9NM71TGXZvGE0w\n' + + 'X8gIZfqLTQWKm9TIS/3tdrth4nwhiye0ASychOboIiN6VIECAMbCQ4/noxGV6yTK\n' + + 'VezsGSz+iCMxz2lV270/Ac2C5WPk+OlxXloxUXeEkGIr6Xkmhhpceed2KL41UC8Y\n' + + 'w5ttGIECAPPsahniKGyqp9CHy6W0B83yhhcIbmLlaVG2ftKyUEDxIggzOlXuVrue\n' + + 'z9XRd6wFqwDd1QMFW0uUyHPDCIFPnv8CAJaDFSZutuWdWMt15NZXjfgRgfJuDrtv\n' + + 'E7yFY/p0el8lCihOT8WoHbTn1PbCYMzNBc0IhHaZKAtA2pjkE+wzz9ClP7QbR2Vv\n' + + 'cmdlIDxnZW9yZ2VAZXhhbXBsZS5jb20+iLkEEwECACMFAlF33iMCGwMHCwkIBwMC\n' + + 'AQYVCAIJCgsEFgIDAQIeAQIXgAAKCRBcqs36fwJCXRbvA/9LPiK6WFKcFoNBnLEJ\n' + + 'mS/CNkL8yTpkslpCP6+TwJMc8uXqwYl9/PW2+CwmzZjs6JsvTzMcR/ZbfZJuSW6Y\n' + + 'EsLNejsSpgcY9aiewGtE+53e5oKYnlmVMTWOPywciIgMvXlzdGhxcwqJ8u0hT+ug\n' + + '9CjcAfuX9yw85LwXtdGwNh7J8Q==\n' + + '=lKiS\n' + + '-----END PGP PRIVATE KEY BLOCK-----'; - var key = new openpgp.packet.List(); - key.read(openpgp.armor.decode(armored_key).data); - key = key[0]; + var key = new openpgp.packet.List(); + key.read(openpgp.armor.decode(armored_key).data); + key = key[0]; - var enc = new openpgp.packet.PublicKeyEncryptedSessionKey(), - secret = '12345678901234567890123456789012'; + var enc = new openpgp.packet.PublicKeyEncryptedSessionKey(), + secret = '12345678901234567890123456789012'; - enc.sessionKey = secret; - enc.publicKeyAlgorithm = 'rsa_encrypt'; - enc.sessionKeyAlgorithm = 'aes256'; - enc.publicKeyId.bytes = '12345678'; + enc.sessionKey = secret; + enc.publicKeyAlgorithm = 'rsa_encrypt'; + enc.sessionKeyAlgorithm = 'aes256'; + enc.publicKeyId.bytes = '12345678'; - enc.encrypt(key); + enc.encrypt(key); - enc.decrypt(key); + enc.decrypt(key); - expect(enc.sessionKey).to.equal(secret); + expect(enc.sessionKey).to.equal(secret); done(); }); it('Public key encrypted packet (reading, GPG)', function(done) { - var armored_key = - '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - '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\n' + - '=FSwA\n' + - '-----END PGP PRIVATE KEY BLOCK-----'; + var armored_key = + '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + '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\n' + + '=FSwA\n' + + '-----END PGP PRIVATE KEY BLOCK-----'; - var armored_msg = - '-----BEGIN PGP MESSAGE-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - 'hIwDFYET+7bfx/ABA/95Uc9942Tg8oqpO0vEu2eSKwPALM3a0DrVdAiFOIK/dJmZ\n' + - 'YrtPRw3EEwHZjl6CO9RD+95iE27tPbsICw1K43gofSV/wWsPO6vvs3eftQYHSxxa\n' + - 'IQbTPImiRaJ73Mf7iM3CNtQM4iUBsx1HnUGl+rtD0nz3fLm6i3CjwiNQWW42I9JH\n' + - 'AWv8EvvpxZ8X2ClFfSW3UVBoROHe9CAWHM/40nGutAZK8MIgmUI4xqkLFBbqqTyx\n' + - '/cDSC4Q+sv65UX4urbfc7uJuk1Cpj54=\n' + - '=iSaK\n' + - '-----END PGP MESSAGE-----'; + var armored_msg = + '-----BEGIN PGP MESSAGE-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'hIwDFYET+7bfx/ABA/95Uc9942Tg8oqpO0vEu2eSKwPALM3a0DrVdAiFOIK/dJmZ\n' + + 'YrtPRw3EEwHZjl6CO9RD+95iE27tPbsICw1K43gofSV/wWsPO6vvs3eftQYHSxxa\n' + + 'IQbTPImiRaJ73Mf7iM3CNtQM4iUBsx1HnUGl+rtD0nz3fLm6i3CjwiNQWW42I9JH\n' + + 'AWv8EvvpxZ8X2ClFfSW3UVBoROHe9CAWHM/40nGutAZK8MIgmUI4xqkLFBbqqTyx\n' + + '/cDSC4Q+sv65UX4urbfc7uJuk1Cpj54=\n' + + '=iSaK\n' + + '-----END PGP MESSAGE-----'; + var key = new openpgp.packet.List(); + key.read(openpgp.armor.decode(armored_key).data); + key = key[3]; - var key = new openpgp.packet.List(); - key.read(openpgp.armor.decode(armored_key).data); - key = key[3]; + var msg = new openpgp.packet.List(); + msg.read(openpgp.armor.decode(armored_msg).data); - var msg = new openpgp.packet.List(); - msg.read(openpgp.armor.decode(armored_msg).data); + msg[0].decrypt(key); + msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - msg[0].decrypt(key); - msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - - var text = msg[1].packets[0].packets[0].data; + var text = msg[1].packets[0].packets[0].data; expect(text).to.equal('Hello world!'); done(); }); it('Sym encrypted session key reading/writing', function(done) { - var passphrase = 'hello', - algo = 'aes256'; + var passphrase = 'hello', + algo = 'aes256'; - var literal = new openpgp.packet.Literal(), - key_enc = new openpgp.packet.SymEncryptedSessionKey(), - enc = new openpgp.packet.SymEncryptedIntegrityProtected(), - msg = new openpgp.packet.List(); + var literal = new openpgp.packet.Literal(), + key_enc = new openpgp.packet.SymEncryptedSessionKey(), + enc = new openpgp.packet.SymEncryptedIntegrityProtected(), + msg = new openpgp.packet.List(); - msg.push(key_enc); - msg.push(enc); + msg.push(key_enc); + msg.push(enc); - key_enc.sessionKeyAlgorithm = algo; - key_enc.decrypt(passphrase); + key_enc.sessionKeyAlgorithm = algo; + key_enc.decrypt(passphrase); - var key = key_enc.sessionKey; + var key = key_enc.sessionKey; - literal.setText('Hello world!'); - enc.packets.push(literal); - enc.encrypt(algo, key); + literal.setText('Hello world!'); + enc.packets.push(literal); + enc.encrypt(algo, key); - var msg2 = new openpgp.packet.List(); - msg2.read(msg.write()); + var msg2 = new openpgp.packet.List(); + msg2.read(msg.write()); - msg2[0].decrypt(passphrase); - var key2 = msg2[0].sessionKey; - msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2); + msg2[0].decrypt(passphrase); + var key2 = msg2[0].sessionKey; + msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2); expect(msg2[1].packets[0].data).to.equal(literal.data); done(); }); it('Secret key encryption/decryption test', function(done) { - var armored_msg = - '-----BEGIN PGP MESSAGE-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - 'hIwD95D9aHS5fxEBA/98CwH54XZmwobOmHUcvWcDDQysBEC4uf7wASiGcRbejDaO\n' + - 'aJqcrK/3k8sBQMO7yOhvrCRqqpGDqnmx7IaaKLnZS7nYAZoHEsK9UyG0hDa8Cfbo\n' + - 'CP4xZVcgIvIfAW/in1LeT2td0QcQNbeewBmPea+vQEEvRgIP10tlE7MK8Ay48dJH\n' + - 'AagMgNYg7MBUjpuOCVrjM1pWja8uzbULfYhTq3IJ8H3QhbdT+k9khY9f0aJPEeYi\n' + - 'dVv6DK9uviMGc/DsVCw5K8lQRLlkcHc=\n' + - '=pR+C\n' + - '-----END PGP MESSAGE-----'; + var armored_msg = + '-----BEGIN PGP MESSAGE-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'hIwD95D9aHS5fxEBA/98CwH54XZmwobOmHUcvWcDDQysBEC4uf7wASiGcRbejDaO\n' + + 'aJqcrK/3k8sBQMO7yOhvrCRqqpGDqnmx7IaaKLnZS7nYAZoHEsK9UyG0hDa8Cfbo\n' + + 'CP4xZVcgIvIfAW/in1LeT2td0QcQNbeewBmPea+vQEEvRgIP10tlE7MK8Ay48dJH\n' + + 'AagMgNYg7MBUjpuOCVrjM1pWja8uzbULfYhTq3IJ8H3QhbdT+k9khY9f0aJPEeYi\n' + + 'dVv6DK9uviMGc/DsVCw5K8lQRLlkcHc=\n' + + '=pR+C\n' + + '-----END PGP MESSAGE-----'; - var key = new openpgp.packet.List(); - key.read(openpgp.armor.decode(armored_key).data); - key = key[3]; - key.decrypt('test'); + var key = new openpgp.packet.List(); + key.read(openpgp.armor.decode(armored_key).data); + key = key[3]; + key.decrypt('test'); - var msg = new openpgp.packet.List(); - msg.read(openpgp.armor.decode(armored_msg).data); + var msg = new openpgp.packet.List(); + msg.read(openpgp.armor.decode(armored_msg).data); - msg[0].decrypt(key); - msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); + msg[0].decrypt(key); + msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - var text = msg[1].packets[0].packets[0].data; + var text = msg[1].packets[0].packets[0].data; expect(text).to.equal('Hello world!'); done(); }); it('Secret key reading with signature verification.', function(done) { - var key = new openpgp.packet.List(); - key.read(openpgp.armor.decode(armored_key).data); + var key = new openpgp.packet.List(); + key.read(openpgp.armor.decode(armored_key).data); - var verified = key[2].verify(key[0], - { - userid: key[1], - key: key[0] - }); + var verified = key[2].verify(key[0], + { + userid: key[1], + key: key[0] + }); - verified = verified && key[4].verify(key[0], - { - key: key[0], - bind: key[3] - }); + verified = verified && key[4].verify(key[0], + { + key: key[0], + bind: key[3] + }); expect(verified).to.be.true; done(); }); it('Reading a signed, encrypted message.', function(done) { - var armored_msg = - '-----BEGIN PGP MESSAGE-----\n' + - 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + - '\n' + - 'hIwD95D9aHS5fxEBA/4/X4myvH+jB1HYNeZvdK+WsBNDMfLsBGOf205Rxr3vSob/\n' + - 'A09boj8/9lFaipqu+AEdQKEjCB8sZ+OY0WiQPEPpuhG+mVqDqEiPFkdpcqNtS0VV\n' + - 'pwqplHo6QnH2MHfxprZHYuwcEC9ynJCxJ6kSCD8Xs99h+PjxNNw7NhMjkF+N69LA\n' + - 'NwGPtbLx2/r2nR4gO8gV92A2RQCOwPP7ZV+6fXgWIs+mhyCHFP3xUP5DaFCNM8mo\n' + - 'PN97i659ucxF6IbOoK56FEaUbOPTD6xdyhWamxKfMsIb0UJgVUNhGaq+VlvOJxaB\n' + - 'iRcnY5UxsypKgtqfcKIseb21MIo4vcNdogyxBIDlAO472Zfxn0udzr6W2aQ77+NK\n' + - 'FE1O0kCXS+DTFOYYVD7X8rXGSglQsdXJmHd89sdYFQkO7D7bOLdRJuXgdgH2czCs\n' + - 'UBGuHZzsGbTdyKvpVBuS3rnyHHBk6oCnsm1Nl7eLs64VkZUxjEUbq5pb4dlr1pw2\n' + - 'ztpmpAnRcmM=\n' + - '=htrB\n' + - '-----END PGP MESSAGE-----' + var armored_msg = + '-----BEGIN PGP MESSAGE-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'hIwD95D9aHS5fxEBA/4/X4myvH+jB1HYNeZvdK+WsBNDMfLsBGOf205Rxr3vSob/\n' + + 'A09boj8/9lFaipqu+AEdQKEjCB8sZ+OY0WiQPEPpuhG+mVqDqEiPFkdpcqNtS0VV\n' + + 'pwqplHo6QnH2MHfxprZHYuwcEC9ynJCxJ6kSCD8Xs99h+PjxNNw7NhMjkF+N69LA\n' + + 'NwGPtbLx2/r2nR4gO8gV92A2RQCOwPP7ZV+6fXgWIs+mhyCHFP3xUP5DaFCNM8mo\n' + + 'PN97i659ucxF6IbOoK56FEaUbOPTD6xdyhWamxKfMsIb0UJgVUNhGaq+VlvOJxaB\n' + + 'iRcnY5UxsypKgtqfcKIseb21MIo4vcNdogyxBIDlAO472Zfxn0udzr6W2aQ77+NK\n' + + 'FE1O0kCXS+DTFOYYVD7X8rXGSglQsdXJmHd89sdYFQkO7D7bOLdRJuXgdgH2czCs\n' + + 'UBGuHZzsGbTdyKvpVBuS3rnyHHBk6oCnsm1Nl7eLs64VkZUxjEUbq5pb4dlr1pw2\n' + + 'ztpmpAnRcmM=\n' + + '=htrB\n' + + '-----END PGP MESSAGE-----' - var key = new openpgp.packet.List(); - key.read(openpgp.armor.decode(armored_key).data); - key[3].decrypt('test') + var key = new openpgp.packet.List(); + key.read(openpgp.armor.decode(armored_key).data); + key[3].decrypt('test') - var msg = new openpgp.packet.List(); - msg.read(openpgp.armor.decode(armored_msg).data); + var msg = new openpgp.packet.List(); + msg.read(openpgp.armor.decode(armored_msg).data); - msg[0].decrypt(key[3]); - msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); + msg[0].decrypt(key[3]); + msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - var payload = msg[1].packets[0].packets + var payload = msg[1].packets[0].packets - var verified = payload[2].verify(key[0], payload[1]); + var verified = payload[2].verify(key[0], payload[1]); expect(verified).to.be.true; done(); }); it('Writing and encryption of a secret key packet.', function(done) { - var key = new openpgp.packet.List(); - key.push(new openpgp.packet.SecretKey); + var key = new openpgp.packet.List(); + key.push(new openpgp.packet.SecretKey); - var rsa = new openpgp.crypto.publicKey.rsa(), - mpi = rsa.generate(512, "10001") + var rsa = new openpgp.crypto.publicKey.rsa(); + rsa.generate(512, "10001", function(err, mipGen) { + expect(err).to.not.exist; - var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; + var mpi = [mipGen.n, mipGen.ee, mipGen.d, mipGen.p, mipGen.q, mipGen.u]; - mpi = mpi.map(function(k) { - var mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; - }); + mpi = mpi.map(function(k) { + var mpi = new openpgp.MPI(); + mpi.fromBigInteger(k); + return mpi; + }); - key[0].mpi = mpi; + key[0].mpi = mpi; - key[0].encrypt('hello'); + key[0].encrypt('hello'); - var raw = key.write(); + var raw = key.write(); - var key2 = new openpgp.packet.List(); - key2.read(raw); - key2[0].decrypt('hello'); - - expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString()); - done(); + var key2 = new openpgp.packet.List(); + key2.read(raw); + key2[0].decrypt('hello'); + + expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString()); + 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, - mpi = rsa.generate(512, "10001") + var rsa = new openpgp.crypto.publicKey.rsa; - var mpi = [mpi.n, mpi.ee, mpi.d, mpi.p, mpi.q, mpi.u]; + rsa.generate(512, "10001", function(err, mpiGen) { + expect(err).to.not.exist; - mpi = mpi.map(function(k) { - var mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; - }); + var mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; - key.mpi = mpi; + mpi = mpi.map(function(k) { + var mpi = new openpgp.MPI(); + mpi.fromBigInteger(k); + return mpi; + }); - var signed = new openpgp.packet.List(), - literal = new openpgp.packet.Literal(), - signature = new openpgp.packet.Signature(); + key.mpi = mpi; - literal.setText('Hello world'); + var signed = new openpgp.packet.List(), + literal = new openpgp.packet.Literal(), + signature = new openpgp.packet.Signature(); - signature.hashAlgorithm = 'sha256'; - signature.publicKeyAlgorithm = 'rsa_sign'; - signature.signatureType = 'binary'; + literal.setText('Hello world'); - signature.sign(key, literal); + signature.hashAlgorithm = 'sha256'; + signature.publicKeyAlgorithm = 'rsa_sign'; + signature.signatureType = 'binary'; - signed.push(literal); - signed.push(signature); + signature.sign(key, literal); - var raw = signed.write(); + signed.push(literal); + signed.push(signature); - var signed2 = new openpgp.packet.List(); - signed2.read(raw); + var raw = signed.write(); - var verified = signed2[1].verify(key, signed2[0]); + var signed2 = new openpgp.packet.List(); + signed2.read(raw); - expect(verified).to.be.true; - done(); + var verified = signed2[1].verify(key, signed2[0]); + + expect(verified).to.be.true; + done(); + }); }); }); diff --git a/test/general/signature.js b/test/general/signature.js index e4b7ebeb..7e05bcdd 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -312,7 +312,7 @@ describe("Signature", function() { }); it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', function(done) { - var signedArmor = + var signedArmor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', @@ -335,7 +335,7 @@ describe("Signature", function() { }); it('Verify V3 signature. Hash: MD5. PK: RSA. Signature Type: 0x01 (text document)', function(done) { - var signedArmor = + var signedArmor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', @@ -358,7 +358,7 @@ describe("Signature", function() { }); it('Verify signature of signed and encrypted message from GPG2 with openpgp.decryptAndVerifyMessage', function(done) { - var msg_armor = + var msg_armor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', @@ -393,7 +393,7 @@ describe("Signature", function() { }); it('Verify signature of signed and encrypted message from PGP 10.3.0 with openpgp.decryptAndVerifyMessage', function(done) { - var msg_armor = + var msg_armor = [ '-----BEGIN PGP MESSAGE-----', 'Version: Encryption Desktop 10.3.0 (Build 9307)', 'Charset: utf-8', @@ -429,7 +429,7 @@ describe("Signature", function() { }); it('Verify signed message with two one pass signatures', function(done) { - var msg_armor = + var msg_armor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', '', @@ -470,7 +470,7 @@ describe("Signature", function() { }); it('Verify cleartext signed message with two signatures with openpgp.verifyClearSignedMessage', function(done) { - var msg_armor = + var msg_armor = [ '-----BEGIN PGP SIGNED MESSAGE-----', 'Hash: SHA256', '', @@ -523,7 +523,7 @@ describe("Signature", function() { var clearSignedArmor = openpgp.signClearMessage([privKey], plaintext); var csMsg = openpgp.cleartext.readArmored(clearSignedArmor); - + var cleartextSig = openpgp.verifyClearSignedMessage([pubKey], csMsg); expect(cleartextSig).to.exist; @@ -636,13 +636,19 @@ describe("Signature", function() { expect(result[0].valid).to.be.true; }); - it('Sign message with key without password', function() { - var key = openpgp.generateKeyPair({numBits: 512, userId: 'ABC', passphrase: null}).key; + it('Sign message with key without password', function(done) { + var opt = {numBits: 512, userId: 'ABC', passphrase: null}; + openpgp.generateKeyPair(opt, function(err, gen) { + expect(err).to.not.exist; - var message = openpgp.message.fromText('hello world'); - message = message.sign([key]); + var key = gen.key; - expect(message).to.exist; + var message = openpgp.message.fromText('hello world'); + message = message.sign([key]); + + expect(message).to.exist; + done(); + }); }); }); diff --git a/test/worker/api.js b/test/worker/api.js index 43efaea3..cba322f5 100644 --- a/test/worker/api.js +++ b/test/worker/api.js @@ -427,14 +427,6 @@ describe('High level API', function() { }); }); - it('Generate 1024-bit RSA/RSA key sync', function () { - var key = openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington ', 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() { From 85d21999714a43dcdc3be37d7d62586f769e98af Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 30 Sep 2014 19:31:12 +0200 Subject: [PATCH 04/19] Activate web crypto api (still fails tests) * Remove api support for safari * Fix error handling --- src/crypto/public_key/rsa.js | 16 +++++++++------- src/openpgp.js | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index f1222bd2..4b5d2923 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -139,9 +139,7 @@ function RSA() { // Native RSA keygen using Web Crypto // - if (false && typeof window !== 'undefined' && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle)) { - var subtle = window.crypto.subtle || window.crypto.webkitSubtle; - + if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { var keyGenOpt = { name: 'RSASSA-PKCS1-v1_5', modulusLength: B, // the specified keysize in bits @@ -153,17 +151,21 @@ function RSA() { var extractable = true; // make generated key extractable - subtle.generateKey(keyGenOpt, extractable, ['sign', 'verify']) - .then(onGenerated, callback) - .then(onExported, callback); + window.crypto.subtle.generateKey(keyGenOpt, extractable, ['sign', 'verify']) + .then(onGenerated, onError) + .then(onExported, onError); return; } + function onError() { + callback(new Error('Generating key failed!')); + } + function onGenerated(key) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 - return subtle.exportKey('jwk', key.privateKey); + return window.crypto.subtle.exportKey('jwk', key.privateKey); } function onExported(jwk) { diff --git a/src/openpgp.js b/src/openpgp.js index c2ecb783..24e9a026 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -272,7 +272,7 @@ function useWorker(callback) { * Check for WebCrypto support */ function useWebCrypto() { - return typeof window !== 'undefined' && window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle); + return typeof window !== 'undefined' && window.crypto && window.crypto.subtle; } /** From 7c2cf89589e54b4ee00692514a31f49ef82e3c74 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Tue, 30 Sep 2014 21:57:46 +0200 Subject: [PATCH 05/19] Fix decoding of JWK. Webcrypto works. --- src/crypto/public_key/rsa.js | 21 +++++++++++++-------- src/openpgp.js | 5 +++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 4b5d2923..d7877d1b 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -140,12 +140,15 @@ function RSA() { // if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { + var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent + var Eunit8 = new Uint8Array(Euint32.buffer); // get bytes of exponent + var keyGenOpt = { name: 'RSASSA-PKCS1-v1_5', modulusLength: B, // the specified keysize in bits - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // Equivalent to 65537, TODO: use provided argument E + publicExponent: Eunit8.subarray(0, 3), // take three bytes (max 65537) hash: { - name: 'SHA-256' // not required for actual RSA keys, but for crypto api 'sign' and 'verifiy' + name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verifiy' } }; @@ -171,15 +174,17 @@ function RSA() { function onExported(jwk) { // map JWK parameters to local BigInteger type system var key = new keyObject(); - key.n = new BigInteger(util.hexstrdump(base64(jwk.n)), 16); + key.n = toBigInteger(jwk.n); key.ee = new BigInteger(E, 16); - key.d = new BigInteger(util.hexstrdump(base64(jwk.d)), 16); - key.p = new BigInteger(util.hexstrdump(base64(jwk.p)), 16); - key.q = new BigInteger(util.hexstrdump(base64(jwk.q)), 16); + key.d = toBigInteger(jwk.d); + key.p = toBigInteger(jwk.p); + key.q = toBigInteger(jwk.q); key.u = key.p.modInverse(key.q); - function base64(base64url) { - return base64url.replace(/-/g, '+').replace(/_/g, '/'); + function toBigInteger(base64url) { + var base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); + var hex = util.hexstrdump(atob(base64)); + return new BigInteger(hex, 16); } callback(null, key); diff --git a/src/openpgp.js b/src/openpgp.js index 24e9a026..9d3e4141 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -240,6 +240,11 @@ function generateKeyPair(options, callback) { } key.generate(options, function(err, newKey) { + if (err) { + callback(err); + return; + } + var result = {}; result.key = newKey; result.privateKeyArmored = newKey.armor(); From 0af12b81a38828070d6dd920e6abafe90d13011f Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 07:40:02 +0200 Subject: [PATCH 06/19] Fix typo --- src/crypto/public_key/rsa.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index d7877d1b..e306e5e5 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -141,14 +141,14 @@ function RSA() { if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent - var Eunit8 = new Uint8Array(Euint32.buffer); // get bytes 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: Eunit8.subarray(0, 3), // take three bytes (max 65537) + 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 'verifiy' + name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; From e6f66b9039d5b31aee492773b2c187765cdbd862 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 08:40:26 +0200 Subject: [PATCH 07/19] Cleanup promise error handling --- src/crypto/public_key/rsa.js | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index e306e5e5..0ffb8b4d 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -152,26 +152,19 @@ function RSA() { } }; - var extractable = true; // make generated key extractable - - window.crypto.subtle.generateKey(keyGenOpt, extractable, ['sign', 'verify']) - .then(onGenerated, onError) - .then(onExported, onError); + var gen = window.crypto.subtle.generateKey(keyGenOpt, true, ['sign', 'verify']); + gen.then(exportKey).then(decodeKey).catch(onError); return; } - function onError() { - callback(new Error('Generating key failed!')); - } - - function onGenerated(key) { + function exportKey(key) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 return window.crypto.subtle.exportKey('jwk', key.privateKey); } - function onExported(jwk) { + function decodeKey(jwk) { // map JWK parameters to local BigInteger type system var key = new keyObject(); key.n = toBigInteger(jwk.n); @@ -190,6 +183,10 @@ function RSA() { callback(null, key); } + function onError() { + callback(new Error('Generating key failed!')); + } + // // JS code // From 72cb1cfc494408fe6e71394249c036a92823a9bd Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 11:17:48 +0200 Subject: [PATCH 08/19] Make WebCrypto optional with config.useWebCrypto --- src/crypto/public_key/rsa.js | 7 ++++--- src/openpgp.js | 10 ++-------- src/util.js | 27 +++++++++++++++++++-------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 0ffb8b4d..05da060e 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -134,12 +134,13 @@ function RSA() { // Generate a new random private key B bits long, using public expt E function generate(B, E, callback) { + var webCrypto = util.getWebCrypto(); // // Native RSA keygen using Web Crypto // - if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { + if (webCrypto) { var Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent var Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent @@ -152,7 +153,7 @@ function RSA() { } }; - var gen = window.crypto.subtle.generateKey(keyGenOpt, true, ['sign', 'verify']); + var gen = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); gen.then(exportKey).then(decodeKey).catch(onError); return; @@ -161,7 +162,7 @@ function RSA() { function exportKey(key) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 - return window.crypto.subtle.exportKey('jwk', key.privateKey); + return webCrypto.exportKey('jwk', key.privateKey); } function decodeKey(jwk) { diff --git a/src/openpgp.js b/src/openpgp.js index 9d3e4141..d71cfb92 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -38,6 +38,7 @@ var armor = require('./encoding/armor.js'), message = require('./message.js'), cleartext = require('./cleartext.js'), key = require('./key.js'), + util = require('./util'), AsyncProxy = require('./worker/async_proxy.js'); var asyncProxy; // instance of the asyncproxy @@ -234,7 +235,7 @@ function generateKeyPair(options, callback) { } // use web worker if web crypto apis are not supported - if (!useWebCrypto() && useWorker(callback)) { + if (!util.getWebCrypto() && useWorker(callback)) { asyncProxy.generateKeyPair(options, callback); return; } @@ -273,13 +274,6 @@ function useWorker(callback) { return true; } -/** - * Check for WebCrypto support - */ -function useWebCrypto() { - return typeof window !== 'undefined' && window.crypto && window.crypto.subtle; -} - /** * Command pattern that handles async calls gracefully */ diff --git a/src/util.js b/src/util.js index 8ec8a74f..8b16a133 100644 --- a/src/util.js +++ b/src/util.js @@ -1,16 +1,16 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -193,7 +193,7 @@ module.exports = { }, /** - * Convert a Uint8Array to a string. This currently functions + * Convert a Uint8Array to a string. This currently functions * the same as bin2str. * @function module:util.Uint8Array2str * @param {Uint8Array} bin An array of (binary) integers to convert @@ -228,7 +228,7 @@ module.exports = { }, /** - * Helper function to print a debug message. Debug + * Helper function to print a debug message. Debug * messages are only printed if * @link module:config/config.debug is set to true. * @param {String} str String of the debug message @@ -240,7 +240,7 @@ module.exports = { }, /** - * Helper function to print a debug message. Debug + * Helper function to print a debug message. Debug * messages are only printed if * @link module:config/config.debug is set to true. * Different than print_debug because will call hexstrdump iff necessary. @@ -265,9 +265,9 @@ module.exports = { /** * Shifting a string to n bits right * @param {String} value The string to shift - * @param {Integer} bitcount Amount of bits to shift (MUST be smaller + * @param {Integer} bitcount Amount of bits to shift (MUST be smaller * than 9) - * @return {String} Resulting string. + * @return {String} Resulting string. */ shiftRight: function (value, bitcount) { var temp = util.str2bin(value); @@ -305,5 +305,16 @@ module.exports = { return "SHA224"; } return "unknown"; + }, + + getWebCrypto: function() { + if (config.useWebCrypto === false) { + // make web crypto optional + return; + } + + if (typeof window !== 'undefined' && window.crypto && window.crypto.subtle) { + return window.crypto.subtle; + } } }; From 5d07ee1eb122a3394c5f0e2fcae403e3f2a0542a Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 11:23:34 +0200 Subject: [PATCH 09/19] Add documentation to getWebCrypto --- src/util.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/util.js b/src/util.js index 8b16a133..97612f5d 100644 --- a/src/util.js +++ b/src/util.js @@ -307,6 +307,11 @@ module.exports = { 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 From 0ac58356b52367a1f70d9046453eebd4c7998eec Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 13:13:09 +0200 Subject: [PATCH 10/19] Refactor keygen to use promises (Work in progress) --- src/crypto/crypto.js | 15 ++---- src/crypto/public_key/rsa.js | 90 ++++++++++++++++++------------------ src/key.js | 49 +++++++++----------- src/openpgp.js | 23 ++++----- src/packet/secret_key.js | 12 ++--- test/worker/api.js | 4 +- 6 files changed, 84 insertions(+), 109 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 6ae3f64e..4667291d 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -178,19 +178,14 @@ module.exports = { } }, - generateMpi: function(algo, bits, callback) { + generateMpi: function(algo, bits) { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': case 'rsa_sign': //remember "publicKey" refers to the crypto/public_key dir var rsa = new publicKey.rsa(); - rsa.generate(bits, "10001", function(err, keyObject) { - if (err) { - callback(err); - return; - } - + return rsa.generate(bits, "10001").then(function(keyObject) { var output = []; output.push(keyObject.n); output.push(keyObject.ee); @@ -198,12 +193,10 @@ module.exports = { output.push(keyObject.p); output.push(keyObject.q); output.push(keyObject.u); - - callback(null, mapResult(output)); + return mapResult(output); }); - break; default: - callback(new Error('Unsupported algorithm for key generation.')); + throw new Error('Unsupported algorithm for key generation.'); } function mapResult(result) { diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 05da060e..71e68479 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -133,8 +133,9 @@ function RSA() { // Generate a new random private key B bits long, using public expt E - function generate(B, E, callback) { + function generate(B, E) { var webCrypto = util.getWebCrypto(); + var promise; // // Native RSA keygen using Web Crypto @@ -143,7 +144,6 @@ function RSA() { 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 @@ -152,11 +152,8 @@ function RSA() { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - - var gen = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); - gen.then(exportKey).then(decodeKey).catch(onError); - - return; + promise = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); + return promise.then(exportKey).then(decodeKey); } function exportKey(key) { @@ -181,52 +178,53 @@ function RSA() { return new BigInteger(hex, 16); } - callback(null, key); - } - - function onError() { - callback(new Error('Generating key failed!')); + return key; } // // JS code // - var key = new keyObject(); - var rng = new SecureRandom(); - var qs = B >> 1; - key.e = parseInt(E, 16); - key.ee = new BigInteger(E, 16); - for (;;) { - for (;;) { - key.p = new BigInteger(B - qs, 1, rng); - if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) - break; - } - for (;;) { - key.q = new BigInteger(qs, 1, rng); - if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) - break; - } - if (key.p.compareTo(key.q) <= 0) { - var t = key.p; - key.p = key.q; - key.q = t; - } - var p1 = key.p.subtract(BigInteger.ONE); - var q1 = key.q.subtract(BigInteger.ONE); - var phi = p1.multiply(q1); - if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) { - key.n = key.p.multiply(key.q); - key.d = key.ee.modInverse(phi); - key.dmp1 = key.d.mod(p1); - key.dmq1 = key.d.mod(q1); - key.u = key.p.modInverse(key.q); - break; - } - } + promise = new Promise(function(resolve) { + var key = new keyObject(); + var rng = new SecureRandom(); + var qs = B >> 1; + key.e = parseInt(E, 16); + key.ee = new BigInteger(E, 16); - callback(null, key); + for (;;) { + for (;;) { + key.p = new BigInteger(B - qs, 1, rng); + if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) + break; + } + for (;;) { + key.q = new BigInteger(qs, 1, rng); + if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) + break; + } + if (key.p.compareTo(key.q) <= 0) { + var t = key.p; + key.p = key.q; + key.q = t; + } + var p1 = key.p.subtract(BigInteger.ONE); + var q1 = key.q.subtract(BigInteger.ONE); + var phi = p1.multiply(q1); + if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) { + key.n = key.p.multiply(key.q); + key.d = key.ee.modInverse(phi); + key.dmp1 = key.d.mod(p1); + key.dmq1 = key.d.mod(q1); + key.u = key.p.modInverse(key.q); + break; + } + } + + resolve(key); + }); + + return promise; } this.encrypt = encrypt; diff --git a/src/key.js b/src/key.js index 2a730375..d51ee41b 100644 --- a/src/key.js +++ b/src/key.js @@ -5,7 +5,7 @@ // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -910,7 +910,7 @@ function readArmored(armoredText) { * @return {module:key~Key} * @static */ -function generate(options, callback) { +function generate(options) { var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket; options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; @@ -923,22 +923,32 @@ function generate(options, callback) { options.unlocked = true; } - packetlist = new packet.List(); + // generate + var genSecretKey = generateSecretKey(); + var genSecretSubkey = generateSecretSubkey(); + return Promise.all([genSecretKey, genSecretSubkey]).then(wrapKeyObject); - secretKeyPacket = new packet.SecretKey(); - secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - secretKeyPacket.generate(options.numBits, onSecretKeyGenerated); + function generateSecretKey() { + secretKeyPacket = new packet.SecretKey(); + secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); + return secretKeyPacket.generate(options.numBits); + } - function onSecretKeyGenerated(err) { - if (err) { - callback(err); - return; - } + 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); @@ -969,21 +979,6 @@ function generate(options, callback) { } signaturePacket.sign(secretKeyPacket, dataToSign); - secretSubkeyPacket = new packet.SecretSubkey(); - secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - secretSubkeyPacket.generate(options.numBits, onSecretSubkeyGenerated); - } - - function onSecretSubkeyGenerated(err) { - if (err) { - callback(err); - return; - } - - if (options.passphrase) { - secretSubkeyPacket.encrypt(options.passphrase); - } - dataToSign = {}; dataToSign.key = secretKeyPacket; dataToSign.bind = secretSubkeyPacket; @@ -1005,7 +1000,7 @@ function generate(options, callback) { secretSubkeyPacket.clearPrivateMPIs(); } - callback(null, new Key(packetlist)); + return new Key(packetlist); } } diff --git a/src/openpgp.js b/src/openpgp.js index d71cfb92..f742c84d 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -229,28 +229,23 @@ function verifyClearSignedMessage(publicKeys, msg, callback) { * @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String} * @static */ -function generateKeyPair(options, callback) { - if (!callback) { - throw new Error('Callback must be set for key generation!'); - } - +function generateKeyPair(options) { // use web worker if web crypto apis are not supported - if (!util.getWebCrypto() && useWorker(callback)) { - asyncProxy.generateKeyPair(options, callback); + if (!util.getWebCrypto() && useWorker()) { + asyncProxy.generateKeyPair(options); return; } - key.generate(options, function(err, newKey) { - if (err) { - callback(err); - return; - } - + return key.generate(options).then(function(newKey) { var result = {}; result.key = newKey; result.privateKeyArmored = newKey.armor(); result.publicKeyArmored = newKey.toPublic().armor(); - callback(null, result); + return result; + + }).catch(function(err) { + console.error(err.stack); + throw new Error('Error generating keypair!'); }); } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 21addf8f..f2c9cab0 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -5,7 +5,7 @@ // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -270,18 +270,12 @@ SecretKey.prototype.decrypt = function (passphrase) { return true; }; -SecretKey.prototype.generate = function (bits, callback) { +SecretKey.prototype.generate = function (bits) { var self = this; - crypto.generateMpi(self.algorithm, bits, function(err, mpi) { - if (err) { - callback(err); - return; - } - + return crypto.generateMpi(self.algorithm, bits).then(function(mpi) { self.mpi = mpi; self.isDecrypted = true; - callback(); }); }; diff --git a/test/worker/api.js b/test/worker/api.js index cba322f5..23a0389e 100644 --- a/test/worker/api.js +++ b/test/worker/api.js @@ -417,8 +417,8 @@ describe('High level API', function() { describe('Key generation', function() { it('Generate 1024-bit RSA/RSA key async', function (done) { - openpgp.generateKeyPair({numBits: 1024, userId: 'Test McTestington ', passphrase: 'hello world'}, function(err, data) { - expect(err).to.not.exist; + var opt = {numBits: 1024, userId: 'Test McTestington ', passphrase: 'hello world'}; + openpgp.generateKeyPair(opt).then(function(data) { expect(data).to.exist; expect(data.publicKeyArmored).to.match(/^-----BEGIN PGP PUBLIC/); expect(data.privateKeyArmored).to.match(/^-----BEGIN PGP PRIVATE/); From 7f2573c77db86235cdc8f7c8e5b6e03153905583 Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 19:12:39 +0200 Subject: [PATCH 11/19] Refactor complete public api to use promises --- .jshintrc | 39 ++++++++++ Gruntfile.js | 4 +- package.json | 1 + src/cleartext.js | 8 +- src/enums.js | 2 + src/index.js | 1 + src/key.js | 2 + src/message.js | 14 ++-- src/openpgp.js | 117 +++++++++++++--------------- src/util.js | 2 + test/general/basic.js | 158 +++++++++++++++++++++----------------- test/general/key.js | 6 +- test/general/packet.js | 58 ++++++-------- test/general/signature.js | 74 +++++++++--------- test/unittests.html | 5 +- test/unittests.js | 5 ++ 16 files changed, 278 insertions(+), 218 deletions(-) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..de1188fd --- /dev/null +++ b/.jshintrc @@ -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": { + } +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index b76f4d8b..b7f0f3b3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -38,7 +38,7 @@ module.exports = function(grunt) { 'test/lib/unittests-bundle.js': [ './test/unittests.js' ] }, options: { - external: [ 'openpgp', 'crypto', 'node-localstorage' ] + external: [ 'openpgp', 'crypto', 'node-localstorage', 'es6-promise'] } } }, @@ -116,7 +116,7 @@ module.exports = function(grunt) { expand: true, flatten: true, cwd: 'node_modules/', - src: ['mocha/mocha.css', 'mocha/mocha.js', 'chai/chai.js'], + src: ['mocha/mocha.css', 'mocha/mocha.js', 'chai/chai.js', 'es6-promise/dist/promise*.js'], dest: 'test/lib/' } }, diff --git a/package.json b/package.json index 8c31da29..95f59b26 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "devDependencies": { "browserify": "~2.35", "chai": "~1.8.1", + "es6-promise": "1.0.0", "grunt": "~0.4.2", "grunt-browserify": "~1.2.11", "grunt-cli": "~0.1.13", diff --git a/src/cleartext.js b/src/cleartext.js index dd18724d..04ab4122 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -1,16 +1,16 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -23,6 +23,8 @@ * @module cleartext */ +'use strict'; + var config = require('./config'), packet = require('./packet'), enums = require('./enums.js'), diff --git a/src/enums.js b/src/enums.js index f1eb61b1..d0866a71 100644 --- a/src/enums.js +++ b/src/enums.js @@ -1,3 +1,5 @@ +'use strict'; + /** * @module enums */ diff --git a/src/index.js b/src/index.js index 4a2a0972..e995aa6d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,4 @@ +'use strict'; module.exports = require('./openpgp.js'); /** diff --git a/src/key.js b/src/key.js index d51ee41b..7193301d 100644 --- a/src/key.js +++ b/src/key.js @@ -23,6 +23,8 @@ * @module key */ +'use strict'; + var packet = require('./packet'), enums = require('./enums.js'), armor = require('./encoding/armor.js'), diff --git a/src/message.js b/src/message.js index 42cf995e..fd37972e 100644 --- a/src/message.js +++ b/src/message.js @@ -1,16 +1,16 @@ // GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH -// +// // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. -// +// // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -24,6 +24,8 @@ * @module message */ +'use strict'; + var packet = require('./packet'), enums = require('./enums.js'), armor = require('./encoding/armor.js'), @@ -83,7 +85,7 @@ Message.prototype.getSigningKeyIds = function() { /** * Decrypt the message - * @param {module:key~Key} privateKey private key with decrypted secret data + * @param {module:key~Key} privateKey private key with decrypted secret data * @return {Array} new message with decrypted content */ Message.prototype.decrypt = function(privateKey) { @@ -186,7 +188,7 @@ Message.prototype.sign = function(privateKeys) { var literalDataPacket = this.packets.findPacket(enums.packet.literal); if (!literalDataPacket) throw new Error('No literal data packet to sign.'); - + var literalFormat = enums.write(enums.literal, literalDataPacket.format); var signatureType = literalFormat == enums.literal.binary ? enums.signature.binary : enums.signature.text; @@ -206,7 +208,7 @@ Message.prototype.sign = function(privateKeys) { } packetlist.push(literalDataPacket); - + for (i = privateKeys.length - 1; i >= 0; i--) { var signaturePacket = new packet.Signature(); signaturePacket.signatureType = signatureType; diff --git a/src/openpgp.js b/src/openpgp.js index f742c84d..1921891a 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -31,10 +31,10 @@ * @module openpgp */ +'use strict'; + var armor = require('./encoding/armor.js'), - packet = require('./packet'), enums = require('./enums.js'), - config = require('./config'), message = require('./message.js'), cleartext = require('./cleartext.js'), key = require('./key.js'), @@ -55,18 +55,16 @@ function initWorker(path) { * Encrypts message text with keys * @param {(Array|module:key~Key)} keys array of keys or single key, used to encrypt the message * @param {String} text message as native JavaScript string - * @param {function} callback (optional) callback(error, result) for async style * @return {String} encrypted ASCII armored message * @static */ -function encryptMessage(keys, text, callback) { +function encryptMessage(keys, text) { if (!keys.length) { keys = [keys]; } - if (useWorker(callback)) { - asyncProxy.encryptMessage(keys, text, callback); - return; + if (useWorker()) { + return asyncProxy.encryptMessage(keys, text); } return execute(function() { @@ -75,7 +73,7 @@ function encryptMessage(keys, text, callback) { msg = msg.encrypt(keys); armored = armor.encode(enums.armor.message, msg.packets.write()); return armored; - }, callback); + }); } /** @@ -83,18 +81,16 @@ function encryptMessage(keys, text, callback) { * @param {(Array|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 {String} text message as native JavaScript string - * @param {function} callback (optional) callback(error, result) for async style * @return {String} encrypted ASCII armored message * @static */ -function signAndEncryptMessage(publicKeys, privateKey, text, callback) { +function signAndEncryptMessage(publicKeys, privateKey, text) { if (!publicKeys.length) { publicKeys = [publicKeys]; } - if (useWorker(callback)) { - asyncProxy.signAndEncryptMessage(publicKeys, privateKey, text, callback); - return; + if (useWorker()) { + return asyncProxy.signAndEncryptMessage(publicKeys, privateKey, text); } return execute(function() { @@ -104,28 +100,26 @@ function signAndEncryptMessage(publicKeys, privateKey, text, callback) { msg = msg.encrypt(publicKeys); armored = armor.encode(enums.armor.message, msg.packets.write()); return armored; - }, callback); + }); } /** * Decrypts message * @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 {function} callback (optional) callback(error, result) for async style * @return {(String|null)} decrypted message as as native JavaScript string * or null if no literal data found * @static */ -function decryptMessage(privateKey, msg, callback) { - if (useWorker(callback)) { - asyncProxy.decryptMessage(privateKey, msg, callback); - return; +function decryptMessage(privateKey, msg) { + if (useWorker()) { + return asyncProxy.decryptMessage(privateKey, msg); } return execute(function() { msg = msg.decrypt(privateKey); return msg.getText(); - }, callback); + }); } /** @@ -133,20 +127,18 @@ function decryptMessage(privateKey, msg, callback) { * @param {module:key~Key} privateKey private key with decrypted secret key data * @param {(Array|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 {function} callback (optional) callback(error, result) for async style * @return {{text: String, signatures: Array<{keyid: module:type/keyid, valid: Boolean}>}} * decrypted message as as native JavaScript string * with verified signatures or null if no literal data found * @static */ -function decryptAndVerifyMessage(privateKey, publicKeys, msg, callback) { +function decryptAndVerifyMessage(privateKey, publicKeys, msg) { if (!publicKeys.length) { publicKeys = [publicKeys]; } - if (useWorker(callback)) { - asyncProxy.decryptAndVerifyMessage(privateKey, publicKeys, msg, callback); - return; + if (useWorker()) { + return asyncProxy.decryptAndVerifyMessage(privateKey, publicKeys, msg); } return execute(function() { @@ -158,51 +150,47 @@ function decryptAndVerifyMessage(privateKey, publicKeys, msg, callback) { return result; } return null; - }, callback); + }); } /** * Signs a cleartext message * @param {(Array|module:key~Key)} privateKeys array of keys or single key with decrypted secret key data to sign cleartext * @param {String} text cleartext - * @param {function} callback (optional) callback(error, result) for async style * @return {String} ASCII armored message * @static */ -function signClearMessage(privateKeys, text, callback) { +function signClearMessage(privateKeys, text) { if (!privateKeys.length) { privateKeys = [privateKeys]; } - if (useWorker(callback)) { - asyncProxy.signClearMessage(privateKeys, text, callback); - return; + if (useWorker()) { + return asyncProxy.signClearMessage(privateKeys, text); } return execute(function() { var cleartextMessage = new cleartext.CleartextMessage(text); cleartextMessage.sign(privateKeys); return cleartextMessage.armor(); - }, callback); + }); } /** * Verifies signatures of cleartext signed message * @param {(Array|module:key~Key)} publicKeys array of keys or single key, to verify 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}>}} * cleartext with status of verified signatures * @static */ -function verifyClearSignedMessage(publicKeys, msg, callback) { +function verifyClearSignedMessage(publicKeys, msg) { if (!publicKeys.length) { publicKeys = [publicKeys]; } - if (useWorker(callback)) { - asyncProxy.verifyClearSignedMessage(publicKeys, msg, callback); - return; + if (useWorker()) { + return asyncProxy.verifyClearSignedMessage(publicKeys, msg); } return execute(function() { @@ -213,7 +201,7 @@ function verifyClearSignedMessage(publicKeys, msg, callback) { result.text = msg.getText(); result.signatures = msg.verify(publicKeys); return result; - }, callback); + }); } /** @@ -225,15 +213,13 @@ function verifyClearSignedMessage(publicKeys, msg, callback) { * @param {String} options.userId assumes already in form of "User Name " * @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 {function} callback(error, result) The required callback * @return {Object} {key: module:key~Key, privateKeyArmored: String, publicKeyArmored: String} * @static */ function generateKeyPair(options) { // use web worker if web crypto apis are not supported if (!util.getWebCrypto() && useWorker()) { - asyncProxy.generateKeyPair(options); - return; + return asyncProxy.generateKeyPair(options); } return key.generate(options).then(function(newKey) { @@ -243,10 +229,7 @@ function generateKeyPair(options) { result.publicKeyArmored = newKey.toPublic().armor(); return result; - }).catch(function(err) { - console.error(err.stack); - throw new Error('Error generating keypair!'); - }); + }).catch(onError.bind(null, 'Error generating keypair!')); } // @@ -270,28 +253,36 @@ function useWorker(callback) { } /** - * 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) { - var result; +function execute(cmd, errMsg) { + // wrap the sync cmd in a promise + var promise = new Promise(function(resolve) { + var result = cmd(); + resolve(result); + }); - try { - result = cmd(); - } catch (err) { - if (callback) { - callback(err); - return; - } + // handler error globally + promise.catch(onError.bind(null, errMsg)); - throw err; - } + return promise; +} - if (callback) { - callback(null, result); - return; - } - - return result; +/** + * Global error handler that logs the stack trace and + * 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 + */ +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; diff --git a/src/util.js b/src/util.js index 97612f5d..b4a56fd8 100644 --- a/src/util.js +++ b/src/util.js @@ -21,6 +21,8 @@ * @module util */ +'use strict'; + var config = require('./config'); module.exports = { diff --git a/test/general/basic.js b/test/general/basic.js index ffb26499..a1e4c6d4 100644 --- a/test/general/basic.js +++ b/test/general/basic.js @@ -10,16 +10,16 @@ describe('Basic', function() { describe("Key generation/encryption/decryption", function() { var testHelper = function(passphrase, userid, message, done) { var opt = {numBits: 512, userId: userid, passphrase: passphrase}; - openpgp.generateKeyPair(opt, function(err, key) { - expect(err).to.not.exist; + var privKey; + var pubKey; + + openpgp.generateKeyPair(opt).then(function(key) { expect(key).to.exist; expect(key.key).to.exist; expect(key.privateKeyArmored).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 publicKeys = openpgp.key.readArmored(key.publicKeyArmored); @@ -27,29 +27,28 @@ describe('Basic', function() { expect(privKeys.err).to.not.exist; expect(privKeys.keys).to.have.length(1); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + privKey = privKeys.keys[0]; + pubKey = publicKeys.keys[0]; expect(privKey).to.exist; expect(pubKey).to.exist; var success = privKey.decrypt(passphrase); - 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; - var msg = openpgp.message.readArmored(encrypted); - expect(msg).to.exist; - var keyids = msg.getEncryptionKeyIds(); - 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.signatures[0].valid).to.be.true; expect(decrypted.text).to.equal(message); @@ -69,35 +68,44 @@ describe('Basic', function() { var userid = 'Test McTestington '; var passphrase = 'password'; var message = 'hello world'; + var privKey; + var pubKey; + var msg; var opt = {numBits: 512, userId: userid, passphrase: passphrase}; - openpgp.generateKeyPair(opt, function(err, key) { - expect(err).to.not.exist; + openpgp.generateKeyPair(opt).then(function(key) { var privKeys = openpgp.key.readArmored(key.privateKeyArmored); var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + privKey = privKeys.keys[0]; + pubKey = publicKeys.keys[0]; 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; - openpgp.generateKeyPair(opt, function(err, anotherKey) { - expect(err).to.not.exist; + return openpgp.generateKeyPair(opt); - var anotherPubKey = openpgp.key.readArmored(anotherKey.publicKeyArmored).keys[0]; + }).then(function(anotherKey) { + + var anotherPubKey = openpgp.key.readArmored(anotherKey.publicKeyArmored).keys[0]; + + return openpgp.decryptAndVerifyMessage(privKey, [anotherPubKey], msg); + + }).then(function(decrypted) { + + expect(decrypted).to.exist; + expect(decrypted.signatures[0].valid).to.be.null; + expect(decrypted.text).to.equal(message); + done(); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [anotherPubKey], msg); - expect(decrypted).to.exist; - expect(decrypted.signatures[0].valid).to.be.null; - expect(decrypted.text).to.equal(message); - done(); - }); }); }); @@ -105,7 +113,9 @@ describe('Basic', function() { // init test data function randomString(length, chars) { 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; } var message = randomString(1024*1024*3, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'); @@ -114,10 +124,7 @@ describe('Basic', function() { var passphrase = 'password'; var opt = {numBits: 512, userId: userid, passphrase: passphrase}; - openpgp.generateKeyPair(opt, function(err, key) { - expect(err).to.not.exist; - - var info = '\npassphrase: ' + passphrase + '\n' + 'userid: ' + userid + '\n' + 'message: ' + message; + openpgp.generateKeyPair(opt).then(function(key) { var privKeys = openpgp.key.readArmored(key.privateKeyArmored); var publicKeys = openpgp.key.readArmored(key.publicKeyArmored); @@ -126,6 +133,7 @@ describe('Basic', function() { var pubKey = publicKeys.keys[0]; var success = privKey.decrypt(passphrase); + expect(success).to.be.true; if (console.profile) { console.profile("encrypt/sign/verify/decrypt"); @@ -148,13 +156,15 @@ describe('Basic', function() { 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.signatures[0].valid).to.be.true; expect(decrypted.text).to.equal(message); - done(); + }); }); }); @@ -240,30 +250,33 @@ describe('Basic', function() { 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; - message = openpgp.message.readArmored(encrypted); + message = openpgp.message.readArmored(encrypted); - expect(message).to.exist; + expect(message).to.exist; - var privKeys = openpgp.key.readArmored(priv_key); + var privKeys = openpgp.key.readArmored(priv_key); - expect(privKeys).to.exist; - expect(privKeys.err).to.not.exist; - expect(privKeys.keys).to.have.length(1); + expect(privKeys).to.exist; + expect(privKeys.err).to.not.exist; + expect(privKeys.keys).to.have.length(1); - privKey = privKeys.keys[0]; + privKey = privKeys.keys[0]; - expect(privKey).to.exist; + expect(privKey).to.exist; - // get key IDs the message is encrypted for - keyids = message.getEncryptionKeyIds(); + // get key IDs the message is encrypted for + keyids = message.getEncryptionKeyIds(); + + expect(keyids).to.exist; + expect(keyids).to.have.length(1); + done(); + + }); - expect(keyids).to.exist; - expect(keyids).to.have.length(1); - done(); }); it('Decrypting key packet with wrong password returns false', function (done) { @@ -274,15 +287,11 @@ describe('Basic', function() { done(); }); - var decrypted, error; - it('Calling decryptMessage with not decrypted key packet leads to exception', function (done) { - function exceptionTest() { - decrypted = openpgp.decryptMessage(privKey, message); - } - - expect(exceptionTest).to.throw(Error); - done(); + openpgp.decryptMessage(privKey, message).catch(function(error) { + expect(error).to.exist; + done(); + }); }); it('Decrypting key packet with correct password returns true', function (done) { @@ -293,16 +302,23 @@ describe('Basic', function() { }); it('Encrypt plain text and afterwards decrypt leads to same result', function (done) { - decrypted = openpgp.decryptMessage(privKey, message); - expect(decrypted).to.exist; - expect(decrypted).to.equal(plaintext); - done(); + openpgp.decryptMessage(privKey, message).then(function(decrypted) { + expect(decrypted).to.exist; + expect(decrypted).to.equal(plaintext); + done(); + }); }); - it('Decrypt message 2x', function() { - decrypted = openpgp.decryptMessage(privKey, message); - var decrypted2 = openpgp.decryptMessage(privKey, message); - expect(decrypted).to.equal(decrypted2); + it('Decrypt message 2x', function(done) { + var decrypted1; + + openpgp.decryptMessage(privKey, message).then(function(decrypted) { + decrypted1 = decrypted; + return openpgp.decryptMessage(privKey, message); + }).then(function(decrypted2) { + expect(decrypted1).to.equal(decrypted2); + done(); + }); }); }); @@ -360,15 +376,17 @@ describe('Basic', function() { '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'); it('Decrypt message', function (done) { - var privKey, message, decrypted; + var privKey, message; privKey = openpgp.key.readArmored(priv_key).keys[0]; privKey.decrypt('1234'); message = openpgp.message.readArmored(pgp_msg); - decrypted = openpgp.decryptMessage(privKey, message); - expect(decrypted).to.equal('hello 3des\n'); - done(); + openpgp.decryptMessage(privKey, message).then(function(decrypted) { + expect(decrypted).to.equal('hello 3des\n'); + done(); + }); + }); }); diff --git a/test/general/key.js b/test/general/key.js index f71b638d..75e4a8f6 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -635,8 +635,7 @@ var pgp_desktop_priv = expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection }; var opt = {numBits: 512, userId: 'test', passphrase: 'hello'}; - openpgp.generateKeyPair(opt, function(err, key) { - expect(err).to.not.exist; + openpgp.generateKeyPair(opt).then(function(key) { testPref(key.key); testPref(openpgp.key.readArmored(key.publicKeyArmored).keys[0]); done(); @@ -659,8 +658,7 @@ var pgp_desktop_priv = it('Generated key is not unlocked by default', function(done) { var opt = {numBits: 512, userId: 'test', passphrase: '123'}; - openpgp.generateKeyPair(opt, function(err, key) { - expect(err).to.not.exist; + openpgp.generateKeyPair(opt).then(function(key) { var msg = openpgp.message.fromText('hello').encrypt([key.key]); msg = msg.decrypt.bind(msg, key.key); expect(msg).to.throw('Private key is not decrypted.'); diff --git a/test/general/packet.js b/test/general/packet.js index 4609370b..09c7b374 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -119,15 +119,13 @@ describe("Packet", function() { it('Public key encrypted symmetric key packet', function(done) { var rsa = new openpgp.crypto.publicKey.rsa(); - rsa.generate(512, "10001", function(error, mpiGen) { - expect(error).to.not.exist; + 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) { - var mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + var mpi = new openpgp.MPI(); + mpi.fromBigInteger(k); + return mpi; }); var enc = new openpgp.packet.PublicKeyEncryptedSessionKey(), @@ -380,50 +378,44 @@ describe("Packet", function() { it('Writing and encryption of a secret key packet.', function(done) { 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(); - rsa.generate(512, "10001", function(err, mipGen) { - expect(err).to.not.exist; + 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) { + var mpi = new openpgp.MPI(); + mpi.fromBigInteger(k); + return mpi; + }); - var mpi = [mipGen.n, mipGen.ee, mipGen.d, mipGen.p, mipGen.q, mipGen.u]; + key[0].mpi = mpi; - mpi = mpi.map(function(k) { - var mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; - }); + key[0].encrypt('hello'); - key[0].mpi = mpi; + var raw = key.write(); - key[0].encrypt('hello'); + var key2 = new openpgp.packet.List(); + key2.read(raw); + key2[0].decrypt('hello'); - var raw = key.write(); - - var key2 = new openpgp.packet.List(); - key2.read(raw); - key2[0].decrypt('hello'); - - expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString()); - done(); + expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString()); + done(); }); }); it('Writing and verification of a signature packet.', function(done) { var key = new openpgp.packet.SecretKey(); - var rsa = new openpgp.crypto.publicKey.rsa; - - rsa.generate(512, "10001", function(err, mpiGen) { - expect(err).to.not.exist; + var rsa = new openpgp.crypto.publicKey.rsa(); + 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) { - var mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + var mpi = new openpgp.MPI(); + mpi.fromBigInteger(k); + return mpi; }); key.mpi = mpi; diff --git a/test/general/signature.js b/test/general/signature.js index 7e05bcdd..466a1036 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -262,10 +262,11 @@ describe("Signature", function() { var pub_key = openpgp.key.readArmored(pub_key_arm1).keys[0]; var msg = openpgp.message.readArmored(msg_arm1); priv_key.decrypt("abcd"); - var decrypted = openpgp.decryptAndVerifyMessage(priv_key, [pub_key], msg); - expect(decrypted).to.exist; - expect(decrypted.signatures[0].valid).to.be.true; - done(); + openpgp.decryptAndVerifyMessage(priv_key, [pub_key], msg).then(function(decrypted) { + expect(decrypted).to.exist; + expect(decrypted.signatures[0].valid).to.be.true; + done(); + }); }); it('Testing GnuPG stripped-key extensions', function(done) { @@ -383,13 +384,13 @@ describe("Signature", function() { var keyids = esMsg.getEncryptionKeyIds(); privKey.decryptKeyPacket(keyids, 'hello world'); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], esMsg); - - expect(decrypted).to.exist; - expect(decrypted.text).to.equal(plaintext); - expect(decrypted.signatures).to.have.length(1); - expect(decrypted.signatures[0].valid).to.be.true; - done(); + openpgp.decryptAndVerifyMessage(privKey, [pubKey], esMsg).then(function(decrypted) { + expect(decrypted).to.exist; + expect(decrypted.text).to.equal(plaintext); + expect(decrypted.signatures).to.have.length(1); + expect(decrypted.signatures[0].valid).to.be.true; + done(); + }); }); it('Verify signature of signed and encrypted message from PGP 10.3.0 with openpgp.decryptAndVerifyMessage', function(done) { @@ -419,13 +420,14 @@ describe("Signature", function() { var keyids = esMsg.getEncryptionKeyIds(); 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.text).to.equal(plaintext); + expect(decrypted.signatures).to.have.length(1); + expect(decrypted.signatures[0].valid).to.be.true; + done(); + }); - expect(decrypted).to.exist; - expect(decrypted.text).to.equal(plaintext); - expect(decrypted.signatures).to.have.length(1); - expect(decrypted.signatures[0].valid).to.be.true; - done(); }); it('Verify signed message with two one pass signatures', function(done) { @@ -504,14 +506,14 @@ describe("Signature", function() { expect(pubKey2.getKeyPacket(keyids)).to.exist; expect(pubKey3.getKeyPacket(keyids)).to.exist; - var cleartextSig = openpgp.verifyClearSignedMessage([pubKey2, pubKey3], csMsg); - - expect(cleartextSig).to.exist; - expect(cleartextSig.text).to.equal(plaintext); - expect(cleartextSig.signatures).to.have.length(2); - expect(cleartextSig.signatures[0].valid).to.be.true; - expect(cleartextSig.signatures[1].valid).to.be.true; - done(); + openpgp.verifyClearSignedMessage([pubKey2, pubKey3], csMsg).then(function(cleartextSig) { + expect(cleartextSig).to.exist; + expect(cleartextSig.text).to.equal(plaintext); + expect(cleartextSig.signatures).to.have.length(2); + expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[1].valid).to.be.true; + done(); + }); }); it('Sign text with openpgp.signClearMessage and verify with openpgp.verifyClearSignedMessage leads to same cleartext and valid signatures', function(done) { @@ -520,17 +522,19 @@ describe("Signature", function() { var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; 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.text).to.equal(plaintext.replace(/\r/g,'')); + expect(cleartextSig.signatures).to.have.length(1); + expect(cleartextSig.signatures[0].valid).to.be.true; + done(); + }); - expect(cleartextSig).to.exist; - expect(cleartextSig.text).to.equal(plaintext.replace(/\r/g,'')); - expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.be.true; - done(); }); it('Verify primary key revocation signature', function(done) { @@ -638,9 +642,7 @@ describe("Signature", function() { it('Sign message with key without password', function(done) { var opt = {numBits: 512, userId: 'ABC', passphrase: null}; - openpgp.generateKeyPair(opt, function(err, gen) { - expect(err).to.not.exist; - + openpgp.generateKeyPair(opt).then(function(gen) { var key = gen.key; var message = openpgp.message.fromText('hello world'); diff --git a/test/unittests.html b/test/unittests.html index 7a79064c..c241aa0d 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -9,7 +9,10 @@
- + + + + diff --git a/test/unittests.js b/test/unittests.js index 5705bf17..c2f48ac8 100644 --- a/test/unittests.js +++ b/test/unittests.js @@ -1,3 +1,8 @@ +if (typeof window === 'undefined') { + // load ES6 Promises polyfill under node.js + require('es6-promise').polyfill(); +} + describe('Unit Tests', function () { require('./general'); require('./crypto'); From f08fc0a4f7b56524c9af5cdd62d79613380c9a0f Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Wed, 1 Oct 2014 21:18:05 +0200 Subject: [PATCH 12/19] Refactor web worker async proxy to use promises. --- src/openpgp.js | 4 +- src/worker/async_proxy.js | 301 ++++++++++++++++++++++---------------- src/worker/worker.js | 132 ++++++++--------- test/unittests.html | 1 + test/worker/api.js | 99 ++++--------- 5 files changed, 264 insertions(+), 273 deletions(-) diff --git a/src/openpgp.js b/src/openpgp.js index 1921891a..42699a2c 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -239,8 +239,8 @@ function generateKeyPair(options) { /** * Are we in a browser and do we support worker? */ -function useWorker(callback) { - if (typeof window === 'undefined' || !window.Worker || typeof callback !== 'function') { +function useWorker() { + if (typeof window === 'undefined' || !window.Worker) { return false; } diff --git a/src/worker/async_proxy.js b/src/worker/async_proxy.js index 9305849d..63a10e30 100644 --- a/src/worker/async_proxy.js +++ b/src/worker/async_proxy.js @@ -24,11 +24,12 @@ * @module async_proxy */ +'use strict'; + var crypto = require('../crypto'), packet = require('../packet'), key = require('../key.js'), - type_keyid = require('../type/keyid.js'), - enums = require('../enums.js'); + type_keyid = require('../type/keyid.js'); var INITIAL_RANDOM_SEED = 50000, // random bytes seeded to worker RANDOM_SEED_REQUEST = 20000; // random bytes seeded after worker request @@ -49,6 +50,24 @@ function AsyncProxy(path) { 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 */ @@ -56,7 +75,13 @@ AsyncProxy.prototype.onMessage = function(event) { var msg = event.data; switch (msg.event) { 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; case 'request-seed': this.seedRandom(RANDOM_SEED_REQUEST); @@ -98,21 +123,23 @@ AsyncProxy.prototype.terminate = function() { * Encrypts message text with keys * @param {(Array|module:key~Key)} keys array of keys or single key, used to encrypt the message * @param {String} text message as native JavaScript string - * @param {Function} callback receives encrypted ASCII armored message */ -AsyncProxy.prototype.encryptMessage = function(keys, text, callback) { - if (!keys.length) { - keys = [keys]; - } - keys = keys.map(function(key) { - return key.toPacketlist(); +AsyncProxy.prototype.encryptMessage = function(keys, text) { + var self = this; + + return self.execute(function() { + if (!keys.length) { + keys = [keys]; + } + keys = keys.map(function(key) { + return key.toPacketlist(); + }); + self.worker.postMessage({ + event: 'encrypt-message', + keys: keys, + text: text + }); }); - this.worker.postMessage({ - event: 'encrypt-message', - keys: keys, - text: text - }); - this.tasks.push(callback); }; /** @@ -120,40 +147,43 @@ AsyncProxy.prototype.encryptMessage = function(keys, text, callback) { * @param {(Array|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 {String} text message as native JavaScript string - * @param {Function} callback receives encrypted ASCII armored message */ -AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text, callback) { - if (!publicKeys.length) { - publicKeys = [publicKeys]; - } - publicKeys = publicKeys.map(function(key) { - return key.toPacketlist(); +AsyncProxy.prototype.signAndEncryptMessage = function(publicKeys, privateKey, text) { + var self = this; + + return self.execute(function() { + if (!publicKeys.length) { + publicKeys = [publicKeys]; + } + publicKeys = publicKeys.map(function(key) { + return key.toPacketlist(); + }); + privateKey = privateKey.toPacketlist(); + self.worker.postMessage({ + event: 'sign-and-encrypt-message', + publicKeys: publicKeys, + privateKey: privateKey, + text: text + }); }); - privateKey = privateKey.toPacketlist(); - this.worker.postMessage({ - event: 'sign-and-encrypt-message', - publicKeys: publicKeys, - privateKey: privateKey, - text: text - }); - this.tasks.push(callback); }; /** * Decrypts message * @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 {Function} callback receives decrypted message as as native JavaScript string - * or null if no literal data found */ -AsyncProxy.prototype.decryptMessage = function(privateKey, message, callback) { - privateKey = privateKey.toPacketlist(); - this.worker.postMessage({ - event: 'decrypt-message', - privateKey: privateKey, - message: message +AsyncProxy.prototype.decryptMessage = function(privateKey, message) { + var self = this; + + return self.execute(function() { + privateKey = privateKey.toPacketlist(); + self.worker.postMessage({ + event: 'decrypt-message', + privateKey: privateKey, + message: message + }); }); - this.tasks.push(callback); }; /** @@ -161,82 +191,91 @@ AsyncProxy.prototype.decryptMessage = function(privateKey, message, callback) { * @param {module:key~Key} privateKey private key with decrypted secret key data * @param {(Array|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 {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) { - privateKey = privateKey.toPacketlist(); - if (!publicKeys.length) { - publicKeys = [publicKeys]; - } - publicKeys = publicKeys.map(function(key) { - return key.toPacketlist(); - }); - this.worker.postMessage({ - event: 'decrypt-and-verify-message', - privateKey: privateKey, - publicKeys: publicKeys, - message: message - }); - this.tasks.push(function(err, data) { - if (data) { +AsyncProxy.prototype.decryptAndVerifyMessage = function(privateKey, publicKeys, message) { + var self = this; + + var promise = new Promise(function(resolve, reject) { + privateKey = privateKey.toPacketlist(); + if (!publicKeys.length) { + publicKeys = [publicKeys]; + } + publicKeys = publicKeys.map(function(key) { + return key.toPacketlist(); + }); + self.worker.postMessage({ + event: 'decrypt-and-verify-message', + privateKey: privateKey, + publicKeys: publicKeys, + message: message + }); + + self.tasks.push({ resolve:function(data) { data.signatures = data.signatures.map(function(sig) { sig.keyid = type_keyid.fromClone(sig.keyid); return sig; }); - } - callback(err, data); + resolve(data); + }, reject:reject }); }); + + return promise; }; /** * Signs a cleartext message * @param {(Array|module:key~Key)} privateKeys array of keys or single key, with decrypted secret key data to sign cleartext * @param {String} text cleartext - * @param {Function} callback receives ASCII armored message */ -AsyncProxy.prototype.signClearMessage = function(privateKeys, text, callback) { - if (!privateKeys.length) { - privateKeys = [privateKeys]; - } - privateKeys = privateKeys.map(function(key) { - return key.toPacketlist(); +AsyncProxy.prototype.signClearMessage = function(privateKeys, text) { + var self = this; + + return self.execute(function() { + if (!privateKeys.length) { + privateKeys = [privateKeys]; + } + privateKeys = privateKeys.map(function(key) { + return key.toPacketlist(); + }); + self.worker.postMessage({ + event: 'sign-clear-message', + privateKeys: privateKeys, + text: text + }); }); - this.worker.postMessage({ - event: 'sign-clear-message', - privateKeys: privateKeys, - text: text - }); - this.tasks.push(callback); }; /** * Verifies signatures of cleartext signed message * @param {(Array|module:key~Key)} publicKeys array of keys or single key, to verify 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) { - if (!publicKeys.length) { - publicKeys = [publicKeys]; - } - publicKeys = publicKeys.map(function(key) { - return key.toPacketlist(); - }); - this.worker.postMessage({ - event: 'verify-clear-signed-message', - publicKeys: publicKeys, - message: message - }); - this.tasks.push(function(err, data) { - if (data) { +AsyncProxy.prototype.verifyClearSignedMessage = function(publicKeys, message) { + var self = this; + + var promise = new Promise(function(resolve, reject) { + if (!publicKeys.length) { + publicKeys = [publicKeys]; + } + publicKeys = publicKeys.map(function(key) { + return key.toPacketlist(); + }); + self.worker.postMessage({ + event: 'verify-clear-signed-message', + publicKeys: publicKeys, + message: message + }); + + self.tasks.push({ resolve:function(data) { data.signatures = data.signatures.map(function(sig) { sig.keyid = type_keyid.fromClone(sig.keyid); return sig; }); - } - callback(err, data); + resolve(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 {String} userId assumes already in form of "User Name " * @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) { - this.worker.postMessage({ - event: 'generate-key-pair', - options: options - }); - this.tasks.push(function(err, data) { - if (data) { +AsyncProxy.prototype.generateKeyPair = function(options) { + var self = this; + + var promise = new Promise(function(resolve, reject) { + self.worker.postMessage({ + event: 'generate-key-pair', + options: options + }); + + self.tasks.push({ resolve:function(data) { var packetlist = packet.List.fromStructuredClone(data.key); data.key = new key.Key(packetlist); - } - callback(err, data); + resolve(data); + }, reject:reject }); }); + + return promise; }; /** * Decrypts secret part of all secret key packets of key. * @param {module:key~Key} privateKey private key with encrypted secret key data * @param {String} password password to unlock the key - * @param {Function} callback receives decrypted key */ -AsyncProxy.prototype.decryptKey = function(privateKey, password, callback) { - privateKey = privateKey.toPacketlist(); - this.worker.postMessage({ - event: 'decrypt-key', - privateKey: privateKey, - password: password - }); - this.tasks.push(function(err, data) { - if (data) { +AsyncProxy.prototype.decryptKey = function(privateKey, password) { + var self = this; + + var promise = new Promise(function(resolve, reject) { + privateKey = privateKey.toPacketlist(); + self.worker.postMessage({ + event: 'decrypt-key', + privateKey: privateKey, + password: password + }); + + self.tasks.push({ resolve:function(data) { var packetlist = packet.List.fromStructuredClone(data); data = new key.Key(packetlist); - } - callback(err, data); + resolve(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 {Array} keyIds * @param {String} password password to unlock the key - * @param {Function} callback receives decrypted key */ -AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password, callback) { - privateKey = privateKey.toPacketlist(); - this.worker.postMessage({ - event: 'decrypt-key-packet', - privateKey: privateKey, - keyIds: keyIds, - password: password - }); - this.tasks.push(function(err, data) { - if (data) { +AsyncProxy.prototype.decryptKeyPacket = function(privateKey, keyIds, password) { + var self = this; + + var promise = new Promise(function(resolve, reject) { + privateKey = privateKey.toPacketlist(); + self.worker.postMessage({ + event: 'decrypt-key-packet', + privateKey: privateKey, + keyIds: keyIds, + password: password + }); + + self.tasks.push({ resolve:function(data) { var packetlist = packet.List.fromStructuredClone(data); data = new key.Key(packetlist); - } - callback(err, data); + resolve(data); + }, reject:reject }); }); + + return promise; }; module.exports = AsyncProxy; diff --git a/src/worker/worker.js b/src/worker/worker.js index 680eddf7..5e6baeb9 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -5,7 +5,7 @@ // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. -// +// // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @@ -24,11 +24,12 @@ var MAX_SIZE_RANDOM_BUFFER = 60000; window.openpgp.crypto.random.randomBuffer.init(MAX_SIZE_RANDOM_BUFFER); -onmessage = function (event) { +self.onmessage = function (event) { var data = null, err = null, msg = event.data, correct = false; + switch (msg.event) { case 'seed-random': if (!(msg.buf instanceof Uint8Array)) { @@ -37,93 +38,78 @@ onmessage = function (event) { window.openpgp.crypto.random.randomBuffer.set(msg.buf); break; case 'encrypt-message': - try { - if (!msg.keys.length) { - msg.keys = [msg.keys]; - } - msg.keys = msg.keys.map(packetlistCloneToKey); - data = window.openpgp.encryptMessage(msg.keys, msg.text); - } catch (e) { - err = e.message; + if (!msg.keys.length) { + msg.keys = [msg.keys]; } - response({event: 'method-return', data: data, err: err}); + msg.keys = msg.keys.map(packetlistCloneToKey); + window.openpgp.encryptMessage(msg.keys, msg.text).then(function(data) { + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'sign-and-encrypt-message': - try { - if (!msg.publicKeys.length) { - msg.publicKeys = [msg.publicKeys]; - } - msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); - msg.privateKey = packetlistCloneToKey(msg.privateKey); - data = window.openpgp.signAndEncryptMessage(msg.publicKeys, msg.privateKey, msg.text); - } catch (e) { - err = e.message; + if (!msg.publicKeys.length) { + msg.publicKeys = [msg.publicKeys]; } - response({event: 'method-return', data: data, err: err}); + msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); + msg.privateKey = packetlistCloneToKey(msg.privateKey); + window.openpgp.signAndEncryptMessage(msg.publicKeys, msg.privateKey, msg.text).then(function(data) { + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'decrypt-message': - try { - msg.privateKey = packetlistCloneToKey(msg.privateKey); - msg.message = packetlistCloneToMessage(msg.message.packets); - data = window.openpgp.decryptMessage(msg.privateKey, msg.message); - } catch (e) { - err = e.message; - } - response({event: 'method-return', data: data, err: err}); + msg.privateKey = packetlistCloneToKey(msg.privateKey); + msg.message = packetlistCloneToMessage(msg.message.packets); + window.openpgp.decryptMessage(msg.privateKey, msg.message).then(function(data) { + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'decrypt-and-verify-message': - try { - msg.privateKey = packetlistCloneToKey(msg.privateKey); - if (!msg.publicKeys.length) { - msg.publicKeys = [msg.publicKeys]; - } - msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); - msg.message = packetlistCloneToMessage(msg.message.packets); - data = window.openpgp.decryptAndVerifyMessage(msg.privateKey, msg.publicKeys, msg.message); - } catch (e) { - err = e.message; + msg.privateKey = packetlistCloneToKey(msg.privateKey); + if (!msg.publicKeys.length) { + msg.publicKeys = [msg.publicKeys]; } - response({event: 'method-return', data: data, err: err}); + msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); + msg.message = packetlistCloneToMessage(msg.message.packets); + window.openpgp.decryptAndVerifyMessage(msg.privateKey, msg.publicKeys, msg.message).then(function(data) { + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'sign-clear-message': - try { - msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey); - data = window.openpgp.signClearMessage(msg.privateKeys, msg.text); - } catch (e) { - err = e.message; - } - response({event: 'method-return', data: data, err: err}); + msg.privateKeys = msg.privateKeys.map(packetlistCloneToKey); + window.openpgp.signClearMessage(msg.privateKeys, msg.text).then(function(data) { + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'verify-clear-signed-message': - try { - if (!msg.publicKeys.length) { - msg.publicKeys = [msg.publicKeys]; - } - msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); - var packetlist = window.openpgp.packet.List.fromStructuredClone(msg.message.packets); - msg.message = new window.openpgp.cleartext.CleartextMessage(msg.message.text, packetlist); - data = window.openpgp.verifyClearSignedMessage(msg.publicKeys, msg.message); - } catch (e) { - err = e.message; + if (!msg.publicKeys.length) { + msg.publicKeys = [msg.publicKeys]; } - response({event: 'method-return', data: data, err: err}); + msg.publicKeys = msg.publicKeys.map(packetlistCloneToKey); + var packetlist = window.openpgp.packet.List.fromStructuredClone(msg.message.packets); + msg.message = new window.openpgp.cleartext.CleartextMessage(msg.message.text, packetlist); + window.openpgp.verifyClearSignedMessage(msg.publicKeys, msg.message).then(function(data) { + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'generate-key-pair': - try { - window.openpgp.generateKeyPair(msg.options, function(error, data) { - if (error) { - err = error.message; - response({event: 'method-return', data: data, err: err}); - return; - } - - data.key = data.key.toPacketlist(); - response({event: 'method-return', data: data, err: err}); - }); - } catch (e) { - err = e.message; - response({event: 'method-return', data: data, err: err}); - } + window.openpgp.generateKeyPair(msg.options).then(function(data) { + data.key = data.key.toPacketlist(); + response({event: 'method-return', data: data}); + }).catch(function(e) { + response({event: 'method-return', err: e.message}); + }); break; case 'decrypt-key': try { diff --git a/test/unittests.html b/test/unittests.html index c241aa0d..4b5e3efd 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -18,6 +18,7 @@ - diff --git a/test/unittests.js b/test/unittests.js index c2f48ac8..5705bf17 100644 --- a/test/unittests.js +++ b/test/unittests.js @@ -1,8 +1,3 @@ -if (typeof window === 'undefined') { - // load ES6 Promises polyfill under node.js - require('es6-promise').polyfill(); -} - describe('Unit Tests', function () { require('./general'); require('./crypto'); From e3b6903eb14c74291f23c784250354c432e74dea Mon Sep 17 00:00:00 2001 From: Tankred Hase Date: Mon, 20 Oct 2014 16:10:09 +0200 Subject: [PATCH 19/19] Use global error catch --- package.json | 4 ++-- src/openpgp.js | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index feaf8b2e..58931e25 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "openpgp", "description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.", - "version": "0.8.0-dev", + "version": "0.8.0", "homepage": "http://openpgpjs.org/", "engines": { "node": ">=0.8" @@ -53,4 +53,4 @@ "type": "git", "url": "https://github.com/openpgpjs/openpgpjs" } -} +} \ No newline at end of file diff --git a/src/openpgp.js b/src/openpgp.js index f37f3ad8..53cce362 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -79,7 +79,7 @@ function encryptMessage(keys, text) { armored = armor.encode(enums.armor.message, msg.packets.write()); return armored; - }).catch(onError.bind(null, 'Error encrypting message!')); + }, 'Error encrypting message!'); } /** @@ -107,7 +107,7 @@ function signAndEncryptMessage(publicKeys, privateKey, text) { armored = armor.encode(enums.armor.message, msg.packets.write()); return armored; - }).catch(onError.bind(null, 'Error signing and encrypting message!')); + }, 'Error signing and encrypting message!'); } /** @@ -127,7 +127,7 @@ function decryptMessage(privateKey, msg) { msg = msg.decrypt(privateKey); return msg.getText(); - }).catch(onError.bind(null, 'Error decrypting message!')); + }, 'Error decrypting message!'); } /** @@ -159,7 +159,7 @@ function decryptAndVerifyMessage(privateKey, publicKeys, msg) { } return null; - }).catch(onError.bind(null, 'Error decrypting and verifying message!')); + }, 'Error decrypting and verifying message!'); } /** @@ -183,7 +183,7 @@ function signClearMessage(privateKeys, text) { cleartextMessage.sign(privateKeys); return cleartextMessage.armor(); - }).catch(onError.bind(null, 'Error signing cleartext message!')); + }, 'Error signing cleartext message!'); } /** @@ -212,7 +212,7 @@ function verifyClearSignedMessage(publicKeys, msg) { result.signatures = msg.verify(publicKeys); return result; - }).catch(onError.bind(null, 'Error verifying cleartext signed message!')); + }, 'Error verifying cleartext signed message!'); } /** @@ -240,7 +240,7 @@ function generateKeyPair(options) { result.publicKeyArmored = newKey.toPublic().armor(); return result; - }).catch(onError.bind(null, 'Error generating keypair!')); + }, 'Error generating keypair!'); } // @@ -303,4 +303,4 @@ exports.decryptMessage = decryptMessage; exports.decryptAndVerifyMessage = decryptAndVerifyMessage; exports.signClearMessage = signClearMessage; exports.verifyClearSignedMessage = verifyClearSignedMessage; -exports.generateKeyPair = generateKeyPair; +exports.generateKeyPair = generateKeyPair; \ No newline at end of file