From 3673e21a770665db118a68e61cb5737851b5aed4 Mon Sep 17 00:00:00 2001 From: nauadratti Date: Thu, 30 Mar 2017 19:05:22 +0000 Subject: [PATCH 01/75] Allow punycode encoded TLDs in email Closes #500 --- src/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.js b/src/util.js index 78394ad4..abf92927 100644 --- a/src/util.js +++ b/src/util.js @@ -43,7 +43,7 @@ export default { if (!this.isString(data)) { return false; } - const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+([a-zA-Z]{2,}|xn--[a-zA-Z\-0-9]+)))$/; return re.test(data); }, From f56d5570361aabf610039cdff272764884cfbf9e Mon Sep 17 00:00:00 2001 From: nauadratti Date: Thu, 30 Mar 2017 19:29:50 +0000 Subject: [PATCH 02/75] Add a test for punycode encoded domains --- test/general/util.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/general/util.js b/test/general/util.js index d805eef1..614cad63 100644 --- a/test/general/util.js +++ b/test/general/util.js @@ -87,6 +87,10 @@ describe('Util unit tests', function() { var data = 'test@example.com'; expect(openpgp.util.isEmailAddress(data)).to.be.true; }); + it('should return true for valid email address', function() { + var data = 'test@xn--wgv.xn--q9jyb4c'; + expect(openpgp.util.isEmailAddress(data)).to.be.true; + }); it('should return false for invalid email address', function() { var data = 'Test User '; expect(openpgp.util.isEmailAddress(data)).to.be.false; From 01be192a35c7cd63cc4b904162a00f90a64b02de Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:22:46 -0300 Subject: [PATCH 03/75] Object identifier type --- src/index.js | 6 ++++ src/type/oid.js | 69 +++++++++++++++++++++++++++++++++++++++++++ test/general/index.js | 1 + test/general/oid.js | 38 ++++++++++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 src/type/oid.js create mode 100644 test/general/oid.js diff --git a/src/index.js b/src/index.js index 7472326b..64c95c8c 100644 --- a/src/index.js +++ b/src/index.js @@ -77,6 +77,12 @@ export { default as S2K } from './type/s2k'; */ export { default as Keyid } from './type/keyid'; +/** + * @see module:type/oid + * @name module:openpgp.Oid + */ +export { default as Oid } from './type/oid'; + /** * @see module:encoding/armor * @name module:openpgp.armor diff --git a/src/type/oid.js b/src/type/oid.js new file mode 100644 index 00000000..a8fd1899 --- /dev/null +++ b/src/type/oid.js @@ -0,0 +1,69 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +/** + * Wrapper to an Oid value
+ *
+ * An object identifier type from {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. + * @requires util + * @module type/oid + */ + +'use strict'; + +import util from '../util.js'; + +module.exports = Oid; + +/** + * @constructor + */ +function Oid(oid) { + if (typeof oid === 'undefined') { + oid = ''; + } else if (util.isArray(oid)) { + oid = util.bin2str(oid); + } else if (util.isUint8Array(oid)) { + oid = util.Uint8Array2str(oid); + } + this.oid = oid; +} + +/** + * Method to read an Oid object + * @param {Uint8Array} input Where to read the Oid from + * @return {Number} Number of read bytes + */ +Oid.prototype.read = function (input) { + if (input.length >= 1) { + var length = input[0]; + if (input.length >= 1+length) { + this.oid = util.Uint8Array2str(input.subarray(1, 1+length)); + return 1+this.oid.length; + } + } + throw new Error('Invalid oid'); +}; + +/** + * Serialize an Oid object + * @return {Uint8Array} Array with the serialized value the Oid + */ +Oid.prototype.write = function () { + return util.str2Uint8Array( + String.fromCharCode(this.oid.length)+this.oid); +}; diff --git a/test/general/index.js b/test/general/index.js index 61de4d72..aed972e7 100644 --- a/test/general/index.js +++ b/test/general/index.js @@ -7,5 +7,6 @@ describe('General', function () { require('./key.js'); require('./openpgp.js'); require('./hkp.js'); + require('./oid.js'); }); diff --git a/test/general/oid.js b/test/general/oid.js new file mode 100644 index 00000000..e093f407 --- /dev/null +++ b/test/general/oid.js @@ -0,0 +1,38 @@ +'use strict'; + +var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); + +var expect = require('chai').expect; + +describe('Oid tests', function() { + var Oid = openpgp.Oid; + var p256_oid = new Uint8Array([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]); + var p384_oid = new Uint8Array([0x2B, 0x81, 0x04, 0x00, 0x22]); + var p521_oid = new Uint8Array([0x2B, 0x81, 0x04, 0x00, 0x23]); + it('Constructing', function() { + var oids = [p256_oid, p384_oid, p521_oid]; + oids.forEach(function (data) { + var oid = new Oid(data); + expect(oid).to.exist; + expect(oid.oid).to.exist; + expect(oid.oid).to.have.length(data.length); + expect(oid.oid).to.equal(openpgp.util.Uint8Array2str(data)); + }); + }); + it('Reading and writing', function() { + var oids = [p256_oid, p384_oid, p521_oid]; + oids.forEach(function (data) { + data = openpgp.util.concatUint8Array([new Uint8Array([data.length]), data]); + var oid = new Oid(); + expect(oid.read(data)).to.equal(data.length); + expect(oid.oid).to.exist; + expect(oid.oid).to.have.length(data.length-1); + expect(oid.oid).to.equal(openpgp.util.Uint8Array2str(data.subarray(1))); + var result = oid.write(); + expect(result).to.exist; + expect(result).to.have.length(data.length); + expect(result[0]).to.equal(data.length-1); + expect(openpgp.util.Uint8Array2str(result.subarray(1))).to.equal(openpgp.util.Uint8Array2str(data.subarray(1))); + }); + }); +}); From d8b1e3359a405f4282f8a4df9f5d20debd5d44fa Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:22:59 -0300 Subject: [PATCH 04/75] Result of wrapping a session key for ECDH key --- src/index.js | 6 ++++ src/type/ecdh_symkey.js | 71 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/type/ecdh_symkey.js diff --git a/src/index.js b/src/index.js index 64c95c8c..e701837b 100644 --- a/src/index.js +++ b/src/index.js @@ -77,6 +77,12 @@ export { default as S2K } from './type/s2k'; */ export { default as Keyid } from './type/keyid'; +/** + * @see module:type/ecdh_symkey + * @name module:openpgp.EcdhSymmetricKey + */ +export { default as EcdhSymmetricKey } from './type/ecdh_symkey'; + /** * @see module:type/oid * @name module:openpgp.Oid diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js new file mode 100644 index 00000000..ef32b68a --- /dev/null +++ b/src/type/ecdh_symkey.js @@ -0,0 +1,71 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +/** + * Encoded symmetric key for ECDH
+ *
+ * @requires util + * @module type/ecdh_symkey + */ + +'use strict'; + +import util from '../util.js'; + +module.exports = EcdhSymmetricKey; + +/** + * @constructor + */ +function EcdhSymmetricKey(data) { + if (typeof data === 'undefined') { + data = new Uint8Array([]); + } else if (typeof data === 'string') { + data = util.str2Uint8Array(data); + } else { + data = new Uint8Array(data); + } + this.data = data; +} + +/** + * Read an EcdhSymmetricKey from an Uint8Array + * @param {Uint8Array} input Where to read the encoded symmetric key from + * @return {Number} Number of read bytes + */ +EcdhSymmetricKey.prototype.read = function (input) { + if (input.length >= 1) + { + var length = input[0]; + if (input.length >= 1+length) + { + this.data = input.subarray(1, 1+length); + return 1+this.data.length; + } + } + throw new Error('Invalid symmetric key'); +}; + +/** + * Write an EcdhSymmetricKey as an Uint8Array + * @return {Uint8Array} An array containing the value + */ +EcdhSymmetricKey.prototype.write = function () { + return util.concatUint8Array([ + new Uint8Array([this.data.length]), + this.data]); +}; From 26c6ab9b9705f619e4449a36baf6300ce2f19f71 Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:23:20 -0300 Subject: [PATCH 05/75] Parameters for the ECDH key derivation function --- src/index.js | 6 +++++ src/type/kdf_params.js | 61 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/type/kdf_params.js diff --git a/src/index.js b/src/index.js index e701837b..fd0e9de5 100644 --- a/src/index.js +++ b/src/index.js @@ -83,6 +83,12 @@ export { default as Keyid } from './type/keyid'; */ export { default as EcdhSymmetricKey } from './type/ecdh_symkey'; +/** + * @see module:type/kdf_params + * @name module:openpgp.KdfParams + */ +export { default as KdfParams } from './type/kdf_params'; + /** * @see module:type/oid * @name module:openpgp.Oid diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js new file mode 100644 index 00000000..1a6d1371 --- /dev/null +++ b/src/type/kdf_params.js @@ -0,0 +1,61 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +/** + * Implementation of type KDF parameters RFC 6637
+ *
+ * @requires enums + * @module type/kdf_params + */ + +'use strict'; + +import enums from '../enums.js'; + +module.exports = KdfParams; + +/** + * @constructor + * @param {enums.hash} hash Hash algorithm + * @param {enums.symmetric} cipher Symmetric algorithm + */ +function KdfParams(hash, cipher) { + this.hash = hash || enums.hash.sha1; + this.cipher = cipher || enums.symmetric.aes128; +} + +/** + * Read KdfParams from an Uint8Array + * @param {Uint8Array} input Where to read the KdfParams from + * @return {Number} Number of read bytes + */ +KdfParams.prototype.read = function (input) { + if (input.length < 4 || input[0] !== 3 || input[1] !== 1) { + throw new Error('Cannot read KdfParams'); + } + this.hash = input[2]; + this.cipher = input[3]; + return 4; +}; + +/** + * Write KdfParams to an Uint8Array + * @return {Uint8Array} Array with the KdfParams value + */ +KdfParams.prototype.write = function () { + return new Uint8Array([3, 1, this.hash, this.cipher]); +}; From 2f69d22130cc00586e707e55ddad45726cef1659 Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:23:32 -0300 Subject: [PATCH 06/75] Key Wrap and Unwrap from RFC 3394 --- package.json | 1 + src/crypto/index.js | 3 + src/crypto/rfc3394.js | 142 +++++++++++++++++++++++++++++++++++++++++ test/crypto/index.js | 1 + test/crypto/rfc3394.js | 59 +++++++++++++++++ 5 files changed, 206 insertions(+) create mode 100644 src/crypto/rfc3394.js create mode 100644 test/crypto/rfc3394.js diff --git a/package.json b/package.json index bc7f0f1a..d9e43876 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "test": "grunt test" }, "devDependencies": { + "aes": "^0.1.0", "asmcrypto-lite": "^1.0.0", "babel-core": "^6.26.0", "babel-preset-es2015": "^6.3.13", diff --git a/src/crypto/index.js b/src/crypto/index.js index e337b4cb..d87a18ee 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -14,6 +14,7 @@ import signature from './signature'; import random from './random'; import pkcs1 from './pkcs1'; import crypto from './crypto.js'; +import rfc3394 from './rfc3394.js'; const mod = { /** @see module:crypto/cipher */ @@ -32,6 +33,8 @@ const mod = { random: random, /** @see module:crypto/pkcs1 */ pkcs1: pkcs1, + /** @see module:crypto/rfc3394 */ + rfc3394: rfc3394, }; for (var i in crypto) { diff --git a/src/crypto/rfc3394.js b/src/crypto/rfc3394.js new file mode 100644 index 00000000..93f3143f --- /dev/null +++ b/src/crypto/rfc3394.js @@ -0,0 +1,142 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Implementation of RFC 3394 Key Wrap & Key Unwrap funcions + +import AES from 'aes'; + +function createArrayBuffer(data) { + if (typeof data === "string") { + var length = data.length; + var buffer = new ArrayBuffer(length); + var view = new Uint8Array(buffer); + for (var j = 0; j < length; ++j) { + view[j] = data.charCodeAt(j); + } + return buffer; + } + return new Uint8Array(data).buffer; +} + +function unpack(data) { + var length = data.length; + var buffer = createArrayBuffer(data); + var view = new DataView(buffer); + var arr = new Uint32Array(length/4); + for (var i=0; i= 0; --j) { + for (var i = n - 1; i >= 0; --i) { + t[1] = n * j + (i + 1); + // B = A ^ t + B[0] = A[0] ^ t[0]; + B[1] = A[1] ^ t[1]; + // B = (A ^ t) || R[i] + B[2] = R[2*i]; + B[3] = R[2*i+1]; + // B = AES-1(B) + B = aes.decrypt(B); + // A = MSB(64, B) + A = B.subarray(0, 2); + // R[i] = LSB(64, B) + R[2*i] = B[2]; + R[2*i+1] = B[3]; + } + } + if (A[0] === IV[0] && A[1] === IV[1]) { + return pack(R); + } + throw new Error("Key Data Integrity failed"); +} + +module.exports = { + wrap: wrap, + unwrap: unwrap +}; diff --git a/test/crypto/index.js b/test/crypto/index.js index f04a73d0..56685ed9 100644 --- a/test/crypto/index.js +++ b/test/crypto/index.js @@ -3,4 +3,5 @@ describe('Crypto', function () { require('./hash'); require('./random.js'); require('./crypto.js'); + require('./rfc3394.js'); }); diff --git a/test/crypto/rfc3394.js b/test/crypto/rfc3394.js new file mode 100644 index 00000000..6887db79 --- /dev/null +++ b/test/crypto/rfc3394.js @@ -0,0 +1,59 @@ +'use strict'; + +var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); + +var expect = require('chai').expect; + +describe('AES Key Wrap and Unwrap', function () { + var test_vectors = [ + [ + "128 bits of Key Data with a 128-bit KEK", + "000102030405060708090A0B0C0D0E0F", + "00112233445566778899AABBCCDDEEFF", + "1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5" + ], + [ + "128 bits of Key Data with a 192-bit KEK", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "00112233445566778899AABBCCDDEEFF", + "96778B25AE6CA435 F92B5B97C050AED2 468AB8A17AD84E5D" + ], + [ + "128 bits of Key Data with a 256-bit KEK", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "00112233445566778899AABBCCDDEEFF", + "64E8C3F9CE0F5BA2 63E9777905818A2A 93C8191E7D6E8AE7" + ], + [ + "192 bits of Key Data with a 192-bit KEK", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "00112233445566778899AABBCCDDEEFF0001020304050607", + "031D33264E15D332 68F24EC260743EDC E1C6C7DDEE725A93 6BA814915C6762D2" + ], + [ + "192 bits of Key Data with a 256-bit KEK", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "00112233445566778899AABBCCDDEEFF0001020304050607", + "A8F9BC1612C68B3F F6E6F4FBE30E71E4 769C8B80A32CB895 8CD5D17D6B254DA1" + ], + [ + "256 bits of Key Data with a 256-bit KEK", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F", + "00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F", + "28C9F404C4B810F4 CBCCB35CFB87F826 3F5786E2D80ED326 CBC7F0E71A99F43B FB988B9B7A02DD21" + ] + ]; + + test_vectors.forEach(function(test) { + it(test[0], function(done) { + var kek = openpgp.util.hex2bin(test[1]); + var input = test[2].replace(/\s/g, ""); + var input_bin = openpgp.util.hex2bin(input); + var output = test[3].replace(/\s/g, ""); + var output_bin = openpgp.util.hex2bin(output); + expect(openpgp.util.hexidump(openpgp.crypto.rfc3394.wrap(kek, input_bin)).toUpperCase()).to.equal(output); + expect(openpgp.util.hexidump(openpgp.crypto.rfc3394.unwrap(kek, output_bin)).toUpperCase()).to.equal(input); + done(); + }); + }); +}); From 54b79be0b0f4e14bdebf5a6b18f26baf2d386b52 Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:23:44 -0300 Subject: [PATCH 07/75] Add and remove PKCS5 padding --- src/crypto/index.js | 3 +++ src/crypto/pkcs5.js | 62 ++++++++++++++++++++++++++++++++++++++++++++ test/crypto/index.js | 1 + test/crypto/pkcs5.js | 41 +++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) create mode 100644 src/crypto/pkcs5.js create mode 100644 test/crypto/pkcs5.js diff --git a/src/crypto/index.js b/src/crypto/index.js index d87a18ee..b82d8b7b 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -13,6 +13,7 @@ import publicKey from './public_key'; import signature from './signature'; import random from './random'; import pkcs1 from './pkcs1'; +import pkcs5 from './pkcs5.js'; import crypto from './crypto.js'; import rfc3394 from './rfc3394.js'; @@ -33,6 +34,8 @@ const mod = { random: random, /** @see module:crypto/pkcs1 */ pkcs1: pkcs1, + /** @see module:crypto/pkcs5 */ + pkcs5: pkcs5, /** @see module:crypto/rfc3394 */ rfc3394: rfc3394, }; diff --git a/src/crypto/pkcs5.js b/src/crypto/pkcs5.js new file mode 100644 index 00000000..fc1e694a --- /dev/null +++ b/src/crypto/pkcs5.js @@ -0,0 +1,62 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Functions to add and remove PKCS5 padding + +/** + * PKCS5 padding + * @module crypto/pkcs5 + */ + +function getPkcs5Padding(length) { + const c = 8 - (length % 8); + var result = []; + for (var i = 0; i < c; ++i) { + result.push(String.fromCharCode(c)); + } + return result.join(""); +} + +/** + * Add pkcs5 padding to a text. + * @param {String} msg Text to add padding + * @return {String} Text with padding added + */ +function addPadding(msg) { + return msg + getPkcs5Padding(msg.length); +} + +/** + * Remove pkcs5 padding from a string. + * @param {String} msg Text to remove padding from + * @return {String} Text with padding removed + */ +function removePadding(msg) { + var len = msg.length; + if (len > 0) { + var c = msg.charCodeAt(len - 1); + if (c >= 1 && c <= 8) { + return msg.substr(0, len - c); + } + } + throw new Error('Invalid padding'); +} + +module.exports = { + addPadding: addPadding, + removePadding: removePadding +}; diff --git a/test/crypto/index.js b/test/crypto/index.js index 56685ed9..7429aaba 100644 --- a/test/crypto/index.js +++ b/test/crypto/index.js @@ -3,5 +3,6 @@ describe('Crypto', function () { require('./hash'); require('./random.js'); require('./crypto.js'); + require('./pkcs5.js'); require('./rfc3394.js'); }); diff --git a/test/crypto/pkcs5.js b/test/crypto/pkcs5.js new file mode 100644 index 00000000..98b6a591 --- /dev/null +++ b/test/crypto/pkcs5.js @@ -0,0 +1,41 @@ +'use strict'; + +var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); + +var expect = require('chai').expect; + +describe('PKCS5 padding', function() { + function repeat(pattern, count) { + var result = ''; + for (var k=0; k 8, 8..15 -> 16 + var l = Math.ceil((s.length+1)/8)*8; + var c = l - s.length; + expect(r.length).to.equal(l); + expect(c).is.at.least(1).is.at.most(8); + expect(r.substr(-1)).to.equal(String.fromCharCode(c)); + s += ' '; + } + }); + it('Remove padding', function () { + for (var k=1; k<=8; ++k) { + var s = repeat(' ', 8-k); + var r = s + repeat(String.fromCharCode(k), k); + var t = pkcs5.removePadding(r); + expect(t).to.equal(s); + } + }); + it('Invalid padding', function () { + expect(function () {pkcs5.removePadding(' ');}).to.throw(Error, /Invalid padding/); + expect(function () {pkcs5.removePadding('');}).to.throw(Error, /Invalid padding/); + }); +}); From a8e82711857f6756f48cf843cf65aee330d27640 Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:24:08 -0300 Subject: [PATCH 08/75] Support functions for Elliptic Curve Cryptography --- Gruntfile.js | 1 + package.json | 1 - src/crypto/public_key/elliptic/curves.js | 118 ++++++++++++++ src/crypto/public_key/elliptic/index.js | 33 ++++ src/crypto/public_key/elliptic/key.js | 74 +++++++++ src/crypto/public_key/index.js | 4 + test/crypto/elliptic.js | 196 +++++++++++++++++++++++ test/crypto/index.js | 1 + 8 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 src/crypto/public_key/elliptic/curves.js create mode 100644 src/crypto/public_key/elliptic/index.js create mode 100644 src/crypto/public_key/elliptic/key.js create mode 100644 test/crypto/elliptic.js diff --git a/Gruntfile.js b/Gruntfile.js index 50aef8cf..84481853 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -15,6 +15,7 @@ module.exports = function(grunt) { 'src/crypto/public_key/elgamal.js', 'src/crypto/public_key/index.js', 'src/crypto/public_key/rsa.js', + 'src/crypto/public_key/elliptic/*.js', 'src/crypto/*.js', 'src/encoding/**/*.js', 'src/hkp/**/*.js', diff --git a/package.json b/package.json index d9e43876..bc7f0f1a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,6 @@ "test": "grunt test" }, "devDependencies": { - "aes": "^0.1.0", "asmcrypto-lite": "^1.0.0", "babel-core": "^6.26.0", "babel-preset-es2015": "^6.3.13", diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js new file mode 100644 index 00000000..c6b33477 --- /dev/null +++ b/src/crypto/public_key/elliptic/curves.js @@ -0,0 +1,118 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Wrapper of an instance of an Elliptic Curve + +/** + * @requires crypto/public_key/elliptic/key + * @requires crypto/public_key/jsbn + * @requires enums + * @requires util + * @module crypto/public_key/elliptic/curve + */ + +'use strict'; + +import {ec as EC} from 'elliptic'; +import {KeyPair} from './key.js'; +import BigInteger from '../jsbn.js'; +import enums from '../../../enums.js'; +import util from '../../../util.js'; + +function Curve(name, {oid, hash, cipher}) { + this.curve = new EC(name); + this.name = name; + this.oid = oid; + this.hash = hash; + this.cipher = cipher; +} + +Curve.prototype.keyFromPrivate = function (priv) { + return new KeyPair(this.curve, {priv: priv}); +}; + +Curve.prototype.keyFromPublic = function (pub) { + return new KeyPair(this.curve, {pub: pub}); +}; + +Curve.prototype.genKeyPair = function () { + var r = this.curve.genKeyPair(); + return new KeyPair(this.curve, { + pub: r.getPublic().encode(), + priv: r.getPrivate().toArray() + }); +}; + +const curves = { + p256: { + oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), + bits: 256, + hash: enums.hash.sha256, + cipher: enums.symmetric.aes128, + }, + p384: { + oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), + bits: 384, + hash: enums.hash.sha384, + cipher: enums.symmetric.aes192, + }, + p521: { + oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), + bits: 521, + hash: enums.hash.sha512, + cipher: enums.symmetric.aes256, + }, + secp256k1: { + oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), + bits: 256, + hash: enums.hash.sha256, + cipher: enums.symmetric.aes128, + } +}; + +function get(oid_or_name) { + for (var name in curves) { + if (curves[name].oid === oid_or_name || name === oid_or_name) { + return new Curve(name, { + oid: curves[name].oid, + hash: curves[name].hash, + cipher: curves[name].cipher + }); + } + } + throw new Error('Not valid curve'); +} + +function generate(curve) { + return new Promise(function (resolve) { + curve = get(curve); + var keyPair = curve.genKeyPair(); + resolve({ + oid: curve.oid, + R: new BigInteger(keyPair.getPublic()), + r: new BigInteger(keyPair.getPrivate()), + hash: curve.hash, + cipher: curve.cipher + }); + }); +} + +module.exports = { + Curve: Curve, + generate: generate, + get: get +}; diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js new file mode 100644 index 00000000..1868f192 --- /dev/null +++ b/src/crypto/public_key/elliptic/index.js @@ -0,0 +1,33 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Function to access Elliptic Curve Cryptography + +/** + * @requires crypto/public_key/elliptic/curve + * @requires crypto/public_key/elliptic/ecdsa + * @module crypto/public_key/elliptic + */ + +'use strict'; + +import {get, generate} from './curves.js'; + +module.exports = { + get: get, + generate: generate +}; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js new file mode 100644 index 00000000..875a62f5 --- /dev/null +++ b/src/crypto/public_key/elliptic/key.js @@ -0,0 +1,74 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Wrapper for a KeyPair of an Elliptic Curve + +/** + * @requires crypto/hash + * @requires util + * @module crypto/public_key/elliptic/key + */ + +'use strict'; + +import hash from '../../hash'; +import util from '../../../util.js'; + +function KeyPair(curve, options) { + this.curve = curve; + this.keyPair = this.curve.keyPair(options); +} + +KeyPair.prototype.sign = function (message, hash_algo) { + if (typeof message === 'string') { + message = util.str2Uint8Array(message); + } + const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); + const signature = this.keyPair.sign(digest); + return { + r: signature.r.toArray(), + s: signature.s.toArray() + }; +}; + +KeyPair.prototype.verify = function (message, signature, hash_algo) { + if (typeof message === 'string') { + message = util.str2Uint8Array(message); + } + const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); + return this.keyPair.verify(digest, signature); +}; + +KeyPair.prototype.derive = function (pub) { + return this.keyPair.derive(pub.keyPair.getPublic()).toArray(); +}; + +KeyPair.prototype.getPublic = function () { + return this.keyPair.getPublic().encode(); +}; + +KeyPair.prototype.getPrivate = function () { + return this.keyPair.getPrivate().toArray(); +}; + +KeyPair.prototype.isValid = function () { + return this.keyPair.validate().result; +}; + +module.exports = { + KeyPair: KeyPair +}; diff --git a/src/crypto/public_key/index.js b/src/crypto/public_key/index.js index 8e5dfc7b..ed29c250 100644 --- a/src/crypto/public_key/index.js +++ b/src/crypto/public_key/index.js @@ -1,6 +1,7 @@ /** * @requires crypto/public_key/dsa * @requires crypto/public_key/elgamal + * @requires crypto/public_key/elliptic * @requires crypto/public_key/rsa * @module crypto/public_key */ @@ -11,11 +12,14 @@ import rsa from './rsa.js'; /** @see module:crypto/public_key/elgamal */ import elgamal from './elgamal.js'; +/** @see module:crypto/public_key/elliptic */ +import elliptic from './elliptic'; /** @see module:crypto/public_key/dsa */ import dsa from './dsa.js'; export default { rsa: rsa, elgamal: elgamal, + elliptic: elliptic, dsa: dsa }; diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js new file mode 100644 index 00000000..55524053 --- /dev/null +++ b/test/crypto/elliptic.js @@ -0,0 +1,196 @@ +'use strict'; + +var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); + +var expect = require('chai').expect; + +describe('Elliptic Curve Cryptography', function () { + var elliptic_curves = openpgp.crypto.publicKey.elliptic; + var key_data = { + p256: { + priv: new Uint8Array([ + 0x2B, 0x48, 0x2B, 0xE9, 0x88, 0x74, 0xE9, 0x49, + 0x1F, 0x89, 0xCC, 0xFF, 0x0A, 0x26, 0x05, 0xA2, + 0x3C, 0x2A, 0x35, 0x25, 0x26, 0x11, 0xD7, 0xEA, + 0xA1, 0xED, 0x29, 0x95, 0xB5, 0xE1, 0x5F, 0x1D]), + pub: new Uint8Array([0x04, + 0x80, 0x2C, 0x40, 0x76, 0x31, 0x20, 0xB6, 0x9B, + 0x48, 0x3B, 0x05, 0xEB, 0x6C, 0x1E, 0x3F, 0x49, + 0x84, 0xF7, 0xD2, 0xAD, 0x16, 0xA1, 0x6F, 0x62, + 0xFD, 0xCA, 0xEC, 0xB4, 0xA0, 0xBD, 0x4C, 0x1A, + 0x6F, 0xAA, 0xE7, 0xFD, 0xC4, 0x7D, 0x89, 0xCC, + 0x06, 0xCA, 0xFE, 0xAE, 0xCD, 0x0E, 0x9E, 0x62, + 0x57, 0xA4, 0xC3, 0xE7, 0x5E, 0x69, 0x10, 0xEE, + 0x67, 0xC2, 0x09, 0xF9, 0xEF, 0xE7, 0x9E, 0x56]) + }, + p384: { + priv: new Uint8Array([ + 0xB5, 0x38, 0xDA, 0xF3, 0x77, 0x58, 0x3F, 0x94, + 0x5B, 0xC2, 0xCA, 0xC6, 0xA9, 0xFC, 0xAA, 0x3F, + 0x97, 0xB0, 0x54, 0x26, 0x10, 0xB4, 0xEC, 0x2A, + 0xA7, 0xC1, 0xA3, 0x4B, 0xC0, 0xBD, 0xFE, 0x3E, + 0xF1, 0xBE, 0x76, 0xCB, 0xE8, 0xAB, 0x3B, 0xBD, + 0xB6, 0x84, 0xC7, 0x8B, 0x91, 0x2F, 0x76, 0x8B]), + pub: new Uint8Array([0x04, + 0x44, 0x83, 0xA0, 0x3E, 0x5B, 0x0A, 0x0D, 0x9B, + 0xA0, 0x06, 0xDF, 0x38, 0xC7, 0x64, 0xCD, 0x62, + 0x7D, 0x5E, 0x3D, 0x3B, 0x50, 0xF5, 0x06, 0xC7, + 0xF7, 0x9B, 0xF0, 0xDE, 0xB1, 0x0C, 0x64, 0x74, + 0x0D, 0x03, 0x67, 0x24, 0xA0, 0xFF, 0xD1, 0x3D, + 0x03, 0x96, 0x48, 0xE7, 0x73, 0x5E, 0xF1, 0xC0, + 0x62, 0xCC, 0x33, 0x5A, 0x2A, 0x66, 0xA7, 0xAB, + 0xCA, 0x77, 0x52, 0xB8, 0xCD, 0xB5, 0x91, 0x16, + 0xAF, 0x42, 0xBB, 0x79, 0x0A, 0x59, 0x51, 0x68, + 0x8E, 0xEA, 0x32, 0x7D, 0x4A, 0x4A, 0xBB, 0x26, + 0x13, 0xFB, 0x95, 0xC0, 0xB1, 0xA4, 0x54, 0xCA, + 0xFA, 0x85, 0x8A, 0x4B, 0x58, 0x7C, 0x61, 0x39]) + }, + p521: { + priv: new Uint8Array([ + 0x00, 0xBB, 0x35, 0x27, 0xBC, 0xD6, 0x7E, 0x35, + 0xD5, 0xC5, 0x99, 0xC9, 0xB4, 0x6C, 0xEE, 0xDE, + 0x79, 0x2D, 0x77, 0xBD, 0x0A, 0x08, 0x9A, 0xC2, + 0x21, 0xF8, 0x35, 0x1C, 0x49, 0x5C, 0x40, 0x11, + 0xAC, 0x95, 0x2A, 0xEE, 0x91, 0x3A, 0x60, 0x5A, + 0x25, 0x5A, 0x95, 0x38, 0xDC, 0xEB, 0x59, 0x8E, + 0x33, 0xAD, 0xC0, 0x0B, 0x56, 0xB1, 0x06, 0x8C, + 0x57, 0x48, 0xA3, 0x73, 0xDB, 0xE0, 0x19, 0x50, + 0x2E, 0x79]), + pub: new Uint8Array([0x04, + 0x01, 0x0D, 0xD5, 0xCA, 0xD8, 0xB0, 0xEF, 0x9F, + 0x2B, 0x7E, 0x58, 0x99, 0xDE, 0x05, 0xF6, 0xF6, + 0x64, 0x6B, 0xCD, 0x59, 0x2E, 0x39, 0xB8, 0x82, + 0xB3, 0x13, 0xE6, 0x7D, 0x50, 0x85, 0xC3, 0xFA, + 0x93, 0xA5, 0x3F, 0x92, 0x85, 0x42, 0x36, 0xC0, + 0x83, 0xC9, 0xA4, 0x38, 0xB3, 0xD1, 0x99, 0xDA, + 0xE1, 0x02, 0x37, 0x7A, 0x3A, 0xC2, 0xB4, 0x55, + 0xEC, 0x1C, 0x0F, 0x00, 0x97, 0xFC, 0x75, 0x93, + 0xFE, 0x87, 0x00, 0x7D, 0xBE, 0x1A, 0xF5, 0xF9, + 0x57, 0x5C, 0xF2, 0x50, 0x2D, 0x14, 0x32, 0xEE, + 0x9B, 0xBE, 0xB3, 0x0E, 0x12, 0x2F, 0xF8, 0x85, + 0x11, 0x1A, 0x4F, 0x88, 0x50, 0xA4, 0xDB, 0x37, + 0xA6, 0x53, 0x5C, 0xB7, 0x87, 0xA6, 0x06, 0x21, + 0x15, 0xCC, 0x12, 0xC0, 0x1C, 0x83, 0x6F, 0x7B, + 0x5A, 0x8A, 0x36, 0x4E, 0x46, 0x9E, 0x54, 0x3F, + 0xE2, 0xF7, 0xED, 0x63, 0xC9, 0x92, 0xA4, 0x38, + 0x2B, 0x9C, 0xE2, 0xB7]) + }, + secp256k1: { + priv: new Uint8Array([ + 0x9E, 0xB0, 0x30, 0xD6, 0xE1, 0xCE, 0xAA, 0x0B, + 0x7B, 0x8F, 0xDE, 0x5D, 0x91, 0x4D, 0xDC, 0xA0, + 0xAD, 0x05, 0xAB, 0x8F, 0x87, 0x9B, 0x57, 0x48, + 0xAE, 0x8A, 0xE0, 0xF9, 0x39, 0xBD, 0x24, 0x00]), + pub: new Uint8Array([0x04, + 0xA8, 0x02, 0x35, 0x2C, 0xB7, 0x24, 0x95, 0x51, + 0x0A, 0x65, 0x26, 0x7D, 0xDF, 0xEA, 0x64, 0xB3, + 0xA8, 0xE1, 0x4F, 0xDD, 0x12, 0x84, 0x7E, 0x59, + 0xDB, 0x81, 0x0F, 0x89, 0xED, 0xFB, 0x29, 0xFB, + 0x07, 0x60, 0x29, 0x7D, 0x39, 0x8F, 0xB8, 0x68, + 0xF0, 0xFD, 0xA6, 0x67, 0x83, 0x55, 0x75, 0x7D, + 0xB8, 0xFD, 0x0B, 0xDF, 0x76, 0xCE, 0xBC, 0x95, + 0x4B, 0x92, 0x26, 0xFC, 0xAA, 0x7A, 0x7C, 0x3F]) + } + }; + var signature_data = { + priv: new Uint8Array([ + 0x14, 0x2B, 0xE2, 0xB7, 0x4D, 0xBD, 0x1B, 0x22, + 0x4D, 0xDF, 0x96, 0xA4, 0xED, 0x8E, 0x5B, 0xF9, + 0xBD, 0xD3, 0xFE, 0xAE, 0x3F, 0xB2, 0xCF, 0xEE, + 0xA7, 0xDB, 0xD0, 0x58, 0xA7, 0x47, 0xF8, 0x7C]), + pub: new Uint8Array([0x04, + 0xD3, 0x36, 0x11, 0xF9, 0xF9, 0xAB, 0x39, 0x23, + 0x15, 0xB9, 0x71, 0x7B, 0x2A, 0x0B, 0xA6, 0x6D, + 0x39, 0x6D, 0x64, 0x87, 0x22, 0x9A, 0xA3, 0x0A, + 0x55, 0x27, 0x14, 0x2E, 0x1C, 0x61, 0xA2, 0x8A, + 0xDA, 0x4E, 0x8F, 0xCE, 0x04, 0xBE, 0xE2, 0xC3, + 0x82, 0x0B, 0x21, 0x4C, 0xBC, 0xED, 0x0E, 0xE2, + 0xF1, 0x14, 0x33, 0x9A, 0x86, 0x5F, 0xC6, 0xF9, + 0x8E, 0x95, 0x24, 0x10, 0x1F, 0x0F, 0x13, 0xE4]), + message: new Uint8Array([ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]), + signature: { + r: new Uint8Array([ + 0xF1, 0x78, 0x1C, 0xA5, 0x13, 0x21, 0x0C, 0xBA, + 0x6F, 0x18, 0x5D, 0xB3, 0x01, 0xE2, 0x17, 0x1B, + 0x67, 0x65, 0x7F, 0xC6, 0x1F, 0x50, 0x12, 0xFB, + 0x2F, 0xD3, 0xA4, 0x29, 0xE3, 0xC2, 0x44, 0x9F]), + s: new Uint8Array([ + 0x7F, 0x08, 0x69, 0x6D, 0xBB, 0x1B, 0x9B, 0xF2, + 0x62, 0x1C, 0xCA, 0x80, 0xC6, 0x15, 0xB2, 0xAE, + 0x60, 0x50, 0xD1, 0xA7, 0x1B, 0x32, 0xF3, 0xB1, + 0x01, 0x0B, 0xDF, 0xC6, 0xAB, 0xF0, 0xEB, 0x01]) + } + }; + describe('Basic Operations', function () { + it('Creating curve with name', function (done) { + var names = ['p256', 'p384', 'p521', 'secp256k1']; + names.forEach(function (name) { + expect(elliptic_curves.get(name)).to.exist; + }); + done(); + }); + it('Creating curve from oid', function (done) { + var oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A']; + oids.forEach(function (oid) { + expect(elliptic_curves.get(openpgp.util.hex2bin(oid))).to.exist; + }); + done(); + }); + it('Creating KeyPair', function (done) { + var names = ['p256', 'p384', 'p521', 'secp256k1']; + names.forEach(function (name) { + var curve = elliptic_curves.get(name); + var keyPair = curve.genKeyPair(); + expect(keyPair).to.exist; + expect(keyPair.isValid()).to.be.true; + }); + done(); + }); + it('Creating KeyPair from data', function (done) { + for (var name in key_data) { + var pair = key_data[name]; + var curve = elliptic_curves.get(name); + expect(curve).to.exist; + var keyPair = curve.keyFromPrivate(pair.priv); + expect(keyPair).to.exist; + var pub = keyPair.getPublic(); + expect(pub).to.exist; + expect(openpgp.util.hexidump(pub)).to.equal(openpgp.util.hexidump(pair.pub)); + } + done(); + }); + it('Signature verification', function (done) { + var curve = elliptic_curves.get('p256'); + var key = curve.keyFromPublic(signature_data.pub); + expect(key.verify(signature_data.message, signature_data.signature, 8)).to.be.true; + done(); + }); + it('Invalid signature', function (done) { + var curve = elliptic_curves.get('p256'); + var key = curve.keyFromPublic(key_data.p256.pub); + expect(key.verify(signature_data.message, signature_data.signature, 8)).to.be.false; + done(); + }); + it('Signature generation', function (done) { + var curve = elliptic_curves.get('p256'); + var key = curve.keyFromPrivate(key_data.p256.priv); + var signature = key.sign(signature_data.message, 8); + key = curve.keyFromPublic(key_data.p256.pub); + expect(key.verify(signature_data.message, signature, 8)).to.be.true; + done(); + }); + it('Shared secret generation', function (done) { + var curve = elliptic_curves.get('p256'); + var key1 = curve.keyFromPrivate(key_data.p256.priv); + var key2 = curve.keyFromPublic(signature_data.pub); + var shared1 = openpgp.util.hexidump(key1.derive(key2)); + key1 = curve.keyFromPublic(key_data.p256.pub); + key2 = curve.keyFromPrivate(signature_data.priv); + var shared2 = openpgp.util.hexidump(key2.derive(key1)); + expect(shared1).to.equal(shared2); + done(); + }); + }); +}); diff --git a/test/crypto/index.js b/test/crypto/index.js index 7429aaba..a61e6f6f 100644 --- a/test/crypto/index.js +++ b/test/crypto/index.js @@ -3,6 +3,7 @@ describe('Crypto', function () { require('./hash'); require('./random.js'); require('./crypto.js'); + require('./elliptic.js'); require('./pkcs5.js'); require('./rfc3394.js'); }); From ba2f49234e3a6d76e0d8acd607d4774b8ec628e2 Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:24:22 -0300 Subject: [PATCH 09/75] Signature creation and verification --- src/crypto/public_key/elliptic/ecdsa.js | 66 +++++++++++++++++ src/crypto/public_key/elliptic/index.js | 2 + test/crypto/elliptic.js | 98 +++++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 src/crypto/public_key/elliptic/ecdsa.js diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js new file mode 100644 index 00000000..b453efc8 --- /dev/null +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -0,0 +1,66 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Implementation of ECDSA following RFC6637 for Openpgpjs + +/** + * @requires crypto/public_key/jsbn + * @requires crypto/public_key/elliptic/curves + * @module crypto/public_key/elliptic/ecdsa + */ + +'use strict'; + +import curves from './curves.js'; +import BigInteger from '../jsbn.js'; + +/** + * Sign a message using the provided key + * @param {String} oid Elliptic curve for the key + * @param {enums.hash} hash_algo Hash algorithm used to sign + * @param {Uint8Array} m Message to sign + * @param {BigInteger} w Private key used to sign + * @return {{r: BigInteger, s: BigInteger}} Signature of the message + */ +function sign(oid, hash_algo, m, w) { + const curve = curves.get(oid); + const key = curve.keyFromPrivate(w.toByteArray()); + const signature = key.sign(m, hash_algo); + return { + r: new BigInteger(signature.r), + s: new BigInteger(signature.s) + }; +} + +/** + * Verifies if a signature is valid for a message + * @param {String} oid Elliptic curve for the key + * @param {enums.hash} hash_algo Hash algorithm used in the signature + * @param {{r: BigInteger, s: BigInteger}} signature Signature to verify + * @param {Uint8Array} m Message to verify + * @param {BigInteger} gw Public key used to verify the message + */ +function verify(oid, hash_algo, signature, m, gw) { + const curve = curves.get(oid); + const key = curve.keyFromPublic(gw.toByteArray()); + return key.verify(m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo); +} + +module.exports = { + sign: sign, + verify: verify +}; diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index 1868f192..fab93a04 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -26,8 +26,10 @@ 'use strict'; import {get, generate} from './curves.js'; +import ecdsa from './ecdsa.js'; module.exports = { + ecdsa: ecdsa, get: get, generate: generate }; diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 55524053..e4b56fa2 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -4,6 +4,13 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : var expect = require('chai').expect; +var bin2bi = function (bytes) { + var mpi = new openpgp.MPI(); + bytes = openpgp.util.bin2str(bytes); + mpi.fromBytes(bytes); + return mpi.toBigInteger(); +}; + describe('Elliptic Curve Cryptography', function () { var elliptic_curves = openpgp.crypto.publicKey.elliptic; var key_data = { @@ -193,4 +200,95 @@ describe('Elliptic Curve Cryptography', function () { done(); }); }); + describe('ECDSA signature', function () { + var verify_signature = function (oid, hash, r, s, pub, message) { + if (openpgp.util.isString(message)) { + message = openpgp.util.str2Uint8Array(message); + } else if (!openpgp.util.isUint8Array(message)) { + message = new Uint8Array(message); + } + return function () { + var ecdsa = elliptic_curves.ecdsa; + return ecdsa.verify(oid, + hash, + {r: bin2bi(r), s: bin2bi(s)}, + message, + bin2bi(pub) + ); + }; + }; + var secp256k1_dummy_value = new Uint8Array([ + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + var secp256k1_dummy_point = new Uint8Array([0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + var secp256k1_invalid_point = new Uint8Array([0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); + it('Invalid curve oid', function (done) { + var res = verify_signature('invalid oid', 8, [], [], [], []); + expect(res).to.throw(Error, /Not valid curve/); + res = verify_signature("\x00", 8, [], [], [], []); + expect(res).to.throw(Error, /Not valid curve/); + done(); + }); + it('Invalid public key', function (done) { + var res = verify_signature('secp256k1', 8, [], [], [], []); + expect(res).to.throw(Error, /Unknown point format/); + res = verify_signature('secp256k1', 8, [], [], secp256k1_invalid_point, []); + expect(res).to.throw(Error, /Unknown point format/); + done(); + }); + it('Invalid signature', function (done) { + var res = verify_signature('secp256k1', 8, [], [], secp256k1_dummy_point, []); + expect(res()).to.be.false; + done(); + }); + + var p384_message = new Uint8Array([ + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]); + var p384_r = new Uint8Array([ + 0x9D, 0x07, 0xCA, 0xA5, 0x9F, 0xBE, 0xB8, 0x76, + 0xA9, 0xB9, 0x66, 0x0F, 0xA0, 0x64, 0x70, 0x5D, + 0xE6, 0x37, 0x40, 0x43, 0xD0, 0x8E, 0x40, 0xA8, + 0x8B, 0x37, 0x83, 0xE7, 0xBC, 0x1C, 0x4C, 0x86, + 0xCB, 0x3C, 0xD5, 0x9B, 0x68, 0xF0, 0x65, 0xEB, + 0x3A, 0xB6, 0xD6, 0xA6, 0xCF, 0x85, 0x3D, 0xA9]); + var p384_s = new Uint8Array([ + 0x32, 0x85, 0x78, 0xCC, 0xEA, 0xC5, 0x22, 0x83, + 0x10, 0x73, 0x1C, 0xCF, 0x10, 0x8A, 0x52, 0x11, + 0x8E, 0x49, 0x9E, 0xCF, 0x7E, 0x17, 0x18, 0xC3, + 0x11, 0x11, 0xBC, 0x0F, 0x6D, 0x98, 0xE2, 0x16, + 0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21, + 0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC]); + it('Valid signature', function (done) { + var res = verify_signature('p384', 8, p384_r, p384_s, key_data.p384.pub, p384_message); + expect(res()).to.be.true; + done(); + }); + it('Sign and verify message', function (done) { + var curve = elliptic_curves.get('p521'); + var keyPair = curve.genKeyPair(); + var keyPublic = bin2bi(keyPair.getPublic()); + var keyPrivate = bin2bi(keyPair.getPrivate()); + var oid = curve.oid; + var message = p384_message; + var signature = elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate); + var verified = elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic); + expect(verified).to.be.true; + done(); + }); + }); }); From 673151ec877631cb07a35d9091707224e2169ebd Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:24:49 -0300 Subject: [PATCH 10/75] Read and write ECDSA packets --- src/crypto/crypto.js | 10 ++++++++++ src/crypto/signature.js | 19 +++++++++++++++++++ src/enums.js | 3 ++- src/key.js | 3 ++- src/packet/public_key.js | 9 +++++++-- src/packet/signature.js | 4 ++-- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index f6e17403..d877ea63 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -144,6 +144,10 @@ export default { // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. return 1; + case 'ecdsa': + // Algorithm-Specific Fields for ECDSA secret keys: + // - MPI of an integer representing the secret key. + return 1; default: throw new Error('Unknown algorithm'); } @@ -175,6 +179,12 @@ export default { case 'dsa': return 4; + // Algorithm-Specific Fields for ECDSA public keys: + // - OID of curve; + // - MPI of EC point representing public key. + case 'ecdsa': + return 2; + default: throw new Error('Unknown algorithm.'); } diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 6ab0ce9f..3638ad32 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -56,6 +56,15 @@ export default { m = data; var dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y); return dopublic.compareTo(s1) === 0; + case 19: + // ECDSA + const ecdsa = publicKey.elliptic.ecdsa; + const curve = publickey_MPIs[0]; + const r = msg_MPIs[0].toBigInteger(); + const s = msg_MPIs[1].toBigInteger(); + m = data; + const gw = publickey_MPIs[1].toBigInteger(); + return ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, gw); default: throw new Error('Invalid signature algorithm.'); } @@ -107,6 +116,16 @@ export default { case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.'); + + case 19: + // ECDSA + const ecdsa = publicKey.elliptic.ecdsa; + const curve = keyIntegers[0]; + const w = keyIntegers[2].toBigInteger(); + m = data; + const signature = ecdsa.sign(curve.oid, hash_algo, m, w); + return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); + default: throw new Error('Invalid signature algorithm.'); } diff --git a/src/enums.js b/src/enums.js index 861f2f01..bee7b985 100644 --- a/src/enums.js +++ b/src/enums.js @@ -26,7 +26,8 @@ export default { rsa_encrypt: 2, rsa_sign: 3, elgamal: 16, - dsa: 17 + dsa: 17, + ecdsa: 19 }, /** {@link http://tools.ietf.org/html/rfc4880#section-9.2|RFC4880, section 9.2} diff --git a/src/key.js b/src/key.js index e93ffb30..75fcf772 100644 --- a/src/key.js +++ b/src/key.js @@ -339,7 +339,8 @@ function isValidEncryptionKeyPacket(keyPacket, signature) { function isValidSigningKeyPacket(keyPacket, signature) { return (keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.dsa) || keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.rsa_sign) || - keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.rsa_encrypt_sign)) && + keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.rsa_encrypt_sign) || + keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.ecdsa)) && (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0); } diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 6542998b..9a083350 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -26,6 +26,7 @@ * @requires enums * @requires type/keyid * @requires type/mpi + * @requires type/oid * @requires util * @module packet/public_key */ @@ -35,6 +36,7 @@ import util from '../util.js'; import type_mpi from '../type/mpi.js'; import type_keyid from '../type/keyid.js'; +import type_oid from '../type/oid.js'; import enums from '../enums.js'; import crypto from '../crypto'; @@ -100,8 +102,11 @@ PublicKey.prototype.read = function (bytes) { var p = 0; for (var i = 0; i < mpicount && p < bmpi.length; i++) { - - this.mpi[i] = new type_mpi(); + if (this.algorithm === 'ecdsa' && i === 0) { + this.mpi[i] = new type_oid(); + } else { + this.mpi[i] = new type_mpi(); + } p += this.mpi[i].read(bmpi.subarray(p, bmpi.length)); diff --git a/src/packet/signature.js b/src/packet/signature.js index 25e545c9..b5f72d69 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -629,10 +629,10 @@ Signature.prototype.verify = function (key, data) { if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) { mpicount = 1; } - // Algorithm-Specific Fields for DSA signatures: + // Algorithm-Specific Fields for DSA and ECDSA signatures: // - MPI of DSA value r. // - MPI of DSA value s. - else if (publicKeyAlgorithm === 17) { + else if (publicKeyAlgorithm === 17 || publicKeyAlgorithm === 19) { mpicount = 2; } From 585a2bd69defda131f9c5ebcd09508bb8788993f Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:25:08 -0300 Subject: [PATCH 11/75] Functions to encrypt and decrypt ECDH keys --- src/crypto/public_key/elliptic/ecdh.js | 129 ++++++++++++++++++++++++ src/crypto/public_key/elliptic/index.js | 3 + src/enums.js | 1 + test/crypto/elliptic.js | 54 ++++++++++ 4 files changed, 187 insertions(+) create mode 100644 src/crypto/public_key/elliptic/ecdh.js diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js new file mode 100644 index 00000000..fc28064d --- /dev/null +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -0,0 +1,129 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2015-2016 Decentral +// +// 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 + +// Key encryption and decryption for RFC 6637 ECDH + +/** + * @requires crypto/hash + * @requires crypto/cipher + * @requires crypto/rfc3394 + * @requires crypto/public_key/elliptic/curves + * @requires crypto/public_key/jsbn + * @requires type/oid + * @requires type/kdf_params + * @requires enums + * @requires util + * @module crypto/public_key/elliptic/ecdh + */ + +'use strict'; + +import BigInteger from '../jsbn.js'; +import curves from './curves.js'; +import cipher from '../../cipher'; +import hash from '../../hash'; +import rfc3394 from '../../rfc3394.js'; +import enums from '../../../enums.js'; +import util from '../../../util.js'; +import type_kdf_params from '../../../type/kdf_params.js'; +import type_oid from '../../../type/oid.js'; + +function hex2Uint8Array(hex) { + var result = new Uint8Array(hex.length/2); + for (var k=0; k Date: Fri, 12 Aug 2016 00:25:21 -0300 Subject: [PATCH 12/75] Parse and create ECDH packets --- src/crypto/crypto.js | 62 +++++++++++++------ src/packet/public_key.js | 6 +- .../public_key_encrypted_session_key.js | 43 ++++++++++--- 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index d877ea63..88849808 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -21,6 +21,7 @@ * @requires crypto/cipher * @requires crypto/public_key * @requires crypto/random + * @requires type/ecdh_symkey * @requires type/mpi * @module crypto/crypto */ @@ -30,8 +31,19 @@ import random from './random.js'; import cipher from './cipher'; import publicKey from './public_key'; +import type_ecdh_symkey from '../type/ecdh_symkey.js'; import type_mpi from '../type/mpi.js'; +function BigInteger2mpi(bn) { + var mpi = new type_mpi(); + mpi.fromBigInteger(bn); + return mpi; +} + +function mapResult(result) { + return result.map(BigInteger2mpi); +} + export default { /** * Encrypts data using the specified public key multiprecision integers @@ -42,7 +54,7 @@ export default { * @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) { + publicKeyEncrypt: function(algo, publicMPIs, data, fingerprint) { var result = (function() { var m; switch (algo) { @@ -52,7 +64,7 @@ export default { var n = publicMPIs[0].toBigInteger(); var e = publicMPIs[1].toBigInteger(); m = data.toBigInteger(); - return [rsa.encrypt(m, e, n)]; + return mapResult([rsa.encrypt(m, e, n)]); case 'elgamal': var elgamal = new publicKey.elgamal(); @@ -60,18 +72,22 @@ export default { var g = publicMPIs[1].toBigInteger(); var y = publicMPIs[2].toBigInteger(); m = data.toBigInteger(); - return elgamal.encrypt(m, g, p, y); + return mapResult(elgamal.encrypt(m, g, p, y)); + + case 'ecdh': + var ecdh = publicKey.elliptic.ecdh; + var curve = publicMPIs[0]; + var kdf_params = publicMPIs[2]; + var R = publicMPIs[1].toBigInteger(); + var res = ecdh.encrypt(curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint); + return [BigInteger2mpi(res.V), new type_ecdh_symkey(res.C)]; default: return []; } })(); - return result.map(function(bn) { - var mpi = new type_mpi(); - mpi.fromBigInteger(bn); - return mpi; - }); + return result; }, /** @@ -86,7 +102,7 @@ export default { * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null */ - publicKeyDecrypt: function(algo, keyIntegers, dataIntegers) { + publicKeyDecrypt: function(algo, keyIntegers, dataIntegers, fingerprint) { var p; var bn = (function() { @@ -111,6 +127,16 @@ export default { var c2 = dataIntegers[1].toBigInteger(); p = keyIntegers[0].toBigInteger(); return elgamal.decrypt(c1, c2, p, x); + + case 'ecdh': + var ecdh = publicKey.elliptic.ecdh; + var curve = keyIntegers[0]; + var kdf_params = keyIntegers[2]; + var V = dataIntegers[0].toBigInteger(); + var C = dataIntegers[1].data; + var r = keyIntegers[3].toBigInteger(); + return ecdh.decrypt(curve.oid, kdf_params.cipher, kdf_params.hash, V, C, r, fingerprint); + default: return null; } @@ -144,8 +170,9 @@ export default { // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. return 1; + case 'ecdh': case 'ecdsa': - // Algorithm-Specific Fields for ECDSA secret keys: + // Algorithm-Specific Fields for ECDSA or ECDH secret keys: // - MPI of an integer representing the secret key. return 1; default: @@ -185,6 +212,13 @@ export default { case 'ecdsa': return 2; + // Algorithm-Specific Fields for ECDH public keys: + // - OID of curve; + // - MPI of EC point representing public key. + // - variable-length field containing KDF parameters. + case 'ecdh': + return 3; + default: throw new Error('Unknown algorithm.'); } @@ -210,14 +244,6 @@ export default { default: throw 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/packet/public_key.js b/src/packet/public_key.js index 9a083350..8e322248 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -24,6 +24,7 @@ * major versions. Consequently, this section is complex. * @requires crypto * @requires enums + * @requires type/kdf_params * @requires type/keyid * @requires type/mpi * @requires type/oid @@ -35,6 +36,7 @@ import util from '../util.js'; import type_mpi from '../type/mpi.js'; +import type_kdf_params from '../type/kdf_params.js'; import type_keyid from '../type/keyid.js'; import type_oid from '../type/oid.js'; import enums from '../enums.js'; @@ -102,8 +104,10 @@ PublicKey.prototype.read = function (bytes) { var p = 0; for (var i = 0; i < mpicount && p < bmpi.length; i++) { - if (this.algorithm === 'ecdsa' && i === 0) { + if ((this.algorithm === 'ecdsa' || this.algorithm === 'ecdh') && i === 0) { this.mpi[i] = new type_oid(); + } else if (this.algorithm === 'ecdh' && i === 2) { + this.mpi[i] = new type_kdf_params(); } else { this.mpi[i] = new type_mpi(); } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index fcdd71d0..673ebc3d 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -31,6 +31,7 @@ * decrypt the message. * @requires crypto * @requires enums + * @requires type/ecdh_symkey * @requires type/keyid * @requires type/mpi * @requires util @@ -41,6 +42,7 @@ import type_keyid from '../type/keyid.js'; import util from '../util.js'; +import type_ecdh_symkey from '../type/ecdh_symkey.js'; import type_mpi from '../type/mpi.js'; import enums from '../enums.js'; import crypto from '../crypto'; @@ -88,6 +90,9 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { case 'elgamal': return 2; + case 'ecdh': + return 2; + default: throw new Error("Invalid algorithm."); } @@ -96,7 +101,12 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { this.encrypted = []; for (var j = 0; j < integerCount; j++) { - var mpi = new type_mpi(); + var mpi; + if (this.publicKeyAlgorithm === 'ecdh' && j === 1) { + mpi = new type_ecdh_symkey(); + } else { + mpi = new type_mpi(); + } i += mpi.read(bytes.subarray(i, bytes.length)); this.encrypted.push(mpi); } @@ -126,15 +136,21 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { var checksum = util.calc_checksum(this.sessionKey); data += util.Uint8Array2str(util.writeNumber(checksum, 2)); - var mpi = new type_mpi(); - mpi.fromBytes(crypto.pkcs1.eme.encode( - data, - key.mpi[0].byteLength())); + var mpi; + if (this.publicKeyAlgorithm === 'ecdh') { + mpi = util.str2Uint8Array(crypto.pkcs5.addPadding(data)); + } else { + mpi = new type_mpi(); + mpi.fromBytes(crypto.pkcs1.eme.encode( + data, + key.mpi[0].byteLength())); + } this.encrypted = crypto.publicKeyEncrypt( this.publicKeyAlgorithm, key.mpi, - mpi); + mpi, + key.fingerprint); }; /** @@ -149,11 +165,18 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = function (key) { var result = crypto.publicKeyDecrypt( this.publicKeyAlgorithm, key.mpi, - this.encrypted).toBytes(); + this.encrypted, + key.fingerprint).toBytes(); - var checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); - - var decoded = crypto.pkcs1.eme.decode(result); + var checksum; + var decoded; + if (this.publicKeyAlgorithm === 'ecdh') { + decoded = crypto.pkcs5.removePadding(result); + checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); + } else { + decoded = crypto.pkcs1.eme.decode(result); + checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); + } key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2)); From 075d7f8e0e7cda08d8e344f77d02447e01ef62cf Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:25:36 -0300 Subject: [PATCH 13/75] Encrypt, decrypt, sign and verify with elliptic curve certificates --- test/general/ecc.js | 214 ++++++++++++++++++++++++++++++++++++++++++ test/general/index.js | 1 + 2 files changed, 215 insertions(+) create mode 100644 test/general/ecc.js diff --git a/test/general/ecc.js b/test/general/ecc.js new file mode 100644 index 00000000..61bef4b6 --- /dev/null +++ b/test/general/ecc.js @@ -0,0 +1,214 @@ +'use strict'; + +var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); + +var chai = require('chai'), + expect = chai.expect; + +describe('Elliptic Curve Cryptography', function () { + var data = { + romeo: { + id: 'c2b12389b401a43d', + pass: 'juliet', + pub: [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + 'Version: OpenPGP.js 1.3+secp256k1', + 'Comment: http://openpgpjs.org', + '', + 'xk8EVjET2xMFK4EEAAoCAwS/zT2gefLhEnISXN3rvdV3eD6MVrPwxNMAR+LM', + 'ZzFO1gdtZbf7XQSZP02CYQe3YFrNQYYuJ4CGkTvOVJSV+yrAzS5Sb21lbyBN', + 'b250YWd1ZSAoc2VjcDI1NmsxKSA8cm9tZW9AZXhhbXBsZS5uZXQ+wnIEEBMI', + 'ACQFAlYxE9sFCwkIBwMJEMKxI4m0AaQ9AxUICgMWAgECGwMCHgEAAOjHAQDM', + 'y6EJPFayCgI4ZSmZlSue3xFShj9y6hZTLZqPJquspQD+MMT00a2Cicnbhrd1', + '8SQUIYRQ//I7oXVoxZN5MA4rmOHOUwRWMRPbEgUrgQQACgIDBLPZgGC257Ra', + 'Z9Bg3ij9OgSoJGwqIu03SfQMTnR2crHkAHqLaUImz/lwhsL/V499zXZ2gEmf', + 'oKCacroXNDM85xUDAQgHwmEEGBMIABMFAlYxE9sJEMKxI4m0AaQ9AhsMAADk', + 'gwEA4B3lysFe/3+KE/PgCSZkUfx7n7xlKqMiqrX+VNyPej8BAMQJgtMVdslQ', + 'HLr5fhoGnRots3JSC0j20UQQOKVOXaW3', + '=VpL9', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'), + priv: [ + '-----BEGIN PGP PRIVATE KEY BLOCK-----', + 'Version: OpenPGP.js 1.3+secp256k1', + 'Comment: http://openpgpjs.org', + '', + 'xaIEVjET2xMFK4EEAAoCAwS/zT2gefLhEnISXN3rvdV3eD6MVrPwxNMAR+LM', + 'ZzFO1gdtZbf7XQSZP02CYQe3YFrNQYYuJ4CGkTvOVJSV+yrA/gkDCILD3FP2', + 'D6eRYNWhI+QTFWAGDw+pIhtXQ/p0zZgK6HSk68Fox0tH6TlGtPmtULkPExs0', + 'cnIdAVSMHI+SnZ9lIeAykAcFoqJYIO5p870XbjzNLlJvbWVvIE1vbnRhZ3Vl', + 'IChzZWNwMjU2azEpIDxyb21lb0BleGFtcGxlLm5ldD7CcgQQEwgAJAUCVjET', + '2wULCQgHAwkQwrEjibQBpD0DFQgKAxYCAQIbAwIeAQAA6McBAMzLoQk8VrIK', + 'AjhlKZmVK57fEVKGP3LqFlMtmo8mq6ylAP4wxPTRrYKJyduGt3XxJBQhhFD/', + '8juhdWjFk3kwDiuY4cemBFYxE9sSBSuBBAAKAgMEs9mAYLbntFpn0GDeKP06', + 'BKgkbCoi7TdJ9AxOdHZyseQAeotpQibP+XCGwv9Xj33NdnaASZ+goJpyuhc0', + 'MzznFQMBCAf+CQMIqp5StLTK+lBgqmaJ8/64E+8+OJVOgzk8EoRp8bS9IEac', + 'VYu2i8ARjAF3sqwGZ5hxxsniORcjQUghf+n+NwEm9LUWfbAGUlT4YfSIq5pV', + 'rsJhBBgTCAATBQJWMRPbCRDCsSOJtAGkPQIbDAAA5IMBAOAd5crBXv9/ihPz', + '4AkmZFH8e5+8ZSqjIqq1/lTcj3o/AQDECYLTFXbJUBy6+X4aBp0aLbNyUgtI', + '9tFEEDilTl2ltw==', + '=C3TW', + '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'), + message: 'Shall I hear more, or shall I speak at this?' + }, + juliet: { + id: '64116021959bdfe0', + pass: 'romeo', + pub: [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + 'Version: OpenPGP.js 1.3+secp256k1', + 'Comment: http://openpgpjs.org', + '', + 'xk8EVjEUUBMFK4EEAAoCAwQRNz0sbftAv3SSE0fm7vE0pD96NDA3YtGdObaj', + 'D0DNUMBL1eoLl5/qdJUc/16xbZLkL2saMsbqtPn/iuahz6bkzS9KdWxpZXQg', + 'Q2FwdWxldCAoc2VjcDI1NmsxKSA8anVsaWV0QGV4YW1wbGUubmV0PsJyBBAT', + 'CAAkBQJWMRRRBQsJCAcDCRBkEWAhlZvf4AMVCAoDFgIBAhsDAh4BAAAr1wEA', + '+39TqKy/tks7dPlEYw+IYkFCW99a60kiSCjLBPxEgNUA/3HeLDP/XbrgklUs', + 'DFOy20aHE7M6i/cFXLLxDJmN6BF3zlMEVjEUUBIFK4EEAAoCAwTQ02rHHP/d', + 'kR4W7y5BY4kRtoNc/HxUloOpxA8svfmxwOoP5stCS/lInD8K+7nSEiPr84z9', + 'EQ47LMjiT1zK2mHZAwEIB8JhBBgTCAATBQJWMRRRCRBkEWAhlZvf4AIbDAAA', + '7FoA/1Y4xDYO49u21I7aqjPyTygLoObdLMAtK6xht+DDc0YKAQDNp2wv0HOJ', + '+0kjoUNu6PRIll/jMgTVAXn0Mov6HqJ95A==', + '=ISmy', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'), + priv: [ + '-----BEGIN PGP PRIVATE KEY BLOCK-----', + 'Version: OpenPGP.js 1.3+secp256k1', + 'Comment: http://openpgpjs.org', + '', + 'xaIEVjEUUBMFK4EEAAoCAwQRNz0sbftAv3SSE0fm7vE0pD96NDA3YtGdObaj', + 'D0DNUMBL1eoLl5/qdJUc/16xbZLkL2saMsbqtPn/iuahz6bk/gkDCD9EH0El', + '7o9qYIbX56Ri3VlfCbpQgy1cVx9RETKI4guW9vUu6SeY2NhXASvfK+zgpLzO', + 'j+hv2a+re549UKBdFbPEcyPUQKo2YJ1AfdAfZcDNL0p1bGlldCBDYXB1bGV0', + 'IChzZWNwMjU2azEpIDxqdWxpZXRAZXhhbXBsZS5uZXQ+wnIEEBMIACQFAlYx', + 'FFEFCwkIBwMJEGQRYCGVm9/gAxUICgMWAgECGwMCHgEAACvXAQD7f1OorL+2', + 'Szt0+URjD4hiQUJb31rrSSJIKMsE/ESA1QD/cd4sM/9duuCSVSwMU7LbRocT', + 'szqL9wVcsvEMmY3oEXfHpgRWMRRQEgUrgQQACgIDBNDTascc/92RHhbvLkFj', + 'iRG2g1z8fFSWg6nEDyy9+bHA6g/my0JL+UicPwr7udISI+vzjP0RDjssyOJP', + 'XMraYdkDAQgH/gkDCA4aIC5h7thWYEM9KvwVEN4/rAYOWVNzUN2K7l25M+NZ', + '1/mEAjEgEW9yPufKtF3hILeNdPBwh6Gcw/0gOJ/9yJwKk7tqwyS/gKF1+VDm', + 'X0LCYQQYEwgAEwUCVjEUUQkQZBFgIZWb3+ACGwwAAOxaAP9WOMQ2DuPbttSO', + '2qoz8k8oC6Dm3SzALSusYbfgw3NGCgEAzadsL9BziftJI6FDbuj0SJZf4zIE', + '1QF59DKL+h6ifeQ=', + '=QvXN', + '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'), + message: 'O Romeo, Romeo! Wherefore art thou Romeo?', + message_signed: [ + '-----BEGIN PGP SIGNED MESSAGE-----', + 'Hash: SHA256', + '', + 'O Romeo, Romeo! Wherefore art thou Romeo?', + '-----BEGIN PGP SIGNATURE-----', + 'Version: GnuPG v2', + 'Comment: GnuPG v2.1+libgcrypt-1.7', + '', + 'iF4EARMIAAYFAlYxF8oACgkQZBFgIZWb3+BfTwD/b1yKtFnKrRjELuD6/gOH9/er', + '6yc7nzn1FBYFzMz8aFIA/3FlcIvR+eLvRTVmfiEatB6IU6JviBnzxR1gA/SOdyS2', + '=GCiR', + '-----END PGP SIGNATURE-----'].join('\n'), + message_encrypted: [ + '-----BEGIN PGP MESSAGE-----', + 'Version: GnuPG v2', + 'Comment: GnuPG v2.1+libgcrypt-1.7', + '', + 'hH4DDYFqRW5CSpsSAgMERfIYgKzriOCHTTQnWhM4VZ6cLjrjJbOaW1VuCfeN03d+', + 'yzhW1Sm1BYYdqxPE0rvjvGfD8VmMB6etaHQsrDQflzA+vGeVa9Mn/wyKq4+j13ur', + 'NOoUhDKX27+LEBNfho6bbEN72J7z3E5/+wVr+wEt3bLSwBcBvuNNkvGCpE19/AmL', + 'GP2lmjE6O9VfiW0o8sxfa+hPEq2A+6DxvMhxi2YPS0f9MMPqn5NFx2PCIGdC0+xY', + 'f0BXl1atBO1z6UXTC9aHH7UULKdynr4nUEkDa3DJW/feCSC6rQxTikn/Gf4341qQ', + 'aiwv66jhgJSdB+2+JrHfh6Znvv2fhl3SQl8K0CiG8Q0QubWdlQwNaNSOmgH7v3T8', + 'j5FhrMbD3Z+TPlrNjJqidAV28XwSBFvhw8Jf5WpaewOxVlxLjUHnnkUGHyvfdEr/', + 'DP/V1yLuBUZuRg==', + '=GEAB', + '-----END PGP MESSAGE-----'].join('\n') + } + }; + function load_pub_key(name) { + if (data[name].pub_key) { + return data[name].pub_key; + } + var pub = openpgp.key.readArmored(data[name].pub); + expect(pub).to.exist; + expect(pub.err).to.not.exist; + expect(pub.keys).to.have.length(1); + expect(pub.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id); + data[name].pub_key = pub.keys[0]; + return data[name].pub_key; + } + function load_priv_key(name) { + if (data[name].priv_key) { + return data[name].priv_key; + } + var pk = openpgp.key.readArmored(data[name].priv); + expect(pk).to.exist; + expect(pk.err).to.not.exist; + expect(pk.keys).to.have.length(1); + expect(pk.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id); + expect(pk.keys[0].decrypt(data[name].pass)).to.be.true; + data[name].priv_key = pk.keys[0]; + return data[name].priv_key; + } + it('Load public key', function (done) { + load_pub_key('romeo'); + load_pub_key('juliet'); + done(); + }); + it('Load private key', function (done) { + load_priv_key('romeo'); + load_priv_key('juliet'); + done(); + }); + it('Verify clear signed message', function (done) { + var pub = load_pub_key('juliet'); + var msg = openpgp.cleartext.readArmored(data.juliet.message_signed); + openpgp.verify({publicKeys: [pub], message: msg}).then(function(result) { + expect(result).to.exist; + expect(result.data.trim()).to.equal(data.juliet.message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + done(); + }); + }); + it('Sign message', function (done) { + var romeo = load_priv_key('romeo'); + openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) { + var romeo = load_pub_key('romeo'); + var msg = openpgp.cleartext.readArmored(signed.data); + openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) { + expect(result).to.exist; + expect(result.data.trim()).to.equal(data.romeo.message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + done(); + }); + }); + }); + it('Decrypt and verify message', function (done) { + var juliet = load_pub_key('juliet'); + var romeo = load_priv_key('romeo'); + var msg = openpgp.message.readArmored(data.juliet.message_encrypted); + openpgp.decrypt({privateKey: romeo, publicKeys: [juliet], message: msg}).then(function (result) { + expect(result).to.exist; + // trim required because https://github.com/openpgpjs/openpgpjs/issues/311 + expect(result.data.trim()).to.equal(data.juliet.message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + done(); + }); + }); + it('Encrypt and sign message', function (done) { + var romeo = load_priv_key('romeo'); + var juliet = load_pub_key('juliet'); + openpgp.encrypt({publicKeys: [juliet], privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (encrypted) { + var message = openpgp.message.readArmored(encrypted.data); + var romeo = load_pub_key('romeo'); + var juliet = load_priv_key('juliet'); + openpgp.decrypt({privateKey: juliet, publicKeys: [romeo], message: message}).then(function (result) { + expect(result).to.exist; + expect(result.data.trim()).to.equal(data.romeo.message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + done(); + }); + }); + }); +}); diff --git a/test/general/index.js b/test/general/index.js index aed972e7..01f27d19 100644 --- a/test/general/index.js +++ b/test/general/index.js @@ -8,5 +8,6 @@ describe('General', function () { require('./openpgp.js'); require('./hkp.js'); require('./oid.js'); + require('./ecc.js'); }); From a5d9e6d09e18b43af36766400efb5eb0f5120f3a Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:25:47 -0300 Subject: [PATCH 14/75] Generation of keys for elliptic curves --- src/crypto/crypto.js | 26 +++++++++++++++++++++++++- src/key.js | 16 ++++++++++++---- src/openpgp.js | 8 ++++++-- src/packet/secret_key.js | 4 ++-- test/general/ecc.js | 15 +++++++++++++++ 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 88849808..73568598 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -22,7 +22,9 @@ * @requires crypto/public_key * @requires crypto/random * @requires type/ecdh_symkey + * @requires type/kdf_params * @requires type/mpi + * @requires type/oid * @module crypto/crypto */ @@ -32,7 +34,9 @@ import random from './random.js'; import cipher from './cipher'; import publicKey from './public_key'; import type_ecdh_symkey from '../type/ecdh_symkey.js'; +import type_kdf_params from '../type/kdf_params.js'; import type_mpi from '../type/mpi.js'; +import type_oid from '../type/oid.js'; function BigInteger2mpi(bn) { var mpi = new type_mpi(); @@ -224,7 +228,7 @@ export default { } }, - generateMpi: function(algo, bits) { + generateMpi: function(algo, bits, curve) { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': @@ -241,6 +245,26 @@ export default { output.push(keyObject.u); return mapResult(output); }); + + case 'ecdsa': + return publicKey.elliptic.generate(curve).then(function (key) { + return [ + new type_oid(key.oid), + BigInteger2mpi(key.R), + BigInteger2mpi(key.r) + ]; + }); + + case 'ecdh': + return publicKey.elliptic.generate(curve).then(function (key) { + return [ + new type_oid(key.oid), + BigInteger2mpi(key.R), + new type_kdf_params(key.hash, key.cipher), + BigInteger2mpi(key.r) + ]; + }); + default: throw new Error('Unsupported algorithm for key generation.'); } diff --git a/src/key.js b/src/key.js index 75fcf772..1a6ae9b9 100644 --- a/src/key.js +++ b/src/key.js @@ -1115,8 +1115,12 @@ export function readArmored(armoredText) { export function generate(options) { var secretKeyPacket, secretSubkeyPacket; return Promise.resolve().then(() => { + if (options.curve) { + options.keyType = enums.publicKey.ecdsa; + } options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; - if (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 && + options.keyType !== enums.publicKey.ecdsa) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated throw new Error('Only RSA Encrypt or Sign supported'); } @@ -1135,13 +1139,17 @@ export function generate(options) { function generateSecretKey() { secretKeyPacket = new packet.SecretKey(); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - return secretKeyPacket.generate(options.numBits); + return secretKeyPacket.generate(options.numBits, options.curve); } function generateSecretSubkey() { secretSubkeyPacket = new packet.SecretSubkey(); - secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - return secretSubkeyPacket.generate(options.numBits); + var subkeyType = options.keyType; + if (subkeyType === enums.publicKey.ecdsa) { + subkeyType = enums.publicKey.ecdh; + } + secretSubkeyPacket.algorithm = enums.read(enums.publicKey, subkeyType); + return secretSubkeyPacket.generate(options.numBits, options.curve); } } diff --git a/src/openpgp.js b/src/openpgp.js index ba4d27af..88074dfd 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -97,8 +97,9 @@ export function destroyWorker() { * { key:Key, privateKeyArmored:String, publicKeyArmored:String } * @static */ -export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0 } = {}) { - const options = formatUserIds({ userIds, passphrase, numBits, unlocked, keyExpirationTime }); + +export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, curve=null } = {}) { + const options = formatUserIds({ userIds, passphrase, numBits, unlocked, curve }); if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported return asyncProxy.delegate('generateKey', options); @@ -455,6 +456,9 @@ function checkCleartextOrMessage(message) { * Format user ids for internal use. */ function formatUserIds(options) { + if (!options.curve) { + delete options.curve; + } if (!options.userIds) { return options; } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index e4f88e41..8e525e41 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -273,10 +273,10 @@ SecretKey.prototype.decrypt = function (passphrase) { return true; }; -SecretKey.prototype.generate = function (bits) { +SecretKey.prototype.generate = function (bits, curve) { var self = this; - return crypto.generateMpi(self.algorithm, bits).then(function(mpi) { + return crypto.generateMpi(self.algorithm, bits, curve).then(function(mpi) { self.mpi = mpi; self.isDecrypted = true; }); diff --git a/test/general/ecc.js b/test/general/ecc.js index 61bef4b6..b4522604 100644 --- a/test/general/ecc.js +++ b/test/general/ecc.js @@ -211,4 +211,19 @@ describe('Elliptic Curve Cryptography', function () { }); }); }); + it('Generate key', function (done) { + var options = { + userIds: {name: "Hamlet (secp256k1)", email: "hamlet@example.net"}, + curve: "secp256k1", + passphrase: "ophelia" + }; + openpgp.generateKey(options).then(function (key) { + expect(key).to.exist; + expect(key.key).to.exist; + expect(key.key.primaryKey).to.exist; + expect(key.privateKeyArmored).to.exist; + expect(key.publicKeyArmored).to.exist; + done(); + }); + }); }); From f87e759cb03a3fc9c2f82f2bdc7b324e7a38c05d Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:25:57 -0300 Subject: [PATCH 15/75] Create key from provided material --- src/crypto/crypto.js | 6 +++--- src/crypto/public_key/elliptic/curves.js | 9 +++++++-- src/key.js | 12 ++++++++++-- src/openpgp.js | 7 +++++-- src/packet/secret_key.js | 4 ++-- 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 73568598..784ae0f6 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -228,7 +228,7 @@ export default { } }, - generateMpi: function(algo, bits, curve) { + generateMpi: function(algo, bits, curve, material) { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': @@ -247,7 +247,7 @@ export default { }); case 'ecdsa': - return publicKey.elliptic.generate(curve).then(function (key) { + return publicKey.elliptic.generate(curve, material).then(function (key) { return [ new type_oid(key.oid), BigInteger2mpi(key.R), @@ -256,7 +256,7 @@ export default { }); case 'ecdh': - return publicKey.elliptic.generate(curve).then(function (key) { + return publicKey.elliptic.generate(curve, material).then(function (key) { return [ new type_oid(key.oid), BigInteger2mpi(key.R), diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index c6b33477..faabd656 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -97,10 +97,15 @@ function get(oid_or_name) { throw new Error('Not valid curve'); } -function generate(curve) { +function generate(curve, material) { return new Promise(function (resolve) { curve = get(curve); - var keyPair = curve.genKeyPair(); + var keyPair; + if (typeof(material) !== "undefined") { + keyPair = curve.keyFromPrivate(material); + } else { + keyPair = curve.genKeyPair(); + } resolve({ oid: curve.oid, R: new BigInteger(keyPair.getPublic()), diff --git a/src/key.js b/src/key.js index 1a6ae9b9..241326de 100644 --- a/src/key.js +++ b/src/key.js @@ -1139,7 +1139,11 @@ export function generate(options) { function generateSecretKey() { secretKeyPacket = new packet.SecretKey(); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - return secretKeyPacket.generate(options.numBits, options.curve); + var material; + if (typeof(options.material) !== "undefined") { + material = options.material.key; + } + return secretKeyPacket.generate(options.numBits, options.curve, material); } function generateSecretSubkey() { @@ -1148,8 +1152,12 @@ export function generate(options) { if (subkeyType === enums.publicKey.ecdsa) { subkeyType = enums.publicKey.ecdh; } + var material; + if (typeof(options.material) !== "undefined") { + material = options.material.subkey; + } secretSubkeyPacket.algorithm = enums.read(enums.publicKey, subkeyType); - return secretSubkeyPacket.generate(options.numBits, options.curve); + return secretSubkeyPacket.generate(options.numBits, options.curve, material); } } diff --git a/src/openpgp.js b/src/openpgp.js index 88074dfd..acb8e737 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -98,8 +98,8 @@ export function destroyWorker() { * @static */ -export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, curve=null } = {}) { - const options = formatUserIds({ userIds, passphrase, numBits, unlocked, curve }); +export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, curve=null, material=null } = {}) { + const options = formatUserIds({ userIds, passphrase, numBits, unlocked, curve, material }); if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported return asyncProxy.delegate('generateKey', options); @@ -459,6 +459,9 @@ function formatUserIds(options) { if (!options.curve) { delete options.curve; } + if (!options.material) { + delete options.material; + } if (!options.userIds) { return options; } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 8e525e41..ea1bd246 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -273,10 +273,10 @@ SecretKey.prototype.decrypt = function (passphrase) { return true; }; -SecretKey.prototype.generate = function (bits, curve) { +SecretKey.prototype.generate = function (bits, curve, material) { var self = this; - return crypto.generateMpi(self.algorithm, bits, curve).then(function(mpi) { + return crypto.generateMpi(self.algorithm, bits, curve, material).then(function(mpi) { self.mpi = mpi; self.isDecrypted = true; }); From ea1b0a1088f9562be346043c32a45ecd36762658 Mon Sep 17 00:00:00 2001 From: Ismael Bejarano Date: Fri, 12 Aug 2016 00:26:08 -0300 Subject: [PATCH 16/75] Readme for modifications to support RFC 6637 - Elliptic Curve Cryptography (ECC) in OpenPGP --- README-ECC.md | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 README-ECC.md diff --git a/README-ECC.md b/README-ECC.md new file mode 100644 index 00000000..6454dc61 --- /dev/null +++ b/README-ECC.md @@ -0,0 +1,134 @@ +# Elliptic Curve Cryptography support for OpenPGPjs + +## Description + +This work is to provide an implementation of [RFC 6637](http://www.ietf.org/rfc/rfc6637.txt) +for OpenPGP.js. + +## Compatibility with GnuPG + +In order to assure compatibility of the provided implementation with RFC 6637, +the keys and messages were tested against GnuPG version v2.1.8, compiled +with a beta version of libgcrypt v1.7.0-beta262. + +It was tested that keys, messages, and signatures generated by GnuPG +were imported correctly. Also keys, messages and signatures generated by this +implementation are correctly imported by GnuPG. + +```txt +> gpg2 --homedir ../home --version +gpg (GnuPG) 2.1.8 +libgcrypt 1.7.0-beta262 +Copyright (C) 2015 Free Software Foundation, Inc. +License GPLv3+: GNU GPL version 3 or later +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +Home: ../home +Supported algorithms: +Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA +Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, + CAMELLIA128, CAMELLIA192, CAMELLIA256 +Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 +Compression: Uncompressed, ZIP, ZLIB +``` + +## Extra dependencies + +There are two new dependencies: + +* [Elliptic](https://github.com/indutny/elliptic/) for the elliptic curve + cryptography. MIT license. + +* [Aes](https://github.com/cryptocoinjs/aes) required to implement RFC 3394 + Key wrap and Key Unwrap functions. BSD License. + +## Examples + +### Generate new key + +```js +var openpgp = require('openpgp'); + +var options = { + curve: 'secp256k1', + userIds: {name: 'Hamlet', email: 'hamlet@example.net'}, + passphrase: 'To be, or not to be: that is the question' +}; + +openpgp.generateKey(options).then(function(key) { + // success + var privkey = key.privateKeyArmored; + var pubkey = key.publicKeyArmored; +}).catch(function(error) { + // failure +}); +``` + +### Generate keypair from bitcoin key + +```js +var openpgp = require('openpgp'); +var bs58check = require('bs58check'); + +var wif = 'KyiAchQgMKuXQu89j6k6UVZQj7brK6cM79JfmDvkNXPVW24L1thi'; +var buff = bs58check.decode(wif); +var privateKey = buff.slice(1, -1); +privateKey = openpgp.util.bin2str(privateKey); + +var options = { + curve: 'secp256k1', + userIds: {name: 'Hamlet', email: 'hamlet@example.net'}, + passphrase: 'To be, or not to be: that is the question', + material: { + key: privateKey, + subkey: privateKey + } +}; + +openpgp.generateKey(options).then(function(key) { + // success + var privkey = key.privateKeyArmored; + var pubkey = key.publicKeyArmored; +}).catch(function(error) { + // failure +}); +``` + +### Signature, encryption and decryption + +The normal operations: signature, encryption and decryption require +no modifications. + +```js +var openpgp = require('openpgp'); + +var keyData = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; +var key = openpgp.key.readArmored(keyData); + +openpgp.encrypt({publicKeys: key.keys, data: 'Hello, World!'}).then(function(msg) { + // success +}).catch(function(error) { + // failure +}); +``` + +## Possible improvements + +* The dependency with AES library can be eliminated, a suitable AES decrypt + function is provided. It is only used by the wrap and unwrap functions + in the crypto/rfc3394.js file. + +## Note + +Although the example uses the same value to generate the main key, and +the subkey, it is a recommended practice to use different keys. +The main key is used for signature and the subkeys are used for encryption. + +## Resources + +* Elliptic Curve Cryptography (ECC) in OpenPGP [RFC 6637](http://www.ietf.org/rfc/rfc6637.txt) +* OpenPGP Message Format [RFC 4880](http://www.ietf.org/rfc/rfc4880.txt) +* Advanced Encryption Standard (AES) Key Wrap Algorithm [RFC 3394](http://www.ietf.org/rfc/rfc3394.txt) +* A JavaScript component for the Advanced Encryption Standard (AES) [AES](https://github.com/cryptocoinjs/aes) +* Fast elliptic-curve cryptography in a plain javascript implementation [Elliptic](https://github.com/indutny/elliptic/) From fdcfcf3a540634ec2a50597301c9bf223d9ab883 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 24 Jul 2017 18:33:46 +0200 Subject: [PATCH 17/75] refactor formatUserIds --- src/openpgp.js | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/openpgp.js b/src/openpgp.js index acb8e737..3bad19b0 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -98,8 +98,9 @@ export function destroyWorker() { * @static */ -export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, curve=null, material=null } = {}) { - const options = formatUserIds({ userIds, passphrase, numBits, unlocked, curve, material }); +export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve=""} = {}) { + userIds = formatUserIds(userIds); + const options = {userIds, passphrase, numBits, unlocked, keyExpirationTime, curve}; if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported return asyncProxy.delegate('generateKey', options); @@ -125,7 +126,9 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal * @static */ export function reformatKey({ privateKey, userIds=[], passphrase="", unlocked=false, keyExpirationTime=0 } = {}) { - const options = formatUserIds({ privateKey, userIds, passphrase, unlocked, keyExpirationTime }); + userIds = formatUserIds(userIds); + + const options = {privateKey, userIds, passphrase, unlocked, keyExpirationTime}; if (asyncProxy) { return asyncProxy.delegate('reformatKey', options); @@ -455,18 +458,12 @@ function checkCleartextOrMessage(message) { /** * Format user ids for internal use. */ -function formatUserIds(options) { - if (!options.curve) { - delete options.curve; +function formatUserIds(userIds) { + if (!userIds) { + return userIds; } - if (!options.material) { - delete options.material; - } - if (!options.userIds) { - return options; - } - options.userIds = toArray(options.userIds); // normalize to array - options.userIds = options.userIds.map(id => { + userIds = toArray(userIds); // normalize to array + userIds = userIds.map(id => { if (util.isString(id) && !util.isUserId(id)) { throw new Error('Invalid user id format'); } @@ -485,7 +482,7 @@ function formatUserIds(options) { } return id.name + '<' + id.email + '>'; }); - return options; + return userIds; } /** From 3b8cea67a2d72aba0d29bdf1e138689c7feb6728 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 24 Jul 2017 18:36:18 +0200 Subject: [PATCH 18/75] some style fixes, update tests --- src/crypto/{rfc3394.js => aes_kw.js} | 0 src/crypto/crypto.js | 20 ++++++++++---------- src/crypto/index.js | 6 +++--- test/crypto/{rfc3394.js => aes_kw.js} | 4 ++-- test/crypto/index.js | 2 +- test/general/key.js | 12 ++---------- test/general/openpgp.js | 6 ++++-- 7 files changed, 22 insertions(+), 28 deletions(-) rename src/crypto/{rfc3394.js => aes_kw.js} (100%) rename test/crypto/{rfc3394.js => aes_kw.js} (90%) diff --git a/src/crypto/rfc3394.js b/src/crypto/aes_kw.js similarity index 100% rename from src/crypto/rfc3394.js rename to src/crypto/aes_kw.js diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 784ae0f6..7eaedcc1 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -228,7 +228,7 @@ export default { } }, - generateMpi: function(algo, bits, curve, material) { + generateMpi: function(algo, bits, curve) { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': @@ -247,21 +247,21 @@ export default { }); case 'ecdsa': - return publicKey.elliptic.generate(curve, material).then(function (key) { + return publicKey.elliptic.generate(curve).then(function (keyObject) { return [ - new type_oid(key.oid), - BigInteger2mpi(key.R), - BigInteger2mpi(key.r) + new type_oid(keyObject.oid), + BigInteger2mpi(keyObject.R), + BigInteger2mpi(keyObject.r) ]; }); case 'ecdh': - return publicKey.elliptic.generate(curve, material).then(function (key) { + return publicKey.elliptic.generate(curve).then(function (keyObject) { return [ - new type_oid(key.oid), - BigInteger2mpi(key.R), - new type_kdf_params(key.hash, key.cipher), - BigInteger2mpi(key.r) + new type_oid(keyObject.oid), + BigInteger2mpi(keyObject.R), + new type_kdf_params(keyObject.hash, keyObject.cipher), + BigInteger2mpi(keyObject.r) ]; }); diff --git a/src/crypto/index.js b/src/crypto/index.js index b82d8b7b..036cee76 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -15,7 +15,7 @@ import random from './random'; import pkcs1 from './pkcs1'; import pkcs5 from './pkcs5.js'; import crypto from './crypto.js'; -import rfc3394 from './rfc3394.js'; +import aes_kw from './aes_kw.js'; const mod = { /** @see module:crypto/cipher */ @@ -36,8 +36,8 @@ const mod = { pkcs1: pkcs1, /** @see module:crypto/pkcs5 */ pkcs5: pkcs5, - /** @see module:crypto/rfc3394 */ - rfc3394: rfc3394, + /** @see module:crypto/aes_kw */ + aes_kw: aes_kw, }; for (var i in crypto) { diff --git a/test/crypto/rfc3394.js b/test/crypto/aes_kw.js similarity index 90% rename from test/crypto/rfc3394.js rename to test/crypto/aes_kw.js index 6887db79..f7e602ba 100644 --- a/test/crypto/rfc3394.js +++ b/test/crypto/aes_kw.js @@ -51,8 +51,8 @@ describe('AES Key Wrap and Unwrap', function () { var input_bin = openpgp.util.hex2bin(input); var output = test[3].replace(/\s/g, ""); var output_bin = openpgp.util.hex2bin(output); - expect(openpgp.util.hexidump(openpgp.crypto.rfc3394.wrap(kek, input_bin)).toUpperCase()).to.equal(output); - expect(openpgp.util.hexidump(openpgp.crypto.rfc3394.unwrap(kek, output_bin)).toUpperCase()).to.equal(input); + expect(openpgp.util.hexidump(openpgp.crypto.aes_kw.wrap(kek, input_bin)).toUpperCase()).to.equal(output); + expect(openpgp.util.hexidump(openpgp.crypto.aes_kw.unwrap(kek, output_bin)).toUpperCase()).to.equal(input); done(); }); }); diff --git a/test/crypto/index.js b/test/crypto/index.js index a61e6f6f..7ab5c758 100644 --- a/test/crypto/index.js +++ b/test/crypto/index.js @@ -5,5 +5,5 @@ describe('Crypto', function () { require('./crypto.js'); require('./elliptic.js'); require('./pkcs5.js'); - require('./rfc3394.js'); + require('./aes_kw.js'); }); diff --git a/test/general/key.js b/test/general/key.js index 10a8dde3..8a790a5f 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -527,6 +527,7 @@ describe('Key', function() { '=Q/kB', '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); +<<<<<<< HEAD var valid_binding_sig_among_many_expired_sigs_pub = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', '', @@ -582,7 +583,7 @@ describe('Key', function() { '=yzZh', '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); - it('Parsing armored text with RSA key and ECC subkey in tolerant mode', function(done) { + it('Parsing armored text with RSA key and ECC subkey', function(done) { openpgp.config.tolerant = true; var pubKeys = openpgp.key.readArmored(rsa_ecc_pub); expect(pubKeys).to.exist; @@ -592,15 +593,6 @@ describe('Key', function() { done(); }); - it('Parsing armored text with RSA key and ECC subkey in non-tolerant mode', function(done) { - openpgp.config.tolerant = false; - var pubKeys = openpgp.key.readArmored(rsa_ecc_pub); - expect(pubKeys).to.exist; - expect(pubKeys.err).to.exist; - done(); - }); - - var multi_uid_key = ['-----BEGIN PGP PUBLIC KEY BLOCK-----', 'Version: GnuPG v1', diff --git a/test/general/openpgp.js b/test/general/openpgp.js index c7d0b904..b3e4e92e 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -304,7 +304,8 @@ describe('OpenPGP.js public api tests', function() { passphrase: 'secret', numBits: 2048, unlocked: true, - keyExpirationTime: 0 + keyExpirationTime: 0, + curve: "" }).calledOnce).to.be.true; expect(newKey.key).to.exist; expect(newKey.privateKeyArmored).to.exist; @@ -319,7 +320,8 @@ describe('OpenPGP.js public api tests', function() { passphrase: undefined, numBits: 2048, unlocked: false, - keyExpirationTime: 0 + keyExpirationTime: 0, + curve: "" }).calledOnce).to.be.true; expect(newKey.key).to.exist; }); From 0de9eecdc8961316c2712b0bba4ec9497eaff0c9 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 24 Jul 2017 18:37:17 +0200 Subject: [PATCH 19/75] distinguish between key and subkey type, when refactoring key just grab type from packets --- src/key.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/key.js b/src/key.js index 241326de..36506c6c 100644 --- a/src/key.js +++ b/src/key.js @@ -1115,13 +1115,23 @@ export function readArmored(armoredText) { export function generate(options) { var secretKeyPacket, secretSubkeyPacket; return Promise.resolve().then(() => { + if (options.curve) { - options.keyType = enums.publicKey.ecdsa; + options.keyType = options.keyType || enums.publicKey.ecdsa; + options.subkeyType = options.subkeyType || enums.publicKey.ecdh; + } else { + options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; + options.subkeyType = options.subkeyType || enums.publicKey.rsa_encrypt_sign; } - options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; + if (options.keyType !== enums.publicKey.rsa_encrypt_sign && options.keyType !== enums.publicKey.ecdsa) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated - throw new Error('Only RSA Encrypt or Sign supported'); + throw new Error('Unsupported key type'); + } + + if (options.subkeyType !== enums.publicKey.rsa_encrypt_sign && + options.subkeyType !== enums.publicKey.ecdh) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated + throw new Error('Unsupported subkey type'); } if (!options.passphrase) { // Key without passphrase is unlocked by definition @@ -1139,25 +1149,13 @@ export function generate(options) { function generateSecretKey() { secretKeyPacket = new packet.SecretKey(); secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); - var material; - if (typeof(options.material) !== "undefined") { - material = options.material.key; - } - return secretKeyPacket.generate(options.numBits, options.curve, material); + return secretKeyPacket.generate(options.numBits, options.curve); } function generateSecretSubkey() { secretSubkeyPacket = new packet.SecretSubkey(); - var subkeyType = options.keyType; - if (subkeyType === enums.publicKey.ecdsa) { - subkeyType = enums.publicKey.ecdh; - } - var material; - if (typeof(options.material) !== "undefined") { - material = options.material.subkey; - } - secretSubkeyPacket.algorithm = enums.read(enums.publicKey, subkeyType); - return secretSubkeyPacket.generate(options.numBits, options.curve, material); + secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.subkeyType); + return secretSubkeyPacket.generate(options.numBits, options.curve); } } @@ -1196,8 +1194,10 @@ export function reformat(options) { for (var i = 0; i < packetlist.length; i++) { if (packetlist[i].tag === enums.packet.secretKey) { secretKeyPacket = packetlist[i]; + options.keyType = secretKeyPacket.algorithm; } else if (packetlist[i].tag === enums.packet.secretSubkey) { secretSubkeyPacket = packetlist[i]; + options.subkeyType = secretSubkeyPacket.algorithm; } } return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options); From fade832619c988aea1c74614237930a912730969 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Mon, 24 Jul 2017 18:38:26 +0200 Subject: [PATCH 20/75] remove material parameter for now --- src/crypto/public_key/elliptic/curves.js | 58 +++++++++++------------- src/crypto/public_key/elliptic/ecdh.js | 8 ++-- src/packet/secret_key.js | 4 +- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index faabd656..7437ee11 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -33,30 +33,6 @@ import BigInteger from '../jsbn.js'; import enums from '../../../enums.js'; import util from '../../../util.js'; -function Curve(name, {oid, hash, cipher}) { - this.curve = new EC(name); - this.name = name; - this.oid = oid; - this.hash = hash; - this.cipher = cipher; -} - -Curve.prototype.keyFromPrivate = function (priv) { - return new KeyPair(this.curve, {priv: priv}); -}; - -Curve.prototype.keyFromPublic = function (pub) { - return new KeyPair(this.curve, {pub: pub}); -}; - -Curve.prototype.genKeyPair = function () { - var r = this.curve.genKeyPair(); - return new KeyPair(this.curve, { - pub: r.getPublic().encode(), - priv: r.getPrivate().toArray() - }); -}; - const curves = { p256: { oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), @@ -84,6 +60,31 @@ const curves = { } }; +function Curve(name, {oid, hash, cipher}) { + this.curve = new EC(name); + this.name = name; + this.oid = oid; + this.hash = hash; + this.cipher = cipher; +} + +Curve.prototype.keyFromPrivate = function (priv) { + return new KeyPair(this.curve, {priv: priv}); +}; + +Curve.prototype.keyFromPublic = function (pub) { + return new KeyPair(this.curve, {pub: pub}); +}; + +Curve.prototype.genKeyPair = function () { + var r = this.curve.genKeyPair(); + return new KeyPair(this.curve, { + pub: r.getPublic().encode(), + priv: r.getPrivate().toArray() + }); +}; + + function get(oid_or_name) { for (var name in curves) { if (curves[name].oid === oid_or_name || name === oid_or_name) { @@ -97,15 +98,10 @@ function get(oid_or_name) { throw new Error('Not valid curve'); } -function generate(curve, material) { +function generate(curve) { return new Promise(function (resolve) { curve = get(curve); - var keyPair; - if (typeof(material) !== "undefined") { - keyPair = curve.keyFromPrivate(material); - } else { - keyPair = curve.genKeyPair(); - } + var keyPair = curve.genKeyPair(); resolve({ oid: curve.oid, R: new BigInteger(keyPair.getPublic()), diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index fc28064d..76df6b79 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -20,7 +20,7 @@ /** * @requires crypto/hash * @requires crypto/cipher - * @requires crypto/rfc3394 + * @requires crypto/aes_kw * @requires crypto/public_key/elliptic/curves * @requires crypto/public_key/jsbn * @requires type/oid @@ -36,7 +36,7 @@ import BigInteger from '../jsbn.js'; import curves from './curves.js'; import cipher from '../../cipher'; import hash from '../../hash'; -import rfc3394 from '../../rfc3394.js'; +import aes_kw from '../../aes_kw.js'; import enums from '../../../enums.js'; import util from '../../../util.js'; import type_kdf_params from '../../../type/kdf_params.js'; @@ -92,7 +92,7 @@ function encrypt(oid, cipher_algo, hash_algo, m, R, fingerprint) { R = curve.keyFromPublic(R.toByteArray()); const S = v.derive(R); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - const C = rfc3394.wrap(Z, m); + const C = aes_kw.wrap(Z, m); return { V: new BigInteger(v.getPublic()), C: C @@ -120,7 +120,7 @@ function decrypt(oid, cipher_algo, hash_algo, V, C, r, fingerprint) { r = curve.keyFromPrivate(r.toByteArray()); const S = r.derive(V); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - return new BigInteger(rfc3394.unwrap(Z, C)); + return new BigInteger(aes_kw.unwrap(Z, C)); } module.exports = { diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index ea1bd246..8e525e41 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -273,10 +273,10 @@ SecretKey.prototype.decrypt = function (passphrase) { return true; }; -SecretKey.prototype.generate = function (bits, curve, material) { +SecretKey.prototype.generate = function (bits, curve) { var self = this; - return crypto.generateMpi(self.algorithm, bits, curve, material).then(function(mpi) { + return crypto.generateMpi(self.algorithm, bits, curve).then(function(mpi) { self.mpi = mpi; self.isDecrypted = true; }); From 840c0229f89b43ea388549b0f8b054f6a531529e Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Wed, 26 Jul 2017 15:22:49 +0200 Subject: [PATCH 21/75] move hex2Uint8array to utils --- src/crypto/public_key/elliptic/ecdh.js | 12 +++--------- src/util.js | 9 +++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 76df6b79..af8ab3ff 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -42,13 +42,6 @@ import util from '../../../util.js'; import type_kdf_params from '../../../type/kdf_params.js'; import type_oid from '../../../type/oid.js'; -function hex2Uint8Array(hex) { - var result = new Uint8Array(hex.length/2); - for (var k=0; k Date: Wed, 26 Jul 2017 23:11:50 +0200 Subject: [PATCH 22/75] refactor mpi, generalize mpi to params to account for non-mpi algorithm-specific data --- src/crypto/crypto.js | 144 +++++++++++------- src/crypto/random.js | 3 +- src/key.js | 6 +- src/packet/public_key.js | 42 +++-- .../public_key_encrypted_session_key.js | 45 ++---- src/packet/secret_key.js | 20 +-- src/packet/signature.js | 4 +- src/type/mpi.js | 10 +- test/general/packet.js | 13 +- 9 files changed, 150 insertions(+), 137 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 7eaedcc1..03deaac3 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -38,14 +38,26 @@ import type_kdf_params from '../type/kdf_params.js'; import type_mpi from '../type/mpi.js'; import type_oid from '../type/oid.js'; -function BigInteger2mpi(bn) { - var mpi = new type_mpi(); - mpi.fromBigInteger(bn); - return mpi; +function createType(data, type) { + switch(type) { + case 'mpi': + return new type_mpi(data); + case 'oid': + return new type_oid(data); + case 'kdf': + return new type_kdf_params(data); + case 'ecdh_symkey': + return new type_ecdh_symkey(data); + default: + return null; + } } -function mapResult(result) { - return result.map(BigInteger2mpi); +function mapResult(result, types) { + for (var i=0; i < result.length; i++) { + result[i] = createType(result[i], types[i]); + } + return result; } export default { @@ -53,38 +65,39 @@ export default { * 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 {Array} publicParams Algorithm dependent multiprecision integers * @param {module:type/mpi} data Data to be encrypted as 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, fingerprint) { + publicKeyEncrypt: function(algo, publicParams, data, fingerprint) { + var types = this.getEncSessionKeyParamTypes(algo); var result = (function() { var m; switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': var rsa = new publicKey.rsa(); - var n = publicMPIs[0].toBigInteger(); - var e = publicMPIs[1].toBigInteger(); + var n = publicParams[0].toBigInteger(); + var e = publicParams[1].toBigInteger(); m = data.toBigInteger(); - return mapResult([rsa.encrypt(m, e, n)]); + return mapResult([rsa.encrypt(m, e, n)], types); case 'elgamal': var elgamal = new publicKey.elgamal(); - var p = publicMPIs[0].toBigInteger(); - var g = publicMPIs[1].toBigInteger(); - var y = publicMPIs[2].toBigInteger(); + var p = publicParams[0].toBigInteger(); + var g = publicParams[1].toBigInteger(); + var y = publicParams[2].toBigInteger(); m = data.toBigInteger(); - return mapResult(elgamal.encrypt(m, g, p, y)); + return mapResult(elgamal.encrypt(m, g, p, y), types); case 'ecdh': var ecdh = publicKey.elliptic.ecdh; - var curve = publicMPIs[0]; - var kdf_params = publicMPIs[2]; - var R = publicMPIs[1].toBigInteger(); + var curve = publicParams[0]; + var kdf_params = publicParams[2]; + var R = publicParams[1].toBigInteger(); var res = ecdh.encrypt(curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint); - return [BigInteger2mpi(res.V), new type_ecdh_symkey(res.C)]; + return mapResult([res.V, res.C], types); default: return []; @@ -98,7 +111,7 @@ export default { * 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} publicParams Algorithm dependent multiprecision integers * of the public key part of the private key * @param {Array} secretMPIs Algorithm dependent multiprecision integers * of the private key used @@ -146,8 +159,7 @@ export default { } })(); - var result = new type_mpi(); - result.fromBigInteger(bn); + var result = new type_mpi(bn); return result; }, @@ -155,7 +167,7 @@ export default { * @param {String} algo The public key algorithm * @return {Integer} The number of integers. */ - getPrivateMpiCount: function(algo) { + getPrivKeyParamTypes: function(algo) { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': @@ -165,27 +177,30 @@ export default { // - MPI of RSA secret prime value p. // - MPI of RSA secret prime value q (p < q). // - MPI of u, the multiplicative inverse of p, mod q. - return 4; + return ['mpi', 'mpi', 'mpi', 'mpi']; case 'elgamal': // Algorithm-Specific Fields for Elgamal secret keys: // - MPI of Elgamal secret exponent x. - return 1; + return ['mpi']; case 'dsa': // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. - return 1; + return ['mpi']; case 'ecdh': case 'ecdsa': // Algorithm-Specific Fields for ECDSA or ECDH secret keys: // - MPI of an integer representing the secret key. - return 1; + return ['mpi']; default: throw new Error('Unknown algorithm'); } }, - getPublicMpiCount: function(algo) { - // - A series of multiprecision integers comprising the key material: + getPrivKeyParamCount: function(algo) { + return this.getPrivKeyParamTypes(algo).length; + }, + + getPubKeyParamTypes: function(algo) { // Algorithm-Specific Fields for RSA public keys: // - a multiprecision integer (MPI) of RSA public modulus n; // - an MPI of RSA public encryption exponent e. @@ -193,42 +208,71 @@ export default { case 'rsa_encrypt': case 'rsa_encrypt_sign': case 'rsa_sign': - return 2; - + return ['mpi', 'mpi']; // Algorithm-Specific Fields for Elgamal public keys: // - MPI of Elgamal prime p; // - MPI of Elgamal group generator g; // - MPI of Elgamal public key value y (= g**x mod p where x is secret). case 'elgamal': - return 3; - + return ['mpi', 'mpi', 'mpi']; // Algorithm-Specific Fields for DSA public keys: // - MPI of DSA prime p; // - MPI of DSA group order q (q is a prime divisor of p-1); // - MPI of DSA group generator g; // - MPI of DSA public-key value y (= g**x mod p where x is secret). case 'dsa': - return 4; - + return ['mpi', 'mpi', 'mpi', 'mpi']; // Algorithm-Specific Fields for ECDSA public keys: // - OID of curve; // - MPI of EC point representing public key. case 'ecdsa': - return 2; - + return ['oid', 'mpi']; // Algorithm-Specific Fields for ECDH public keys: // - OID of curve; // - MPI of EC point representing public key. // - variable-length field containing KDF parameters. case 'ecdh': - return 3; + return ['oid', 'mpi', 'kdf']; + default: + throw new Error('Unknown algorithm.'); + } + }, + + getPubKeyParamCount: function(algo) { + return this.getPubKeyParamTypes(algo).length; + }, + + getEncSessionKeyParamTypes: function(algo) { + switch (algo) { + // Algorithm-Specific Fields for RSA encrypted session keys: + // - MPI of RSA encrypted value m**e mod n. + case 'rsa_encrypt': + case 'rsa_encrypt_sign': + return ['mpi']; + + // Algorithm-Specific Fields for Elgamal encrypted session keys: + // - MPI of Elgamal value g**k mod p + // - MPI of Elgamal value m * y**k mod p + case 'elgamal': + return ['mpi', 'mpi']; + + // Algorithm-Specific Fields for ECDH encrypted session keys: + // - MPI containing the ephemeral key used to establish the shared secret + // - EcdhSymmetricKey + case 'ecdh': + return ['mpi', 'ecdh_symkey']; default: throw new Error('Unknown algorithm.'); } }, - generateMpi: function(algo, bits, curve) { + getEncSessionKeyParamCount: function(algo) { + return this.getEncSessionKeyParamTypes(algo).length; + }, + + generateParams: function(algo, bits, curve) { + var types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': @@ -236,33 +280,17 @@ export default { //remember "publicKey" refers to the crypto/public_key dir var rsa = new publicKey.rsa(); return rsa.generate(bits, "10001").then(function(keyObject) { - var output = []; - output.push(keyObject.n); - output.push(keyObject.ee); - output.push(keyObject.d); - output.push(keyObject.p); - output.push(keyObject.q); - output.push(keyObject.u); - return mapResult(output); + return mapResult([keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u], types); }); case 'ecdsa': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return [ - new type_oid(keyObject.oid), - BigInteger2mpi(keyObject.R), - BigInteger2mpi(keyObject.r) - ]; + return mapResult([keyObject.oid, keyObject.R, keyObject.r], types); }); case 'ecdh': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return [ - new type_oid(keyObject.oid), - BigInteger2mpi(keyObject.R), - new type_kdf_params(keyObject.hash, keyObject.cipher), - BigInteger2mpi(keyObject.r) - ]; + return mapResult([keyObject.oid, keyObject.R, [keyObject.hash, keyObject.cipher], keyObject.r], types); }); default: diff --git a/src/crypto/random.js b/src/crypto/random.js index 4e2331a4..54e656ea 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -113,8 +113,7 @@ export default { randomBits.charCodeAt(0)) + randomBits.substring(1); } - var mpi = new type_mpi(); - mpi.fromBytes(randomBits); + var mpi = new type_mpi(randomBits); return mpi.toBigInteger(); }, diff --git a/src/key.js b/src/key.js index 36506c6c..fce34b1a 100644 --- a/src/key.js +++ b/src/key.js @@ -380,7 +380,7 @@ Key.prototype.encrypt = function(passphrase) { var keys = this.getAllKeyPackets(); for (var i = 0; i < keys.length; i++) { keys[i].encrypt(passphrase); - keys[i].clearPrivateMPIs(); + keys[i].clearPrivateParams(); } }; @@ -1279,8 +1279,8 @@ function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { packetlist.push(subkeySignaturePacket); if (!options.unlocked) { - secretKeyPacket.clearPrivateMPIs(); - secretSubkeyPacket.clearPrivateMPIs(); + secretKeyPacket.clearPrivateParams(); + secretSubkeyPacket.clearPrivateParams(); } return new Key(packetlist); diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 8e322248..57b4ca98 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -51,12 +51,8 @@ export default function PublicKey() { /** Key creation date. * @type {Date} */ this.created = new Date(); - /** A list of multiprecision integers - * @type {module:type/mpi} */ - this.mpi = []; - /** Public key algorithm - * @type {module:enums.publicKey} */ - this.algorithm = 'rsa_sign'; + /* Algorithm specific params */ + this.params = []; // time in days (V3 only) this.expirationTimeV3 = 0; /** @@ -97,22 +93,22 @@ PublicKey.prototype.read = function (bytes) { // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = enums.read(enums.publicKey, bytes[pos++]); - var mpicount = crypto.getPublicMpiCount(this.algorithm); - this.mpi = []; + var param_count = crypto.getPubKeyParamCount(this.algorithm); + this.params = []; var bmpi = bytes.subarray(pos, bytes.length); var p = 0; - for (var i = 0; i < mpicount && p < bmpi.length; i++) { + for (var i = 0; i < param_count && p < bmpi.length; i++) { if ((this.algorithm === 'ecdsa' || this.algorithm === 'ecdh') && i === 0) { - this.mpi[i] = new type_oid(); + this.params[i] = new type_oid(); } else if (this.algorithm === 'ecdh' && i === 2) { - this.mpi[i] = new type_kdf_params(); + this.params[i] = new type_kdf_params(); } else { - this.mpi[i] = new type_mpi(); + this.params[i] = new type_mpi(); } - p += this.mpi[i].read(bmpi.subarray(p, bmpi.length)); + p += this.params[i].read(bmpi.subarray(p, bmpi.length)); if (p > bmpi.length) { throw new Error('Error reading MPI @:' + p); @@ -147,10 +143,10 @@ PublicKey.prototype.write = function () { } arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); - var mpicount = crypto.getPublicMpiCount(this.algorithm); + var param_count = crypto.getPubKeyParamCount(this.algorithm); - for (var i = 0; i < mpicount; i++) { - arr.push(this.mpi[i].write()); + for (var i = 0; i < param_count; i++) { + arr.push(this.params[i].write()); } return util.concatUint8Array(arr); @@ -183,7 +179,7 @@ PublicKey.prototype.getKeyId = function () { if (this.version === 4) { this.keyid.read(util.str2Uint8Array(util.hex2bin(this.getFingerprint()).substr(12, 8))); } else if (this.version === 3) { - var arr = this.mpi[0].write(); + var arr = this.params[0].write(); this.keyid.read(arr.subarray(arr.length - 8, arr.length)); } return this.keyid; @@ -202,9 +198,9 @@ PublicKey.prototype.getFingerprint = function () { toHash = this.writeOld(); this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); } else if (this.version === 3) { - var mpicount = crypto.getPublicMpiCount(this.algorithm); - for (var i = 0; i < mpicount; i++) { - toHash += this.mpi[i].toBytes(); + var param_count = crypto.getPubKeyParamCount(this.algorithm); + for (var i = 0; i < param_count; i++) { + toHash += this.params[i].toBytes(); } this.fingerprint = util.Uint8Array2str(crypto.hash.md5(util.str2Uint8Array(toHash))); } @@ -217,15 +213,15 @@ PublicKey.prototype.getFingerprint = function () { * @return {int} Number of bits */ PublicKey.prototype.getBitSize = function () { - return this.mpi[0].byteLength() * 8; + return this.params[0].byteLength() * 8; }; /** * Fix custom types after cloning */ PublicKey.prototype.postCloneTypeFix = function() { - for (var i = 0; i < this.mpi.length; i++) { - this.mpi[i] = type_mpi.fromClone(this.mpi[i]); + for (var i = 0; i < this.params.length; i++) { + this.params[i] = type_mpi.fromClone(this.params[i]); } if (this.keyid) { this.keyid = type_keyid.fromClone(this.keyid); diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 673ebc3d..9678f479 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -55,10 +55,7 @@ export default function PublicKeyEncryptedSessionKey() { this.version = 3; this.publicKeyId = new type_keyid(); - this.publicKeyAlgorithm = 'rsa_encrypt'; - this.sessionKey = null; - this.sessionKeyAlgorithm = 'aes256'; /** @type {Array} */ this.encrypted = []; @@ -81,34 +78,20 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { var i = 10; - var integerCount = (function(algo) { - switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - return 1; + var integerCount = crypto.getEncSessionKeyParamCount(this.publicKeyAlgorithm); - case 'elgamal': - return 2; - - case 'ecdh': - return 2; - - default: - throw new Error("Invalid algorithm."); - } - })(this.publicKeyAlgorithm); this.encrypted = []; for (var j = 0; j < integerCount; j++) { - var mpi; + var param; if (this.publicKeyAlgorithm === 'ecdh' && j === 1) { - mpi = new type_ecdh_symkey(); + param = new type_ecdh_symkey(); } else { - mpi = new type_mpi(); + param = new type_mpi(); } - i += mpi.read(bytes.subarray(i, bytes.length)); - this.encrypted.push(mpi); + i += param.read(bytes.subarray(i, bytes.length)); + this.encrypted.push(param); } }; @@ -136,20 +119,20 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { var checksum = util.calc_checksum(this.sessionKey); data += util.Uint8Array2str(util.writeNumber(checksum, 2)); - var mpi; + var param; if (this.publicKeyAlgorithm === 'ecdh') { - mpi = util.str2Uint8Array(crypto.pkcs5.addPadding(data)); + param = util.str2Uint8Array(crypto.pkcs5.addPadding(data)); } else { - mpi = new type_mpi(); - mpi.fromBytes(crypto.pkcs1.eme.encode( + param = new type_mpi(); + param.fromBytes(crypto.pkcs1.eme.encode( data, - key.mpi[0].byteLength())); + key.params[0].byteLength())); } this.encrypted = crypto.publicKeyEncrypt( this.publicKeyAlgorithm, - key.mpi, - mpi, + key.params, + param, key.fingerprint); }; @@ -164,7 +147,7 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { PublicKeyEncryptedSessionKey.prototype.decrypt = function (key) { var result = crypto.publicKeyDecrypt( this.publicKeyAlgorithm, - key.mpi, + key.params, this.encrypted, key.fingerprint).toBytes(); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 8e525e41..52bfce8f 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -89,7 +89,7 @@ function parse_cleartext_mpi(hash_algorithm, cleartext, algorithm) { return new Error("Hash mismatch."); } - var mpis = crypto.getPrivateMpiCount(algorithm); + var mpis = crypto.getPrivKeyParamCount(algorithm); var j = 0; var mpi = []; @@ -104,7 +104,7 @@ function parse_cleartext_mpi(hash_algorithm, cleartext, algorithm) { function write_cleartext_mpi(hash_algorithm, algorithm, mpi) { var arr = []; - var discard = crypto.getPublicMpiCount(algorithm); + var discard = crypto.getPubKeyParamCount(algorithm); for (var i = discard; i < mpi.length; i++) { arr.push(mpi[i].write()); @@ -147,7 +147,7 @@ SecretKey.prototype.read = function (bytes) { if (parsedMPI instanceof Error) { throw parsedMPI; } - this.mpi = this.mpi.concat(parsedMPI); + this.params = this.params.concat(parsedMPI); this.isDecrypted = true; } @@ -161,7 +161,7 @@ SecretKey.prototype.write = function () { if (!this.encrypted) { arr.push(new Uint8Array([0])); - arr.push(write_cleartext_mpi('mod', this.algorithm, this.mpi)); + arr.push(write_cleartext_mpi('mod', this.algorithm, this.params)); } else { arr.push(this.encrypted); } @@ -188,7 +188,7 @@ SecretKey.prototype.encrypt = function (passphrase) { var s2k = new type_s2k(), symmetric = 'aes256', - cleartext = write_cleartext_mpi('sha1', this.algorithm, this.mpi), + cleartext = write_cleartext_mpi('sha1', this.algorithm, this.params), key = produceEncryptionKey(s2k, passphrase, symmetric), blockLen = crypto.cipher[symmetric].blockSize, iv = crypto.random.getRandomBytes(blockLen); @@ -267,7 +267,7 @@ SecretKey.prototype.decrypt = function (passphrase) { if (parsedMPI instanceof Error) { return false; } - this.mpi = this.mpi.concat(parsedMPI); + this.params = this.params.concat(parsedMPI); this.isDecrypted = true; this.encrypted = null; return true; @@ -276,8 +276,8 @@ SecretKey.prototype.decrypt = function (passphrase) { SecretKey.prototype.generate = function (bits, curve) { var self = this; - return crypto.generateMpi(self.algorithm, bits, curve).then(function(mpi) { - self.mpi = mpi; + return crypto.generateParams(self.algorithm, bits, curve).then(function(params) { + self.params = params; self.isDecrypted = true; }); }; @@ -285,10 +285,10 @@ SecretKey.prototype.generate = function (bits, curve) { /** * Clear private MPIs, return to initial state */ -SecretKey.prototype.clearPrivateMPIs = function () { +SecretKey.prototype.clearPrivateParams = function () { if (!this.encrypted) { throw new Error('If secret key is not encrypted, clearing private MPIs is irreversible.'); } - this.mpi = this.mpi.slice(0, crypto.getPublicMpiCount(this.algorithm)); + this.params = this.params.slice(0, crypto.getPubKeyParamCount(this.algorithm)); this.isDecrypted = false; }; diff --git a/src/packet/signature.js b/src/packet/signature.js index b5f72d69..89d953c4 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -243,7 +243,7 @@ Signature.prototype.sign = function (key, data) { this.signedHashValue = hash.subarray(0, 2); this.signature = crypto.signature.sign(hashAlgorithm, - publicKeyAlgorithm, key.mpi, toHash); + publicKeyAlgorithm, key.params, toHash); }; /** @@ -644,7 +644,7 @@ Signature.prototype.verify = function (key, data) { } this.verified = crypto.signature.verify(publicKeyAlgorithm, - hashAlgorithm, mpi, key.mpi, + hashAlgorithm, mpi, key.params, util.concatUint8Array([bytes, this.signatureData, trailer])); return this.verified; diff --git a/src/type/mpi.js b/src/type/mpi.js index d787b668..11e50504 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -42,9 +42,15 @@ import util from '../util.js'; /** * @constructor */ -export default function MPI() { +export default function MPI(data) { /** An implementation dependent integer */ - this.data = null; + if (data instanceof BigInteger) { + this.fromBigInteger(data); + } else if (typeof data === 'string') { + this.fromBytes(data); + } else { + this.data = null; + } } /** diff --git a/test/general/packet.js b/test/general/packet.js index 431e1f3a..95c03d78 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -191,13 +191,13 @@ describe("Packet", function() { enc.publicKeyAlgorithm = 'rsa_encrypt'; enc.sessionKeyAlgorithm = 'aes256'; enc.publicKeyId.bytes = '12345678'; - enc.encrypt({ mpi: mpi }); + enc.encrypt({ params: mpi }); msg.push(enc); msg2.read(msg.write()); - msg2[0].decrypt({ mpi: mpi }); + msg2[0].decrypt({ params: mpi }); expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey)); expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); @@ -445,8 +445,8 @@ describe("Packet", function() { return mpi; }); - key[0].mpi = mpi; - + key[0].params = mpi; + key[0].algorithm = "rsa_sign"; key[0].encrypt('hello'); var raw = key.write(); @@ -455,7 +455,7 @@ describe("Packet", function() { key2.read(raw); key2[0].decrypt('hello'); - expect(key[0].mpi.toString()).to.equal(key2[0].mpi.toString()); + expect(key[0].params.toString()).to.equal(key2[0].params.toString()); }); }); @@ -473,7 +473,8 @@ describe("Packet", function() { return mpi; }); - key.mpi = mpi; + key.params = mpi; + key.algorithm = "rsa_sign"; var signed = new openpgp.packet.List(), literal = new openpgp.packet.Literal(), From b718cf359d5c2551dd5e9678d4adb194936f2a2d Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Thu, 27 Jul 2017 00:59:21 +0200 Subject: [PATCH 23/75] simplify packet parsing using type maps --- src/crypto/crypto.js | 22 ++++++++++--------- src/crypto/public_key/elliptic/ecdh.js | 4 ++-- src/index.js | 12 +++++----- src/packet/public_key.js | 21 +++++------------- .../public_key_encrypted_session_key.js | 17 ++++---------- src/type/ecdh_symkey.js | 12 +++++----- src/type/kdf_params.js | 18 +++++++-------- src/type/oid.js | 18 +++++++-------- test/general/oid.js | 8 +++---- 9 files changed, 58 insertions(+), 74 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 03deaac3..912633ab 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -53,8 +53,8 @@ function createType(data, type) { } } -function mapResult(result, types) { - for (var i=0; i < result.length; i++) { +function constructParams(result, types) { + for (var i=0; i < types.length; i++) { result[i] = createType(result[i], types[i]); } return result; @@ -81,7 +81,7 @@ export default { var n = publicParams[0].toBigInteger(); var e = publicParams[1].toBigInteger(); m = data.toBigInteger(); - return mapResult([rsa.encrypt(m, e, n)], types); + return constructParams([rsa.encrypt(m, e, n)], types); case 'elgamal': var elgamal = new publicKey.elgamal(); @@ -89,7 +89,7 @@ export default { var g = publicParams[1].toBigInteger(); var y = publicParams[2].toBigInteger(); m = data.toBigInteger(); - return mapResult(elgamal.encrypt(m, g, p, y), types); + return constructParams(elgamal.encrypt(m, g, p, y), types); case 'ecdh': var ecdh = publicKey.elliptic.ecdh; @@ -97,7 +97,7 @@ export default { var kdf_params = publicParams[2]; var R = publicParams[1].toBigInteger(); var res = ecdh.encrypt(curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint); - return mapResult([res.V, res.C], types); + return constructParams([res.V, res.C], types); default: return []; @@ -258,7 +258,7 @@ export default { // Algorithm-Specific Fields for ECDH encrypted session keys: // - MPI containing the ephemeral key used to establish the shared secret - // - EcdhSymmetricKey + // - ECDHSymmetricKey case 'ecdh': return ['mpi', 'ecdh_symkey']; @@ -280,17 +280,17 @@ export default { //remember "publicKey" refers to the crypto/public_key dir var rsa = new publicKey.rsa(); return rsa.generate(bits, "10001").then(function(keyObject) { - return mapResult([keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u], types); + return constructParams([keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u], types); }); case 'ecdsa': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return mapResult([keyObject.oid, keyObject.R, keyObject.r], types); + return constructParams([keyObject.oid, keyObject.R, keyObject.r], types); }); case 'ecdh': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return mapResult([keyObject.oid, keyObject.R, [keyObject.hash, keyObject.cipher], keyObject.r], types); + return constructParams([keyObject.oid, keyObject.R, [keyObject.hash, keyObject.cipher], keyObject.r], types); }); default: @@ -316,5 +316,7 @@ export default { */ generateSessionKey: function(algo) { return random.getRandomBytes(cipher[algo].keySize); - } + }, + + constructParams: constructParams }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index af8ab3ff..8a19a378 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -69,7 +69,7 @@ function kdf(hash_algo, X, length, param) { /** * Encrypt and wrap a session key * - * @param {String} oid Oid of the curve to use + * @param {String} oid OID of the curve to use * @param {Enums} cipher_algo Symmetric cipher to use * @param {Enums} hash_algo Hash to use * @param {Uint8Array} m Value derived from session key (RFC 6637) @@ -96,7 +96,7 @@ function encrypt(oid, cipher_algo, hash_algo, m, R, fingerprint) { /** * Decrypt and unwrap the value derived from session key * - * @param {String} oid Curve oid + * @param {String} oid Curve OID * @param {Enums} cipher_algo Symmetric cipher to use * @param {Enums} hash_algo Hash algorithm to use * @param {BigInteger} V Public part of ephemeral key diff --git a/src/index.js b/src/index.js index fd0e9de5..46bec340 100644 --- a/src/index.js +++ b/src/index.js @@ -79,21 +79,21 @@ export { default as Keyid } from './type/keyid'; /** * @see module:type/ecdh_symkey - * @name module:openpgp.EcdhSymmetricKey + * @name module:openpgp.ECDHSymmetricKey */ -export { default as EcdhSymmetricKey } from './type/ecdh_symkey'; +export { default as ECDHSymmetricKey } from './type/ecdh_symkey'; /** * @see module:type/kdf_params - * @name module:openpgp.KdfParams + * @name module:openpgp.KDFParams */ -export { default as KdfParams } from './type/kdf_params'; +export { default as KDFParams } from './type/kdf_params'; /** * @see module:type/oid - * @name module:openpgp.Oid + * @name module:openpgp.OID */ -export { default as Oid } from './type/oid'; +export { default as OID } from './type/oid'; /** * @see module:encoding/armor diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 57b4ca98..c9f6af1e 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -93,24 +93,15 @@ PublicKey.prototype.read = function (bytes) { // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = enums.read(enums.publicKey, bytes[pos++]); - var param_count = crypto.getPubKeyParamCount(this.algorithm); - this.params = []; + var types = crypto.getPubKeyParamTypes(this.algorithm); + this.params = crypto.constructParams(new Array(types.length), types); - var bmpi = bytes.subarray(pos, bytes.length); + var b = bytes.subarray(pos, bytes.length); var p = 0; - for (var i = 0; i < param_count && p < bmpi.length; i++) { - if ((this.algorithm === 'ecdsa' || this.algorithm === 'ecdh') && i === 0) { - this.params[i] = new type_oid(); - } else if (this.algorithm === 'ecdh' && i === 2) { - this.params[i] = new type_kdf_params(); - } else { - this.params[i] = new type_mpi(); - } - - p += this.params[i].read(bmpi.subarray(p, bmpi.length)); - - if (p > bmpi.length) { + for (var i = 0; i < types.length && p < b.length; i++) { + p += this.params[i].read(b.subarray(p, b.length)); + if (p > b.length) { throw new Error('Error reading MPI @:' + p); } } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 9678f479..a05ff206 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -78,20 +78,11 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { var i = 10; - var integerCount = crypto.getEncSessionKeyParamCount(this.publicKeyAlgorithm); + var types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); + this.encrypted = crypto.constructParams(new Array(types.length), types); - - this.encrypted = []; - - for (var j = 0; j < integerCount; j++) { - var param; - if (this.publicKeyAlgorithm === 'ecdh' && j === 1) { - param = new type_ecdh_symkey(); - } else { - param = new type_mpi(); - } - i += param.read(bytes.subarray(i, bytes.length)); - this.encrypted.push(param); + for (var j = 0; j < types.length; j++) { + i += this.encrypted[j].read(bytes.subarray(i, bytes.length)); } }; diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index ef32b68a..84b3be2d 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -26,12 +26,12 @@ import util from '../util.js'; -module.exports = EcdhSymmetricKey; +module.exports = ECDHSymmetricKey; /** * @constructor */ -function EcdhSymmetricKey(data) { +function ECDHSymmetricKey(data) { if (typeof data === 'undefined') { data = new Uint8Array([]); } else if (typeof data === 'string') { @@ -43,11 +43,11 @@ function EcdhSymmetricKey(data) { } /** - * Read an EcdhSymmetricKey from an Uint8Array + * Read an ECDHSymmetricKey from an Uint8Array * @param {Uint8Array} input Where to read the encoded symmetric key from * @return {Number} Number of read bytes */ -EcdhSymmetricKey.prototype.read = function (input) { +ECDHSymmetricKey.prototype.read = function (input) { if (input.length >= 1) { var length = input[0]; @@ -61,10 +61,10 @@ EcdhSymmetricKey.prototype.read = function (input) { }; /** - * Write an EcdhSymmetricKey as an Uint8Array + * Write an ECDHSymmetricKey as an Uint8Array * @return {Uint8Array} An array containing the value */ -EcdhSymmetricKey.prototype.write = function () { +ECDHSymmetricKey.prototype.write = function () { return util.concatUint8Array([ new Uint8Array([this.data.length]), this.data]); diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 1a6d1371..4720c280 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -26,26 +26,26 @@ import enums from '../enums.js'; -module.exports = KdfParams; +module.exports = KDFParams; /** * @constructor * @param {enums.hash} hash Hash algorithm * @param {enums.symmetric} cipher Symmetric algorithm */ -function KdfParams(hash, cipher) { +function KDFParams(hash, cipher) { this.hash = hash || enums.hash.sha1; this.cipher = cipher || enums.symmetric.aes128; } /** - * Read KdfParams from an Uint8Array - * @param {Uint8Array} input Where to read the KdfParams from + * Read KDFParams from an Uint8Array + * @param {Uint8Array} input Where to read the KDFParams from * @return {Number} Number of read bytes */ -KdfParams.prototype.read = function (input) { +KDFParams.prototype.read = function (input) { if (input.length < 4 || input[0] !== 3 || input[1] !== 1) { - throw new Error('Cannot read KdfParams'); + throw new Error('Cannot read KDFParams'); } this.hash = input[2]; this.cipher = input[3]; @@ -53,9 +53,9 @@ KdfParams.prototype.read = function (input) { }; /** - * Write KdfParams to an Uint8Array - * @return {Uint8Array} Array with the KdfParams value + * Write KDFParams to an Uint8Array + * @return {Uint8Array} Array with the KDFParams value */ -KdfParams.prototype.write = function () { +KDFParams.prototype.write = function () { return new Uint8Array([3, 1, this.hash, this.cipher]); }; diff --git a/src/type/oid.js b/src/type/oid.js index a8fd1899..c4253889 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -16,7 +16,7 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Wrapper to an Oid value
+ * Wrapper to an OID value
*
* An object identifier type from {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. * @requires util @@ -27,12 +27,12 @@ import util from '../util.js'; -module.exports = Oid; +module.exports = OID; /** * @constructor */ -function Oid(oid) { +function OID(oid) { if (typeof oid === 'undefined') { oid = ''; } else if (util.isArray(oid)) { @@ -44,11 +44,11 @@ function Oid(oid) { } /** - * Method to read an Oid object - * @param {Uint8Array} input Where to read the Oid from + * Method to read an OID object + * @param {Uint8Array} input Where to read the OID from * @return {Number} Number of read bytes */ -Oid.prototype.read = function (input) { +OID.prototype.read = function (input) { if (input.length >= 1) { var length = input[0]; if (input.length >= 1+length) { @@ -60,10 +60,10 @@ Oid.prototype.read = function (input) { }; /** - * Serialize an Oid object - * @return {Uint8Array} Array with the serialized value the Oid + * Serialize an OID object + * @return {Uint8Array} Array with the serialized value the OID */ -Oid.prototype.write = function () { +OID.prototype.write = function () { return util.str2Uint8Array( String.fromCharCode(this.oid.length)+this.oid); }; diff --git a/test/general/oid.js b/test/general/oid.js index e093f407..b4f4e68a 100644 --- a/test/general/oid.js +++ b/test/general/oid.js @@ -5,14 +5,14 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : var expect = require('chai').expect; describe('Oid tests', function() { - var Oid = openpgp.Oid; + var OID = openpgp.OID; var p256_oid = new Uint8Array([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]); var p384_oid = new Uint8Array([0x2B, 0x81, 0x04, 0x00, 0x22]); var p521_oid = new Uint8Array([0x2B, 0x81, 0x04, 0x00, 0x23]); it('Constructing', function() { var oids = [p256_oid, p384_oid, p521_oid]; oids.forEach(function (data) { - var oid = new Oid(data); + var oid = new OID(data); expect(oid).to.exist; expect(oid.oid).to.exist; expect(oid.oid).to.have.length(data.length); @@ -22,8 +22,8 @@ describe('Oid tests', function() { it('Reading and writing', function() { var oids = [p256_oid, p384_oid, p521_oid]; oids.forEach(function (data) { - data = openpgp.util.concatUint8Array([new Uint8Array([data.length]), data]); - var oid = new Oid(); + data = openpgp.util.concatUint8Array([new Uint8Array([data.length]), data]); + var oid = new OID(); expect(oid.read(data)).to.equal(data.length); expect(oid.oid).to.exist; expect(oid.oid).to.have.length(data.length-1); From b40408d42fb5f766afe4dcefbd36cf2dcc29fba8 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Thu, 27 Jul 2017 14:38:45 +0200 Subject: [PATCH 24/75] standardize packet reading code, make pkcs5 function names same as pkcs1 --- src/crypto/pkcs5.js | 29 ++++-------- src/crypto/public_key/elliptic/ecdh.js | 2 +- src/packet/public_key.js | 8 ++-- .../public_key_encrypted_session_key.js | 13 ++--- src/packet/secret_key.js | 47 +++++++++---------- test/crypto/pkcs5.js | 8 ++-- 6 files changed, 47 insertions(+), 60 deletions(-) diff --git a/src/crypto/pkcs5.js b/src/crypto/pkcs5.js index fc1e694a..a9e364ff 100644 --- a/src/crypto/pkcs5.js +++ b/src/crypto/pkcs5.js @@ -17,27 +17,18 @@ // Functions to add and remove PKCS5 padding -/** - * PKCS5 padding - * @module crypto/pkcs5 - */ - -function getPkcs5Padding(length) { - const c = 8 - (length % 8); - var result = []; - for (var i = 0; i < c; ++i) { - result.push(String.fromCharCode(c)); - } - return result.join(""); -} - /** * Add pkcs5 padding to a text. * @param {String} msg Text to add padding * @return {String} Text with padding added */ -function addPadding(msg) { - return msg + getPkcs5Padding(msg.length); +function encode(msg) { + const c = 8 - (msg.length % 8); + var result = []; + for (var i = 0; i < c; ++i) { + result.push(String.fromCharCode(c)); + } + return msg + result.join(""); } /** @@ -45,7 +36,7 @@ function addPadding(msg) { * @param {String} msg Text to remove padding from * @return {String} Text with padding removed */ -function removePadding(msg) { +function decode(msg) { var len = msg.length; if (len > 0) { var c = msg.charCodeAt(len - 1); @@ -57,6 +48,6 @@ function removePadding(msg) { } module.exports = { - addPadding: addPadding, - removePadding: removePadding + encode: encode, + decode: decode }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 8a19a378..d1cf9f0c 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -86,7 +86,7 @@ function encrypt(oid, cipher_algo, hash_algo, m, R, fingerprint) { R = curve.keyFromPublic(R.toByteArray()); const S = v.derive(R); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - const C = aes_kw.wrap(Z, m); + const C = aes_kw.wrap(Z, m.toBytes()); return { V: new BigInteger(v.getPublic()), C: C diff --git a/src/packet/public_key.js b/src/packet/public_key.js index c9f6af1e..faf8eec1 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -134,9 +134,9 @@ PublicKey.prototype.write = function () { } arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); - var param_count = crypto.getPubKeyParamCount(this.algorithm); + var paramCount = crypto.getPubKeyParamCount(this.algorithm); - for (var i = 0; i < param_count; i++) { + for (var i = 0; i < paramCount; i++) { arr.push(this.params[i].write()); } @@ -189,8 +189,8 @@ PublicKey.prototype.getFingerprint = function () { toHash = this.writeOld(); this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); } else if (this.version === 3) { - var param_count = crypto.getPubKeyParamCount(this.algorithm); - for (var i = 0; i < param_count; i++) { + var paramCount = crypto.getPubKeyParamCount(this.algorithm); + for (var i = 0; i < paramCount; i++) { toHash += this.params[i].toBytes(); } this.fingerprint = util.Uint8Array2str(crypto.hash.md5(util.str2Uint8Array(toHash))); diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index a05ff206..925abd3d 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -110,20 +110,17 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { var checksum = util.calc_checksum(this.sessionKey); data += util.Uint8Array2str(util.writeNumber(checksum, 2)); - var param; + var toEncrypt; if (this.publicKeyAlgorithm === 'ecdh') { - param = util.str2Uint8Array(crypto.pkcs5.addPadding(data)); + toEncrypt = new type_mpi(crypto.pkcs5.encode(data)); } else { - param = new type_mpi(); - param.fromBytes(crypto.pkcs1.eme.encode( - data, - key.params[0].byteLength())); + toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength())); } this.encrypted = crypto.publicKeyEncrypt( this.publicKeyAlgorithm, key.params, - param, + toEncrypt, key.fingerprint); }; @@ -145,7 +142,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = function (key) { var checksum; var decoded; if (this.publicKeyAlgorithm === 'ecdh') { - decoded = crypto.pkcs5.removePadding(result); + decoded = crypto.pkcs5.decode(result); checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); } else { decoded = crypto.pkcs1.eme.decode(result); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 52bfce8f..94a5e703 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -37,7 +37,6 @@ import publicKey from './public_key.js'; import enums from '../enums.js'; import util from '../util.js'; import crypto from '../crypto'; -import type_mpi from '../type/mpi.js'; import type_s2k from '../type/s2k.js'; /** @@ -76,38 +75,38 @@ function get_hash_fn(hash) { // Helper function -function parse_cleartext_mpi(hash_algorithm, cleartext, algorithm) { +function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { var hashlen = get_hash_len(hash_algorithm), hashfn = get_hash_fn(hash_algorithm); var hashtext = util.Uint8Array2str(cleartext.subarray(cleartext.length - hashlen, cleartext.length)); cleartext = cleartext.subarray(0, cleartext.length - hashlen); - var hash = util.Uint8Array2str(hashfn(cleartext)); if (hash !== hashtext) { return new Error("Hash mismatch."); } - var mpis = crypto.getPrivKeyParamCount(algorithm); + var types = crypto.getPrivKeyParamTypes(algorithm); + var params = crypto.constructParams(new Array(types.length), types); + var p = 0; - var j = 0; - var mpi = []; - - for (var i = 0; i < mpis && j < cleartext.length; i++) { - mpi[i] = new type_mpi(); - j += mpi[i].read(cleartext.subarray(j, cleartext.length)); + for (var i = 0; i < types.length && p < cleartext.length; i++) { + p += params[i].read(cleartext.subarray(p, cleartext.length)); + if (p > cleartext.length) { + throw new Error('Error reading MPI @:' + p); + } } - return mpi; + return params; } -function write_cleartext_mpi(hash_algorithm, algorithm, mpi) { +function write_cleartext_params(hash_algorithm, algorithm, params) { var arr = []; - var discard = crypto.getPubKeyParamCount(algorithm); + var numPublicParams = crypto.getPubKeyParamCount(algorithm); - for (var i = discard; i < mpi.length; i++) { - arr.push(mpi[i].write()); + for (var i = numPublicParams; i < params.length; i++) { + arr.push(params[i].write()); } var bytes = util.concatUint8Array(arr); @@ -143,11 +142,11 @@ SecretKey.prototype.read = function (bytes) { // - Plain or encrypted multiprecision integers comprising the secret // key data. These algorithm-specific fields are as described // below. - var parsedMPI = parse_cleartext_mpi('mod', bytes.subarray(1, bytes.length), this.algorithm); - if (parsedMPI instanceof Error) { - throw parsedMPI; + var privParams = parse_cleartext_params('mod', bytes.subarray(1, bytes.length), this.algorithm); + if (privParams instanceof Error) { + throw privParams; } - this.params = this.params.concat(parsedMPI); + this.params = this.params.concat(privParams); this.isDecrypted = true; } @@ -161,7 +160,7 @@ SecretKey.prototype.write = function () { if (!this.encrypted) { arr.push(new Uint8Array([0])); - arr.push(write_cleartext_mpi('mod', this.algorithm, this.params)); + arr.push(write_cleartext_params('mod', this.algorithm, this.params)); } else { arr.push(this.encrypted); } @@ -188,7 +187,7 @@ SecretKey.prototype.encrypt = function (passphrase) { var s2k = new type_s2k(), symmetric = 'aes256', - cleartext = write_cleartext_mpi('sha1', this.algorithm, this.params), + cleartext = write_cleartext_params('sha1', this.algorithm, this.params), key = produceEncryptionKey(s2k, passphrase, symmetric), blockLen = crypto.cipher[symmetric].blockSize, iv = crypto.random.getRandomBytes(blockLen); @@ -263,11 +262,11 @@ SecretKey.prototype.decrypt = function (passphrase) { 'sha1' : 'mod'; - var parsedMPI = parse_cleartext_mpi(hash, cleartext, this.algorithm); - if (parsedMPI instanceof Error) { + var privParams = parse_cleartext_params(hash, cleartext, this.algorithm); + if (privParams instanceof Error) { return false; } - this.params = this.params.concat(parsedMPI); + this.params = this.params.concat(privParams); this.isDecrypted = true; this.encrypted = null; return true; diff --git a/test/crypto/pkcs5.js b/test/crypto/pkcs5.js index 98b6a591..2e078ffa 100644 --- a/test/crypto/pkcs5.js +++ b/test/crypto/pkcs5.js @@ -16,7 +16,7 @@ describe('PKCS5 padding', function() { it('Add padding', function () { var s = ''; while (s.length < 16) { - var r = pkcs5.addPadding(s); + var r = pkcs5.encode(s); // 0..7 -> 8, 8..15 -> 16 var l = Math.ceil((s.length+1)/8)*8; var c = l - s.length; @@ -30,12 +30,12 @@ describe('PKCS5 padding', function() { for (var k=1; k<=8; ++k) { var s = repeat(' ', 8-k); var r = s + repeat(String.fromCharCode(k), k); - var t = pkcs5.removePadding(r); + var t = pkcs5.decode(r); expect(t).to.equal(s); } }); it('Invalid padding', function () { - expect(function () {pkcs5.removePadding(' ');}).to.throw(Error, /Invalid padding/); - expect(function () {pkcs5.removePadding('');}).to.throw(Error, /Invalid padding/); + expect(function () {pkcs5.decode(' ');}).to.throw(Error, /Invalid padding/); + expect(function () {pkcs5.decode('');}).to.throw(Error, /Invalid padding/); }); }); From 26de17150cdf239a98e647beb49c4331dc053035 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Sat, 29 Jul 2017 18:34:18 +0200 Subject: [PATCH 25/75] use internal aes encrypt in key wrap, start refactoring aes to do decryption as well --- src/crypto/aes_kw.js | 18 +- src/crypto/cipher/aes.js | 370 +++++++++++++++++++++++++++++++++++++-- test/general/key.js | 1 - 3 files changed, 365 insertions(+), 24 deletions(-) diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index 93f3143f..265cd053 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -15,8 +15,9 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// Implementation of RFC 3394 Key Wrap & Key Unwrap funcions +// Implementation of RFC 3394 AES Key Wrap & Key Unwrap funcions +import cipher from './cipher'; import AES from 'aes'; function createArrayBuffer(data) { @@ -60,7 +61,7 @@ function pack() { return new Uint8Array(buffer); } -function createCipher(key) { +function formatKey(key) { var length = key.length; var buffer = createArrayBuffer(key); var view = new DataView(buffer); @@ -68,12 +69,13 @@ function createCipher(key) { for (var i=0; i> 24) & 255); } -function F1(x0, x1, x2, x3) { - return B1(T1[x0 & 255]) | (B1(T1[(x1 >> 8) & 255]) << 8) | (B1(T1[(x2 >> 16) & 255]) << 16) | (B1(T1[x3 >>> 24]) << 24); +function F1_enc(x0, x1, x2, x3) { + return B1(T1_enc[x0 & 255]) | (B1(T1_enc[(x1 >> 8) & 255]) << 8) | (B1(T1_enc[(x2 >> 16) & 255]) << 16) | (B1(T1_enc[x3 >>> 24]) << 24); +} + +function F1_dec(x0, x1, x2, x3) { + return B1(T1_dec[x0 & 255]) | (B1(T1_dec[(x1 >> 8) & 255]) << 8) | (B1(T1_dec[(x2 >> 16) & 255]) << 16) | (B1(T1_dec[x3 >>> 24]) << 24); } function packBytes(octets) { @@ -370,7 +664,7 @@ function unpackBytes(packed) { var maxkc = 8; var maxrk = 14; -function keyExpansion(key) { +function scheduleEncKey(key) { var kc, i, j, r, t; var rounds; var keySched = new Array(maxrk + 1); @@ -456,6 +750,8 @@ function keyExpansion(key) { }; } + + function AESencrypt(block, ctx, t) { var r, rounds, b; @@ -468,10 +764,10 @@ function AESencrypt(block, ctx, t) { t[2] = b[2] ^ ctx.rk[r][2]; t[3] = b[3] ^ ctx.rk[r][3]; - b[0] = T1[t[0] & 255] ^ T2[(t[1] >> 8) & 255] ^ T3[(t[2] >> 16) & 255] ^ T4[t[3] >>> 24]; - b[1] = T1[t[1] & 255] ^ T2[(t[2] >> 8) & 255] ^ T3[(t[3] >> 16) & 255] ^ T4[t[0] >>> 24]; - b[2] = T1[t[2] & 255] ^ T2[(t[3] >> 8) & 255] ^ T3[(t[0] >> 16) & 255] ^ T4[t[1] >>> 24]; - b[3] = T1[t[3] & 255] ^ T2[(t[0] >> 8) & 255] ^ T3[(t[1] >> 16) & 255] ^ T4[t[2] >>> 24]; + b[0] = T1_enc[t[0] & 255] ^ T2_enc[(t[1] >> 8) & 255] ^ T3_enc[(t[2] >> 16) & 255] ^ T4_enc[t[3] >>> 24]; + b[1] = T1_enc[t[1] & 255] ^ T2_enc[(t[2] >> 8) & 255] ^ T3_enc[(t[3] >> 16) & 255] ^ T4_enc[t[0] >>> 24]; + b[2] = T1_enc[t[2] & 255] ^ T2_enc[(t[3] >> 8) & 255] ^ T3_enc[(t[0] >> 16) & 255] ^ T4_enc[t[1] >>> 24]; + b[3] = T1_enc[t[3] & 255] ^ T2_enc[(t[0] >> 8) & 255] ^ T3_enc[(t[1] >> 16) & 255] ^ T4_enc[t[2] >>> 24]; } // last round is special @@ -482,10 +778,44 @@ function AESencrypt(block, ctx, t) { t[2] = b[2] ^ ctx.rk[r][2]; t[3] = b[3] ^ ctx.rk[r][3]; - b[0] = F1(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; - b[1] = F1(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; - b[2] = F1(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; - b[3] = F1(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; + b[0] = F1_enc(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; + b[1] = F1_enc(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; + b[2] = F1_enc(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; + b[3] = F1_enc(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; + + return unpackBytes(b); +} + +function AESdecrypt(block, ctx, t) { + var r, rounds, b; + + b = packBytes(block); + rounds = ctx.rounds; + + for (r = 0; r < rounds - 1; r++) { + t[0] = b[0] ^ ctx.rk[r][0]; + t[1] = b[3] ^ ctx.rk[r][1]; + t[2] = b[2] ^ ctx.rk[r][2]; + t[3] = b[1] ^ ctx.rk[r][3]; + + b[0] = T1_dec[t[0] & 255] ^ T2_dec[(t[1] >> 8) & 255] ^ T3_dec[(t[2] >> 16) & 255] ^ T4_dec[t[3] >>> 24]; + b[1] = T1_dec[t[1] & 255] ^ T2_dec[(t[2] >> 8) & 255] ^ T3_dec[(t[3] >> 16) & 255] ^ T4_dec[t[0] >>> 24]; + b[2] = T1_dec[t[2] & 255] ^ T2_dec[(t[3] >> 8) & 255] ^ T3_dec[(t[0] >> 16) & 255] ^ T4_dec[t[1] >>> 24]; + b[3] = T1_dec[t[3] & 255] ^ T2_dec[(t[0] >> 8) & 255] ^ T3_dec[(t[1] >> 16) & 255] ^ T4_dec[t[2] >>> 24]; + } + + // last round is special + r = rounds - 1; + + t[0] = b[0] ^ ctx.rk[r][0]; + t[1] = b[3] ^ ctx.rk[r][1]; + t[2] = b[2] ^ ctx.rk[r][2]; + t[3] = b[1] ^ ctx.rk[r][3]; + + b[0] = F1_dec(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; + b[3] = F1_dec(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; + b[2] = F1_dec(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; + b[1] = F1_dec(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; return unpackBytes(b); } @@ -493,11 +823,15 @@ function AESencrypt(block, ctx, t) { function makeClass(length) { var c = function(key) { - this.key = keyExpansion(key); - this._temp = new Uint32Array(this.blockSize / 4); + this.enc_key = scheduleEncKey(key); + //this.dec_key = scheduleDecKey(this.enc_key); this.encrypt = function(block) { - return AESencrypt(block, this.key, this._temp); + return AESencrypt(block, this.enc_key, new Uint32Array(this.blockSize / 4)); + }; + + this.decrypt = function(block) { + return AESdecrypt(block, this.enc_key, new Uint32Array(this.blockSize / 4)); }; }; diff --git a/test/general/key.js b/test/general/key.js index 8a790a5f..4ab27a69 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -527,7 +527,6 @@ describe('Key', function() { '=Q/kB', '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); -<<<<<<< HEAD var valid_binding_sig_among_many_expired_sigs_pub = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', '', From cbe61e6febe0660d2d69a6ccd876163d663da49d Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Sun, 30 Jul 2017 12:12:02 +0200 Subject: [PATCH 26/75] key expansion for aes decryption keys, aesdecrypt function --- src/crypto/aes_kw.js | 14 +- src/crypto/cipher/aes.js | 355 +++++++++++++++++++++++++++++++++++---- 2 files changed, 328 insertions(+), 41 deletions(-) diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index 265cd053..ff6f7a88 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -18,7 +18,6 @@ // Implementation of RFC 3394 AES Key Wrap & Key Unwrap funcions import cipher from './cipher'; -import AES from 'aes'; function createArrayBuffer(data) { if (typeof data === "string") { @@ -91,10 +90,8 @@ function wrap(key, data) { // B = A || R[i] B[2] = R[2*i]; B[3] = R[2*i+1]; - B = pack(B); // B = AES(K, B) - B = aes.encrypt(B); - B = unpack(B); + B = unpack(aes.encrypt(pack(B))); // A = MSB(64, B) ^ t A = B.subarray(0, 2); A[0] = A[0] ^ t[0]; @@ -108,9 +105,8 @@ function wrap(key, data) { } function unwrap(key, data) { - //key = pack(formatKey(key)); - //var aes = new cipher["aes" + (key.length*8)](key); - var aes = new AES(formatKey(key)); + key = pack(formatKey(key)); + var aes = new cipher["aes" + (key.length*8)](key); var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); var C = unpack(data); var A = C.subarray(0, 2); @@ -128,9 +124,7 @@ function unwrap(key, data) { B[2] = R[2*i]; B[3] = R[2*i+1]; // B = AES-1(B) - //B = pack(B); - B = aes.decrypt(B); - //B = unpack(B); + B = unpack(aes.decrypt(pack(B))); // A = MSB(64, B) A = B.subarray(0, 2); // R[i] = LSB(64, B) diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js index 339f70c1..d3ea5938 100644 --- a/src/crypto/cipher/aes.js +++ b/src/crypto/cipher/aes.js @@ -46,7 +46,7 @@ var S = new Uint8Array([ 22 ]); -/* Precomputed lookup table for the inverse SBox +// Precomputed lookup table for the inverse SBox var S_inv = new Uint8Array([ 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, @@ -66,7 +66,7 @@ var S_inv = new Uint8Array([ 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125 -]); */ +]); var T1_enc = new Uint32Array([ 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, @@ -604,6 +604,276 @@ var T4_dec = new Uint32Array([ 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 ]); + +var U1 = new Uint32Array([ + 0x00000000,0x0b0d090e,0x161a121c,0x1d171b12, + 0x2c342438,0x27392d36,0x3a2e3624,0x31233f2a, + 0x58684870,0x5365417e,0x4e725a6c,0x457f5362, + 0x745c6c48,0x7f516546,0x62467e54,0x694b775a, + 0xb0d090e0,0xbbdd99ee,0xa6ca82fc,0xadc78bf2, + 0x9ce4b4d8,0x97e9bdd6,0x8afea6c4,0x81f3afca, + 0xe8b8d890,0xe3b5d19e,0xfea2ca8c,0xf5afc382, + 0xc48cfca8,0xcf81f5a6,0xd296eeb4,0xd99be7ba, + 0x7bbb3bdb,0x70b632d5,0x6da129c7,0x66ac20c9, + 0x578f1fe3,0x5c8216ed,0x41950dff,0x4a9804f1, + 0x23d373ab,0x28de7aa5,0x35c961b7,0x3ec468b9, + 0x0fe75793,0x04ea5e9d,0x19fd458f,0x12f04c81, + 0xcb6bab3b,0xc066a235,0xdd71b927,0xd67cb029, + 0xe75f8f03,0xec52860d,0xf1459d1f,0xfa489411, + 0x9303e34b,0x980eea45,0x8519f157,0x8e14f859, + 0xbf37c773,0xb43ace7d,0xa92dd56f,0xa220dc61, + 0xf66d76ad,0xfd607fa3,0xe07764b1,0xeb7a6dbf, + 0xda595295,0xd1545b9b,0xcc434089,0xc74e4987, + 0xae053edd,0xa50837d3,0xb81f2cc1,0xb31225cf, + 0x82311ae5,0x893c13eb,0x942b08f9,0x9f2601f7, + 0x46bde64d,0x4db0ef43,0x50a7f451,0x5baafd5f, + 0x6a89c275,0x6184cb7b,0x7c93d069,0x779ed967, + 0x1ed5ae3d,0x15d8a733,0x08cfbc21,0x03c2b52f, + 0x32e18a05,0x39ec830b,0x24fb9819,0x2ff69117, + 0x8dd64d76,0x86db4478,0x9bcc5f6a,0x90c15664, + 0xa1e2694e,0xaaef6040,0xb7f87b52,0xbcf5725c, + 0xd5be0506,0xdeb30c08,0xc3a4171a,0xc8a91e14, + 0xf98a213e,0xf2872830,0xef903322,0xe49d3a2c, + 0x3d06dd96,0x360bd498,0x2b1ccf8a,0x2011c684, + 0x1132f9ae,0x1a3ff0a0,0x0728ebb2,0x0c25e2bc, + 0x656e95e6,0x6e639ce8,0x737487fa,0x78798ef4, + 0x495ab1de,0x4257b8d0,0x5f40a3c2,0x544daacc, + 0xf7daec41,0xfcd7e54f,0xe1c0fe5d,0xeacdf753, + 0xdbeec879,0xd0e3c177,0xcdf4da65,0xc6f9d36b, + 0xafb2a431,0xa4bfad3f,0xb9a8b62d,0xb2a5bf23, + 0x83868009,0x888b8907,0x959c9215,0x9e919b1b, + 0x470a7ca1,0x4c0775af,0x51106ebd,0x5a1d67b3, + 0x6b3e5899,0x60335197,0x7d244a85,0x7629438b, + 0x1f6234d1,0x146f3ddf,0x097826cd,0x02752fc3, + 0x335610e9,0x385b19e7,0x254c02f5,0x2e410bfb, + 0x8c61d79a,0x876cde94,0x9a7bc586,0x9176cc88, + 0xa055f3a2,0xab58faac,0xb64fe1be,0xbd42e8b0, + 0xd4099fea,0xdf0496e4,0xc2138df6,0xc91e84f8, + 0xf83dbbd2,0xf330b2dc,0xee27a9ce,0xe52aa0c0, + 0x3cb1477a,0x37bc4e74,0x2aab5566,0x21a65c68, + 0x10856342,0x1b886a4c,0x069f715e,0x0d927850, + 0x64d90f0a,0x6fd40604,0x72c31d16,0x79ce1418, + 0x48ed2b32,0x43e0223c,0x5ef7392e,0x55fa3020, + 0x01b79aec,0x0aba93e2,0x17ad88f0,0x1ca081fe, + 0x2d83bed4,0x268eb7da,0x3b99acc8,0x3094a5c6, + 0x59dfd29c,0x52d2db92,0x4fc5c080,0x44c8c98e, + 0x75ebf6a4,0x7ee6ffaa,0x63f1e4b8,0x68fcedb6, + 0xb1670a0c,0xba6a0302,0xa77d1810,0xac70111e, + 0x9d532e34,0x965e273a,0x8b493c28,0x80443526, + 0xe90f427c,0xe2024b72,0xff155060,0xf418596e, + 0xc53b6644,0xce366f4a,0xd3217458,0xd82c7d56, + 0x7a0ca137,0x7101a839,0x6c16b32b,0x671bba25, + 0x5638850f,0x5d358c01,0x40229713,0x4b2f9e1d, + 0x2264e947,0x2969e049,0x347efb5b,0x3f73f255, + 0x0e50cd7f,0x055dc471,0x184adf63,0x1347d66d, + 0xcadc31d7,0xc1d138d9,0xdcc623cb,0xd7cb2ac5, + 0xe6e815ef,0xede51ce1,0xf0f207f3,0xfbff0efd, + 0x92b479a7,0x99b970a9,0x84ae6bbb,0x8fa362b5, + 0xbe805d9f,0xb58d5491,0xa89a4f83,0xa397468d +]); + +var U2 = new Uint32Array([ + 0x00000000,0x0d090e0b,0x1a121c16,0x171b121d, + 0x3424382c,0x392d3627,0x2e36243a,0x233f2a31, + 0x68487058,0x65417e53,0x725a6c4e,0x7f536245, + 0x5c6c4874,0x5165467f,0x467e5462,0x4b775a69, + 0xd090e0b0,0xdd99eebb,0xca82fca6,0xc78bf2ad, + 0xe4b4d89c,0xe9bdd697,0xfea6c48a,0xf3afca81, + 0xb8d890e8,0xb5d19ee3,0xa2ca8cfe,0xafc382f5, + 0x8cfca8c4,0x81f5a6cf,0x96eeb4d2,0x9be7bad9, + 0xbb3bdb7b,0xb632d570,0xa129c76d,0xac20c966, + 0x8f1fe357,0x8216ed5c,0x950dff41,0x9804f14a, + 0xd373ab23,0xde7aa528,0xc961b735,0xc468b93e, + 0xe757930f,0xea5e9d04,0xfd458f19,0xf04c8112, + 0x6bab3bcb,0x66a235c0,0x71b927dd,0x7cb029d6, + 0x5f8f03e7,0x52860dec,0x459d1ff1,0x489411fa, + 0x03e34b93,0x0eea4598,0x19f15785,0x14f8598e, + 0x37c773bf,0x3ace7db4,0x2dd56fa9,0x20dc61a2, + 0x6d76adf6,0x607fa3fd,0x7764b1e0,0x7a6dbfeb, + 0x595295da,0x545b9bd1,0x434089cc,0x4e4987c7, + 0x053eddae,0x0837d3a5,0x1f2cc1b8,0x1225cfb3, + 0x311ae582,0x3c13eb89,0x2b08f994,0x2601f79f, + 0xbde64d46,0xb0ef434d,0xa7f45150,0xaafd5f5b, + 0x89c2756a,0x84cb7b61,0x93d0697c,0x9ed96777, + 0xd5ae3d1e,0xd8a73315,0xcfbc2108,0xc2b52f03, + 0xe18a0532,0xec830b39,0xfb981924,0xf691172f, + 0xd64d768d,0xdb447886,0xcc5f6a9b,0xc1566490, + 0xe2694ea1,0xef6040aa,0xf87b52b7,0xf5725cbc, + 0xbe0506d5,0xb30c08de,0xa4171ac3,0xa91e14c8, + 0x8a213ef9,0x872830f2,0x903322ef,0x9d3a2ce4, + 0x06dd963d,0x0bd49836,0x1ccf8a2b,0x11c68420, + 0x32f9ae11,0x3ff0a01a,0x28ebb207,0x25e2bc0c, + 0x6e95e665,0x639ce86e,0x7487fa73,0x798ef478, + 0x5ab1de49,0x57b8d042,0x40a3c25f,0x4daacc54, + 0xdaec41f7,0xd7e54ffc,0xc0fe5de1,0xcdf753ea, + 0xeec879db,0xe3c177d0,0xf4da65cd,0xf9d36bc6, + 0xb2a431af,0xbfad3fa4,0xa8b62db9,0xa5bf23b2, + 0x86800983,0x8b890788,0x9c921595,0x919b1b9e, + 0x0a7ca147,0x0775af4c,0x106ebd51,0x1d67b35a, + 0x3e58996b,0x33519760,0x244a857d,0x29438b76, + 0x6234d11f,0x6f3ddf14,0x7826cd09,0x752fc302, + 0x5610e933,0x5b19e738,0x4c02f525,0x410bfb2e, + 0x61d79a8c,0x6cde9487,0x7bc5869a,0x76cc8891, + 0x55f3a2a0,0x58faacab,0x4fe1beb6,0x42e8b0bd, + 0x099fead4,0x0496e4df,0x138df6c2,0x1e84f8c9, + 0x3dbbd2f8,0x30b2dcf3,0x27a9ceee,0x2aa0c0e5, + 0xb1477a3c,0xbc4e7437,0xab55662a,0xa65c6821, + 0x85634210,0x886a4c1b,0x9f715e06,0x9278500d, + 0xd90f0a64,0xd406046f,0xc31d1672,0xce141879, + 0xed2b3248,0xe0223c43,0xf7392e5e,0xfa302055, + 0xb79aec01,0xba93e20a,0xad88f017,0xa081fe1c, + 0x83bed42d,0x8eb7da26,0x99acc83b,0x94a5c630, + 0xdfd29c59,0xd2db9252,0xc5c0804f,0xc8c98e44, + 0xebf6a475,0xe6ffaa7e,0xf1e4b863,0xfcedb668, + 0x670a0cb1,0x6a0302ba,0x7d1810a7,0x70111eac, + 0x532e349d,0x5e273a96,0x493c288b,0x44352680, + 0x0f427ce9,0x024b72e2,0x155060ff,0x18596ef4, + 0x3b6644c5,0x366f4ace,0x217458d3,0x2c7d56d8, + 0x0ca1377a,0x01a83971,0x16b32b6c,0x1bba2567, + 0x38850f56,0x358c015d,0x22971340,0x2f9e1d4b, + 0x64e94722,0x69e04929,0x7efb5b34,0x73f2553f, + 0x50cd7f0e,0x5dc47105,0x4adf6318,0x47d66d13, + 0xdc31d7ca,0xd138d9c1,0xc623cbdc,0xcb2ac5d7, + 0xe815efe6,0xe51ce1ed,0xf207f3f0,0xff0efdfb, + 0xb479a792,0xb970a999,0xae6bbb84,0xa362b58f, + 0x805d9fbe,0x8d5491b5,0x9a4f83a8,0x97468da3, +]); + +var U3 = new Uint32Array([ + 0x00000000,0x090e0b0d,0x121c161a,0x1b121d17, + 0x24382c34,0x2d362739,0x36243a2e,0x3f2a3123, + 0x48705868,0x417e5365,0x5a6c4e72,0x5362457f, + 0x6c48745c,0x65467f51,0x7e546246,0x775a694b, + 0x90e0b0d0,0x99eebbdd,0x82fca6ca,0x8bf2adc7, + 0xb4d89ce4,0xbdd697e9,0xa6c48afe,0xafca81f3, + 0xd890e8b8,0xd19ee3b5,0xca8cfea2,0xc382f5af, + 0xfca8c48c,0xf5a6cf81,0xeeb4d296,0xe7bad99b, + 0x3bdb7bbb,0x32d570b6,0x29c76da1,0x20c966ac, + 0x1fe3578f,0x16ed5c82,0x0dff4195,0x04f14a98, + 0x73ab23d3,0x7aa528de,0x61b735c9,0x68b93ec4, + 0x57930fe7,0x5e9d04ea,0x458f19fd,0x4c8112f0, + 0xab3bcb6b,0xa235c066,0xb927dd71,0xb029d67c, + 0x8f03e75f,0x860dec52,0x9d1ff145,0x9411fa48, + 0xe34b9303,0xea45980e,0xf1578519,0xf8598e14, + 0xc773bf37,0xce7db43a,0xd56fa92d,0xdc61a220, + 0x76adf66d,0x7fa3fd60,0x64b1e077,0x6dbfeb7a, + 0x5295da59,0x5b9bd154,0x4089cc43,0x4987c74e, + 0x3eddae05,0x37d3a508,0x2cc1b81f,0x25cfb312, + 0x1ae58231,0x13eb893c,0x08f9942b,0x01f79f26, + 0xe64d46bd,0xef434db0,0xf45150a7,0xfd5f5baa, + 0xc2756a89,0xcb7b6184,0xd0697c93,0xd967779e, + 0xae3d1ed5,0xa73315d8,0xbc2108cf,0xb52f03c2, + 0x8a0532e1,0x830b39ec,0x981924fb,0x91172ff6, + 0x4d768dd6,0x447886db,0x5f6a9bcc,0x566490c1, + 0x694ea1e2,0x6040aaef,0x7b52b7f8,0x725cbcf5, + 0x0506d5be,0x0c08deb3,0x171ac3a4,0x1e14c8a9, + 0x213ef98a,0x2830f287,0x3322ef90,0x3a2ce49d, + 0xdd963d06,0xd498360b,0xcf8a2b1c,0xc6842011, + 0xf9ae1132,0xf0a01a3f,0xebb20728,0xe2bc0c25, + 0x95e6656e,0x9ce86e63,0x87fa7374,0x8ef47879, + 0xb1de495a,0xb8d04257,0xa3c25f40,0xaacc544d, + 0xec41f7da,0xe54ffcd7,0xfe5de1c0,0xf753eacd, + 0xc879dbee,0xc177d0e3,0xda65cdf4,0xd36bc6f9, + 0xa431afb2,0xad3fa4bf,0xb62db9a8,0xbf23b2a5, + 0x80098386,0x8907888b,0x9215959c,0x9b1b9e91, + 0x7ca1470a,0x75af4c07,0x6ebd5110,0x67b35a1d, + 0x58996b3e,0x51976033,0x4a857d24,0x438b7629, + 0x34d11f62,0x3ddf146f,0x26cd0978,0x2fc30275, + 0x10e93356,0x19e7385b,0x02f5254c,0x0bfb2e41, + 0xd79a8c61,0xde94876c,0xc5869a7b,0xcc889176, + 0xf3a2a055,0xfaacab58,0xe1beb64f,0xe8b0bd42, + 0x9fead409,0x96e4df04,0x8df6c213,0x84f8c91e, + 0xbbd2f83d,0xb2dcf330,0xa9ceee27,0xa0c0e52a, + 0x477a3cb1,0x4e7437bc,0x55662aab,0x5c6821a6, + 0x63421085,0x6a4c1b88,0x715e069f,0x78500d92, + 0x0f0a64d9,0x06046fd4,0x1d1672c3,0x141879ce, + 0x2b3248ed,0x223c43e0,0x392e5ef7,0x302055fa, + 0x9aec01b7,0x93e20aba,0x88f017ad,0x81fe1ca0, + 0xbed42d83,0xb7da268e,0xacc83b99,0xa5c63094, + 0xd29c59df,0xdb9252d2,0xc0804fc5,0xc98e44c8, + 0xf6a475eb,0xffaa7ee6,0xe4b863f1,0xedb668fc, + 0x0a0cb167,0x0302ba6a,0x1810a77d,0x111eac70, + 0x2e349d53,0x273a965e,0x3c288b49,0x35268044, + 0x427ce90f,0x4b72e202,0x5060ff15,0x596ef418, + 0x6644c53b,0x6f4ace36,0x7458d321,0x7d56d82c, + 0xa1377a0c,0xa8397101,0xb32b6c16,0xba25671b, + 0x850f5638,0x8c015d35,0x97134022,0x9e1d4b2f, + 0xe9472264,0xe0492969,0xfb5b347e,0xf2553f73, + 0xcd7f0e50,0xc471055d,0xdf63184a,0xd66d1347, + 0x31d7cadc,0x38d9c1d1,0x23cbdcc6,0x2ac5d7cb, + 0x15efe6e8,0x1ce1ede5,0x07f3f0f2,0x0efdfbff, + 0x79a792b4,0x70a999b9,0x6bbb84ae,0x62b58fa3, + 0x5d9fbe80,0x5491b58d,0x4f83a89a,0x468da397 +]); + +var U4 = new Uint32Array([ + 0x00000000,0x0e0b0d09,0x1c161a12,0x121d171b, + 0x382c3424,0x3627392d,0x243a2e36,0x2a31233f, + 0x70586848,0x7e536541,0x6c4e725a,0x62457f53, + 0x48745c6c,0x467f5165,0x5462467e,0x5a694b77, + 0xe0b0d090,0xeebbdd99,0xfca6ca82,0xf2adc78b, + 0xd89ce4b4,0xd697e9bd,0xc48afea6,0xca81f3af, + 0x90e8b8d8,0x9ee3b5d1,0x8cfea2ca,0x82f5afc3, + 0xa8c48cfc,0xa6cf81f5,0xb4d296ee,0xbad99be7, + 0xdb7bbb3b,0xd570b632,0xc76da129,0xc966ac20, + 0xe3578f1f,0xed5c8216,0xff41950d,0xf14a9804, + 0xab23d373,0xa528de7a,0xb735c961,0xb93ec468, + 0x930fe757,0x9d04ea5e,0x8f19fd45,0x8112f04c, + 0x3bcb6bab,0x35c066a2,0x27dd71b9,0x29d67cb0, + 0x03e75f8f,0x0dec5286,0x1ff1459d,0x11fa4894, + 0x4b9303e3,0x45980eea,0x578519f1,0x598e14f8, + 0x73bf37c7,0x7db43ace,0x6fa92dd5,0x61a220dc, + 0xadf66d76,0xa3fd607f,0xb1e07764,0xbfeb7a6d, + 0x95da5952,0x9bd1545b,0x89cc4340,0x87c74e49, + 0xddae053e,0xd3a50837,0xc1b81f2c,0xcfb31225, + 0xe582311a,0xeb893c13,0xf9942b08,0xf79f2601, + 0x4d46bde6,0x434db0ef,0x5150a7f4,0x5f5baafd, + 0x756a89c2,0x7b6184cb,0x697c93d0,0x67779ed9, + 0x3d1ed5ae,0x3315d8a7,0x2108cfbc,0x2f03c2b5, + 0x0532e18a,0x0b39ec83,0x1924fb98,0x172ff691, + 0x768dd64d,0x7886db44,0x6a9bcc5f,0x6490c156, + 0x4ea1e269,0x40aaef60,0x52b7f87b,0x5cbcf572, + 0x06d5be05,0x08deb30c,0x1ac3a417,0x14c8a91e, + 0x3ef98a21,0x30f28728,0x22ef9033,0x2ce49d3a, + 0x963d06dd,0x98360bd4,0x8a2b1ccf,0x842011c6, + 0xae1132f9,0xa01a3ff0,0xb20728eb,0xbc0c25e2, + 0xe6656e95,0xe86e639c,0xfa737487,0xf478798e, + 0xde495ab1,0xd04257b8,0xc25f40a3,0xcc544daa, + 0x41f7daec,0x4ffcd7e5,0x5de1c0fe,0x53eacdf7, + 0x79dbeec8,0x77d0e3c1,0x65cdf4da,0x6bc6f9d3, + 0x31afb2a4,0x3fa4bfad,0x2db9a8b6,0x23b2a5bf, + 0x09838680,0x07888b89,0x15959c92,0x1b9e919b, + 0xa1470a7c,0xaf4c0775,0xbd51106e,0xb35a1d67, + 0x996b3e58,0x97603351,0x857d244a,0x8b762943, + 0xd11f6234,0xdf146f3d,0xcd097826,0xc302752f, + 0xe9335610,0xe7385b19,0xf5254c02,0xfb2e410b, + 0x9a8c61d7,0x94876cde,0x869a7bc5,0x889176cc, + 0xa2a055f3,0xacab58fa,0xbeb64fe1,0xb0bd42e8, + 0xead4099f,0xe4df0496,0xf6c2138d,0xf8c91e84, + 0xd2f83dbb,0xdcf330b2,0xceee27a9,0xc0e52aa0, + 0x7a3cb147,0x7437bc4e,0x662aab55,0x6821a65c, + 0x42108563,0x4c1b886a,0x5e069f71,0x500d9278, + 0x0a64d90f,0x046fd406,0x1672c31d,0x1879ce14, + 0x3248ed2b,0x3c43e022,0x2e5ef739,0x2055fa30, + 0xec01b79a,0xe20aba93,0xf017ad88,0xfe1ca081, + 0xd42d83be,0xda268eb7,0xc83b99ac,0xc63094a5, + 0x9c59dfd2,0x9252d2db,0x804fc5c0,0x8e44c8c9, + 0xa475ebf6,0xaa7ee6ff,0xb863f1e4,0xb668fced, + 0x0cb1670a,0x02ba6a03,0x10a77d18,0x1eac7011, + 0x349d532e,0x3a965e27,0x288b493c,0x26804435, + 0x7ce90f42,0x72e2024b,0x60ff1550,0x6ef41859, + 0x44c53b66,0x4ace366f,0x58d32174,0x56d82c7d, + 0x377a0ca1,0x397101a8,0x2b6c16b3,0x25671bba, + 0x0f563885,0x015d358c,0x13402297,0x1d4b2f9e, + 0x472264e9,0x492969e0,0x5b347efb,0x553f73f2, + 0x7f0e50cd,0x71055dc4,0x63184adf,0x6d1347d6, + 0xd7cadc31,0xd9c1d138,0xcbdcc623,0xc5d7cb2a, + 0xefe6e815,0xe1ede51c,0xf3f0f207,0xfdfbff0e, + 0xa792b479,0xa999b970,0xbb84ae6b,0xb58fa362, + 0x9fbe805d,0x91b58d54,0x83a89a4f,0x8da39746 +]); + + function B0(x) { return (x & 255); } @@ -620,14 +890,10 @@ function B3(x) { return ((x >> 24) & 255); } -function F1_enc(x0, x1, x2, x3) { +function F1(x0, x1, x2, x3) { return B1(T1_enc[x0 & 255]) | (B1(T1_enc[(x1 >> 8) & 255]) << 8) | (B1(T1_enc[(x2 >> 16) & 255]) << 16) | (B1(T1_enc[x3 >>> 24]) << 24); } -function F1_dec(x0, x1, x2, x3) { - return B1(T1_dec[x0 & 255]) | (B1(T1_dec[(x1 >> 8) & 255]) << 8) | (B1(T1_dec[(x2 >> 16) & 255]) << 16) | (B1(T1_dec[x3 >>> 24]) << 24); -} - function packBytes(octets) { var i, j; var len = octets.length; @@ -664,7 +930,7 @@ function unpackBytes(packed) { var maxkc = 8; var maxrk = 14; -function scheduleEncKey(key) { +function keyExpansionEnc(key) { var kc, i, j, r, t; var rounds; var keySched = new Array(maxrk + 1); @@ -750,7 +1016,31 @@ function scheduleEncKey(key) { }; } +function keyExpansionDec(ctx) { + var tmp; + var keySched = new Array(ctx.length); + var rounds = ctx.rounds; + + for(var r = 0; r < maxrk + 1; r++) { + keySched[r] = new Uint32Array(4); + keySched[r][0] = ctx.rk[r][0]; + keySched[r][1] = ctx.rk[r][1]; + keySched[r][2] = ctx.rk[r][2]; + keySched[r][3] = ctx.rk[r][3]; + } + + for(r = 1; r < rounds; r++) { + tmp=keySched[r][0]; keySched[r][0] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; + tmp=keySched[r][1]; keySched[r][1] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; + tmp=keySched[r][2]; keySched[r][2] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; + tmp=keySched[r][3]; keySched[r][3] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; + } + return { + rounds : rounds, + rk : keySched + }; +} function AESencrypt(block, ctx, t) { var r, rounds, b; @@ -778,10 +1068,10 @@ function AESencrypt(block, ctx, t) { t[2] = b[2] ^ ctx.rk[r][2]; t[3] = b[3] ^ ctx.rk[r][3]; - b[0] = F1_enc(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; - b[1] = F1_enc(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; - b[2] = F1_enc(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; - b[3] = F1_enc(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; + b[0] = F1(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; + b[1] = F1(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; + b[2] = F1(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; + b[3] = F1(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; return unpackBytes(b); } @@ -792,30 +1082,33 @@ function AESdecrypt(block, ctx, t) { b = packBytes(block); rounds = ctx.rounds; - for (r = 0; r < rounds - 1; r++) { + for (r = rounds; r > 1; r--) { t[0] = b[0] ^ ctx.rk[r][0]; - t[1] = b[3] ^ ctx.rk[r][1]; + t[1] = b[1] ^ ctx.rk[r][1]; t[2] = b[2] ^ ctx.rk[r][2]; - t[3] = b[1] ^ ctx.rk[r][3]; + t[3] = b[3] ^ ctx.rk[r][3]; - b[0] = T1_dec[t[0] & 255] ^ T2_dec[(t[1] >> 8) & 255] ^ T3_dec[(t[2] >> 16) & 255] ^ T4_dec[t[3] >>> 24]; - b[1] = T1_dec[t[1] & 255] ^ T2_dec[(t[2] >> 8) & 255] ^ T3_dec[(t[3] >> 16) & 255] ^ T4_dec[t[0] >>> 24]; - b[2] = T1_dec[t[2] & 255] ^ T2_dec[(t[3] >> 8) & 255] ^ T3_dec[(t[0] >> 16) & 255] ^ T4_dec[t[1] >>> 24]; - b[3] = T1_dec[t[3] & 255] ^ T2_dec[(t[0] >> 8) & 255] ^ T3_dec[(t[1] >> 16) & 255] ^ T4_dec[t[2] >>> 24]; + b[0] = T1_dec[B0(t[0])] ^ T2_dec[B1(t[3])] ^ T3_dec[B2(t[2])] ^ T4_dec[B3(t[1])]; + b[1] = T1_dec[B0(t[1])] ^ T2_dec[B1(t[0])] ^ T3_dec[B2(t[3])] ^ T4_dec[B3(t[2])]; + b[2] = T1_dec[B0(t[2])] ^ T2_dec[B1(t[1])] ^ T3_dec[B2(t[0])] ^ T4_dec[B3(t[3])]; + b[3] = T1_dec[B0(t[3])] ^ T2_dec[B1(t[2])] ^ T3_dec[B2(t[1])] ^ T4_dec[B3(t[0])]; } // last round is special - r = rounds - 1; + t[0] = b[0] ^ ctx.rk[1][0]; + t[1] = b[1] ^ ctx.rk[1][1]; + t[2] = b[2] ^ ctx.rk[1][2]; + t[3] = b[3] ^ ctx.rk[1][3]; - t[0] = b[0] ^ ctx.rk[r][0]; - t[1] = b[3] ^ ctx.rk[r][1]; - t[2] = b[2] ^ ctx.rk[r][2]; - t[3] = b[1] ^ ctx.rk[r][3]; + b[0] = S_inv[B0(t[0])] | (S_inv[B1(t[3])]<<8) | (S_inv[B2(t[2])]<<16) | (S_inv[B3(t[1])]<<24); + b[1] = S_inv[B0(t[1])] | (S_inv[B1(t[0])]<<8) | (S_inv[B2(t[3])]<<16) | (S_inv[B3(t[2])]<<24); + b[2] = S_inv[B0(t[2])] | (S_inv[B1(t[1])]<<8) | (S_inv[B2(t[0])]<<16) | (S_inv[B3(t[3])]<<24); + b[3] = S_inv[B0(t[3])] | (S_inv[B1(t[2])]<<8) | (S_inv[B2(t[1])]<<16) | (S_inv[B3(t[0])]<<24); - b[0] = F1_dec(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; - b[3] = F1_dec(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; - b[2] = F1_dec(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; - b[1] = F1_dec(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; + b[0] ^= ctx.rk[0][0]; + b[1] ^= ctx.rk[0][1]; + b[2] ^= ctx.rk[0][2]; + b[3] ^= ctx.rk[0][3]; return unpackBytes(b); } @@ -823,15 +1116,15 @@ function AESdecrypt(block, ctx, t) { function makeClass(length) { var c = function(key) { - this.enc_key = scheduleEncKey(key); - //this.dec_key = scheduleDecKey(this.enc_key); + this.enc_key = keyExpansionEnc(key); + this.dec_key = keyExpansionDec(this.enc_key); this.encrypt = function(block) { return AESencrypt(block, this.enc_key, new Uint32Array(this.blockSize / 4)); }; this.decrypt = function(block) { - return AESdecrypt(block, this.enc_key, new Uint32Array(this.blockSize / 4)); + return AESdecrypt(block, this.dec_key, new Uint32Array(this.blockSize / 4)); }; }; From f8bfde74478aac1e179fb087be9f9651bae2e19f Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Sun, 30 Jul 2017 14:03:15 +0200 Subject: [PATCH 27/75] comments, docs --- README-ECC.md | 134 --------------------------------------- src/crypto/crypto.js | 45 ++++++------- src/packet/public_key.js | 4 +- src/packet/secret_key.js | 4 +- 4 files changed, 24 insertions(+), 163 deletions(-) delete mode 100644 README-ECC.md diff --git a/README-ECC.md b/README-ECC.md deleted file mode 100644 index 6454dc61..00000000 --- a/README-ECC.md +++ /dev/null @@ -1,134 +0,0 @@ -# Elliptic Curve Cryptography support for OpenPGPjs - -## Description - -This work is to provide an implementation of [RFC 6637](http://www.ietf.org/rfc/rfc6637.txt) -for OpenPGP.js. - -## Compatibility with GnuPG - -In order to assure compatibility of the provided implementation with RFC 6637, -the keys and messages were tested against GnuPG version v2.1.8, compiled -with a beta version of libgcrypt v1.7.0-beta262. - -It was tested that keys, messages, and signatures generated by GnuPG -were imported correctly. Also keys, messages and signatures generated by this -implementation are correctly imported by GnuPG. - -```txt -> gpg2 --homedir ../home --version -gpg (GnuPG) 2.1.8 -libgcrypt 1.7.0-beta262 -Copyright (C) 2015 Free Software Foundation, Inc. -License GPLv3+: GNU GPL version 3 or later -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. - -Home: ../home -Supported algorithms: -Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA -Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH, - CAMELLIA128, CAMELLIA192, CAMELLIA256 -Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224 -Compression: Uncompressed, ZIP, ZLIB -``` - -## Extra dependencies - -There are two new dependencies: - -* [Elliptic](https://github.com/indutny/elliptic/) for the elliptic curve - cryptography. MIT license. - -* [Aes](https://github.com/cryptocoinjs/aes) required to implement RFC 3394 - Key wrap and Key Unwrap functions. BSD License. - -## Examples - -### Generate new key - -```js -var openpgp = require('openpgp'); - -var options = { - curve: 'secp256k1', - userIds: {name: 'Hamlet', email: 'hamlet@example.net'}, - passphrase: 'To be, or not to be: that is the question' -}; - -openpgp.generateKey(options).then(function(key) { - // success - var privkey = key.privateKeyArmored; - var pubkey = key.publicKeyArmored; -}).catch(function(error) { - // failure -}); -``` - -### Generate keypair from bitcoin key - -```js -var openpgp = require('openpgp'); -var bs58check = require('bs58check'); - -var wif = 'KyiAchQgMKuXQu89j6k6UVZQj7brK6cM79JfmDvkNXPVW24L1thi'; -var buff = bs58check.decode(wif); -var privateKey = buff.slice(1, -1); -privateKey = openpgp.util.bin2str(privateKey); - -var options = { - curve: 'secp256k1', - userIds: {name: 'Hamlet', email: 'hamlet@example.net'}, - passphrase: 'To be, or not to be: that is the question', - material: { - key: privateKey, - subkey: privateKey - } -}; - -openpgp.generateKey(options).then(function(key) { - // success - var privkey = key.privateKeyArmored; - var pubkey = key.publicKeyArmored; -}).catch(function(error) { - // failure -}); -``` - -### Signature, encryption and decryption - -The normal operations: signature, encryption and decryption require -no modifications. - -```js -var openpgp = require('openpgp'); - -var keyData = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'; -var key = openpgp.key.readArmored(keyData); - -openpgp.encrypt({publicKeys: key.keys, data: 'Hello, World!'}).then(function(msg) { - // success -}).catch(function(error) { - // failure -}); -``` - -## Possible improvements - -* The dependency with AES library can be eliminated, a suitable AES decrypt - function is provided. It is only used by the wrap and unwrap functions - in the crypto/rfc3394.js file. - -## Note - -Although the example uses the same value to generate the main key, and -the subkey, it is a recommended practice to use different keys. -The main key is used for signature and the subkeys are used for encryption. - -## Resources - -* Elliptic Curve Cryptography (ECC) in OpenPGP [RFC 6637](http://www.ietf.org/rfc/rfc6637.txt) -* OpenPGP Message Format [RFC 4880](http://www.ietf.org/rfc/rfc4880.txt) -* Advanced Encryption Standard (AES) Key Wrap Algorithm [RFC 3394](http://www.ietf.org/rfc/rfc3394.txt) -* A JavaScript component for the Advanced Encryption Standard (AES) [AES](https://github.com/cryptocoinjs/aes) -* Fast elliptic-curve cryptography in a plain javascript implementation [Elliptic](https://github.com/indutny/elliptic/) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 912633ab..0c16a44d 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -65,10 +65,9 @@ export default { * 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} publicParams Algorithm dependent multiprecision integers + * @param {Array} publicParams Algorithm dependent params * @param {module:type/mpi} data Data to be encrypted as MPI - * @return {Array} if RSA an module:type/mpi; - * if elgamal encryption an array of two module:type/mpi is returned; otherwise null + * @return {Array} encrypted session key parameters */ publicKeyEncrypt: function(algo, publicParams, data, fingerprint) { var types = this.getEncSessionKeyParamTypes(algo); @@ -111,17 +110,13 @@ export default { * 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} publicParams Algorithm dependent multiprecision integers - * of the public key part of the private key - * @param {Array} secretMPIs Algorithm dependent multiprecision integers - * of the private key used - * @param {module:type/mpi} data Data to be encrypted as MPI + * @param {Array} keyIntegers Algorithm dependent params + * @param {String} fingerprint Recipient fingerprint * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null */ publicKeyDecrypt: function(algo, keyIntegers, dataIntegers, fingerprint) { var p; - var bn = (function() { switch (algo) { case 'rsa_encrypt_sign': @@ -163,9 +158,9 @@ export default { return result; }, - /** Returns the number of integers comprising the private key of an algorithm + /** Returns the types comprising the private key of an algorithm * @param {String} algo The public key algorithm - * @return {Integer} The number of integers. + * @return {Array} The array of types */ getPrivKeyParamTypes: function(algo) { switch (algo) { @@ -196,10 +191,10 @@ export default { } }, - getPrivKeyParamCount: function(algo) { - return this.getPrivKeyParamTypes(algo).length; - }, - + /** Returns the types comprising the public key of an algorithm + * @param {String} algo The public key algorithm + * @return {Array} The array of types + */ getPubKeyParamTypes: function(algo) { // Algorithm-Specific Fields for RSA public keys: // - a multiprecision integer (MPI) of RSA public modulus n; @@ -230,7 +225,7 @@ export default { // Algorithm-Specific Fields for ECDH public keys: // - OID of curve; // - MPI of EC point representing public key. - // - variable-length field containing KDF parameters. + // - KDF: variable-length field containing KDF parameters. case 'ecdh': return ['oid', 'mpi', 'kdf']; default: @@ -238,10 +233,10 @@ export default { } }, - getPubKeyParamCount: function(algo) { - return this.getPubKeyParamTypes(algo).length; - }, - + /** Returns the types comprising the encrypted session key of an algorithm + * @param {String} algo The public key algorithm + * @return {Array} The array of types + */ getEncSessionKeyParamTypes: function(algo) { switch (algo) { // Algorithm-Specific Fields for RSA encrypted session keys: @@ -258,7 +253,7 @@ export default { // Algorithm-Specific Fields for ECDH encrypted session keys: // - MPI containing the ephemeral key used to establish the shared secret - // - ECDHSymmetricKey + // - ECDH Symmetric Key case 'ecdh': return ['mpi', 'ecdh_symkey']; @@ -267,10 +262,10 @@ export default { } }, - getEncSessionKeyParamCount: function(algo) { - return this.getEncSessionKeyParamTypes(algo).length; - }, - + /** Generate algorithm-specific key parameters + * @param {String} algo The public key algorithm + * @return {Array} The array of parameters + */ generateParams: function(algo, bits, curve) { var types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); switch (algo) { diff --git a/src/packet/public_key.js b/src/packet/public_key.js index faf8eec1..d8f5a33e 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -134,7 +134,7 @@ PublicKey.prototype.write = function () { } arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); - var paramCount = crypto.getPubKeyParamCount(this.algorithm); + var paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; for (var i = 0; i < paramCount; i++) { arr.push(this.params[i].write()); @@ -189,7 +189,7 @@ PublicKey.prototype.getFingerprint = function () { toHash = this.writeOld(); this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); } else if (this.version === 3) { - var paramCount = crypto.getPubKeyParamCount(this.algorithm); + var paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; for (var i = 0; i < paramCount; i++) { toHash += this.params[i].toBytes(); } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 94a5e703..a666175b 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -103,7 +103,7 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { function write_cleartext_params(hash_algorithm, algorithm, params) { var arr = []; - var numPublicParams = crypto.getPubKeyParamCount(algorithm); + var numPublicParams = crypto.getPubKeyParamTypes(algorithm).length; for (var i = numPublicParams; i < params.length; i++) { arr.push(params[i].write()); @@ -288,6 +288,6 @@ SecretKey.prototype.clearPrivateParams = function () { if (!this.encrypted) { throw new Error('If secret key is not encrypted, clearing private MPIs is irreversible.'); } - this.params = this.params.slice(0, crypto.getPubKeyParamCount(this.algorithm)); + this.params = this.params.slice(0, crypto.getPubKeyParamTypes(this.algorithm).length); this.isDecrypted = false; }; From 86e15dbd0a19531bdbfbca0f9b554560369df9de Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Sun, 26 Nov 2017 19:34:43 -0800 Subject: [PATCH 28/75] aes kw update --- src/crypto/aes_kw.js | 143 +++++++++++++++++++----------------------- test/crypto/aes_kw.js | 2 +- 2 files changed, 66 insertions(+), 79 deletions(-) diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index ff6f7a88..b0ea1d1d 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -19,6 +19,71 @@ import cipher from './cipher'; +function wrap(key, data) { + var aes = new cipher["aes" + (key.length*8)](key); + var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); + var P = unpack(data); + var A = IV; + var R = P; + var n = P.length/2; + var t = new Uint32Array([0, 0]); + var B = new Uint32Array(4); + for (var j = 0; j <= 5; ++j) { + for (var i = 0; i < n; ++i) { + t[1] = n * j + (1 + i); + // B = A + B[0] = A[0]; + B[1] = A[1]; + // B = A || R[i] + B[2] = R[2*i]; + B[3] = R[2*i+1]; + // B = AES(K, B) + B = unpack(aes.encrypt(pack(B))); + // A = MSB(64, B) ^ t + A = B.subarray(0, 2); + A[0] = A[0] ^ t[0]; + A[1] = A[1] ^ t[1]; + // R[i] = LSB(64, B) + R[2*i] = B[2]; + R[2*i+1] = B[3]; + } + } + return pack(A, R); +} + +function unwrap(key, data) { + var aes = new cipher["aes" + (key.length*8)](key); + var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); + var C = unpack(data); + var A = C.subarray(0, 2); + var R = C.subarray(2); + var n = C.length/2-1; + var t = new Uint32Array([0, 0]); + var B = new Uint32Array(4); + for (var j = 5; j >= 0; --j) { + for (var i = n - 1; i >= 0; --i) { + t[1] = n * j + (i + 1); + // B = A ^ t + B[0] = A[0] ^ t[0]; + B[1] = A[1] ^ t[1]; + // B = (A ^ t) || R[i] + B[2] = R[2*i]; + B[3] = R[2*i+1]; + // B = AES-1(B) + B = unpack(aes.decrypt(pack(B))); + // A = MSB(64, B) + A = B.subarray(0, 2); + // R[i] = LSB(64, B) + R[2*i] = B[2]; + R[2*i+1] = B[3]; + } + } + if (A[0] === IV[0] && A[1] === IV[1]) { + return pack(R); + } + throw new Error("Key Data Integrity failed"); +} + function createArrayBuffer(data) { if (typeof data === "string") { var length = data.length; @@ -60,84 +125,6 @@ function pack() { return new Uint8Array(buffer); } -function formatKey(key) { - var length = key.length; - var buffer = createArrayBuffer(key); - var view = new DataView(buffer); - key = new Array(length/4); - for (var i=0; i= 0; --j) { - for (var i = n - 1; i >= 0; --i) { - t[1] = n * j + (i + 1); - // B = A ^ t - B[0] = A[0] ^ t[0]; - B[1] = A[1] ^ t[1]; - // B = (A ^ t) || R[i] - B[2] = R[2*i]; - B[3] = R[2*i+1]; - // B = AES-1(B) - B = unpack(aes.decrypt(pack(B))); - // A = MSB(64, B) - A = B.subarray(0, 2); - // R[i] = LSB(64, B) - R[2*i] = B[2]; - R[2*i+1] = B[3]; - } - } - if (A[0] === IV[0] && A[1] === IV[1]) { - return pack(R); - } - throw new Error("Key Data Integrity failed"); -} - module.exports = { wrap: wrap, unwrap: unwrap diff --git a/test/crypto/aes_kw.js b/test/crypto/aes_kw.js index f7e602ba..9500711d 100644 --- a/test/crypto/aes_kw.js +++ b/test/crypto/aes_kw.js @@ -46,7 +46,7 @@ describe('AES Key Wrap and Unwrap', function () { test_vectors.forEach(function(test) { it(test[0], function(done) { - var kek = openpgp.util.hex2bin(test[1]); + var kek = openpgp.util.hex2Uint8Array(test[1]); var input = test[2].replace(/\s/g, ""); var input_bin = openpgp.util.hex2bin(input); var output = test[3].replace(/\s/g, ""); From 311d288bb7afba530c972c1732a2c6c223296198 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Sun, 26 Nov 2017 19:41:06 -0800 Subject: [PATCH 29/75] clone processing --- src/packet/public_key.js | 7 +++++-- src/packet/secret_key.js | 16 ++++++++++++++++ src/type/kdf_params.js | 4 ++++ src/type/mpi.js | 1 - src/type/oid.js | 5 +++++ 5 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/packet/public_key.js b/src/packet/public_key.js index d8f5a33e..cf2ff8ca 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -211,8 +211,11 @@ PublicKey.prototype.getBitSize = function () { * Fix custom types after cloning */ PublicKey.prototype.postCloneTypeFix = function() { - for (var i = 0; i < this.params.length; i++) { - this.params[i] = type_mpi.fromClone(this.params[i]); + const types = crypto.getPubKeyParamTypes(this.algorithm); + for (var i = 0; i < types.length; i++) { + const param = this.params[i]; + const cloneFn = crypto.getCloneFn(types[i]); + this.params[i] = cloneFn(param); } if (this.keyid) { this.keyid = type_keyid.fromClone(this.keyid); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index a666175b..7d8c130f 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -38,6 +38,7 @@ import enums from '../enums.js'; import util from '../util.js'; import crypto from '../crypto'; import type_s2k from '../type/s2k.js'; +import type_keyid from '../type/keyid.js'; /** * @constructor @@ -291,3 +292,18 @@ SecretKey.prototype.clearPrivateParams = function () { this.params = this.params.slice(0, crypto.getPubKeyParamTypes(this.algorithm).length); this.isDecrypted = false; }; + +/** + * Fix custom types after cloning + */ + SecretKey.prototype.postCloneTypeFix = function() { + const types = crypto.getPubKeyParamTypes(this.algorithm).concat(crypto.getPrivKeyParamTypes(this.algorithm)); + for (var i = 0; i < this.params.length; i++) { + const param = this.params[i]; + const cloneFn = crypto.getCloneFn(types[i]); + this.params[i] = cloneFn(param); + } + if (this.keyid) { + this.keyid = type_keyid.fromClone(this.keyid); + } +}; diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 4720c280..b5c8545b 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -59,3 +59,7 @@ KDFParams.prototype.read = function (input) { KDFParams.prototype.write = function () { return new Uint8Array([3, 1, this.hash, this.cipher]); }; + +KDFParams.fromClone = function (clone) { + return new KDFParams(clone.hash, clone.cipher); +}; \ No newline at end of file diff --git a/src/type/mpi.js b/src/type/mpi.js index 11e50504..cf5450f6 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -77,7 +77,6 @@ MPI.prototype.read = function (bytes) { // TODO: Verification of this size method! This size calculation as // specified above is not applicable in JavaScript var bytelen = Math.ceil(bits / 8); - var raw = util.Uint8Array2str(bytes.subarray(2, 2 + bytelen)); this.fromBytes(raw); diff --git a/src/type/oid.js b/src/type/oid.js index c4253889..e6cbad0b 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -67,3 +67,8 @@ OID.prototype.write = function () { return util.str2Uint8Array( String.fromCharCode(this.oid.length)+this.oid); }; + +OID.fromClone = function (clone) { + var oid = new OID(clone.oid); + return oid; +}; From 8794446a66c5bb8fdfab4bddba1d7a8a97d6e9c6 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Sun, 26 Nov 2017 22:15:03 -0800 Subject: [PATCH 30/75] change some variable names, add some curve parameters --- src/crypto/crypto.js | 26 ++++++++++++++++---- src/crypto/public_key/elliptic/curves.js | 30 +++++++++++++++++------- src/crypto/public_key/elliptic/ecdh.js | 16 ++++++------- src/crypto/public_key/elliptic/ecdsa.js | 13 +++++----- src/crypto/signature.js | 21 +++++++---------- 5 files changed, 68 insertions(+), 38 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 0c16a44d..fbd22cdd 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -45,11 +45,14 @@ function createType(data, type) { case 'oid': return new type_oid(data); case 'kdf': - return new type_kdf_params(data); + if (data) { + return new type_kdf_params(data[0], data[1]); + } + return new type_kdf_params(); case 'ecdh_symkey': return new type_ecdh_symkey(data); default: - return null; + throw new Error('Unknown type.'); } } @@ -280,12 +283,12 @@ export default { case 'ecdsa': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return constructParams([keyObject.oid, keyObject.R, keyObject.r], types); + return constructParams([keyObject.oid, keyObject.Q, keyObject.d], types); }); case 'ecdh': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return constructParams([keyObject.oid, keyObject.R, [keyObject.hash, keyObject.cipher], keyObject.r], types); + return constructParams([keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d], types); }); default: @@ -294,6 +297,21 @@ export default { }, + getCloneFn: function(type) { + switch(type) { + case 'mpi': + return type_mpi.fromClone; + case 'oid': + return type_oid.fromClone; + case 'kdf': + return type_kdf_params.fromClone; + case 'ecdh_symkey': + return type_ecdh_symkey.fromClone; + default: + throw new Error('Unknown type.'); + } + }, + /** * generate random byte prefix as string for the specified algorithm * @param {module:enums.symmetric} algo Algorithm to use (see {@link http://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2}) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 7437ee11..8c6bd280 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -36,36 +36,47 @@ import util from '../../../util.js'; const curves = { p256: { oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), - bits: 256, + curveName: 'P-256', + hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, + nist: true }, p384: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), - bits: 384, + curveName: 'P-384', + hashName: 'SHA-384', hash: enums.hash.sha384, cipher: enums.symmetric.aes192, + nist: true }, p521: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), - bits: 521, + curveName: 'P-521', + hashName: 'SHA-512', hash: enums.hash.sha512, cipher: enums.symmetric.aes256, + nist: true }, secp256k1: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), - bits: 256, + curveName: 'SECP-256K1', + hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, + nist: false } }; -function Curve(name, {oid, hash, cipher}) { +function Curve(name, {oid, hash, cipher, curveName, hashName, nist}) { this.curve = new EC(name); this.name = name; this.oid = oid; this.hash = hash; this.cipher = cipher; + this.curveName= curveName; + this.hashName = hashName; + this.nist = nist; } Curve.prototype.keyFromPrivate = function (priv) { @@ -91,7 +102,10 @@ function get(oid_or_name) { return new Curve(name, { oid: curves[name].oid, hash: curves[name].hash, - cipher: curves[name].cipher + cipher: curves[name].cipher, + curveName: curves[name].curveName, + hashName: curves[name].hashName, + nist: curves[name].nist }); } } @@ -104,8 +118,8 @@ function generate(curve) { var keyPair = curve.genKeyPair(); resolve({ oid: curve.oid, - R: new BigInteger(keyPair.getPublic()), - r: new BigInteger(keyPair.getPrivate()), + Q: new BigInteger(keyPair.getPublic()), + d: new BigInteger(keyPair.getPrivate()), hash: curve.hash, cipher: curve.cipher }); diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index d1cf9f0c..d43392f2 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -73,18 +73,18 @@ function kdf(hash_algo, X, length, param) { * @param {Enums} cipher_algo Symmetric cipher to use * @param {Enums} hash_algo Hash to use * @param {Uint8Array} m Value derived from session key (RFC 6637) - * @param {BigInteger} R Recipient public key + * @param {BigInteger} Q Recipient public key * @param {String} fingerprint Recipient fingerprint * @return {{V: BigInteger, C: Uint8Array}} Returns ephemeral key and encoded session key */ -function encrypt(oid, cipher_algo, hash_algo, m, R, fingerprint) { +function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); const curve = curves.get(oid); cipher_algo = enums.read(enums.symmetric, cipher_algo); const v = curve.genKeyPair(); - R = curve.keyFromPublic(R.toByteArray()); - const S = v.derive(R); + Q = curve.keyFromPublic(Q.toByteArray()); + const S = v.derive(Q); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); const C = aes_kw.wrap(Z, m.toBytes()); return { @@ -101,18 +101,18 @@ function encrypt(oid, cipher_algo, hash_algo, m, R, fingerprint) { * @param {Enums} hash_algo Hash algorithm to use * @param {BigInteger} V Public part of ephemeral key * @param {Uint8Array} C Encrypted and wrapped value derived from session key - * @param {BigInteger} r Recipient private key + * @param {BigInteger} d Recipient private key * @param {String} fingerprint Recipient fingerprint * @return {Uint8Array} Value derived from session */ -function decrypt(oid, cipher_algo, hash_algo, V, C, r, fingerprint) { +function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); const curve = curves.get(oid); cipher_algo = enums.read(enums.symmetric, cipher_algo); V = curve.keyFromPublic(V.toByteArray()); - r = curve.keyFromPrivate(r.toByteArray()); - const S = r.derive(V); + d = curve.keyFromPrivate(d.toByteArray()); + const S = d.derive(V); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); return new BigInteger(aes_kw.unwrap(Z, C)); } diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index b453efc8..37cba51c 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -31,14 +31,15 @@ import BigInteger from '../jsbn.js'; /** * Sign a message using the provided key * @param {String} oid Elliptic curve for the key + * @param {BigInteger} Q Public key used to sign * @param {enums.hash} hash_algo Hash algorithm used to sign * @param {Uint8Array} m Message to sign - * @param {BigInteger} w Private key used to sign +* @param {BigInteger} d Private key used to sign * @return {{r: BigInteger, s: BigInteger}} Signature of the message */ -function sign(oid, hash_algo, m, w) { +function sign(oid, hash_algo, m, d) { const curve = curves.get(oid); - const key = curve.keyFromPrivate(w.toByteArray()); + const key = curve.keyFromPrivate(d.toByteArray()); const signature = key.sign(m, hash_algo); return { r: new BigInteger(signature.r), @@ -52,11 +53,11 @@ function sign(oid, hash_algo, m, w) { * @param {enums.hash} hash_algo Hash algorithm used in the signature * @param {{r: BigInteger, s: BigInteger}} signature Signature to verify * @param {Uint8Array} m Message to verify - * @param {BigInteger} gw Public key used to verify the message + * @param {BigInteger} Q Public key used to verify the message */ -function verify(oid, hash_algo, signature, m, gw) { +function verify(oid, hash_algo, signature, m, Q) { const curve = curves.get(oid); - const key = curve.keyFromPublic(gw.toByteArray()); + const key = curve.keyFromPublic(Q.toByteArray()); return key.verify(m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo); } diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 3638ad32..18ffd225 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -63,8 +63,8 @@ export default { const r = msg_MPIs[0].toBigInteger(); const s = msg_MPIs[1].toBigInteger(); m = data; - const gw = publickey_MPIs[1].toBigInteger(); - return ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, gw); + const Q = publickey_MPIs[1].toBigInteger(); + return ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); default: throw new Error('Invalid signature algorithm.'); } @@ -74,10 +74,7 @@ export default { * Create a signature on data using the specified algorithm * @param {module:enums.hash} hash_algo hash Algorithm to use (See {@link http://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}) * @param {module:enums.publicKey} algo Asymmetric cipher algorithm to use (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) - * @param {Array} publicMPIs Public key multiprecision integers - * of the private key - * @param {Array} secretMPIs Private key multiprecision - * integers which is used to sign the data + * @param {Array} publicMPIs Public followed by Private key multiprecision algorithm-specific parameters * @param {Uint8Array} data Data to be signed * @return {Array} */ @@ -86,6 +83,7 @@ export default { data = util.Uint8Array2str(data); var m; + var d; switch (algo) { case 1: @@ -95,7 +93,7 @@ export default { case 3: // RSA Sign-Only [HAC] var rsa = new publicKey.rsa(); - var d = keyIntegers[2].toBigInteger(); + d = keyIntegers[2].toBigInteger(); var n = keyIntegers[0].toBigInteger(); m = pkcs1.emsa.encode(hash_algo, data, keyIntegers[0].byteLength()); @@ -111,7 +109,6 @@ export default { var x = keyIntegers[4].toBigInteger(); m = data; var result = dsa.sign(hash_algo, m, g, p, q, x); - return util.str2Uint8Array(result[0].toString() + result[1].toString()); case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] @@ -119,11 +116,11 @@ export default { case 19: // ECDSA - const ecdsa = publicKey.elliptic.ecdsa; - const curve = keyIntegers[0]; - const w = keyIntegers[2].toBigInteger(); + var ecdsa = publicKey.elliptic.ecdsa; + var curve = keyIntegers[0]; + d = keyIntegers[2].toBigInteger(); m = data; - const signature = ecdsa.sign(curve.oid, hash_algo, m, w); + const signature = ecdsa.sign(curve.oid, hash_algo, m, d); return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); default: From c443988ec4fc5ca43e9dfaa245f821a113658679 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Thu, 4 Jan 2018 16:31:55 +0100 Subject: [PATCH 31/75] restore package.json --- package.json | 49 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index bc7f0f1a..7b0b161d 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": "2.6.2", + "version": "2.6.1", "license": "LGPL-3.0+", "homepage": "http://openpgpjs.org/", "engines": { @@ -32,38 +32,55 @@ "test": "grunt test" }, "devDependencies": { - "asmcrypto-lite": "^1.0.0", "babel-core": "^6.26.0", - "babel-preset-es2015": "^6.3.13", + "babel-plugin-syntax-async-functions": "^6.13.0", + "babel-plugin-transform-async-to-generator": "^6.24.1", + "babel-plugin-transform-regenerator": "^6.26.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-polyfill": "^6.26.0", + "babel-preset-env": "^1.6.1", "babelify": "^8.0.0", "browserify-derequire": "^0.9.4", "chai": "~4.1.2", - "es6-promise": "^4.1.1", + "chai-as-promised": "^7.1.1", + "eslint": "^4.16.0", + "eslint-config-airbnb": "^16.1.0", + "eslint-config-airbnb-base": "^12.1.0", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-jsx-a11y": "^6.0.3", + "eslint-plugin-react": "^7.6.1", "grunt": "~1.0.1", "grunt-browserify": "~5.2.0", "grunt-contrib-clean": "~1.1.0", "grunt-contrib-connect": "~1.0.2", "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-jshint": "~1.1.0", "grunt-contrib-uglify": "~3.2.1", "grunt-contrib-watch": "^1.0.0", - "grunt-jsbeautifier": "~0.2.10", - "grunt-jscs": "^3.0.1", - "grunt-jsdoc": "~2.2.0", - "grunt-mocha-istanbul": "^5.0.1", - "grunt-mocha-test": "~0.13.3", + "grunt-jsbeautifier": "^0.2.13", + "grunt-jsdoc": "^2.2.1", + "grunt-keepalive": "^1.0.0", + "grunt-mocha-istanbul": "^5.0.2", + "grunt-mocha-test": "^0.13.3", "grunt-saucelabs": "9.0.0", "grunt-text-replace": "~0.4.0", - "istanbul": "~0.4.1", - "mocha": "~4.0.1", - "rusha": "^0.8.3", - "sinon": "^1.17.3", + "gruntify-eslint": "^4.0.0", + "istanbul": "^0.4.5", + "mocha": "^4.0.1", + "sinon": "^4.2.2", "whatwg-fetch": "~2.0.3", "zlibjs": "~0.3.1" }, "dependencies": { - "node-fetch": "^1.3.3", - "node-localstorage": "~1.3.0" + "bn.js": "^4.11.8", + "buffer": "^5.0.8", + "asn1.js": "^5.0.0", + "asmcrypto-lite": "git+https://github.com/openpgpjs/asmcrypto-lite.git", + "elliptic": "git+https://github.com/openpgpjs/elliptic.git", + "es6-promise": "^4.2.4", + "jwk-to-pem": "^1.2.6", + "node-fetch": "^1.7.3", + "node-localstorage": "~1.3.0", + "rusha": "^0.8.12" }, "repository": { "type": "git", From 6886cd648aecedd0e1d5689f8f521a6595149f65 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 07:58:15 -0800 Subject: [PATCH 32/75] elliptic/curves.js uses native code for genkey and sign/verify; sign/verify use async/await --- Gruntfile.js | 30 ++++- src/crypto/public_key/elliptic/curves.js | 113 ++++++++++++---- src/crypto/public_key/elliptic/ecdsa.js | 162 ++++++++++++++++++++++- src/openpgp.js | 21 +-- 4 files changed, 278 insertions(+), 48 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 84481853..10af27be 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -34,10 +34,6 @@ module.exports = function(grunt) { browser_capabilities = JSON.parse(process.env.SELENIUM_BROWSER_CAPABILITIES); } - var getSauceKey = function getSaucekey () { - return '60ffb656-2346-4b77-81f3-bc435ff4c103'; - }; - // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), @@ -53,8 +49,18 @@ module.exports = function(grunt) { external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ], transform: [ ["babelify", { + plugins: ["transform-async-to-generator", + "syntax-async-functions", + "transform-regenerator", + "transform-runtime"], ignore: ['*.min.js'], - presets: ["es2015"] + presets: [ + ["babel-preset-env", { + "targets": { + "node": "current" + } + }] + ] }] ], plugin: [ 'browserify-derequire' ] @@ -72,8 +78,18 @@ module.exports = function(grunt) { external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ], transform: [ ["babelify", { + plugins: ["transform-async-to-generator", + "syntax-async-functions", + "transform-regenerator", + "transform-runtime"], ignore: ['*.min.js'], - presets: ["es2015"] + presets: [ + ["babel-preset-env", { + "targets": { + "node": "current" + } + }] + ] }] ], plugin: [ 'browserify-derequire' ] @@ -226,7 +242,7 @@ module.exports = function(grunt) { all: { options: { username: 'openpgpjs', - key: getSauceKey, + key: '60ffb656-2346-4b77-81f3-bc435ff4c103', urls: ['http://127.0.0.1:3000/test/unittests.html'], build: process.env.TRAVIS_BUILD_ID, testname: 'Sauce Unit Test for openpgpjs', diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 8c6bd280..b8dae437 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -30,53 +30,76 @@ import {ec as EC} from 'elliptic'; import {KeyPair} from './key.js'; import BigInteger from '../jsbn.js'; +import config from '../../../config'; import enums from '../../../enums.js'; import util from '../../../util.js'; +const webCrypto = util.getWebCrypto(); +const nodeCrypto = util.getNodeCrypto(); + +var webCurves = [], nodeCurves = []; +if (webCrypto && config.use_native) { + // see https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API/Supported_algorithms + webCurves = ['P-256', 'P-384', 'P-521']; +} else if (nodeCrypto && config.use_native) { + // FIXME make sure the name translations are correct + nodeCurves = nodeCrypto.getCurves(); +} + const curves = { p256: { oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), - curveName: 'P-256', + namedCurve: 'P-256', + opensslCurve: 'prime256v1', hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - nist: true + node: nodeCurves.includes('prime256v1'), + web: webCurves.includes('P-256') }, p384: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), - curveName: 'P-384', + namedCurve: 'P-384', + opensslCurve: 'secp384r1', // FIXME hashName: 'SHA-384', hash: enums.hash.sha384, cipher: enums.symmetric.aes192, - nist: true + node: nodeCurves.includes('secp384r1'), // FIXME + web: webCurves.includes('P-384') }, p521: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), - curveName: 'P-521', + namedCurve: 'P-521', + opensslCurve: 'secp521r1', // FIXME hashName: 'SHA-512', hash: enums.hash.sha512, cipher: enums.symmetric.aes256, - nist: true + node: nodeCurves.includes('secp521r1'), // FIXME + web: webCurves.includes('P-521') }, secp256k1: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), - curveName: 'SECP-256K1', + namedCurve: 'SECP-256K1', + opensslCurve: 'secp256k1', hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - nist: false + node: nodeCurves.includes('secp256k1'), + web: false } }; -function Curve(name, {oid, hash, cipher, curveName, hashName, nist}) { +function Curve(name, {oid, hash, cipher, namedCurve, opensslCurve, hashName, node, web}) { this.curve = new EC(name); this.name = name; this.oid = oid; this.hash = hash; this.cipher = cipher; - this.curveName= curveName; + this.namedCurve= namedCurve; + this.opensslCurve = opensslCurve; this.hashName = hashName; - this.nist = nist; + this.node = node; + this.web = web; } Curve.prototype.keyFromPrivate = function (priv) { @@ -88,25 +111,26 @@ Curve.prototype.keyFromPublic = function (pub) { }; Curve.prototype.genKeyPair = function () { - var r = this.curve.genKeyPair(); - return new KeyPair(this.curve, { - pub: r.getPublic().encode(), - priv: r.getPrivate().toArray() - }); + var keyPair; + if (webCrypto && config.use_native && this.web) { + keyPair = webGenKeyPair(this.namedCurve); + } else if (nodeCrypto && config.use_native && this.node) { + keyPair = nodeGenKeyPair(this.opensslCurve); + } else { + var r = this.curve.genKeyPair(); + keyPair = { + pub: r.getPublic().encode(), + priv: r.getPrivate().toArray() + }; + } + return new KeyPair(this.curve, keyPair); }; function get(oid_or_name) { for (var name in curves) { if (curves[name].oid === oid_or_name || name === oid_or_name) { - return new Curve(name, { - oid: curves[name].oid, - hash: curves[name].hash, - cipher: curves[name].cipher, - curveName: curves[name].curveName, - hashName: curves[name].hashName, - nist: curves[name].nist - }); + return new Curve(name, curves[name]); } } throw new Error('Not valid curve'); @@ -131,3 +155,44 @@ module.exports = { generate: generate, get: get }; + + +////////////////////////// +// // +// Helper functions // +// // +////////////////////////// + + +function webGenKeyPair(namedCurve) { + return webCrypto.generateKey( + { + name: "ECDSA", +// FIXME +// name: "ECDH", + namedCurve: namedCurve, // "P-256", "P-384", or "P-521" + }, +// FIXME + false, // whether the key is extractable (i.e. can be used in exportKey) + ["sign", "verify"] // can be any combination of "sign" and "verify" +// FIXME +// ["deriveKey", "deriveBits"] // can be any combination of "deriveKey" and "deriveBits" + ).then(function(key){ + return { + pub: key.publicKey.encode(), // FIXME encoding + priv: key.privateKey.toArray() // FIXME encoding + }; + }).catch(function(err){ + throw new Error(err); + }); +} + +function nodeGenKeyPair(opensslCurve) { + // TODO turn this into a promise + var ecc = nodeCrypto.createECDH(opensslCurve); + ecc.generateKeys(); + return { + pub: ecc.getPrivateKey().toJSON().data, + priv: ecc.getPublicKey().toJSON().data + }; +} diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 37cba51c..82df266f 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -25,22 +25,44 @@ 'use strict'; +import asn1 from 'asn1'; +import jwk2pem from 'jwk-to-pem'; + import curves from './curves.js'; import BigInteger from '../jsbn.js'; +import config from '../../../config'; +import enums from '../../../enums.js'; +import util from '../../../util.js'; + +const webCrypto = util.getWebCrypto(); +const nodeCrypto = util.getNodeCrypto(); + +var ECDSASignature = asn1.define('ecdsa-sig', function() { + return this.seq().obj( + this.key('r').int(), // FIXME int or BN? + this.key('s').int() // FIXME int or BN? + ); +}); /** * Sign a message using the provided key * @param {String} oid Elliptic curve for the key - * @param {BigInteger} Q Public key used to sign * @param {enums.hash} hash_algo Hash algorithm used to sign * @param {Uint8Array} m Message to sign -* @param {BigInteger} d Private key used to sign + * @param {BigInteger} d Private key used to sign * @return {{r: BigInteger, s: BigInteger}} Signature of the message */ function sign(oid, hash_algo, m, d) { + var signature; const curve = curves.get(oid); - const key = curve.keyFromPrivate(d.toByteArray()); - const signature = key.sign(m, hash_algo); + if (webCrypto && config.use_native && curve.web) { + signature = webSign(curve, hash_algo, m, d); + } else if (nodeCrypto && config.use_native && curve.node) { + signature = nodeSign(curve, hash_algo, m, d); + } else { + const key = curve.keyFromPrivate(d.toByteArray()); + signature = key.sign(m, hash_algo); + } return { r: new BigInteger(signature.r), s: new BigInteger(signature.s) @@ -53,15 +75,141 @@ function sign(oid, hash_algo, m, d) { * @param {enums.hash} hash_algo Hash algorithm used in the signature * @param {{r: BigInteger, s: BigInteger}} signature Signature to verify * @param {Uint8Array} m Message to verify - * @param {BigInteger} Q Public key used to verify the message + * @param {BigInteger} Q Public key used to verify the message + * @return {Boolean} */ function verify(oid, hash_algo, signature, m, Q) { const curve = curves.get(oid); - const key = curve.keyFromPublic(Q.toByteArray()); - return key.verify(m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo); + if (webCrypto && config.use_native && curve.web) { + return webVerify(curve, hash_algo, signature, m, Q); + } else if (nodeCrypto && config.use_native && curve.node) { + return nodeVerify(curve, hash_algo, signature, m, Q); + } else { + const key = curve.keyFromPublic(Q.toByteArray()); + return key.verify(m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo); + } } module.exports = { sign: sign, verify: verify }; + + +////////////////////////// +// // +// Helper functions // +// // +////////////////////////// + + +async function webSign(curve, hash_algo, m, d) { + const publicKey = curve.keyFromPrivate(d).getPublic(); + const privateKey = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": curve.namedCurve, + "x": publicKey.getX().toBuffer().base64Slice(), + "y": publicKey.getY().toBuffer().base64Slice(), + "d": d.toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Private Key" + }, + { + "name": "ECDSA", + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.hash, hash_algo) } + }, + false, + ["sign"] + ); + + try { + return await webCrypto.sign( + { + "name": 'ECDSA', + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.hash, hash_algo) } + }, + privateKey, + m + ); + } catch(err) { + throw new Error(err); + } +} + +async function webVerify(curve, hash_algo, signature, m, Q) { + const publicKey = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": curve.namedCurve, + "x": Q.getX().toBuffer().base64Slice(), + "y": Q.getY().toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Public Key" + }, + { + "name": "ECDSA", + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.hash, hash_algo) } + }, + false, + ["verify"] + ); + + try { + return await webCrypto.verify( + { + "name": 'ECDSA', + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.hash, hash_algo) } + }, + publicKey, + signature, + m + ); + } catch(err) { + throw new Error(err); + } +} + + +async function nodeSign(curve, hash_algo, m, d) { + const publicKey = curve.keyFromPrivate(d).getPublic(); + const privateKey = jwk2pem( + {"kty": "EC", + "crv": curve.namedCurve, + "x": publicKey.getX().toBuffer().base64Slice(), + "y": publicKey.getY().toBuffer().base64Slice(), + "d": d.toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Private Key"}, + {private: true} + ); + + const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo)); + sign.write(m); + sign.end(); + const signature = await sign.sign(privateKey); + return ECDSASignature.decode(signature, 'der'); +} + +async function nodeVerify(curve, hash_algo, signature, m, Q) { + const publicKey = jwk2pem( + {"kty": "EC", + "crv": curve.namedCurve, + "x": Q.getX().toBuffer().base64Slice(), + "y": Q.getY().toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Public Key"}, + {private: false} + ); + + const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); + verify.write(m); + verify.end(); + return await verify.verify(publicKey, signature); +} diff --git a/src/openpgp.js b/src/openpgp.js index 3bad19b0..3d56d87c 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -300,9 +300,9 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { } var result = {}; - return execute(() => { + const promise = async function(){ var message; - + if (util.isString(data)) { message = new cleartext.CleartextMessage(data); } else { @@ -310,14 +310,14 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { } if (detached) { - var signature = message.signDetached(privateKeys); + var signature = await message.signDetached(privateKeys); if (armor) { result.signature = signature.armor(); } else { result.signature = signature; } } else { - message = message.sign(privateKeys); + message = await message.sign(privateKeys); if (armor) { result.data = message.armor(); } else { @@ -326,8 +326,8 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { } return result; - - }, 'Error signing cleartext message'); + } + return promise().catch(onError.bind(null, 'Error signing cleartext message')); } /** @@ -348,7 +348,7 @@ export function verify({ message, publicKeys, signature=null }) { } var result = {}; - return execute(() => { + const promise = async function(){ if (cleartext.CleartextMessage.prototype.isPrototypeOf(message)) { result.data = message.getText(); } else { @@ -356,13 +356,14 @@ export function verify({ message, publicKeys, signature=null }) { } if (signature) { //detached signature - result.signatures = message.verifyDetached(signature, publicKeys); + result.signatures = await message.verifyDetached(signature, publicKeys); } else { - result.signatures = message.verify(publicKeys); + result.signatures = await message.verify(publicKeys); } return result; - }, 'Error verifying cleartext signed message'); + } + return promise().catch(onError.bind(null, 'Error verifying cleartext signed message')); } From 370a15e2a176fbe40d70d44a55c43ed9d48243ec Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 07:59:21 -0800 Subject: [PATCH 33/75] Remove jshint/jscs and fix babelify (cherry picked from commit e4b810fe412bd5e383507668ef2d60320c31b1ca) --- Gruntfile.js | 16 ++++------------ src/crypto/public_key/elliptic/ecdsa.js | 2 +- src/index.js | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 10af27be..834b5d2a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -46,7 +46,7 @@ module.exports = function(grunt) { browserifyOptions: { standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -55,11 +55,7 @@ module.exports = function(grunt) { "transform-runtime"], ignore: ['*.min.js'], presets: [ - ["babel-preset-env", { - "targets": { - "node": "current" - } - }] + "es2015" ] }] ], @@ -84,11 +80,7 @@ module.exports = function(grunt) { "transform-runtime"], ignore: ['*.min.js'], presets: [ - ["babel-preset-env", { - "targets": { - "node": "current" - } - }] + "es2015" ] }] ], @@ -321,7 +313,7 @@ module.exports = function(grunt) { grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']); grunt.registerTask('documentation', ['jsdoc']); // Test/Dev tasks - grunt.registerTask('test', ['jshint', 'jscs', 'mochaTest']); + grunt.registerTask('test', [ 'mochaTest']); grunt.registerTask('coverage', ['mocha_istanbul:coverage']); grunt.registerTask('saucelabs', ['default', 'copy:browsertest', 'connect:test', 'saucelabs-mocha']); diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 82df266f..c4b230c9 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -25,7 +25,7 @@ 'use strict'; -import asn1 from 'asn1'; +import asn1 from 'asn1.js'; import jwk2pem from 'jwk-to-pem'; import curves from './curves.js'; diff --git a/src/index.js b/src/index.js index 46bec340..b5ec138e 100644 --- a/src/index.js +++ b/src/index.js @@ -17,7 +17,7 @@ export default openpgp; * import { encryptMessage } from 'openpgp.js' * encryptMessage(keys, text) */ -export * from './openpgp'; +// export * from './openpgp'; /** * @see module:key From 11a2d0070bbd89f47dbc629de884ca3f7aa021dc Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:10:12 -0800 Subject: [PATCH 34/75] ESLint is happy! --- .eslintrc.js | 366 ++++++++++++++++++ .jscsrc | 4 - .jshintrc | 36 -- Gruntfile.js | 27 +- src/crypto/aes_kw.js | 4 +- src/crypto/cipher/aes.js | 4 +- src/crypto/cipher/blowfish.js | 6 +- src/crypto/cipher/cast5.js | 12 +- src/crypto/cipher/des.js | 17 +- src/crypto/crypto.js | 8 +- src/crypto/gcm.js | 7 +- src/crypto/hash/index.js | 4 +- src/crypto/hash/ripe-md.js | 1 - src/crypto/hash/sha.js | 2 +- src/crypto/index.js | 2 +- src/crypto/pkcs1.js | 15 +- src/crypto/public_key/elliptic/curves.js | 6 +- src/crypto/public_key/elliptic/ecdsa.js | 3 +- src/crypto/public_key/jsbn.js | 2 +- src/crypto/random.js | 1 + src/encoding/armor.js | 1 - src/encoding/base64.js | 4 +- src/index.js | 4 +- src/key.js | 4 +- src/keyring/index.js | 2 +- src/message.js | 8 +- src/openpgp.js | 8 +- src/packet/compressed.js | 1 - src/packet/packetlist.js | 11 +- src/packet/secret_key.js | 4 +- src/packet/signature.js | 43 +- src/packet/sym_encrypted_aead_protected.js | 4 +- .../sym_encrypted_integrity_protected.js | 5 +- src/packet/sym_encrypted_session_key.js | 3 +- src/packet/symmetrically_encrypted.js | 2 +- src/type/ecdh_symkey.js | 4 +- src/worker/worker.js | 4 +- 37 files changed, 476 insertions(+), 163 deletions(-) create mode 100644 .eslintrc.js delete mode 100644 .jscsrc delete mode 100644 .jshintrc diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..2992d31a --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,366 @@ +module.exports = { + "extends": "airbnb-base", + "parserOptions": { "sourceType": "module" }, + + "env": { + "browser": true, + "es6": true, + "node": true + }, + + "globals": { // TODO are all these necessary? + "console": true, + "Promise": true, + "importScripts": true, + "process": true, + "Event": true, + "describe": true, + "it": true, + "sinon": true, + "mocha": true, + "before": true, + "beforeEach": true, + "after": true, + "afterEach": true, + "escape": true, + "unescape": true, + "postMessage": true, + "resolves": true, + "rejects": true + }, + + "rules": { + // Auto generated rules: + "accessor-pairs": "error", + "array-bracket-newline": "error", + "array-bracket-spacing": [ + "error", + "never" + ], + "array-callback-return": "error", + "array-element-newline": "off", + "arrow-body-style": "off", + "arrow-parens": [ + "error", + "as-needed" + ], + "arrow-spacing": [ + "error", + { + "after": true, + "before": true + } + ], + "block-scoped-var": "off", + "block-spacing": [ + "error", + "always" + ], + "brace-style": "off", + "callback-return": "error", + "camelcase": [ + "error", + { + "properties": "never" + } + ], + "capitalized-comments": "off", + "class-methods-use-this": "error", + "comma-dangle": [ "error", "never" ], + "comma-spacing": "off", + "comma-style": [ + "error", + "last" + ], + "complexity": "off", + "computed-property-spacing": [ + "error", + "never" + ], + "consistent-return": "off", + "consistent-this": "error", + "curly": "error", + "default-case": "off", + "dot-location": "error", + "dot-notation": [ + "error", + { + "allowKeywords": true + } + ], + "eol-last": "off", + "eqeqeq": "error", + "for-direction": "error", + "func-call-spacing": "error", + "func-name-matching": "error", + "func-names": [ + "error", + "never" + ], + "func-style": "off", + "function-paren-newline": "off", + "generator-star-spacing": "error", + "getter-return": "error", + "global-require": "off", + "guard-for-in": "off", + "handle-callback-err": "error", + "id-blacklist": "error", + "id-length": "off", + "id-match": "error", + "implicit-arrow-linebreak": [ + "error", + "beside" + ], + "init-declarations": "off", + "jsx-quotes": "error", + "key-spacing": "off", + "keyword-spacing": "off", + "line-comment-position": "off", + "linebreak-style": [ + "error", + "unix" + ], + "lines-around-comment": "off", + "lines-around-directive": "error", + "lines-between-class-members": "error", + "max-depth": "off", + "max-len": "off", + "max-lines": "off", + "max-nested-callbacks": "error", + "max-params": "off", + "max-statements": "off", + "max-statements-per-line": "off", + "multiline-comment-style": "off", + "multiline-ternary": "off", + "new-parens": "error", + "newline-after-var": "off", + "newline-before-return": "off", + "newline-per-chained-call": "off", + "no-alert": "error", + "no-array-constructor": "error", + "no-await-in-loop": "error", + "no-bitwise": "off", + "no-buffer-constructor": "error", + "no-caller": "error", + "no-catch-shadow": "error", + "no-confusing-arrow": "error", + "no-continue": "off", + "no-div-regex": "error", + "no-duplicate-imports": "error", + "no-else-return": "off", + "no-empty": [ + "error", + { + "allowEmptyCatch": true + } + ], + "no-empty-function": "off", + "no-eq-null": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-label": "error", + "no-extra-parens": "off", + "no-floating-decimal": "error", + "no-implicit-globals": "error", + "no-implied-eval": "error", + "no-inline-comments": "off", + "no-inner-declarations": [ + "error", + "functions" + ], + "no-invalid-this": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-lonely-if": "error", + "no-loop-func": "error", + "no-magic-numbers": "off", + "no-mixed-operators": "off", + "no-mixed-requires": "error", + "no-multi-assign": "error", + "no-multi-spaces": [ + "error", + { + "ignoreEOLComments": true + } + ], + "no-multi-str": "error", + "no-multiple-empty-lines": "error", + "no-native-reassign": "error", + "no-negated-condition": "off", + "no-negated-in-lhs": "error", + "no-nested-ternary": "off", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-require": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-param-reassign": "off", + "no-path-concat": "error", + "no-plusplus": "off", + "no-process-env": "error", + "no-process-exit": "error", + "no-proto": "error", + "no-prototype-builtins": "off", + "no-restricted-globals": "error", + "no-restricted-imports": "error", + "no-restricted-modules": "error", + "no-restricted-properties": "error", + "no-restricted-syntax": "error", + "no-return-assign": "error", + "no-return-await": "error", + "no-script-url": "error", + "no-self-compare": "error", + "no-shadow": "off", + "no-shadow-restricted-names": "error", + "no-spaced-func": "error", + "no-sync": "error", + "no-tabs": "error", + "no-template-curly-in-string": "error", + "no-ternary": "off", + "no-throw-literal": "error", + "no-undef-init": "error", + "no-undefined": "off", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": [ + "error", + { + "defaultAssignment": true + } + ], + "no-unused-expressions": "error", + "no-use-before-define": "off", + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-concat": "error", + "no-useless-constructor": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-var": "off", + "no-void": "error", + "no-warning-comments": "off", + "no-whitespace-before-property": "error", + "no-with": "error", + "nonblock-statement-body-position": "error", + "object-curly-newline": "off", + "object-curly-spacing": "off", + "object-property-newline": [ + "error", + { + "allowMultiplePropertiesPerLine": true + } + ], + "object-shorthand": "off", + "one-var": "off", + "one-var-declaration-per-line": [ + "error", + "initializations" + ], + "operator-assignment": "off", + "operator-linebreak": [ + "error", + "after" + ], + "padded-blocks": "off", + "padding-line-between-statements": "error", + "prefer-arrow-callback": "off", + "prefer-const": "off", + "prefer-destructuring": "off", + "prefer-numeric-literals": "error", + "prefer-promise-reject-errors": "error", + "prefer-reflect": "off", + "prefer-rest-params": "off", + "prefer-spread": "off", + "prefer-template": "off", + "quote-props": "off", + "quotes": "off", + "radix": [ + "error", + "as-needed" + ], + "require-await": "error", + "require-jsdoc": "off", + "rest-spread-spacing": "error", + "semi": "off", + "semi-spacing": [ + "error", + { + "after": true, + "before": false + } + ], + "semi-style": [ + "error", + "last" + ], + "sort-imports": "off", + "sort-keys": "off", + "sort-vars": "off", + "space-before-blocks": "off", + "space-before-function-paren": "off", + "space-in-parens": [ + "error", + "never" + ], + "space-infix-ops": "off", + "space-unary-ops": "error", + "spaced-comment": "off", + "strict": "off", + "switch-colon-spacing": "error", + "symbol-description": "error", + "template-curly-spacing": "error", + "template-tag-spacing": "error", + "unicode-bom": [ + "error", + "never" + ], + "valid-jsdoc": "off", + "vars-on-top": "off", + "wrap-iife": "error", + "wrap-regex": "off", + "yield-star-spacing": "error", + "yoda": [ + "error", + "never" + ], + + // Custom silencers: + "camelcase": 0, + "no-debugger": 0, + "no-multi-assign": 0, + "no-underscore-dangle": 0, + "one-var-declaration-per-line": 0, + + // Custom errors: + "no-undef": 2, + "no-trailing-spaces": 2, + "no-mixed-operators": [ 2, {"groups": [["&", "|", "^", "~", "<<", ">>", ">>>"], ["&&", "||"]]}], + "no-use-before-define": [ 2, { "functions": false, "classes": true, "variables": false }], + "no-unused-expressions": [ 2, { "allowShortCircuit": true } ], + + // Custom warnings: + "no-console": 0, + "no-unused-vars": 0, + "indent": [ 0, 2, { "SwitchCase": 1 } ], + + // Consider fixing these: + "new-cap": [ 0, { "properties": false, "capIsNewExceptionPattern": "^type_.*" }], + "no-lonely-if": 0, + "no-fallthrough": 0, + "consistent-this": 0, + "no-invalid-this": 0, + "callback-return": 0, + "no-useless-call": 0, // only occurs for workers + "import/extensions": 0, + "no-useless-escape": 0, + "no-case-declarations": 0, + "no-restricted-syntax": 0, + "no-array-constructor": 0, + "no-constant-condition": 0, + "no-buffer-constructor": 0, // deprecated + "no-restricted-properties": 0 // Math.pow + } +}; diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 7d15b2b9..00000000 --- a/.jscsrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "disallowTrailingWhitespace": true, - "validateIndentation": 2 -} \ No newline at end of file diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index 5091d409..00000000 --- a/.jshintrc +++ /dev/null @@ -1,36 +0,0 @@ -{ - "node": true, - "browser": true, - "nonew": true, - "curly": true, - "eqeqeq": true, - "immed": true, - "regexp": true, - "evil": true, - "eqnull": true, - "expr": true, - "undef": true, - "unused": true, - "esnext": true, - - "globals": { - "console": true, - "Promise": true, - "importScripts": true, - "process": true, - "Event": true, - "describe": true, - "it": true, - "sinon": true, - "mocha": true, - "before": true, - "beforeEach": true, - "after": true, - "afterEach": true, - "escape": true, - "unescape": true, - "postMessage": true, - "resolves": true, - "rejects": true - } -} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 834b5d2a..b3254c2c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,7 +20,7 @@ module.exports = function(grunt) { 'src/encoding/**/*.js', 'src/hkp/**/*.js', 'src/keyring/**/*.js', - 'src/packet/**/*.jss', + 'src/packet/**/*.js', 'src/type/**/*.js', 'src/worker/**/*.js', 'src/*.js', @@ -71,7 +71,7 @@ module.exports = function(grunt) { debug: true, standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -158,19 +158,9 @@ module.exports = function(grunt) { wrap_line_length: 120 } }, - jshint: { - src: lintFiles, - build: ['Gruntfile.js', '*.json'], - options: { - jshintrc: '.jshintrc' - } - }, - jscs: { - src: lintFiles, - build: ['Gruntfile.js'], - options: { - config: ".jscsrc" - } + eslint: { + target: lintFiles, + options: { configFile: '.eslintrc.js' } }, jsdoc: { dist: { @@ -264,9 +254,8 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-text-replace'); grunt.loadNpmTasks('grunt-jsbeautifier'); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-jscs'); grunt.loadNpmTasks('grunt-jsdoc'); + grunt.loadNpmTasks('gruntify-eslint'); grunt.loadNpmTasks('grunt-mocha-istanbul'); grunt.loadNpmTasks('grunt-mocha-test'); grunt.loadNpmTasks('grunt-contrib-copy'); @@ -310,10 +299,10 @@ module.exports = function(grunt) { // Build tasks grunt.registerTask('version', ['replace:openpgp', 'replace:openpgp_debug']); grunt.registerTask('replace_min', ['replace:openpgp_min', 'replace:worker_min']); - grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']); + grunt.registerTask('default',['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']); grunt.registerTask('documentation', ['jsdoc']); // Test/Dev tasks - grunt.registerTask('test', [ 'mochaTest']); + grunt.registerTask('test', [ 'eslint', 'mochaTest']); grunt.registerTask('coverage', ['mocha_istanbul:coverage']); grunt.registerTask('saucelabs', ['default', 'copy:browsertest', 'connect:test', 'saucelabs-mocha']); diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index b0ea1d1d..e70aafd5 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -20,7 +20,7 @@ import cipher from './cipher'; function wrap(key, data) { - var aes = new cipher["aes" + (key.length*8)](key); + var aes = new cipher["aes" + (key.length*8)](key); var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); var P = unpack(data); var A = IV; @@ -52,7 +52,7 @@ function wrap(key, data) { } function unwrap(key, data) { - var aes = new cipher["aes" + (key.length*8)](key); + var aes = new cipher["aes" + (key.length*8)](key); var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]); var C = unpack(data); var A = C.subarray(0, 2); diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js index d3ea5938..db6b32bb 100644 --- a/src/crypto/cipher/aes.js +++ b/src/crypto/cipher/aes.js @@ -736,7 +736,7 @@ var U2 = new Uint32Array([ 0xdc31d7ca,0xd138d9c1,0xc623cbdc,0xcb2ac5d7, 0xe815efe6,0xe51ce1ed,0xf207f3f0,0xff0efdfb, 0xb479a792,0xb970a999,0xae6bbb84,0xa362b58f, - 0x805d9fbe,0x8d5491b5,0x9a4f83a8,0x97468da3, + 0x805d9fbe,0x8d5491b5,0x9a4f83a8,0x97468da3 ]); var U3 = new Uint32Array([ @@ -1138,4 +1138,4 @@ export default { 128: makeClass(128), 192: makeClass(192), 256: makeClass(256) -}; \ No newline at end of file +}; diff --git a/src/crypto/cipher/blowfish.js b/src/crypto/cipher/blowfish.js index e910b050..279e4497 100644 --- a/src/crypto/cipher/blowfish.js +++ b/src/crypto/cipher/blowfish.js @@ -312,8 +312,8 @@ Blowfish.prototype.encrypt_block = function(vector) { var ret = []; for (ii = 0; ii < this.BLOCKSIZE / 2; ++ii) { - ret[ii + 0] = (vals[0] >>> (24 - 8 * (ii)) & 0x00FF); - ret[ii + off] = (vals[1] >>> (24 - 8 * (ii)) & 0x00FF); + ret[ii + 0] = ((vals[0] >>> (24 - 8 * (ii))) & 0x00FF); + ret[ii + off] = ((vals[1] >>> (24 - 8 * (ii))) & 0x00FF); // vals[ 0 ] = ( vals[ 0 ] >>> 8 ); // vals[ 1 ] = ( vals[ 1 ] >>> 8 ); } @@ -404,4 +404,4 @@ export default function BF(key) { }; } BF.keySize = BF.prototype.keySize = 16; -BF.blockSize = BF.prototype.blockSize = 16; \ No newline at end of file +BF.blockSize = BF.prototype.blockSize = 16; diff --git a/src/crypto/cipher/cast5.js b/src/crypto/cipher/cast5.js index cf2ef6f1..3a4a2d0c 100644 --- a/src/crypto/cipher/cast5.js +++ b/src/crypto/cipher/cast5.js @@ -51,8 +51,8 @@ function OpenpgpSymencCast5() { var dst = new Array(src.length); for (var i = 0; i < src.length; i += 8) { - var l = src[i] << 24 | src[i + 1] << 16 | src[i + 2] << 8 | src[i + 3]; - var r = src[i + 4] << 24 | src[i + 5] << 16 | src[i + 6] << 8 | src[i + 7]; + var l = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3]; + var r = (src[i + 4] << 24) | (src[i + 5] << 16) | (src[i + 6] << 8) | src[i + 7]; var t; t = r; @@ -124,8 +124,8 @@ function OpenpgpSymencCast5() { var dst = new Array(src.length); for (var i = 0; i < src.length; i += 8) { - var l = src[i] << 24 | src[i + 1] << 16 | src[i + 2] << 8 | src[i + 3]; - var r = src[i + 4] << 24 | src[i + 5] << 16 | src[i + 6] << 8 | src[i + 7]; + var l = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3]; + var r = (src[i + 4] << 24) | (src[i + 5] << 16) | (src[i + 6] << 8) | src[i + 7]; var t; t = r; @@ -256,7 +256,7 @@ function OpenpgpSymencCast5() { for (i = 0; i < 4; i++) { j = i * 4; - t[i] = inn[j] << 24 | inn[j + 1] << 16 | inn[j + 2] << 8 | inn[j + 3]; + t[i] = (inn[j] << 24) | (inn[j + 1] << 16) | (inn[j + 2] << 8) | inn[j + 3]; } var x = [6, 7, 4, 5]; @@ -602,4 +602,4 @@ export default function Cast5(key) { } Cast5.blockSize = Cast5.prototype.blockSize = 8; -Cast5.keySize = Cast5.prototype.keySize = 16; \ No newline at end of file +Cast5.keySize = Cast5.prototype.keySize = 16; diff --git a/src/crypto/cipher/des.js b/src/crypto/cipher/des.js index 81c72a04..321f3bce 100644 --- a/src/crypto/cipher/des.js +++ b/src/crypto/cipher/des.js @@ -35,13 +35,13 @@ function des(keys, message, encrypt, mode, iv, padding) { 0x1010000, 0x1010400, 0x1000000, 0x1000000, 0x400, 0x1010004, 0x10000, 0x10400, 0x1000004, 0x400, 0x4, 0x1000404, 0x10404, 0x1010404, 0x10004, 0x1010000, 0x1000404, 0x1000004, 0x404, 0x10404, 0x1010400, 0x404, 0x1000400, 0x1000400, 0, 0x10004, 0x10400, 0, 0x1010004); - var spfunction2 = new Array(-0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0, - - 0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x80000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020, - - 0x7fff7fe0, 0, -0x80000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000, - - 0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000, - - 0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x80000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0, - 0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x80000000, -0x7fefffe0, - - 0x7fef7fe0, 0x108000); + var spfunction2 = new Array(-0x7fef7fe0, -0x7fff8000, 0x8000, 0x108020, 0x100000, 0x20, -0x7fefffe0, -0x7fff7fe0, + -0x7fffffe0, -0x7fef7fe0, -0x7fef8000, -0x80000000, -0x7fff8000, 0x100000, 0x20, -0x7fefffe0, 0x108000, 0x100020, + -0x7fff7fe0, 0, -0x80000000, 0x8000, 0x108020, -0x7ff00000, 0x100020, -0x7fffffe0, 0, 0x108000, 0x8020, -0x7fef8000, + -0x7ff00000, 0x8020, 0, 0x108020, -0x7fefffe0, 0x100000, -0x7fff7fe0, -0x7ff00000, -0x7fef8000, 0x8000, -0x7ff00000, + -0x7fff8000, 0x20, -0x7fef7fe0, 0x108020, 0x20, 0x8000, -0x80000000, 0x8020, -0x7fef8000, 0x100000, -0x7fffffe0, + 0x100020, -0x7fff7fe0, -0x7fffffe0, 0x100020, 0x108000, 0, -0x7fff8000, 0x8020, -0x80000000, -0x7fefffe0, + -0x7fef7fe0, 0x108000); var spfunction3 = new Array(0x208, 0x8020200, 0, 0x8020008, 0x8000200, 0, 0x20208, 0x8000200, 0x20008, 0x8000008, 0x8000008, 0x20000, 0x8020208, 0x20008, 0x8020000, 0x208, 0x8000000, 0x8, 0x8020200, 0x200, 0x20200, 0x8020000, 0x8020008, 0x20208, 0x8000208, 0x20200, 0x20000, 0x8000208, 0x8, 0x8020208, 0x200, 0x8000000, 0x8020200, 0x8000000, @@ -220,7 +220,6 @@ function des(keys, message, encrypt, mode, iv, padding) { } //end of des - //des_createKeys //this takes as input a 64 bit key (even though only 56 bits are used) //as an array of 2 integers, and returns 16 48 bit keys @@ -427,4 +426,4 @@ export default { des: Des, /** @static */ originalDes: OriginalDes -}; \ No newline at end of file +}; diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index fbd22cdd..d3e3750f 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -73,7 +73,7 @@ export default { * @return {Array} encrypted session key parameters */ publicKeyEncrypt: function(algo, publicParams, data, fingerprint) { - var types = this.getEncSessionKeyParamTypes(algo); + var types = this.getEncSessionKeyParamTypes(algo); var result = (function() { var m; switch (algo) { @@ -104,7 +104,7 @@ export default { default: return []; } - })(); + }()); return result; }, @@ -155,7 +155,7 @@ export default { default: return null; } - })(); + }()); var result = new type_mpi(bn); return result; @@ -270,7 +270,7 @@ export default { * @return {Array} The array of parameters */ generateParams: function(algo, bits, curve) { - var types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); + var types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': diff --git a/src/crypto/gcm.js b/src/crypto/gcm.js index b06b7318..e07f2ebf 100644 --- a/src/crypto/gcm.js +++ b/src/crypto/gcm.js @@ -22,9 +22,10 @@ 'use strict'; +import asmCrypto from 'asmcrypto-lite'; import util from '../util.js'; import config from '../config'; -import asmCrypto from 'asmcrypto-lite'; + const webCrypto = util.getWebCrypto(); // no GCM support in IE11, Safari 9 const nodeCrypto = util.getNodeCrypto(); const Buffer = util.getNodeBuffer(); @@ -49,7 +50,7 @@ export function encrypt(cipher, plaintext, key, iv) { if (webCrypto && config.use_native && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support return webEncrypt(plaintext, key, iv); } else if (nodeCrypto && config.use_native) { // Node crypto library - return nodeEncrypt(plaintext, key, iv) ; + return nodeEncrypt(plaintext, key, iv); } else { // asm.js fallback return Promise.resolve(asmCrypto.AES_GCM.encrypt(plaintext, key, iv)); } @@ -114,4 +115,4 @@ function nodeDecrypt(ct, key, iv) { de.setAuthTag(ct.slice(ct.length - TAG_LEN, ct.length)); // read auth tag at end of ciphertext const pt = Buffer.concat([de.update(ct.slice(0, ct.length - TAG_LEN)), de.final()]); return Promise.resolve(new Uint8Array(pt)); -} \ No newline at end of file +} diff --git a/src/crypto/hash/index.js b/src/crypto/hash/index.js index 27be2d77..ca246ffc 100644 --- a/src/crypto/hash/index.js +++ b/src/crypto/hash/index.js @@ -8,9 +8,9 @@ 'use strict'; -import sha from './sha.js'; -import asmCrypto from 'asmcrypto-lite'; import Rusha from 'rusha'; +import asmCrypto from 'asmcrypto-lite'; +import sha from './sha.js'; import md5 from './md5.js'; import ripemd from './ripe-md.js'; import util from '../../util.js'; diff --git a/src/crypto/hash/ripe-md.js b/src/crypto/hash/ripe-md.js index 48d74d2e..cae10d56 100644 --- a/src/crypto/hash/ripe-md.js +++ b/src/crypto/hash/ripe-md.js @@ -92,7 +92,6 @@ function mixOneRound(a, b, c, d, e, x, s, roundNumber) { default: throw new Error("Bogus round number"); - break; } a = ROL(a, s) + e; diff --git a/src/crypto/hash/sha.js b/src/crypto/hash/sha.js index 1802ca32..921784c4 100644 --- a/src/crypto/hash/sha.js +++ b/src/crypto/hash/sha.js @@ -1320,7 +1320,7 @@ var jsSHA = function(srcString, inputFormat, encoding) } /* Validate the numRounds argument */ - if ((numRounds !== parseInt(numRounds, 10)) || (1 > numRounds)) + if ((numRounds !== parseInt(numRounds)) || (1 > numRounds)) { throw "numRounds must a integer >= 1"; } diff --git a/src/crypto/index.js b/src/crypto/index.js index 036cee76..51ce77d6 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -37,7 +37,7 @@ const mod = { /** @see module:crypto/pkcs5 */ pkcs5: pkcs5, /** @see module:crypto/aes_kw */ - aes_kw: aes_kw, + aes_kw: aes_kw }; for (var i in crypto) { diff --git a/src/crypto/pkcs1.js b/src/crypto/pkcs1.js index 17632a55..8f033cf2 100644 --- a/src/crypto/pkcs1.js +++ b/src/crypto/pkcs1.js @@ -37,22 +37,17 @@ import hash from './hash'; */ var hash_headers = []; hash_headers[1] = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, - 0x10 -]; + 0x10]; hash_headers[2] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14]; hash_headers[3] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14]; hash_headers[8] = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, - 0x04, 0x20 -]; + 0x04, 0x20]; hash_headers[9] = [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, - 0x04, 0x30 -]; + 0x04, 0x30]; hash_headers[10] = [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, - 0x00, 0x04, 0x40 -]; + 0x00, 0x04, 0x40]; hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, - 0x00, 0x04, 0x1C -]; + 0x00, 0x04, 0x1C]; /** * Create padding with secure random data diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index b8dae437..4288458d 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -168,14 +168,14 @@ function webGenKeyPair(namedCurve) { return webCrypto.generateKey( { name: "ECDSA", -// FIXME +// FIXME // name: "ECDH", - namedCurve: namedCurve, // "P-256", "P-384", or "P-521" + namedCurve: namedCurve // "P-256", "P-384", or "P-521" }, // FIXME false, // whether the key is extractable (i.e. can be used in exportKey) ["sign", "verify"] // can be any combination of "sign" and "verify" -// FIXME +// FIXME // ["deriveKey", "deriveBits"] // can be any combination of "deriveKey" and "deriveBits" ).then(function(key){ return { diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index c4b230c9..20f25ffe 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -211,5 +211,6 @@ async function nodeVerify(curve, hash_algo, signature, m, Q) { const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); verify.write(m); verify.end(); - return await verify.verify(publicKey, signature); + const result = await verify.verify(publicKey, signature); + return result; } diff --git a/src/crypto/public_key/jsbn.js b/src/crypto/public_key/jsbn.js index bc8b6a3f..908a6be7 100644 --- a/src/crypto/public_key/jsbn.js +++ b/src/crypto/public_key/jsbn.js @@ -114,7 +114,7 @@ function am3(i, x, w, j, c, n) { return c; } /*if(j_lm && (navigator != undefined && - navigator.appName == "Microsoft Internet Explorer")) { + navigator.appName == "Microsoft Internet Explorer")) { BigInteger.prototype.am = am2; dbits = 30; } diff --git a/src/crypto/random.js b/src/crypto/random.js index 54e656ea..dafa16b7 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -27,6 +27,7 @@ import type_mpi from '../type/mpi.js'; import util from '../util.js'; + const nodeCrypto = util.detectNode() && require('crypto'); export default { diff --git a/src/encoding/armor.js b/src/encoding/armor.js index 8db6ff36..4c854910 100644 --- a/src/encoding/armor.js +++ b/src/encoding/armor.js @@ -115,7 +115,6 @@ function addheader() { } - /** * Calculates a checksum over the given data and returns it base64 encoded * @param {String} data Data to create a CRC-24 checksum for diff --git a/src/encoding/base64.js b/src/encoding/base64.js index 298b1858..bb670945 100644 --- a/src/encoding/base64.js +++ b/src/encoding/base64.js @@ -39,7 +39,7 @@ function s2r(t, o) { r.push(b64s.charAt((c >> 2) & 63)); a = (c & 3) << 4; } else if (s === 1) { - r.push(b64s.charAt((a | (c >> 4) & 15))); + r.push(b64s.charAt(a | ((c >> 4) & 15))); a = (c & 15) << 2; } else if (s === 2) { r.push(b64s.charAt(a | ((c >> 6) & 3))); @@ -99,7 +99,7 @@ function r2s(t) { c = b64s.indexOf(t.charAt(n)); if (c >= 0) { if (s) { - r.push(a | (c >> (6 - s)) & 255); + r.push(a | ((c >> (6 - s)) & 255)); } s = (s + 2) & 7; a = (c << s) & 255; diff --git a/src/index.js b/src/index.js index b5ec138e..a77e4635 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,5 @@ +/* eslint-disable import/newline-after-import, import/first */ + 'use strict'; /** @@ -135,4 +137,4 @@ export { default as AsyncProxy } from './worker/async_proxy'; * @see module:hkp * @name module:openpgp.HKP */ -export { default as HKP } from './hkp'; \ No newline at end of file +export { default as HKP } from './hkp'; diff --git a/src/key.js b/src/key.js index fce34b1a..b411773b 100644 --- a/src/key.js +++ b/src/key.js @@ -583,8 +583,8 @@ Key.prototype.update = function(key) { key.users.forEach(function(srcUser) { var found = false; for (var i = 0; i < that.users.length; i++) { - if (srcUser.userId && (srcUser.userId.userid === that.users[i].userId.userid) || - srcUser.userAttribute && (srcUser.userAttribute.equals(that.users[i].userAttribute))) { + if ((srcUser.userId && (srcUser.userId.userid === that.users[i].userId.userid)) || + (srcUser.userAttribute && (srcUser.userAttribute.equals(that.users[i].userAttribute)))) { that.users[i].update(srcUser, that.primaryKey); found = true; break; diff --git a/src/keyring/index.js b/src/keyring/index.js index 54bea772..430e4caa 100644 --- a/src/keyring/index.js +++ b/src/keyring/index.js @@ -5,8 +5,8 @@ * @module keyring */ import Keyring from './keyring.js'; - import localstore from './localstore.js'; + Keyring.localstore = localstore; export default Keyring; diff --git a/src/message.js b/src/message.js index 139ca4de..87f01b52 100644 --- a/src/message.js +++ b/src/message.js @@ -184,7 +184,7 @@ Message.prototype.decryptSessionKey = function(privateKey, password) { */ Message.prototype.getLiteralData = function() { var literal = this.packets.findPacket(enums.packet.literal); - return literal && literal.data || null; + return (literal && literal.data) || null; }; /** @@ -193,7 +193,7 @@ Message.prototype.getLiteralData = function() { */ Message.prototype.getFilename = function() { var literal = this.packets.findPacket(enums.packet.literal); - return literal && literal.getFilename() || null; + return (literal && literal.getFilename()) || null; }; /** @@ -323,7 +323,7 @@ Message.prototype.sign = function(privateKeys=[], signature=null) { var literalFormat = enums.write(enums.literal, literalDataPacket.format); var signatureType = literalFormat === enums.literal.binary ? - enums.signature.binary : enums.signature.text; + enums.signature.binary : enums.signature.text; var i, signingKeyPacket, existingSigPacketlist, onePassSig; if (signature) { @@ -401,7 +401,7 @@ Message.prototype.signDetached = function(privateKeys=[], signature=null) { var literalFormat = enums.write(enums.literal, literalDataPacket.format); var signatureType = literalFormat === enums.literal.binary ? - enums.signature.binary : enums.signature.text; + enums.signature.binary : enums.signature.text; for (var i = 0; i < privateKeys.length; i++) { var signingKeyPacket = privateKeys[i].getSigningKeyPacket(); diff --git a/src/openpgp.js b/src/openpgp.js index 3d56d87c..c28228ac 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -32,13 +32,15 @@ 'use strict'; +import es6Promise from 'es6-promise'; + import * as messageLib from './message.js'; import * as cleartext from './cleartext.js'; import * as key from './key.js'; import config from './config/config.js'; import util from './util'; import AsyncProxy from './worker/async_proxy.js'; -import es6Promise from 'es6-promise'; + es6Promise.polyfill(); // load ES6 Promises polyfill @@ -57,7 +59,7 @@ let asyncProxy; // instance of the asyncproxy * @param {Object} worker alternative to path parameter: web worker initialized with 'openpgp.worker.js' */ export function initWorker({ path='openpgp.worker.js', worker } = {}) { - if (worker || typeof window !== 'undefined' && window.Worker) { + if (worker || (typeof window !== 'undefined' && window.Worker)) { asyncProxy = new AsyncProxy({ path, worker, config }); return true; } @@ -302,7 +304,7 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { var result = {}; const promise = async function(){ var message; - + if (util.isString(data)) { message = new cleartext.CleartextMessage(data); } else { diff --git a/src/packet/compressed.js b/src/packet/compressed.js index 619a87da..86e9e9d0 100644 --- a/src/packet/compressed.js +++ b/src/packet/compressed.js @@ -79,7 +79,6 @@ Compressed.prototype.read = function (bytes) { }; - /** * Return the compressed packet. * @return {String} binary compressed packet diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index 01d5c0cd..4ec59e9d 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -45,7 +45,10 @@ Packetlist.prototype.read = function (bytes) { pushed = true; packet.read(parsed.packet); } catch(e) { - if (!config.tolerant || parsed.tag == enums.packet.symmetricallyEncrypted || parsed.tag == enums.packet.literal || parsed.tag == enums.packet.compressed) { + if (!config.tolerant || + parsed.tag === enums.packet.symmetricallyEncrypted || + parsed.tag === enums.packet.literal || + parsed.tag === enums.packet.compressed) { throw e; } if (pushed) { @@ -127,7 +130,7 @@ Packetlist.prototype.filterByTag = function () { var filtered = new Packetlist(); var that = this; - function handle(packetType) {return that[i].tag === packetType;} + function handle(packetType) { return that[i].tag === packetType; } for (var i = 0; i < this.length; i++) { if (args.some(handle)) { filtered.push(this[i]); @@ -177,7 +180,7 @@ Packetlist.prototype.indexOfTag = function () { var tagIndex = []; var that = this; - function handle(packetType) {return that[i].tag === packetType;} + function handle(packetType) { return that[i].tag === packetType; } for (var i = 0; i < this.length; i++) { if (args.some(handle)) { tagIndex.push(i); @@ -228,4 +231,4 @@ Packetlist.fromStructuredClone = function(packetlistClone) { } } return packetlist; -}; \ No newline at end of file +}; diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 7d8c130f..dd3edc30 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -170,8 +170,6 @@ SecretKey.prototype.write = function () { }; - - /** Encrypt the payload. By default, we use aes256 and iterated, salted string * to key specifier. If the key is in a decrypted state (isDecrypted === true) * and the passphrase is empty or undefined, the key will be set as not encrypted. @@ -193,7 +191,7 @@ SecretKey.prototype.encrypt = function (passphrase) { blockLen = crypto.cipher[symmetric].blockSize, iv = crypto.random.getRandomBytes(blockLen); - var arr = [ new Uint8Array([254, enums.write(enums.symmetric, symmetric)]) ]; + var arr = [new Uint8Array([254, enums.write(enums.symmetric, symmetric)])]; arr.push(s2k.write()); arr.push(iv); arr.push(crypto.cfb.normalEncrypt(symmetric, key, cleartext, iv)); diff --git a/src/packet/signature.js b/src/packet/signature.js index 89d953c4..c818b900 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -97,6 +97,28 @@ export default function Signature() { Signature.prototype.read = function (bytes) { var i = 0; this.version = bytes[i++]; + + function subpackets(bytes) { + // Two-octet scalar octet count for following subpacket data. + var subpacket_length = util.readNumber( + bytes.subarray(0, 2)); + + var i = 2; + + // subpacket data set (zero or more subpackets) + while (i < 2 + subpacket_length) { + + var len = packet.readSimpleLength(bytes.subarray(i, bytes.length)); + i += len.offset; + + this.read_sub_packet(bytes.subarray(i, i + len.len)); + + i += len.len; + } + + return i; + } + // switch on version (3 and 4) switch (this.version) { case 3: @@ -133,27 +155,6 @@ Signature.prototype.read = function (bytes) { this.publicKeyAlgorithm = bytes[i++]; this.hashAlgorithm = bytes[i++]; - function subpackets(bytes) { - // Two-octet scalar octet count for following subpacket data. - var subpacket_length = util.readNumber( - bytes.subarray(0, 2)); - - var i = 2; - - // subpacket data set (zero or more subpackets) - while (i < 2 + subpacket_length) { - - var len = packet.readSimpleLength(bytes.subarray(i, bytes.length)); - i += len.offset; - - this.read_sub_packet(bytes.subarray(i, i + len.len)); - - i += len.len; - } - - return i; - } - // hashed subpackets i += subpackets.call(this, bytes.subarray(i, bytes.length), true); diff --git a/src/packet/sym_encrypted_aead_protected.js b/src/packet/sym_encrypted_aead_protected.js index 41f44aec..36284b13 100644 --- a/src/packet/sym_encrypted_aead_protected.js +++ b/src/packet/sym_encrypted_aead_protected.js @@ -37,7 +37,7 @@ export default function SymEncryptedAEADProtected() { this.version = VERSION; this.iv = null; this.encrypted = null; - this.packets = null; + this.packets = null; } /** @@ -85,4 +85,4 @@ SymEncryptedAEADProtected.prototype.encrypt = function (sessionKeyAlgorithm, key return crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv).then(encrypted => { this.encrypted = encrypted; }); -}; \ No newline at end of file +}; diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 24fbd842..60e2844a 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -34,10 +34,11 @@ 'use strict'; +import asmCrypto from 'asmcrypto-lite'; import util from '../util.js'; import crypto from '../crypto'; import enums from '../enums.js'; -import asmCrypto from 'asmcrypto-lite'; + const nodeCrypto = util.getNodeCrypto(); const Buffer = util.getNodeBuffer(); @@ -176,4 +177,4 @@ function nodeDecrypt(algo, ct, key) { const decipherObj = new nodeCrypto.createDecipheriv('aes-' + algo.substr(3,3) + '-cfb', key, iv); const pt = decipherObj.update(ct); return new Uint8Array(pt); -} \ No newline at end of file +} diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js index 045d24bb..d40efa85 100644 --- a/src/packet/sym_encrypted_session_key.js +++ b/src/packet/sym_encrypted_session_key.js @@ -139,8 +139,7 @@ SymEncryptedSessionKey.prototype.encrypt = function(passphrase) { var length = crypto.cipher[algo].keySize; var key = this.s2k.produce_key(passphrase, length); - var algo_enum = new Uint8Array([ - enums.write(enums.symmetric, this.sessionKeyAlgorithm)]); + var algo_enum = new Uint8Array([enums.write(enums.symmetric, this.sessionKeyAlgorithm)]); var private_key; if(this.sessionKey === null) { diff --git a/src/packet/symmetrically_encrypted.js b/src/packet/symmetrically_encrypted.js index 04a0b8a1..13f69280 100644 --- a/src/packet/symmetrically_encrypted.js +++ b/src/packet/symmetrically_encrypted.js @@ -42,7 +42,7 @@ export default function SymmetricallyEncrypted() { this.encrypted = null; /** Decrypted packets contained within. * @type {module:packet/packetlist} */ - this.packets = null; + this.packets = null; this.ignore_mdc_error = config.ignore_mdc_error; } diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index 84b3be2d..9df5410f 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -65,7 +65,5 @@ ECDHSymmetricKey.prototype.read = function (input) { * @return {Uint8Array} An array containing the value */ ECDHSymmetricKey.prototype.write = function () { - return util.concatUint8Array([ - new Uint8Array([this.data.length]), - this.data]); + return util.concatUint8Array([new Uint8Array([this.data.length]), this.data]); }; diff --git a/src/worker/worker.js b/src/worker/worker.js index ff1e357b..2b25f319 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -15,7 +15,7 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -/* globals self: true */ +/* eslint-disable no-restricted-globals */ self.window = {}; // to make UMD bundles work @@ -99,4 +99,4 @@ function response(event) { self.postMessage({event: 'request-seed'}); } self.postMessage(event, openpgp.util.getTransferables.call(openpgp.util, event.data)); -} \ No newline at end of file +} From 12eb037ba7298b542e59a21238070168bb682ebb Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:13:34 -0800 Subject: [PATCH 35/75] Everything in test/crypto/elliptic.js passes; working on test/general/ecc.js --- src/crypto/public_key/elliptic/curves.js | 88 ++++++----- src/crypto/public_key/elliptic/ecdsa.js | 192 +++++++++++++---------- src/index.js | 7 +- test/crypto/elliptic.js | 109 +++++++------ 4 files changed, 223 insertions(+), 173 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 4288458d..839f731e 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -84,7 +84,8 @@ const curves = { hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: nodeCurves.includes('secp256k1'), + node: false, // FIXME nodeCurves.includes('secp256k1'), + // this is because jwk-to-pem does not support this curve. web: false } }; @@ -110,12 +111,12 @@ Curve.prototype.keyFromPublic = function (pub) { return new KeyPair(this.curve, {pub: pub}); }; -Curve.prototype.genKeyPair = function () { +Curve.prototype.genKeyPair = async function () { var keyPair; if (webCrypto && config.use_native && this.web) { - keyPair = webGenKeyPair(this.namedCurve); + keyPair = await webGenKeyPair(this.namedCurve); } else if (nodeCrypto && config.use_native && this.node) { - keyPair = nodeGenKeyPair(this.opensslCurve); + keyPair = await nodeGenKeyPair(this.opensslCurve); } else { var r = this.curve.genKeyPair(); keyPair = { @@ -136,18 +137,16 @@ function get(oid_or_name) { throw new Error('Not valid curve'); } -function generate(curve) { - return new Promise(function (resolve) { - curve = get(curve); - var keyPair = curve.genKeyPair(); - resolve({ - oid: curve.oid, - Q: new BigInteger(keyPair.getPublic()), - d: new BigInteger(keyPair.getPrivate()), - hash: curve.hash, - cipher: curve.cipher - }); - }); +async function generate(curve) { + curve = get(curve); + var keyPair = await curve.genKeyPair(); + return { + oid: curve.oid, + Q: new BigInteger(keyPair.getPublic()), + d: new BigInteger(keyPair.getPrivate()), + hash: curve.hash, + cipher: curve.cipher + }; } module.exports = { @@ -164,35 +163,40 @@ module.exports = { ////////////////////////// -function webGenKeyPair(namedCurve) { - return webCrypto.generateKey( - { - name: "ECDSA", -// FIXME -// name: "ECDH", - namedCurve: namedCurve // "P-256", "P-384", or "P-521" - }, -// FIXME - false, // whether the key is extractable (i.e. can be used in exportKey) - ["sign", "verify"] // can be any combination of "sign" and "verify" -// FIXME -// ["deriveKey", "deriveBits"] // can be any combination of "deriveKey" and "deriveBits" - ).then(function(key){ +async function webGenKeyPair(namedCurve) { + try { + var keyPair = await webCrypto.generateKey( + { + name: "ECDSA", // FIXME or "ECDH" + // "P-256", "P-384", or "P-521" + namedCurve: namedCurve + }, + // TODO whether the key is extractable (i.e. can be used in exportKey) + false, + // FIXME this can be any combination of "sign" and "verify" + // or "deriveKey" and "deriveBits" for ECDH + ["sign", "verify"] + ); + return { - pub: key.publicKey.encode(), // FIXME encoding - priv: key.privateKey.toArray() // FIXME encoding + pub: keyPair.publicKey.encode(), // FIXME encoding + priv: keyPair.privateKey.toArray() // FIXME encoding }; - }).catch(function(err){ + } catch(err) { throw new Error(err); - }); + } } -function nodeGenKeyPair(opensslCurve) { - // TODO turn this into a promise - var ecc = nodeCrypto.createECDH(opensslCurve); - ecc.generateKeys(); - return { - pub: ecc.getPrivateKey().toJSON().data, - priv: ecc.getPublicKey().toJSON().data - }; +async function nodeGenKeyPair(opensslCurve) { + try { + var ecdh = nodeCrypto.createECDH(opensslCurve); + await ecdh.generateKeys(); + + return { + pub: ecdh.getPublicKey().toJSON().data, + priv: ecdh.getPrivateKey().toJSON().data + }; + } catch(err) { + throw new Error(err); + } } diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 20f25ffe..957b18d1 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -25,20 +25,21 @@ 'use strict'; -import asn1 from 'asn1.js'; -import jwk2pem from 'jwk-to-pem'; +import BN from 'bn.js'; +import ASN1 from 'asn1.js'; +import jwkToPem from 'jwk-to-pem'; import curves from './curves.js'; import BigInteger from '../jsbn.js'; import config from '../../../config'; import enums from '../../../enums.js'; -import util from '../../../util.js'; +import util from '../../../util.js' const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); -var ECDSASignature = asn1.define('ecdsa-sig', function() { - return this.seq().obj( +var ECDSASignature = ASN1.define('ecdsa-sig', function() { + this.seq().obj( this.key('r').int(), // FIXME int or BN? this.key('s').int() // FIXME int or BN? ); @@ -52,16 +53,16 @@ var ECDSASignature = asn1.define('ecdsa-sig', function() { * @param {BigInteger} d Private key used to sign * @return {{r: BigInteger, s: BigInteger}} Signature of the message */ -function sign(oid, hash_algo, m, d) { +async function sign(oid, hash_algo, m, d) { var signature; const curve = curves.get(oid); + const key = curve.keyFromPrivate(d.toByteArray()); if (webCrypto && config.use_native && curve.web) { - signature = webSign(curve, hash_algo, m, d); + signature = await webSign(curve, hash_algo, m, key.keyPair); } else if (nodeCrypto && config.use_native && curve.node) { - signature = nodeSign(curve, hash_algo, m, d); + signature = await nodeSign(curve, hash_algo, m, key.keyPair); } else { - const key = curve.keyFromPrivate(d.toByteArray()); - signature = key.sign(m, hash_algo); + signature = await key.sign(m, hash_algo); } return { r: new BigInteger(signature.r), @@ -78,16 +79,20 @@ function sign(oid, hash_algo, m, d) { * @param {BigInteger} Q Public key used to verify the message * @return {Boolean} */ -function verify(oid, hash_algo, signature, m, Q) { +async function verify(oid, hash_algo, signature, m, Q) { + var result; const curve = curves.get(oid); + const key = curve.keyFromPublic(Q.toByteArray()); if (webCrypto && config.use_native && curve.web) { - return webVerify(curve, hash_algo, signature, m, Q); + result = await webVerify(curve, hash_algo, signature, m, key.keyPair.getPublic()); } else if (nodeCrypto && config.use_native && curve.node) { - return nodeVerify(curve, hash_algo, signature, m, Q); + result = await nodeVerify(curve, hash_algo, signature, m, key.keyPair.getPublic()); } else { - const key = curve.keyFromPublic(Q.toByteArray()); - return key.verify(m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo); + result = await key.verify( + m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo + ); } + return result; } module.exports = { @@ -103,36 +108,35 @@ module.exports = { ////////////////////////// -async function webSign(curve, hash_algo, m, d) { - const publicKey = curve.keyFromPrivate(d).getPublic(); - const privateKey = await webCrypto.importKey( - "jwk", - { - "kty": "EC", - "crv": curve.namedCurve, - "x": publicKey.getX().toBuffer().base64Slice(), - "y": publicKey.getY().toBuffer().base64Slice(), - "d": d.toBuffer().base64Slice(), - "use": "sig", - "kid": "ECDSA Private Key" - }, - { - "name": "ECDSA", - "namedCurve": curve.namedCurve, - "hash": { name: enums.read(enums.hash, hash_algo) } - }, - false, - ["sign"] - ); - +async function webSign(curve, hash_algo, m, keyPair) { try { + const key = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": curve.namedCurve, + "x": keyPair.getPublic().getX().toBuffer().base64Slice(), + "y": keyPair.getPublic().getY().toBuffer().base64Slice(), + "d": keyPair.getPrivate().toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Private Key" + }, + { + "name": "ECDSA", + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.hash, hash_algo) } + }, + false, + ["sign"] + ); + return await webCrypto.sign( { "name": 'ECDSA', "namedCurve": curve.namedCurve, "hash": { name: enums.read(enums.hash, hash_algo) } }, - privateKey, + key, m ); } catch(err) { @@ -140,34 +144,34 @@ async function webSign(curve, hash_algo, m, d) { } } -async function webVerify(curve, hash_algo, signature, m, Q) { - const publicKey = await webCrypto.importKey( - "jwk", - { - "kty": "EC", - "crv": curve.namedCurve, - "x": Q.getX().toBuffer().base64Slice(), - "y": Q.getY().toBuffer().base64Slice(), - "use": "sig", - "kid": "ECDSA Public Key" - }, - { - "name": "ECDSA", - "namedCurve": curve.namedCurve, - "hash": { name: enums.read(enums.hash, hash_algo) } - }, - false, - ["verify"] - ); - +async function webVerify(curve, hash_algo, signature, m, publicKey) { try { + const key = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": curve.namedCurve, + "x": publicKey.getX().toBuffer().base64Slice(), + "y": publicKey.getY().toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Public Key" + }, + { + "name": "ECDSA", + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.hash, hash_algo) } + }, + false, + ["verify"] + ); + return await webCrypto.verify( { "name": 'ECDSA', "namedCurve": curve.namedCurve, "hash": { name: enums.read(enums.hash, hash_algo) } }, - publicKey, + key, signature, m ); @@ -177,40 +181,60 @@ async function webVerify(curve, hash_algo, signature, m, Q) { } -async function nodeSign(curve, hash_algo, m, d) { - const publicKey = curve.keyFromPrivate(d).getPublic(); - const privateKey = jwk2pem( - {"kty": "EC", - "crv": curve.namedCurve, - "x": publicKey.getX().toBuffer().base64Slice(), - "y": publicKey.getY().toBuffer().base64Slice(), - "d": d.toBuffer().base64Slice(), - "use": "sig", - "kid": "ECDSA Private Key"}, - {private: true} +async function nodeSign(curve, hash_algo, message, keyPair) { + if (typeof message === 'string') { + message = util.str2Uint8Array(message); + } + const key = jwkToPem( + { + "kty": "EC", + "crv": curve.namedCurve, + "x": keyPair.getPublic().getX().toBuffer().base64Slice(), + "y": keyPair.getPublic().getY().toBuffer().base64Slice(), + "d": keyPair.getPrivate().toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Private Key" + }, + { private: true } ); + // FIXME what happens when hash_algo = undefined? const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo)); - sign.write(m); + sign.write(message); sign.end(); - const signature = await sign.sign(privateKey); - return ECDSASignature.decode(signature, 'der'); + const signature = await ECDSASignature.decode(sign.sign(key), 'der'); + return { + r: signature.r.toArray(), + s: signature.s.toArray() + }; } -async function nodeVerify(curve, hash_algo, signature, m, Q) { - const publicKey = jwk2pem( - {"kty": "EC", - "crv": curve.namedCurve, - "x": Q.getX().toBuffer().base64Slice(), - "y": Q.getY().toBuffer().base64Slice(), - "use": "sig", - "kid": "ECDSA Public Key"}, - {private: false} +async function nodeVerify(curve, hash_algo, signature, message, publicKey) { + signature = ECDSASignature.encode( + { + r: new BN(signature.r.toByteArray()), + s: new BN(signature.s.toByteArray()) + }, + 'der'); + if (typeof message === 'string') { + message = util.str2Uint8Array(message); + } + const key = jwkToPem( + { + "kty": "EC", + "crv": curve.namedCurve, + "x": publicKey.getX().toBuffer().base64Slice(), + "y": publicKey.getY().toBuffer().base64Slice(), + "use": "sig", + "kid": "ECDSA Public Key" + }, + { private: false } ); + // FIXME what happens when hash_algo = undefined? const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); - verify.write(m); + verify.write(message); verify.end(); - const result = await verify.verify(publicKey, signature); + const result = await verify.verify(key, signature); return result; } diff --git a/src/index.js b/src/index.js index a77e4635..30987f0b 100644 --- a/src/index.js +++ b/src/index.js @@ -19,7 +19,12 @@ export default openpgp; * import { encryptMessage } from 'openpgp.js' * encryptMessage(keys, text) */ -// export * from './openpgp'; +export { + encrypt, decrypt, sign, verify, + generateKey, reformatKey, decryptKey, + encryptSessionKey, decryptSessionKey, + initWorker, getWorker, destroyWorker +} from './openpgp'; /** * @see module:key diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index c52dcf4d..5a1b0c6d 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -2,7 +2,11 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -var expect = require('chai').expect; +var chai = require('chai'); +var chaiAsPromised = require('chai-as-promised'); +chai.use(chaiAsPromised); +const expect = chai.expect; + var bin2bi = function (bytes) { var mpi = new openpgp.MPI(); @@ -149,9 +153,10 @@ describe('Elliptic Curve Cryptography', function () { var names = ['p256', 'p384', 'p521', 'secp256k1']; names.forEach(function (name) { var curve = elliptic_curves.get(name); - var keyPair = curve.genKeyPair(); - expect(keyPair).to.exist; - expect(keyPair.isValid()).to.be.true; + curve.genKeyPair().then(keyPair => { + expect(keyPair).to.exist; + expect(keyPair.isValid()).to.be.true; // FIXME done will skip this. + }); }); done(); }); @@ -201,21 +206,22 @@ describe('Elliptic Curve Cryptography', function () { }); }); describe('ECDSA signature', function () { - var verify_signature = function (oid, hash, r, s, pub, message) { + var verify_signature = function (oid, hash, r, s, message, pub) { if (openpgp.util.isString(message)) { message = openpgp.util.str2Uint8Array(message); } else if (!openpgp.util.isUint8Array(message)) { message = new Uint8Array(message); } - return function () { + return Promise.resolve().then(() => { var ecdsa = elliptic_curves.ecdsa; - return ecdsa.verify(oid, + return ecdsa.verify( + oid, hash, {r: bin2bi(r), s: bin2bi(s)}, message, bin2bi(pub) ); - }; + }); }; var secp256k1_dummy_value = new Uint8Array([ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -236,24 +242,30 @@ describe('Elliptic Curve Cryptography', function () { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); - it('Invalid curve oid', function (done) { - var res = verify_signature('invalid oid', 8, [], [], [], []); - expect(res).to.throw(Error, /Not valid curve/); - res = verify_signature("\x00", 8, [], [], [], []); - expect(res).to.throw(Error, /Not valid curve/); - done(); + it('Invalid curve oid', function () { + return Promise.all([ + expect(verify_signature( + 'invalid oid', 8, [], [], [], [] + )).to.be.rejectedWith(Error, /Not valid curve/), + expect(verify_signature( + "\x00", 8, [], [], [], [] + )).to.be.rejectedWith(Error, /Not valid curve/) + ]); }); - it('Invalid public key', function (done) { - var res = verify_signature('secp256k1', 8, [], [], [], []); - expect(res).to.throw(Error, /Unknown point format/); - res = verify_signature('secp256k1', 8, [], [], secp256k1_invalid_point, []); - expect(res).to.throw(Error, /Unknown point format/); - done(); + it('Invalid public key', function () { + return Promise.all([ + expect(verify_signature( + 'secp256k1', 8, [], [], [], [] + )).to.be.rejectedWith(Error, /Unknown point format/), + expect(verify_signature( + 'secp256k1', 8, [], [], [], secp256k1_invalid_point + )).to.be.rejectedWith(Error, /Unknown point format/) + ]); }); it('Invalid signature', function (done) { - var res = verify_signature('secp256k1', 8, [], [], secp256k1_dummy_point, []); - expect(res()).to.be.false; - done(); + expect(verify_signature( + 'secp256k1', 8, [], [], [], secp256k1_dummy_point + )).to.eventually.equal(false).notify(done); }); var p384_message = new Uint8Array([ @@ -274,21 +286,26 @@ describe('Elliptic Curve Cryptography', function () { 0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21, 0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC]); it('Valid signature', function (done) { - var res = verify_signature('p384', 8, p384_r, p384_s, key_data.p384.pub, p384_message); - expect(res()).to.be.true; - done(); + verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub) + .then(res => { + expect(res).to.be.true; + done(); + }); }); it('Sign and verify message', function (done) { var curve = elliptic_curves.get('p521'); - var keyPair = curve.genKeyPair(); - var keyPublic = bin2bi(keyPair.getPublic()); - var keyPrivate = bin2bi(keyPair.getPrivate()); - var oid = curve.oid; - var message = p384_message; - var signature = elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate); - var verified = elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic); - expect(verified).to.be.true; - done(); + curve.genKeyPair().then(keyPair => { + var keyPublic = bin2bi(keyPair.getPublic()); + var keyPrivate = bin2bi(keyPair.getPrivate()); + var oid = curve.oid; + var message = p384_message; + elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => { + elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic).then(verified => { + expect(verified).to.be.true; + done(); + }); + }); + }); }); }); describe('ECDH key exchange', function () { @@ -298,7 +315,7 @@ describe('Elliptic Curve Cryptography', function () { } else { data = new Uint8Array(data); } - return function () { + return Promise.resolve().then(() => { var ecdh = elliptic_curves.ecdh; return ecdh.decrypt( oid, @@ -309,7 +326,7 @@ describe('Elliptic Curve Cryptography', function () { bin2bi(priv), fingerprint ); - }; + }); }; var secp256k1_value = new Uint8Array([ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -330,19 +347,19 @@ describe('Elliptic Curve Cryptography', function () { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]); it('Invalid curve oid', function (done) { - var res = decrypt_message('', 2, 7, [], [], [], ''); - expect(res).to.throw(Error, /Not valid curve/); - done(); + expect(decrypt_message( + '', 2, 7, [], [], [], '' + )).to.be.rejectedWith(Error, /Not valid curve/).notify(done); }); it('Invalid ephemeral key', function (done) { - var res = decrypt_message('secp256k1', 2, 7, [], [], [], ''); - expect(res).to.throw(Error, /Unknown point format/); - done(); + expect(decrypt_message( + 'secp256k1', 2, 7, [], [], [], '' + )).to.be.rejectedWith(Error, /Unknown point format/).notify(done);; }); it('Invalid key data integrity', function (done) { - var res = decrypt_message('secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_data, ''); - expect(res).to.throw(Error, /Key Data Integrity failed/); - done(); + expect(decrypt_message( + 'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_data, '' + )).to.be.rejectedWith(Error, /Key Data Integrity failed/).notify(done); }); }); }); From 1a714cec736797dd70677fe94533131232297f68 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:25:13 -0800 Subject: [PATCH 36/75] ECC with async/await is 'Done, without errors.' --- src/cleartext.js | 18 +- src/crypto/crypto.js | 6 +- src/crypto/public_key/elliptic/ecdh.js | 4 +- src/crypto/signature.js | 9 +- src/key.js | 592 +++++++++++------- src/keyring/keyring.js | 4 +- src/message.js | 26 +- src/openpgp.js | 29 +- .../public_key_encrypted_session_key.js | 4 +- src/packet/signature.js | 8 +- test/crypto/crypto.js | 81 ++- test/crypto/elliptic.js | 8 +- test/general/ecc.js | 48 +- test/general/key.js | 230 ++++--- test/general/openpgp.js | 25 +- test/general/packet.js | 71 +-- test/general/signature.js | 132 ++-- 17 files changed, 735 insertions(+), 560 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index 94b56eb3..be3bee3b 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -69,8 +69,8 @@ CleartextMessage.prototype.getSigningKeyIds = function() { * @param {Array} privateKeys private keys with decrypted secret key data for signing * @return {module:message~CleartextMessage} new cleartext message with signed content */ -CleartextMessage.prototype.sign = function(privateKeys) { - return new CleartextMessage(this.text, this.signDetached(privateKeys)); +CleartextMessage.prototype.sign = async function(privateKeys) { + return new CleartextMessage(this.text, await this.signDetached(privateKeys)); }; /** @@ -78,25 +78,26 @@ CleartextMessage.prototype.sign = function(privateKeys) { * @param {Array} privateKeys private keys with decrypted secret key data for signing * @return {module:signature~Signature} new detached signature of message content */ -CleartextMessage.prototype.signDetached = function(privateKeys) { +CleartextMessage.prototype.signDetached = async function(privateKeys) { var packetlist = new packet.List(); var literalDataPacket = new packet.Literal(); literalDataPacket.setText(this.text); - for (var i = 0; i < privateKeys.length; i++) { - if (privateKeys[i].isPublic()) { + await Promise.all(privateKeys.map(async function(privateKey) { + if (privateKey.isPublic()) { throw new Error('Need private key for signing'); } var signaturePacket = new packet.Signature(); signaturePacket.signatureType = enums.signature.text; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - var signingKeyPacket = privateKeys[i].getSigningKeyPacket(); + var signingKeyPacket = privateKey.getSigningKeyPacket(); signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } - signaturePacket.sign(signingKeyPacket, literalDataPacket); + await signaturePacket.sign(signingKeyPacket, literalDataPacket); packetlist.push(signaturePacket); - } + })); + return new sigModule.Signature(packetlist); }; @@ -120,6 +121,7 @@ CleartextMessage.prototype.verifyDetached = function(signature, keys) { var literalDataPacket = new packet.Literal(); // we assume that cleartext signature is generated based on UTF8 cleartext literalDataPacket.setText(this.text); + // TODO await Promise.all(signatureList.map(async function(signature) { })); for (var i = 0; i < signatureList.length; i++) { var keyPacket = null; for (var j = 0; j < keys.length; j++) { diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index d3e3750f..f2eaa1eb 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -74,7 +74,7 @@ export default { */ publicKeyEncrypt: function(algo, publicParams, data, fingerprint) { var types = this.getEncSessionKeyParamTypes(algo); - var result = (function() { + var result = (async function() { var m; switch (algo) { case 'rsa_encrypt': @@ -98,7 +98,9 @@ export default { var curve = publicParams[0]; var kdf_params = publicParams[2]; var R = publicParams[1].toBigInteger(); - var res = ecdh.encrypt(curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint); + var res = await ecdh.encrypt( + curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint + ); return constructParams([res.V, res.C], types); default: diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index d43392f2..4419a995 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -77,12 +77,12 @@ function kdf(hash_algo, X, length, param) { * @param {String} fingerprint Recipient fingerprint * @return {{V: BigInteger, C: Uint8Array}} Returns ephemeral key and encoded session key */ -function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { +async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); const curve = curves.get(oid); cipher_algo = enums.read(enums.symmetric, cipher_algo); - const v = curve.genKeyPair(); + const v = await curve.genKeyPair(); Q = curve.keyFromPublic(Q.toByteArray()); const S = v.derive(Q); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 18ffd225..284e8779 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -21,7 +21,7 @@ export default { * @param {Uint8Array} data Data on where the signature was computed on. * @return {Boolean} true if signature (sig_data was equal to data over hash) */ - verify: function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { + verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { var m; data = util.Uint8Array2str(data); @@ -64,7 +64,8 @@ export default { const s = msg_MPIs[1].toBigInteger(); m = data; const Q = publickey_MPIs[1].toBigInteger(); - return ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); + const result = await ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); + return result; default: throw new Error('Invalid signature algorithm.'); } @@ -78,7 +79,7 @@ export default { * @param {Uint8Array} data Data to be signed * @return {Array} */ - sign: function(hash_algo, algo, keyIntegers, data) { + sign: async function(hash_algo, algo, keyIntegers, data) { data = util.Uint8Array2str(data); @@ -120,7 +121,7 @@ export default { var curve = keyIntegers[0]; d = keyIntegers[2].toBigInteger(); m = data; - const signature = ecdsa.sign(curve.oid, hash_algo, m, d); + const signature = await ecdsa.sign(curve.oid, hash_algo, m, d); return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); default: diff --git a/src/key.js b/src/key.js index b411773b..787f4a83 100644 --- a/src/key.js +++ b/src/key.js @@ -30,6 +30,7 @@ import enums from './enums.js'; import armor from './encoding/armor.js'; import config from './config'; import util from './util'; +import crypto from './crypto'; /** * @class @@ -299,69 +300,81 @@ Key.prototype.armor = function() { */ Key.prototype.getSigningKeyPacket = function(keyId, allowExpired=false) { var primaryUser = this.getPrimaryUser(allowExpired); - if (primaryUser && - isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate) && - (!keyId || this.primaryKey.getKeyId().equals(keyId)) && - this.verifyPrimaryKey(allowExpired) === enums.keyStatus.valid) { + if (primaryUser && (!keyId || this.primaryKey.getKeyId().equals(keyId)) && + isValidSigningKeyPacket(this.primaryKey, primaryUser.selfCertificate, allowExpired)) { return this.primaryKey; } if (this.subKeys) { for (var i = 0; i < this.subKeys.length; i++) { - if (this.subKeys[i].isValidSigningKey(this.primaryKey, allowExpired) && - (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId))) { - return this.subKeys[i].subKey; + if (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId)) { + for (var j = 0; j < this.subKeys[i].bindingSignatures.length; j++) { + if (isValidSigningKeyPacket( + this.subKeys[i].subKey, this.subKeys[i].bindingSignatures[j], allowExpired)) { + return this.subKeys[i].subKey; + } + } } } } return null; }; -/** - * Returns preferred signature hash algorithm of this key - * @return {String} - */ -Key.prototype.getPreferredHashAlgorithm = function() { - var primaryUser = this.getPrimaryUser(); - if (primaryUser && primaryUser.selfCertificate.preferredHashAlgorithms) { - return primaryUser.selfCertificate.preferredHashAlgorithms[0]; - } - return config.prefer_hash_algorithm; -}; - -function isValidEncryptionKeyPacket(keyPacket, signature) { +function isValidEncryptionKeyPacket(keyPacket, signature, allowExpired=false) { return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.dsa) && keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_sign) && + keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdsa) && + keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.eddsa) && (!signature.keyFlags || (signature.keyFlags[0] & enums.keyFlags.encrypt_communication) !== 0 || - (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0); + (signature.keyFlags[0] & enums.keyFlags.encrypt_storage) !== 0) && + (allowExpired || (!signature.isExpired() && + // check expiration time of V3 key packet + !(keyPacket.version === 3 && keyPacket.expirationTimeV3 !== 0 && + Date.now() > (keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000)) && + // check expiration time of V4 key packet + !(keyPacket.version === 4 && signature.keyNeverExpires === false && + Date.now() > (keyPacket.created.getTime() + signature.keyExpirationTime*1000)))); } -function isValidSigningKeyPacket(keyPacket, signature) { - return (keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.dsa) || - keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.rsa_sign) || - keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.rsa_encrypt_sign) || - keyPacket.algorithm === enums.read(enums.publicKey, enums.publicKey.ecdsa)) && +function isValidSigningKeyPacket(keyPacket, signature, allowExpired=false) { + return keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.rsa_encrypt) && + keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.elgamal) && + keyPacket.algorithm !== enums.read(enums.publicKey, enums.publicKey.ecdh) && (!signature.keyFlags || - (signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0); + (signature.keyFlags[0] & enums.keyFlags.sign_data) !== 0) && + (allowExpired || (!signature.isExpired() && + // check expiration time of V3 key packet + !(keyPacket.version === 3 && keyPacket.expirationTimeV3 !== 0 && + Date.now() > (keyPacket.created.getTime() + keyPacket.expirationTimeV3*24*3600*1000)) && + // check expiration time of V4 key packet + !(keyPacket.version === 4 && signature.keyNeverExpires === false && + Date.now() > (keyPacket.created.getTime() + signature.keyExpirationTime*1000)))); + } /** - * Returns the first valid encryption key packet for this key + * Returns first key packet or key packet by given keyId that is available for encryption or decryption + * @param {module:type/keyid} keyId, optional * @returns {(module:packet/public_subkey|module:packet/secret_subkey|module:packet/secret_key|module:packet/public_key|null)} key packet or null if no encryption key has been found */ -Key.prototype.getEncryptionKeyPacket = function() { +Key.prototype.getEncryptionKeyPacket = function(keyId) { // V4: by convention subkeys are prefered for encryption service // V3: keys MUST NOT have subkeys if (this.subKeys) { for (var i = 0; i < this.subKeys.length; i++) { - if (this.subKeys[i].isValidEncryptionKey(this.primaryKey)) { - return this.subKeys[i].subKey; + if (!keyId || this.subKeys[i].subKey.getKeyId().equals(keyId)) { + for (var j = 0; j < this.subKeys[i].bindingSignatures.length; j++) { + if (isValidEncryptionKeyPacket( + this.subKeys[i].subKey, this.subKeys[i].bindingSignatures[j])) { + return this.subKeys[i].subKey; + } + } } } } // if no valid subkey for encryption, evaluate primary key var primaryUser = this.getPrimaryUser(); - if (primaryUser && primaryUser.selfCertificate && !primaryUser.selfCertificate.isExpired() && + if (primaryUser && (!keyId || this.primaryKey.getKeyId().equals(keyId)) && isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate)) { return this.primaryKey; } @@ -436,11 +449,12 @@ Key.prototype.decryptKeyPacket = function(keyIds, passphrase) { * @param {Boolean} allowExpired allows signature verification with expired keys * @return {module:enums.keyStatus} The status of the primary key */ -Key.prototype.verifyPrimaryKey = function(allowExpired=false) { +Key.prototype.verifyPrimaryKey = async function(allowExpired=false) { + // TODO clarify OpenPGP's behavior given an expired revocation signature // check revocation signature if (this.revocationSignature && !this.revocationSignature.isExpired() && (this.revocationSignature.verified || - this.revocationSignature.verify(this.primaryKey, {key: this.primaryKey}))) { + await this.revocationSignature.verify(this.primaryKey, {key: this.primaryKey}))) { return enums.keyStatus.revoked; } // check V3 expiration time @@ -450,17 +464,12 @@ Key.prototype.verifyPrimaryKey = function(allowExpired=false) { } // check for at least one self signature. Self signature of user ID not mandatory // See {@link http://tools.ietf.org/html/rfc4880#section-11.1} - var selfSigned = false; - for (var i = 0; i < this.users.length; i++) { - if (this.users[i].userId && this.users[i].selfCertifications) { - selfSigned = true; - } - } - if (!selfSigned) { + if (!this.users.some(user => user.userId && user.selfCertifications)) { return enums.keyStatus.no_self_cert; } // check for valid self signature - var primaryUser = this.getPrimaryUser(); + await this.verifyPrimaryUser(); + var primaryUser = this.getPrimaryUser(allowExpired); if (!primaryUser) { return enums.keyStatus.invalid; } @@ -510,36 +519,30 @@ function getExpirationTime(keyPacket, selfCertificate) { * @return {{user: Array, selfCertificate: Array}|null} The primary user and the self signature */ Key.prototype.getPrimaryUser = function(allowExpired=false) { - var primUser = []; + var primaryUsers = []; for (var i = 0; i < this.users.length; i++) { + // here we only check the primary user ID, ignoring the primary user attribute if (!this.users[i].userId || !this.users[i].selfCertifications) { continue; } for (var j = 0; j < this.users[i].selfCertifications.length; j++) { - primUser.push({index: i, user: this.users[i], selfCertificate: this.users[i].selfCertifications[j]}); + // only consider already validated certificates + if (!this.users[i].selfCertifications[j].verified || + this.users[i].selfCertifications[j].revoked || + (this.users[i].selfCertifications[j].isExpired() && !allowExpired)) { + continue; + } + primaryUsers.push( + { index: i, user: this.users[i], selfCertificate: this.users[i].selfCertifications[j] } + ); } } // sort by primary user flag and signature creation time - primUser = primUser.sort(function(a, b) { - if (a.selfCertificate.isPrimaryUserID > b.selfCertificate.isPrimaryUserID) { - return -1; - } else if (a.selfCertificate.isPrimaryUserID < b.selfCertificate.isPrimaryUserID) { - return 1; - } else if (a.selfCertificate.created > b.selfCertificate.created) { - return -1; - } else if (a.selfCertificate.created < b.selfCertificate.created) { - return 1; - } else { - return 0; - } + primaryUsers = primaryUsers.sort(function(a, b) { + var A = a.selfCertificate, B = b.selfCertificate; + return A.isPrimaryUserID < B.isPrimaryUserID || A.created < B.created; }); - // return first valid - for (var k = 0; k < primUser.length; k++) { - if (primUser[k].user.isValidSelfCertificate(this.primaryKey, primUser[k].selfCertificate, allowExpired)) { - return primUser[k]; - } - } - return null; + return primaryUsers.pop(); }; /** @@ -550,9 +553,9 @@ Key.prototype.getPrimaryUser = function(allowExpired=false) { * the destination key is tranformed to a private key. * @param {module:key~Key} key source key to merge */ -Key.prototype.update = function(key) { +Key.prototype.update = async function(key) { var that = this; - if (key.verifyPrimaryKey() === enums.keyStatus.invalid) { + if (await key.verifyPrimaryKey() === enums.keyStatus.invalid) { return; } if (this.primaryKey.getFingerprint() !== key.primaryKey.getFingerprint()) { @@ -571,44 +574,45 @@ Key.prototype.update = function(key) { } this.primaryKey = key.primaryKey; } + // TODO clarify OpenPGP's behavior given an expired revocation signature // revocation signature if (!this.revocationSignature && key.revocationSignature && !key.revocationSignature.isExpired() && (key.revocationSignature.verified || - key.revocationSignature.verify(key.primaryKey, {key: key.primaryKey}))) { + await key.revocationSignature.verify(key.primaryKey, {key: key.primaryKey}))) { this.revocationSignature = key.revocationSignature; } // direct signatures - mergeSignatures(key, this, 'directSignatures'); + await mergeSignatures(key, this, 'directSignatures'); + // TODO replace when Promise.some or Promise.any are implemented // users - key.users.forEach(function(srcUser) { + await Promise.all(key.users.map(async function(srcUser) { var found = false; - for (var i = 0; i < that.users.length; i++) { - if ((srcUser.userId && (srcUser.userId.userid === that.users[i].userId.userid)) || - (srcUser.userAttribute && (srcUser.userAttribute.equals(that.users[i].userAttribute)))) { - that.users[i].update(srcUser, that.primaryKey); + await Promise.all(that.users.map(async function(dstUser) { + if ((srcUser.userId && (srcUser.userId.userid === dstUser.userId.userid)) || + (srcUser.userAttribute && (srcUser.userAttribute.equals(dstUser.userAttribute)))) { + await dstUser.update(srcUser, that.primaryKey); found = true; - break; } - } + })); if (!found) { that.users.push(srcUser); } - }); + })); + // TODO replace when Promise.some or Promise.any are implemented // subkeys if (key.subKeys) { - key.subKeys.forEach(function(srcSubKey) { + await Promise.all(key.subKeys.map(async function(srcSubKey) { var found = false; - for (var i = 0; i < that.subKeys.length; i++) { - if (srcSubKey.subKey.getFingerprint() === that.subKeys[i].subKey.getFingerprint()) { - that.subKeys[i].update(srcSubKey, that.primaryKey); + await Promise.all(that.subKeys.map(async function(dstSubKey) { + if (srcSubKey.subKey.getFingerprint() === dstSubKey.subKey.getFingerprint()) { + await dstSubKey.update(srcSubKey, that.primaryKey); found = true; - break; } - } + })); if (!found) { that.subKeys.push(srcSubKey); } - }); + })); } }; @@ -620,20 +624,20 @@ Key.prototype.update = function(key) { * @param {String} attr * @param {Function} checkFn optional, signature only merged if true */ -function mergeSignatures(source, dest, attr, checkFn) { +async function mergeSignatures(source, dest, attr, checkFn) { source = source[attr]; if (source) { if (!dest[attr]) { dest[attr] = source; } else { - source.forEach(function(sourceSig) { - if (!sourceSig.isExpired() && (!checkFn || checkFn(sourceSig)) && + await Promise.all(source.map(async function(sourceSig) { + if (!sourceSig.isExpired() && (!checkFn || await checkFn(sourceSig)) && !dest[attr].some(function(destSig) { return util.equalsUint8Array(destSig.signature,sourceSig.signature); })) { dest[attr].push(sourceSig); } - }); + })); } } } @@ -648,12 +652,13 @@ Key.prototype.revoke = function() { * @param {Array} privateKey decrypted private keys for signing * @return {module:key~Key} new public key with new certificate signature */ -Key.prototype.signPrimaryUser = function(privateKeys) { +Key.prototype.signPrimaryUser = async function(privateKeys) { + await this.verifyPrimaryUser(); var {index, user} = this.getPrimaryUser() || {}; if (!user) { throw new Error('Could not find primary user'); } - user = user.sign(this.primaryKey, privateKeys); + user = await user.sign(this.primaryKey, privateKeys); var key = new Key(this.toPacketlist()); key.users[index] = user; return key; @@ -664,41 +669,77 @@ Key.prototype.signPrimaryUser = function(privateKeys) { * @param {Array} privateKeys decrypted private keys for signing * @return {module:key~Key} new public key with new certificate signature */ -Key.prototype.signAllUsers = function(privateKeys) { - var users = this.users.map(user => user.sign(this.primaryKey, privateKeys)); +Key.prototype.signAllUsers = async function(privateKeys) { + var that = this; var key = new Key(this.toPacketlist()); - key.users = users; + key.users = await Promise.all(this.users.map(function(user) { + return user.sign(that.primaryKey, privateKeys); + })); return key; }; /** * Verifies primary user of key + * - if no arguments are given, verifies the self certificates; + * - otherwise, verifies all certificates signed with given keys. * @param {Array} keys array of keys to verify certificate signatures * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ -Key.prototype.verifyPrimaryUser = function(keys) { - var {user} = this.getPrimaryUser() || {}; - if (!user) { - throw new Error('Could not find primary user'); - } - return user.verifyAllSignatures(this.primaryKey, keys); +Key.prototype.verifyPrimaryUser = async function(keys) { + var primaryKey = this.primaryKey, primaryUsers = []; + var lastCreated = null, lastPrimaryUserID = null; + await Promise.all(this.users.map(async function(user) { + // here we verify both the primary user ID or the primary user attribute + if (!(user.userId || user.userAttribute) || !user.selfCertifications) { + return; + } + var dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey }; + await Promise.all(user.selfCertifications.map(async function(selfCertification) { + // skip if certificate is not the most recent + if ((selfCertification.isPrimaryUserID && + selfCertification.isPrimaryUserID < lastPrimaryUserID) || + (!lastPrimaryUserID && selfCertification.created < lastCreated)) { + return; + } + // skip if certificates is not valid + if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify)) || + (selfCertification.revoked || await user.isRevoked(primaryKey, selfCertification)) || + selfCertification.isExpired()) { + return; + } + lastPrimaryUserID = selfCertification.isPrimaryUserID; + lastCreated = selfCertification.created; + primaryUsers.push(user); + })); + })); + var user = primaryUsers.pop(); + var results = !user ? [] : keys ? await user.verifyAllCertifications(primaryKey, keys) : + [{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey) === enums.keyStatus.valid }]; + return results; }; /** * Verifies all users of key + * - if no arguments are given, verifies the self certificates; + * - otherwise, verifies all certificates signed with given keys. * @param {Array} keys array of keys to verify certificate signatures * @return {Array<({userid: String, keyid: module:type/keyid, valid: Boolean})>} list of userid, signer's keyid and validity of signature */ -Key.prototype.verifyAllUsers = function(keys) { - return this.users.reduce((signatures, user) => { - return signatures.concat( - user.verifyAllSignatures(this.primaryKey, keys).map(signature => ({ +Key.prototype.verifyAllUsers = async function(keys) { + var results = []; + var primaryKey = this.primaryKey; + await Promise.all(this.users.map(async function(user) { + var signatures = keys ? await user.verifyAllCertifications(primaryKey, keys) : + [{ keyid: primaryKey.keyid, valid: await user.verify(primaryKey) === enums.keyStatus.valid }]; + signatures.forEach(signature => { + results.push({ userid: user.userId.userid, keyid: signature.keyid, valid: signature.valid - })) - ); - }, []); + }); + }); + })); + return results; }; /** @@ -730,102 +771,112 @@ User.prototype.toPacketlist = function() { }; /** - * Checks if a self signature of the user is revoked - * @param {module:packet/signature} certificate + * Checks if a self certificate of the user is revoked * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet - * @return {Boolean} True if the certificate is revoked + * @param {module:packet/signature} certificate The certificate to verify + * @param {module:packet/public_subkey|module:packet/public_key| + * module:packet/secret_subkey|module:packet/secret_key} key, optional The key to verify the signature + * @return {Boolean} True if the certificate is revoked */ -User.prototype.isRevoked = function(certificate, primaryKey) { +User.prototype.isRevoked = async function(primaryKey, certificate, key) { if (this.revocationCertifications) { - var that = this; - return this.revocationCertifications.some(function(revCert) { + var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; + // TODO clarify OpenPGP's behavior given an expired revocation signature + var results = await Promise.all(this.revocationCertifications.map(async function(revCert) { return revCert.issuerKeyId.equals(certificate.issuerKeyId) && - !revCert.isExpired() && - (revCert.verified || - revCert.verify(primaryKey, {userid: that.userId || that.userAttribute, key: primaryKey})); - }); + !revCert.isExpired() && + (revCert.verified || revCert.verify(key ? key : primaryKey, dataToVerify)); + })); + certificate.revoked = results.some(result => result === true); + return certificate.revoked; } else { return false; } }; -/** - * Returns true if the self certificate is valid - * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet - * @param {module:packet/signature} selfCertificate A self certificate of this user - * @param {Boolean} allowExpired allows signature verification with expired keys - * @return {Boolean} - */ -User.prototype.isValidSelfCertificate = function(primaryKey, selfCertificate, allowExpired=false) { - if (this.isRevoked(selfCertificate, primaryKey)) { - return false; - } - if ((!selfCertificate.isExpired() || allowExpired) && - (selfCertificate.verified || - selfCertificate.verify(primaryKey, {userid: this.userId || this.userAttribute, key: primaryKey}))) { - return true; - } - return false; -}; - /** * Signs user * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet * @param {Array} privateKeys decrypted private keys for signing * @return {module:key~Key} new user with new certificate signatures */ -User.prototype.sign = function(primaryKey, privateKeys) { - var user, dataToSign, signingKeyPacket, signaturePacket; - dataToSign = {}; - dataToSign.key = primaryKey; - dataToSign.userid = this.userId || this.userAttribute; - user = new User(this.userId || this.userAttribute); - user.otherCertifications = []; - privateKeys.forEach(privateKey => { +User.prototype.sign = async function(primaryKey, privateKeys) { + const dataToSign = { userid: this.userId || this.userAttribute, key: primaryKey }; + const user = new User(dataToSign.userid); + user.otherCertifications = await Promise.all(privateKeys.map(async function(privateKey) { if (privateKey.isPublic()) { throw new Error('Need private key for signing'); } if (privateKey.primaryKey.getFingerprint() === primaryKey.getFingerprint()) { throw new Error('Not implemented for self signing'); } - signingKeyPacket = privateKey.getSigningKeyPacket(); + await privateKey.verifyPrimaryUser(); + const signingKeyPacket = privateKey.getSigningKeyPacket(); if (!signingKeyPacket) { - throw new Error('Could not find valid signing key packet'); + throw new Error('Could not find valid signing key packet in key ' + + privateKey.primaryKey.getKeyId().toHex()); } if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } - signaturePacket = new packet.Signature(); + const signaturePacket = new packet.Signature(); // Most OpenPGP implementations use generic certification (0x10) signaturePacket.signatureType = enums.write(enums.signature, enums.signature.cert_generic); signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; - signaturePacket.hashAlgorithm = privateKey.getPreferredHashAlgorithm(); signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; + signaturePacket.hashAlgorithm = getPreferredHashAlgorithm(privateKey); signaturePacket.signingKeyId = signingKeyPacket.getKeyId(); signaturePacket.sign(signingKeyPacket, dataToSign); - user.otherCertifications.push(signaturePacket); - }); - user.update(this, primaryKey); + return signaturePacket; + })); + await user.update(this, primaryKey); return user; }; /** - * Verifies all user signatures + * Verifies the user certificate + * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet + * @param {module:packet/signature} certificate A certificate of this user + * @param {Array} keys array of keys to verify certificate signatures + * @param {Boolean} allowExpired allows signature verification with expired keys + * @return {module:enums.keyStatus} status of the certificate + */ +User.prototype.verifyCertificate = async function(primaryKey, certificate, keys, allowExpired=false) { + var that = this; + var keyid = certificate.issuerKeyId; + var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; + var results = await Promise.all(keys.map(async function(key) { + if (!key.getKeyIds().some(id => id.equals(keyid))) { return; } + await key.verifyPrimaryUser(); + var keyPacket = key.getSigningKeyPacket(keyid); + if (certificate.revoked || await that.isRevoked(primaryKey, certificate, keyPacket)) { + return enums.keyStatus.revoked; + } + if (!(certificate.verified || await certificate.verify(keyPacket, dataToVerify))) { + return enums.keyStatus.invalid; + } + if (certificate.isExpired()) { + return enums.keyStatus.expired; + } + return enums.keyStatus.valid; + })); + return results.find(result => result !== undefined); +}; + +/** + * Verifies all user certificates * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet * @param {Array} keys array of keys to verify certificate signatures * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ -User.prototype.verifyAllSignatures = function(primaryKey, keys) { - var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; - var certificates = this.selfCertifications.concat(this.otherCertifications || []); - return certificates.map(signaturePacket => { - var keyPackets = keys.filter(key => key.getSigningKeyPacket(signaturePacket.issuerKeyId)); - var valid = null; - if (keyPackets.length > 0) { - valid = keyPackets.some(keyPacket => signaturePacket.verify(keyPacket.primaryKey, dataToVerify)); - } - return { keyid: signaturePacket.issuerKeyId, valid: valid }; - }); +User.prototype.verifyAllCertifications = async function(primaryKey, keys) { + var that = this; + var certifications = this.selfCertifications.concat(this.otherCertifications || []); + return Promise.all(certifications.map(async function(certification) { + var status = await that.verifyCertificate(primaryKey, certification, keys); + return { keyid: certification.issuerKeyId, + valid: status === undefined ? null : status === enums.keyStatus.valid }; + })); }; /** @@ -834,29 +885,28 @@ User.prototype.verifyAllSignatures = function(primaryKey, keys) { * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet * @return {module:enums.keyStatus} status of user */ -User.prototype.verify = function(primaryKey) { +User.prototype.verify = async function(primaryKey) { if (!this.selfCertifications) { return enums.keyStatus.no_self_cert; } - var status; - for (var i = 0; i < this.selfCertifications.length; i++) { - if (this.isRevoked(this.selfCertifications[i], primaryKey)) { - status = enums.keyStatus.revoked; - continue; - } - if (!(this.selfCertifications[i].verified || - this.selfCertifications[i].verify(primaryKey, {userid: this.userId || this.userAttribute, key: primaryKey}))) { - status = enums.keyStatus.invalid; - continue; - } - if (this.selfCertifications[i].isExpired()) { - status = enums.keyStatus.expired; - continue; - } - status = enums.keyStatus.valid; - break; - } - return status; + var that = this; + var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; + // TODO replace when Promise.some or Promise.any are implemented + var results = [enums.keyStatus.invalid].concat( + await Promise.all(this.selfCertifications.map(async function(selfCertification, i) { + if (selfCertification.revoked || await that.isRevoked(primaryKey, selfCertification)) { + return enums.keyStatus.revoked; + } + if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify))) { + return enums.keyStatus.invalid; + } + if (selfCertification.isExpired()) { + return enums.keyStatus.expired; + } + return enums.keyStatus.valid; + }))); + return results.some(status => status === enums.keyStatus.valid)? + enums.keyStatus.valid : results.pop(); }; /** @@ -864,17 +914,16 @@ User.prototype.verify = function(primaryKey) { * @param {module:key~User} user source user to merge * @param {module:packet/signature} primaryKey primary key used for validation */ -User.prototype.update = function(user, primaryKey) { - var that = this; +User.prototype.update = async function(user, primaryKey) { + var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; // self signatures - mergeSignatures(user, this, 'selfCertifications', function(srcSelfSig) { - return srcSelfSig.verified || - srcSelfSig.verify(primaryKey, {userid: that.userId || that.userAttribute, key: primaryKey}); + await mergeSignatures(user, this, 'selfCertifications', async function(srcSelfSig) { + return srcSelfSig.verified || srcSelfSig.verify(primaryKey, dataToVerify); }); // other signatures - mergeSignatures(user, this, 'otherCertifications'); + await mergeSignatures(user, this, 'otherCertifications'); // revocation signatures - mergeSignatures(user, this, 'revocationCertifications'); + await mergeSignatures(user, this, 'revocationCertifications'); }; /** @@ -909,8 +958,8 @@ SubKey.prototype.toPacketlist = function() { * @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet * @return {Boolean} */ -SubKey.prototype.isValidEncryptionKey = function(primaryKey) { - if(this.verify(primaryKey) !== enums.keyStatus.valid) { +SubKey.prototype.isValidEncryptionKey = async function(primaryKey) { + if(await this.verify(primaryKey) !== enums.keyStatus.valid) { return false; } for(var i = 0; i < this.bindingSignatures.length; i++) { @@ -927,12 +976,12 @@ SubKey.prototype.isValidEncryptionKey = function(primaryKey) { * @param {Boolean} allowExpired allows signature verification with expired keys * @return {Boolean} */ -SubKey.prototype.isValidSigningKey = function(primaryKey, allowExpired=false) { - if(this.verify(primaryKey, allowExpired) !== enums.keyStatus.valid) { +SubKey.prototype.isValidSigningKey = async function(primaryKey, allowExpired=false) { + if(await this.verify(primaryKey, allowExpired) !== enums.keyStatus.valid) { return false; } for(var i = 0; i < this.bindingSignatures.length; i++) { - if(isValidSigningKeyPacket(this.subKey, this.bindingSignatures[i])) { + if(isValidSigningKeyPacket(this.subKey, this.bindingSignatures[i], allowExpired)) { return true; } } @@ -946,11 +995,13 @@ SubKey.prototype.isValidSigningKey = function(primaryKey, allowExpired=false) { * @param {Boolean} allowExpired allows signature verification with expired keys * @return {module:enums.keyStatus} The status of the subkey */ -SubKey.prototype.verify = function(primaryKey, allowExpired=false) { +SubKey.prototype.verify = async function(primaryKey, allowExpired=false) { + var that = this; + // TODO clarify OpenPGP's behavior given an expired revocation signature // check subkey revocation signature if (this.revocationSignature && !this.revocationSignature.isExpired() && (this.revocationSignature.verified || - this.revocationSignature.verify(primaryKey, {key:primaryKey, bind: this.subKey}))) { + await this.revocationSignature.verify(primaryKey, {key:primaryKey, bind: this.subKey}))) { return enums.keyStatus.revoked; } // check V3 expiration time @@ -959,38 +1010,29 @@ SubKey.prototype.verify = function(primaryKey, allowExpired=false) { return enums.keyStatus.expired; } // check subkey binding signatures (at least one valid binding sig needed) - for(var i = 0; i < this.bindingSignatures.length; i++) { - var isLast = (i === this.bindingSignatures.length - 1); - var sig = this.bindingSignatures[i]; + // TODO replace when Promise.some or Promise.any are implemented + var results = [enums.keyStatus.invalid].concat( + await Promise.all(this.bindingSignatures.map(async function(bindingSignature) { // check binding signature is not expired - if(!allowExpired && sig.isExpired()) { - if(isLast) { - return enums.keyStatus.expired; // last expired binding signature - } else { - continue; - } + if(!allowExpired && bindingSignature.isExpired()) { + return enums.keyStatus.expired; // last expired binding signature } // check binding signature can verify - if (!(sig.verified || sig.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) { - if(isLast) { - return enums.keyStatus.invalid; // last invalid binding signature - } else { - continue; - } + if (!(bindingSignature.verified || + await bindingSignature.verify(primaryKey, {key: primaryKey, bind: that.subKey}))) { + return enums.keyStatus.invalid; // last invalid binding signature } // check V4 expiration time - if (this.subKey.version === 4) { - if(!allowExpired && sig.keyNeverExpires === false && Date.now() > (this.subKey.created.getTime() + sig.keyExpirationTime*1000)) { - if(isLast) { - return enums.keyStatus.expired; // last V4 expired binding signature - } else { - continue; - } + if (that.subKey.version === 4) { + if(!allowExpired && bindingSignature.keyNeverExpires === false && + Date.now() > (that.subKey.created.getTime() + bindingSignature.keyExpirationTime*1000)) { + return enums.keyStatus.expired; // last V4 expired binding signature } } return enums.keyStatus.valid; // found a binding signature that passed all checks - } - return enums.keyStatus.invalid; // no binding signatures to check + }))); + return results.some(status => status === enums.keyStatus.valid) ? + enums.keyStatus.valid : results.pop(); }; /** @@ -1016,8 +1058,8 @@ SubKey.prototype.getExpirationTime = function() { * @param {module:key~SubKey} subKey source subkey to merge * @param {module:packet/signature} primaryKey primary key used for validation */ -SubKey.prototype.update = function(subKey, primaryKey) { - if (subKey.verify(primaryKey) === enums.keyStatus.invalid) { +SubKey.prototype.update = async function(subKey, primaryKey) { + if (await subKey.verify(primaryKey) === enums.keyStatus.invalid) { return; } if (this.subKey.getFingerprint() !== subKey.subKey.getFingerprint()) { @@ -1029,18 +1071,28 @@ SubKey.prototype.update = function(subKey, primaryKey) { this.subKey = subKey.subKey; } // update missing binding signatures - if(this.bindingSignatures.length < subKey.bindingSignatures.length) { - for(var i = this.bindingSignatures.length; i < subKey.bindingSignatures.length; i++) { - var newSig = subKey.bindingSignatures[i]; - if (newSig.verified || newSig.verify(primaryKey, {key: primaryKey, bind: this.subKey})) { - this.bindingSignatures.push(newSig); + var that = this; + await Promise.all(subKey.bindingSignatures.map(async function(newBindingSignature) { + if (newBindingSignature.verified || + await newBindingSignature.verify(primaryKey, {key: primaryKey, bind: that.subKey })) { + for (var i = 0; i < that.bindingSignatures.length; i++) { + if (that.bindingSignatures[i].issuerKeyId.equals(newBindingSignature.issuerKeyId)) { + that.bindingSignatures[i] = newBindingSignature; + return; + } } + that.bindingSignatures.push(newBindingSignature); } - } + })); + // TODO clarify OpenPGP's behavior given an expired revocation signature // revocation signature - if (!this.revocationSignature && subKey.revocationSignature && !subKey.revocationSignature.isExpired() && - (subKey.revocationSignature.verified || - subKey.revocationSignature.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) { + if (!this.revocationSignature && + subKey.revocationSignature && + !subKey.revocationSignature.isExpired() && + (subKey.revocationSignature.verified || + await subKey.revocationSignature.verify( + primaryKey, {key: primaryKey, bind: this.subKey} + ))) { this.revocationSignature = subKey.revocationSignature; } }; @@ -1099,7 +1151,7 @@ export function readArmored(armoredText) { } /** - * Generates a new OpenPGP key. Currently only supports RSA keys. + * Generates a new OpenPGP key. Supports RSA and ECC keys. * Primary and subkey will be of same type. * @param {module:enums.publicKey} [options.keyType=module:enums.publicKey.rsa_encrypt_sign] to indicate what type of key to make. * RSA is 1. See {@link http://tools.ietf.org/html/rfc4880#section-9.1} @@ -1117,20 +1169,34 @@ export function generate(options) { return Promise.resolve().then(() => { if (options.curve) { - options.keyType = options.keyType || enums.publicKey.ecdsa; + try { + options.curve = enums.write(enums.curve, options.curve); + } catch (e) { + throw new Error('Not valid curve.') + } + if (options.curve === enums.curve.ed25519 || options.curve === enums.curve.curve25519) { + options.keyType = options.keyType || enums.publicKey.eddsa; + } else { + options.keyType = options.keyType || enums.publicKey.ecdsa; + } options.subkeyType = options.subkeyType || enums.publicKey.ecdh; - } else { + } else if (options.numBits) { options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign; options.subkeyType = options.subkeyType || enums.publicKey.rsa_encrypt_sign; + } else { + throw new Error('Key type not specified.'); } if (options.keyType !== enums.publicKey.rsa_encrypt_sign && - options.keyType !== enums.publicKey.ecdsa) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated + options.keyType !== enums.publicKey.ecdsa && + options.keyType !== enums.publicKey.eddsa) { + // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated throw new Error('Unsupported key type'); } if (options.subkeyType !== enums.publicKey.rsa_encrypt_sign && - options.subkeyType !== enums.publicKey.ecdh) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated + options.subkeyType !== enums.publicKey.ecdh) { + // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated throw new Error('Unsupported subkey type'); } @@ -1148,13 +1214,17 @@ export function generate(options) { function generateSecretKey() { secretKeyPacket = new packet.SecretKey(); + secretKeyPacket.packets = null; secretKeyPacket.algorithm = enums.read(enums.publicKey, options.keyType); + options.curve = options.curve === enums.curve.curve25519 ? enums.curve.ed25519 : options.curve; return secretKeyPacket.generate(options.numBits, options.curve); } function generateSecretSubkey() { secretSubkeyPacket = new packet.SecretSubkey(); + secretKeyPacket.packets = null; secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.subkeyType); + options.curve = options.curve === enums.curve.ed25519 ? enums.curve.curve25519 : options.curve; return secretSubkeyPacket.generate(options.numBits, options.curve); } } @@ -1204,7 +1274,7 @@ export function reformat(options) { }); } -function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { +async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { // set passphrase protection if (options.passphrase) { secretKeyPacket.encrypt(options.passphrase); @@ -1215,7 +1285,7 @@ function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { packetlist.push(secretKeyPacket); - options.userIds.forEach(function(userId, index) { + await Promise.all(options.userIds.map(async function(userId, index) { var userIdPacket = new packet.Userid(); userIdPacket.read(util.str2Uint8Array(userId)); @@ -1226,7 +1296,7 @@ function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { var signaturePacket = new packet.Signature(); signaturePacket.signatureType = enums.signature.cert_generic; signaturePacket.publicKeyAlgorithm = options.keyType; - signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; + signaturePacket.hashAlgorithm = getPreferredHashAlgorithm(secretKeyPacket); signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; signaturePacket.preferredSymmetricAlgorithms = []; // prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support) @@ -1254,11 +1324,14 @@ function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { signaturePacket.keyExpirationTime = options.keyExpirationTime; signaturePacket.keyNeverExpires = false; } - signaturePacket.sign(secretKeyPacket, dataToSign); - - packetlist.push(userIdPacket); - packetlist.push(signaturePacket); + await signaturePacket.sign(secretKeyPacket, dataToSign); + return {userIdPacket, signaturePacket}; + })).then(list => { + list.forEach(({userIdPacket, signaturePacket}) => { + packetlist.push(userIdPacket); + packetlist.push(signaturePacket); + }); }); var dataToSign = {}; @@ -1267,13 +1340,13 @@ function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { var subkeySignaturePacket = new packet.Signature(); subkeySignaturePacket.signatureType = enums.signature.subkey_binding; subkeySignaturePacket.publicKeyAlgorithm = options.keyType; - subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm; + subkeySignaturePacket.hashAlgorithm = getPreferredHashAlgorithm(secretSubkeyPacket); subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage]; if (options.keyExpirationTime > 0) { subkeySignaturePacket.keyExpirationTime = options.keyExpirationTime; subkeySignaturePacket.keyNeverExpires = false; } - subkeySignaturePacket.sign(secretKeyPacket, dataToSign); + await subkeySignaturePacket.sign(secretKeyPacket, dataToSign); packetlist.push(secretSubkeyPacket); packetlist.push(subkeySignaturePacket); @@ -1286,6 +1359,39 @@ function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { return new Key(packetlist); } +/** + * Returns the preferred signature hash algorithm of a key + * @param {object} key + * @return {String} + */ +export function getPreferredHashAlgorithm(key) { + var hash_algo = config.prefer_hash_algorithm, + pref_algo = hash_algo; + if (Key.prototype.isPrototypeOf(key)) { + var primaryUser = key.getPrimaryUser(); + if (primaryUser && primaryUser.selfCertificate.preferredHashAlgorithms) { + pref_algo = primaryUser.selfCertificate.preferredHashAlgorithms[0]; + hash_algo = crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ? + pref_algo : hash_algo; + } + key = key.getSigningKeyPacket(); + } + switch(Object.getPrototypeOf(key)) { + case packet.SecretKey.prototype: + case packet.PublicKey.prototype: + case packet.SecretSubkey.prototype: + case packet.PublicSubkey.prototype: + switch(key.algorithm) { + case 'ecdh': + case 'ecdsa': + case 'eddsa': + pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgorithm(key.params[0]); + } + } + return crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ? + pref_algo : hash_algo; +} + /** * Returns the preferred symmetric algorithm for a set of keys * @param {Array} keys Set of keys diff --git a/src/keyring/keyring.js b/src/keyring/keyring.js index 97e8975f..11fd8d5e 100644 --- a/src/keyring/keyring.js +++ b/src/keyring/keyring.js @@ -183,12 +183,12 @@ KeyArray.prototype.getForId = function (keyId, deep) { KeyArray.prototype.importKey = function (armored) { var imported = keyModule.readArmored(armored); var that = this; - imported.keys.forEach(function(key) { + imported.keys.forEach(async function(key) { // check if key already in key array var keyidHex = key.primaryKey.getKeyId().toHex(); var keyFound = that.getForId(keyidHex); if (keyFound) { - keyFound.update(key); + await keyFound.update(key); } else { that.push(key); } diff --git a/src/message.js b/src/message.js index 87f01b52..bcf31282 100644 --- a/src/message.js +++ b/src/message.js @@ -312,7 +312,7 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) { * @param {Signature} signature (optional) any existing detached signature to add to the message * @return {module:message~Message} new message with signed content */ -Message.prototype.sign = function(privateKeys=[], signature=null) { +Message.prototype.sign = async function(privateKeys=[], signature=null) { var packetlist = new packet.List(); @@ -349,7 +349,7 @@ Message.prototype.sign = function(privateKeys=[], signature=null) { } onePassSig = new packet.OnePassSignature(); onePassSig.type = signatureType; - //TODO get preferred hashg algo from key signature + //TODO get preferred hash algo from key signature onePassSig.hashAlgorithm = config.prefer_hash_algorithm; signingKeyPacket = privateKeys[i].getSigningKeyPacket(); if (!signingKeyPacket) { @@ -365,17 +365,20 @@ Message.prototype.sign = function(privateKeys=[], signature=null) { packetlist.push(literalDataPacket); - for (i = privateKeys.length - 1; i >= 0; i--) { + // FIXME does the order matter here? It used to be n-1..0 + await Promise.all(privateKeys.map(async function(privateKey) { + var signingKeyPacket = privateKey.getSigningKeyPacket(); var signaturePacket = new packet.Signature(); signaturePacket.signatureType = signatureType; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; + // FIXME FIXME were we just signing with the last key? signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } - signaturePacket.sign(signingKeyPacket, literalDataPacket); + await signaturePacket.sign(signingKeyPacket, literalDataPacket); packetlist.push(signaturePacket); - } + })); if (signature) { packetlist.concat(existingSigPacketlist); @@ -390,7 +393,7 @@ Message.prototype.sign = function(privateKeys=[], signature=null) { * @param {Signature} signature (optional) any existing detached signature * @return {module:signature~Signature} new detached signature of message content */ -Message.prototype.signDetached = function(privateKeys=[], signature=null) { +Message.prototype.signDetached = async function(privateKeys=[], signature=null) { var packetlist = new packet.List(); @@ -403,8 +406,8 @@ Message.prototype.signDetached = function(privateKeys=[], signature=null) { var signatureType = literalFormat === enums.literal.binary ? enums.signature.binary : enums.signature.text; - for (var i = 0; i < privateKeys.length; i++) { - var signingKeyPacket = privateKeys[i].getSigningKeyPacket(); + await Promise.all(privateKeys.map(async function(privateKey) { + var signingKeyPacket = privateKey.getSigningKeyPacket(); var signaturePacket = new packet.Signature(); signaturePacket.signatureType = signatureType; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; @@ -412,9 +415,10 @@ Message.prototype.signDetached = function(privateKeys=[], signature=null) { if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } - signaturePacket.sign(signingKeyPacket, literalDataPacket); + await signaturePacket.sign(signingKeyPacket, literalDataPacket); packetlist.push(signaturePacket); - } + })); + if (signature) { var existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature); packetlist.concat(existingSigPacketlist); @@ -489,7 +493,7 @@ function createVerificationObjects(signatureList, literalDataList, keys) { result.push(verifiedSig); } - return result; + return Promise.all(result); } /** diff --git a/src/openpgp.js b/src/openpgp.js index c28228ac..fd3394f1 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -201,7 +201,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, sessionKey, filename, armor, detached, signature, returnSessionKey }); } var result = {}; - return Promise.resolve().then(() => { + return Promise.resolve().then(async function() { let message = createMessage(data, filename); if (!privateKeys) { @@ -209,14 +209,14 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, } if (privateKeys.length || signature) { // sign the message only if private keys or signature is specified if (detached) { - var detachedSignature = message.signDetached(privateKeys, signature); + var detachedSignature = await message.signDetached(privateKeys, signature); if (armor) { result.signature = detachedSignature.armor(); } else { result.signature = detachedSignature; } } else { - message = message.sign(privateKeys, signature); + message = await message.sign(privateKeys, signature); } } return message.encrypt(publicKeys, passwords, sessionKey); @@ -231,6 +231,7 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, sessionKey, result.sessionKey = encrypted.sessionKey; } return result; + }).catch(onError.bind(null, 'Error encrypting message')); } @@ -255,7 +256,7 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format, signature }); } - return message.decrypt(privateKey, sessionKey, password).then(message => { + return message.decrypt(privateKey, sessionKey, password).then(async function(message) { const result = parseMessage(message, format); @@ -264,9 +265,9 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password, } if (signature) { //detached signature - result.signatures = message.verifyDetached(signature, publicKeys); + result.signatures = await message.verifyDetached(signature, publicKeys); } else { - result.signatures = message.verify(publicKeys); + result.signatures = await message.verify(publicKeys); } return result; @@ -302,7 +303,7 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { } var result = {}; - const promise = async function(){ + return Promise.resolve().then(async function() { var message; if (util.isString(data)) { @@ -326,10 +327,9 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { result.message = message; } } - return result; - } - return promise().catch(onError.bind(null, 'Error signing cleartext message')); + + }).catch(onError.bind(null, 'Error signing cleartext message')); } /** @@ -349,13 +349,15 @@ export function verify({ message, publicKeys, signature=null }) { return asyncProxy.delegate('verify', { message, publicKeys, signature }); } - var result = {}; - const promise = async function(){ + return Promise.resolve().then(async function() { + + var result = {}; if (cleartext.CleartextMessage.prototype.isPrototypeOf(message)) { result.data = message.getText(); } else { result.data = message.getLiteralData(); } + if (signature) { //detached signature result.signatures = await message.verifyDetached(signature, publicKeys); @@ -364,8 +366,7 @@ export function verify({ message, publicKeys, signature=null }) { } return result; - } - return promise().catch(onError.bind(null, 'Error verifying cleartext signed message')); + }).catch(onError.bind(null, 'Error verifying cleartext signed message')); } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 925abd3d..4785a6c9 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -102,7 +102,7 @@ PublicKeyEncryptedSessionKey.prototype.write = function () { return util.concatUint8Array(arr); }; -PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { +PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { var data = String.fromCharCode( enums.write(enums.symmetric, this.sessionKeyAlgorithm)); @@ -117,7 +117,7 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = function (key) { toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength())); } - this.encrypted = crypto.publicKeyEncrypt( + this.encrypted = await crypto.publicKeyEncrypt( this.publicKeyAlgorithm, key.params, toEncrypt, diff --git a/src/packet/signature.js b/src/packet/signature.js index c818b900..cce0fd5c 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -211,7 +211,7 @@ Signature.prototype.write = function () { * @param {module:packet/secret_key} key private key used to sign the message. * @param {Object} data Contains packets to be signed. */ -Signature.prototype.sign = function (key, data) { +Signature.prototype.sign = async function (key, data) { var signatureType = enums.write(enums.signature, this.signatureType), publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm), hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm); @@ -243,7 +243,7 @@ Signature.prototype.sign = function (key, data) { this.signedHashValue = hash.subarray(0, 2); - this.signature = crypto.signature.sign(hashAlgorithm, + this.signature = await crypto.signature.sign(hashAlgorithm, publicKeyAlgorithm, key.params, toHash); }; @@ -615,7 +615,7 @@ Signature.prototype.calculateTrailer = function () { * module:packet/secret_subkey|module:packet/secret_key} key the public key to verify the signature * @return {boolean} True if message is verified, else false. */ -Signature.prototype.verify = function (key, data) { +Signature.prototype.verify = async function (key, data) { var signatureType = enums.write(enums.signature, this.signatureType), publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm), hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm); @@ -644,7 +644,7 @@ Signature.prototype.verify = function (key, data) { i += mpi[j].read(this.signature.subarray(i, this.signature.length)); } - this.verified = crypto.signature.verify(publicKeyAlgorithm, + this.verified = await crypto.signature.verify(publicKeyAlgorithm, hashAlgorithm, mpi, key.params, util.concatUint8Array([bytes, this.signatureData, trailer])); diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index c4dda83f..e6c7d8c2 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -2,8 +2,9 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -var chai = require('chai'), - expect = chai.expect; +var chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; describe('API functional testing', function() { var util = openpgp.util; @@ -227,29 +228,39 @@ describe('API functional testing', function() { var data = util.str2Uint8Array("foobar"); describe('Sign and verify', function () { - it('RSA', function (done) { + it('RSA', function () { //Originally we passed public and secret MPI separately, now they are joined. Is this what we want to do long term? // RSA - var RSAsignedData = openpgp.crypto.signature.sign(2, 1, RSApubMPIs.concat(RSAsecMPIs), data); - var RSAsignedDataMPI = new openpgp.MPI(); - RSAsignedDataMPI.read(RSAsignedData); - var success = openpgp.crypto.signature.verify(1, 2, [RSAsignedDataMPI], RSApubMPIs, data); - expect(success).to.be.true; - done(); + return openpgp.crypto.signature.sign( + 2, 1, RSApubMPIs.concat(RSAsecMPIs), data + ).then(RSAsignedData => { + var RSAsignedDataMPI = new openpgp.MPI(); + RSAsignedDataMPI.read(RSAsignedData); + return openpgp.crypto.signature.verify( + 1, 2, [RSAsignedDataMPI], RSApubMPIs, data + ).then(success => { + return expect(success).to.be.true; + }); + }); }); - it('DSA', function (done) { + it('DSA', function () { // DSA - var DSAsignedData = util.Uint8Array2str(openpgp.crypto.signature.sign(2, 17, DSApubMPIs.concat(DSAsecMPIs), data)); - - var DSAmsgMPIs = []; - DSAmsgMPIs[0] = new openpgp.MPI(); - DSAmsgMPIs[1] = new openpgp.MPI(); - DSAmsgMPIs[0].read(DSAsignedData.substring(0,34)); - DSAmsgMPIs[1].read(DSAsignedData.substring(34,68)); - var success = openpgp.crypto.signature.verify(17, 2, DSAmsgMPIs, DSApubMPIs, data); - expect(success).to.be.true; - done(); + return openpgp.crypto.signature.sign( + 2, 17, DSApubMPIs.concat(DSAsecMPIs), data + ).then(DSAsignedData => { + var DSAsignedData = util.Uint8Array2str(DSAsignedData); + var DSAmsgMPIs = []; + DSAmsgMPIs[0] = new openpgp.MPI(); + DSAmsgMPIs[1] = new openpgp.MPI(); + DSAmsgMPIs[0].read(DSAsignedData.substring(0,34)); + DSAmsgMPIs[1].read(DSAsignedData.substring(34,68)); + return openpgp.crypto.signature.verify( + 17, 2, DSAmsgMPIs, DSApubMPIs, data + ).then(success => { + return expect(success).to.be.true; + }); + }); }); }); @@ -357,28 +368,34 @@ describe('API functional testing', function() { var symmKey = util.Uint8Array2str(openpgp.crypto.generateSessionKey('aes256')); var RSAUnencryptedData = new openpgp.MPI(); RSAUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); - var RSAEncryptedData = openpgp.crypto.publicKeyEncrypt("rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData); + openpgp.crypto.publicKeyEncrypt( + "rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData + ).then(RSAEncryptedData => { - var data = openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).write(); - data = util.Uint8Array2str(data.subarray(2, data.length)); + var data = openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).write(); + data = util.Uint8Array2str(data.subarray(2, data.length)); - var result = openpgp.crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); - expect(result).to.equal(symmKey); - done(); + var result = openpgp.crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); + expect(result).to.equal(symmKey); + done(); + }); }); it('Asymmetric using Elgamal with eme_pkcs1 padding', function (done) { var symmKey = util.Uint8Array2str(openpgp.crypto.generateSessionKey('aes256')); var ElgamalUnencryptedData = new openpgp.MPI(); ElgamalUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); - var ElgamalEncryptedData = openpgp.crypto.publicKeyEncrypt("elgamal", ElgamalpubMPIs, ElgamalUnencryptedData); + openpgp.crypto.publicKeyEncrypt( + "elgamal", ElgamalpubMPIs, ElgamalUnencryptedData + ).then(ElgamalEncryptedData => { - var data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).write(); - data = util.Uint8Array2str(data.subarray(2, data.length)); + var data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).write(); + data = util.Uint8Array2str(data.subarray(2, data.length)); - var result = openpgp.crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength()); - expect(result).to.equal(symmKey); - done(); + var result = openpgp.crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength()); + expect(result).to.equal(symmKey); + done(); + }); }); }); }); diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 5a1b0c6d..d2c67d27 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -3,10 +3,8 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); var chai = require('chai'); -var chaiAsPromised = require('chai-as-promised'); -chai.use(chaiAsPromised); -const expect = chai.expect; - +chai.use(require('chai-as-promised')); +var expect = chai.expect; var bin2bi = function (bytes) { var mpi = new openpgp.MPI(); @@ -265,7 +263,7 @@ describe('Elliptic Curve Cryptography', function () { it('Invalid signature', function (done) { expect(verify_signature( 'secp256k1', 8, [], [], [], secp256k1_dummy_point - )).to.eventually.equal(false).notify(done); + )).to.eventually.be.false.notify(done); }); var p384_message = new Uint8Array([ diff --git a/test/general/ecc.js b/test/general/ecc.js index b4522604..59e19fe5 100644 --- a/test/general/ecc.js +++ b/test/general/ecc.js @@ -2,8 +2,9 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -var chai = require('chai'), - expect = chai.expect; +var chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; describe('Elliptic Curve Cryptography', function () { var data = { @@ -157,73 +158,76 @@ describe('Elliptic Curve Cryptography', function () { load_priv_key('juliet'); done(); }); - it('Verify clear signed message', function (done) { + it('Verify clear signed message', function () { var pub = load_pub_key('juliet'); var msg = openpgp.cleartext.readArmored(data.juliet.message_signed); - openpgp.verify({publicKeys: [pub], message: msg}).then(function(result) { + return openpgp.verify({publicKeys: [pub], message: msg}).then(function(result) { expect(result).to.exist; expect(result.data.trim()).to.equal(data.juliet.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.be.true; - done(); + expect(result.signatures[0].valid).to.eventually.be.true; }); }); - it('Sign message', function (done) { + // FIXME is this pattern correct? + it('Sign message', function () { var romeo = load_priv_key('romeo'); openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) { var romeo = load_pub_key('romeo'); var msg = openpgp.cleartext.readArmored(signed.data); - openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) { + return openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) { expect(result).to.exist; expect(result.data.trim()).to.equal(data.romeo.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.be.true; - done(); + expect(result.signatures[0].valid).to.eventually.be.true; }); }); }); - it('Decrypt and verify message', function (done) { + it('Decrypt and verify message', function () { var juliet = load_pub_key('juliet'); var romeo = load_priv_key('romeo'); var msg = openpgp.message.readArmored(data.juliet.message_encrypted); - openpgp.decrypt({privateKey: romeo, publicKeys: [juliet], message: msg}).then(function (result) { + return openpgp.decrypt( + {privateKey: romeo, publicKeys: [juliet], message: msg} + ).then(function (result) { expect(result).to.exist; // trim required because https://github.com/openpgpjs/openpgpjs/issues/311 expect(result.data.trim()).to.equal(data.juliet.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.be.true; - done(); + expect(result.signatures[0].valid).to.eventually.be.true; }); }); - it('Encrypt and sign message', function (done) { + it('Encrypt and sign message', function () { var romeo = load_priv_key('romeo'); var juliet = load_pub_key('juliet'); - openpgp.encrypt({publicKeys: [juliet], privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (encrypted) { + expect(romeo.decrypt(data['romeo'].pass)).to.be.true; + openpgp.encrypt( + {publicKeys: [juliet], privateKeys: [romeo], data: data.romeo.message + "\n"} + ).then(function (encrypted) { var message = openpgp.message.readArmored(encrypted.data); var romeo = load_pub_key('romeo'); var juliet = load_priv_key('juliet'); - openpgp.decrypt({privateKey: juliet, publicKeys: [romeo], message: message}).then(function (result) { + return openpgp.decrypt( + {privateKey: juliet, publicKeys: [romeo], message: message} + ).then(function (result) { expect(result).to.exist; expect(result.data.trim()).to.equal(data.romeo.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.be.true; - done(); + expect(result.signatures[0].valid).to.eventually.be.true; }); }); }); - it('Generate key', function (done) { + it('Generate key', function () { var options = { userIds: {name: "Hamlet (secp256k1)", email: "hamlet@example.net"}, curve: "secp256k1", passphrase: "ophelia" }; - openpgp.generateKey(options).then(function (key) { + return openpgp.generateKey(options).then(function (key) { expect(key).to.exist; expect(key.key).to.exist; expect(key.key.primaryKey).to.exist; expect(key.privateKeyArmored).to.exist; expect(key.publicKeyArmored).to.exist; - done(); }); }); }); diff --git a/test/general/key.js b/test/general/key.js index 4ab27a69..4eab2195 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -2,8 +2,9 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -var chai = require('chai'), - expect = chai.expect; +var chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; describe('Key', function() { var twoKeys = @@ -718,9 +719,9 @@ describe('Key', function() { expect(pubKey.subKeys).to.exist; expect(pubKey.subKeys).to.have.length(2); - var status = pubKey.subKeys[0].verify(pubKey.primaryKey); - expect(status).to.equal(openpgp.enums.keyStatus.revoked); - done(); + expect(pubKey.subKeys[0].verify( + pubKey.primaryKey + )).to.eventually.equal(openpgp.enums.keyStatus.revoked).notify(done); }); it('Evaluate key flags to find valid encryption key packet', function() { @@ -751,73 +752,96 @@ describe('Key', function() { expect(pubKey.subKeys[0].getExpirationTime().toISOString()).to.be.equal('2018-11-26T10:58:29.000Z'); }); - it('update() - throw error if fingerprints not equal', function() { + it('update() - throw error if fingerprints not equal', function(done) { var keys = openpgp.key.readArmored(twoKeys).keys; - expect(keys[0].update.bind(keys[0], keys[1])).to.throw('Key update method: fingerprints of keys not equal'); + expect(keys[0].update.bind( + keys[0], keys[1] + )()).to.be.rejectedWith('Key update method: fingerprints of keys not equal').notify(done); }); - it('update() - merge revocation signature', function() { + it('update() - merge revocation signature', function(done) { var source = openpgp.key.readArmored(pub_revoked).keys[0]; var dest = openpgp.key.readArmored(pub_revoked).keys[0]; expect(source.revocationSignature).to.exist; dest.revocationSignature = null; - dest.update(source); - expect(dest.revocationSignature).to.exist.and.be.an.instanceof(openpgp.packet.Signature); + dest.update(source).then(() => { + expect(dest.revocationSignature).to.exist.and.be.an.instanceof(openpgp.packet.Signature); + done(); + }); }); - it('update() - merge user', function() { + it('update() - merge user', function(done) { var source = openpgp.key.readArmored(pub_sig_test).keys[0]; var dest = openpgp.key.readArmored(pub_sig_test).keys[0]; expect(source.users[1]).to.exist; dest.users.pop(); - dest.update(source); - expect(dest.users[1]).to.exist; - expect(dest.users[1].userId).to.equal(source.users[1].userId); + dest.update(source).then(() => { + expect(dest.users[1]).to.exist; + expect(dest.users[1].userId).to.equal(source.users[1].userId); + done(); + }); }); - it('update() - merge user - other and revocation certification', function() { + it('update() - merge user - other and revocation certification', function(done) { var source = openpgp.key.readArmored(pub_sig_test).keys[0]; var dest = openpgp.key.readArmored(pub_sig_test).keys[0]; expect(source.users[1].otherCertifications).to.exist; expect(source.users[1].revocationCertifications).to.exist; dest.users[1].otherCertifications = null; dest.users[1].revocationCertifications.pop(); - dest.update(source); - expect(dest.users[1].otherCertifications).to.exist.and.to.have.length(1); - expect(dest.users[1].otherCertifications[0].signature).to.equal(source.users[1].otherCertifications[0].signature); - expect(dest.users[1].revocationCertifications).to.exist.and.to.have.length(2); - expect(dest.users[1].revocationCertifications[1].signature).to.equal(source.users[1].revocationCertifications[1].signature); + dest.update(source).then(() => { + expect(dest.users[1].otherCertifications).to.exist.and.to.have.length(1); + expect(dest.users[1].otherCertifications[0].signature).to.equal(source.users[1].otherCertifications[0].signature); + expect(dest.users[1].revocationCertifications).to.exist.and.to.have.length(2); + expect(dest.users[1].revocationCertifications[1].signature).to.equal(source.users[1].revocationCertifications[1].signature); + done(); + }); }); - it('update() - merge subkey', function() { + it('update() - merge subkey', function(done) { var source = openpgp.key.readArmored(pub_sig_test).keys[0]; var dest = openpgp.key.readArmored(pub_sig_test).keys[0]; expect(source.subKeys[1]).to.exist; dest.subKeys.pop(); - dest.update(source); - expect(dest.subKeys[1]).to.exist; - expect(dest.subKeys[1].subKey.getKeyId().toHex()).to.equal(source.subKeys[1].subKey.getKeyId().toHex()); + dest.update(source).then(() => { + expect(dest.subKeys[1]).to.exist; + expect( + dest.subKeys[1].subKey.getKeyId().toHex() + ).to.equal(source.subKeys[1].subKey.getKeyId().toHex()); + done(); + }); }); - it('update() - merge subkey - revocation signature', function() { + it('update() - merge subkey - revocation signature', function(done) { var source = openpgp.key.readArmored(pub_sig_test).keys[0]; var dest = openpgp.key.readArmored(pub_sig_test).keys[0]; expect(source.subKeys[0].revocationSignature).to.exist; dest.subKeys[0].revocationSignature = null; - dest.update(source); - expect(dest.subKeys[0].revocationSignature).to.exist; - expect(dest.subKeys[0].revocationSignature.signature).to.equal(dest.subKeys[0].revocationSignature.signature); + dest.update(source).then(() => { + expect(dest.subKeys[0].revocationSignature).to.exist; + expect(dest.subKeys[0].revocationSignature.signature).to.equal(dest.subKeys[0].revocationSignature.signature); + done(); + }); }); it('update() - merge private key into public key', function() { var source = openpgp.key.readArmored(priv_key_rsa).keys[0]; var dest = openpgp.key.readArmored(twoKeys).keys[0]; expect(dest.isPublic()).to.be.true; - dest.update(source); - expect(dest.isPrivate()).to.be.true; - expect(source.verifyPrimaryKey()).to.equal(dest.verifyPrimaryKey()); - expect(source.users[0].verify(source.primaryKey)).to.equal(dest.users[0].verify(dest.primaryKey)); - expect(source.subKeys[0].verify(source.primaryKey)).to.equal(dest.subKeys[0].verify(dest.primaryKey)); + return dest.update(source).then(() => { + expect(dest.isPrivate()).to.be.true; + return Promise.all([ + dest.verifyPrimaryKey().then(result => { + expect(source.verifyPrimaryKey()).to.eventually.equal(result); + }), + dest.users[0].verify(dest.primaryKey).then(result => { + expect(source.users[0].verify(source.primaryKey)).to.eventually.equal(result); + }), + dest.subKeys[0].verify(dest.primaryKey).then(result => { + expect(source.subKeys[0].verify(source.primaryKey)).to.eventually.equal(result); + }) + ]); + }); }); it('update() - merge private key into public key - no subkeys', function() { @@ -826,30 +850,42 @@ describe('Key', function() { source.subKeys = null; dest.subKeys = null; expect(dest.isPublic()).to.be.true; - dest.update(source); - expect(dest.isPrivate()).to.be.true; - expect(source.verifyPrimaryKey()).to.equal(dest.verifyPrimaryKey()); - expect(source.users[0].verify(source.primaryKey)).to.equal(dest.users[0].verify(dest.primaryKey)); + return dest.update(source).then(() => { + expect(dest.isPrivate()).to.be.true; + return Promise.all([ + dest.verifyPrimaryKey().then(result => { + expect(source.verifyPrimaryKey()).to.eventually.equal(result); + }), + dest.users[0].verify(dest.primaryKey).then(result => { + expect(source.users[0].verify(source.primaryKey)).to.eventually.equal(result); + }) + ]); + }); }); - it('update() - merge private key into public key - mismatch throws error', function() { + it('update() - merge private key into public key - mismatch throws error', function(done) { var source = openpgp.key.readArmored(priv_key_rsa).keys[0]; var dest = openpgp.key.readArmored(twoKeys).keys[0]; source.subKeys = null; expect(dest.subKeys).to.exist; expect(dest.isPublic()).to.be.true; - expect(dest.update.bind(dest, source)).to.throw('Cannot update public key with private key if subkey mismatch'); + expect(dest.update.bind(dest, source)()) + .to.be.rejectedWith('Cannot update public key with private key if subkey mismatch').notify(done); }); - it('update() - merge subkey binding signatures', function() { + it('update() - merge subkey binding signatures', function(done) { var source = openpgp.key.readArmored(pgp_desktop_pub).keys[0]; var dest = openpgp.key.readArmored(pgp_desktop_priv).keys[0]; expect(source.subKeys[0].bindingSignatures[0]).to.exist; - expect(source.subKeys[0].verify(source.primaryKey)).to.equal(openpgp.enums.keyStatus.valid); + expect(source.subKeys[0].verify(source.primaryKey)) + .to.eventually.equal(openpgp.enums.keyStatus.valid); expect(dest.subKeys[0].bindingSignatures[0]).to.not.exist; - dest.update(source); - expect(dest.subKeys[0].bindingSignatures[0]).to.exist; - expect(dest.subKeys[0].verify(source.primaryKey)).to.equal(openpgp.enums.keyStatus.valid); + dest.update(source).then(() => { + expect(dest.subKeys[0].bindingSignatures[0]).to.exist; + expect(dest.subKeys[0].verify(source.primaryKey)) + .to.eventually.equal(openpgp.enums.keyStatus.valid); + done(); + }); }); it('getPreferredSymAlgo() - one key - AES256', function() { @@ -1002,14 +1038,16 @@ describe('Key', function() { var key = openpgp.key.readArmored(pub_sig_test).keys[0]; var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; privateKey.decrypt('hello world'); - key = key.signPrimaryUser([privateKey]); - var signatures = key.verifyPrimaryUser([privateKey]); - expect(signatures.length).to.equal(2); - expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[0].valid).to.be.null; - expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[1].valid).to.be.true; - done(); + key.signPrimaryUser([privateKey]).then(key => { + key.verifyPrimaryUser([privateKey]).then(signatures => { + expect(signatures.length).to.equal(2); + expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[0].valid).to.be.null; + expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[1].valid).to.be.true; + done(); + }); + }); }); it('Sign key and verify with wrong key - primary user', function(done) { @@ -1017,36 +1055,40 @@ describe('Key', function() { var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; var wrongKey = openpgp.key.readArmored(wrong_key).keys[0]; privateKey.decrypt('hello world'); - key = key.signPrimaryUser([privateKey]); - var signatures = key.verifyPrimaryUser([wrongKey]); - expect(signatures.length).to.equal(2); - expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[0].valid).to.be.null; - expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[1].valid).to.be.null; - done(); + key.signPrimaryUser([privateKey]).then(key => { + key.verifyPrimaryUser([wrongKey]).then(signatures => { + expect(signatures.length).to.equal(2); + expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[0].valid).to.be.null; + expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[1].valid).to.be.null; + done(); + }); + }); }); it('Sign and verify key - all users', function(done) { var key = openpgp.key.readArmored(multi_uid_key).keys[0]; var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; privateKey.decrypt('hello world'); - key = key.signAllUsers([privateKey]); - var signatures = key.verifyAllUsers([privateKey]); - expect(signatures.length).to.equal(4); - expect(signatures[0].userid).to.equal(key.users[0].userId.userid); - expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[0].valid).to.be.null; - expect(signatures[1].userid).to.equal(key.users[0].userId.userid); - expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[1].valid).to.be.true; - expect(signatures[2].userid).to.equal(key.users[1].userId.userid); - expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[2].valid).to.be.null; - expect(signatures[3].userid).to.equal(key.users[1].userId.userid); - expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[3].valid).to.be.true; - done(); + key.signAllUsers([privateKey]).then(key => { + key.verifyAllUsers([privateKey]).then(signatures => { + expect(signatures.length).to.equal(4); + expect(signatures[0].userid).to.equal(key.users[0].userId.userid); + expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[0].valid).to.be.null; + expect(signatures[1].userid).to.equal(key.users[0].userId.userid); + expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[1].valid).to.be.true; + expect(signatures[2].userid).to.equal(key.users[1].userId.userid); + expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[2].valid).to.be.null; + expect(signatures[3].userid).to.equal(key.users[1].userId.userid); + expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[3].valid).to.be.true; + done(); + }); + }); }); it('Sign key and verify with wrong key - all users', function(done) { @@ -1054,22 +1096,24 @@ describe('Key', function() { var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; var wrongKey = openpgp.key.readArmored(wrong_key).keys[0]; privateKey.decrypt('hello world'); - key = key.signAllUsers([privateKey]); - var signatures = key.verifyAllUsers([wrongKey]); - expect(signatures.length).to.equal(4); - expect(signatures[0].userid).to.equal(key.users[0].userId.userid); - expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[0].valid).to.be.null; - expect(signatures[1].userid).to.equal(key.users[0].userId.userid); - expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[1].valid).to.be.null; - expect(signatures[2].userid).to.equal(key.users[1].userId.userid); - expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[2].valid).to.be.null; - expect(signatures[3].userid).to.equal(key.users[1].userId.userid); - expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); - expect(signatures[3].valid).to.be.null; - done(); + key.signAllUsers([privateKey]).then(key => { + key.verifyAllUsers([wrongKey]).then(signatures => { + expect(signatures.length).to.equal(4); + expect(signatures[0].userid).to.equal(key.users[0].userId.userid); + expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[0].valid).to.be.null; + expect(signatures[1].userid).to.equal(key.users[0].userId.userid); + expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[1].valid).to.be.null; + expect(signatures[2].userid).to.equal(key.users[1].userId.userid); + expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[2].valid).to.be.null; + expect(signatures[3].userid).to.equal(key.users[1].userId.userid); + expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); + expect(signatures[3].valid).to.be.null; + done(); + }); + }); }); it('Reformat key without passphrase', function() { var userId1 = 'test1 '; @@ -1128,7 +1172,7 @@ describe('Key', function() { return openpgp.encrypt({data: 'hello', publicKeys: newKey.toPublic(), privateKeys: newKey, armor: true}).then(function(encrypted) { return openpgp.decrypt({message: openpgp.message.readArmored(encrypted.data), privateKey: newKey, publicKeys: newKey.toPublic()}).then(function(decrypted) { expect(decrypted.data).to.equal('hello'); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; }); }); }); diff --git a/test/general/openpgp.js b/test/general/openpgp.js index b3e4e92e..6070b3a2 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -5,8 +5,9 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); var sinon = require('sinon'), - chai = require('chai'), - expect = chai.expect; + chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; var pub_key = ['-----BEGIN PGP PUBLIC KEY BLOCK-----', @@ -711,7 +712,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -755,7 +756,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -788,7 +789,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -825,10 +826,10 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); - expect(decrypted.signatures[1].valid).to.be.true; + expect(decrypted.signatures[1].valid).to.eventually.be.true; expect(decrypted.signatures[1].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[1].signature.packets.length).to.equal(1); }); @@ -997,7 +998,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].valid).to.eventually.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1018,7 +1019,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].valid).to.eventually.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1078,7 +1079,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].valid).to.eventually.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1100,7 +1101,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].valid).to.eventually.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1125,7 +1126,7 @@ describe('OpenPGP.js public api tests', function() { }).then(function(encrypted) { expect(encrypted.data).to.exist; expect(encrypted.data).to.equal(plaintext); - expect(encrypted.signatures[0].valid).to.be.true; + expect(encrypted.signatures[0].valid).to.eventually.be.true; expect(encrypted.signatures[0].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); expect(encrypted.signatures[0].signature.packets.length).to.equal(1); }); diff --git a/test/general/packet.js b/test/general/packet.js index 95c03d78..33282cc0 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -2,6 +2,10 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); +var chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; + function stringify(array) { if(!Uint8Array.prototype.isPrototypeOf(array)) { throw new Error('Data must be in the form of a Uint8Array'); @@ -14,9 +18,6 @@ function stringify(array) { return result.join(''); } -var chai = require('chai'), - expect = chai.expect; - describe("Packet", function() { var armored_key = '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + @@ -191,16 +192,17 @@ describe("Packet", function() { enc.publicKeyAlgorithm = 'rsa_encrypt'; enc.sessionKeyAlgorithm = 'aes256'; enc.publicKeyId.bytes = '12345678'; - enc.encrypt({ params: mpi }); + enc.encrypt({ params: mpi }).then(() => { - msg.push(enc); + msg.push(enc); - msg2.read(msg.write()); + msg2.read(msg.write()); - msg2[0].decrypt({ params: mpi }); + msg2[0].decrypt({ params: mpi }); - expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey)); - expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); + expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey)); + expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); + }); }); }); @@ -239,12 +241,13 @@ describe("Packet", function() { enc.sessionKeyAlgorithm = 'aes256'; enc.publicKeyId.bytes = '12345678'; - enc.encrypt(key); + enc.encrypt(key).then(() => { - enc.decrypt(key); + enc.decrypt(key); - expect(stringify(enc.sessionKey)).to.equal(stringify(secret)); - done(); + expect(stringify(enc.sessionKey)).to.equal(stringify(secret)); + done(); + }); }); it('Public key encrypted packet (reading, GPG)', function(done) { @@ -374,25 +377,21 @@ describe("Packet", function() { done(); }); - it('Secret key reading with signature verification.', function(done) { + it('Secret key reading with signature verification.', function() { var key = new openpgp.packet.List(); key.read(openpgp.armor.decode(armored_key).data); - - - var verified = key[2].verify(key[0], + return Promise.all([ + expect(key[2].verify(key[0], { userid: key[1], key: key[0] - }); - - verified = verified && key[4].verify(key[0], + })).to.eventually.be.true, + expect(key[4].verify(key[0], { key: key[0], bind: key[3] - }); - - expect(verified).to.be.true; - done(); + })).to.eventually.be.true + ]); }); it('Reading a signed, encrypted message.', function(done) { @@ -424,10 +423,9 @@ describe("Packet", function() { var payload = msg[1].packets[0].packets; - var verified = payload[2].verify(key[0], payload[1]); - - expect(verified).to.be.true; - done(); + expect(payload[2].verify( + key[0], payload[1] + )).to.eventually.be.true.notify(done); }); it('Writing and encryption of a secret key packet.', function() { @@ -486,19 +484,18 @@ describe("Packet", function() { signature.publicKeyAlgorithm = 'rsa_sign'; signature.signatureType = 'binary'; - signature.sign(key, literal); + signature.sign(key, literal).then(() => { - signed.push(literal); - signed.push(signature); + signed.push(literal); + signed.push(signature); - var raw = signed.write(); + var raw = signed.write(); - var signed2 = new openpgp.packet.List(); - signed2.read(raw); + var signed2 = new openpgp.packet.List(); + signed2.read(raw); - var verified = signed2[1].verify(key, signed2[0]); - - expect(verified).to.be.true; + expect(signed2[1].verify(key, signed2[0])).to.eventually.be.true; + }); }); }); }); diff --git a/test/general/signature.js b/test/general/signature.js index 7c287205..57c0eff2 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -2,8 +2,9 @@ var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -var chai = require('chai'), - expect = chai.expect; +var chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; describe("Signature", function() { var priv_key_arm1 = @@ -341,7 +342,7 @@ describe("Signature", function() { priv_key.decrypt("abcd"); return openpgp.decrypt({ privateKey: priv_key, publicKeys:[pub_key], message:msg }).then(function(decrypted) { expect(decrypted.data).to.exist; - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); }); @@ -382,15 +383,16 @@ describe("Signature", function() { priv_key_gnupg_ext.subKeys[0].subKey.decrypt("abcd"); return msg.decrypt(priv_key_gnupg_ext).then(function(msg) { - var verified = msg.verify([pub_key]); - expect(verified).to.exist; - expect(verified).to.have.length(1); - expect(verified[0].valid).to.be.true; - expect(verified[0].signature.packets.length).to.equal(1); + return msg.verify([pub_key]).then(verified => { + expect(verified).to.exist; + expect(verified).to.have.length(1); + expect(verified[0].valid).to.eventually.be.true; + expect(verified[0].signature.packets.length).to.equal(1); + }); }); }); - it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', function(done) { + it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', function() { var signedArmor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', @@ -406,15 +408,15 @@ describe("Signature", function() { var sMsg = openpgp.message.readArmored(signedArmor); var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0]; - var verified = sMsg.verify([pub_key]); - expect(verified).to.exist; - expect(verified).to.have.length(1); - expect(verified[0].valid).to.be.true; - expect(verified[0].signature.packets.length).to.equal(1); - done(); + return sMsg.verify([pub_key]).then(verified => { + expect(verified).to.exist; + expect(verified).to.have.length(1); + expect(verified[0].valid).to.eventually.be.true; + expect(verified[0].signature.packets.length).to.equal(1); + }); }); - it('Verify V3 signature. Hash: MD5. PK: RSA. Signature Type: 0x01 (text document)', function(done) { + it('Verify V3 signature. Hash: MD5. PK: RSA. Signature Type: 0x01 (text document)', function() { var signedArmor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', @@ -430,12 +432,12 @@ describe("Signature", function() { var sMsg = openpgp.message.readArmored(signedArmor); var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0]; - var verified = sMsg.verify([pub_key]); - expect(verified).to.exist; - expect(verified).to.have.length(1); - expect(verified[0].valid).to.be.true; - expect(verified[0].signature.packets.length).to.equal(1); - done(); + sMsg.verify([pub_key]).then(verified => { + expect(verified).to.exist; + expect(verified).to.have.length(1); + expect(verified[0].valid).to.eventually.be.true; + expect(verified[0].signature.packets.length).to.equal(1); + }); }); it('Verify signature of signed and encrypted message from GPG2 with openpgp.decrypt', function() { @@ -468,7 +470,7 @@ describe("Signature", function() { expect(decrypted.data).to.exist; expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); }); @@ -504,13 +506,13 @@ describe("Signature", function() { expect(decrypted.data).to.exist; expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); - expect(decrypted.signatures[0].valid).to.be.true; + expect(decrypted.signatures[0].valid).to.eventually.be.true; expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); }); - it('Verify signed message with two one pass signatures', function(done) { + it('Verify signed message with two one pass signatures', function() { var msg_armor = [ '-----BEGIN PGP MESSAGE-----', 'Version: GnuPG v2.0.19 (GNU/Linux)', @@ -542,15 +544,14 @@ describe("Signature", function() { expect(sMsg.getText()).to.equal(plaintext); - var verifiedSig = sMsg.verify([pubKey2, pubKey3]); - - expect(verifiedSig).to.exist; - expect(verifiedSig).to.have.length(2); - expect(verifiedSig[0].valid).to.be.true; - expect(verifiedSig[1].valid).to.be.true; - expect(verifiedSig[0].signature.packets.length).to.equal(1); - expect(verifiedSig[1].signature.packets.length).to.equal(1); - done(); + sMsg.verify([pubKey2, pubKey3]).then(verifiedSig => { + expect(verifiedSig).to.exist; + expect(verifiedSig).to.have.length(2); + expect(verifiedSig[0].valid).to.eventually.be.true; + expect(verifiedSig[1].valid).to.eventually.be.true; + expect(verifiedSig[0].signature.packets.length).to.equal(1); + expect(verifiedSig[1].signature.packets.length).to.equal(1); + }); }); it('Verify cleartext signed message with two signatures with openpgp.verify', function() { @@ -592,8 +593,8 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).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; + expect(cleartextSig.signatures[0].valid).to.eventually.be.true; + expect(cleartextSig.signatures[1].valid).to.eventually.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); expect(cleartextSig.signatures[1].signature.packets.length).to.equal(1); }); @@ -614,7 +615,7 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.equal(plaintext.replace(/\r/g,'')); expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[0].valid).to.eventually.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); @@ -627,6 +628,7 @@ describe("Signature", function() { privKey.getSigningKeyPacket().decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext }).then(function(signed) { + var csMsg = openpgp.message.readArmored(signed.data); return openpgp.verify({ publicKeys:[pubKey], message:csMsg }); @@ -634,7 +636,7 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.deep.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[0].valid).to.eventually.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); @@ -647,6 +649,7 @@ describe("Signature", function() { privKey.getSigningKeyPacket().decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext, armor:false }).then(function(signed) { + var csMsg = signed.message; return openpgp.verify({ publicKeys:[pubKey], message:csMsg }); @@ -654,7 +657,7 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.deep.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[0].valid).to.eventually.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); @@ -688,20 +691,16 @@ describe("Signature", function() { it('Verify primary key revocation signature', function(done) { var pubKey = openpgp.key.readArmored(pub_revoked).keys[0]; - - var verified = pubKey.revocationSignature.verify(pubKey.primaryKey, {key: pubKey.primaryKey}); - - expect(verified).to.be.true; - done(); + expect(pubKey.revocationSignature.verify( + pubKey.primaryKey, {key: pubKey.primaryKey} + )).to.eventually.be.true.notify(done); }); it('Verify subkey revocation signature', function(done) { var pubKey = openpgp.key.readArmored(pub_revoked).keys[0]; - - var verified = pubKey.subKeys[0].revocationSignature.verify(pubKey.primaryKey, {key: pubKey.primaryKey, bind: pubKey.subKeys[0].subKey}); - - expect(verified).to.be.true; - done(); + expect(pubKey.subKeys[0].revocationSignature.verify( + pubKey.primaryKey, {key: pubKey.primaryKey, bind: pubKey.subKeys[0].subKey} + )).to.eventually.be.true.notify(done); }); it('Verify key expiration date', function(done) { @@ -715,11 +714,7 @@ describe("Signature", function() { it('Verify V3 certification signature', function(done) { var pubKey = openpgp.key.readArmored(pub_v3).keys[0]; - - var verified = pubKey.users[0].selfCertifications[0].verify(pubKey.primaryKey, {key: pubKey.primaryKey, userid: pubKey.users[0].userId}); - - expect(verified).to.be.true; - done(); + expect(pubKey.users[0].selfCertifications[0].verify(pubKey.primaryKey, {key: pubKey.primaryKey, userid: pubKey.users[0].userId})).to.eventually.be.true.notify(done); }); it('Write unhashed subpackets', function() { @@ -785,8 +780,9 @@ describe("Signature", function() { var publicKeys = openpgp.key.readArmored(publicKeyArmored).keys; var msg = openpgp.message.readSignedContent(content, detachedSig); - var result = msg.verify(publicKeys); - expect(result[0].valid).to.be.true; + return msg.verify(publicKeys).then(result => { + expect(result[0].valid).to.eventually.be.true; + }); }); it('Detached signature signing and verification', function() { @@ -799,10 +795,12 @@ describe("Signature", function() { if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys return openpgp.generateKey(opt).then(function(gen) { var generatedKey = gen.key; - var detachedSig = msg.signDetached([generatedKey, privKey2]); - var result = msg.verifyDetached(detachedSig, [generatedKey.toPublic(), pubKey2]); - expect(result[0].valid).to.be.true; - expect(result[1].valid).to.be.true; + return msg.signDetached([generatedKey, privKey2]).then(detachedSig => { + return msg.verifyDetached(detachedSig, [generatedKey.toPublic(), pubKey2]).then(result => { + expect(result[0].valid).to.eventually.be.true; + expect(result[1].valid).to.eventually.be.true; + }); + }); }); }); @@ -817,7 +815,7 @@ describe("Signature", function() { }); }); - it('Verify signed key', function(done) { + it('Verify signed key', function() { var signedArmor = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', 'Version: GnuPG v1', @@ -847,12 +845,12 @@ describe("Signature", function() { var signedKey = openpgp.key.readArmored(signedArmor).keys[0]; var signerKey = openpgp.key.readArmored(priv_key_arm1).keys[0]; - var signatures = signedKey.verifyPrimaryUser([signerKey]); - expect(signatures[0].valid).to.be.null; - expect(signatures[0].keyid.toHex()).to.equal(signedKey.primaryKey.getKeyId().toHex()); - expect(signatures[1].valid).to.be.true; - expect(signatures[1].keyid.toHex()).to.equal(signerKey.primaryKey.getKeyId().toHex()); - done(); + return signedKey.verifyPrimaryUser([signerKey]).then(signatures => { + expect(signatures[0].valid).to.be.null; + expect(signatures[0].keyid.toHex()).to.equal(signedKey.primaryKey.getKeyId().toHex()); + expect(signatures[1].valid).to.be.true; + expect(signatures[1].keyid.toHex()).to.equal(signerKey.primaryKey.getKeyId().toHex()); + }); }); }); From 21ae66c604c1563ae702bf298753d06b10986698 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:27:37 -0800 Subject: [PATCH 37/75] encrypt/decrypt/sign/verify will always return promises Note: publicKeyEncryptedSessionKey uses promises, symEncryptedSessionKey does not --- .eslintrc.js | 1 + src/crypto/crypto.js | 15 +- src/crypto/public_key/elliptic/ecdh.js | 2 +- src/crypto/signature.js | 3 +- src/message.js | 205 ++++++++++-------- src/openpgp.js | 6 +- src/packet/packetlist.js | 14 ++ .../public_key_encrypted_session_key.js | 9 +- .../sym_encrypted_integrity_protected.js | 2 +- src/packet/sym_encrypted_session_key.js | 7 +- test/crypto/crypto.js | 24 +- test/general/packet.js | 50 +++-- 12 files changed, 182 insertions(+), 156 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 2992d31a..ed553c48 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -330,6 +330,7 @@ module.exports = { // Custom silencers: "camelcase": 0, "no-debugger": 0, + "require-await": 0, "no-multi-assign": 0, "no-underscore-dangle": 0, "one-var-declaration-per-line": 0, diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index f2eaa1eb..92178491 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -72,9 +72,9 @@ export default { * @param {module:type/mpi} data Data to be encrypted as MPI * @return {Array} encrypted session key parameters */ - publicKeyEncrypt: function(algo, publicParams, data, fingerprint) { + publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { var types = this.getEncSessionKeyParamTypes(algo); - var result = (async function() { + return (async function() { var m; switch (algo) { case 'rsa_encrypt': @@ -107,8 +107,6 @@ export default { return []; } }()); - - return result; }, /** @@ -120,9 +118,9 @@ export default { * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null */ - publicKeyDecrypt: function(algo, keyIntegers, dataIntegers, fingerprint) { + publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) { var p; - var bn = (function() { + return new type_mpi(await (async function() { switch (algo) { case 'rsa_encrypt_sign': case 'rsa_encrypt': @@ -157,10 +155,7 @@ export default { default: return null; } - }()); - - var result = new type_mpi(bn); - return result; + }())); }, /** Returns the types comprising the private key of an algorithm diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 4419a995..8db86be6 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -105,7 +105,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { * @param {String} fingerprint Recipient fingerprint * @return {Uint8Array} Value derived from session */ -function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { +async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); const curve = curves.get(oid); diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 284e8779..13bb3ba4 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -64,8 +64,7 @@ export default { const s = msg_MPIs[1].toBigInteger(); m = data; const Q = publickey_MPIs[1].toBigInteger(); - const result = await ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); - return result; + return ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); default: throw new Error('Invalid signature algorithm.'); } diff --git a/src/message.js b/src/message.js index bcf31282..a73d4215 100644 --- a/src/message.js +++ b/src/message.js @@ -92,30 +92,28 @@ Message.prototype.getSigningKeyIds = function() { * @param {String} password (optional) password used to decrypt * @return {Message} new message with decrypted content */ -Message.prototype.decrypt = function(privateKey, sessionKey, password) { - return Promise.resolve().then(() => { - const keyObj = sessionKey || this.decryptSessionKey(privateKey, password); - if (!keyObj || !util.isUint8Array(keyObj.data) || !util.isString(keyObj.algorithm)) { - throw new Error('Invalid session key for decryption.'); - } +Message.prototype.decrypt = async function(privateKey, sessionKey, password) { + const keyObj = sessionKey || await this.decryptSessionKey(privateKey, password); + if (!keyObj || !util.isUint8Array(keyObj.data) || !util.isString(keyObj.algorithm)) { + throw new Error('Invalid session key for decryption.'); + } - const symEncryptedPacketlist = this.packets.filterByTag( - enums.packet.symmetricallyEncrypted, - enums.packet.symEncryptedIntegrityProtected, - enums.packet.symEncryptedAEADProtected - ); + const symEncryptedPacketlist = this.packets.filterByTag( + enums.packet.symmetricallyEncrypted, + enums.packet.symEncryptedIntegrityProtected, + enums.packet.symEncryptedAEADProtected + ); - if (symEncryptedPacketlist.length === 0) { - return; - } + if (symEncryptedPacketlist.length === 0) { + return; + } - const symEncryptedPacket = symEncryptedPacketlist[0]; - return symEncryptedPacket.decrypt(keyObj.algorithm, keyObj.data).then(() => { - const resultMsg = new Message(symEncryptedPacket.packets); - symEncryptedPacket.packets = new packet.List(); // remove packets after decryption - return resultMsg; - }); - }); + const symEncryptedPacket = symEncryptedPacketlist[0]; + await symEncryptedPacket.decrypt(keyObj.algorithm, keyObj.data); + const resultMsg = new Message(symEncryptedPacket.packets); + symEncryptedPacket.packets = new packet.List(); // remove packets after decryption + + return resultMsg; }; /** @@ -126,56 +124,64 @@ Message.prototype.decrypt = function(privateKey, sessionKey, password) { * { data:Uint8Array, algorithm:String } */ Message.prototype.decryptSessionKey = function(privateKey, password) { - var keyPacket; - - if (password) { - var symEncryptedSessionKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey); - var symLength = symEncryptedSessionKeyPacketlist.length; - for (var i = 0; i < symLength; i++) { - keyPacket = symEncryptedSessionKeyPacketlist[i]; - try { - keyPacket.decrypt(password); - break; + var keyPacket, results, error; + return Promise.resolve().then(async () => { + if (password) { + var symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey); + // FIXME need a circuit breaker here + if (!symESKeyPacketlist) { + throw new Error('No symmetrically encrypted session key packet found.'); } - catch(err) { - if (i === (symLength - 1)) { - throw err; + results = await Promise.all(symESKeyPacketlist.map(async function(packet) { + try { + await packet.decrypt(password); + return packet; + } catch (err) { + error = err; } + })); + keyPacket = results.find(result => result !== undefined); + + } else if (privateKey) { + var encryptionKeyIds = this.getEncryptionKeyIds(); + if (!encryptionKeyIds.length) { + // nothing to decrypt + return; } - } - if (!keyPacket) { - throw new Error('No symmetrically encrypted session key packet found.'); - } - - } else if (privateKey) { - var encryptionKeyIds = this.getEncryptionKeyIds(); - if (!encryptionKeyIds.length) { - // nothing to decrypt - return; - } - var privateKeyPacket = privateKey.getKeyPacket(encryptionKeyIds); - if (!privateKeyPacket.isDecrypted) { - throw new Error('Private key is not decrypted.'); - } - var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey); - for (var j = 0; j < pkESKeyPacketlist.length; j++) { - if (pkESKeyPacketlist[j].publicKeyId.equals(privateKeyPacket.getKeyId())) { - keyPacket = pkESKeyPacketlist[j]; - keyPacket.decrypt(privateKeyPacket); - break; + var privateKeyPacket = privateKey.getKeyPacket(encryptionKeyIds); + if (!privateKeyPacket.isDecrypted) { + throw new Error('Private key is not decrypted.'); } + var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey); + if (!pkESKeyPacketlist) { + throw new Error('No public key encrypted session key packet found.'); + } + // FIXME need a circuit breaker here + results = await Promise.all(pkESKeyPacketlist.map(async function(packet) { + if (packet.publicKeyId.equals(privateKeyPacket.getKeyId())) { + try { + await packet.decrypt(privateKeyPacket) + return packet; + } catch (err) { + error = err; + } + } + })); + keyPacket = results.find(result => result !== undefined); + + } else { + throw new Error('No key or password specified.'); } - - } else { - throw new Error('No key or password specified.'); - } - - if (keyPacket) { - return { - data: keyPacket.sessionKey, - algorithm: keyPacket.sessionKeyAlgorithm - }; - } + }).then(() => { + if (keyPacket) { + return { + data: keyPacket.sessionKey, + algorithm: keyPacket.sessionKeyAlgorithm + }; + } else { + throw new Error('Session key decryption failed.'); + } + }); }; /** @@ -218,7 +224,15 @@ Message.prototype.getText = function() { */ Message.prototype.encrypt = function(keys, passwords, sessionKey) { let symAlgo, msg, symEncryptedPacket; - return Promise.resolve().then(() => { + return Promise.resolve().then(async () => { + if (keys) { + symAlgo = enums.read(enums.symmetric, keyModule.getPreferredSymAlgo(keys)); + } else if (passwords) { + symAlgo = enums.read(enums.symmetric, config.encryption_cipher); + } else { + throw new Error('No keys or passwords'); + } + if (sessionKey) { if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) { throw new Error('Invalid session key for encryption.'); @@ -237,7 +251,7 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey) { sessionKey = crypto.generateSessionKey(symAlgo); } - msg = encryptSessionKey(sessionKey, symAlgo, keys, passwords); + msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords); if (config.aead_protect) { symEncryptedPacket = new packet.SymEncryptedAEADProtected(); @@ -272,38 +286,41 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey) { * @return {Message} new message with encrypted content */ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) { - var packetlist = new packet.List(); + var results, packetlist = new packet.List(); - if (publicKeys) { - publicKeys.forEach(function(key) { - var encryptionKeyPacket = key.getEncryptionKeyPacket(); - if (encryptionKeyPacket) { + return Promise.resolve().then(async () => { + if (publicKeys) { + results = await Promise.all(publicKeys.map(async function(key) { + var encryptionKeyPacket = key.getEncryptionKeyPacket(); + if (!encryptionKeyPacket) { + throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex()); + } var pkESKeyPacket = new packet.PublicKeyEncryptedSessionKey(); pkESKeyPacket.publicKeyId = encryptionKeyPacket.getKeyId(); pkESKeyPacket.publicKeyAlgorithm = encryptionKeyPacket.algorithm; pkESKeyPacket.sessionKey = sessionKey; pkESKeyPacket.sessionKeyAlgorithm = symAlgo; - pkESKeyPacket.encrypt(encryptionKeyPacket); + await pkESKeyPacket.encrypt(encryptionKeyPacket); delete pkESKeyPacket.sessionKey; // delete plaintext session key after encryption - packetlist.push(pkESKeyPacket); - } else { - throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex()); - } - }); - } + return pkESKeyPacket; + })); + packetlist.concat(results); + } - if (passwords) { - passwords.forEach(function(password) { - var symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey(); - symEncryptedSessionKeyPacket.sessionKey = sessionKey; - symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo; - symEncryptedSessionKeyPacket.encrypt(password); - delete symEncryptedSessionKeyPacket.sessionKey; // delete plaintext session key after encryption - packetlist.push(symEncryptedSessionKeyPacket); - }); - } - - return new Message(packetlist); + if (passwords) { + results = await Promise.all(passwords.map(async function(password) { + var symEncryptedSessionKeyPacket = new packet.SymEncryptedSessionKey(); + symEncryptedSessionKeyPacket.sessionKey = sessionKey; + symEncryptedSessionKeyPacket.sessionKeyAlgorithm = symAlgo; + await symEncryptedSessionKeyPacket.encrypt(password); + delete symEncryptedSessionKeyPacket.sessionKey; // delete plaintext session key after encryption + return symEncryptedSessionKeyPacket; + })); + packetlist.concat(results); + } + }).then(() => { + return new Message(packetlist); + }); } /** @@ -365,13 +382,11 @@ Message.prototype.sign = async function(privateKeys=[], signature=null) { packetlist.push(literalDataPacket); - // FIXME does the order matter here? It used to be n-1..0 - await Promise.all(privateKeys.map(async function(privateKey) { + await Promise.all(privateKeys.reverse().map(async function(privateKey) { var signingKeyPacket = privateKey.getSigningKeyPacket(); var signaturePacket = new packet.Signature(); signaturePacket.signatureType = signatureType; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - // FIXME FIXME were we just signing with the last key? signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); diff --git a/src/openpgp.js b/src/openpgp.js index fd3394f1..c2aca38f 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -394,10 +394,8 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords }) { return asyncProxy.delegate('encryptSessionKey', { data, algorithm, publicKeys, passwords }); } - return execute(() => ({ - - message: messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords) - + return execute(async () => ({ + message: await messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords) }), 'Error encrypting session key'); } diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index 4ec59e9d..7f096fba 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -149,6 +149,20 @@ Packetlist.prototype.forEach = function (callback) { } }; +/** +* Returns an array containing return values of callback +* on each element +*/ +Packetlist.prototype.map = function (callback) { + var packetArray = []; + + for (var i = 0; i < this.length; i++) { + packetArray.push(callback(this[i], i, this)); + } + + return packetArray; +}; + /** * Traverses packet tree and returns first matching packet * @param {module:enums.packet} type The packet type diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 4785a6c9..00b9287f 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -132,12 +132,12 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { * Private key with secMPIs unlocked * @return {String} The unencrypted session key */ -PublicKeyEncryptedSessionKey.prototype.decrypt = function (key) { - var result = crypto.publicKeyDecrypt( +PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { + var result = (await crypto.publicKeyDecrypt( this.publicKeyAlgorithm, key.params, this.encrypted, - key.fingerprint).toBytes(); + key.fingerprint)).toBytes(); var checksum; var decoded; @@ -155,8 +155,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = function (key) { throw new Error('Checksum mismatch'); } else { this.sessionKey = key; - this.sessionKeyAlgorithm = - enums.read(enums.symmetric, decoded.charCodeAt(0)); + this.sessionKeyAlgorithm = enums.read(enums.symmetric, decoded.charCodeAt(0)); } }; diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 60e2844a..605f4dd2 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -133,7 +133,7 @@ SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm this.packets.read(decrypted.subarray(0, decrypted.length - 22)); } - return Promise.resolve(); + return true; }; diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js index d40efa85..f6be141f 100644 --- a/src/packet/sym_encrypted_session_key.js +++ b/src/packet/sym_encrypted_session_key.js @@ -122,9 +122,7 @@ SymEncryptedSessionKey.prototype.decrypt = function(passphrase) { var decrypted = crypto.cfb.normalDecrypt( algo, key, this.encrypted, null); - this.sessionKeyAlgorithm = enums.read(enums.symmetric, - decrypted[0]); - + this.sessionKeyAlgorithm = enums.read(enums.symmetric, decrypted[0]); this.sessionKey = decrypted.subarray(1,decrypted.length); } }; @@ -147,8 +145,7 @@ SymEncryptedSessionKey.prototype.encrypt = function(passphrase) { } private_key = util.concatUint8Array([algo_enum, this.sessionKey]); - this.encrypted = crypto.cfb.normalEncrypt( - algo, key, private_key, null); + this.encrypted = crypto.cfb.normalEncrypt(algo, key, private_key, null); }; /** diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index e6c7d8c2..ee1e852f 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -372,12 +372,14 @@ describe('API functional testing', function() { "rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData ).then(RSAEncryptedData => { - var data = openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).write(); - data = util.Uint8Array2str(data.subarray(2, data.length)); + openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).then(data => { + data = data.write(); + data = util.Uint8Array2str(data.subarray(2, data.length)); - var result = openpgp.crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); - expect(result).to.equal(symmKey); - done(); + var result = openpgp.crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); + expect(result).to.equal(symmKey); + done(); + }); }); }); @@ -389,12 +391,14 @@ describe('API functional testing', function() { "elgamal", ElgamalpubMPIs, ElgamalUnencryptedData ).then(ElgamalEncryptedData => { - var data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).write(); - data = util.Uint8Array2str(data.subarray(2, data.length)); + var data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).then(data => { + data = data.write(); + data = util.Uint8Array2str(data.subarray(2, data.length)); - var result = openpgp.crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength()); - expect(result).to.equal(symmKey); - done(); + var result = openpgp.crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength()); + expect(result).to.equal(symmKey); + done(); + }); }); }); }); diff --git a/test/general/packet.js b/test/general/packet.js index 33282cc0..e5637348 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -198,10 +198,11 @@ describe("Packet", function() { msg2.read(msg.write()); - msg2[0].decrypt({ params: mpi }); + msg2[0].decrypt({ params: mpi }).then(() => { - expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey)); - expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); + expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey)); + expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm); + }); }); }); }); @@ -243,10 +244,11 @@ describe("Packet", function() { enc.encrypt(key).then(() => { - enc.decrypt(key); + enc.decrypt(key).then(() => { - expect(stringify(enc.sessionKey)).to.equal(stringify(secret)); - done(); + expect(stringify(enc.sessionKey)).to.equal(stringify(secret)); + done(); + }); }); }); @@ -305,13 +307,14 @@ describe("Packet", function() { 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).then(() => { + msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - var text = stringify(msg[1].packets[0].packets[0].data); + var text = stringify(msg[1].packets[0].packets[0].data); - expect(text).to.equal('Hello world!'); - done(); + expect(text).to.equal('Hello world!'); + done(); + }); }); it('Sym encrypted session key reading/writing', function(done) { @@ -335,7 +338,6 @@ describe("Packet", function() { enc.packets.push(literal); enc.encrypt(algo, key); - var msg2 = new openpgp.packet.List(); msg2.read(msg.write()); @@ -368,13 +370,14 @@ describe("Packet", function() { 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).then(() => { + msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - var text = stringify(msg[1].packets[0].packets[0].data); + var text = stringify(msg[1].packets[0].packets[0].data); - expect(text).to.equal('Hello world!'); - done(); + expect(text).to.equal('Hello world!'); + done(); + }); }); it('Secret key reading with signature verification.', function() { @@ -418,14 +421,15 @@ describe("Packet", function() { 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]).then(() => { + msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); - var payload = msg[1].packets[0].packets; + var payload = msg[1].packets[0].packets; - expect(payload[2].verify( - key[0], payload[1] - )).to.eventually.be.true.notify(done); + expect(payload[2].verify( + key[0], payload[1] + )).to.eventually.be.true.notify(done); + }); }); it('Writing and encryption of a secret key packet.', function() { From dcff16d32b934f967b796480a99790b7ebf7e8d7 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:32:04 -0800 Subject: [PATCH 38/75] npm test and grunt browsertest can run side by side now; npm tests pass, browser stuck on problem related to asn1.js --- Gruntfile.js | 33 +++++++++++++++++-- src/crypto/public_key/elliptic/curves.js | 41 ++++++++++++++++-------- src/crypto/public_key/elliptic/ecdsa.js | 25 ++++++++------- src/encoding/base64.js | 35 +++++++++++--------- test/unittests.html | 4 +-- 5 files changed, 93 insertions(+), 45 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index b3254c2c..d52d2c60 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -87,6 +87,31 @@ module.exports = function(grunt) { plugin: [ 'browserify-derequire' ] } }, + openpgp_browser: { + files: { + 'dist/openpgp_browser.js': [ './src/index.js' ] + }, + options: { + browserifyOptions: { + debug: true, + standalone: 'openpgp' + }, + external: [ 'crypto', 'node-localstorage', 'node-fetch' ], + transform: [ + ["babelify", { + plugins: ["transform-async-to-generator", + "syntax-async-functions", + "transform-regenerator", + "transform-runtime"], + ignore: ['*.min.js'], + presets: [ + "es2015" + ] + }] + ], + plugin: [ 'browserify-derequire' ] + } + }, worker: { files: { 'dist/openpgp.worker.js': [ './src/worker/worker.js' ] @@ -97,7 +122,7 @@ module.exports = function(grunt) { 'test/lib/unittests-bundle.js': [ './test/unittests.js' ] }, options: { - external: [ 'crypto', 'buffer' , 'node-localstorage', 'node-fetch', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp' ] + external: [ 'crypto', 'node-localstorage', 'node-fetch', 'openpgp', '../../dist/openpgp' ] } } }, @@ -190,7 +215,7 @@ module.exports = function(grunt) { } }, copy: { - browsertest: { + browser: { expand: true, flatten: true, cwd: 'node_modules/', @@ -262,6 +287,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-saucelabs'); + grunt.loadNpmTasks('grunt-keepalive'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('set_version', function() { @@ -304,6 +330,7 @@ module.exports = function(grunt) { // Test/Dev tasks grunt.registerTask('test', [ 'eslint', 'mochaTest']); grunt.registerTask('coverage', ['mocha_istanbul:coverage']); - grunt.registerTask('saucelabs', ['default', 'copy:browsertest', 'connect:test', 'saucelabs-mocha']); + grunt.registerTask('saucelabs', ['default', 'copy:browser', 'connect:test', 'saucelabs-mocha']); + grunt.registerTask('browsertest', ['browserify:openpgp_browser', 'copy:browser', 'connect:test', 'keepalive']); }; diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 839f731e..ca874b40 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -27,16 +27,27 @@ 'use strict'; +import ASN1 from 'asn1.js'; + import {ec as EC} from 'elliptic'; import {KeyPair} from './key.js'; import BigInteger from '../jsbn.js'; import config from '../../../config'; import enums from '../../../enums.js'; import util from '../../../util.js'; +import base64 from '../../../encoding/base64.js'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); + +var ECPrivateKey = ASN1.define('ECPrivateKey', function() { + this.seq().obj( + this.key('r').int(), // FIXME int or BN? + this.key('s').int() // FIXME int or BN? + ); +}); + var webCurves = [], nodeCurves = []; if (webCrypto && config.use_native) { // see https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API/Supported_algorithms @@ -87,7 +98,9 @@ const curves = { node: false, // FIXME nodeCurves.includes('secp256k1'), // this is because jwk-to-pem does not support this curve. web: false - } + }, + curve25519 : {}, + ed25519 : {} }; function Curve(name, {oid, hash, cipher, namedCurve, opensslCurve, hashName, node, web}) { @@ -114,7 +127,7 @@ Curve.prototype.keyFromPublic = function (pub) { Curve.prototype.genKeyPair = async function () { var keyPair; if (webCrypto && config.use_native && this.web) { - keyPair = await webGenKeyPair(this.namedCurve); + keyPair = await webGenKeyPair(this.namedCurve, "ECDSA"); // FIXME } else if (nodeCrypto && config.use_native && this.node) { keyPair = await nodeGenKeyPair(this.opensslCurve); } else { @@ -163,24 +176,26 @@ module.exports = { ////////////////////////// -async function webGenKeyPair(namedCurve) { +async function webGenKeyPair(namedCurve, algorithm) { try { - var keyPair = await webCrypto.generateKey( + var webCryptoKey = await webCrypto.generateKey( { - name: "ECDSA", // FIXME or "ECDH" - // "P-256", "P-384", or "P-521" + name: algorithm === "ECDH" ? "ECDH" : "ECDSA", namedCurve: namedCurve }, - // TODO whether the key is extractable (i.e. can be used in exportKey) - false, - // FIXME this can be any combination of "sign" and "verify" - // or "deriveKey" and "deriveBits" for ECDH - ["sign", "verify"] + true, + algorithm === "ECDH" ? ["deriveKey", "deriveBits"] : ["sign", "verify"] ); + var privateKey = await webCrypto.exportKey("jwk", webCryptoKey.privateKey); + var publicKey = await webCrypto.exportKey("jwk", webCryptoKey.publicKey); + return { - pub: keyPair.publicKey.encode(), // FIXME encoding - priv: keyPair.privateKey.toArray() // FIXME encoding + pub: { + x: base64.decode(publicKey.x, 'base64url'), + y: base64.decode(publicKey.y, 'base64url') + }, + priv: base64.decode(privateKey.d, 'base64url') }; } catch(err) { throw new Error(err); diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 957b18d1..e14163a2 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -33,12 +33,13 @@ import curves from './curves.js'; import BigInteger from '../jsbn.js'; import config from '../../../config'; import enums from '../../../enums.js'; -import util from '../../../util.js' +import util from '../../../util.js'; +import base64 from '../../../encoding/base64.js'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); -var ECDSASignature = ASN1.define('ecdsa-sig', function() { +var ECDSASignature = ASN1.define('ECDSASignature', function() { this.seq().obj( this.key('r').int(), // FIXME int or BN? this.key('s').int() // FIXME int or BN? @@ -115,9 +116,9 @@ async function webSign(curve, hash_algo, m, keyPair) { { "kty": "EC", "crv": curve.namedCurve, - "x": keyPair.getPublic().getX().toBuffer().base64Slice(), - "y": keyPair.getPublic().getY().toBuffer().base64Slice(), - "d": keyPair.getPrivate().toBuffer().base64Slice(), + "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray()), null, 'base64url'), + "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray()), null, 'base64url'), + "d": base64.encode(new Uint8Array(keyPair.getPrivate()), null, 'base64url'), "use": "sig", "kid": "ECDSA Private Key" }, @@ -151,8 +152,8 @@ async function webVerify(curve, hash_algo, signature, m, publicKey) { { "kty": "EC", "crv": curve.namedCurve, - "x": publicKey.getX().toBuffer().base64Slice(), - "y": publicKey.getY().toBuffer().base64Slice(), + "x": base64.encode(new Uint8Array(publicKey.getX().toArray()), null, 'base64url'), + "y": base64.encode(new Uint8Array(publicKey.getY().toArray()), null, 'base64url'), "use": "sig", "kid": "ECDSA Public Key" }, @@ -189,9 +190,9 @@ async function nodeSign(curve, hash_algo, message, keyPair) { { "kty": "EC", "crv": curve.namedCurve, - "x": keyPair.getPublic().getX().toBuffer().base64Slice(), - "y": keyPair.getPublic().getY().toBuffer().base64Slice(), - "d": keyPair.getPrivate().toBuffer().base64Slice(), + "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray())), + "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray())), + "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray())), "use": "sig", "kid": "ECDSA Private Key" }, @@ -223,8 +224,8 @@ async function nodeVerify(curve, hash_algo, signature, message, publicKey) { { "kty": "EC", "crv": curve.namedCurve, - "x": publicKey.getX().toBuffer().base64Slice(), - "y": publicKey.getY().toBuffer().base64Slice(), + "x": base64.encode(new Uint8Array(publicKey.getX().toArray())), + "y": base64.encode(new Uint8Array(publicKey.getY().toArray())), "use": "sig", "kid": "ECDSA Public Key" }, diff --git a/src/encoding/base64.js b/src/encoding/base64.js index bb670945..403a73d5 100644 --- a/src/encoding/base64.js +++ b/src/encoding/base64.js @@ -18,6 +18,7 @@ 'use strict'; var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var b64u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; /** * Convert binary array to radix-64 @@ -25,8 +26,9 @@ var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; * @returns {string} radix-64 version of input string * @static */ -function s2r(t, o) { +function s2r(t, o, u) { // TODO check btoa alternative + var b64 = (u === "base64url") ? b64u : b64s; var a, c, n; var r = o ? o : [], l = 0, @@ -36,21 +38,21 @@ function s2r(t, o) { for (n = 0; n < tl; n++) { c = t[n]; if (s === 0) { - r.push(b64s.charAt((c >> 2) & 63)); + r.push(b64.charAt((c >> 2) & 63)); a = (c & 3) << 4; } else if (s === 1) { - r.push(b64s.charAt(a | ((c >> 4) & 15))); + r.push(b64.charAt(a | ((c >> 4) & 15))); a = (c & 15) << 2; } else if (s === 2) { - r.push(b64s.charAt(a | ((c >> 6) & 3))); + r.push(b64.charAt(a | ((c >> 6) & 3))); l += 1; - if ((l % 60) === 0) { + if ((l % 60) === 0 && !u) { r.push("\n"); } - r.push(b64s.charAt(c & 63)); + r.push(b64.charAt(c & 63)); } l += 1; - if ((l % 60) === 0) { + if ((l % 60) === 0 && !u) { r.push("\n"); } @@ -60,16 +62,18 @@ function s2r(t, o) { } } if (s > 0) { - r.push(b64s.charAt(a)); + r.push(b64.charAt(a)); l += 1; - if ((l % 60) === 0) { + if ((l % 60) === 0 && !u) { r.push("\n"); } - r.push('='); - l += 1; + if (u !== 'base64url') { + r.push('='); + l += 1; + } } - if (s === 1) { - if ((l % 60) === 0) { + if (s === 1 && u !== 'base64url') { + if ((l % 60) === 0 && !u) { r.push("\n"); } r.push('='); @@ -87,8 +91,9 @@ function s2r(t, o) { * @returns {Uint8Array} binary array version of input string * @static */ -function r2s(t) { +function r2s(t, u) { // TODO check atob alternative + var b64 = (u === "base64url") ? b64u : b64s; var c, n; var r = [], s = 0, @@ -96,7 +101,7 @@ function r2s(t) { var tl = t.length; for (n = 0; n < tl; n++) { - c = b64s.indexOf(t.charAt(n)); + c = b64.indexOf(t.charAt(n)); if (c >= 0) { if (s) { r.push(a | ((c >> (6 - s)) & 255)); diff --git a/test/unittests.html b/test/unittests.html index 47f16d5d..8f860f19 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -10,11 +10,11 @@
- + - From e6820d7b2a16c5d517806dca6c27bda77603ef1f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:40:08 -0800 Subject: [PATCH 39/75] Passing all tests, on Node, Firefox, and Chrome --- src/cleartext.js | 25 ++-- src/crypto/public_key/elliptic/curves.js | 71 +++++------ src/crypto/public_key/elliptic/ecdsa.js | 142 +++++++++++---------- src/enums.js | 11 ++ src/message.js | 150 +++++++++++------------ src/worker/worker.js | 2 +- test/general/ecc.js | 8 +- test/general/key.js | 2 +- test/general/openpgp.js | 20 +-- test/general/signature.js | 32 ++--- test/unittests.html | 2 +- 11 files changed, 233 insertions(+), 232 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index be3bee3b..cf12f9e6 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -95,8 +95,10 @@ CleartextMessage.prototype.signDetached = async function(privateKeys) { throw new Error('Private key is not decrypted.'); } await signaturePacket.sign(signingKeyPacket, literalDataPacket); - packetlist.push(signaturePacket); - })); + return signaturePacket; + })).then(signatureList => { + signatureList.forEach(signaturePacket => packetlist.push(signaturePacket)); + }); return new sigModule.Signature(packetlist); }; @@ -116,16 +118,14 @@ CleartextMessage.prototype.verify = function(keys) { * @return {Array<{keyid: module:type/keyid, valid: Boolean}>} list of signer's keyid and validity of signature */ CleartextMessage.prototype.verifyDetached = function(signature, keys) { - var result = []; var signatureList = signature.packets; var literalDataPacket = new packet.Literal(); // we assume that cleartext signature is generated based on UTF8 cleartext literalDataPacket.setText(this.text); - // TODO await Promise.all(signatureList.map(async function(signature) { })); - for (var i = 0; i < signatureList.length; i++) { + return Promise.all(signatureList.map(async function(signature) { var keyPacket = null; for (var j = 0; j < keys.length; j++) { - keyPacket = keys[j].getSigningKeyPacket(signatureList[i].issuerKeyId); + keyPacket = keys[j].getSigningKeyPacket(signature.issuerKeyId); if (keyPacket) { break; } @@ -133,20 +133,19 @@ CleartextMessage.prototype.verifyDetached = function(signature, keys) { var verifiedSig = {}; if (keyPacket) { - verifiedSig.keyid = signatureList[i].issuerKeyId; - verifiedSig.valid = signatureList[i].verify(keyPacket, literalDataPacket); + verifiedSig.keyid = signature.issuerKeyId; + verifiedSig.valid = await signature.verify(keyPacket, literalDataPacket); } else { - verifiedSig.keyid = signatureList[i].issuerKeyId; + verifiedSig.keyid = signature.issuerKeyId; verifiedSig.valid = null; } var packetlist = new packet.List(); - packetlist.push(signatureList[i]); + packetlist.push(signature); verifiedSig.signature = new sigModule.Signature(packetlist); - result.push(verifiedSig); - } - return result; + return verifiedSig; + })); }; /** diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index ca874b40..4d03e045 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -27,8 +27,6 @@ 'use strict'; -import ASN1 from 'asn1.js'; - import {ec as EC} from 'elliptic'; import {KeyPair} from './key.js'; import BigInteger from '../jsbn.js'; @@ -40,14 +38,6 @@ import base64 from '../../../encoding/base64.js'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); - -var ECPrivateKey = ASN1.define('ECPrivateKey', function() { - this.seq().obj( - this.key('r').int(), // FIXME int or BN? - this.key('s').int() // FIXME int or BN? - ); -}); - var webCurves = [], nodeCurves = []; if (webCrypto && config.use_native) { // see https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API/Supported_algorithms @@ -60,6 +50,7 @@ if (webCrypto && config.use_native) { const curves = { p256: { oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), + pointSize: 66, // FIXME namedCurve: 'P-256', opensslCurve: 'prime256v1', hashName: 'SHA-256', @@ -70,6 +61,7 @@ const curves = { }, p384: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), + pointSize: 48, namedCurve: 'P-384', opensslCurve: 'secp384r1', // FIXME hashName: 'SHA-384', @@ -80,6 +72,7 @@ const curves = { }, p521: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), + pointSize: 66, namedCurve: 'P-521', opensslCurve: 'secp521r1', // FIXME hashName: 'SHA-512', @@ -90,6 +83,7 @@ const curves = { }, secp256k1: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), + pointSize: 66, // FIXME namedCurve: 'SECP-256K1', opensslCurve: 'secp256k1', hashName: 'SHA-256', @@ -103,10 +97,11 @@ const curves = { ed25519 : {} }; -function Curve(name, {oid, hash, cipher, namedCurve, opensslCurve, hashName, node, web}) { +function Curve(name, {oid, pointSize, hash, cipher, namedCurve, opensslCurve, hashName, node, web}) { this.curve = new EC(name); this.name = name; this.oid = oid; + this.pointSize = pointSize; this.hash = hash; this.cipher = cipher; this.namedCurve= namedCurve; @@ -177,41 +172,33 @@ module.exports = { async function webGenKeyPair(namedCurve, algorithm) { - try { - var webCryptoKey = await webCrypto.generateKey( - { - name: algorithm === "ECDH" ? "ECDH" : "ECDSA", - namedCurve: namedCurve - }, - true, - algorithm === "ECDH" ? ["deriveKey", "deriveBits"] : ["sign", "verify"] - ); + var webCryptoKey = await webCrypto.generateKey( + { + name: algorithm === "ECDH" ? "ECDH" : "ECDSA", + namedCurve: namedCurve + }, + true, + algorithm === "ECDH" ? ["deriveKey", "deriveBits"] : ["sign", "verify"] + ); - var privateKey = await webCrypto.exportKey("jwk", webCryptoKey.privateKey); - var publicKey = await webCrypto.exportKey("jwk", webCryptoKey.publicKey); + var privateKey = await webCrypto.exportKey("jwk", webCryptoKey.privateKey); + var publicKey = await webCrypto.exportKey("jwk", webCryptoKey.publicKey); - return { - pub: { - x: base64.decode(publicKey.x, 'base64url'), - y: base64.decode(publicKey.y, 'base64url') - }, - priv: base64.decode(privateKey.d, 'base64url') - }; - } catch(err) { - throw new Error(err); - } + return { + pub: { + x: base64.decode(publicKey.x, 'base64url'), + y: base64.decode(publicKey.y, 'base64url') + }, + priv: base64.decode(privateKey.d, 'base64url') + }; } async function nodeGenKeyPair(opensslCurve) { - try { - var ecdh = nodeCrypto.createECDH(opensslCurve); - await ecdh.generateKeys(); + var ecdh = nodeCrypto.createECDH(opensslCurve); + await ecdh.generateKeys(); - return { - pub: ecdh.getPublicKey().toJSON().data, - priv: ecdh.getPrivateKey().toJSON().data - }; - } catch(err) { - throw new Error(err); - } + return { + pub: ecdh.getPublicKey().toJSON().data, + priv: ecdh.getPrivateKey().toJSON().data + }; } diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index e14163a2..dd75150b 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -57,6 +57,7 @@ var ECDSASignature = ASN1.define('ECDSASignature', function() { async function sign(oid, hash_algo, m, d) { var signature; const curve = curves.get(oid); + hash_algo = hash_algo ? hash_algo : curve.hash; const key = curve.keyFromPrivate(d.toByteArray()); if (webCrypto && config.use_native && curve.web) { signature = await webSign(curve, hash_algo, m, key.keyPair); @@ -83,6 +84,7 @@ async function sign(oid, hash_algo, m, d) { async function verify(oid, hash_algo, signature, m, Q) { var result; const curve = curves.get(oid); + hash_algo = hash_algo ? hash_algo : curve.hash; // FIXME is this according to the RFC? const key = curve.keyFromPublic(Q.toByteArray()); if (webCrypto && config.use_native && curve.web) { result = await webVerify(curve, hash_algo, signature, m, key.keyPair.getPublic()); @@ -109,76 +111,83 @@ module.exports = { ////////////////////////// -async function webSign(curve, hash_algo, m, keyPair) { - try { - const key = await webCrypto.importKey( - "jwk", - { - "kty": "EC", - "crv": curve.namedCurve, - "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray()), null, 'base64url'), - "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray()), null, 'base64url'), - "d": base64.encode(new Uint8Array(keyPair.getPrivate()), null, 'base64url'), - "use": "sig", - "kid": "ECDSA Private Key" - }, - { - "name": "ECDSA", - "namedCurve": curve.namedCurve, - "hash": { name: enums.read(enums.hash, hash_algo) } - }, - false, - ["sign"] - ); - - return await webCrypto.sign( - { - "name": 'ECDSA', - "namedCurve": curve.namedCurve, - "hash": { name: enums.read(enums.hash, hash_algo) } - }, - key, - m - ); - } catch(err) { - throw new Error(err); +async function webSign(curve, hash_algo, message, keyPair) { + var l = curve.pointSize; + if (typeof message === 'string') { + message = util.str2Uint8Array(message); } + const key = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": curve.namedCurve, + "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), null, 'base64url'), + "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), null, 'base64url'), + "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray('be', l)), null, 'base64url'), + "use": "sig", + "kid": "ECDSA Private Key" + }, + { + "name": "ECDSA", + "namedCurve": curve.namedCurve, + "hash": { name: curve.hashName } + }, + false, + ["sign"] + ); + + const signature = new Uint8Array(await webCrypto.sign( + { + "name": 'ECDSA', + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.webHash, hash_algo) } + }, + key, + message + )); + return { + r: signature.slice(0, l), + s: signature.slice(l, 2 * l) + }; } -async function webVerify(curve, hash_algo, signature, m, publicKey) { - try { - const key = await webCrypto.importKey( - "jwk", - { - "kty": "EC", - "crv": curve.namedCurve, - "x": base64.encode(new Uint8Array(publicKey.getX().toArray()), null, 'base64url'), - "y": base64.encode(new Uint8Array(publicKey.getY().toArray()), null, 'base64url'), - "use": "sig", - "kid": "ECDSA Public Key" - }, - { - "name": "ECDSA", - "namedCurve": curve.namedCurve, - "hash": { name: enums.read(enums.hash, hash_algo) } - }, - false, - ["verify"] - ); - - return await webCrypto.verify( - { - "name": 'ECDSA', - "namedCurve": curve.namedCurve, - "hash": { name: enums.read(enums.hash, hash_algo) } - }, - key, - signature, - m - ); - } catch(err) { - throw new Error(err); +async function webVerify(curve, hash_algo, signature, message, publicKey) { + var r = signature.r.toByteArray(), s = signature.s.toByteArray(), l = curve.pointSize; + r = (r.length === l) ? r : [0].concat(r); + s = (s.length === l) ? s : [0].concat(s); + signature = new Uint8Array(r.concat(s)).buffer; + if (typeof message === 'string') { + message = util.str2Uint8Array(message); } + const key = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": curve.namedCurve, + "x": base64.encode(new Uint8Array(publicKey.getX().toArray('be', l)), null, 'base64url'), + "y": base64.encode(new Uint8Array(publicKey.getY().toArray('be', l)), null, 'base64url'), + "use": "sig", + "kid": "ECDSA Public Key" + }, + { + "name": "ECDSA", + "namedCurve": curve.namedCurve, + "hash": { name: curve.hashName } + }, + false, + ["verify"] + ); + + return webCrypto.verify( + { + "name": 'ECDSA', + "namedCurve": curve.namedCurve, + "hash": { name: enums.read(enums.webHash, hash_algo) } + }, + key, + signature, + message + ); } @@ -199,7 +208,6 @@ async function nodeSign(curve, hash_algo, message, keyPair) { { private: true } ); - // FIXME what happens when hash_algo = undefined? const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo)); sign.write(message); sign.end(); diff --git a/src/enums.js b/src/enums.js index edaecb5d..a043429b 100644 --- a/src/enums.js +++ b/src/enums.js @@ -75,6 +75,17 @@ export default { sha224: 11 }, + /** A list of hash names as accepted by webCrypto functions. + * {@link https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest|Parameters, algo} + * @enum {String} + */ + webHash: { + 'SHA-1': 2, + 'SHA-256': 8, + 'SHA-384': 9, + 'SHA-512': 10 + }, + /** A list of packet types and numeric tags associated with them. * @enum {Integer} * @readonly diff --git a/src/message.js b/src/message.js index a73d4215..311dbcd9 100644 --- a/src/message.js +++ b/src/message.js @@ -124,23 +124,21 @@ Message.prototype.decrypt = async function(privateKey, sessionKey, password) { * { data:Uint8Array, algorithm:String } */ Message.prototype.decryptSessionKey = function(privateKey, password) { - var keyPacket, results, error; + var keyPacket; return Promise.resolve().then(async () => { if (password) { var symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey); - // FIXME need a circuit breaker here if (!symESKeyPacketlist) { throw new Error('No symmetrically encrypted session key packet found.'); } - results = await Promise.all(symESKeyPacketlist.map(async function(packet) { + // TODO replace when Promise.some or Promise.any are implemented + await symESKeyPacketlist.some(async function(packet) { try { await packet.decrypt(password); - return packet; - } catch (err) { - error = err; - } - })); - keyPacket = results.find(result => result !== undefined); + keyPacket = packet; + return true; + } catch (err) {} + }); } else if (privateKey) { var encryptionKeyIds = this.getEncryptionKeyIds(); @@ -156,19 +154,17 @@ Message.prototype.decryptSessionKey = function(privateKey, password) { if (!pkESKeyPacketlist) { throw new Error('No public key encrypted session key packet found.'); } - // FIXME need a circuit breaker here - results = await Promise.all(pkESKeyPacketlist.map(async function(packet) { + // TODO replace when Promise.some or Promise.any are implemented + // eslint-disable-next-line no-await-in-loop + await pkESKeyPacketlist.some(async function(packet) { if (packet.publicKeyId.equals(privateKeyPacket.getKeyId())) { try { - await packet.decrypt(privateKeyPacket) - return packet; - } catch (err) { - error = err; - } + await packet.decrypt(privateKeyPacket); + keyPacket = packet; + return true; + } catch (err) {} } - })); - keyPacket = results.find(result => result !== undefined); - + }); } else { throw new Error('No key or password specified.'); } @@ -225,14 +221,6 @@ Message.prototype.getText = function() { Message.prototype.encrypt = function(keys, passwords, sessionKey) { let symAlgo, msg, symEncryptedPacket; return Promise.resolve().then(async () => { - if (keys) { - symAlgo = enums.read(enums.symmetric, keyModule.getPreferredSymAlgo(keys)); - } else if (passwords) { - symAlgo = enums.read(enums.symmetric, config.encryption_cipher); - } else { - throw new Error('No keys or passwords'); - } - if (sessionKey) { if (!util.isUint8Array(sessionKey.data) || !util.isString(sessionKey.algorithm)) { throw new Error('Invalid session key for encryption.'); @@ -291,6 +279,7 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) { return Promise.resolve().then(async () => { if (publicKeys) { results = await Promise.all(publicKeys.map(async function(key) { + await key.verifyPrimaryUser(); var encryptionKeyPacket = key.getEncryptionKeyPacket(); if (!encryptionKeyPacket) { throw new Error('Could not find valid key packet for encryption in key ' + key.primaryKey.getKeyId().toHex()); @@ -338,62 +327,67 @@ Message.prototype.sign = async function(privateKeys=[], signature=null) { throw new Error('No literal data packet to sign.'); } + var i; var literalFormat = enums.write(enums.literal, literalDataPacket.format); var signatureType = literalFormat === enums.literal.binary ? enums.signature.binary : enums.signature.text; - var i, signingKeyPacket, existingSigPacketlist, onePassSig; if (signature) { - existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature); - if (existingSigPacketlist.length) { - for (i = existingSigPacketlist.length - 1; i >= 0; i--) { - var sigPacket = existingSigPacketlist[i]; - onePassSig = new packet.OnePassSignature(); - onePassSig.type = signatureType; - onePassSig.hashAlgorithm = config.prefer_hash_algorithm; - onePassSig.publicKeyAlgorithm = sigPacket.publicKeyAlgorithm; - onePassSig.signingKeyId = sigPacket.issuerKeyId; - if (!privateKeys.length && i === 0) { - onePassSig.flags = 1; - } - packetlist.push(onePassSig); + var existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature); + for (i = existingSigPacketlist.length - 1; i >= 0; i--) { + var signaturePacket = existingSigPacketlist[i]; + var onePassSig = new packet.OnePassSignature(); + onePassSig.type = signatureType; + onePassSig.hashAlgorithm = signaturePacket.hashAlgorithm; + onePassSig.publicKeyAlgorithm = signaturePacket.publicKeyAlgorithm; + onePassSig.signingKeyId = signaturePacket.issuerKeyId; + if (!privateKeys.length && i === 0) { + onePassSig.flags = 1; } + packetlist.push(onePassSig); } } - for (i = 0; i < privateKeys.length; i++) { - if (privateKeys[i].isPublic()) { + + await Promise.all(privateKeys.map(async function (privateKey, i) { + if (privateKey.isPublic()) { throw new Error('Need private key for signing'); } + await privateKey.verifyPrimaryUser(); + var signingKeyPacket = privateKey.getSigningKeyPacket(); + if (!signingKeyPacket) { + throw new Error('Could not find valid key packet for signing in key ' + + privateKey.primaryKey.getKeyId().toHex()); + } onePassSig = new packet.OnePassSignature(); onePassSig.type = signatureType; //TODO get preferred hash algo from key signature - onePassSig.hashAlgorithm = config.prefer_hash_algorithm; - signingKeyPacket = privateKeys[i].getSigningKeyPacket(); - if (!signingKeyPacket) { - throw new Error('Could not find valid key packet for signing in key ' + privateKeys[i].primaryKey.getKeyId().toHex()); - } + onePassSig.hashAlgorithm = keyModule.getPreferredHashAlgorithm(privateKey); onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm; onePassSig.signingKeyId = signingKeyPacket.getKeyId(); if (i === privateKeys.length - 1) { onePassSig.flags = 1; } - packetlist.push(onePassSig); - } + return onePassSig; + })).then(onePassSignatureList => { + onePassSignatureList.forEach(onePassSig => packetlist.push(onePassSig)); + }); packetlist.push(literalDataPacket); await Promise.all(privateKeys.reverse().map(async function(privateKey) { - var signingKeyPacket = privateKey.getSigningKeyPacket(); var signaturePacket = new packet.Signature(); - signaturePacket.signatureType = signatureType; - signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; + var signingKeyPacket = privateKey.getSigningKeyPacket(); if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } + signaturePacket.signatureType = signatureType; + signaturePacket.hashAlgorithm = keyModule.getPreferredHashAlgorithm(privateKey); + signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; await signaturePacket.sign(signingKeyPacket, literalDataPacket); - packetlist.push(signaturePacket); - })); + return signaturePacket; + })).then(signatureList => { + signatureList.forEach(signaturePacket => packetlist.push(signaturePacket)); + }); if (signature) { packetlist.concat(existingSigPacketlist); @@ -422,17 +416,20 @@ Message.prototype.signDetached = async function(privateKeys=[], signature=null) enums.signature.binary : enums.signature.text; await Promise.all(privateKeys.map(async function(privateKey) { - var signingKeyPacket = privateKey.getSigningKeyPacket(); var signaturePacket = new packet.Signature(); - signaturePacket.signatureType = signatureType; - signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; + await privateKey.verifyPrimaryUser(); + var signingKeyPacket = privateKey.getSigningKeyPacket(); if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); } + signaturePacket.signatureType = signatureType; + signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; + signaturePacket.hashAlgorithm = keyModule.getPreferredHashAlgorithm(privateKey); await signaturePacket.sign(signingKeyPacket, literalDataPacket); - packetlist.push(signaturePacket); - })); + return signaturePacket; + })).then(signatureList => { + signatureList.forEach(signaturePacket => packetlist.push(signaturePacket)); + }); if (signature) { var existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature); @@ -481,34 +478,33 @@ Message.prototype.verifyDetached = function(signature, keys) { * @param {Array} keys array of keys to verify signatures * @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature */ -function createVerificationObjects(signatureList, literalDataList, keys) { - var result = []; - for (var i = 0; i < signatureList.length; i++) { +async function createVerificationObjects(signatureList, literalDataList, keys) { + return Promise.all(signatureList.map(async function(signature) { var keyPacket = null; - for (var j = 0; j < keys.length; j++) { - keyPacket = keys[j].getSigningKeyPacket(signatureList[i].issuerKeyId, config.verify_expired_keys); - if (keyPacket) { - break; + await Promise.all(keys.map(async function(key) { + await key.verifyPrimaryUser(); + var result = key.getSigningKeyPacket(signature.issuerKeyId, config.verify_expired_keys); + if (result) { + keyPacket = result; } - } + })); var verifiedSig = {}; if (keyPacket) { //found a key packet that matches keyId of signature - verifiedSig.keyid = signatureList[i].issuerKeyId; - verifiedSig.valid = signatureList[i].verify(keyPacket, literalDataList[0]); + verifiedSig.keyid = signature.issuerKeyId; + verifiedSig.valid = await signature.verify(keyPacket, literalDataList[0]); } else { - verifiedSig.keyid = signatureList[i].issuerKeyId; + verifiedSig.keyid = signature.issuerKeyId; verifiedSig.valid = null; } var packetlist = new packet.List(); - packetlist.push(signatureList[i]); + packetlist.push(signature); verifiedSig.signature = new sigModule.Signature(packetlist); - result.push(verifiedSig); - } - return Promise.all(result); + return verifiedSig; + })); } /** diff --git a/src/worker/worker.js b/src/worker/worker.js index 2b25f319..1e411d18 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -19,7 +19,7 @@ self.window = {}; // to make UMD bundles work -importScripts('openpgp.js'); +importScripts('openpgp_browser.js'); // FIXME var openpgp = window.openpgp; var MIN_SIZE_RANDOM_BUFFER = 40000; diff --git a/test/general/ecc.js b/test/general/ecc.js index 59e19fe5..92bb7c52 100644 --- a/test/general/ecc.js +++ b/test/general/ecc.js @@ -165,7 +165,7 @@ describe('Elliptic Curve Cryptography', function () { expect(result).to.exist; expect(result.data.trim()).to.equal(data.juliet.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.eventually.be.true; + expect(result.signatures[0].valid).to.be.true; }); }); // FIXME is this pattern correct? @@ -178,7 +178,7 @@ describe('Elliptic Curve Cryptography', function () { expect(result).to.exist; expect(result.data.trim()).to.equal(data.romeo.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.eventually.be.true; + expect(result.signatures[0].valid).to.be.true; }); }); }); @@ -193,7 +193,7 @@ describe('Elliptic Curve Cryptography', function () { // trim required because https://github.com/openpgpjs/openpgpjs/issues/311 expect(result.data.trim()).to.equal(data.juliet.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.eventually.be.true; + expect(result.signatures[0].valid).to.be.true; }); }); it('Encrypt and sign message', function () { @@ -212,7 +212,7 @@ describe('Elliptic Curve Cryptography', function () { expect(result).to.exist; expect(result.data.trim()).to.equal(data.romeo.message); expect(result.signatures).to.have.length(1); - expect(result.signatures[0].valid).to.eventually.be.true; + expect(result.signatures[0].valid).to.be.true; }); }); }); diff --git a/test/general/key.js b/test/general/key.js index 4eab2195..2722b07d 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -1172,7 +1172,7 @@ describe('Key', function() { return openpgp.encrypt({data: 'hello', publicKeys: newKey.toPublic(), privateKeys: newKey, armor: true}).then(function(encrypted) { return openpgp.decrypt({message: openpgp.message.readArmored(encrypted.data), privateKey: newKey, publicKeys: newKey.toPublic()}).then(function(decrypted) { expect(decrypted.data).to.equal('hello'); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; }); }); }); diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 6070b3a2..09b4eee1 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -712,7 +712,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -756,7 +756,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -789,7 +789,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -826,10 +826,10 @@ describe('OpenPGP.js public api tests', function() { return openpgp.decrypt(decOpt); }).then(function(decrypted) { expect(decrypted.data).to.equal(plaintext); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); - expect(decrypted.signatures[1].valid).to.eventually.be.true; + expect(decrypted.signatures[1].valid).to.be.true; expect(decrypted.signatures[1].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[1].signature.packets.length).to.equal(1); }); @@ -998,7 +998,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.eventually.be.true; + expect(verified.signatures[0].valid).to.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1019,7 +1019,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.eventually.be.true; + expect(verified.signatures[0].valid).to.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1079,7 +1079,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.eventually.be.true; + expect(verified.signatures[0].valid).to.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1101,7 +1101,7 @@ describe('OpenPGP.js public api tests', function() { return openpgp.verify(verifyOpt); }).then(function(verified) { expect(verified.data).to.equal(plaintext); - expect(verified.signatures[0].valid).to.eventually.be.true; + expect(verified.signatures[0].valid).to.be.true; expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(verified.signatures[0].signature.packets.length).to.equal(1); }); @@ -1126,7 +1126,7 @@ describe('OpenPGP.js public api tests', function() { }).then(function(encrypted) { expect(encrypted.data).to.exist; expect(encrypted.data).to.equal(plaintext); - expect(encrypted.signatures[0].valid).to.eventually.be.true; + expect(encrypted.signatures[0].valid).to.be.true; expect(encrypted.signatures[0].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); expect(encrypted.signatures[0].signature.packets.length).to.equal(1); }); diff --git a/test/general/signature.js b/test/general/signature.js index 57c0eff2..efe2db14 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -342,7 +342,7 @@ describe("Signature", function() { priv_key.decrypt("abcd"); return openpgp.decrypt({ privateKey: priv_key, publicKeys:[pub_key], message:msg }).then(function(decrypted) { expect(decrypted.data).to.exist; - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); }); @@ -386,7 +386,7 @@ describe("Signature", function() { return msg.verify([pub_key]).then(verified => { expect(verified).to.exist; expect(verified).to.have.length(1); - expect(verified[0].valid).to.eventually.be.true; + expect(verified[0].valid).to.be.true; expect(verified[0].signature.packets.length).to.equal(1); }); }); @@ -411,7 +411,7 @@ describe("Signature", function() { return sMsg.verify([pub_key]).then(verified => { expect(verified).to.exist; expect(verified).to.have.length(1); - expect(verified[0].valid).to.eventually.be.true; + expect(verified[0].valid).to.be.true; expect(verified[0].signature.packets.length).to.equal(1); }); }); @@ -435,7 +435,7 @@ describe("Signature", function() { sMsg.verify([pub_key]).then(verified => { expect(verified).to.exist; expect(verified).to.have.length(1); - expect(verified[0].valid).to.eventually.be.true; + expect(verified[0].valid).to.be.true; expect(verified[0].signature.packets.length).to.equal(1); }); }); @@ -470,7 +470,7 @@ describe("Signature", function() { expect(decrypted.data).to.exist; expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); }); @@ -506,7 +506,7 @@ describe("Signature", function() { expect(decrypted.data).to.exist; expect(decrypted.data).to.equal(plaintext); expect(decrypted.signatures).to.have.length(1); - expect(decrypted.signatures[0].valid).to.eventually.be.true; + expect(decrypted.signatures[0].valid).to.be.true; expect(decrypted.signatures[0].signature.packets.length).to.equal(1); }); @@ -547,8 +547,8 @@ describe("Signature", function() { sMsg.verify([pubKey2, pubKey3]).then(verifiedSig => { expect(verifiedSig).to.exist; expect(verifiedSig).to.have.length(2); - expect(verifiedSig[0].valid).to.eventually.be.true; - expect(verifiedSig[1].valid).to.eventually.be.true; + expect(verifiedSig[0].valid).to.be.true; + expect(verifiedSig[1].valid).to.be.true; expect(verifiedSig[0].signature.packets.length).to.equal(1); expect(verifiedSig[1].signature.packets.length).to.equal(1); }); @@ -593,8 +593,8 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.equal(plaintext); expect(cleartextSig.signatures).to.have.length(2); - expect(cleartextSig.signatures[0].valid).to.eventually.be.true; - expect(cleartextSig.signatures[1].valid).to.eventually.be.true; + expect(cleartextSig.signatures[0].valid).to.be.true; + expect(cleartextSig.signatures[1].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); expect(cleartextSig.signatures[1].signature.packets.length).to.equal(1); }); @@ -615,7 +615,7 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.equal(plaintext.replace(/\r/g,'')); expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.eventually.be.true; + expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); @@ -636,7 +636,7 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.deep.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.eventually.be.true; + expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); @@ -657,7 +657,7 @@ describe("Signature", function() { expect(cleartextSig).to.exist; expect(cleartextSig.data).to.deep.equal(plaintext); expect(cleartextSig.signatures).to.have.length(1); - expect(cleartextSig.signatures[0].valid).to.eventually.be.true; + expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); @@ -781,7 +781,7 @@ describe("Signature", function() { var msg = openpgp.message.readSignedContent(content, detachedSig); return msg.verify(publicKeys).then(result => { - expect(result[0].valid).to.eventually.be.true; + expect(result[0].valid).to.be.true; }); }); @@ -797,8 +797,8 @@ describe("Signature", function() { var generatedKey = gen.key; return msg.signDetached([generatedKey, privKey2]).then(detachedSig => { return msg.verifyDetached(detachedSig, [generatedKey.toPublic(), pubKey2]).then(result => { - expect(result[0].valid).to.eventually.be.true; - expect(result[1].valid).to.eventually.be.true; + expect(result[0].valid).to.be.true; + expect(result[1].valid).to.be.true; }); }); }); diff --git a/test/unittests.html b/test/unittests.html index 8f860f19..3ee2b4a6 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -14,7 +14,7 @@ -] From 35f18444b058c4117838491344193819ee9600b4 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:43:08 -0800 Subject: [PATCH 40/75] keygen and sign/verify with ed25519 works --- src/crypto/crypto.js | 5 +- src/crypto/public_key/elliptic/curves.js | 160 +++++++++++++---------- src/crypto/public_key/elliptic/ecdsa.js | 30 +++-- src/crypto/public_key/elliptic/eddsa.js | 50 +++++++ src/crypto/public_key/elliptic/index.js | 11 +- src/crypto/public_key/elliptic/key.js | 23 ++-- src/crypto/signature.js | 35 ++++- src/enums.js | 34 ++++- src/openpgp.js | 6 +- src/packet/signature.js | 4 +- 10 files changed, 252 insertions(+), 106 deletions(-) create mode 100644 src/crypto/public_key/elliptic/eddsa.js diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 92178491..4c832726 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -183,6 +183,7 @@ export default { return ['mpi']; case 'ecdh': case 'ecdsa': + case 'eddsa': // Algorithm-Specific Fields for ECDSA or ECDH secret keys: // - MPI of an integer representing the secret key. return ['mpi']; @@ -217,10 +218,11 @@ export default { // - MPI of DSA public-key value y (= g**x mod p where x is secret). case 'dsa': return ['mpi', 'mpi', 'mpi', 'mpi']; - // Algorithm-Specific Fields for ECDSA public keys: + // Algorithm-Specific Fields for ECDSA/EdDSA public keys: // - OID of curve; // - MPI of EC point representing public key. case 'ecdsa': + case 'eddsa': return ['oid', 'mpi']; // Algorithm-Specific Fields for ECDH public keys: // - OID of curve; @@ -279,6 +281,7 @@ export default { }); case 'ecdsa': + case 'eddsa': return publicKey.elliptic.generate(curve).then(function (keyObject) { return constructParams([keyObject.oid, keyObject.Q, keyObject.d], types); }); diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 4d03e045..16ec7f53 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -27,118 +27,144 @@ 'use strict'; -import {ec as EC} from 'elliptic'; -import {KeyPair} from './key.js'; -import BigInteger from '../jsbn.js'; +import { ec as EC, eddsa as EdDSA } from 'elliptic'; +import { KeyPair } from './key'; +import BigInteger from '../jsbn'; +import random from '../../random'; import config from '../../../config'; -import enums from '../../../enums.js'; -import util from '../../../util.js'; -import base64 from '../../../encoding/base64.js'; +import enums from '../../../enums'; +import util from '../../../util'; +import base64 from '../../../encoding/base64'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); -var webCurves = [], nodeCurves = []; +var webCurves = {}, nodeCurves = {}; if (webCrypto && config.use_native) { - // see https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API/Supported_algorithms - webCurves = ['P-256', 'P-384', 'P-521']; + webCurves = { + 'p256': 'P-256', + 'p384': 'P-384', + 'p521': 'P-521' + }; } else if (nodeCrypto && config.use_native) { - // FIXME make sure the name translations are correct - nodeCurves = nodeCrypto.getCurves(); + var knownCurves = nodeCrypto.getCurves(); + nodeCurves = { + 'secp256k1': knownCurves.includes('secp256k1') ? 'secp256k1' : undefined, + 'p256': knownCurves.includes('prime256v1') ? 'prime256v1' : undefined, + 'p384': knownCurves.includes('secp384r1') ? 'secp384r1' : undefined, + 'p521': knownCurves.includes('secp521r1') ? 'secp521r1' : undefined + // TODO add more here + }; } const curves = { p256: { oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), - pointSize: 66, // FIXME - namedCurve: 'P-256', - opensslCurve: 'prime256v1', - hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: nodeCurves.includes('prime256v1'), - web: webCurves.includes('P-256') + node: nodeCurves.secp256r1, + web: webCurves.secp256r1, + payloadSize: 32 }, p384: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), - pointSize: 48, - namedCurve: 'P-384', - opensslCurve: 'secp384r1', // FIXME - hashName: 'SHA-384', hash: enums.hash.sha384, cipher: enums.symmetric.aes192, - node: nodeCurves.includes('secp384r1'), // FIXME - web: webCurves.includes('P-384') + node: nodeCurves.secp384r1, + web: webCurves.secp384r1, + payloadSize: 48 }, p521: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), - pointSize: 66, - namedCurve: 'P-521', - opensslCurve: 'secp521r1', // FIXME - hashName: 'SHA-512', hash: enums.hash.sha512, cipher: enums.symmetric.aes256, - node: nodeCurves.includes('secp521r1'), // FIXME - web: webCurves.includes('P-521') + node: nodeCurves.secp521r1, + web: webCurves.secp521r1, + payloadSize: 66 }, secp256k1: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), - pointSize: 66, // FIXME - namedCurve: 'SECP-256K1', - opensslCurve: 'secp256k1', - hashName: 'SHA-256', hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: false, // FIXME nodeCurves.includes('secp256k1'), - // this is because jwk-to-pem does not support this curve. - web: false + node: false // FIXME when we replace jwk-to-pem or it supports this curve }, - curve25519 : {}, - ed25519 : {} + ed25519: { + oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), + hash: enums.hash.sha512, + keyType: enums.publicKey.eddsa + }, + curve25519: { + oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), + hash: enums.hash.sha256, + cipher: enums.symmetric.aes128 + }, + brainpoolP256r1: { // TODO 1.3.36.3.3.2.8.1.1.7 + oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07]) + }, + brainpoolP512r1: { // TODO 1.3.36.3.3.2.8.1.1.13 + oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D]) + } }; -function Curve(name, {oid, pointSize, hash, cipher, namedCurve, opensslCurve, hashName, node, web}) { - this.curve = new EC(name); - this.name = name; - this.oid = oid; - this.pointSize = pointSize; - this.hash = hash; - this.cipher = cipher; - this.namedCurve= namedCurve; - this.opensslCurve = opensslCurve; - this.hashName = hashName; - this.node = node; - this.web = web; +function Curve(name, params) { + if (params.keyType === enums.publicKey.eddsa) { + this.curve = new EdDSA(name); + this.keyType = enums.publicKey.eddsa; + } else { + this.curve = new EC(name); + this.keyType = enums.publicKey.ecdsa; + } + this.oid = curves[name].oid; + this.hash = params.hash; + this.cipher = params.cipher; + this.node = params.node && curves[name].node; + this.web = params.web && curves[name].web; + this.payloadSize = curves[name].payloadSize; } -Curve.prototype.keyFromPrivate = function (priv) { - return new KeyPair(this.curve, {priv: priv}); +Curve.prototype.keyFromPrivate = function (priv) { // Not for ed25519 + return new KeyPair(this.curve, { priv: priv }); +}; + +Curve.prototype.keyFromSecret = function (secret) { // Only for ed25519 + return new KeyPair(this.curve, { secret: secret }); }; Curve.prototype.keyFromPublic = function (pub) { - return new KeyPair(this.curve, {pub: pub}); + return new KeyPair(this.curve, { pub: pub }); }; Curve.prototype.genKeyPair = async function () { - var keyPair; + var r, keyPair; if (webCrypto && config.use_native && this.web) { - keyPair = await webGenKeyPair(this.namedCurve, "ECDSA"); // FIXME + keyPair = await webGenKeyPair(this.name, "ECDSA"); // FIXME is ECDH different? } else if (nodeCrypto && config.use_native && this.node) { - keyPair = await nodeGenKeyPair(this.opensslCurve); + keyPair = await nodeGenKeyPair(this.name); } else { - var r = this.curve.genKeyPair(); - keyPair = { - pub: r.getPublic().encode(), - priv: r.getPrivate().toArray() - }; + if (this.keyType === enums.publicKey.eddsa) { + keyPair = { + secret: util.hexidump(random.getRandomBytes(32)) + }; + } else { + r = this.curve.genKeyPair(); + keyPair = { + pub: r.getPublic().encode(), + priv: r.getPrivate().toArray() + }; + } } return new KeyPair(this.curve, keyPair); }; function get(oid_or_name) { - for (var name in curves) { - if (curves[name].oid === oid_or_name || name === oid_or_name) { + var name; + if (enums.curve[oid_or_name]) { + name = enums.write(enums.curve, oid_or_name); + return new Curve(name, curves[name]); + } + for (name in curves) { + if (curves[name].oid === oid_or_name) { return new Curve(name, curves[name]); } } @@ -171,11 +197,11 @@ module.exports = { ////////////////////////// -async function webGenKeyPair(namedCurve, algorithm) { +async function webGenKeyPair(name, algorithm) { var webCryptoKey = await webCrypto.generateKey( { name: algorithm === "ECDH" ? "ECDH" : "ECDSA", - namedCurve: namedCurve + namedCurve: webCurves[name] }, true, algorithm === "ECDH" ? ["deriveKey", "deriveBits"] : ["sign", "verify"] @@ -193,8 +219,8 @@ async function webGenKeyPair(namedCurve, algorithm) { }; } -async function nodeGenKeyPair(opensslCurve) { - var ecdh = nodeCrypto.createECDH(opensslCurve); +async function nodeGenKeyPair(name) { + var ecdh = nodeCrypto.createECDH(name === "secp256r1" ? "prime256v1" : name); await ecdh.generateKeys(); return { diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index dd75150b..23c60860 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -37,7 +37,9 @@ import util from '../../../util.js'; import base64 from '../../../encoding/base64.js'; const webCrypto = util.getWebCrypto(); +const webCurves = curves.webCurves; const nodeCrypto = util.getNodeCrypto(); +const nodeCurves = curves.nodeCurves; var ECDSASignature = ASN1.define('ECDSASignature', function() { this.seq().obj( @@ -67,8 +69,8 @@ async function sign(oid, hash_algo, m, d) { signature = await key.sign(m, hash_algo); } return { - r: new BigInteger(signature.r), - s: new BigInteger(signature.s) + r: new BigInteger(signature.r.toArray()), + s: new BigInteger(signature.s.toArray()) }; } @@ -112,7 +114,7 @@ module.exports = { async function webSign(curve, hash_algo, message, keyPair) { - var l = curve.pointSize; + var l = curve.payloadSize; if (typeof message === 'string') { message = util.str2Uint8Array(message); } @@ -120,7 +122,7 @@ async function webSign(curve, hash_algo, message, keyPair) { "jwk", { "kty": "EC", - "crv": curve.namedCurve, + "crv": webCurves[curve.name], "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), null, 'base64url'), "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), null, 'base64url'), "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray('be', l)), null, 'base64url'), @@ -129,8 +131,8 @@ async function webSign(curve, hash_algo, message, keyPair) { }, { "name": "ECDSA", - "namedCurve": curve.namedCurve, - "hash": { name: curve.hashName } + "namedCurve": webCurves[curve.name], + "hash": { name: enums.read(enums.webHash, curve.hash) } }, false, ["sign"] @@ -139,7 +141,7 @@ async function webSign(curve, hash_algo, message, keyPair) { const signature = new Uint8Array(await webCrypto.sign( { "name": 'ECDSA', - "namedCurve": curve.namedCurve, + "namedCurve": webCurves[curve.name], "hash": { name: enums.read(enums.webHash, hash_algo) } }, key, @@ -152,7 +154,7 @@ async function webSign(curve, hash_algo, message, keyPair) { } async function webVerify(curve, hash_algo, signature, message, publicKey) { - var r = signature.r.toByteArray(), s = signature.s.toByteArray(), l = curve.pointSize; + var r = signature.r.toByteArray(), s = signature.s.toByteArray(), l = curve.payloadSize; r = (r.length === l) ? r : [0].concat(r); s = (s.length === l) ? s : [0].concat(s); signature = new Uint8Array(r.concat(s)).buffer; @@ -163,7 +165,7 @@ async function webVerify(curve, hash_algo, signature, message, publicKey) { "jwk", { "kty": "EC", - "crv": curve.namedCurve, + "crv": webCurves[curve.name], "x": base64.encode(new Uint8Array(publicKey.getX().toArray('be', l)), null, 'base64url'), "y": base64.encode(new Uint8Array(publicKey.getY().toArray('be', l)), null, 'base64url'), "use": "sig", @@ -171,8 +173,8 @@ async function webVerify(curve, hash_algo, signature, message, publicKey) { }, { "name": "ECDSA", - "namedCurve": curve.namedCurve, - "hash": { name: curve.hashName } + "namedCurve": webCurves[curve.name], + "hash": { name: enums.read(enums.webHash, curve.hash) } }, false, ["verify"] @@ -181,7 +183,7 @@ async function webVerify(curve, hash_algo, signature, message, publicKey) { return webCrypto.verify( { "name": 'ECDSA', - "namedCurve": curve.namedCurve, + "namedCurve": webCurves[curve.name], "hash": { name: enums.read(enums.webHash, hash_algo) } }, key, @@ -198,7 +200,7 @@ async function nodeSign(curve, hash_algo, message, keyPair) { const key = jwkToPem( { "kty": "EC", - "crv": curve.namedCurve, + "crv": nodeCurves[curve.name], "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray())), "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray())), "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray())), @@ -231,7 +233,7 @@ async function nodeVerify(curve, hash_algo, signature, message, publicKey) { const key = jwkToPem( { "kty": "EC", - "crv": curve.namedCurve, + "crv": nodeCurves[curve.name], "x": base64.encode(new Uint8Array(publicKey.getX().toArray())), "y": base64.encode(new Uint8Array(publicKey.getY().toArray())), "use": "sig", diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js new file mode 100644 index 00000000..3309979e --- /dev/null +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -0,0 +1,50 @@ +// Implementation of EdDSA for OpenPGP + +'use strict'; + +import curves from './curves.js'; +import BigInteger from '../jsbn.js'; + +/** + * Sign a message using the provided key + * @param {String} oid Elliptic curve for the key + * @param {enums.hash} hash_algo Hash algorithm used to sign + * @param {Uint8Array} m Message to sign + * @param {BigInteger} d Private key used to sign + * @return {{r: BigInteger, s: BigInteger}} Signature of the message + */ +async function sign(oid, hash_algo, m, d) { + var signature; + const curve = curves.get(oid); + hash_algo = hash_algo ? hash_algo : curve.hash; + const key = curve.keyFromSecret(d.toByteArray()); + signature = await key.sign(m, hash_algo); + return { + r: new BigInteger(signature.Rencoded()), + s: new BigInteger(signature.Sencoded()) + }; +} + +/** + * Verifies if a signature is valid for a message + * @param {String} oid Elliptic curve for the key + * @param {enums.hash} hash_algo Hash algorithm used in the signature + * @param {{r: BigInteger, s: BigInteger}} signature Signature to verify + * @param {Uint8Array} m Message to verify + * @param {BigInteger} Q Public key used to verify the message + * @return {Boolean} + */ +async function verify(oid, hash_algo, signature, m, Q) { + var result; + const curve = curves.get(oid); + hash_algo = hash_algo ? hash_algo : curve.hash; // FIXME is this according to the RFC? + const key = curve.keyFromPublic(Q.toByteArray()); + return key.verify( + m, {R: signature.r.toByteArray(), S: signature.s.toByteArray()}, hash_algo + ); +} + +module.exports = { + sign: sign, + verify: verify +}; diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index f6acefb1..f4a5cc47 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -21,18 +21,21 @@ * @requires crypto/public_key/elliptic/curve * @requires crypto/public_key/elliptic/ecdh * @requires crypto/public_key/elliptic/ecdsa + * @requires crypto/public_key/elliptic/eddsa * @module crypto/public_key/elliptic */ 'use strict'; -import {get, generate} from './curves.js'; -import ecdh from './ecdh.js'; -import ecdsa from './ecdsa.js'; +import {get, generate} from './curves'; +import ecdsa from './ecdsa'; +import eddsa from './eddsa'; +import ecdh from './ecdh'; module.exports = { - ecdh: ecdh, ecdsa: ecdsa, + eddsa: eddsa, + ecdh: ecdh, get: get, generate: generate }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 875a62f5..66a21c35 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -26,10 +26,12 @@ 'use strict'; import hash from '../../hash'; -import util from '../../../util.js'; +import util from '../../../util'; +import enums from '../../../enums'; function KeyPair(curve, options) { this.curve = curve; + this.keyType = curve.curve.type === 'edwards' ? enums.publicKey.eddsa : enums.publicKey.ecdsa; this.keyPair = this.curve.keyPair(options); } @@ -38,11 +40,7 @@ KeyPair.prototype.sign = function (message, hash_algo) { message = util.str2Uint8Array(message); } const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); - const signature = this.keyPair.sign(digest); - return { - r: signature.r.toArray(), - s: signature.s.toArray() - }; + return this.keyPair.sign(digest); }; KeyPair.prototype.verify = function (message, signature, hash_algo) { @@ -54,18 +52,25 @@ KeyPair.prototype.verify = function (message, signature, hash_algo) { }; KeyPair.prototype.derive = function (pub) { + if (this.keyType === enums.publicKey.eddsa) { + throw new Error('Key can only be used for EdDSA'); + } return this.keyPair.derive(pub.keyPair.getPublic()).toArray(); }; KeyPair.prototype.getPublic = function () { - return this.keyPair.getPublic().encode(); + return this.keyPair.getPublic('array'); }; KeyPair.prototype.getPrivate = function () { - return this.keyPair.getPrivate().toArray(); + if (this.keyType === enums.publicKey.eddsa) { + return this.keyPair.getSecret(); + } else { + return this.keyPair.getPrivate().toArray(); + } }; -KeyPair.prototype.isValid = function () { +KeyPair.prototype.isValid = function () { // FIXME return this.keyPair.validate().result; }; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 13bb3ba4..d30d87c3 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -23,6 +23,10 @@ export default { */ verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { var m; + var r; + var s; + var Q; + var curve; data = util.Uint8Array2str(data); @@ -59,12 +63,21 @@ export default { case 19: // ECDSA const ecdsa = publicKey.elliptic.ecdsa; - const curve = publickey_MPIs[0]; - const r = msg_MPIs[0].toBigInteger(); - const s = msg_MPIs[1].toBigInteger(); + curve = publickey_MPIs[0]; + r = msg_MPIs[0].toBigInteger(); + s = msg_MPIs[1].toBigInteger(); m = data; - const Q = publickey_MPIs[1].toBigInteger(); + Q = publickey_MPIs[1].toBigInteger(); return ecdsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); + case 22: + // EdDSA + const eddsa = publicKey.elliptic.eddsa; + curve = publickey_MPIs[0]; + r = msg_MPIs[0].toBigInteger(); + s = msg_MPIs[1].toBigInteger(); + m = data; + Q = publickey_MPIs[1].toBigInteger(); + return eddsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); default: throw new Error('Invalid signature algorithm.'); } @@ -84,6 +97,8 @@ export default { var m; var d; + var curve; + var signature; switch (algo) { case 1: @@ -117,10 +132,18 @@ export default { case 19: // ECDSA var ecdsa = publicKey.elliptic.ecdsa; - var curve = keyIntegers[0]; + curve = keyIntegers[0]; d = keyIntegers[2].toBigInteger(); m = data; - const signature = await ecdsa.sign(curve.oid, hash_algo, m, d); + signature = await ecdsa.sign(curve.oid, hash_algo, m, d); + return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); + case 22: + // EdDSA + var eddsa = publicKey.elliptic.eddsa; + curve = keyIntegers[0]; + d = keyIntegers[2].toBigInteger(); + m = data; + signature = await eddsa.sign(curve.oid, hash_algo, m, d); return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); default: diff --git a/src/enums.js b/src/enums.js index a043429b..c4bf3651 100644 --- a/src/enums.js +++ b/src/enums.js @@ -6,6 +6,37 @@ export default { + /** Maps curve names under various standards to one + * @enum {String} + * @readonly + */ + curve: { + "p256": "p256", + "P-256": "p256", + "secp256r1": "p256", + "prime256v1": "p256", + "1.2.840.10045.3.1.7": "p256", + + "p384": "p384", + "P-384": "p384", + "secp384r1": "p384", + "1.3.132.0.34": "p384", + + "p521": "p521", + "P-521": "p521", + "secp521r1": "p521", + "1.3.132.0.35": "p521", + + "secp256k1": "secp256k1", + "1.3.132.0.10": "secp256k1", + + "ed25519": "ed25519", + "1.3.6.1.4.1.11591.15.1": "ed25519", + + "curve25519": "curve25519", + "1.3.6.1.4.1.3029.1.5.1": "curve25519" + }, + /** A string to key specifier type * @enum {Integer} * @readonly @@ -28,7 +59,8 @@ export default { elgamal: 16, dsa: 17, ecdh: 18, - ecdsa: 19 + ecdsa: 19, + eddsa: 22 }, /** {@link http://tools.ietf.org/html/rfc4880#section-9.2|RFC4880, section 9.2} diff --git a/src/openpgp.js b/src/openpgp.js index c2aca38f..006e2ce3 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -89,7 +89,7 @@ export function destroyWorker() { /** - * Generates a new OpenPGP key pair. Currently only supports RSA keys. Primary and subkey will be of same type. + * Generates a new OpenPGP key pair. Supports RSA and ECC keys. Primary and subkey will be of same type. * @param {Array} userIds array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }] * @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key * @param {Number} numBits (optional) number of bits for the key creation. (should be 2048 or 4096) @@ -100,7 +100,7 @@ export function destroyWorker() { * @static */ -export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve=""} = {}) { +export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0, curve="" } = {}) { userIds = formatUserIds(userIds); const options = {userIds, passphrase, numBits, unlocked, keyExpirationTime, curve}; @@ -338,7 +338,7 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { * @param {CleartextMessage} message cleartext message object with signatures * @param {Signature} signature (optional) detached signature for verification * @return {Promise} cleartext with status of verified signatures in the form of: - * { data:String, signatures: [{ keyid:String, valid:Boolean }] } + * { data:String, signatures: [{ keyid:String, valid:Boolean }] } * @static */ export function verify({ message, publicKeys, signature=null }) { diff --git a/src/packet/signature.js b/src/packet/signature.js index cce0fd5c..291611aa 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -633,7 +633,9 @@ Signature.prototype.verify = async function (key, data) { // Algorithm-Specific Fields for DSA and ECDSA signatures: // - MPI of DSA value r. // - MPI of DSA value s. - else if (publicKeyAlgorithm === 17 || publicKeyAlgorithm === 19) { + else if (publicKeyAlgorithm === enums.publicKey.dsa || + publicKeyAlgorithm === enums.publicKey.ecdsa || + publicKeyAlgorithm === enums.publicKey.eddsa) { mpicount = 2; } From 3e1d9c4d0d2de18907777d661f0457979a71cc0f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:44:50 -0800 Subject: [PATCH 41/75] Fixes ECDH with Curve25519 and key import/export problems --- src/crypto/public_key/elliptic/curves.js | 38 ++++++++++++++---------- src/crypto/public_key/elliptic/ecdh.js | 2 ++ src/crypto/public_key/elliptic/key.js | 10 +++++-- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 16ec7f53..d21d8121 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -60,6 +60,7 @@ if (webCrypto && config.use_native) { const curves = { p256: { oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), + keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, node: nodeCurves.secp256r1, @@ -68,6 +69,7 @@ const curves = { }, p384: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), + keyType: enums.publicKey.ecdsa, hash: enums.hash.sha384, cipher: enums.symmetric.aes192, node: nodeCurves.secp384r1, @@ -76,6 +78,7 @@ const curves = { }, p521: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), + keyType: enums.publicKey.ecdsa, hash: enums.hash.sha512, cipher: enums.symmetric.aes256, node: nodeCurves.secp521r1, @@ -84,17 +87,19 @@ const curves = { }, secp256k1: { oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), + keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, node: false // FIXME when we replace jwk-to-pem or it supports this curve }, ed25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), - hash: enums.hash.sha512, - keyType: enums.publicKey.eddsa + keyType: enums.publicKey.eddsa, + hash: enums.hash.sha512 }, curve25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), + keyType: enums.publicKey.ecdh, hash: enums.hash.sha256, cipher: enums.symmetric.aes128 }, @@ -107,12 +112,17 @@ const curves = { }; function Curve(name, params) { - if (params.keyType === enums.publicKey.eddsa) { - this.curve = new EdDSA(name); - this.keyType = enums.publicKey.eddsa; - } else { - this.curve = new EC(name); - this.keyType = enums.publicKey.ecdsa; + this.keyType = params.keyType; + switch (this.keyType) { + case enums.publicKey.eddsa: + this.curve = new EdDSA(name); + break; + case enums.publicKey.ecdsa: + case enums.publicKey.ecdh: + this.curve = new EC(name); + break; + default: + throw new Error('Unknown elliptic key type;'); } this.oid = curves[name].oid; this.hash = params.hash; @@ -141,16 +151,12 @@ Curve.prototype.genKeyPair = async function () { } else if (nodeCrypto && config.use_native && this.node) { keyPair = await nodeGenKeyPair(this.name); } else { + var compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; + r = await this.curve.genKeyPair(); if (this.keyType === enums.publicKey.eddsa) { - keyPair = { - secret: util.hexidump(random.getRandomBytes(32)) - }; + keyPair = { secret: r.getSecret() }; } else { - r = this.curve.genKeyPair(); - keyPair = { - pub: r.getPublic().encode(), - priv: r.getPrivate().toArray() - }; + keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() }; } } return new KeyPair(this.curve, keyPair); diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 8db86be6..f6a412c8 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -118,6 +118,8 @@ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { } module.exports = { + buildEcdhParam: buildEcdhParam, + kdf: kdf, encrypt: encrypt, decrypt: decrypt }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 66a21c35..f8e5b0e1 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -55,11 +55,12 @@ KeyPair.prototype.derive = function (pub) { if (this.keyType === enums.publicKey.eddsa) { throw new Error('Key can only be used for EdDSA'); } - return this.keyPair.derive(pub.keyPair.getPublic()).toArray(); + return this.keyPair.derive(pub.keyPair.getPublic()); }; KeyPair.prototype.getPublic = function () { - return this.keyPair.getPublic('array'); + var compact = (this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'); + return this.keyPair.getPublic('array', compact); }; KeyPair.prototype.getPrivate = function () { @@ -70,7 +71,10 @@ KeyPair.prototype.getPrivate = function () { } }; -KeyPair.prototype.isValid = function () { // FIXME +KeyPair.prototype.isValid = function () { + if (this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont') { + throw new Error('Validation is not Implemented for this curve.'); + } return this.keyPair.validate().result; }; From 3129e7c4e319258627dac738a1fff972c5740ae5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 4 Jan 2018 01:53:53 -0800 Subject: [PATCH 42/75] Adds X25519 tests and updates README.md --- Gruntfile.js | 33 ++--- README.md | 12 ++ test/crypto/elliptic.js | 48 +++---- test/general/index.js | 1 + test/general/x25519.js | 305 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 354 insertions(+), 45 deletions(-) create mode 100644 test/general/x25519.js diff --git a/Gruntfile.js b/Gruntfile.js index d52d2c60..77ec18a7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -34,6 +34,10 @@ module.exports = function(grunt) { browser_capabilities = JSON.parse(process.env.SELENIUM_BROWSER_CAPABILITIES); } + var getSauceKey = function getSaucekey () { + return '60ffb656-2346-4b77-81f3-bc435ff4c103'; + }; + // Project configuration. grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), @@ -54,9 +58,7 @@ module.exports = function(grunt) { "transform-regenerator", "transform-runtime"], ignore: ['*.min.js'], - presets: [ - "es2015" - ] + presets: ["env"] }] ], plugin: [ 'browserify-derequire' ] @@ -79,9 +81,7 @@ module.exports = function(grunt) { "transform-regenerator", "transform-runtime"], ignore: ['*.min.js'], - presets: [ - "es2015" - ] + presets: ["env"] }] ], plugin: [ 'browserify-derequire' ] @@ -93,10 +93,9 @@ module.exports = function(grunt) { }, options: { browserifyOptions: { - debug: true, standalone: 'openpgp' }, - external: [ 'crypto', 'node-localstorage', 'node-fetch' ], + external: [ 'crypto' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -104,9 +103,7 @@ module.exports = function(grunt) { "transform-regenerator", "transform-runtime"], ignore: ['*.min.js'], - presets: [ - "es2015" - ] + presets: ["env"] }] ], plugin: [ 'browserify-derequire' ] @@ -122,7 +119,7 @@ module.exports = function(grunt) { 'test/lib/unittests-bundle.js': [ './test/unittests.js' ] }, options: { - external: [ 'crypto', 'node-localstorage', 'node-fetch', 'openpgp', '../../dist/openpgp' ] + external: [ 'crypto', 'openpgp', '../../dist/openpgp' ] } } }, @@ -215,7 +212,7 @@ module.exports = function(grunt) { } }, copy: { - browser: { + browsertest: { expand: true, flatten: true, cwd: 'node_modules/', @@ -249,7 +246,7 @@ module.exports = function(grunt) { all: { options: { username: 'openpgpjs', - key: '60ffb656-2346-4b77-81f3-bc435ff4c103', + key: getSauceKey, urls: ['http://127.0.0.1:3000/test/unittests.html'], build: process.env.TRAVIS_BUILD_ID, testname: 'Sauce Unit Test for openpgpjs', @@ -325,12 +322,12 @@ module.exports = function(grunt) { // Build tasks grunt.registerTask('version', ['replace:openpgp', 'replace:openpgp_debug']); grunt.registerTask('replace_min', ['replace:openpgp_min', 'replace:worker_min']); - grunt.registerTask('default',['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']); + grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']); grunt.registerTask('documentation', ['jsdoc']); // Test/Dev tasks - grunt.registerTask('test', [ 'eslint', 'mochaTest']); + grunt.registerTask('test', ['eslint', 'mochaTest']); grunt.registerTask('coverage', ['mocha_istanbul:coverage']); - grunt.registerTask('saucelabs', ['default', 'copy:browser', 'connect:test', 'saucelabs-mocha']); - grunt.registerTask('browsertest', ['browserify:openpgp_browser', 'copy:browser', 'connect:test', 'keepalive']); + grunt.registerTask('saucelabs', ['default', 'copy:browsertest', 'connect:test', 'saucelabs-mocha']); + grunt.registerTask('browsertest', ['browserify:openpgp_browser', 'copy:browsertest', 'connect:test', 'keepalive']); }; diff --git a/README.md b/README.md index 3d74c70c..58d7e668 100644 --- a/README.md +++ b/README.md @@ -118,13 +118,25 @@ openpgp.decrypt(options).then(function(plaintext) { #### Generate new key pair +RSA keys: ```js var options = { userIds: [{ name:'Jon Smith', email:'jon@example.com' }], // multiple user IDs numBits: 4096, // RSA key size passphrase: 'super long and hard to guess secret' // protects the private key }; +``` +ECC keys: +```js +var options = { + userIds: [{ name:'Jon Smith', email:'jon@example.com' }], // multiple user IDs + curve: "curve25519", // ECC curve (curve25519, p256, p384, p521, or secp256k1) + passphrase: 'super long and hard to guess secret' // protects the private key +}; +``` + +```js openpgp.generateKey(options).then(function(key) { var privkey = key.privateKeyArmored; // '-----BEGIN PGP PRIVATE KEY BLOCK ... ' var pubkey = key.publicKeyArmored; // '-----BEGIN PGP PUBLIC KEY BLOCK ... ' diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index d2c67d27..6a7b63a5 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -134,7 +134,7 @@ describe('Elliptic Curve Cryptography', function () { }; describe('Basic Operations', function () { it('Creating curve with name', function (done) { - var names = ['p256', 'p384', 'p521', 'secp256k1']; + var names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519']; names.forEach(function (name) { expect(elliptic_curves.get(name)).to.exist; }); @@ -147,16 +147,17 @@ describe('Elliptic Curve Cryptography', function () { }); done(); }); - it('Creating KeyPair', function (done) { - var names = ['p256', 'p384', 'p521', 'secp256k1']; - names.forEach(function (name) { + it('Creating KeyPair', function () { + var names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519']; + return Promise.all(names.map(function (name) { var curve = elliptic_curves.get(name); - curve.genKeyPair().then(keyPair => { + return curve.genKeyPair().then(keyPair => { expect(keyPair).to.exist; - expect(keyPair.isValid()).to.be.true; // FIXME done will skip this. + // FIXME validation is not implemented for Curve25519/Ed25519 key pairs + if (name !== 'curve25519') + expect(keyPair.isValid()).to.be.true; }); - }); - done(); + })); }); it('Creating KeyPair from data', function (done) { for (var name in key_data) { @@ -210,16 +211,14 @@ describe('Elliptic Curve Cryptography', function () { } else if (!openpgp.util.isUint8Array(message)) { message = new Uint8Array(message); } - return Promise.resolve().then(() => { - var ecdsa = elliptic_curves.ecdsa; - return ecdsa.verify( - oid, - hash, - {r: bin2bi(r), s: bin2bi(s)}, - message, - bin2bi(pub) - ); - }); + var ecdsa = elliptic_curves.ecdsa; + return ecdsa.verify( + oid, + hash, + {r: bin2bi(r), s: bin2bi(s)}, + message, + bin2bi(pub) + ); }; var secp256k1_dummy_value = new Uint8Array([ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -284,11 +283,8 @@ describe('Elliptic Curve Cryptography', function () { 0x68, 0x58, 0x23, 0x1D, 0x11, 0xEF, 0x3D, 0x21, 0x30, 0x75, 0x24, 0x39, 0x48, 0x89, 0x03, 0xDC]); it('Valid signature', function (done) { - verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub) - .then(res => { - expect(res).to.be.true; - done(); - }); + expect(verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub)) + .to.eventually.be.true.notify(done); }); it('Sign and verify message', function (done) { var curve = elliptic_curves.get('p521'); @@ -298,10 +294,8 @@ describe('Elliptic Curve Cryptography', function () { var oid = curve.oid; var message = p384_message; elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => { - elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic).then(verified => { - expect(verified).to.be.true; - done(); - }); + expect(elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic)) + .to.eventually.be.true.notify(done); }); }); }); diff --git a/test/general/index.js b/test/general/index.js index 01f27d19..49535869 100644 --- a/test/general/index.js +++ b/test/general/index.js @@ -9,5 +9,6 @@ describe('General', function () { require('./hkp.js'); require('./oid.js'); require('./ecc.js'); + require('./x25519.js'); }); diff --git a/test/general/x25519.js b/test/general/x25519.js new file mode 100644 index 00000000..4b35e26d --- /dev/null +++ b/test/general/x25519.js @@ -0,0 +1,305 @@ +'use strict'; + +var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); +var elliptic = openpgp.crypto.publicKey.elliptic; + +var chai = require('chai'); +chai.use(require('chai-as-promised')); +var expect = chai.expect; + +describe('X25519 Cryptography', function () { + var data = { + light: { + id: '1ecdf026c0245830', + pass: 'sun', + pub: [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + '', + 'mDMEWkN+5BYJKwYBBAHaRw8BAQdAIGqj23Kp273IPkgjwA7ue5MDIRAfWLYRqnFy', + 'c2AFMcC0EUxpZ2h0IDxsaWdodEBzdW4+iJAEExYIADgWIQSGS0GuVELT3Rs0woce', + 'zfAmwCRYMAUCWkN+5AIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRAezfAm', + 'wCRYMLteAQCFZcl8kBxCH86wmqpc2+KtEA8l/hsfh7jd+JWuyFuuRAD7BOix8Vo1', + 'P/hv8qUYwSn3IRXPeGXucoWVoKGgxRd+zAO4OARaQ37kEgorBgEEAZdVAQUBAQdA', + 'L1KkHCFxtK1CgvZlInT/y6OQeCfXiYzd/i452t2ZR2ADAQgHiHgEGBYIACAWIQSG', + 'S0GuVELT3Rs0wocezfAmwCRYMAUCWkN+5AIbDAAKCRAezfAmwCRYMJ71AQDmoQTg', + '36pfjrl82srS6XPRJxl3r/6lpWGaNij0VptB2wEA2V10ifOhnwILCw1qBle6On7a', + 'Ba257lrFM+cOSMaEsgo=', + '=D8HS', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'), + priv: [ + '-----BEGIN PGP PRIVATE KEY BLOCK-----', + '', + 'lIYEWkN+5BYJKwYBBAHaRw8BAQdAIGqj23Kp273IPkgjwA7ue5MDIRAfWLYRqnFy', + 'c2AFMcD+BwMCeaL+cNXzgI7uJQ7HBv53TAXO3y5uyJQMonkFtQtldL8YDbNP3pbd', + '3zzo9fxU12bWAJyFwBlBWJqkrxZN+0jt0ElsG3kp+V67MESJkrRhKrQRTGlnaHQg', + 'PGxpZ2h0QHN1bj6IkAQTFggAOBYhBIZLQa5UQtPdGzTChx7N8CbAJFgwBQJaQ37k', + 'AhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEB7N8CbAJFgwu14BAIVlyXyQ', + 'HEIfzrCaqlzb4q0QDyX+Gx+HuN34la7IW65EAPsE6LHxWjU/+G/ypRjBKfchFc94', + 'Ze5yhZWgoaDFF37MA5yLBFpDfuQSCisGAQQBl1UBBQEBB0AvUqQcIXG0rUKC9mUi', + 'dP/Lo5B4J9eJjN3+Ljna3ZlHYAMBCAf+BwMCvyW2D5Yx6dbujE3yHi1XQ9MbhOY5', + 'XRFFgYIUYzzi1qmaL+8Gr9zODsUdeO60XHnMXOmqVa6/sdx32TWo5s3sgS19kRUM', + 'D+pbxS/aZnxvrYh4BBgWCAAgFiEEhktBrlRC090bNMKHHs3wJsAkWDAFAlpDfuQC', + 'GwwACgkQHs3wJsAkWDCe9QEA5qEE4N+qX465fNrK0ulz0ScZd6/+paVhmjYo9Fab', + 'QdsBANlddInzoZ8CCwsNagZXujp+2gWtue5axTPnDkjGhLIK', + '=wo91', + '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'), + message: 'Hi, Light wrote this!', + message_signed: [ + '-----BEGIN PGP SIGNED MESSAGE-----', + 'Hash: SHA256', + '', + 'Hi, Light wrote this!', + '-----BEGIN PGP SIGNATURE-----', + '', + 'iIAEARYIACgWIQSGS0GuVELT3Rs0wocezfAmwCRYMAUCWkyVkAocbGlnaHRAc3Vu', + 'AAoJEB7N8CbAJFgwdqAA/RwTsy9Nt5HEJLnokUNgHVX8wNr7Ef9wfAG1RaMgMMWs', + 'AP9KEEohpHqaj8smb1oLjYU9DgOugE40LrkujvnWNbOZBQ==', + '=T9p+', + '-----END PGP SIGNATURE-----'].join('\n') + }, + night: { + id: 'f25e5f24bb372cfa', + pass: 'moon', + pub: [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + '', + 'mDMEWkN/RRYJKwYBBAHaRw8BAQdAM359sYg+LtcQo9G+mzMwxiu6wgY7UTVyip+V', + 'y8CWMhy0Ek5pZ2h0IDxuaWdodEBtb29uPoiQBBMWCAA4FiEEdracm9388E/nI0Df', + '8l5fJLs3LPoFAlpDf0UCGwMFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AACgkQ8l5f', + 'JLs3LPqoFAD+IkES10NVLoInYf6rMcxKY2/Nn+Dg4aYtdvphY8hY0b0A/jl34YEe', + 'cZAQvGWueGa5X2sCJvR1WZEMUWjW9cfR0TIHuDgEWkN/RRIKKwYBBAGXVQEFAQEH', + 'QCeuETdjFsEorruYHXmASKo7VNVgm29EZeA4bgbX1gsVAwEIB4h4BBgWCAAgFiEE', + 'dracm9388E/nI0Df8l5fJLs3LPoFAlpDf0UCGwwACgkQ8l5fJLs3LPojTgEApyg3', + 'Gd7R77zhC8mkSDIssegrFCoLqDgNYOSISgixUdgA/j7tIDGF45C9JC4LQsjfKY9W', + 'Td0I97hWRfub9tYo0P8K', + '=nbhM', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'), + priv: [ + '-----BEGIN PGP PRIVATE KEY BLOCK-----', + '', + 'lIYEWkN/RRYJKwYBBAHaRw8BAQdAM359sYg+LtcQo9G+mzMwxiu6wgY7UTVyip+V', + 'y8CWMhz+BwMCxwCG2X+GJp7uQHSoj4fmvArR8d9hzyKBKDX84QsC1nCqMNRARz1v', + 'aSqXfCt4gLzR3sZh4yS0cDUB0UdDfFhh3XiG2j8zRJ3cKkXdV3GcSbQSTmlnaHQg', + 'PG5pZ2h0QG1vb24+iJAEExYIADgWIQR2tpyb3fzwT+cjQN/yXl8kuzcs+gUCWkN/', + 'RQIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgAAKCRDyXl8kuzcs+qgUAP4iQRLX', + 'Q1Uugidh/qsxzEpjb82f4ODhpi12+mFjyFjRvQD+OXfhgR5xkBC8Za54ZrlfawIm', + '9HVZkQxRaNb1x9HRMgeciwRaQ39FEgorBgEEAZdVAQUBAQdAJ64RN2MWwSiuu5gd', + 'eYBIqjtU1WCbb0Rl4DhuBtfWCxUDAQgH/gcDAoeG6mA2BitC7sbt5erYFzAndJx3', + 'fOBDIo9MF2xo/JX1OrL5Z9Fro1UP+A3P+YyZQ3W/PMMVFArfnyiEoJAmQOkashgd', + 'CocKYaKUNrgbYl2IeAQYFggAIBYhBHa2nJvd/PBP5yNA3/JeXyS7Nyz6BQJaQ39F', + 'AhsMAAoJEPJeXyS7Nyz6I04BAKcoNxne0e+84QvJpEgyLLHoKxQqC6g4DWDkiEoI', + 'sVHYAP4+7SAxheOQvSQuC0LI3ymPVk3dCPe4VkX7m/bWKND/Cg==', + '=NDSU', + '-----END PGP PRIVATE KEY BLOCK-----'].join('\n'), + message: 'Oh hi, this is a private message from Light to Night!', + message_encrypted: [ + '-----BEGIN PGP MESSAGE-----', + '', + 'hF4DzfwiGcVT05ISAQdAetSWotgG0+MTEfyKvagrHAeGw0Denjph+Mu2KcpAajIw', + 'kE398hrqnc6qYFdf3p761kzvgjX0auua8L2WFlhAzGh1ULodxHVLmvxwiId4JwHq', + '0sAzAaM+Vn5hfDM5799p2DpPK8635LN0UvtlOqGIdaNfu5DgfoherMSb3zlBa4YF', + 'WJG1Fa9glfWTOlMNKKoFl4LUh1BUF4TbqUv3a0BR6GcDy6zSp4KRl3NIq22fUD/F', + 'BZWuhPRhnsvDAoBTbvlgjyuActYhtXU5srMAEh4UeVvKyU8xImDfLgJReU4500JU', + 'VjZkMXTileVhAprvE5KCCDWi6YWzV+SSpn+VhtnShAfoF870GI+DOnvFwEnhQlol', + 'JRZdfjq5haoEjWTuqSIS+O40AgmQYPIjnO5ALehFuWTHKLDFVv4EDqx7MatXZidz', + 'drpAMWGi', + '=erKa', + '-----END PGP MESSAGE-----'].join('\n') + } + }; + function load_pub_key(name) { + if (data[name].pub_key) { + return data[name].pub_key; + } + var pub = openpgp.key.readArmored(data[name].pub); + expect(pub).to.exist; + expect(pub.err).to.not.exist; + expect(pub.keys).to.have.length(1); + expect(pub.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id); + data[name].pub_key = pub.keys[0]; + return data[name].pub_key; + } + function load_priv_key(name) { + if (data[name].priv_key) { + return data[name].priv_key; + } + var pk = openpgp.key.readArmored(data[name].priv); + expect(pk).to.exist; + expect(pk.err).to.not.exist; + expect(pk.keys).to.have.length(1); + expect(pk.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id); + expect(pk.keys[0].decrypt(data[name].pass)).to.be.true; + data[name].priv_key = pk.keys[0]; + return data[name].priv_key; + } + it('Load public key', function (done) { + load_pub_key('light'); + load_pub_key('night'); + done(); + }); + it('Load private key', function (done) { + load_priv_key('light'); + load_priv_key('night'); + done(); + }).timeout(10000); + it('Verify clear signed message', function () { + var name = 'light'; + var pub = load_pub_key(name); + var msg = openpgp.cleartext.readArmored(data[name].message_signed); + return openpgp.verify({publicKeys: [pub], message: msg}).then(function(result) { + expect(result).to.exist; + expect(result.data.trim()).to.equal(data[name].message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + }); + }); + // FIXME is this pattern correct? + it('Sign message', function () { + var name = 'light' + var priv = load_priv_key(name); + return openpgp.sign({privateKeys: [priv], data: data[name].message + "\n"}).then(function (signed) { + var pub = load_pub_key(name); + var msg = openpgp.cleartext.readArmored(signed.data); + return openpgp.verify({publicKeys: [pub], message: msg}).then(function (result) { + expect(result).to.exist; + expect(result.data.trim()).to.equal(data[name].message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + }); + }); + }); + it('Decrypt and verify message', function () { + var light = load_pub_key('light'); + var night = load_priv_key('night'); + expect(night.decrypt(data['night'].pass)).to.be.true; + var msg = openpgp.message.readArmored(data['night'].message_encrypted); + return openpgp.decrypt( + {privateKey: night, publicKeys: [light], message: msg} + ).then(function (result) { + expect(result).to.exist; + // trim required because https://github.com/openpgpjs/openpgpjs/issues/311 + expect(result.data.trim()).to.equal(data['night'].message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + }); + }); + it('Encrypt and sign message', function () { + var night = load_pub_key('night'); + var light = load_priv_key('light'); + expect(light.decrypt(data['light'].pass)).to.be.true; + openpgp.encrypt( + {publicKeys: [night], privateKeys: [light], data: data['light'].message + "\n"} + ).then(function (encrypted) { + var message = openpgp.message.readArmored(encrypted.data); + var light = load_pub_key('light'); + var night = load_priv_key('night'); + return openpgp.decrypt( + {privateKey: night, publicKeys: [light], message: message} + ).then(function (result) { + expect(result).to.exist; + expect(result.data.trim()).to.equal(data['light'].message); + expect(result.signatures).to.have.length(1); + expect(result.signatures[0].valid).to.be.true; + }); + }); + }); + + // TODO generate, export, then reimport key and validate + it('Omnibus Ed25519/Curve25519 Test', function () { + var options = { + userIds: {name: "Hi", email: "hi@hel.lo"}, + curve: "ed25519" + }; + return openpgp.generateKey(options).then(function (firstKey) { + expect(firstKey).to.exist; + expect(firstKey.privateKeyArmored).to.exist; + expect(firstKey.publicKeyArmored).to.exist; + expect(firstKey.key).to.exist; + expect(firstKey.key.primaryKey).to.exist; + expect(firstKey.key.subKeys).to.have.length(1); + expect(firstKey.key.subKeys[0].subKey).to.exist; + + var hi = firstKey.key; + var primaryKey = hi.primaryKey; + var subKey = hi.subKeys[0].subKey; + expect(primaryKey.params[0].oid).to.equal(elliptic.get('ed25519').oid); + expect(primaryKey.algorithm).to.equal('eddsa'); + expect(subKey.params[0].oid).to.equal(elliptic.get('curve25519').oid); + expect(subKey.algorithm).to.equal('ecdh'); + + // Self Certificate is valid + var user = hi.users[0] + expect(user.selfCertifications[0].verify( + primaryKey, {userid: user.userId, key: primaryKey} + )).to.eventually.be.true; + expect(user.isValidSelfCertificate( + primaryKey, user.selfCertifications[0] + )).to.eventually.be.true; + + var options = { + userIds: {name: "Bye", email: "bye@good.bye"}, + curve: "curve25519" + }; + return openpgp.generateKey(options).then(function (secondKey) { + var bye = secondKey.key; + expect(bye.primaryKey.params[0].oid).to.equal(elliptic.get('ed25519').oid); + expect(bye.primaryKey.algorithm).to.equal('eddsa'); + expect(bye.subKeys[0].subKey.params[0].oid).to.equal(elliptic.get('curve25519').oid); + expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh'); + + // Self Certificate is valid + var user = bye.users[0] + expect(user.selfCertifications[0].verify( + bye.primaryKey, {userid: user.userId, key: bye.primaryKey} + )).to.eventually.be.true; + expect(user.isValidSelfCertificate( + bye.primaryKey, user.selfCertifications[0] + )).to.eventually.be.true; + + return Promise.all([ + // Hi trusts Bye! + bye.toPublic().signPrimaryUser([ hi ]).then(trustedBye => { + expect(trustedBye.users[0].otherCertifications[0].verify( + primaryKey, { userid: user.userId, key: bye.toPublic().primaryKey } + )).to.eventually.be.true; + }), + // Signing message + openpgp.sign( + { data: 'Hi, this is me, Hi!', privateKeys: hi } + ).then(signed => { + var msg = openpgp.cleartext.readArmored(signed.data); + // Verifying signed message + return Promise.all([ + openpgp.verify( + { message: msg, publicKeys: hi.toPublic() } + ).then(output => expect(output.signatures[0].valid).to.be.true), + // Verifying detached signature + openpgp.verify( + { message: openpgp.message.fromText('Hi, this is me, Hi!'), + publicKeys: hi.toPublic(), + signature: openpgp.signature.readArmored(signed.data) } + ).then(output => expect(output.signatures[0].valid).to.be.true) + ]); + }), + // Encrypting and signing + openpgp.encrypt( + { data: 'Hi, Hi wrote this but only Bye can read it!', + publicKeys: [ bye.toPublic() ], + privateKeys: [ hi ] } + ).then(encrypted => { + var msg = openpgp.message.readArmored(encrypted.data) + // Decrypting and verifying + return openpgp.decrypt( + { message: msg, + privateKey: bye, + publicKeys: [ hi.toPublic() ] } + ).then(output => { + expect(output.data).to.equal('Hi, Hi wrote this but only Bye can read it!'); + expect(output.signatures[0].valid).to.be.true; + }); + }) + ]); + }); + }); + }); +}); From 5cb89f4f257fe7bbbc88c8a6b02345b8e4b8ebe0 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 18 Jan 2018 00:34:03 -0800 Subject: [PATCH 43/75] Addresses various review comments by @sanjanarajan * Various FIXME tags are removed * In curve.js: - webCrypto/nodeCrypto fallback bug is fixed - Curve25519 has keyType ecdsa (won't be used for signing, but technically can be) - webGenKeyPair is simplifed * In base64.js: - documentation added and arguments simplified * In ecdsa.js and eddsa.js: - hash_algo is now at least as strong as the default curve hash - simplified the code by moving webSign/nodeSign and webVerify/nodeVerify to live in key.js (ht @ismaelbej) * In message.js: - in decryptSessionKey, loops break once a key packet is decrypted * In key.js: - getPreferredHashAlgorithm returns the best hash algorithm - enums are used for curve selection --- src/crypto/public_key/elliptic/curves.js | 82 +++++----- src/crypto/public_key/elliptic/ecdsa.js | 191 +---------------------- src/crypto/public_key/elliptic/eddsa.js | 9 +- src/crypto/public_key/elliptic/index.js | 5 +- src/crypto/public_key/elliptic/key.js | 171 +++++++++++++++++++- src/encoding/base64.js | 22 ++- src/enums.js | 17 +- src/packet/packetlist.js | 16 +- src/type/oid.js | 8 + test/crypto/elliptic.js | 14 +- test/general/ecc.js | 2 +- 11 files changed, 278 insertions(+), 259 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index d21d8121..3fa52cf5 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -34,19 +34,19 @@ import random from '../../random'; import config from '../../../config'; import enums from '../../../enums'; import util from '../../../util'; +import OID from '../../../type/oid'; import base64 from '../../../encoding/base64'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); var webCurves = {}, nodeCurves = {}; -if (webCrypto && config.use_native) { - webCurves = { - 'p256': 'P-256', - 'p384': 'P-384', - 'p521': 'P-521' - }; -} else if (nodeCrypto && config.use_native) { +webCurves = { + 'p256': 'P-256', + 'p384': 'P-384', + 'p521': 'P-521' +}; +if (nodeCrypto && config.use_native) { var knownCurves = nodeCrypto.getCurves(); nodeCurves = { 'secp256k1': knownCurves.includes('secp256k1') ? 'secp256k1' : undefined, @@ -63,8 +63,8 @@ const curves = { keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, - node: nodeCurves.secp256r1, - web: webCurves.secp256r1, + node: nodeCurves.p256, + web: webCurves.p256, payloadSize: 32 }, p384: { @@ -72,8 +72,8 @@ const curves = { keyType: enums.publicKey.ecdsa, hash: enums.hash.sha384, cipher: enums.symmetric.aes192, - node: nodeCurves.secp384r1, - web: webCurves.secp384r1, + node: nodeCurves.p384, + web: webCurves.p384, payloadSize: 48 }, p521: { @@ -81,8 +81,8 @@ const curves = { keyType: enums.publicKey.ecdsa, hash: enums.hash.sha512, cipher: enums.symmetric.aes256, - node: nodeCurves.secp521r1, - web: webCurves.secp521r1, + node: nodeCurves.p521, + web: webCurves.p521, payloadSize: 66 }, secp256k1: { @@ -99,13 +99,16 @@ const curves = { }, curve25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), - keyType: enums.publicKey.ecdh, + keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128 }, brainpoolP256r1: { // TODO 1.3.36.3.3.2.8.1.1.7 oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07]) }, + brainpoolP384r1: { // TODO 1.3.36.3.3.2.8.1.1.11 + oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B]) + }, brainpoolP512r1: { // TODO 1.3.36.3.3.2.8.1.1.13 oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D]) } @@ -118,12 +121,12 @@ function Curve(name, params) { this.curve = new EdDSA(name); break; case enums.publicKey.ecdsa: - case enums.publicKey.ecdh: this.curve = new EC(name); break; default: throw new Error('Unknown elliptic key type;'); } + this.name = name; this.oid = curves[name].oid; this.hash = params.hash; this.cipher = params.cipher; @@ -145,14 +148,14 @@ Curve.prototype.keyFromPublic = function (pub) { }; Curve.prototype.genKeyPair = async function () { - var r, keyPair; + var keyPair; if (webCrypto && config.use_native && this.web) { - keyPair = await webGenKeyPair(this.name, "ECDSA"); // FIXME is ECDH different? + keyPair = await webGenKeyPair(this.name); } else if (nodeCrypto && config.use_native && this.node) { keyPair = await nodeGenKeyPair(this.name); } else { var compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; - r = await this.curve.genKeyPair(); + var r = await this.curve.genKeyPair(); if (this.keyType === enums.publicKey.eddsa) { keyPair = { secret: r.getSecret() }; } else { @@ -162,17 +165,18 @@ Curve.prototype.genKeyPair = async function () { return new KeyPair(this.curve, keyPair); }; - function get(oid_or_name) { var name; - if (enums.curve[oid_or_name]) { - name = enums.write(enums.curve, oid_or_name); + if (OID.prototype.isPrototypeOf(oid_or_name) && + enums.curve[oid_or_name.toHex()]) { + name = enums.write(enums.curve, oid_or_name.toHex()); // by curve OID + return new Curve(name, curves[name]); + } else if (enums.curve[oid_or_name]) { + name = enums.write(enums.curve, oid_or_name); // by curve name + return new Curve(name, curves[name]); + } else if (enums.curve[util.hexstrdump(oid_or_name)]) { + name = enums.write(enums.curve, util.hexstrdump(oid_or_name)); // by oid string return new Curve(name, curves[name]); - } - for (name in curves) { - if (curves[name].oid === oid_or_name) { - return new Curve(name, curves[name]); - } } throw new Error('Not valid curve'); } @@ -189,8 +193,16 @@ async function generate(curve) { }; } +function getPreferredHashAlgorithm(oid) { + return curves[enums.write(enums.curve, oid.toHex())].hash; +} + module.exports = { Curve: Curve, + curves: curves, + webCurves: webCurves, + nodeCurves: nodeCurves, + getPreferredHashAlgorithm: getPreferredHashAlgorithm, generate: generate, get: get }; @@ -203,14 +215,10 @@ module.exports = { ////////////////////////// -async function webGenKeyPair(name, algorithm) { +async function webGenKeyPair(name) { + // Note: keys generated with ECDSA and ECDH are structurally equivalent var webCryptoKey = await webCrypto.generateKey( - { - name: algorithm === "ECDH" ? "ECDH" : "ECDSA", - namedCurve: webCurves[name] - }, - true, - algorithm === "ECDH" ? ["deriveKey", "deriveBits"] : ["sign", "verify"] + { name: "ECDSA", namedCurve: webCurves[name] }, true, ["sign", "verify"] ); var privateKey = await webCrypto.exportKey("jwk", webCryptoKey.privateKey); @@ -218,15 +226,15 @@ async function webGenKeyPair(name, algorithm) { return { pub: { - x: base64.decode(publicKey.x, 'base64url'), - y: base64.decode(publicKey.y, 'base64url') + x: base64.decode(publicKey.x, true), + y: base64.decode(publicKey.y, true) }, - priv: base64.decode(privateKey.d, 'base64url') + priv: base64.decode(privateKey.d, true) }; } async function nodeGenKeyPair(name) { - var ecdh = nodeCrypto.createECDH(name === "secp256r1" ? "prime256v1" : name); + var ecdh = nodeCrypto.createECDH(nodeCurves[name]); await ecdh.generateKeys(); return { diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 23c60860..0493c55d 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -25,28 +25,9 @@ 'use strict'; -import BN from 'bn.js'; -import ASN1 from 'asn1.js'; -import jwkToPem from 'jwk-to-pem'; - +import hash from '../../hash'; import curves from './curves.js'; import BigInteger from '../jsbn.js'; -import config from '../../../config'; -import enums from '../../../enums.js'; -import util from '../../../util.js'; -import base64 from '../../../encoding/base64.js'; - -const webCrypto = util.getWebCrypto(); -const webCurves = curves.webCurves; -const nodeCrypto = util.getNodeCrypto(); -const nodeCurves = curves.nodeCurves; - -var ECDSASignature = ASN1.define('ECDSASignature', function() { - this.seq().obj( - this.key('r').int(), // FIXME int or BN? - this.key('s').int() // FIXME int or BN? - ); -}); /** * Sign a message using the provided key @@ -57,17 +38,9 @@ var ECDSASignature = ASN1.define('ECDSASignature', function() { * @return {{r: BigInteger, s: BigInteger}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - var signature; const curve = curves.get(oid); - hash_algo = hash_algo ? hash_algo : curve.hash; const key = curve.keyFromPrivate(d.toByteArray()); - if (webCrypto && config.use_native && curve.web) { - signature = await webSign(curve, hash_algo, m, key.keyPair); - } else if (nodeCrypto && config.use_native && curve.node) { - signature = await nodeSign(curve, hash_algo, m, key.keyPair); - } else { - signature = await key.sign(m, hash_algo); - } + const signature = await key.sign(m, hash_algo); return { r: new BigInteger(signature.r.toArray()), s: new BigInteger(signature.s.toArray()) @@ -84,168 +57,14 @@ async function sign(oid, hash_algo, m, d) { * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - var result; const curve = curves.get(oid); - hash_algo = hash_algo ? hash_algo : curve.hash; // FIXME is this according to the RFC? const key = curve.keyFromPublic(Q.toByteArray()); - if (webCrypto && config.use_native && curve.web) { - result = await webVerify(curve, hash_algo, signature, m, key.keyPair.getPublic()); - } else if (nodeCrypto && config.use_native && curve.node) { - result = await nodeVerify(curve, hash_algo, signature, m, key.keyPair.getPublic()); - } else { - result = await key.verify( - m, {r: signature.r.toByteArray(), s: signature.s.toByteArray()}, hash_algo - ); - } - return result; + return key.verify( + m, { r: signature.r.toByteArray(), s: signature.s.toByteArray() }, hash_algo + ); } module.exports = { sign: sign, verify: verify }; - - -////////////////////////// -// // -// Helper functions // -// // -////////////////////////// - - -async function webSign(curve, hash_algo, message, keyPair) { - var l = curve.payloadSize; - if (typeof message === 'string') { - message = util.str2Uint8Array(message); - } - const key = await webCrypto.importKey( - "jwk", - { - "kty": "EC", - "crv": webCurves[curve.name], - "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), null, 'base64url'), - "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), null, 'base64url'), - "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray('be', l)), null, 'base64url'), - "use": "sig", - "kid": "ECDSA Private Key" - }, - { - "name": "ECDSA", - "namedCurve": webCurves[curve.name], - "hash": { name: enums.read(enums.webHash, curve.hash) } - }, - false, - ["sign"] - ); - - const signature = new Uint8Array(await webCrypto.sign( - { - "name": 'ECDSA', - "namedCurve": webCurves[curve.name], - "hash": { name: enums.read(enums.webHash, hash_algo) } - }, - key, - message - )); - return { - r: signature.slice(0, l), - s: signature.slice(l, 2 * l) - }; -} - -async function webVerify(curve, hash_algo, signature, message, publicKey) { - var r = signature.r.toByteArray(), s = signature.s.toByteArray(), l = curve.payloadSize; - r = (r.length === l) ? r : [0].concat(r); - s = (s.length === l) ? s : [0].concat(s); - signature = new Uint8Array(r.concat(s)).buffer; - if (typeof message === 'string') { - message = util.str2Uint8Array(message); - } - const key = await webCrypto.importKey( - "jwk", - { - "kty": "EC", - "crv": webCurves[curve.name], - "x": base64.encode(new Uint8Array(publicKey.getX().toArray('be', l)), null, 'base64url'), - "y": base64.encode(new Uint8Array(publicKey.getY().toArray('be', l)), null, 'base64url'), - "use": "sig", - "kid": "ECDSA Public Key" - }, - { - "name": "ECDSA", - "namedCurve": webCurves[curve.name], - "hash": { name: enums.read(enums.webHash, curve.hash) } - }, - false, - ["verify"] - ); - - return webCrypto.verify( - { - "name": 'ECDSA', - "namedCurve": webCurves[curve.name], - "hash": { name: enums.read(enums.webHash, hash_algo) } - }, - key, - signature, - message - ); -} - - -async function nodeSign(curve, hash_algo, message, keyPair) { - if (typeof message === 'string') { - message = util.str2Uint8Array(message); - } - const key = jwkToPem( - { - "kty": "EC", - "crv": nodeCurves[curve.name], - "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray())), - "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray())), - "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray())), - "use": "sig", - "kid": "ECDSA Private Key" - }, - { private: true } - ); - - const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo)); - sign.write(message); - sign.end(); - const signature = await ECDSASignature.decode(sign.sign(key), 'der'); - return { - r: signature.r.toArray(), - s: signature.s.toArray() - }; -} - -async function nodeVerify(curve, hash_algo, signature, message, publicKey) { - signature = ECDSASignature.encode( - { - r: new BN(signature.r.toByteArray()), - s: new BN(signature.s.toByteArray()) - }, - 'der'); - if (typeof message === 'string') { - message = util.str2Uint8Array(message); - } - const key = jwkToPem( - { - "kty": "EC", - "crv": nodeCurves[curve.name], - "x": base64.encode(new Uint8Array(publicKey.getX().toArray())), - "y": base64.encode(new Uint8Array(publicKey.getY().toArray())), - "use": "sig", - "kid": "ECDSA Public Key" - }, - { private: false } - ); - - // FIXME what happens when hash_algo = undefined? - const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); - verify.write(message); - verify.end(); - const result = await verify.verify(key, signature); - return result; -} diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 3309979e..cccb23dd 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -2,6 +2,7 @@ 'use strict'; +import hash from '../../hash'; import curves from './curves.js'; import BigInteger from '../jsbn.js'; @@ -14,11 +15,9 @@ import BigInteger from '../jsbn.js'; * @return {{r: BigInteger, s: BigInteger}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - var signature; const curve = curves.get(oid); - hash_algo = hash_algo ? hash_algo : curve.hash; const key = curve.keyFromSecret(d.toByteArray()); - signature = await key.sign(m, hash_algo); + const signature = await key.sign(m, hash_algo); return { r: new BigInteger(signature.Rencoded()), s: new BigInteger(signature.Sencoded()) @@ -35,12 +34,10 @@ async function sign(oid, hash_algo, m, d) { * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - var result; const curve = curves.get(oid); - hash_algo = hash_algo ? hash_algo : curve.hash; // FIXME is this according to the RFC? const key = curve.keyFromPublic(Q.toByteArray()); return key.verify( - m, {R: signature.r.toByteArray(), S: signature.s.toByteArray()}, hash_algo + m, { R: signature.r.toByteArray(), S: signature.s.toByteArray() }, hash_algo ); } diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index f4a5cc47..a171c3cb 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -27,7 +27,7 @@ 'use strict'; -import {get, generate} from './curves'; +import {get, generate, getPreferredHashAlgorithm} from './curves'; import ecdsa from './ecdsa'; import eddsa from './eddsa'; import ecdh from './ecdh'; @@ -37,5 +37,6 @@ module.exports = { eddsa: eddsa, ecdh: ecdh, get: get, - generate: generate + generate: generate, + getPreferredHashAlgorithm: getPreferredHashAlgorithm }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index f8e5b0e1..22131762 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -25,9 +25,28 @@ 'use strict'; +import BN from 'bn.js'; +import ASN1 from 'asn1.js'; +import jwkToPem from 'jwk-to-pem'; + +import curves from './curves'; import hash from '../../hash'; import util from '../../../util'; import enums from '../../../enums'; +import config from '../../../config'; +import base64 from '../../../encoding/base64'; + +const webCrypto = util.getWebCrypto(); +const webCurves = curves.webCurves; +const nodeCrypto = util.getNodeCrypto(); +const nodeCurves = curves.nodeCurves; + +var ECDSASignature = ASN1.define('ECDSASignature', function() { + this.seq().obj( + this.key('r').int(), + this.key('s').int() + ); +}); function KeyPair(curve, options) { this.curve = curve; @@ -35,20 +54,32 @@ function KeyPair(curve, options) { this.keyPair = this.curve.keyPair(options); } -KeyPair.prototype.sign = function (message, hash_algo) { +KeyPair.prototype.sign = async function (message, hash_algo) { if (typeof message === 'string') { message = util.str2Uint8Array(message); } - const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); - return this.keyPair.sign(digest); + if (webCrypto && config.use_native && this.curve.web) { + return webSign(this.curve, hash_algo, message, this.keyPair); + } else if (nodeCrypto && config.use_native && this.curve.node) { + return nodeSign(this.curve, hash_algo, message, this.keyPair); + } else { + const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); + return this.keyPair.sign(digest); + } }; -KeyPair.prototype.verify = function (message, signature, hash_algo) { +KeyPair.prototype.verify = async function (message, signature, hash_algo) { if (typeof message === 'string') { message = util.str2Uint8Array(message); } - const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); - return this.keyPair.verify(digest, signature); + if (webCrypto && config.use_native && this.curve.web) { + return webVerify(this.curve, hash_algo, signature, message, this.keyPair.getPublic()); + } else if (nodeCrypto && config.use_native && this.curve.node) { + return nodeVerify(this.curve, hash_algo, signature, message, this.keyPair.getPublic()); + } else { + const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); + return this.keyPair.verify(digest, signature); + } }; KeyPair.prototype.derive = function (pub) { @@ -81,3 +112,131 @@ KeyPair.prototype.isValid = function () { module.exports = { KeyPair: KeyPair }; + + +////////////////////////// +// // +// Helper functions // +// // +////////////////////////// + + +async function webSign(curve, hash_algo, message, keyPair) { + var l = curve.payloadSize; + const key = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": webCurves[curve.name], + "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), true), + "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), true), + "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray('be', l)), true), + "use": "sig", + "kid": "ECDSA Private Key" + }, + { + "name": "ECDSA", + "namedCurve": webCurves[curve.name], + "hash": { name: enums.read(enums.webHash, curve.hash) } + }, + false, + ["sign"] + ); + + const signature = new Uint8Array(await webCrypto.sign( + { + "name": 'ECDSA', + "namedCurve": webCurves[curve.name], + "hash": { name: enums.read(enums.webHash, hash_algo) } + }, + key, + message + )); + return { + r: signature.slice(0, l), + s: signature.slice(l, 2 * l) + }; +} + +async function webVerify(curve, hash_algo, {r, s}, message, publicKey) { + var l = curve.payloadSize; + r = (r.length === l) ? r : [0].concat(r); + s = (s.length === l) ? s : [0].concat(s); + var signature = new Uint8Array(r.concat(s)).buffer; + const key = await webCrypto.importKey( + "jwk", + { + "kty": "EC", + "crv": webCurves[curve.name], + "x": base64.encode(new Uint8Array(publicKey.getX().toArray('be', l)), true), + "y": base64.encode(new Uint8Array(publicKey.getY().toArray('be', l)), true), + "use": "sig", + "kid": "ECDSA Public Key" + }, + { + "name": "ECDSA", + "namedCurve": webCurves[curve.name], + "hash": { name: enums.read(enums.webHash, curve.hash) } + }, + false, + ["verify"] + ); + + return webCrypto.verify( + { + "name": 'ECDSA', + "namedCurve": webCurves[curve.name], + "hash": { name: enums.read(enums.webHash, hash_algo) } + }, + key, + signature, + message + ); +} + + +async function nodeSign(curve, hash_algo, message, keyPair) { + const key = jwkToPem( + { + "kty": "EC", + "crv": webCurves[curve.name], + "x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray())), + "y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray())), + "d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray())), + "use": "sig", + "kid": "ECDSA Private Key" + }, + { private: true } + ); + + const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo)); + sign.write(message); + sign.end(); + const signature = await ECDSASignature.decode(sign.sign(key), 'der'); + return { + r: signature.r.toArray(), + s: signature.s.toArray() + }; +} + +async function nodeVerify(curve, hash_algo, {r, s}, message, publicKey) { + var signature = ECDSASignature.encode({ r: new BN(r), s: new BN(s) }, 'der'); + const key = jwkToPem( + { + "kty": "EC", + "crv": webCurves[curve.name], + "x": base64.encode(new Uint8Array(publicKey.getX().toArray())), + "y": base64.encode(new Uint8Array(publicKey.getY().toArray())), + "use": "sig", + "kid": "ECDSA Public Key" + }, + { private: false } + ); + + // FIXME what happens when hash_algo = undefined? + const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); + verify.write(message); + verify.end(); + const result = await verify.verify(key, signature); + return result; +} diff --git a/src/encoding/base64.js b/src/encoding/base64.js index 403a73d5..e4d22cfe 100644 --- a/src/encoding/base64.js +++ b/src/encoding/base64.js @@ -17,20 +17,21 @@ 'use strict'; -var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; -var b64u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; +var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // Standard radix-64 +var b64u = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; // URL-safe radix-64 /** * Convert binary array to radix-64 * @param {Uint8Array} t Uint8Array to convert + * @param {bool} u if true, output is URL-safe * @returns {string} radix-64 version of input string * @static */ -function s2r(t, o, u) { +function s2r(t, u = false) { // TODO check btoa alternative - var b64 = (u === "base64url") ? b64u : b64s; + var b64 = u ? b64u : b64s; var a, c, n; - var r = o ? o : [], + var r = [], l = 0, s = 0; var tl = t.length; @@ -67,33 +68,30 @@ function s2r(t, o, u) { if ((l % 60) === 0 && !u) { r.push("\n"); } - if (u !== 'base64url') { + if (!u) { r.push('='); l += 1; } } - if (s === 1 && u !== 'base64url') { + if (s === 1 && !u) { if ((l % 60) === 0 && !u) { r.push("\n"); } r.push('='); } - if (o) - { - return; - } return r.join(''); } /** * Convert radix-64 to binary array * @param {String} t radix-64 string to convert + * @param {bool} u if true, input is interpreted as URL-safe * @returns {Uint8Array} binary array version of input string * @static */ function r2s(t, u) { // TODO check atob alternative - var b64 = (u === "base64url") ? b64u : b64s; + var b64 = u ? b64u : b64s; var c, n; var r = [], s = 0, diff --git a/src/enums.js b/src/enums.js index c4bf3651..66929a01 100644 --- a/src/enums.js +++ b/src/enums.js @@ -16,25 +16,40 @@ export default { "secp256r1": "p256", "prime256v1": "p256", "1.2.840.10045.3.1.7": "p256", + "2a8648ce3d030107": "p256", + "2A8648CE3D030107": "p256", "p384": "p384", "P-384": "p384", "secp384r1": "p384", "1.3.132.0.34": "p384", + "2b81040022": "p384", + "2B81040022": "p384", "p521": "p521", "P-521": "p521", "secp521r1": "p521", "1.3.132.0.35": "p521", + "2b81040023": "p521", + "2B81040023": "p521", "secp256k1": "secp256k1", "1.3.132.0.10": "secp256k1", + "2b8104000a": "secp256k1", + "2B8104000A": "secp256k1", "ed25519": "ed25519", + "Ed25519": "ed25519", "1.3.6.1.4.1.11591.15.1": "ed25519", + "2b06010401da470f01": "ed25519", + "2B06010401DA470F01": "ed25519", + "cv25519": "curve25519", "curve25519": "curve25519", - "1.3.6.1.4.1.3029.1.5.1": "curve25519" + "Curve25519": "curve25519", + "1.3.6.1.4.1.3029.1.5.1": "curve25519", + "2b060104019755010501": "curve25519", + "2B060104019755010501": "curve25519" }, /** A string to key specifier type diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index 7f096fba..752cc1b5 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -145,7 +145,7 @@ Packetlist.prototype.filterByTag = function () { */ Packetlist.prototype.forEach = function (callback) { for (var i = 0; i < this.length; i++) { - callback(this[i]); + callback(this[i], i, this); } }; @@ -163,6 +163,20 @@ Packetlist.prototype.map = function (callback) { return packetArray; }; +/** +* Executes the callback function once for each element +* until it finds one where callback returns a truthy value +*/ +Packetlist.prototype.some = async function (callback) { + for (var i = 0; i < this.length; i++) { + // eslint-disable-next-line no-await-in-loop + if (await callback(this[i], i, this)) { + return true; + } + } + return false; +}; + /** * Traverses packet tree and returns first matching packet * @param {module:enums.packet} type The packet type diff --git a/src/type/oid.js b/src/type/oid.js index e6cbad0b..be633321 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -68,6 +68,14 @@ OID.prototype.write = function () { String.fromCharCode(this.oid.length)+this.oid); }; +/** + * Serialize an OID object as a hex string + * @return {string} String with the hex value of the OID + */ +OID.prototype.toHex = function() { + return util.hexstrdump(this.oid); +}; + OID.fromClone = function (clone) { var oid = new OID(clone.oid); return oid; diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 6a7b63a5..48a84fc5 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -175,22 +175,22 @@ describe('Elliptic Curve Cryptography', function () { it('Signature verification', function (done) { var curve = elliptic_curves.get('p256'); var key = curve.keyFromPublic(signature_data.pub); - expect(key.verify(signature_data.message, signature_data.signature, 8)).to.be.true; + expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.true; done(); }); it('Invalid signature', function (done) { var curve = elliptic_curves.get('p256'); var key = curve.keyFromPublic(key_data.p256.pub); - expect(key.verify(signature_data.message, signature_data.signature, 8)).to.be.false; + expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.false; done(); }); - it('Signature generation', function (done) { + it('Signature generation', function () { var curve = elliptic_curves.get('p256'); var key = curve.keyFromPrivate(key_data.p256.priv); - var signature = key.sign(signature_data.message, 8); - key = curve.keyFromPublic(key_data.p256.pub); - expect(key.verify(signature_data.message, signature, 8)).to.be.true; - done(); + return key.sign(signature_data.message, 8).then(signature => { + key = curve.keyFromPublic(key_data.p256.pub); + expect(key.verify(signature_data.message, signature, 8)).to.eventually.be.true; + }); }); it('Shared secret generation', function (done) { var curve = elliptic_curves.get('p256'); diff --git a/test/general/ecc.js b/test/general/ecc.js index 92bb7c52..0213b349 100644 --- a/test/general/ecc.js +++ b/test/general/ecc.js @@ -200,7 +200,7 @@ describe('Elliptic Curve Cryptography', function () { var romeo = load_priv_key('romeo'); var juliet = load_pub_key('juliet'); expect(romeo.decrypt(data['romeo'].pass)).to.be.true; - openpgp.encrypt( + return openpgp.encrypt( {publicKeys: [juliet], privateKeys: [romeo], data: data.romeo.message + "\n"} ).then(function (encrypted) { var message = openpgp.message.readArmored(encrypted.data); From 3ce63097885d6c2c65007824210fa2aab252ee5d Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Fri, 19 Jan 2018 13:02:00 +0100 Subject: [PATCH 44/75] cleanup --- src/crypto/crypto.js | 82 ++++++------------- src/crypto/public_key/elliptic/ecdh.js | 2 +- src/packet/public_key.js | 5 +- .../public_key_encrypted_session_key.js | 2 +- src/packet/secret_key.js | 5 +- src/type/kdf_params.js | 11 ++- 6 files changed, 40 insertions(+), 67 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 4c832726..00dc28bd 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -38,29 +38,15 @@ import type_kdf_params from '../type/kdf_params.js'; import type_mpi from '../type/mpi.js'; import type_oid from '../type/oid.js'; -function createType(data, type) { - switch(type) { - case 'mpi': - return new type_mpi(data); - case 'oid': - return new type_oid(data); - case 'kdf': - if (data) { - return new type_kdf_params(data[0], data[1]); - } - return new type_kdf_params(); - case 'ecdh_symkey': - return new type_ecdh_symkey(data); - default: - throw new Error('Unknown type.'); - } -} -function constructParams(result, types) { - for (var i=0; i < types.length; i++) { - result[i] = createType(result[i], types[i]); - } - return result; +function constructParams(types, data) { + return types.map(function(type, i) { + if (data && data[i]) { + return new type(data[i]); + } else { + return new type(); + } + }); } export default { @@ -83,7 +69,7 @@ export default { var n = publicParams[0].toBigInteger(); var e = publicParams[1].toBigInteger(); m = data.toBigInteger(); - return constructParams([rsa.encrypt(m, e, n)], types); + return constructParams(types, [rsa.encrypt(m, e, n)]); case 'elgamal': var elgamal = new publicKey.elgamal(); @@ -91,7 +77,7 @@ export default { var g = publicParams[1].toBigInteger(); var y = publicParams[2].toBigInteger(); m = data.toBigInteger(); - return constructParams(elgamal.encrypt(m, g, p, y), types); + return constructParams(types, elgamal.encrypt(m, g, p, y)); case 'ecdh': var ecdh = publicKey.elliptic.ecdh; @@ -101,7 +87,7 @@ export default { var res = await ecdh.encrypt( curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint ); - return constructParams([res.V, res.C], types); + return constructParams(types, [res.V, res.C]); default: return []; @@ -172,21 +158,21 @@ export default { // - MPI of RSA secret prime value p. // - MPI of RSA secret prime value q (p < q). // - MPI of u, the multiplicative inverse of p, mod q. - return ['mpi', 'mpi', 'mpi', 'mpi']; + return [type_mpi, type_mpi, type_mpi, type_mpi]; case 'elgamal': // Algorithm-Specific Fields for Elgamal secret keys: // - MPI of Elgamal secret exponent x. - return ['mpi']; + return [type_mpi]; case 'dsa': // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. - return ['mpi']; + return [type_mpi]; case 'ecdh': case 'ecdsa': case 'eddsa': // Algorithm-Specific Fields for ECDSA or ECDH secret keys: // - MPI of an integer representing the secret key. - return ['mpi']; + return [type_mpi]; default: throw new Error('Unknown algorithm'); } @@ -204,32 +190,32 @@ export default { case 'rsa_encrypt': case 'rsa_encrypt_sign': case 'rsa_sign': - return ['mpi', 'mpi']; + return [type_mpi, type_mpi]; // Algorithm-Specific Fields for Elgamal public keys: // - MPI of Elgamal prime p; // - MPI of Elgamal group generator g; // - MPI of Elgamal public key value y (= g**x mod p where x is secret). case 'elgamal': - return ['mpi', 'mpi', 'mpi']; + return [type_mpi, type_mpi, type_mpi]; // Algorithm-Specific Fields for DSA public keys: // - MPI of DSA prime p; // - MPI of DSA group order q (q is a prime divisor of p-1); // - MPI of DSA group generator g; // - MPI of DSA public-key value y (= g**x mod p where x is secret). case 'dsa': - return ['mpi', 'mpi', 'mpi', 'mpi']; + return [type_mpi, type_mpi, type_mpi, type_mpi]; // Algorithm-Specific Fields for ECDSA/EdDSA public keys: // - OID of curve; // - MPI of EC point representing public key. case 'ecdsa': case 'eddsa': - return ['oid', 'mpi']; + return [type_oid, type_mpi]; // Algorithm-Specific Fields for ECDH public keys: // - OID of curve; // - MPI of EC point representing public key. // - KDF: variable-length field containing KDF parameters. case 'ecdh': - return ['oid', 'mpi', 'kdf']; + return [type_oid, type_mpi, type_kdf_params]; default: throw new Error('Unknown algorithm.'); } @@ -245,19 +231,19 @@ export default { // - MPI of RSA encrypted value m**e mod n. case 'rsa_encrypt': case 'rsa_encrypt_sign': - return ['mpi']; + return [type_mpi]; // Algorithm-Specific Fields for Elgamal encrypted session keys: // - MPI of Elgamal value g**k mod p // - MPI of Elgamal value m * y**k mod p case 'elgamal': - return ['mpi', 'mpi']; + return [type_mpi, type_mpi]; // Algorithm-Specific Fields for ECDH encrypted session keys: // - MPI containing the ephemeral key used to establish the shared secret // - ECDH Symmetric Key case 'ecdh': - return ['mpi', 'ecdh_symkey']; + return [type_mpi, type_ecdh_symkey]; default: throw new Error('Unknown algorithm.'); @@ -277,18 +263,18 @@ export default { //remember "publicKey" refers to the crypto/public_key dir var rsa = new publicKey.rsa(); return rsa.generate(bits, "10001").then(function(keyObject) { - return constructParams([keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u], types); + return constructParams(types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u]); }); case 'ecdsa': case 'eddsa': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return constructParams([keyObject.oid, keyObject.Q, keyObject.d], types); + return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]); }); case 'ecdh': return publicKey.elliptic.generate(curve).then(function (keyObject) { - return constructParams([keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d], types); + return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); }); default: @@ -296,22 +282,6 @@ export default { } }, - - getCloneFn: function(type) { - switch(type) { - case 'mpi': - return type_mpi.fromClone; - case 'oid': - return type_oid.fromClone; - case 'kdf': - return type_kdf_params.fromClone; - case 'ecdh_symkey': - return type_ecdh_symkey.fromClone; - default: - throw new Error('Unknown type.'); - } - }, - /** * generate random byte prefix as string for the specified algorithm * @param {module:enums.symmetric} algo Algorithm to use (see {@link http://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2}) diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index f6a412c8..c65dc26d 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -46,7 +46,7 @@ import type_oid from '../../../type/oid.js'; // Build Param for ECDH algorithm (RFC 6637) function buildEcdhParam(public_algo, oid, cipher_algo, hash_algo, fingerprint) { oid = new type_oid(oid); - const kdf_params = new type_kdf_params(hash_algo, cipher_algo); + const kdf_params = new type_kdf_params([hash_algo, cipher_algo]); return util.concatUint8Array([ oid.write(), new Uint8Array([public_algo]), diff --git a/src/packet/public_key.js b/src/packet/public_key.js index cf2ff8ca..819dbdaf 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -94,7 +94,7 @@ PublicKey.prototype.read = function (bytes) { this.algorithm = enums.read(enums.publicKey, bytes[pos++]); var types = crypto.getPubKeyParamTypes(this.algorithm); - this.params = crypto.constructParams(new Array(types.length), types); + this.params = crypto.constructParams(types); var b = bytes.subarray(pos, bytes.length); var p = 0; @@ -214,8 +214,7 @@ PublicKey.prototype.postCloneTypeFix = function() { const types = crypto.getPubKeyParamTypes(this.algorithm); for (var i = 0; i < types.length; i++) { const param = this.params[i]; - const cloneFn = crypto.getCloneFn(types[i]); - this.params[i] = cloneFn(param); + this.params[i] = types[i].fromClone(param); } if (this.keyid) { this.keyid = type_keyid.fromClone(this.keyid); diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 00b9287f..b650f144 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -79,7 +79,7 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { var i = 10; var types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); - this.encrypted = crypto.constructParams(new Array(types.length), types); + this.encrypted = crypto.constructParams(types); for (var j = 0; j < types.length; j++) { i += this.encrypted[j].read(bytes.subarray(i, bytes.length)); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index dd3edc30..d2ed2d28 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -89,7 +89,7 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { } var types = crypto.getPrivKeyParamTypes(algorithm); - var params = crypto.constructParams(new Array(types.length), types); + var params = crypto.constructParams(types); var p = 0; for (var i = 0; i < types.length && p < cleartext.length; i++) { @@ -298,8 +298,7 @@ SecretKey.prototype.clearPrivateParams = function () { const types = crypto.getPubKeyParamTypes(this.algorithm).concat(crypto.getPrivKeyParamTypes(this.algorithm)); for (var i = 0; i < this.params.length; i++) { const param = this.params[i]; - const cloneFn = crypto.getCloneFn(types[i]); - this.params[i] = cloneFn(param); + this.params[i] = types[i].fromClone(param); } if (this.keyid) { this.keyid = type_keyid.fromClone(this.keyid); diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index b5c8545b..686830b5 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -33,9 +33,14 @@ module.exports = KDFParams; * @param {enums.hash} hash Hash algorithm * @param {enums.symmetric} cipher Symmetric algorithm */ -function KDFParams(hash, cipher) { - this.hash = hash || enums.hash.sha1; - this.cipher = cipher || enums.symmetric.aes128; +function KDFParams(data) { + if (data && data.length === 2) { + this.hash = data[0]; + this.cipher = data[1]; + } else { + this.hash = enums.hash.sha1; + this.cipher = enums.symmetric.aes128; + } } /** From a173a63b16bae3ff059222eba6146c498ab6f087 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Fri, 19 Jan 2018 14:38:27 +0100 Subject: [PATCH 45/75] doc fixes --- src/crypto/signature.js | 2 +- src/packet/public_key_encrypted_session_key.js | 2 +- src/packet/secret_key.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/crypto/signature.js b/src/crypto/signature.js index d30d87c3..c233a8b2 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -87,7 +87,7 @@ export default { * Create a signature on data using the specified algorithm * @param {module:enums.hash} hash_algo hash Algorithm to use (See {@link http://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}) * @param {module:enums.publicKey} algo Asymmetric cipher algorithm to use (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) - * @param {Array} publicMPIs Public followed by Private key multiprecision algorithm-specific parameters + * @param {Array} keyIntegers Public followed by Private key multiprecision algorithm-specific parameters * @param {Uint8Array} data Data to be signed * @return {Array} */ diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index b650f144..79672d8f 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -129,7 +129,7 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { * packets (tag 1) * * @param {module:packet/secret_key} key - * Private key with secMPIs unlocked + * Private key with secret params unlocked * @return {String} The unencrypted session key */ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index d2ed2d28..284d5c89 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -25,7 +25,7 @@ * @requires crypto * @requires enums * @requires packet/public_key - * @requires type/mpi + * @requires type/keyid * @requires type/s2k * @requires util * @module packet/secret_key @@ -95,7 +95,7 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { for (var i = 0; i < types.length && p < cleartext.length; i++) { p += params[i].read(cleartext.subarray(p, cleartext.length)); if (p > cleartext.length) { - throw new Error('Error reading MPI @:' + p); + throw new Error('Error reading param @:' + p); } } @@ -205,13 +205,13 @@ function produceEncryptionKey(s2k, passphrase, algorithm) { } /** - * Decrypts the private key MPIs which are needed to use the key. + * Decrypts the private key params which are needed to use the key. * @link module:packet/secret_key.isDecrypted should be * false otherwise a call to this function is not needed * * @param {String} str_passphrase The passphrase for this private key * as string - * @return {Boolean} True if the passphrase was correct or MPI already + * @return {Boolean} True if the passphrase was correct or param already * decrypted; false if not */ SecretKey.prototype.decrypt = function (passphrase) { @@ -281,11 +281,11 @@ SecretKey.prototype.generate = function (bits, curve) { }; /** - * Clear private MPIs, return to initial state + * Clear private params, return to initial state */ SecretKey.prototype.clearPrivateParams = function () { if (!this.encrypted) { - throw new Error('If secret key is not encrypted, clearing private MPIs is irreversible.'); + throw new Error('If secret key is not encrypted, clearing private params is irreversible.'); } this.params = this.params.slice(0, crypto.getPubKeyParamTypes(this.algorithm).length); this.isDecrypted = false; From fa5805eaf72f2b18d89e80dbee30acd70d20cc85 Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Fri, 19 Jan 2018 15:02:17 +0100 Subject: [PATCH 46/75] don't assume all mpi type for encsessionkey --- src/packet/public_key_encrypted_session_key.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 79672d8f..2b4d5c58 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -164,7 +164,8 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { */ PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() { this.publicKeyId = type_keyid.fromClone(this.publicKeyId); + var types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); for (var i = 0; i < this.encrypted.length; i++) { - this.encrypted[i] = type_mpi.fromClone(this.encrypted[i]); + this.encrypted[i] = types[i].fromClone(this.encrypted[i]); } }; From d02b9c7bf0f013b47b6f4c60e892c77573b18398 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 23 Jan 2018 05:16:19 -0800 Subject: [PATCH 47/75] Fixes problems with user certificate verification. TODO: if using lower level functions, key.verifyPrimaryUser() MUST be called and resolved before calling key.getSigning/EncryptionKeyPacket(), key.getPrimaryUser(), etc. Also: adds a flag to allow verification with expired keys from: (cherry picked from commit b9d175d99853f0f50cb0d45d5dac29fc421d12f1) (cherry picked from commit 50940b2a1e1d087aa6ac07784ae9751ed28c22b8) --- src/cleartext.js | 18 +++++--- test/general/key.js | 94 ++++++++++++++++++++++++--------------- test/general/openpgp.js | 21 ++++++--- test/general/signature.js | 37 ++++++++++++--- 4 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index cf12f9e6..e5909392 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -86,10 +86,15 @@ CleartextMessage.prototype.signDetached = async function(privateKeys) { if (privateKey.isPublic()) { throw new Error('Need private key for signing'); } + await privateKey.verifyPrimaryUser(); + var signingKeyPacket = privateKey.getSigningKeyPacket(); + if (!signingKeyPacket) { + throw new Error('Could not find valid key packet for signing in key ' + + privateKey.primaryKey.getKeyId().toHex()); + } var signaturePacket = new packet.Signature(); signaturePacket.signatureType = enums.signature.text; signaturePacket.hashAlgorithm = config.prefer_hash_algorithm; - var signingKeyPacket = privateKey.getSigningKeyPacket(); signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; if (!signingKeyPacket.isDecrypted) { throw new Error('Private key is not decrypted.'); @@ -124,12 +129,13 @@ CleartextMessage.prototype.verifyDetached = function(signature, keys) { literalDataPacket.setText(this.text); return Promise.all(signatureList.map(async function(signature) { var keyPacket = null; - for (var j = 0; j < keys.length; j++) { - keyPacket = keys[j].getSigningKeyPacket(signature.issuerKeyId); - if (keyPacket) { - break; + await Promise.all(keys.map(async function(key) { + await key.verifyPrimaryUser(); + var result = key.getSigningKeyPacket(signature.issuerKeyId, config.verify_expired_keys); + if (result) { + keyPacket = result; } - } + })); var verifiedSig = {}; if (keyPacket) { diff --git a/test/general/key.js b/test/general/key.js index 2722b07d..47974849 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -742,7 +742,9 @@ describe('Key', function() { var pubKey = openpgp.key.readArmored(twoKeys).keys[1]; expect(pubKey).to.exist; expect(pubKey).to.be.an.instanceof(openpgp.key.Key); - expect(pubKey.getExpirationTime().toISOString()).to.be.equal('2018-11-26T10:58:29.000Z'); + return pubKey.verifyPrimaryUser().then(() => { + expect(pubKey.getExpirationTime().toISOString()).to.be.equal('2018-11-26T10:58:29.000Z'); + }); }); it('Method getExpirationTime V4 SubKey', function() { @@ -890,26 +892,32 @@ describe('Key', function() { it('getPreferredSymAlgo() - one key - AES256', function() { var key1 = openpgp.key.readArmored(twoKeys).keys[0]; - var prefAlgo = openpgp.key.getPreferredSymAlgo([key1]); - expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256); + return key1.verifyPrimaryUser().then(() => { + var prefAlgo = openpgp.key.getPreferredSymAlgo([key1]); + expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256); + }); }); it('getPreferredSymAlgo() - two key - AES128', function() { var keys = openpgp.key.readArmored(twoKeys).keys; var key1 = keys[0]; var key2 = keys[1]; - key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = [6,7,3]; - var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]); - expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128); + return Promise.all([key1.verifyPrimaryUser(), key2.verifyPrimaryUser()]).then(() => { + key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = [6,7,3]; + var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]); + expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128); + }); }); it('getPreferredSymAlgo() - two key - one without pref', function() { var keys = openpgp.key.readArmored(twoKeys).keys; var key1 = keys[0]; var key2 = keys[1]; - key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = null; - var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]); - expect(prefAlgo).to.equal(openpgp.config.encryption_cipher); + return Promise.all([key1.verifyPrimaryUser(), key2.verifyPrimaryUser()]).then(() => { + key2.getPrimaryUser().selfCertificate.preferredSymmetricAlgorithms = null; + var prefAlgo = openpgp.key.getPreferredSymAlgo([key1, key2]); + expect(prefAlgo).to.equal(openpgp.config.encryption_cipher); + }); }); it('Preferences of generated key', function() { @@ -944,10 +952,12 @@ describe('Key', function() { it('getPrimaryUser()', function() { var key = openpgp.key.readArmored(pub_sig_test).keys[0]; - var primUser = key.getPrimaryUser(); - expect(primUser).to.exist; - expect(primUser.user.userId.userid).to.equal('Signature Test '); - expect(primUser.selfCertificate).to.be.an.instanceof(openpgp.packet.Signature); + return key.verifyPrimaryUser().then(() => { + var primUser = key.getPrimaryUser(); + expect(primUser).to.exist; + expect(primUser.user.userId.userid).to.equal('Signature Test '); + expect(primUser.selfCertificate).to.be.an.instanceof(openpgp.packet.Signature); + }); }); it('Generated key is not unlocked by default', function() { @@ -1020,59 +1030,68 @@ describe('Key', function() { return openpgp.generateKey(opt).then(function(key) { key = key.key; - var expiration = key.getExpirationTime(); - expect(expiration).to.exist; + return key.verifyPrimaryUser().then(() => { + var expiration = key.getExpirationTime(); + expect(expiration).to.exist; - var actual_delta = (new Date(expiration) - new Date()) / 1000; - expect(Math.abs(actual_delta - expect_delta)).to.be.below(60); + var actual_delta = (new Date(expiration) - new Date()) / 1000; + expect(Math.abs(actual_delta - expect_delta)).to.be.below(60); - var subKeyExpiration = key.subKeys[0].getExpirationTime(); - expect(subKeyExpiration).to.exist; + var subKeyExpiration = key.subKeys[0].getExpirationTime(); + expect(subKeyExpiration).to.exist; - var actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000; - expect(Math.abs(actual_subKeyDelta - expect_delta)).to.be.below(60); + var actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000; + expect(Math.abs(actual_subKeyDelta - expect_delta)).to.be.below(60); + }); }); }); - it('Sign and verify key - primary user', function(done) { + it('Sign and verify key - primary user', function() { var key = openpgp.key.readArmored(pub_sig_test).keys[0]; var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; privateKey.decrypt('hello world'); - key.signPrimaryUser([privateKey]).then(key => { - key.verifyPrimaryUser([privateKey]).then(signatures => { + return key.signPrimaryUser([privateKey]).then(key => { + return Promise.all( + [key.verifyPrimaryUser([privateKey]), privateKey.verifyPrimaryUser()] + ).then(results => { + var signatures = results[0]; expect(signatures.length).to.equal(2); expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); expect(signatures[0].valid).to.be.null; expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); expect(signatures[1].valid).to.be.true; - done(); }); }); }); - it('Sign key and verify with wrong key - primary user', function(done) { + it('Sign key and verify with wrong key - primary user', function() { var key = openpgp.key.readArmored(pub_sig_test).keys[0]; var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; var wrongKey = openpgp.key.readArmored(wrong_key).keys[0]; privateKey.decrypt('hello world'); - key.signPrimaryUser([privateKey]).then(key => { - key.verifyPrimaryUser([wrongKey]).then(signatures => { + return key.signPrimaryUser([privateKey]).then(key => { + return Promise.all( + [key.verifyPrimaryUser([wrongKey]), privateKey.verifyPrimaryUser()] + ).then(results => { + var signatures = results[0]; expect(signatures.length).to.equal(2); expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); expect(signatures[0].valid).to.be.null; expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); expect(signatures[1].valid).to.be.null; - done(); }); }); }); - it('Sign and verify key - all users', function(done) { + it('Sign and verify key - all users', function() { var key = openpgp.key.readArmored(multi_uid_key).keys[0]; var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; privateKey.decrypt('hello world'); - key.signAllUsers([privateKey]).then(key => { - key.verifyAllUsers([privateKey]).then(signatures => { + return key.signAllUsers([privateKey]).then(key => { + return Promise.all( + [key.verifyAllUsers([privateKey]), key.verifyPrimaryUser(), privateKey.verifyPrimaryUser()] + ).then(results => { + var signatures = results[0]; expect(signatures.length).to.equal(4); expect(signatures[0].userid).to.equal(key.users[0].userId.userid); expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); @@ -1086,18 +1105,20 @@ describe('Key', function() { expect(signatures[3].userid).to.equal(key.users[1].userId.userid); expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); expect(signatures[3].valid).to.be.true; - done(); }); }); }); - it('Sign key and verify with wrong key - all users', function(done) { + it('Sign key and verify with wrong key - all users', function() { var key = openpgp.key.readArmored(multi_uid_key).keys[0]; var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0]; var wrongKey = openpgp.key.readArmored(wrong_key).keys[0]; privateKey.decrypt('hello world'); - key.signAllUsers([privateKey]).then(key => { - key.verifyAllUsers([wrongKey]).then(signatures => { + return key.signAllUsers([privateKey]).then(key => { + return Promise.all( + [key.verifyAllUsers([wrongKey]), key.verifyPrimaryUser(), privateKey.verifyPrimaryUser()] + ).then(results => { + var signatures = results[0]; expect(signatures.length).to.equal(4); expect(signatures[0].userid).to.equal(key.users[0].userId.userid); expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex()); @@ -1111,7 +1132,6 @@ describe('Key', function() { expect(signatures[3].userid).to.equal(key.users[1].userId.userid); expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex()); expect(signatures[3].valid).to.be.null; - done(); }); }); }); diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 09b4eee1..9f874e21 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -405,7 +405,7 @@ describe('OpenPGP.js public api tests', function() { describe('encrypt, decrypt, sign, verify - integration tests', function() { var privateKey, publicKey, zero_copyVal, use_nativeVal, aead_protectVal; - beforeEach(function() { + beforeEach(function(done) { publicKey = openpgp.key.readArmored(pub_key); expect(publicKey.keys).to.have.length(1); expect(publicKey.err).to.not.exist; @@ -415,6 +415,7 @@ describe('OpenPGP.js public api tests', function() { zero_copyVal = openpgp.config.zero_copy; use_nativeVal = openpgp.config.use_native; aead_protectVal = openpgp.config.aead_protect; + privateKey.keys[0].verifyPrimaryUser().then(() => done()); }); afterEach(function() { @@ -515,8 +516,9 @@ describe('OpenPGP.js public api tests', function() { describe('encryptSessionKey, decryptSessionKey', function() { var sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]); - beforeEach(function() { + beforeEach(function(done) { expect(privateKey.keys[0].decrypt(passphrase)).to.be.true; + privateKey.keys[0].verifyPrimaryUser().then(() => done()); }); it('should encrypt with public key', function() { @@ -609,8 +611,9 @@ describe('OpenPGP.js public api tests', function() { '=6XMW\r\n' + '-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n'; - beforeEach(function() { + beforeEach(function(done) { expect(privateKey.keys[0].decrypt(passphrase)).to.be.true; + privateKey.keys[0].verifyPrimaryUser().then(() => done()); }); it('should encrypt then decrypt', function() { @@ -830,8 +833,10 @@ describe('OpenPGP.js public api tests', function() { expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex()); expect(decrypted.signatures[0].signature.packets.length).to.equal(1); expect(decrypted.signatures[1].valid).to.be.true; - expect(decrypted.signatures[1].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); - expect(decrypted.signatures[1].signature.packets.length).to.equal(1); + return privKeyDE.verifyPrimaryUser().then(() => { + expect(decrypted.signatures[1].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); + expect(decrypted.signatures[1].signature.packets.length).to.equal(1); + }); }); }); @@ -1127,8 +1132,10 @@ describe('OpenPGP.js public api tests', function() { expect(encrypted.data).to.exist; expect(encrypted.data).to.equal(plaintext); expect(encrypted.signatures[0].valid).to.be.true; - expect(encrypted.signatures[0].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); - expect(encrypted.signatures[0].signature.packets.length).to.equal(1); + return privKeyDE.verifyPrimaryUser().then(() => { + expect(encrypted.signatures[0].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex()); + expect(encrypted.signatures[0].signature.packets.length).to.equal(1); + }); }); }); }); diff --git a/test/general/signature.js b/test/general/signature.js index efe2db14..1c1089bd 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -432,7 +432,7 @@ describe("Signature", function() { var sMsg = openpgp.message.readArmored(signedArmor); var pub_key = openpgp.key.readArmored(pub_key_arm2).keys[0]; - sMsg.verify([pub_key]).then(verified => { + return sMsg.verify([pub_key]).then(verified => { expect(verified).to.exist; expect(verified).to.have.length(1); expect(verified[0].valid).to.be.true; @@ -544,7 +544,7 @@ describe("Signature", function() { expect(sMsg.getText()).to.equal(plaintext); - sMsg.verify([pubKey2, pubKey3]).then(verifiedSig => { + return sMsg.verify([pubKey2, pubKey3]).then(verifiedSig => { expect(verifiedSig).to.exist; expect(verifiedSig).to.have.length(2); expect(verifiedSig[0].valid).to.be.true; @@ -604,7 +604,7 @@ describe("Signature", function() { var plaintext = 'short message\nnext line\n한국어/조선말'; var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey.getSigningKeyPacket().decrypt('hello world'); + privKey.primaryKey.decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext }).then(function(signed) { @@ -618,14 +618,13 @@ describe("Signature", function() { expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); - }); it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', function() { var plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말'); var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey.getSigningKeyPacket().decrypt('hello world'); + privKey.primaryKey.decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext }).then(function(signed) { @@ -639,14 +638,13 @@ describe("Signature", function() { expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); - }); it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', function() { var plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말'); var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; - privKey.getSigningKeyPacket().decrypt('hello world'); + privKey.primaryKey.decrypt('hello world'); return openpgp.sign({ privateKeys:[privKey], data:plaintext, armor:false }).then(function(signed) { @@ -660,6 +658,31 @@ describe("Signature", function() { expect(cleartextSig.signatures[0].valid).to.be.true; expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1); }); + }); + + it('Verify test with expired verification public key and verify_expired_keys set to false', function() { + openpgp.config.verify_expired_keys = false; + var pubKey = openpgp.key.readArmored(pub_expired).keys[0]; + var message = openpgp.message.readArmored(msg_sig_expired); + return openpgp.verify({ publicKeys:[pubKey], message:message }).then(function(verified) { + expect(verified).to.exist; + expect(verified.signatures).to.have.length(1); + expect(verified.signatures[0].valid).to.not.be.true; + expect(verified.signatures[0].signature.packets.length).to.equal(1); + }); + + }); + + it('Verify test with expired verification public key and verify_expired_keys set to true', function() { + openpgp.config.verify_expired_keys = true; + var pubKey = openpgp.key.readArmored(pub_expired).keys[0]; + var message = openpgp.message.readArmored(msg_sig_expired); + return openpgp.verify({ publicKeys:[pubKey], message:message }).then(function(verified) { + expect(verified).to.exist; + expect(verified.signatures).to.have.length(1); + expect(verified.signatures[0].valid).to.be.true; + expect(verified.signatures[0].signature.packets.length).to.equal(1); + }); }); From 2d1a1130d740eda4b407de8ac1631ce64b0a1dac Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 24 Jan 2018 18:03:48 -0800 Subject: [PATCH 48/75] Fixes bug in isRevoked, removes isValidSelfCertificate, adds verifyCertificate --- test/general/x25519.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/general/x25519.js b/test/general/x25519.js index 4b35e26d..9f401103 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -232,9 +232,9 @@ describe('X25519 Cryptography', function () { expect(user.selfCertifications[0].verify( primaryKey, {userid: user.userId, key: primaryKey} )).to.eventually.be.true; - expect(user.isValidSelfCertificate( - primaryKey, user.selfCertifications[0] - )).to.eventually.be.true; + expect(user.verifyCertificate( + primaryKey, user.selfCertifications[0], [hi.toPublic()] + )).to.eventually.equal(openpgp.enums.keyStatus.valid); var options = { userIds: {name: "Bye", email: "bye@good.bye"}, @@ -252,9 +252,9 @@ describe('X25519 Cryptography', function () { expect(user.selfCertifications[0].verify( bye.primaryKey, {userid: user.userId, key: bye.primaryKey} )).to.eventually.be.true; - expect(user.isValidSelfCertificate( - bye.primaryKey, user.selfCertifications[0] - )).to.eventually.be.true; + expect(user.verifyCertificate( + bye.primaryKey, user.selfCertifications[0], [bye.toPublic()] + )).to.eventually.equal(openpgp.enums.keyStatus.valid); return Promise.all([ // Hi trusts Bye! From bf00fbd3ea8980c7da88f6300c22c6ce2fe19b48 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 28 Jan 2018 21:13:29 -0800 Subject: [PATCH 49/75] added ECC curve options to docs --- src/crypto/public_key/elliptic/ecdsa.js | 1 + src/crypto/public_key/elliptic/eddsa.js | 9 ++++++++- src/crypto/public_key/elliptic/key.js | 7 +++++++ src/openpgp.js | 3 ++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 0493c55d..4b73166e 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -18,6 +18,7 @@ // Implementation of ECDSA following RFC6637 for Openpgpjs /** + * @requires crypto/hash * @requires crypto/public_key/jsbn * @requires crypto/public_key/elliptic/curves * @module crypto/public_key/elliptic/ecdsa diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index cccb23dd..58a9512d 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -1,4 +1,11 @@ -// Implementation of EdDSA for OpenPGP +// Implementation of EdDSA following RFC4880bis-02 for OpenPGP + +/** + * @requires crypto/hash + * @requires crypto/public_key/jsbn + * @requires crypto/public_key/elliptic/curves + * @module crypto/public_key/elliptic/eddsa + */ 'use strict'; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 22131762..026beaae 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -18,8 +18,15 @@ // Wrapper for a KeyPair of an Elliptic Curve /** + * @requires bn.js + * @requires asn1.js + * @requires jwk-to-pem + * @requires crypto/public_key/elliptic/curves * @requires crypto/hash * @requires util + * @requires enums + * @requires config + * @requires encoding/base64 * @module crypto/public_key/elliptic/key */ diff --git a/src/openpgp.js b/src/openpgp.js index 006e2ce3..7540ee0e 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -92,7 +92,8 @@ export function destroyWorker() { * Generates a new OpenPGP key pair. Supports RSA and ECC keys. Primary and subkey will be of same type. * @param {Array} userIds array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }] * @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key - * @param {Number} numBits (optional) number of bits for the key creation. (should be 2048 or 4096) + * @param {Number} numBits (optional) number of bits for RSA keys: 2048 or 4096. + * @param {String} curve (optional) elliptic curve for ECC keys: curve25519, p256, p384, p521, or secp256k1 * @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked * @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires * @return {Promise} The generated key object in the form: From be2c605e5b5bc3023ba060cb2ce46a84cd4ba210 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 29 Jan 2018 12:14:02 -0800 Subject: [PATCH 50/75] Improves secret key encryption/decryption performance by 35% --- src/type/s2k.js | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/type/s2k.js b/src/type/s2k.js index 6667cefa..8cc1c75a 100644 --- a/src/type/s2k.js +++ b/src/type/s2k.js @@ -156,15 +156,11 @@ S2K.prototype.produce_key = function (passphrase, numBytes) { util.concatUint8Array([prefix, s2k.salt, passphrase])); case 'iterated': - var isp = [], - count = s2k.get_count(), - data = util.concatUint8Array([s2k.salt,passphrase]); + var count = s2k.get_count(), + data = util.concatUint8Array([s2k.salt,passphrase]), + isp = new Array(Math.ceil(count / data.length)); - while (isp.length * data.length < count) { - isp.push(data); - } - - isp = util.concatUint8Array(isp); + isp = util.concatUint8Array(isp.fill(data)); if (isp.length > count) { isp = isp.subarray(0, count); From aba8a7a6479f1a646e53dd9aeb9652a86bb66d40 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 30 Jan 2018 05:15:03 -0800 Subject: [PATCH 51/75] Switches to asmcrypto-lite for AES --- src/crypto/cipher/aes.js | 1126 +----------------------------------- src/crypto/cipher/index.js | 10 +- src/crypto/hash/index.js | 1 + src/crypto/hash/sha.js | 1 - test/general/x25519.js | 1 + 5 files changed, 14 insertions(+), 1125 deletions(-) diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js index db6b32bb..4259d3bd 100644 --- a/src/crypto/cipher/aes.js +++ b/src/crypto/cipher/aes.js @@ -1,1130 +1,24 @@ -/* Rijndael (AES) Encryption - * Copyright 2005 Herbert Hanewinkel, www.haneWIN.de - * version 1.1, check www.haneWIN.de for the latest version - - * This software is provided as-is, without express or implied warranty. - * Permission to use, copy, modify, distribute or sell this software, with or - * without fee, for any purpose and by any individual or organization, is hereby - * granted, provided that the above copyright notice and this paragraph appear - * in all copies. Distribution as a part of an application or binary must - * include the above copyright notice in the documentation and/or other - * materials provided with the application or distribution. - */ - /** * @module crypto/cipher/aes */ 'use strict'; -// The round constants used in subkey expansion -var Rcon = new Uint8Array([ - 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, - 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, - 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 -]); +import asmCrypto from 'asmcrypto-lite'; -// Precomputed lookup table for the SBox -var S = new Uint8Array([ - 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, - 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, - 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, - 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, - 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, - 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, - 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, - 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, - 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, - 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, - 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, - 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, - 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, - 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, - 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, - 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, - 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, - 22 -]); - -// Precomputed lookup table for the inverse SBox -var S_inv = new Uint8Array([ - 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, - 251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, - 233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, - 250, 195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, - 109, 139, 209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, - 204, 93, 101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, - 70, 87, 167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, - 228, 88, 5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, - 193, 175, 189, 3, 1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, - 234, 151, 242, 207, 206, 240, 180, 230, 115, 150, 172, 116, 34, 231, 173, - 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, 71, 241, 26, 113, 29, - 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, 252, 86, 62, 75, - 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, 31, 221, 168, - 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, 96, 81, - 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, 160, - 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, - 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, - 125 -]); - -var T1_enc = new Uint32Array([ - 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, - 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, - 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, - 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, - 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, - 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, - 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, - 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, - 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, - 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, - 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, - 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, - 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, - 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, - 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, - 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, - 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, - 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, - 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, - 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, - 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, - 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, - 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, - 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, - 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, - 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, - 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, - 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, - 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, - 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, - 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, - 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, - 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, - 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, - 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, - 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, - 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, - 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, - 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, - 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, - 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, - 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, - 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, - 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, - 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, - 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, - 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, - 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, - 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, - 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, - 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, - 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, - 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, - 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, - 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, - 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, - 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, - 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, - 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, - 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, - 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, - 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, - 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, - 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c -]); - -var T1_dec = new Uint32Array([ - 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, - 0xcb6bab3b, 0xf1459d1f, 0xab58faac, 0x9303e34b, - 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, - 0xfcd7e54f, 0xd7cb2ac5, 0x80443526, 0x8fa362b5, - 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, - 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b, - 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, - 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, - 0x6a89c275, 0x78798ef4, 0x6b3e5899, 0xdd71b927, - 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, - 0x184adf63, 0x82311ae5, 0x60335197, 0x457f5362, - 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, - 0x58684870, 0x19fd458f, 0x876cde94, 0xb7f87b52, - 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, - 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3, - 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, - 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, - 0xcdf4da65, 0xd5be0506, 0x1f6234d1, 0x8afea6c4, - 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, - 0x39ec830b, 0xaaef6040, 0x069f715e, 0x51106ebd, - 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, - 0xb58d5491, 0x055dc471, 0x6fd40604, 0xff155060, - 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, - 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879, - 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, - 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, - 0xfbff0efd, 0x5638850f, 0x1ed5ae3d, 0x27392d36, - 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, - 0xb1670a0c, 0x0fe75793, 0xd296eeb4, 0x9e919b1b, - 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, - 0x0aba93e2, 0xe52aa0c0, 0x43e0223c, 0x1d171b12, - 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, - 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3, - 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, - 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, - 0xcadc31d7, 0x10856342, 0x40229713, 0x2011c684, - 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, - 0x4b2f9e1d, 0xf330b2dc, 0xec52860d, 0xd0e3c177, - 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, - 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56, 0xef903322, - 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, - 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f, - 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, - 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, - 0xbe805d9f, 0x7c93d069, 0xa92dd56f, 0xb31225cf, - 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, - 0x097826cd, 0xf418596e, 0x01b79aec, 0xa89a4f83, - 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, - 0xd99be7ba, 0xce366f4a, 0xd4099fea, 0xd67cb029, - 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, - 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733, - 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, - 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, - 0xe3b5d19e, 0x1b886a4c, 0xb81f2cc1, 0x7f516546, - 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, - 0x5a1d67b3, 0x52d2db92, 0x335610e9, 0x1347d66d, - 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, - 0xee27a9ce, 0x35c961b7, 0xede51ce1, 0x3cb1477a, - 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, - 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478, - 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, - 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, - 0x7101a839, 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, - 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0 -]); - -var T2_enc = new Uint32Array([ - 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, - 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, - 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, - 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, - 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, - 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, - 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, - 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, - 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, - 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, - 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, - 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, - 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, - 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, - 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, - 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, - 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, - 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, - 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, - 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, - 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, - 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, - 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, - 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, - 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, - 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, - 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, - 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, - 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, - 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, - 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, - 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, - 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, - 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, - 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, - 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, - 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, - 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, - 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, - 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, - 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, - 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, - 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, - 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, - 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, - 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, - 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, - 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, - 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, - 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, - 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, - 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, - 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, - 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, - 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, - 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, - 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, - 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, - 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, - 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, - 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, - 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, - 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, - 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a -]); - -var T2_dec = new Uint32Array([ - 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, - 0x6bab3bcb, 0x459d1ff1, 0x58faacab, 0x03e34b93, - 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, - 0xd7e54ffc, 0xcb2ac5d7, 0x44352680, 0xa362b58f, - 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, - 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6, - 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, - 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, - 0x89c2756a, 0x798ef478, 0x3e58996b, 0x71b927dd, - 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, - 0x4adf6318, 0x311ae582, 0x33519760, 0x7f536245, - 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, - 0x68487058, 0xfd458f19, 0x6cde9487, 0xf87b52b7, - 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, - 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5, - 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, - 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, - 0xf4da65cd, 0xbe0506d5, 0x6234d11f, 0xfea6c48a, - 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, - 0xec830b39, 0xef6040aa, 0x9f715e06, 0x106ebd51, - 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, - 0x8d5491b5, 0x5dc47105, 0xd406046f, 0x155060ff, - 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, - 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db, - 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, - 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, - 0xff0efdfb, 0x38850f56, 0xd5ae3d1e, 0x392d3627, - 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, - 0x670a0cb1, 0xe757930f, 0x96eeb4d2, 0x919b1b9e, - 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, - 0xba93e20a, 0x2aa0c0e5, 0xe0223c43, 0x171b121d, - 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, - 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd, - 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, - 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, - 0xdc31d7ca, 0x85634210, 0x22971340, 0x11c68420, - 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, - 0x2f9e1d4b, 0x30b2dcf3, 0x52860dec, 0xe3c177d0, - 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, - 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8, 0x903322ef, - 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, - 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4, - 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, - 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, - 0x805d9fbe, 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, - 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, - 0x7826cd09, 0x18596ef4, 0xb79aec01, 0x9a4f83a8, - 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, - 0x9be7bad9, 0x366f4ace, 0x099fead4, 0x7cb029d6, - 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, - 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315, - 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, - 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, - 0xb5d19ee3, 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, - 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, - 0x1d67b35a, 0xd2db9252, 0x5610e933, 0x47d66d13, - 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, - 0x27a9ceee, 0xc961b735, 0xe51ce1ed, 0xb1477a3c, - 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, - 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886, - 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, - 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, - 0x01a83971, 0xb30c08de, 0xe4b4d89c, 0xc1566490, - 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042 -]); - -var T3_enc = new Uint32Array([ - 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, - 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, - 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, - 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, - 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, - 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, - 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, - 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, - 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, - 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, - 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, - 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, - 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, - 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, - 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, - 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, - 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, - 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, - 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, - 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, - 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, - 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, - 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, - 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, - 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, - 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, - 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, - 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, - 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, - 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, - 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, - 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, - 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, - 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, - 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, - 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, - 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, - 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, - 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, - 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, - 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, - 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, - 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, - 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, - 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, - 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, - 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, - 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, - 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, - 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, - 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, - 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, - 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, - 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, - 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, - 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, - 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, - 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, - 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, - 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, - 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, - 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, - 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, - 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16 -]); - -var T3_dec = new Uint32Array([ - 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, - 0xab3bcb6b, 0x9d1ff145, 0xfaacab58, 0xe34b9303, - 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, - 0xe54ffcd7, 0x2ac5d7cb, 0x35268044, 0x62b58fa3, - 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, - 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9, - 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, - 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, - 0xc2756a89, 0x8ef47879, 0x58996b3e, 0xb927dd71, - 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, - 0xdf63184a, 0x1ae58231, 0x51976033, 0x5362457f, - 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, - 0x48705868, 0x458f19fd, 0xde94876c, 0x7b52b7f8, - 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, - 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508, - 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, - 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, - 0xda65cdf4, 0x0506d5be, 0x34d11f62, 0xa6c48afe, - 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, - 0x830b39ec, 0x6040aaef, 0x715e069f, 0x6ebd5110, - 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, - 0x5491b58d, 0xc471055d, 0x06046fd4, 0x5060ff15, - 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, - 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee, - 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, - 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, - 0x0efdfbff, 0x850f5638, 0xae3d1ed5, 0x2d362739, - 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, - 0x0a0cb167, 0x57930fe7, 0xeeb4d296, 0x9b1b9e91, - 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, - 0x93e20aba, 0xa0c0e52a, 0x223c43e0, 0x1b121d17, - 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, - 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60, - 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, - 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, - 0x31d7cadc, 0x63421085, 0x97134022, 0xc6842011, - 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, - 0x9e1d4b2f, 0xb2dcf330, 0x860dec52, 0xc177d0e3, - 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, - 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c, 0x3322ef90, - 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, - 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf, - 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, - 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, - 0x5d9fbe80, 0xd0697c93, 0xd56fa92d, 0x25cfb312, - 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, - 0x26cd0978, 0x596ef418, 0x9aec01b7, 0x4f83a89a, - 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, - 0xe7bad99b, 0x6f4ace36, 0x9fead409, 0xb029d67c, - 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, - 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8, - 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, - 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, - 0xd19ee3b5, 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, - 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, - 0x67b35a1d, 0xdb9252d2, 0x10e93356, 0xd66d1347, - 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, - 0xa9ceee27, 0x61b735c9, 0x1ce1ede5, 0x477a3cb1, - 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, - 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db, - 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, - 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, - 0xa8397101, 0x0c08deb3, 0xb4d89ce4, 0x566490c1, - 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257 -]); - -var T4_enc = new Uint32Array([ - 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, - 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, - 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, - 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, - 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, - 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, - 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, - 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, - 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, - 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, - 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, - 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, - 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, - 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, - 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, - 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, - 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, - 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, - 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, - 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, - 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, - 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, - 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, - 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, - 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, - 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, - 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, - 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, - 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f, - 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, - 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, - 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, - 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, - 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, - 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, - 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, - 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, - 0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888, - 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, - 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, - 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, - 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, - 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, - 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, - 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, - 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, - 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, - 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, - 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, - 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, - 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, - 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, - 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, - 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, - 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, - 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, - 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, - 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, - 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, - 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, - 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, - 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, - 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, - 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616 -]); - -var T4_dec = new Uint32Array([ - 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, - 0x3bcb6bab, 0x1ff1459d, 0xacab58fa, 0x4b9303e3, - 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, - 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435, 0xb58fa362, - 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, - 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3, - 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, - 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, - 0x756a89c2, 0xf478798e, 0x996b3e58, 0x27dd71b9, - 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, - 0x63184adf, 0xe582311a, 0x97603351, 0x62457f53, - 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, - 0x70586848, 0x8f19fd45, 0x94876cde, 0x52b7f87b, - 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, - 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837, - 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, - 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, - 0x65cdf4da, 0x06d5be05, 0xd11f6234, 0xc48afea6, - 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, - 0x0b39ec83, 0x40aaef60, 0x5e069f71, 0xbd51106e, - 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, - 0x91b58d54, 0x71055dc4, 0x046fd406, 0x60ff1550, - 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, - 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8, - 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, - 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, - 0xfdfbff0e, 0x0f563885, 0x3d1ed5ae, 0x3627392d, - 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, - 0x0cb1670a, 0x930fe757, 0xb4d296ee, 0x1b9e919b, - 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, - 0xe20aba93, 0xc0e52aa0, 0x3c43e022, 0x121d171b, - 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, - 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f, - 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, - 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, - 0xd7cadc31, 0x42108563, 0x13402297, 0x842011c6, - 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, - 0x1d4b2f9e, 0xdcf330b2, 0x0dec5286, 0x77d0e3c1, - 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, - 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d, 0x22ef9033, - 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, - 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad, - 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, - 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, - 0x9fbe805d, 0x697c93d0, 0x6fa92dd5, 0xcfb31225, - 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, - 0xcd097826, 0x6ef41859, 0xec01b79a, 0x83a89a4f, - 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, - 0xbad99be7, 0x4ace366f, 0xead4099f, 0x29d67cb0, - 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, - 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7, - 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, - 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, - 0x9ee3b5d1, 0x4c1b886a, 0xc1b81f2c, 0x467f5165, - 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, - 0xb35a1d67, 0x9252d2db, 0xe9335610, 0x6d1347d6, - 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, - 0xceee27a9, 0xb735c961, 0xe1ede51c, 0x7a3cb147, - 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, - 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44, - 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, - 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, - 0x397101a8, 0x08deb30c, 0xd89ce4b4, 0x6490c156, - 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 -]); - - -var U1 = new Uint32Array([ - 0x00000000,0x0b0d090e,0x161a121c,0x1d171b12, - 0x2c342438,0x27392d36,0x3a2e3624,0x31233f2a, - 0x58684870,0x5365417e,0x4e725a6c,0x457f5362, - 0x745c6c48,0x7f516546,0x62467e54,0x694b775a, - 0xb0d090e0,0xbbdd99ee,0xa6ca82fc,0xadc78bf2, - 0x9ce4b4d8,0x97e9bdd6,0x8afea6c4,0x81f3afca, - 0xe8b8d890,0xe3b5d19e,0xfea2ca8c,0xf5afc382, - 0xc48cfca8,0xcf81f5a6,0xd296eeb4,0xd99be7ba, - 0x7bbb3bdb,0x70b632d5,0x6da129c7,0x66ac20c9, - 0x578f1fe3,0x5c8216ed,0x41950dff,0x4a9804f1, - 0x23d373ab,0x28de7aa5,0x35c961b7,0x3ec468b9, - 0x0fe75793,0x04ea5e9d,0x19fd458f,0x12f04c81, - 0xcb6bab3b,0xc066a235,0xdd71b927,0xd67cb029, - 0xe75f8f03,0xec52860d,0xf1459d1f,0xfa489411, - 0x9303e34b,0x980eea45,0x8519f157,0x8e14f859, - 0xbf37c773,0xb43ace7d,0xa92dd56f,0xa220dc61, - 0xf66d76ad,0xfd607fa3,0xe07764b1,0xeb7a6dbf, - 0xda595295,0xd1545b9b,0xcc434089,0xc74e4987, - 0xae053edd,0xa50837d3,0xb81f2cc1,0xb31225cf, - 0x82311ae5,0x893c13eb,0x942b08f9,0x9f2601f7, - 0x46bde64d,0x4db0ef43,0x50a7f451,0x5baafd5f, - 0x6a89c275,0x6184cb7b,0x7c93d069,0x779ed967, - 0x1ed5ae3d,0x15d8a733,0x08cfbc21,0x03c2b52f, - 0x32e18a05,0x39ec830b,0x24fb9819,0x2ff69117, - 0x8dd64d76,0x86db4478,0x9bcc5f6a,0x90c15664, - 0xa1e2694e,0xaaef6040,0xb7f87b52,0xbcf5725c, - 0xd5be0506,0xdeb30c08,0xc3a4171a,0xc8a91e14, - 0xf98a213e,0xf2872830,0xef903322,0xe49d3a2c, - 0x3d06dd96,0x360bd498,0x2b1ccf8a,0x2011c684, - 0x1132f9ae,0x1a3ff0a0,0x0728ebb2,0x0c25e2bc, - 0x656e95e6,0x6e639ce8,0x737487fa,0x78798ef4, - 0x495ab1de,0x4257b8d0,0x5f40a3c2,0x544daacc, - 0xf7daec41,0xfcd7e54f,0xe1c0fe5d,0xeacdf753, - 0xdbeec879,0xd0e3c177,0xcdf4da65,0xc6f9d36b, - 0xafb2a431,0xa4bfad3f,0xb9a8b62d,0xb2a5bf23, - 0x83868009,0x888b8907,0x959c9215,0x9e919b1b, - 0x470a7ca1,0x4c0775af,0x51106ebd,0x5a1d67b3, - 0x6b3e5899,0x60335197,0x7d244a85,0x7629438b, - 0x1f6234d1,0x146f3ddf,0x097826cd,0x02752fc3, - 0x335610e9,0x385b19e7,0x254c02f5,0x2e410bfb, - 0x8c61d79a,0x876cde94,0x9a7bc586,0x9176cc88, - 0xa055f3a2,0xab58faac,0xb64fe1be,0xbd42e8b0, - 0xd4099fea,0xdf0496e4,0xc2138df6,0xc91e84f8, - 0xf83dbbd2,0xf330b2dc,0xee27a9ce,0xe52aa0c0, - 0x3cb1477a,0x37bc4e74,0x2aab5566,0x21a65c68, - 0x10856342,0x1b886a4c,0x069f715e,0x0d927850, - 0x64d90f0a,0x6fd40604,0x72c31d16,0x79ce1418, - 0x48ed2b32,0x43e0223c,0x5ef7392e,0x55fa3020, - 0x01b79aec,0x0aba93e2,0x17ad88f0,0x1ca081fe, - 0x2d83bed4,0x268eb7da,0x3b99acc8,0x3094a5c6, - 0x59dfd29c,0x52d2db92,0x4fc5c080,0x44c8c98e, - 0x75ebf6a4,0x7ee6ffaa,0x63f1e4b8,0x68fcedb6, - 0xb1670a0c,0xba6a0302,0xa77d1810,0xac70111e, - 0x9d532e34,0x965e273a,0x8b493c28,0x80443526, - 0xe90f427c,0xe2024b72,0xff155060,0xf418596e, - 0xc53b6644,0xce366f4a,0xd3217458,0xd82c7d56, - 0x7a0ca137,0x7101a839,0x6c16b32b,0x671bba25, - 0x5638850f,0x5d358c01,0x40229713,0x4b2f9e1d, - 0x2264e947,0x2969e049,0x347efb5b,0x3f73f255, - 0x0e50cd7f,0x055dc471,0x184adf63,0x1347d66d, - 0xcadc31d7,0xc1d138d9,0xdcc623cb,0xd7cb2ac5, - 0xe6e815ef,0xede51ce1,0xf0f207f3,0xfbff0efd, - 0x92b479a7,0x99b970a9,0x84ae6bbb,0x8fa362b5, - 0xbe805d9f,0xb58d5491,0xa89a4f83,0xa397468d -]); - -var U2 = new Uint32Array([ - 0x00000000,0x0d090e0b,0x1a121c16,0x171b121d, - 0x3424382c,0x392d3627,0x2e36243a,0x233f2a31, - 0x68487058,0x65417e53,0x725a6c4e,0x7f536245, - 0x5c6c4874,0x5165467f,0x467e5462,0x4b775a69, - 0xd090e0b0,0xdd99eebb,0xca82fca6,0xc78bf2ad, - 0xe4b4d89c,0xe9bdd697,0xfea6c48a,0xf3afca81, - 0xb8d890e8,0xb5d19ee3,0xa2ca8cfe,0xafc382f5, - 0x8cfca8c4,0x81f5a6cf,0x96eeb4d2,0x9be7bad9, - 0xbb3bdb7b,0xb632d570,0xa129c76d,0xac20c966, - 0x8f1fe357,0x8216ed5c,0x950dff41,0x9804f14a, - 0xd373ab23,0xde7aa528,0xc961b735,0xc468b93e, - 0xe757930f,0xea5e9d04,0xfd458f19,0xf04c8112, - 0x6bab3bcb,0x66a235c0,0x71b927dd,0x7cb029d6, - 0x5f8f03e7,0x52860dec,0x459d1ff1,0x489411fa, - 0x03e34b93,0x0eea4598,0x19f15785,0x14f8598e, - 0x37c773bf,0x3ace7db4,0x2dd56fa9,0x20dc61a2, - 0x6d76adf6,0x607fa3fd,0x7764b1e0,0x7a6dbfeb, - 0x595295da,0x545b9bd1,0x434089cc,0x4e4987c7, - 0x053eddae,0x0837d3a5,0x1f2cc1b8,0x1225cfb3, - 0x311ae582,0x3c13eb89,0x2b08f994,0x2601f79f, - 0xbde64d46,0xb0ef434d,0xa7f45150,0xaafd5f5b, - 0x89c2756a,0x84cb7b61,0x93d0697c,0x9ed96777, - 0xd5ae3d1e,0xd8a73315,0xcfbc2108,0xc2b52f03, - 0xe18a0532,0xec830b39,0xfb981924,0xf691172f, - 0xd64d768d,0xdb447886,0xcc5f6a9b,0xc1566490, - 0xe2694ea1,0xef6040aa,0xf87b52b7,0xf5725cbc, - 0xbe0506d5,0xb30c08de,0xa4171ac3,0xa91e14c8, - 0x8a213ef9,0x872830f2,0x903322ef,0x9d3a2ce4, - 0x06dd963d,0x0bd49836,0x1ccf8a2b,0x11c68420, - 0x32f9ae11,0x3ff0a01a,0x28ebb207,0x25e2bc0c, - 0x6e95e665,0x639ce86e,0x7487fa73,0x798ef478, - 0x5ab1de49,0x57b8d042,0x40a3c25f,0x4daacc54, - 0xdaec41f7,0xd7e54ffc,0xc0fe5de1,0xcdf753ea, - 0xeec879db,0xe3c177d0,0xf4da65cd,0xf9d36bc6, - 0xb2a431af,0xbfad3fa4,0xa8b62db9,0xa5bf23b2, - 0x86800983,0x8b890788,0x9c921595,0x919b1b9e, - 0x0a7ca147,0x0775af4c,0x106ebd51,0x1d67b35a, - 0x3e58996b,0x33519760,0x244a857d,0x29438b76, - 0x6234d11f,0x6f3ddf14,0x7826cd09,0x752fc302, - 0x5610e933,0x5b19e738,0x4c02f525,0x410bfb2e, - 0x61d79a8c,0x6cde9487,0x7bc5869a,0x76cc8891, - 0x55f3a2a0,0x58faacab,0x4fe1beb6,0x42e8b0bd, - 0x099fead4,0x0496e4df,0x138df6c2,0x1e84f8c9, - 0x3dbbd2f8,0x30b2dcf3,0x27a9ceee,0x2aa0c0e5, - 0xb1477a3c,0xbc4e7437,0xab55662a,0xa65c6821, - 0x85634210,0x886a4c1b,0x9f715e06,0x9278500d, - 0xd90f0a64,0xd406046f,0xc31d1672,0xce141879, - 0xed2b3248,0xe0223c43,0xf7392e5e,0xfa302055, - 0xb79aec01,0xba93e20a,0xad88f017,0xa081fe1c, - 0x83bed42d,0x8eb7da26,0x99acc83b,0x94a5c630, - 0xdfd29c59,0xd2db9252,0xc5c0804f,0xc8c98e44, - 0xebf6a475,0xe6ffaa7e,0xf1e4b863,0xfcedb668, - 0x670a0cb1,0x6a0302ba,0x7d1810a7,0x70111eac, - 0x532e349d,0x5e273a96,0x493c288b,0x44352680, - 0x0f427ce9,0x024b72e2,0x155060ff,0x18596ef4, - 0x3b6644c5,0x366f4ace,0x217458d3,0x2c7d56d8, - 0x0ca1377a,0x01a83971,0x16b32b6c,0x1bba2567, - 0x38850f56,0x358c015d,0x22971340,0x2f9e1d4b, - 0x64e94722,0x69e04929,0x7efb5b34,0x73f2553f, - 0x50cd7f0e,0x5dc47105,0x4adf6318,0x47d66d13, - 0xdc31d7ca,0xd138d9c1,0xc623cbdc,0xcb2ac5d7, - 0xe815efe6,0xe51ce1ed,0xf207f3f0,0xff0efdfb, - 0xb479a792,0xb970a999,0xae6bbb84,0xa362b58f, - 0x805d9fbe,0x8d5491b5,0x9a4f83a8,0x97468da3 -]); - -var U3 = new Uint32Array([ - 0x00000000,0x090e0b0d,0x121c161a,0x1b121d17, - 0x24382c34,0x2d362739,0x36243a2e,0x3f2a3123, - 0x48705868,0x417e5365,0x5a6c4e72,0x5362457f, - 0x6c48745c,0x65467f51,0x7e546246,0x775a694b, - 0x90e0b0d0,0x99eebbdd,0x82fca6ca,0x8bf2adc7, - 0xb4d89ce4,0xbdd697e9,0xa6c48afe,0xafca81f3, - 0xd890e8b8,0xd19ee3b5,0xca8cfea2,0xc382f5af, - 0xfca8c48c,0xf5a6cf81,0xeeb4d296,0xe7bad99b, - 0x3bdb7bbb,0x32d570b6,0x29c76da1,0x20c966ac, - 0x1fe3578f,0x16ed5c82,0x0dff4195,0x04f14a98, - 0x73ab23d3,0x7aa528de,0x61b735c9,0x68b93ec4, - 0x57930fe7,0x5e9d04ea,0x458f19fd,0x4c8112f0, - 0xab3bcb6b,0xa235c066,0xb927dd71,0xb029d67c, - 0x8f03e75f,0x860dec52,0x9d1ff145,0x9411fa48, - 0xe34b9303,0xea45980e,0xf1578519,0xf8598e14, - 0xc773bf37,0xce7db43a,0xd56fa92d,0xdc61a220, - 0x76adf66d,0x7fa3fd60,0x64b1e077,0x6dbfeb7a, - 0x5295da59,0x5b9bd154,0x4089cc43,0x4987c74e, - 0x3eddae05,0x37d3a508,0x2cc1b81f,0x25cfb312, - 0x1ae58231,0x13eb893c,0x08f9942b,0x01f79f26, - 0xe64d46bd,0xef434db0,0xf45150a7,0xfd5f5baa, - 0xc2756a89,0xcb7b6184,0xd0697c93,0xd967779e, - 0xae3d1ed5,0xa73315d8,0xbc2108cf,0xb52f03c2, - 0x8a0532e1,0x830b39ec,0x981924fb,0x91172ff6, - 0x4d768dd6,0x447886db,0x5f6a9bcc,0x566490c1, - 0x694ea1e2,0x6040aaef,0x7b52b7f8,0x725cbcf5, - 0x0506d5be,0x0c08deb3,0x171ac3a4,0x1e14c8a9, - 0x213ef98a,0x2830f287,0x3322ef90,0x3a2ce49d, - 0xdd963d06,0xd498360b,0xcf8a2b1c,0xc6842011, - 0xf9ae1132,0xf0a01a3f,0xebb20728,0xe2bc0c25, - 0x95e6656e,0x9ce86e63,0x87fa7374,0x8ef47879, - 0xb1de495a,0xb8d04257,0xa3c25f40,0xaacc544d, - 0xec41f7da,0xe54ffcd7,0xfe5de1c0,0xf753eacd, - 0xc879dbee,0xc177d0e3,0xda65cdf4,0xd36bc6f9, - 0xa431afb2,0xad3fa4bf,0xb62db9a8,0xbf23b2a5, - 0x80098386,0x8907888b,0x9215959c,0x9b1b9e91, - 0x7ca1470a,0x75af4c07,0x6ebd5110,0x67b35a1d, - 0x58996b3e,0x51976033,0x4a857d24,0x438b7629, - 0x34d11f62,0x3ddf146f,0x26cd0978,0x2fc30275, - 0x10e93356,0x19e7385b,0x02f5254c,0x0bfb2e41, - 0xd79a8c61,0xde94876c,0xc5869a7b,0xcc889176, - 0xf3a2a055,0xfaacab58,0xe1beb64f,0xe8b0bd42, - 0x9fead409,0x96e4df04,0x8df6c213,0x84f8c91e, - 0xbbd2f83d,0xb2dcf330,0xa9ceee27,0xa0c0e52a, - 0x477a3cb1,0x4e7437bc,0x55662aab,0x5c6821a6, - 0x63421085,0x6a4c1b88,0x715e069f,0x78500d92, - 0x0f0a64d9,0x06046fd4,0x1d1672c3,0x141879ce, - 0x2b3248ed,0x223c43e0,0x392e5ef7,0x302055fa, - 0x9aec01b7,0x93e20aba,0x88f017ad,0x81fe1ca0, - 0xbed42d83,0xb7da268e,0xacc83b99,0xa5c63094, - 0xd29c59df,0xdb9252d2,0xc0804fc5,0xc98e44c8, - 0xf6a475eb,0xffaa7ee6,0xe4b863f1,0xedb668fc, - 0x0a0cb167,0x0302ba6a,0x1810a77d,0x111eac70, - 0x2e349d53,0x273a965e,0x3c288b49,0x35268044, - 0x427ce90f,0x4b72e202,0x5060ff15,0x596ef418, - 0x6644c53b,0x6f4ace36,0x7458d321,0x7d56d82c, - 0xa1377a0c,0xa8397101,0xb32b6c16,0xba25671b, - 0x850f5638,0x8c015d35,0x97134022,0x9e1d4b2f, - 0xe9472264,0xe0492969,0xfb5b347e,0xf2553f73, - 0xcd7f0e50,0xc471055d,0xdf63184a,0xd66d1347, - 0x31d7cadc,0x38d9c1d1,0x23cbdcc6,0x2ac5d7cb, - 0x15efe6e8,0x1ce1ede5,0x07f3f0f2,0x0efdfbff, - 0x79a792b4,0x70a999b9,0x6bbb84ae,0x62b58fa3, - 0x5d9fbe80,0x5491b58d,0x4f83a89a,0x468da397 -]); - -var U4 = new Uint32Array([ - 0x00000000,0x0e0b0d09,0x1c161a12,0x121d171b, - 0x382c3424,0x3627392d,0x243a2e36,0x2a31233f, - 0x70586848,0x7e536541,0x6c4e725a,0x62457f53, - 0x48745c6c,0x467f5165,0x5462467e,0x5a694b77, - 0xe0b0d090,0xeebbdd99,0xfca6ca82,0xf2adc78b, - 0xd89ce4b4,0xd697e9bd,0xc48afea6,0xca81f3af, - 0x90e8b8d8,0x9ee3b5d1,0x8cfea2ca,0x82f5afc3, - 0xa8c48cfc,0xa6cf81f5,0xb4d296ee,0xbad99be7, - 0xdb7bbb3b,0xd570b632,0xc76da129,0xc966ac20, - 0xe3578f1f,0xed5c8216,0xff41950d,0xf14a9804, - 0xab23d373,0xa528de7a,0xb735c961,0xb93ec468, - 0x930fe757,0x9d04ea5e,0x8f19fd45,0x8112f04c, - 0x3bcb6bab,0x35c066a2,0x27dd71b9,0x29d67cb0, - 0x03e75f8f,0x0dec5286,0x1ff1459d,0x11fa4894, - 0x4b9303e3,0x45980eea,0x578519f1,0x598e14f8, - 0x73bf37c7,0x7db43ace,0x6fa92dd5,0x61a220dc, - 0xadf66d76,0xa3fd607f,0xb1e07764,0xbfeb7a6d, - 0x95da5952,0x9bd1545b,0x89cc4340,0x87c74e49, - 0xddae053e,0xd3a50837,0xc1b81f2c,0xcfb31225, - 0xe582311a,0xeb893c13,0xf9942b08,0xf79f2601, - 0x4d46bde6,0x434db0ef,0x5150a7f4,0x5f5baafd, - 0x756a89c2,0x7b6184cb,0x697c93d0,0x67779ed9, - 0x3d1ed5ae,0x3315d8a7,0x2108cfbc,0x2f03c2b5, - 0x0532e18a,0x0b39ec83,0x1924fb98,0x172ff691, - 0x768dd64d,0x7886db44,0x6a9bcc5f,0x6490c156, - 0x4ea1e269,0x40aaef60,0x52b7f87b,0x5cbcf572, - 0x06d5be05,0x08deb30c,0x1ac3a417,0x14c8a91e, - 0x3ef98a21,0x30f28728,0x22ef9033,0x2ce49d3a, - 0x963d06dd,0x98360bd4,0x8a2b1ccf,0x842011c6, - 0xae1132f9,0xa01a3ff0,0xb20728eb,0xbc0c25e2, - 0xe6656e95,0xe86e639c,0xfa737487,0xf478798e, - 0xde495ab1,0xd04257b8,0xc25f40a3,0xcc544daa, - 0x41f7daec,0x4ffcd7e5,0x5de1c0fe,0x53eacdf7, - 0x79dbeec8,0x77d0e3c1,0x65cdf4da,0x6bc6f9d3, - 0x31afb2a4,0x3fa4bfad,0x2db9a8b6,0x23b2a5bf, - 0x09838680,0x07888b89,0x15959c92,0x1b9e919b, - 0xa1470a7c,0xaf4c0775,0xbd51106e,0xb35a1d67, - 0x996b3e58,0x97603351,0x857d244a,0x8b762943, - 0xd11f6234,0xdf146f3d,0xcd097826,0xc302752f, - 0xe9335610,0xe7385b19,0xf5254c02,0xfb2e410b, - 0x9a8c61d7,0x94876cde,0x869a7bc5,0x889176cc, - 0xa2a055f3,0xacab58fa,0xbeb64fe1,0xb0bd42e8, - 0xead4099f,0xe4df0496,0xf6c2138d,0xf8c91e84, - 0xd2f83dbb,0xdcf330b2,0xceee27a9,0xc0e52aa0, - 0x7a3cb147,0x7437bc4e,0x662aab55,0x6821a65c, - 0x42108563,0x4c1b886a,0x5e069f71,0x500d9278, - 0x0a64d90f,0x046fd406,0x1672c31d,0x1879ce14, - 0x3248ed2b,0x3c43e022,0x2e5ef739,0x2055fa30, - 0xec01b79a,0xe20aba93,0xf017ad88,0xfe1ca081, - 0xd42d83be,0xda268eb7,0xc83b99ac,0xc63094a5, - 0x9c59dfd2,0x9252d2db,0x804fc5c0,0x8e44c8c9, - 0xa475ebf6,0xaa7ee6ff,0xb863f1e4,0xb668fced, - 0x0cb1670a,0x02ba6a03,0x10a77d18,0x1eac7011, - 0x349d532e,0x3a965e27,0x288b493c,0x26804435, - 0x7ce90f42,0x72e2024b,0x60ff1550,0x6ef41859, - 0x44c53b66,0x4ace366f,0x58d32174,0x56d82c7d, - 0x377a0ca1,0x397101a8,0x2b6c16b3,0x25671bba, - 0x0f563885,0x015d358c,0x13402297,0x1d4b2f9e, - 0x472264e9,0x492969e0,0x5b347efb,0x553f73f2, - 0x7f0e50cd,0x71055dc4,0x63184adf,0x6d1347d6, - 0xd7cadc31,0xd9c1d138,0xcbdcc623,0xc5d7cb2a, - 0xefe6e815,0xe1ede51c,0xf3f0f207,0xfdfbff0e, - 0xa792b479,0xa999b970,0xbb84ae6b,0xb58fa362, - 0x9fbe805d,0x91b58d54,0x83a89a4f,0x8da39746 -]); - - -function B0(x) { - return (x & 255); -} - -function B1(x) { - return ((x >> 8) & 255); -} - -function B2(x) { - return ((x >> 16) & 255); -} - -function B3(x) { - return ((x >> 24) & 255); -} - -function F1(x0, x1, x2, x3) { - return B1(T1_enc[x0 & 255]) | (B1(T1_enc[(x1 >> 8) & 255]) << 8) | (B1(T1_enc[(x2 >> 16) & 255]) << 16) | (B1(T1_enc[x3 >>> 24]) << 24); -} - -function packBytes(octets) { - var i, j; - var len = octets.length; - var b = new Array(len / 4); - - if (!octets || len % 4) { - return; - } - - for (i = 0, j = 0; j < len; j += 4) { - b[i++] = octets[j] | (octets[j + 1] << 8) | (octets[j + 2] << 16) | (octets[j + 3] << 24); - } - - return b; -} - -function unpackBytes(packed) { - var j; - var i = 0, - l = packed.length; - var r = new Array(l * 4); - - for (j = 0; j < l; j++) { - r[i++] = B0(packed[j]); - r[i++] = B1(packed[j]); - r[i++] = B2(packed[j]); - r[i++] = B3(packed[j]); - } - return r; -} - -// ------------------------------------------------ - -var maxkc = 8; -var maxrk = 14; - -function keyExpansionEnc(key) { - var kc, i, j, r, t; - var rounds; - var keySched = new Array(maxrk + 1); - var keylen = key.length; - var k = new Array(maxkc); - var tk = new Array(maxkc); - var rconpointer = 0; - - if (keylen === 16) { - rounds = 10; - kc = 4; - } else if (keylen === 24) { - rounds = 12; - kc = 6; - } else if (keylen === 32) { - rounds = 14; - kc = 8; - } else { - throw new Error('Invalid key-length for AES key:' + keylen); - } - - for (i = 0; i < maxrk + 1; i++) { - keySched[i] = new Uint32Array(4); - } - - for (i = 0, j = 0; j < keylen; j++, i += 4) { - k[j] = key[i] | (key[i + 1] << 8) | (key[i + 2] << 16) | (key[i + 3] << 24); - } - - for (j = kc - 1; j >= 0; j--) { - tk[j] = k[j]; - } - - r = 0; - t = 0; - for (j = 0; (j < kc) && (r < rounds + 1);) { - for (; (j < kc) && (t < 4); j++, t++) { - keySched[r][t] = tk[j]; - } - if (t === 4) { - r++; - t = 0; - } - } - - while (r < rounds + 1) { - var temp = tk[kc - 1]; - - tk[0] ^= S[B1(temp)] | (S[B2(temp)] << 8) | (S[B3(temp)] << 16) | (S[B0(temp)] << 24); - tk[0] ^= Rcon[rconpointer++]; - - if (kc !== 8) { - for (j = 1; j < kc; j++) { - tk[j] ^= tk[j - 1]; - } - } else { - for (j = 1; j < kc / 2; j++) { - tk[j] ^= tk[j - 1]; - } - - temp = tk[kc / 2 - 1]; - tk[kc / 2] ^= S[B0(temp)] | (S[B1(temp)] << 8) | (S[B2(temp)] << 16) | (S[B3(temp)] << 24); - - for (j = kc / 2 + 1; j < kc; j++) { - tk[j] ^= tk[j - 1]; - } - } - - for (j = 0; (j < kc) && (r < rounds + 1);) { - for (; (j < kc) && (t < 4); j++, t++) { - keySched[r][t] = tk[j]; - } - if (t === 4) { - r++; - t = 0; - } - } - } - - return { - rounds: rounds, - rk: keySched - }; -} - -function keyExpansionDec(ctx) { - var tmp; - var keySched = new Array(ctx.length); - - var rounds = ctx.rounds; - - for(var r = 0; r < maxrk + 1; r++) { - keySched[r] = new Uint32Array(4); - keySched[r][0] = ctx.rk[r][0]; - keySched[r][1] = ctx.rk[r][1]; - keySched[r][2] = ctx.rk[r][2]; - keySched[r][3] = ctx.rk[r][3]; - } - - for(r = 1; r < rounds; r++) { - tmp=keySched[r][0]; keySched[r][0] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; - tmp=keySched[r][1]; keySched[r][1] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; - tmp=keySched[r][2]; keySched[r][2] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; - tmp=keySched[r][3]; keySched[r][3] = U1[B0(tmp)] ^ U2[B1(tmp)] ^ U3[B2(tmp)] ^ U4[B3(tmp)]; - } - return { - rounds : rounds, - rk : keySched - }; -} - -function AESencrypt(block, ctx, t) { - var r, rounds, b; - - b = packBytes(block); - rounds = ctx.rounds; - - for (r = 0; r < rounds - 1; r++) { - t[0] = b[0] ^ ctx.rk[r][0]; - t[1] = b[1] ^ ctx.rk[r][1]; - t[2] = b[2] ^ ctx.rk[r][2]; - t[3] = b[3] ^ ctx.rk[r][3]; - - b[0] = T1_enc[t[0] & 255] ^ T2_enc[(t[1] >> 8) & 255] ^ T3_enc[(t[2] >> 16) & 255] ^ T4_enc[t[3] >>> 24]; - b[1] = T1_enc[t[1] & 255] ^ T2_enc[(t[2] >> 8) & 255] ^ T3_enc[(t[3] >> 16) & 255] ^ T4_enc[t[0] >>> 24]; - b[2] = T1_enc[t[2] & 255] ^ T2_enc[(t[3] >> 8) & 255] ^ T3_enc[(t[0] >> 16) & 255] ^ T4_enc[t[1] >>> 24]; - b[3] = T1_enc[t[3] & 255] ^ T2_enc[(t[0] >> 8) & 255] ^ T3_enc[(t[1] >> 16) & 255] ^ T4_enc[t[2] >>> 24]; - } - - // last round is special - r = rounds - 1; - - t[0] = b[0] ^ ctx.rk[r][0]; - t[1] = b[1] ^ ctx.rk[r][1]; - t[2] = b[2] ^ ctx.rk[r][2]; - t[3] = b[3] ^ ctx.rk[r][3]; - - b[0] = F1(t[0], t[1], t[2], t[3]) ^ ctx.rk[rounds][0]; - b[1] = F1(t[1], t[2], t[3], t[0]) ^ ctx.rk[rounds][1]; - b[2] = F1(t[2], t[3], t[0], t[1]) ^ ctx.rk[rounds][2]; - b[3] = F1(t[3], t[0], t[1], t[2]) ^ ctx.rk[rounds][3]; - - return unpackBytes(b); -} - -function AESdecrypt(block, ctx, t) { - var r, rounds, b; - - b = packBytes(block); - rounds = ctx.rounds; - - for (r = rounds; r > 1; r--) { - t[0] = b[0] ^ ctx.rk[r][0]; - t[1] = b[1] ^ ctx.rk[r][1]; - t[2] = b[2] ^ ctx.rk[r][2]; - t[3] = b[3] ^ ctx.rk[r][3]; - - b[0] = T1_dec[B0(t[0])] ^ T2_dec[B1(t[3])] ^ T3_dec[B2(t[2])] ^ T4_dec[B3(t[1])]; - b[1] = T1_dec[B0(t[1])] ^ T2_dec[B1(t[0])] ^ T3_dec[B2(t[3])] ^ T4_dec[B3(t[2])]; - b[2] = T1_dec[B0(t[2])] ^ T2_dec[B1(t[1])] ^ T3_dec[B2(t[0])] ^ T4_dec[B3(t[3])]; - b[3] = T1_dec[B0(t[3])] ^ T2_dec[B1(t[2])] ^ T3_dec[B2(t[1])] ^ T4_dec[B3(t[0])]; - } - - // last round is special - t[0] = b[0] ^ ctx.rk[1][0]; - t[1] = b[1] ^ ctx.rk[1][1]; - t[2] = b[2] ^ ctx.rk[1][2]; - t[3] = b[3] ^ ctx.rk[1][3]; - - b[0] = S_inv[B0(t[0])] | (S_inv[B1(t[3])]<<8) | (S_inv[B2(t[2])]<<16) | (S_inv[B3(t[1])]<<24); - b[1] = S_inv[B0(t[1])] | (S_inv[B1(t[0])]<<8) | (S_inv[B2(t[3])]<<16) | (S_inv[B3(t[2])]<<24); - b[2] = S_inv[B0(t[2])] | (S_inv[B1(t[1])]<<8) | (S_inv[B2(t[0])]<<16) | (S_inv[B3(t[3])]<<24); - b[3] = S_inv[B0(t[3])] | (S_inv[B1(t[2])]<<8) | (S_inv[B2(t[1])]<<16) | (S_inv[B3(t[0])]<<24); - - b[0] ^= ctx.rk[0][0]; - b[1] ^= ctx.rk[0][1]; - b[2] ^= ctx.rk[0][2]; - b[3] ^= ctx.rk[0][3]; - - return unpackBytes(b); -} - -function makeClass(length) { +export default function aes(length) { var c = function(key) { - this.enc_key = keyExpansionEnc(key); - this.dec_key = keyExpansionDec(this.enc_key); + this.key = Uint8Array.from(key); this.encrypt = function(block) { - return AESencrypt(block, this.enc_key, new Uint32Array(this.blockSize / 4)); + block = Uint8Array.from(block); + return Array.from(asmCrypto.AES_ECB.encrypt(block, this.key, false)); }; this.decrypt = function(block) { - return AESdecrypt(block, this.dec_key, new Uint32Array(this.blockSize / 4)); + block = Uint8Array.from(block); + return Array.from(asmCrypto.AES_ECB.decrypt(block, this.key, false)); }; }; @@ -1133,9 +27,3 @@ function makeClass(length) { return c; } - -export default { - 128: makeClass(128), - 192: makeClass(192), - 256: makeClass(256) -}; diff --git a/src/crypto/cipher/index.js b/src/crypto/cipher/index.js index ec09835a..3f1274a6 100644 --- a/src/crypto/cipher/index.js +++ b/src/crypto/cipher/index.js @@ -8,7 +8,7 @@ 'use strict'; -import aes from'./aes.js'; +import aes from './aes.js'; import desModule from './des.js'; import cast5 from './cast5.js'; import twofish from './twofish.js'; @@ -16,9 +16,9 @@ import blowfish from './blowfish.js'; export default { /** @see module:crypto/cipher/aes */ - aes128: aes[128], - aes192: aes[192], - aes256: aes[256], + aes128: aes(128), + aes192: aes(192), + aes256: aes(256), /** @see module:crypto/cipher/des.originalDes */ des: desModule.originalDes, /** @see module:crypto/cipher/des.des */ @@ -33,4 +33,4 @@ export default { idea: function() { throw new Error('IDEA symmetric-key algorithm not implemented'); } -}; \ No newline at end of file +}; diff --git a/src/crypto/hash/index.js b/src/crypto/hash/index.js index ca246ffc..98a22b61 100644 --- a/src/crypto/hash/index.js +++ b/src/crypto/hash/index.js @@ -55,6 +55,7 @@ if(nodeCrypto) { // Use Node native crypto for all hash functions sha256: asmCrypto.SHA256.bytes, /** @see module:crypto/hash/sha.sha384 */ sha384: sha.sha384, + // TODO: compare sha512 in asmcrypto.js and jsSHA /** @see module:crypto/hash/sha.sha512 */ sha512: sha.sha512, /** @see module:crypto/hash/ripe-md */ diff --git a/src/crypto/hash/sha.js b/src/crypto/hash/sha.js index 921784c4..1f3f851f 100644 --- a/src/crypto/hash/sha.js +++ b/src/crypto/hash/sha.js @@ -1594,7 +1594,6 @@ export default { sha384: function(str) { var shaObj = new jsSHA(str, "TYPED", "UTF8"); return shaObj.getHash("SHA-384", "TYPED"); - }, /** SHA512 hash */ sha512: function(str) { diff --git a/test/general/x25519.js b/test/general/x25519.js index 9f401103..f8aa8122 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -270,6 +270,7 @@ describe('X25519 Cryptography', function () { var msg = openpgp.cleartext.readArmored(signed.data); // Verifying signed message return Promise.all([ + // FIXME this test sporadically fails openpgp.verify( { message: msg, publicKeys: hi.toPublic() } ).then(output => expect(output.signatures[0].valid).to.be.true), From d97bc064eaaf8a9af6c320d92873991045b31d67 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 31 Jan 2018 04:20:21 -0800 Subject: [PATCH 52/75] Cleanups and doc fixes --- src/cleartext.js | 30 +++++------ src/crypto/gcm.js | 12 +++-- src/crypto/index.js | 2 +- src/crypto/public_key/elliptic/curves.js | 4 +- src/crypto/public_key/elliptic/index.js | 4 +- src/key.js | 20 ++++---- src/keyring/keyring.js | 10 ++-- src/keyring/localstore.js | 6 ++- src/message.js | 43 ++++++++-------- src/openpgp.js | 65 +++++++++++------------- src/packet/clone.js | 24 ++++----- test/crypto/elliptic.js | 8 +-- 12 files changed, 115 insertions(+), 113 deletions(-) diff --git a/src/cleartext.js b/src/cleartext.js index e5909392..b90e25b7 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -20,16 +20,17 @@ * @requires encoding/armor * @requires enums * @requires packet + * @requires signature * @module cleartext */ 'use strict'; import config from './config'; -import packet from './packet'; -import enums from './enums.js'; import armor from './encoding/armor.js'; -import * as sigModule from './signature.js'; +import enums from './enums.js'; +import packet from './packet'; +import { Signature } from './signature.js'; /** * @class @@ -45,10 +46,10 @@ export function CleartextMessage(text, signature) { } // normalize EOL to canonical form this.text = text.replace(/\r/g, '').replace(/[\t ]+\n/g, "\n").replace(/\n/g,"\r\n"); - if (signature && !(signature instanceof sigModule.Signature)) { + if (signature && !(signature instanceof Signature)) { throw new Error('Invalid signature input'); } - this.signature = signature || new sigModule.Signature(new packet.List()); + this.signature = signature || new Signature(new packet.List()); } /** @@ -105,7 +106,7 @@ CleartextMessage.prototype.signDetached = async function(privateKeys) { signatureList.forEach(signaturePacket => packetlist.push(signaturePacket)); }); - return new sigModule.Signature(packetlist); + return new Signature(packetlist); }; /** @@ -131,24 +132,21 @@ CleartextMessage.prototype.verifyDetached = function(signature, keys) { var keyPacket = null; await Promise.all(keys.map(async function(key) { await key.verifyPrimaryUser(); + // Look for the unique key packet that matches issuerKeyId of signature var result = key.getSigningKeyPacket(signature.issuerKeyId, config.verify_expired_keys); if (result) { keyPacket = result; } })); - var verifiedSig = {}; - if (keyPacket) { - verifiedSig.keyid = signature.issuerKeyId; - verifiedSig.valid = await signature.verify(keyPacket, literalDataPacket); - } else { - verifiedSig.keyid = signature.issuerKeyId; - verifiedSig.valid = null; - } + var verifiedSig = { + keyid: signature.issuerKeyId, + valid: keyPacket ? await signature.verify(keyPacket, literalDataPacket) : null + }; var packetlist = new packet.List(); packetlist.push(signature); - verifiedSig.signature = new sigModule.Signature(packetlist); + verifiedSig.signature = new Signature(packetlist); return verifiedSig; })); @@ -191,7 +189,7 @@ export function readArmored(armoredText) { var packetlist = new packet.List(); packetlist.read(input.data); verifyHeaders(input.headers, packetlist); - var signature = new sigModule.Signature(packetlist); + var signature = new Signature(packetlist); var newMessage = new CleartextMessage(input.text, signature); return newMessage; } diff --git a/src/crypto/gcm.js b/src/crypto/gcm.js index e07f2ebf..39786cc8 100644 --- a/src/crypto/gcm.js +++ b/src/crypto/gcm.js @@ -30,7 +30,7 @@ const webCrypto = util.getWebCrypto(); // no GCM support in IE11, Safari 9 const nodeCrypto = util.getNodeCrypto(); const Buffer = util.getNodeBuffer(); -export const ivLength = 12; // size of the IV in bytes +const ivLength = 12; // size of the IV in bytes const TAG_LEN = 16; // size of the tag in bytes const ALGO = 'AES-GCM'; @@ -42,7 +42,7 @@ const ALGO = 'AES-GCM'; * @param {Uint8Array} iv The initialization vector (12 bytes) * @return {Promise} The ciphertext output */ -export function encrypt(cipher, plaintext, key, iv) { +function encrypt(cipher, plaintext, key, iv) { if (cipher.substr(0,3) !== 'aes') { return Promise.reject(new Error('GCM mode supports only AES cipher')); } @@ -64,7 +64,7 @@ export function encrypt(cipher, plaintext, key, iv) { * @param {Uint8Array} iv The initialization vector (12 bytes) * @return {Promise} The plaintext output */ -export function decrypt(cipher, ciphertext, key, iv) { +function decrypt(cipher, ciphertext, key, iv) { if (cipher.substr(0,3) !== 'aes') { return Promise.reject(new Error('GCM mode supports only AES cipher')); } @@ -78,6 +78,12 @@ export function decrypt(cipher, ciphertext, key, iv) { } } +export default { + ivLength, + encrypt, + decrypt +}; + ////////////////////////// // // diff --git a/src/crypto/index.js b/src/crypto/index.js index 51ce77d6..c1530db2 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -8,7 +8,7 @@ import cipher from './cipher'; import hash from './hash'; import cfb from './cfb'; -import * as gcm from './gcm'; +import gcm from './gcm'; import publicKey from './public_key'; import signature from './signature'; import random from './random'; diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 3fa52cf5..fa9fe764 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -193,7 +193,7 @@ async function generate(curve) { }; } -function getPreferredHashAlgorithm(oid) { +function getPreferredHashAlgo(oid) { return curves[enums.write(enums.curve, oid.toHex())].hash; } @@ -202,7 +202,7 @@ module.exports = { curves: curves, webCurves: webCurves, nodeCurves: nodeCurves, - getPreferredHashAlgorithm: getPreferredHashAlgorithm, + getPreferredHashAlgo: getPreferredHashAlgo, generate: generate, get: get }; diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index a171c3cb..39108f0a 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -27,7 +27,7 @@ 'use strict'; -import {get, generate, getPreferredHashAlgorithm} from './curves'; +import {get, generate, getPreferredHashAlgo} from './curves'; import ecdsa from './ecdsa'; import eddsa from './eddsa'; import ecdh from './ecdh'; @@ -38,5 +38,5 @@ module.exports = { ecdh: ecdh, get: get, generate: generate, - getPreferredHashAlgorithm: getPreferredHashAlgorithm + getPreferredHashAlgo: getPreferredHashAlgo }; diff --git a/src/key.js b/src/key.js index 787f4a83..c79daa7d 100644 --- a/src/key.js +++ b/src/key.js @@ -17,20 +17,22 @@ /** * @requires config + * @requires crypto * @requires encoding/armor * @requires enums + * @requires util * @requires packet * @module key */ 'use strict'; -import packet from './packet'; -import enums from './enums.js'; -import armor from './encoding/armor.js'; import config from './config'; -import util from './util'; import crypto from './crypto'; +import armor from './encoding/armor.js'; +import enums from './enums.js'; +import util from './util'; +import packet from './packet'; /** * @class @@ -824,7 +826,7 @@ User.prototype.sign = async function(primaryKey, privateKeys) { signaturePacket.signatureType = enums.write(enums.signature, enums.signature.cert_generic); signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; - signaturePacket.hashAlgorithm = getPreferredHashAlgorithm(privateKey); + signaturePacket.hashAlgorithm = getPreferredHashAlgo(privateKey); signaturePacket.signingKeyId = signingKeyPacket.getKeyId(); signaturePacket.sign(signingKeyPacket, dataToSign); return signaturePacket; @@ -1296,7 +1298,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { var signaturePacket = new packet.Signature(); signaturePacket.signatureType = enums.signature.cert_generic; signaturePacket.publicKeyAlgorithm = options.keyType; - signaturePacket.hashAlgorithm = getPreferredHashAlgorithm(secretKeyPacket); + signaturePacket.hashAlgorithm = getPreferredHashAlgo(secretKeyPacket); signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data]; signaturePacket.preferredSymmetricAlgorithms = []; // prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support) @@ -1340,7 +1342,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { var subkeySignaturePacket = new packet.Signature(); subkeySignaturePacket.signatureType = enums.signature.subkey_binding; subkeySignaturePacket.publicKeyAlgorithm = options.keyType; - subkeySignaturePacket.hashAlgorithm = getPreferredHashAlgorithm(secretSubkeyPacket); + subkeySignaturePacket.hashAlgorithm = getPreferredHashAlgo(secretSubkeyPacket); subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage]; if (options.keyExpirationTime > 0) { subkeySignaturePacket.keyExpirationTime = options.keyExpirationTime; @@ -1364,7 +1366,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { * @param {object} key * @return {String} */ -export function getPreferredHashAlgorithm(key) { +export function getPreferredHashAlgo(key) { var hash_algo = config.prefer_hash_algorithm, pref_algo = hash_algo; if (Key.prototype.isPrototypeOf(key)) { @@ -1385,7 +1387,7 @@ export function getPreferredHashAlgorithm(key) { case 'ecdh': case 'ecdsa': case 'eddsa': - pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgorithm(key.params[0]); + pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(key.params[0]); } } return crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ? diff --git a/src/keyring/keyring.js b/src/keyring/keyring.js index 11fd8d5e..c7b860ca 100644 --- a/src/keyring/keyring.js +++ b/src/keyring/keyring.js @@ -17,22 +17,22 @@ /** * The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage. - * @requires enums * @requires key - * @requires util + * @requires keyring/localstore * @module keyring/keyring + * @param {keyring/localstore} [storeHandler] class implementing loadPublic(), loadPrivate(), storePublic(), and storePrivate() methods */ 'use strict'; -import * as keyModule from '../key.js'; +import { readArmored } from '../key.js'; import LocalStore from './localstore.js'; /** * Initialization routine for the keyring. This method reads the * keyring from HTML5 local storage and initializes this instance. * @constructor - * @param {class} [storeHandler] class implementing loadPublic(), loadPrivate(), storePublic(), and storePrivate() methods + * @param {keyring/localstore} [storeHandler] class implementing loadPublic(), loadPrivate(), storePublic(), and storePrivate() methods */ export default function Keyring(storeHandler) { this.storeHandler = storeHandler || new LocalStore(); @@ -181,7 +181,7 @@ KeyArray.prototype.getForId = function (keyId, deep) { * @return {Array|null} array of error objects or null */ KeyArray.prototype.importKey = function (armored) { - var imported = keyModule.readArmored(armored); + var imported = readArmored(armored); var that = this; imported.keys.forEach(async function(key) { // check if key already in key array diff --git a/src/keyring/localstore.js b/src/keyring/localstore.js index b190359a..aaa84c7e 100644 --- a/src/keyring/localstore.js +++ b/src/keyring/localstore.js @@ -18,6 +18,8 @@ /** * The class that deals with storage of the keyring. Currently the only option is to use HTML5 local storage. * @requires config + * @requires key + * @requires util * @module keyring/localstore * @param {String} prefix prefix for itemnames in localstore */ @@ -25,7 +27,7 @@ 'use strict'; import config from '../config'; -import * as keyModule from '../key.js'; +import { readArmored } from '../key.js'; import util from '../util.js'; export default function LocalStore(prefix) { @@ -67,7 +69,7 @@ function loadKeys(storage, itemname) { if (armoredKeys !== null && armoredKeys.length !== 0) { var key; for (var i = 0; i < armoredKeys.length; i++) { - key = keyModule.readArmored(armoredKeys[i]); + key = readArmored(armoredKeys[i]); if (!key.err) { keys.push(key.keys[0]); } else { diff --git a/src/message.js b/src/message.js index 311dbcd9..4546981c 100644 --- a/src/message.js +++ b/src/message.js @@ -20,20 +20,24 @@ * @requires crypto * @requires encoding/armor * @requires enums + * @requires util * @requires packet + * @requires signature + * @requires key * @module message */ 'use strict'; -import util from './util.js'; -import packet from './packet'; -import enums from './enums.js'; -import armor from './encoding/armor.js'; import config from './config'; import crypto from './crypto'; -import * as sigModule from './signature.js'; -import * as keyModule from './key.js'; +import armor from './encoding/armor.js'; +import enums from './enums.js'; +import util from './util.js'; +import packet from './packet'; +import { Signature } from './signature.js'; +import { getPreferredHashAlgo, getPreferredSymAlgo } from './key.js'; + /** * @class @@ -228,7 +232,7 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey) { symAlgo = sessionKey.algorithm; sessionKey = sessionKey.data; } else if (keys && keys.length) { - symAlgo = enums.read(enums.symmetric, keyModule.getPreferredSymAlgo(keys)); + symAlgo = enums.read(enums.symmetric, getPreferredSymAlgo(keys)); } else if (passwords && passwords.length) { symAlgo = enums.read(enums.symmetric, config.encryption_cipher); } else { @@ -361,7 +365,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null) { onePassSig = new packet.OnePassSignature(); onePassSig.type = signatureType; //TODO get preferred hash algo from key signature - onePassSig.hashAlgorithm = keyModule.getPreferredHashAlgorithm(privateKey); + onePassSig.hashAlgorithm = getPreferredHashAlgo(privateKey); onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm; onePassSig.signingKeyId = signingKeyPacket.getKeyId(); if (i === privateKeys.length - 1) { @@ -381,7 +385,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null) { throw new Error('Private key is not decrypted.'); } signaturePacket.signatureType = signatureType; - signaturePacket.hashAlgorithm = keyModule.getPreferredHashAlgorithm(privateKey); + signaturePacket.hashAlgorithm = getPreferredHashAlgo(privateKey); signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; await signaturePacket.sign(signingKeyPacket, literalDataPacket); return signaturePacket; @@ -424,7 +428,7 @@ Message.prototype.signDetached = async function(privateKeys=[], signature=null) } signaturePacket.signatureType = signatureType; signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm; - signaturePacket.hashAlgorithm = keyModule.getPreferredHashAlgorithm(privateKey); + signaturePacket.hashAlgorithm = getPreferredHashAlgo(privateKey); await signaturePacket.sign(signingKeyPacket, literalDataPacket); return signaturePacket; })).then(signatureList => { @@ -436,7 +440,7 @@ Message.prototype.signDetached = async function(privateKeys=[], signature=null) packetlist.concat(existingSigPacketlist); } - return new sigModule.Signature(packetlist); + return new Signature(packetlist); }; @@ -483,25 +487,22 @@ async function createVerificationObjects(signatureList, literalDataList, keys) { var keyPacket = null; await Promise.all(keys.map(async function(key) { await key.verifyPrimaryUser(); + // Look for the unique key packet that matches issuerKeyId of signature var result = key.getSigningKeyPacket(signature.issuerKeyId, config.verify_expired_keys); if (result) { keyPacket = result; } })); - var verifiedSig = {}; - if (keyPacket) { - //found a key packet that matches keyId of signature - verifiedSig.keyid = signature.issuerKeyId; - verifiedSig.valid = await signature.verify(keyPacket, literalDataList[0]); - } else { - verifiedSig.keyid = signature.issuerKeyId; - verifiedSig.valid = null; - } + // Look for the unique key packet that matches issuerKeyId of signature + var verifiedSig = { + keyid: signature.issuerKeyId, + valid: keyPacket ? await signature.verify(keyPacket, literalDataList[0]) : null + }; var packetlist = new packet.List(); packetlist.push(signature); - verifiedSig.signature = new sigModule.Signature(packetlist); + verifiedSig.signature = new Signature(packetlist); return verifiedSig; })); diff --git a/src/openpgp.js b/src/openpgp.js index 7540ee0e..6a6daa31 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -16,11 +16,13 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** + * @requires ed6-promise * @requires message * @requires cleartext * @requires key * @requires config * @requires util + * @requires worker/async_proxy * @module openpgp */ @@ -35,8 +37,8 @@ import es6Promise from 'es6-promise'; import * as messageLib from './message.js'; -import * as cleartext from './cleartext.js'; -import * as key from './key.js'; +import { CleartextMessage } from './cleartext.js'; +import { generate, reformat } from './key.js'; import config from './config/config.js'; import util from './util'; import AsyncProxy from './worker/async_proxy.js'; @@ -109,11 +111,11 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal return asyncProxy.delegate('generateKey', options); } - return key.generate(options).then(newKey => ({ + return generate(options).then(key => ({ - key: newKey, - privateKeyArmored: newKey.armor(), - publicKeyArmored: newKey.toPublic().armor() + key: key, + privateKeyArmored: key.armor(), + publicKeyArmored: key.toPublic().armor() })).catch(onError.bind(null, 'Error generating keypair')); } @@ -137,11 +139,11 @@ export function reformatKey({ privateKey, userIds=[], passphrase="", unlocked=fa return asyncProxy.delegate('reformatKey', options); } - return key.reformat(options).then(newKey => ({ + return reformat(options).then(key => ({ - key: newKey, - privateKeyArmored: newKey.armor(), - publicKeyArmored: newKey.toPublic().armor() + key: key, + privateKeyArmored: key.armor(), + publicKeyArmored: key.toPublic().armor() })).catch(onError.bind(null, 'Error reformatting keypair')); } @@ -157,16 +159,15 @@ export function decryptKey({ privateKey, passphrase }) { return asyncProxy.delegate('decryptKey', { privateKey, passphrase }); } - return execute(() => { + return Promise.resolve().then(async function() { + + await privateKey.decrypt(passphrase); - if (!privateKey.decrypt(passphrase)) { - throw new Error('Invalid passphrase'); - } return { key: privateKey }; - }, 'Error decrypting private key'); + }).catch(onError.bind(null, 'Error decrypting private key')); } @@ -308,7 +309,7 @@ export function sign({ data, privateKeys, armor=true, detached=false}) { var message; if (util.isString(data)) { - message = new cleartext.CleartextMessage(data); + message = new CleartextMessage(data); } else { message = messageLib.fromBinary(data); } @@ -353,7 +354,7 @@ export function verify({ message, publicKeys, signature=null }) { return Promise.resolve().then(async function() { var result = {}; - if (cleartext.CleartextMessage.prototype.isPrototypeOf(message)) { + if (CleartextMessage.prototype.isPrototypeOf(message)) { result.data = message.getText(); } else { result.data = message.getLiteralData(); @@ -395,9 +396,11 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords }) { return asyncProxy.delegate('encryptSessionKey', { data, algorithm, publicKeys, passwords }); } - return execute(async () => ({ - message: await messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords) - }), 'Error encrypting session key'); + return Promise.resolve().then(async function() { + + return { message: await messageLib.encryptSessionKey(data, algorithm, publicKeys, passwords) }; + + }).catch(onError.bind(null, 'Error encrypting session key')); } /** @@ -418,7 +421,11 @@ export function decryptSessionKey({ message, privateKey, password }) { return asyncProxy.delegate('decryptSessionKey', { message, privateKey, password }); } - return execute(() => message.decryptSessionKey(privateKey, password), 'Error decrypting session key'); + return Promise.resolve().then(async function() { + + return message.decryptSessionKey(privateKey, password); + + }).catch(onError.bind(null, 'Error decrypting session key')); } @@ -453,7 +460,7 @@ function checkMessage(message) { } } function checkCleartextOrMessage(message) { - if (!cleartext.CleartextMessage.prototype.isPrototypeOf(message) && !messageLib.Message.prototype.isPrototypeOf(message)) { + if (!CleartextMessage.prototype.isPrototypeOf(message) && !messageLib.Message.prototype.isPrototypeOf(message)) { throw new Error('Parameter [message] needs to be of type Message or CleartextMessage'); } } @@ -540,20 +547,6 @@ function parseMessage(message, format) { } } -/** - * 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} message A human readable error Message - * @return {Promise} The promise wrapped around cmd - */ -function execute(cmd, message) { - // wrap the sync cmd in a promise - const promise = new Promise(resolve => resolve(cmd())); - // handler error globally - return promise.catch(onError.bind(null, message)); -} - /** * 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 diff --git a/src/packet/clone.js b/src/packet/clone.js index c61b0f0a..07e0a632 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -23,10 +23,10 @@ 'use strict'; -import * as key from '../key.js'; -import * as message from '../message.js'; -import * as cleartext from '../cleartext.js'; -import * as signature from '../signature.js' +import { Key } from '../key.js'; +import { Message } from '../message.js'; +import { CleartextMessage } from '../cleartext.js'; +import { Signature } from '../signature.js' import Packetlist from './packetlist.js'; import type_keyid from '../type/keyid.js'; @@ -58,13 +58,13 @@ export function clonePackets(options) { } if (options.message) { //could be either a Message or CleartextMessage object - if (options.message instanceof message.Message) { + if (options.message instanceof Message) { options.message = options.message.packets; - } else if (options.message instanceof cleartext.CleartextMessage) { + } else if (options.message instanceof CleartextMessage) { options.message.signature = options.message.signature.packets; } } - if (options.signature && (options.signature instanceof signature.Signature)) { + if (options.signature && (options.signature instanceof Signature)) { options.signature = options.signature.packets; } if (options.signatures) { @@ -120,23 +120,23 @@ export function parseClonedPackets(options, method) { function packetlistCloneToKey(clone) { const packetlist = Packetlist.fromStructuredClone(clone); - return new key.Key(packetlist); + return new Key(packetlist); } function packetlistCloneToMessage(clone) { const packetlist = Packetlist.fromStructuredClone(clone); - return new message.Message(packetlist); + return new Message(packetlist); } function packetlistCloneToCleartextMessage(clone) { var packetlist = Packetlist.fromStructuredClone(clone.signature); - return new cleartext.CleartextMessage(clone.text, new signature.Signature(packetlist)); + return new CleartextMessage(clone.text, new Signature(packetlist)); } //verification objects function packetlistCloneToSignatures(clone) { clone.keyid = type_keyid.fromClone(clone.keyid); - clone.signature = new signature.Signature(clone.signature); + clone.signature = new Signature(clone.signature); return clone; } @@ -146,5 +146,5 @@ function packetlistCloneToSignature(clone) { return clone; } var packetlist = Packetlist.fromStructuredClone(clone); - return new signature.Signature(packetlist); + return new Signature(packetlist); } diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 48a84fc5..803afd1d 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -286,16 +286,16 @@ describe('Elliptic Curve Cryptography', function () { expect(verify_signature('p384', 8, p384_r, p384_s, p384_message, key_data.p384.pub)) .to.eventually.be.true.notify(done); }); - it('Sign and verify message', function (done) { + it('Sign and verify message', function () { var curve = elliptic_curves.get('p521'); - curve.genKeyPair().then(keyPair => { + return curve.genKeyPair().then(keyPair => { var keyPublic = bin2bi(keyPair.getPublic()); var keyPrivate = bin2bi(keyPair.getPrivate()); var oid = curve.oid; var message = p384_message; - elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => { + return elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => { expect(elliptic_curves.ecdsa.verify(oid, 10, signature, message, keyPublic)) - .to.eventually.be.true.notify(done); + .to.eventually.be.true; }); }); }); From a4134b9f55dc7f52db603cb90021b609c7d46c73 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 31 Jan 2018 17:47:33 -0800 Subject: [PATCH 53/75] Addresses various review comments by @bartbutler + some cleanups --- Gruntfile.js | 10 ++++++---- package.json | 2 +- src/cleartext.js | 6 +++--- src/crypto/cipher/aes.js | 1 + src/crypto/crypto.js | 4 +++- src/crypto/public_key/elliptic/eddsa.js | 19 ++++++++++++++++++- src/crypto/public_key/elliptic/key.js | 24 +++++++++++------------- src/key.js | 8 ++++---- src/keyring/keyring.js | 4 ++-- src/keyring/localstore.js | 4 ++-- src/message.js | 23 +++++++++-------------- src/openpgp.js | 14 +++++--------- src/packet/clone.js | 8 ++++---- src/packet/signature.js | 2 +- src/type/kdf_params.js | 2 +- test/crypto/cipher/des.js | 3 ++- test/general/{ecc.js => ecc_nist.js} | 3 +-- test/general/index.js | 2 +- test/general/x25519.js | 1 - 19 files changed, 75 insertions(+), 65 deletions(-) rename test/general/{ecc.js => ecc_nist.js} (98%) diff --git a/Gruntfile.js b/Gruntfile.js index 77ec18a7..792fa5ea 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -50,7 +50,8 @@ module.exports = function(grunt) { browserifyOptions: { standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], + // Don't bundle these packages with openpgp.js + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'bn.js', 'jwk-to-pem' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -73,7 +74,7 @@ module.exports = function(grunt) { debug: true, standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'bn.js', 'jwk-to-pem' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -87,6 +88,7 @@ module.exports = function(grunt) { plugin: [ 'browserify-derequire' ] } }, + // TODO: remove this, as it is only necessary to get browsertest working. openpgp_browser: { files: { 'dist/openpgp_browser.js': [ './src/index.js' ] @@ -95,7 +97,7 @@ module.exports = function(grunt) { browserifyOptions: { standalone: 'openpgp' }, - external: [ 'crypto' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -119,7 +121,7 @@ module.exports = function(grunt) { 'test/lib/unittests-bundle.js': [ './test/unittests.js' ] }, options: { - external: [ 'crypto', 'openpgp', '../../dist/openpgp' ] + external: [ 'buffer', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp' ] } } }, diff --git a/package.json b/package.json index 7b0b161d..5fe7e38a 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": "2.6.1", + "version": "3.0.0", "license": "LGPL-3.0+", "homepage": "http://openpgpjs.org/", "engines": { diff --git a/src/cleartext.js b/src/cleartext.js index b90e25b7..970621ea 100644 --- a/src/cleartext.js +++ b/src/cleartext.js @@ -27,10 +27,10 @@ 'use strict'; import config from './config'; -import armor from './encoding/armor.js'; -import enums from './enums.js'; +import armor from './encoding/armor'; +import enums from './enums'; import packet from './packet'; -import { Signature } from './signature.js'; +import { Signature } from './signature'; /** * @class diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js index 4259d3bd..8fc9c2e6 100644 --- a/src/crypto/cipher/aes.js +++ b/src/crypto/cipher/aes.js @@ -6,6 +6,7 @@ import asmCrypto from 'asmcrypto-lite'; +// TODO use webCrypto or nodeCrypto when possible. export default function aes(length) { var c = function(key) { diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 00dc28bd..17aa6f1b 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -56,6 +56,7 @@ export default { * @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} publicParams Algorithm dependent params * @param {module:type/mpi} data Data to be encrypted as MPI + * @param {String} fingerprint Recipient fingerprint * @return {Array} encrypted session key parameters */ publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { @@ -99,7 +100,8 @@ export default { * 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} keyIntegers Algorithm dependent params + * @param {Array} dataIntegers encrypted session key parameters * @param {String} fingerprint Recipient fingerprint * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null */ diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 58a9512d..1927d72f 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -1,4 +1,21 @@ -// Implementation of EdDSA following RFC4880bis-02 for OpenPGP +// OpenPGP.js - An OpenPGP implementation in javascript +// Copyright (C) 2018 ProtonMail.ch +// +// 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 + +// Implementation of EdDSA following RFC4880bis-03 for OpenPGP /** * @requires crypto/hash diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 026beaae..a896d361 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -32,10 +32,6 @@ 'use strict'; -import BN from 'bn.js'; -import ASN1 from 'asn1.js'; -import jwkToPem from 'jwk-to-pem'; - import curves from './curves'; import hash from '../../hash'; import util from '../../../util'; @@ -48,12 +44,15 @@ const webCurves = curves.webCurves; const nodeCrypto = util.getNodeCrypto(); const nodeCurves = curves.nodeCurves; -var ECDSASignature = ASN1.define('ECDSASignature', function() { - this.seq().obj( - this.key('r').int(), - this.key('s').int() - ); -}); +const BN = nodeCrypto ? require('bn.js') : undefined; +const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined; +const ECDSASignature = nodeCrypto ? + require('asn1.js').define('ECDSASignature', function() { + this.seq().obj( + this.key('r').int(), + this.key('s').int() + ); + }) : undefined; function KeyPair(curve, options) { this.curve = curve; @@ -62,7 +61,7 @@ function KeyPair(curve, options) { } KeyPair.prototype.sign = async function (message, hash_algo) { - if (typeof message === 'string') { + if (util.isString(message)) { message = util.str2Uint8Array(message); } if (webCrypto && config.use_native && this.curve.web) { @@ -76,7 +75,7 @@ KeyPair.prototype.sign = async function (message, hash_algo) { }; KeyPair.prototype.verify = async function (message, signature, hash_algo) { - if (typeof message === 'string') { + if (util.isString(message)) { message = util.str2Uint8Array(message); } if (webCrypto && config.use_native && this.curve.web) { @@ -240,7 +239,6 @@ async function nodeVerify(curve, hash_algo, {r, s}, message, publicKey) { { private: false } ); - // FIXME what happens when hash_algo = undefined? const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); verify.write(message); verify.end(); diff --git a/src/key.js b/src/key.js index c79daa7d..912f1abe 100644 --- a/src/key.js +++ b/src/key.js @@ -29,8 +29,8 @@ import config from './config'; import crypto from './crypto'; -import armor from './encoding/armor.js'; -import enums from './enums.js'; +import armor from './encoding/armor'; +import enums from './enums'; import util from './util'; import packet from './packet'; @@ -542,7 +542,7 @@ Key.prototype.getPrimaryUser = function(allowExpired=false) { // sort by primary user flag and signature creation time primaryUsers = primaryUsers.sort(function(a, b) { var A = a.selfCertificate, B = b.selfCertificate; - return A.isPrimaryUserID < B.isPrimaryUserID || A.created < B.created; + return (B.isPrimaryUserID - A.isPrimaryUserID) || (B.created - A.created); }); return primaryUsers.pop(); }; @@ -907,7 +907,7 @@ User.prototype.verify = async function(primaryKey) { } return enums.keyStatus.valid; }))); - return results.some(status => status === enums.keyStatus.valid)? + return results.some(status => status === enums.keyStatus.valid) ? enums.keyStatus.valid : results.pop(); }; diff --git a/src/keyring/keyring.js b/src/keyring/keyring.js index c7b860ca..7bb89ba9 100644 --- a/src/keyring/keyring.js +++ b/src/keyring/keyring.js @@ -25,8 +25,8 @@ 'use strict'; -import { readArmored } from '../key.js'; -import LocalStore from './localstore.js'; +import { readArmored } from '../key'; +import LocalStore from './localstore'; /** * Initialization routine for the keyring. This method reads the diff --git a/src/keyring/localstore.js b/src/keyring/localstore.js index aaa84c7e..4bbc8588 100644 --- a/src/keyring/localstore.js +++ b/src/keyring/localstore.js @@ -27,8 +27,8 @@ 'use strict'; import config from '../config'; -import { readArmored } from '../key.js'; -import util from '../util.js'; +import { readArmored } from '../key'; +import util from '../util'; export default function LocalStore(prefix) { prefix = prefix || 'openpgp-'; diff --git a/src/message.js b/src/message.js index 4546981c..87363d18 100644 --- a/src/message.js +++ b/src/message.js @@ -31,12 +31,12 @@ import config from './config'; import crypto from './crypto'; -import armor from './encoding/armor.js'; -import enums from './enums.js'; -import util from './util.js'; +import armor from './encoding/armor'; +import enums from './enums'; +import util from './util'; import packet from './packet'; -import { Signature } from './signature.js'; -import { getPreferredHashAlgo, getPreferredSymAlgo } from './key.js'; +import { Signature } from './signature'; +import { getPreferredHashAlgo, getPreferredSymAlgo } from './key'; /** @@ -145,19 +145,14 @@ Message.prototype.decryptSessionKey = function(privateKey, password) { }); } else if (privateKey) { - var encryptionKeyIds = this.getEncryptionKeyIds(); - if (!encryptionKeyIds.length) { - // nothing to decrypt - return; - } - var privateKeyPacket = privateKey.getKeyPacket(encryptionKeyIds); - if (!privateKeyPacket.isDecrypted) { - throw new Error('Private key is not decrypted.'); - } var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey); if (!pkESKeyPacketlist) { throw new Error('No public key encrypted session key packet found.'); } + var privateKeyPacket = privateKey.getKeyPacket(this.getEncryptionKeyIds()); + if (!privateKeyPacket.isDecrypted) { + throw new Error('Private key is not decrypted.'); + } // TODO replace when Promise.some or Promise.any are implemented // eslint-disable-next-line no-await-in-loop await pkESKeyPacketlist.some(async function(packet) { diff --git a/src/openpgp.js b/src/openpgp.js index 6a6daa31..22d331ed 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -34,16 +34,12 @@ 'use strict'; -import es6Promise from 'es6-promise'; - -import * as messageLib from './message.js'; -import { CleartextMessage } from './cleartext.js'; -import { generate, reformat } from './key.js'; -import config from './config/config.js'; +import * as messageLib from './message'; +import { CleartextMessage } from './cleartext'; +import { generate, reformat } from './key'; +import config from './config/config'; import util from './util'; -import AsyncProxy from './worker/async_proxy.js'; - -es6Promise.polyfill(); // load ES6 Promises polyfill +import AsyncProxy from './worker/async_proxy'; ////////////////////////// diff --git a/src/packet/clone.js b/src/packet/clone.js index 07e0a632..ab5e41a9 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -23,10 +23,10 @@ 'use strict'; -import { Key } from '../key.js'; -import { Message } from '../message.js'; -import { CleartextMessage } from '../cleartext.js'; -import { Signature } from '../signature.js' +import { Key } from '../key'; +import { Message } from '../message'; +import { CleartextMessage } from '../cleartext'; +import { Signature } from '../signature' import Packetlist from './packetlist.js'; import type_keyid from '../type/keyid.js'; diff --git a/src/packet/signature.js b/src/packet/signature.js index 291611aa..9aed991b 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -630,7 +630,7 @@ Signature.prototype.verify = async function (key, data) { if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) { mpicount = 1; } - // Algorithm-Specific Fields for DSA and ECDSA signatures: + // Algorithm-Specific Fields for DSA, ECDSA, and EdDSA signatures: // - MPI of DSA value r. // - MPI of DSA value s. else if (publicKeyAlgorithm === enums.publicKey.dsa || diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 686830b5..8a33a026 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -67,4 +67,4 @@ KDFParams.prototype.write = function () { KDFParams.fromClone = function (clone) { return new KDFParams(clone.hash, clone.cipher); -}; \ No newline at end of file +}; diff --git a/test/crypto/cipher/des.js b/test/crypto/cipher/des.js index 044a0696..62f2122d 100644 --- a/test/crypto/cipher/des.js +++ b/test/crypto/cipher/des.js @@ -6,7 +6,8 @@ var util = openpgp.util, chai = require('chai'), expect = chai.expect; -describe('TripleDES (EDE) cipher test with test vectors from http://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf', function() { +describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', function() { + // see http://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf var key = new Uint8Array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); var testvectors = [[[0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x95,0xF8,0xA5,0xE5,0xDD,0x31,0xD9,0x00]], [[0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0xDD,0x7F,0x12,0x1C,0xA5,0x01,0x56,0x19]], diff --git a/test/general/ecc.js b/test/general/ecc_nist.js similarity index 98% rename from test/general/ecc.js rename to test/general/ecc_nist.js index 0213b349..27149763 100644 --- a/test/general/ecc.js +++ b/test/general/ecc_nist.js @@ -168,10 +168,9 @@ describe('Elliptic Curve Cryptography', function () { expect(result.signatures[0].valid).to.be.true; }); }); - // FIXME is this pattern correct? it('Sign message', function () { var romeo = load_priv_key('romeo'); - openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) { + return openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) { var romeo = load_pub_key('romeo'); var msg = openpgp.cleartext.readArmored(signed.data); return openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) { diff --git a/test/general/index.js b/test/general/index.js index 49535869..0e9b6ff2 100644 --- a/test/general/index.js +++ b/test/general/index.js @@ -8,7 +8,7 @@ describe('General', function () { require('./openpgp.js'); require('./hkp.js'); require('./oid.js'); - require('./ecc.js'); + require('./ecc_nist.js'); require('./x25519.js'); }); diff --git a/test/general/x25519.js b/test/general/x25519.js index f8aa8122..3ba34002 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -153,7 +153,6 @@ describe('X25519 Cryptography', function () { expect(result.signatures[0].valid).to.be.true; }); }); - // FIXME is this pattern correct? it('Sign message', function () { var name = 'light' var priv = load_priv_key(name); From 496a45a97c4dc70fb340ba9ea950a85fa7d9a174 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Wed, 31 Jan 2018 19:04:26 -0800 Subject: [PATCH 54/75] Update eddsa.js --- src/crypto/public_key/elliptic/eddsa.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 1927d72f..857f5ecd 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -1,5 +1,5 @@ // OpenPGP.js - An OpenPGP implementation in javascript -// Copyright (C) 2018 ProtonMail.ch +// Copyright (C) 2018 Proton Technologies AG // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public From 76d7fac967ab08cb47d2f043cec6cb2923ef5ab5 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Wed, 31 Jan 2018 19:27:00 -0800 Subject: [PATCH 55/75] eliminate openpgp_browser.js --- Gruntfile.js | 29 +++-------------------------- src/worker/worker.js | 2 +- test/unittests.html | 2 +- 3 files changed, 5 insertions(+), 28 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 792fa5ea..8b78d813 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -51,7 +51,7 @@ module.exports = function(grunt) { standalone: 'openpgp' }, // Don't bundle these packages with openpgp.js - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'bn.js', 'jwk-to-pem' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -74,30 +74,7 @@ module.exports = function(grunt) { debug: true, standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'bn.js', 'jwk-to-pem' ], - transform: [ - ["babelify", { - plugins: ["transform-async-to-generator", - "syntax-async-functions", - "transform-regenerator", - "transform-runtime"], - ignore: ['*.min.js'], - presets: ["env"] - }] - ], - plugin: [ 'browserify-derequire' ] - } - }, - // TODO: remove this, as it is only necessary to get browsertest working. - openpgp_browser: { - files: { - 'dist/openpgp_browser.js': [ './src/index.js' ] - }, - options: { - browserifyOptions: { - standalone: 'openpgp' - }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -330,6 +307,6 @@ module.exports = function(grunt) { grunt.registerTask('test', ['eslint', 'mochaTest']); grunt.registerTask('coverage', ['mocha_istanbul:coverage']); grunt.registerTask('saucelabs', ['default', 'copy:browsertest', 'connect:test', 'saucelabs-mocha']); - grunt.registerTask('browsertest', ['browserify:openpgp_browser', 'copy:browsertest', 'connect:test', 'keepalive']); + grunt.registerTask('browsertest', ['browserify:openpgp', 'copy:browsertest', 'connect:test', 'keepalive']); }; diff --git a/src/worker/worker.js b/src/worker/worker.js index 1e411d18..2b25f319 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -19,7 +19,7 @@ self.window = {}; // to make UMD bundles work -importScripts('openpgp_browser.js'); // FIXME +importScripts('openpgp.js'); var openpgp = window.openpgp; var MIN_SIZE_RANDOM_BUFFER = 40000; diff --git a/test/unittests.html b/test/unittests.html index 3ee2b4a6..47f16d5d 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -10,7 +10,7 @@
- + From 40a8c54342174160e3d89349ba8f1fb8427273ab Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Wed, 31 Jan 2018 19:38:07 -0800 Subject: [PATCH 56/75] exclude jwk-to-pem --- Gruntfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8b78d813..0472fa1d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -51,7 +51,7 @@ module.exports = function(grunt) { standalone: 'openpgp' }, // Don't bundle these packages with openpgp.js - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -74,7 +74,7 @@ module.exports = function(grunt) { debug: true, standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], + external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem' ], transform: [ ["babelify", { plugins: ["transform-async-to-generator", From 5a6e65c00f54701f169adaa5ac290ffb44665baf Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Thu, 1 Feb 2018 13:14:26 +0100 Subject: [PATCH 57/75] verification when decoding pkcs5 --- src/crypto/pkcs5.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/crypto/pkcs5.js b/src/crypto/pkcs5.js index a9e364ff..f7e166a4 100644 --- a/src/crypto/pkcs5.js +++ b/src/crypto/pkcs5.js @@ -24,11 +24,8 @@ */ function encode(msg) { const c = 8 - (msg.length % 8); - var result = []; - for (var i = 0; i < c; ++i) { - result.push(String.fromCharCode(c)); - } - return msg + result.join(""); + const padding = String.fromCharCode(c).repeat(c); + return msg + padding; } /** @@ -37,11 +34,15 @@ function encode(msg) { * @return {String} Text with padding removed */ function decode(msg) { - var len = msg.length; + const len = msg.length; if (len > 0) { - var c = msg.charCodeAt(len - 1); + const c = msg.charCodeAt(len - 1); if (c >= 1 && c <= 8) { - return msg.substr(0, len - c); + const provided = msg.substr(len - c); + const computed = String.fromCharCode(c).repeat(c); + if (provided === computed) { + return msg.substr(0, len - c); + } } } throw new Error('Invalid padding'); From 41f1e697b5edc713650fdccc3adefe916b788dcd Mon Sep 17 00:00:00 2001 From: Sanjana Rajan Date: Thu, 1 Feb 2018 13:41:19 +0100 Subject: [PATCH 58/75] remove isValid --- src/crypto/public_key/elliptic/key.js | 7 ------- test/crypto/elliptic.js | 3 --- 2 files changed, 10 deletions(-) diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index a896d361..3dea11bd 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -108,13 +108,6 @@ KeyPair.prototype.getPrivate = function () { } }; -KeyPair.prototype.isValid = function () { - if (this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont') { - throw new Error('Validation is not Implemented for this curve.'); - } - return this.keyPair.validate().result; -}; - module.exports = { KeyPair: KeyPair }; diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 803afd1d..0c7d32d4 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -153,9 +153,6 @@ describe('Elliptic Curve Cryptography', function () { var curve = elliptic_curves.get(name); return curve.genKeyPair().then(keyPair => { expect(keyPair).to.exist; - // FIXME validation is not implemented for Curve25519/Ed25519 key pairs - if (name !== 'curve25519') - expect(keyPair.isValid()).to.be.true; }); })); }); From 9e6005ee392bb423b68731c1557893d4c8965196 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Thu, 1 Feb 2018 10:34:48 -0800 Subject: [PATCH 59/75] auto-scroll browser unit tests --- Gruntfile.js | 3 +-- test/unittests.js | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0472fa1d..d759d9c1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -263,7 +263,6 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-saucelabs'); - grunt.loadNpmTasks('grunt-keepalive'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.registerTask('set_version', function() { @@ -307,6 +306,6 @@ module.exports = function(grunt) { grunt.registerTask('test', ['eslint', 'mochaTest']); grunt.registerTask('coverage', ['mocha_istanbul:coverage']); grunt.registerTask('saucelabs', ['default', 'copy:browsertest', 'connect:test', 'saucelabs-mocha']); - grunt.registerTask('browsertest', ['browserify:openpgp', 'copy:browsertest', 'connect:test', 'keepalive']); + grunt.registerTask('browsertest', ['default', 'copy:browsertest', 'connect:test', 'watch']); }; diff --git a/test/unittests.js b/test/unittests.js index 6af30273..2e07de4b 100644 --- a/test/unittests.js +++ b/test/unittests.js @@ -14,6 +14,7 @@ tests(); + //if (typeof window !== 'undefined') { afterEach(function () { window.scrollTo(0, document.body.scrollHeight); }); } if (options.afterEach) { afterEach(options.afterEach); } if (options.after) { after(options.after); } }); @@ -23,6 +24,9 @@ }; describe('Unit Tests', function () { + + if (typeof window !== 'undefined') { afterEach(function () { window.scrollTo(0, document.body.scrollHeight); }); } + require('./crypto'); require('./general'); require('./worker'); From 9cbfbf453b0ca77252e4e58f73a848b3fdc0d52e Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 1 Feb 2018 14:31:17 -0800 Subject: [PATCH 60/75] Added contingency for when browser does not support NIST curves --- src/crypto/public_key/elliptic/curves.js | 22 ++++++++++++++-------- src/crypto/public_key/elliptic/key.js | 24 ++++++++++++++++-------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index fa9fe764..f835171a 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -150,17 +150,23 @@ Curve.prototype.keyFromPublic = function (pub) { Curve.prototype.genKeyPair = async function () { var keyPair; if (webCrypto && config.use_native && this.web) { - keyPair = await webGenKeyPair(this.name); + // If browser doesn't support a curve, we'll catch it + try { + keyPair = await webGenKeyPair(this.name); + return new KeyPair(this.curve, keyPair); + } catch (err) { + util.print_debug("Browser did not support signing: " + err.message); + } } else if (nodeCrypto && config.use_native && this.node) { keyPair = await nodeGenKeyPair(this.name); + return new KeyPair(this.curve, keyPair); + } + var compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; + var r = await this.curve.genKeyPair(); + if (this.keyType === enums.publicKey.eddsa) { + keyPair = { secret: r.getSecret() }; } else { - var compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; - var r = await this.curve.genKeyPair(); - if (this.keyType === enums.publicKey.eddsa) { - keyPair = { secret: r.getSecret() }; - } else { - keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() }; - } + keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() }; } return new KeyPair(this.curve, keyPair); }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 3dea11bd..417b34ec 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -65,13 +65,17 @@ KeyPair.prototype.sign = async function (message, hash_algo) { message = util.str2Uint8Array(message); } if (webCrypto && config.use_native && this.curve.web) { - return webSign(this.curve, hash_algo, message, this.keyPair); + // If browser doesn't support a curve, we'll catch it + try { + return webSign(this.curve, hash_algo, message, this.keyPair); + } catch (err) { + util.print_debug("Browser did not support signing: " + err.message); + } } else if (nodeCrypto && config.use_native && this.curve.node) { return nodeSign(this.curve, hash_algo, message, this.keyPair); - } else { - const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); - return this.keyPair.sign(digest); } + const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); + return this.keyPair.sign(digest); }; KeyPair.prototype.verify = async function (message, signature, hash_algo) { @@ -79,13 +83,17 @@ KeyPair.prototype.verify = async function (message, signature, hash_algo) { message = util.str2Uint8Array(message); } if (webCrypto && config.use_native && this.curve.web) { - return webVerify(this.curve, hash_algo, signature, message, this.keyPair.getPublic()); + // If browser doesn't support a curve, we'll catch it + try { + return webVerify(this.curve, hash_algo, signature, message, this.keyPair.getPublic()); + } catch (err) { + util.print_debug("Browser did not support signing: " + err.message); + } } else if (nodeCrypto && config.use_native && this.curve.node) { return nodeVerify(this.curve, hash_algo, signature, message, this.keyPair.getPublic()); - } else { - const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); - return this.keyPair.verify(digest, signature); } + const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); + return this.keyPair.verify(digest, signature); }; KeyPair.prototype.derive = function (pub) { From 3370eaa2aa27ea3cca31da6cc4731412f43ef13f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 2 Feb 2018 05:42:54 -0800 Subject: [PATCH 61/75] Expands truncated little-endian parameters in EdDSA --- src/crypto/public_key/elliptic/curves.js | 3 ++- src/crypto/public_key/elliptic/ecdh.js | 20 +++++++-------- src/crypto/public_key/elliptic/ecdsa.js | 4 +-- src/crypto/public_key/elliptic/eddsa.js | 8 +++--- src/crypto/public_key/elliptic/key.js | 14 +++++------ src/crypto/public_key/jsbn.js | 4 +-- test/general/openpgp.js | 1 + test/general/x25519.js | 32 +++++++++++++++++++++++- 8 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index f835171a..9b7cf974 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -95,7 +95,8 @@ const curves = { ed25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), keyType: enums.publicKey.eddsa, - hash: enums.hash.sha512 + hash: enums.hash.sha512, + payloadSize: 32 }, curve25519: { oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index c65dc26d..ff5d3f4e 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -18,11 +18,11 @@ // Key encryption and decryption for RFC 6637 ECDH /** - * @requires crypto/hash - * @requires crypto/cipher - * @requires crypto/aes_kw * @requires crypto/public_key/elliptic/curves * @requires crypto/public_key/jsbn + * @requires crypto/cipher + * @requires crypto/hash + * @requires crypto/aes_kw * @requires type/oid * @requires type/kdf_params * @requires enums @@ -32,15 +32,15 @@ 'use strict'; -import BigInteger from '../jsbn.js'; -import curves from './curves.js'; +import curves from './curves'; +import BigInteger from '../jsbn'; import cipher from '../../cipher'; import hash from '../../hash'; -import aes_kw from '../../aes_kw.js'; -import enums from '../../../enums.js'; -import util from '../../../util.js'; -import type_kdf_params from '../../../type/kdf_params.js'; -import type_oid from '../../../type/oid.js'; +import aes_kw from '../../aes_kw'; +import type_kdf_params from '../../../type/kdf_params'; +import type_oid from '../../../type/oid'; +import enums from '../../../enums'; +import util from '../../../util'; // Build Param for ECDH algorithm (RFC 6637) diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 4b73166e..0e2dd6e3 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -27,8 +27,8 @@ 'use strict'; import hash from '../../hash'; -import curves from './curves.js'; -import BigInteger from '../jsbn.js'; +import curves from './curves'; +import BigInteger from '../jsbn'; /** * Sign a message using the provided key diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 857f5ecd..0b62a1f8 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -27,8 +27,8 @@ 'use strict'; import hash from '../../hash'; -import curves from './curves.js'; -import BigInteger from '../jsbn.js'; +import curves from './curves'; +import BigInteger from '../jsbn'; /** * Sign a message using the provided key @@ -60,8 +60,10 @@ async function sign(oid, hash_algo, m, d) { async function verify(oid, hash_algo, signature, m, Q) { const curve = curves.get(oid); const key = curve.keyFromPublic(Q.toByteArray()); + const R = signature.r.toByteArray(), S = signature.s.toByteArray(); return key.verify( - m, { R: signature.r.toByteArray(), S: signature.s.toByteArray() }, hash_algo + m, { R: Array(curve.payloadSize - R.length).fill(0).concat(R), + S: Array(curve.payloadSize - S.length).fill(0).concat(S) }, hash_algo ); } diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 417b34ec..cc9d7404 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -18,21 +18,22 @@ // Wrapper for a KeyPair of an Elliptic Curve /** - * @requires bn.js - * @requires asn1.js - * @requires jwk-to-pem * @requires crypto/public_key/elliptic/curves + * @requires crypto/public_key/jsbn * @requires crypto/hash * @requires util * @requires enums * @requires config * @requires encoding/base64 + * @requires jwk-to-pem + * @requires asn1.js * @module crypto/public_key/elliptic/key */ 'use strict'; import curves from './curves'; +import BigInteger from '../jsbn'; import hash from '../../hash'; import util from '../../../util'; import enums from '../../../enums'; @@ -44,7 +45,6 @@ const webCurves = curves.webCurves; const nodeCrypto = util.getNodeCrypto(); const nodeCurves = curves.nodeCurves; -const BN = nodeCrypto ? require('bn.js') : undefined; const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined; const ECDSASignature = nodeCrypto ? require('asn1.js').define('ECDSASignature', function() { @@ -167,8 +167,8 @@ async function webSign(curve, hash_algo, message, keyPair) { async function webVerify(curve, hash_algo, {r, s}, message, publicKey) { var l = curve.payloadSize; - r = (r.length === l) ? r : [0].concat(r); - s = (s.length === l) ? s : [0].concat(s); + r = Array(l - r.length).fill(0).concat(r); + s = Array(l - s.length).fill(0).concat(s); var signature = new Uint8Array(r.concat(s)).buffer; const key = await webCrypto.importKey( "jwk", @@ -227,7 +227,7 @@ async function nodeSign(curve, hash_algo, message, keyPair) { } async function nodeVerify(curve, hash_algo, {r, s}, message, publicKey) { - var signature = ECDSASignature.encode({ r: new BN(r), s: new BN(s) }, 'der'); + var signature = ECDSASignature.encode({ r: new BigInteger(r), s: new BigInteger(s) }, 'der'); const key = jwkToPem( { "kty": "EC", diff --git a/src/crypto/public_key/jsbn.js b/src/crypto/public_key/jsbn.js index 908a6be7..bcfe7705 100644 --- a/src/crypto/public_key/jsbn.js +++ b/src/crypto/public_key/jsbn.js @@ -37,7 +37,7 @@ * @module crypto/public_key/jsbn */ -import util from '../../util.js'; +import util from '../../util'; // Basic JavaScript BN library - subset useful for RSA encryption. @@ -53,7 +53,7 @@ var j_lm = ((canary & 0xffffff) == 0xefcafe); export default function BigInteger(a, b, c) { if (a != null) if ("number" == typeof a) this.fromNumber(a, b, c); - else if (b == null && "string" != typeof a) this.fromString(a, 256); + else if (b == null && !util.isString(a)) this.fromString(a, 256); else this.fromString(a, b); } diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 9f874e21..4991b0e2 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -1224,6 +1224,7 @@ describe('OpenPGP.js public api tests', function() { }); }); + // FIXME this test sporadically fails it('should encrypt and decrypt with two passwords', function() { var encOpt = { data: plaintext, diff --git a/test/general/x25519.js b/test/general/x25519.js index 3ba34002..1cd5e88f 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -107,6 +107,7 @@ describe('X25519 Cryptography', function () { '-----END PGP MESSAGE-----'].join('\n') } }; + function load_pub_key(name) { if (data[name].pub_key) { return data[name].pub_key; @@ -269,7 +270,6 @@ describe('X25519 Cryptography', function () { var msg = openpgp.cleartext.readArmored(signed.data); // Verifying signed message return Promise.all([ - // FIXME this test sporadically fails openpgp.verify( { message: msg, publicKeys: hi.toPublic() } ).then(output => expect(output.signatures[0].valid).to.be.true), @@ -302,4 +302,34 @@ describe('X25519 Cryptography', function () { }); }); }); + + it('Should handle little-endian parameters in EdDSA', function () { + var pubKey = [ + '-----BEGIN PGP PUBLIC KEY BLOCK-----', + 'Version: OpenPGP.js VERSION', + 'Comment: https://openpgpjs.org', + '', + 'xjMEWnRgnxYJKwYBBAHaRw8BAQdAZ8gxxCdUxIv4tBwhfUMW2uoEb1KvOfP8', + 'D+0ObBtsLnfNDkhpIDxoaUBoZWwubG8+wnYEEBYKACkFAlp0YJ8GCwkHCAMC', + 'CRDAYsFlymHCFQQVCAoCAxYCAQIZAQIbAwIeAQAAswsA/3qNZnwBn/ef4twv', + 'uvmFicYK//DDX1jIkpDiQ+/okLUEAPdAr3J/Z2WA7OD0d36trHNB06WLXJUu', + 'aCVm1TwoJHcNzjgEWnRgnxIKKwYBBAGXVQEFAQEHQPBVH+skap0NHMBw2HMe', + 'xWYUQ67I9Did3KoJuuEJ/ctQAwEIB8JhBBgWCAATBQJadGCfCRDAYsFlymHC', + 'FQIbDAAAhNQBAKmy4gPorjbwTwy5usylHttP28XnTdaGkZ1E7Rc3G9luAQCs', + 'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==', + '=xeG/', + '-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); + var hi = openpgp.key.readArmored(pubKey).keys[0]; + return hi.verifyPrimaryUser().then(() => { + var results = hi.getPrimaryUser(); + expect(results.user).to.exist; + var user = results.user; + expect(user.selfCertifications[0].verify( + hi.primaryKey, {userid: user.userId, key: hi.primaryKey} + )).to.eventually.be.true; + expect(user.verifyCertificate( + hi.primaryKey, user.selfCertifications[0], [hi] + )).to.eventually.equal(openpgp.enums.keyStatus.valid); + }); + }); }); From 115d1c09492980306d1e61bf74f782b0c4738c45 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 3 Feb 2018 14:36:20 -0800 Subject: [PATCH 62/75] A better patch for EdDSA truncation issue. The last test in test/general/x25519.js is commented, as it seems to defy logic. Yet it can be successfully imported by GPG2. TODO: How? --- src/crypto/aes_kw.js | 3 +- src/crypto/public_key/elliptic/eddsa.js | 21 +++++++------ src/crypto/signature.js | 7 +++-- src/key.js | 6 ++-- src/message.js | 2 +- src/packet/clone.js | 7 +++-- src/packet/public_key.js | 14 +++------ src/packet/signature.js | 5 ++- src/type/ecdh_symkey.js | 4 +-- src/type/mpi.js | 17 ++++++---- src/util.js | 42 +++++++++++++++++++++++++ test/crypto/elliptic.js | 2 +- test/general/x25519.js | 6 ++-- 13 files changed, 95 insertions(+), 41 deletions(-) diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index e70aafd5..e96172c5 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -18,6 +18,7 @@ // Implementation of RFC 3394 AES Key Wrap & Key Unwrap funcions import cipher from './cipher'; +import util from '../util'; function wrap(key, data) { var aes = new cipher["aes" + (key.length*8)](key); @@ -85,7 +86,7 @@ function unwrap(key, data) { } function createArrayBuffer(data) { - if (typeof data === "string") { + if (util.isString(data)) { var length = data.length; var buffer = new ArrayBuffer(length); var view = new Uint8Array(buffer); diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 0b62a1f8..e3f83a13 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -18,33 +18,34 @@ // Implementation of EdDSA following RFC4880bis-03 for OpenPGP /** + * @requires bn.js * @requires crypto/hash - * @requires crypto/public_key/jsbn * @requires crypto/public_key/elliptic/curves * @module crypto/public_key/elliptic/eddsa */ 'use strict'; +import BN from 'bn.js'; import hash from '../../hash'; import curves from './curves'; -import BigInteger from '../jsbn'; /** * Sign a message using the provided key * @param {String} oid Elliptic curve for the key * @param {enums.hash} hash_algo Hash algorithm used to sign * @param {Uint8Array} m Message to sign - * @param {BigInteger} d Private key used to sign - * @return {{r: BigInteger, s: BigInteger}} Signature of the message + * @param {BN} d Private key used to sign + * @return {{r: BN, s: BN}} Signature of the message */ async function sign(oid, hash_algo, m, d) { const curve = curves.get(oid); const key = curve.keyFromSecret(d.toByteArray()); const signature = await key.sign(m, hash_algo); + // EdDSA signature params are returned in little-endian format return { - r: new BigInteger(signature.Rencoded()), - s: new BigInteger(signature.Sencoded()) + R: new BN(Array.from(signature.Rencoded()).reverse()), + S: new BN(Array.from(signature.Sencoded()).reverse()) }; } @@ -60,10 +61,12 @@ async function sign(oid, hash_algo, m, d) { async function verify(oid, hash_algo, signature, m, Q) { const curve = curves.get(oid); const key = curve.keyFromPublic(Q.toByteArray()); - const R = signature.r.toByteArray(), S = signature.s.toByteArray(); + // EdDSA signature params are expected in little-endian format + const R = Array.from(signature.R.toByteArray()).reverse(), + S = Array.from(signature.S.toByteArray()).reverse(); return key.verify( - m, { R: Array(curve.payloadSize - R.length).fill(0).concat(R), - S: Array(curve.payloadSize - S.length).fill(0).concat(S) }, hash_algo + m, { R: [].concat(R, Array(curve.payloadSize - R.length).fill(0)), + S: [].concat(S, Array(curve.payloadSize - S.length).fill(0)) }, hash_algo ); } diff --git a/src/crypto/signature.js b/src/crypto/signature.js index c233a8b2..cf0090ea 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -77,7 +77,7 @@ export default { s = msg_MPIs[1].toBigInteger(); m = data; Q = publickey_MPIs[1].toBigInteger(); - return eddsa.verify(curve.oid, hash_algo, {r: r, s: s}, m, Q); + return eddsa.verify(curve.oid, hash_algo, { R: r, S: s }, m, Q); default: throw new Error('Invalid signature algorithm.'); } @@ -144,7 +144,10 @@ export default { d = keyIntegers[2].toBigInteger(); m = data; signature = await eddsa.sign(curve.oid, hash_algo, m, d); - return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); + return new Uint8Array([].concat( + util.Uint8Array2MPI(signature.R.toArrayLike(Uint8Array, 'le', 32)), + util.Uint8Array2MPI(signature.S.toArrayLike(Uint8Array, 'le', 32)) + )); default: throw new Error('Invalid signature algorithm.'); diff --git a/src/key.js b/src/key.js index 912f1abe..e7220ff8 100644 --- a/src/key.js +++ b/src/key.js @@ -850,7 +850,7 @@ User.prototype.verifyCertificate = async function(primaryKey, certificate, keys, var results = await Promise.all(keys.map(async function(key) { if (!key.getKeyIds().some(id => id.equals(keyid))) { return; } await key.verifyPrimaryUser(); - var keyPacket = key.getSigningKeyPacket(keyid); + var keyPacket = key.getSigningKeyPacket(keyid, allowExpired); if (certificate.revoked || await that.isRevoked(primaryKey, certificate, keyPacket)) { return enums.keyStatus.revoked; } @@ -1205,7 +1205,7 @@ export function generate(options) { if (!options.passphrase) { // Key without passphrase is unlocked by definition options.unlocked = true; } - if (String.prototype.isPrototypeOf(options.userIds) || typeof options.userIds === 'string') { + if (util.isString(options.userIds)) { options.userIds = [options.userIds]; } @@ -1259,7 +1259,7 @@ export function reformat(options) { if (!options.passphrase) { // Key without passphrase is unlocked by definition options.unlocked = true; } - if (String.prototype.isPrototypeOf(options.userIds) || typeof options.userIds === 'string') { + if (util.isString(options.userIds)) { options.userIds = [options.userIds]; } var packetlist = options.privateKey.toPacketlist(); diff --git a/src/message.js b/src/message.js index 87363d18..f39a29a2 100644 --- a/src/message.js +++ b/src/message.js @@ -373,7 +373,7 @@ Message.prototype.sign = async function(privateKeys=[], signature=null) { packetlist.push(literalDataPacket); - await Promise.all(privateKeys.reverse().map(async function(privateKey) { + await Promise.all(Array.from(privateKeys).reverse().map(async function(privateKey) { var signaturePacket = new packet.Signature(); var signingKeyPacket = privateKey.getSigningKeyPacket(); if (!signingKeyPacket.isDecrypted) { diff --git a/src/packet/clone.js b/src/packet/clone.js index ab5e41a9..15ba9d72 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -27,8 +27,9 @@ import { Key } from '../key'; import { Message } from '../message'; import { CleartextMessage } from '../cleartext'; import { Signature } from '../signature' -import Packetlist from './packetlist.js'; -import type_keyid from '../type/keyid.js'; +import Packetlist from './packetlist'; +import type_keyid from '../type/keyid'; +import util from '../util'; ////////////////////////////// @@ -141,7 +142,7 @@ function packetlistCloneToSignatures(clone) { } function packetlistCloneToSignature(clone) { - if (typeof clone === "string") { + if (util.isString(clone)) { //signature is armored return clone; } diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 819dbdaf..a01b41be 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -24,23 +24,17 @@ * major versions. Consequently, this section is complex. * @requires crypto * @requires enums - * @requires type/kdf_params - * @requires type/keyid - * @requires type/mpi - * @requires type/oid * @requires util + * @requires type/keyid * @module packet/public_key */ 'use strict'; -import util from '../util.js'; -import type_mpi from '../type/mpi.js'; -import type_kdf_params from '../type/kdf_params.js'; -import type_keyid from '../type/keyid.js'; -import type_oid from '../type/oid.js'; -import enums from '../enums.js'; import crypto from '../crypto'; +import enums from '../enums'; +import util from '../util'; +import type_keyid from '../type/keyid'; /** * @constructor diff --git a/src/packet/signature.js b/src/packet/signature.js index 9aed991b..3826bf4a 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -639,11 +639,14 @@ Signature.prototype.verify = async function (key, data) { mpicount = 2; } + // EdDSA signature parameters are encoded in litte-endian format + // https://tools.ietf.org/html/rfc8032#section-5.1.2 + var endian = publicKeyAlgorithm === enums.publicKey.eddsa ? 'le' : 'be'; var mpi = [], i = 0; for (var j = 0; j < mpicount; j++) { mpi[j] = new type_mpi(); - i += mpi[j].read(this.signature.subarray(i, this.signature.length)); + i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian); } this.verified = await crypto.signature.verify(publicKeyAlgorithm, diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index 9df5410f..e49cd155 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -24,7 +24,7 @@ 'use strict'; -import util from '../util.js'; +import util from '../util'; module.exports = ECDHSymmetricKey; @@ -34,7 +34,7 @@ module.exports = ECDHSymmetricKey; function ECDHSymmetricKey(data) { if (typeof data === 'undefined') { data = new Uint8Array([]); - } else if (typeof data === 'string') { + } else if (util.isString(data)) { data = util.str2Uint8Array(data); } else { data = new Uint8Array(data); diff --git a/src/type/mpi.js b/src/type/mpi.js index cf5450f6..daf29ba5 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -36,8 +36,8 @@ 'use strict'; -import BigInteger from '../crypto/public_key/jsbn.js'; -import util from '../util.js'; +import BigInteger from '../crypto/public_key/jsbn'; +import util from '../util'; /** * @constructor @@ -46,7 +46,7 @@ export default function MPI(data) { /** An implementation dependent integer */ if (data instanceof BigInteger) { this.fromBigInteger(data); - } else if (typeof data === 'string') { + } else if (util.isString(data)) { this.fromBytes(data); } else { this.data = null; @@ -56,11 +56,12 @@ export default function MPI(data) { /** * Parsing function for a mpi ({@link http://tools.ietf.org/html/rfc4880#section3.2|RFC 4880 3.2}). * @param {String} input Payload of mpi data + * @param {String} endian Endianness of the payload; 'be' for big-endian and 'le' for little-endian * @return {Integer} Length of data read */ -MPI.prototype.read = function (bytes) { +MPI.prototype.read = function (bytes, endian='be') { - if(typeof bytes === 'string' || String.prototype.isPrototypeOf(bytes)) { + if(util.isString(bytes)) { bytes = util.str2Uint8Array(bytes); } @@ -77,7 +78,11 @@ MPI.prototype.read = function (bytes) { // TODO: Verification of this size method! This size calculation as // specified above is not applicable in JavaScript var bytelen = Math.ceil(bits / 8); - var raw = util.Uint8Array2str(bytes.subarray(2, 2 + bytelen)); + var payload = bytes.subarray(2, 2 + bytelen); + if (endian === 'le') { + payload = new Uint8Array(payload).reverse(); + } + var raw = util.Uint8Array2str(payload); this.fromBytes(raw); return 2 + bytelen; diff --git a/src/util.js b/src/util.js index 8f9573ac..ff2f440b 100644 --- a/src/util.js +++ b/src/util.js @@ -290,6 +290,48 @@ export default { return result.join(''); }, + // returns bit length of the integer x + nbits: function (x) { + var r = 1, + t = x >>> 16; + if (t !== 0) { + x = t; + r += 16; + } + t = x >> 8; + if (t !== 0) { + x = t; + r += 8; + } + t = x >> 4; + if (t !== 0) { + x = t; + r += 4; + } + t = x >> 2; + if (t !== 0) { + x = t; + r += 2; + } + t = x >> 1; + if (t !== 0) { + x = t; + r += 1; + } + return r; + }, + + /** + * Convert a Uint8Array to an MPI array. + * @function module:util.Uint8Array2MPI + * @param {Uint8Array} bin An array of (binary) integers to convert + * @return {Array} MPI-formatted array + */ + Uint8Array2MPI: function (bin) { + var size = (bin.length - 1) * 8 + this.nbits(bin[0]); + return [(size & 0xFF00) >> 8, size & 0xFF].concat(Array.from(bin)); + }, + /** * Concat Uint8arrays * @function module:util.concatUint8Array diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 0c7d32d4..2c9ed482 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -299,7 +299,7 @@ describe('Elliptic Curve Cryptography', function () { }); describe('ECDH key exchange', function () { var decrypt_message = function (oid, hash, cipher, priv, ephemeral, data, fingerprint) { - if (typeof data === 'string') { + if (openpgp.util.isString(data)) { data = openpgp.util.str2Uint8Array(data); } else { data = new Uint8Array(data); diff --git a/test/general/x25519.js b/test/general/x25519.js index 1cd5e88f..6591c4f8 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -303,10 +303,11 @@ describe('X25519 Cryptography', function () { }); }); +/* TODO how does GPG2 accept this? it('Should handle little-endian parameters in EdDSA', function () { var pubKey = [ '-----BEGIN PGP PUBLIC KEY BLOCK-----', - 'Version: OpenPGP.js VERSION', + 'Version: OpenPGP.js v3.0.0', 'Comment: https://openpgpjs.org', '', 'xjMEWnRgnxYJKwYBBAHaRw8BAQdAZ8gxxCdUxIv4tBwhfUMW2uoEb1KvOfP8', @@ -322,6 +323,7 @@ describe('X25519 Cryptography', function () { var hi = openpgp.key.readArmored(pubKey).keys[0]; return hi.verifyPrimaryUser().then(() => { var results = hi.getPrimaryUser(); + expect(results).to.exist; expect(results.user).to.exist; var user = results.user; expect(user.selfCertifications[0].verify( @@ -331,5 +333,5 @@ describe('X25519 Cryptography', function () { hi.primaryKey, user.selfCertifications[0], [hi] )).to.eventually.equal(openpgp.enums.keyStatus.valid); }); - }); + }); */ }); From 37eec2bde27bbbf99b7b24960c46d35a52419308 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 4 Feb 2018 02:44:05 -0800 Subject: [PATCH 63/75] Added Ed25519 Test Vectors from RFC8032 --- src/crypto/public_key/elliptic/eddsa.js | 6 +- test/general/x25519.js | 185 ++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 3 deletions(-) diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index e3f83a13..4cf1e85b 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -35,8 +35,8 @@ import curves from './curves'; * @param {String} oid Elliptic curve for the key * @param {enums.hash} hash_algo Hash algorithm used to sign * @param {Uint8Array} m Message to sign - * @param {BN} d Private key used to sign - * @return {{r: BN, s: BN}} Signature of the message + * @param {BigInteger} d Private key used to sign + * @return {{R: BN, S: BN}} Signature of the message */ async function sign(oid, hash_algo, m, d) { const curve = curves.get(oid); @@ -53,7 +53,7 @@ async function sign(oid, hash_algo, m, d) { * Verifies if a signature is valid for a message * @param {String} oid Elliptic curve for the key * @param {enums.hash} hash_algo Hash algorithm used in the signature - * @param {{r: BigInteger, s: BigInteger}} signature Signature to verify + * @param {{R: BigInteger, S: BigInteger}} signature Signature to verify * @param {Uint8Array} m Message to verify * @param {BigInteger} Q Public key used to verify the message * @return {Boolean} diff --git a/test/general/x25519.js b/test/general/x25519.js index 6591c4f8..fc43bad8 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -303,6 +303,191 @@ describe('X25519 Cryptography', function () { }); }); + describe('Ed25519 Test Vectors from RFC8032', function () { + // https://tools.ietf.org/html/rfc8032#section-7.1 + const crypto = openpgp.crypto.signature; + const curve = openpgp.crypto.publicKey.elliptic.get('ed25519'); + const util = openpgp.util; + function testVector(vector) { + var S = curve.keyFromSecret(vector.SECRET_KEY); + var P = curve.keyFromPublic(vector.PUBLIC_KEY); + expect(S.getPublic()).to.deep.equal(P.getPublic()); + var data = util.str2Uint8Array(vector.MESSAGE); + var keyIntegers = [ + openpgp.OID.fromClone(curve), + new openpgp.MPI(util.hex2bin(vector.PUBLIC_KEY)), + new openpgp.MPI(util.hex2bin(vector.SECRET_KEY)) + ]; + var msg_MPIs = [ + new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(vector.SIGNATURE.R).reverse())), + new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(vector.SIGNATURE.S).reverse())), + ]; + return Promise.all([ + crypto.sign(undefined, 22, keyIntegers, data).then(signature => { + var len = ((signature[0] << 8| signature[1]) + 7) / 8; + expect(util.hex2Uint8Array(vector.SIGNATURE.R)).to.deep.eq(signature.slice(2, 2 + len)); + expect(util.hex2Uint8Array(vector.SIGNATURE.S)).to.deep.eq(signature.slice(4 + len)); + }), + crypto.verify(22, undefined, msg_MPIs, keyIntegers, data).then(result => { + expect(result).to.be.true; + }) + ]); + }; + + it('Signature of empty string', function () { + return testVector({ + SECRET_KEY: + ['9d61b19deffd5a60ba844af492ec2cc4', + '4449c5697b326919703bac031cae7f60'].join(''), + PUBLIC_KEY: + ['d75a980182b10ab7d54bfed3c964073a', + '0ee172f3daa62325af021a68f707511a'].join(''), + MESSAGE: '', + SIGNATURE: + { R: ['e5564300c360ac729086e2cc806e828a', + '84877f1eb8e5d974d873e06522490155'].join(''), + S: ['5fb8821590a33bacc61e39701cf9b46b', + 'd25bf5f0595bbe24655141438e7a100b'].join('') } + }); + }); + + it('Signature of single byte', function () { + return testVector({ + SECRET_KEY: + ['4ccd089b28ff96da9db6c346ec114e0f', + '5b8a319f35aba624da8cf6ed4fb8a6fb'].join(''), + PUBLIC_KEY: + ['3d4017c3e843895a92b70aa74d1b7ebc', + '9c982ccf2ec4968cc0cd55f12af4660c'].join(''), + MESSAGE: util.hex2bin('72'), + SIGNATURE: + { R: ['92a009a9f0d4cab8720e820b5f642540', + 'a2b27b5416503f8fb3762223ebdb69da'].join(''), + S: ['085ac1e43e15996e458f3613d0f11d8c', + '387b2eaeb4302aeeb00d291612bb0c00'].join('') } + }); + }); + + it('Signature of two bytes', function () { + return testVector({ + SECRET_KEY: + ['c5aa8df43f9f837bedb7442f31dcb7b1', + '66d38535076f094b85ce3a2e0b4458f7'].join(''), + PUBLIC_KEY: + ['fc51cd8e6218a1a38da47ed00230f058', + '0816ed13ba3303ac5deb911548908025'].join(''), + MESSAGE: util.hex2bin('af82'), + SIGNATURE: + { R: ['6291d657deec24024827e69c3abe01a3', + '0ce548a284743a445e3680d7db5ac3ac'].join(''), + S: ['18ff9b538d16f290ae67f760984dc659', + '4a7c15e9716ed28dc027beceea1ec40a'].join('') } + }); + }); + + it('Signature of 1023 bytes', function () { + return testVector({ + SECRET_KEY: + ['f5e5767cf153319517630f226876b86c', + '8160cc583bc013744c6bf255f5cc0ee5'].join(''), + PUBLIC_KEY: + ['278117fc144c72340f67d0f2316e8386', + 'ceffbf2b2428c9c51fef7c597f1d426e'].join(''), + MESSAGE: util.hex2bin([ + '08b8b2b733424243760fe426a4b54908', + '632110a66c2f6591eabd3345e3e4eb98', + 'fa6e264bf09efe12ee50f8f54e9f77b1', + 'e355f6c50544e23fb1433ddf73be84d8', + '79de7c0046dc4996d9e773f4bc9efe57', + '38829adb26c81b37c93a1b270b20329d', + '658675fc6ea534e0810a4432826bf58c', + '941efb65d57a338bbd2e26640f89ffbc', + '1a858efcb8550ee3a5e1998bd177e93a', + '7363c344fe6b199ee5d02e82d522c4fe', + 'ba15452f80288a821a579116ec6dad2b', + '3b310da903401aa62100ab5d1a36553e', + '06203b33890cc9b832f79ef80560ccb9', + 'a39ce767967ed628c6ad573cb116dbef', + 'efd75499da96bd68a8a97b928a8bbc10', + '3b6621fcde2beca1231d206be6cd9ec7', + 'aff6f6c94fcd7204ed3455c68c83f4a4', + '1da4af2b74ef5c53f1d8ac70bdcb7ed1', + '85ce81bd84359d44254d95629e9855a9', + '4a7c1958d1f8ada5d0532ed8a5aa3fb2', + 'd17ba70eb6248e594e1a2297acbbb39d', + '502f1a8c6eb6f1ce22b3de1a1f40cc24', + '554119a831a9aad6079cad88425de6bd', + 'e1a9187ebb6092cf67bf2b13fd65f270', + '88d78b7e883c8759d2c4f5c65adb7553', + '878ad575f9fad878e80a0c9ba63bcbcc', + '2732e69485bbc9c90bfbd62481d9089b', + 'eccf80cfe2df16a2cf65bd92dd597b07', + '07e0917af48bbb75fed413d238f5555a', + '7a569d80c3414a8d0859dc65a46128ba', + 'b27af87a71314f318c782b23ebfe808b', + '82b0ce26401d2e22f04d83d1255dc51a', + 'ddd3b75a2b1ae0784504df543af8969b', + 'e3ea7082ff7fc9888c144da2af58429e', + 'c96031dbcad3dad9af0dcbaaaf268cb8', + 'fcffead94f3c7ca495e056a9b47acdb7', + '51fb73e666c6c655ade8297297d07ad1', + 'ba5e43f1bca32301651339e22904cc8c', + '42f58c30c04aafdb038dda0847dd988d', + 'cda6f3bfd15c4b4c4525004aa06eeff8', + 'ca61783aacec57fb3d1f92b0fe2fd1a8', + '5f6724517b65e614ad6808d6f6ee34df', + 'f7310fdc82aebfd904b01e1dc54b2927', + '094b2db68d6f903b68401adebf5a7e08', + 'd78ff4ef5d63653a65040cf9bfd4aca7', + '984a74d37145986780fc0b16ac451649', + 'de6188a7dbdf191f64b5fc5e2ab47b57', + 'f7f7276cd419c17a3ca8e1b939ae49e4', + '88acba6b965610b5480109c8b17b80e1', + 'b7b750dfc7598d5d5011fd2dcc5600a3', + '2ef5b52a1ecc820e308aa342721aac09', + '43bf6686b64b2579376504ccc493d97e', + '6aed3fb0f9cd71a43dd497f01f17c0e2', + 'cb3797aa2a2f256656168e6c496afc5f', + 'b93246f6b1116398a346f1a641f3b041', + 'e989f7914f90cc2c7fff357876e506b5', + '0d334ba77c225bc307ba537152f3f161', + '0e4eafe595f6d9d90d11faa933a15ef1', + '369546868a7f3a45a96768d40fd9d034', + '12c091c6315cf4fde7cb68606937380d', + 'b2eaaa707b4c4185c32eddcdd306705e', + '4dc1ffc872eeee475a64dfac86aba41c', + '0618983f8741c5ef68d3a101e8a3b8ca', + 'c60c905c15fc910840b94c00a0b9d0'].join('')), + SIGNATURE: + { R: ['0aab4c900501b3e24d7cdf4663326a3a', + '87df5e4843b2cbdb67cbf6e460fec350'].join(''), + S: ['aa5371b1508f9f4528ecea23c436d94b', + '5e8fcd4f681e30a6ac00a9704a188a03'].join('') } + }); + }); + + it('Signature of SHA(abc)', function () { + return testVector({ + SECRET_KEY: + ['833fe62409237b9d62ec77587520911e', + '9a759cec1d19755b7da901b96dca3d42'].join(''), + PUBLIC_KEY: + ['ec172b93ad5e563bf4932c70e1245034', + 'c35467ef2efd4d64ebf819683467e2bf'].join(''), + MESSAGE: util.hex2bin([ + 'ddaf35a193617abacc417349ae204131', + '12e6fa4e89a97ea20a9eeee64b55d39a', + '2192992a274fc1a836ba3c23a3feebbd', + '454d4423643ce80e2a9ac94fa54ca49f'].join('')), + SIGNATURE: + { R: ['dc2a4459e7369633a52b1bf277839a00', + '201009a3efbf3ecb69bea2186c26b589'].join(''), + S: ['09351fc9ac90b3ecfdfbc7c66431e030', + '3dca179c138ac17ad9bef1177331a704'].join('') } + }); + }); + }); + /* TODO how does GPG2 accept this? it('Should handle little-endian parameters in EdDSA', function () { var pubKey = [ From ac440b9ae641acf9f521d2c9c1734e58c64a6da2 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Mon, 5 Feb 2018 14:21:35 -0800 Subject: [PATCH 64/75] use ES6 polyfill for worker and tests, not in openpgp.js --- npm-shrinkwrap.json | 2199 +++++++++++++++++++++++++++++------------- package.json | 4 +- src/worker/worker.js | 5 + test/unittests.js | 5 + 4 files changed, 1538 insertions(+), 675 deletions(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f3e79e6d..beaebdb7 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "openpgp", - "version": "2.6.2", + "version": "3.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -14,12 +14,6 @@ "through": "2.3.8" } }, - "JSV": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz", - "integrity": "sha1-0Hf2glVx+CEy+d/67Vh7QCn+/1c=", - "dev": true - }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -42,6 +36,23 @@ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", "dev": true }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, "agent-base": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.1.1.tgz", @@ -72,6 +83,12 @@ "json-schema-traverse": "0.3.1" } }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -89,6 +106,12 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, + "ansi-escapes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "dev": true + }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", @@ -120,6 +143,16 @@ "sprintf-js": "1.0.3" } }, + "aria-query": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-0.7.1.tgz", + "integrity": "sha1-Jsu1r/ZBRLCoJb4YRuCxbPoAsR4=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7", + "commander": "2.12.2" + } + }, "arr-diff": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", @@ -147,6 +180,16 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", "dev": true }, + "array-includes": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", + "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", + "dev": true, + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.10.0" + } + }, "array-map": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", @@ -159,18 +202,42 @@ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, "array-unique": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", "dev": true }, - "asmcrypto-lite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/asmcrypto-lite/-/asmcrypto-lite-1.1.0.tgz", - "integrity": "sha1-6Zb6AnHFjDwlqegBWMRJv6SY2fs=", + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "asmcrypto-lite": { + "version": "git+https://github.com/openpgpjs/asmcrypto-lite.git#57ef213b6677d118c1b9668a35c74b6c716a5310" + }, "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", @@ -178,10 +245,9 @@ "dev": true }, "asn1.js": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", - "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", - "dev": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.0.0.tgz", + "integrity": "sha512-Y+FKviD0uyIWWo/xE0XkUl0x1allKFhzEVJ+//2Dgqpy+n+B77MlPNqvyk7Vx50M9XyVzjnRhDqJAEAsyivlbA==", "requires": { "bn.js": "4.11.8", "inherits": "2.0.3", @@ -209,6 +275,12 @@ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", "dev": true }, + "ast-types-flow": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", + "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", + "dev": true + }, "astw": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", @@ -248,6 +320,15 @@ "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", "dev": true }, + "axobject-query": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-0.1.0.tgz", + "integrity": "sha1-YvWdvFnJ+SQnWco0mWDnov48NsA=", + "dev": true, + "requires": { + "ast-types-flow": "0.0.7" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -302,6 +383,17 @@ "trim-right": "1.0.1" } }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, "babel-helper-call-delegate": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", @@ -326,6 +418,17 @@ "lodash": "4.17.4" } }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, "babel-helper-function-name": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", @@ -380,6 +483,19 @@ "lodash": "4.17.4" } }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "dev": true, + "requires": { + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, "babel-helper-replace-supers": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", @@ -422,6 +538,35 @@ "babel-runtime": "6.26.0" } }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=", + "dev": true + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=", + "dev": true + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=", + "dev": true + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "dev": true, + "requires": { + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-runtime": "6.26.0" + } + }, "babel-plugin-transform-es2015-arrow-functions": { "version": "6.22.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", @@ -656,6 +801,17 @@ "regexpu-core": "2.0.0" } }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "dev": true, + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", + "babel-plugin-syntax-exponentiation-operator": "6.13.0", + "babel-runtime": "6.26.0" + } + }, "babel-plugin-transform-regenerator": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", @@ -665,6 +821,15 @@ "regenerator-transform": "0.10.1" } }, + "babel-plugin-transform-runtime": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", + "integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, "babel-plugin-transform-strict-mode": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", @@ -675,13 +840,34 @@ "babel-types": "6.26.0" } }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "core-js": "2.5.1", + "regenerator-runtime": "0.10.5" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", + "dev": true + } + } + }, + "babel-preset-env": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.6.1.tgz", + "integrity": "sha512-W6VIyA6Ch9ePMI7VptNn2wBM6dbG0eSz25HEiL40nQXCsXGTGZSTZu1Iap+cj3Q0S5a7T9+529l/5Bkvd+afNA==", "dev": true, "requires": { "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-to-generator": "6.24.1", "babel-plugin-transform-es2015-arrow-functions": "6.22.0", "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", "babel-plugin-transform-es2015-block-scoping": "6.26.0", @@ -704,7 +890,11 @@ "babel-plugin-transform-es2015-template-literals": "6.22.0", "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0" + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-regenerator": "6.26.0", + "browserslist": "2.11.3", + "invariant": "2.2.2", + "semver": "5.4.1" } }, "babel-register": { @@ -795,8 +985,7 @@ "base64-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", - "dev": true + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" }, "basic-auth": { "version": "2.0.0", @@ -838,8 +1027,7 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "body-parser": { "version": "1.14.2", @@ -931,8 +1119,7 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browser-pack": { "version": "6.0.2", @@ -1217,7 +1404,7 @@ "browserify-rsa": "4.0.1", "create-hash": "1.1.3", "create-hmac": "1.1.6", - "elliptic": "6.4.0", + "elliptic": "git+https://github.com/openpgpjs/elliptic.git#0bd7555723b84ac2b747e00c8cea1e64e99b4f4f", "inherits": "2.0.3", "parse-asn1": "5.1.0" } @@ -1231,11 +1418,20 @@ "pako": "1.0.6" } }, + "browserslist": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-2.11.3.tgz", + "integrity": "sha512-yWu5cXT7Av6mVwzWc8lMsJMHWn4xyjSuGYi4IozbVTLUOEYPSagUB8kiMDUHA1fS3zjr8nkxkn9jdvug4BBRmA==", + "dev": true, + "requires": { + "caniuse-lite": "1.0.30000792", + "electron-to-chromium": "1.3.32" + } + }, "buffer": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.8.tgz", "integrity": "sha512-xXvjQhVNz50v2nPeoOsNqWCLGfiv4ji/gXZM28jnVwdLJxH4mFyqgqCKfaK9zf1KUbG6zTkjLOy7ou+jSMarGA==", - "dev": true, "requires": { "base64-js": "1.2.1", "ieee754": "1.1.8" @@ -1271,6 +1467,21 @@ "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=", "dev": true }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, "camelcase": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", @@ -1295,6 +1506,12 @@ } } }, + "caniuse-lite": { + "version": "1.0.30000792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000792.tgz", + "integrity": "sha1-0M6pgfgRjzlhRxr7tDyaHlu/AzI=", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1335,6 +1552,15 @@ "type-detect": "4.0.5" } }, + "chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "requires": { + "check-error": "1.0.2" + } + }, "chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", @@ -1348,6 +1574,12 @@ "supports-color": "2.0.0" } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -1381,48 +1613,26 @@ "safe-buffer": "5.1.1" } }, - "cli": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", - "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "exit": "0.1.2", - "glob": "7.1.2" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - } + "restore-cursor": "2.0.0" } }, - "cli-table": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", - "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", - "dev": true, - "requires": { - "colors": "1.0.3" - }, - "dependencies": { - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", - "dev": true - } - } + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true }, "cliui": { "version": "3.2.0", @@ -1453,6 +1663,21 @@ "integrity": "sha1-EpOLz5vhlI+gBvkuDEyegXBRCMA=", "dev": true }, + "color-convert": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -1494,15 +1719,6 @@ "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", "dev": true }, - "comment-parser": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-0.3.2.tgz", - "integrity": "sha1-PAPwd2uGo239mgosl8YwfzMggv4=", - "dev": true, - "requires": { - "readable-stream": "2.3.3" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -1563,6 +1779,12 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -1594,7 +1816,7 @@ "dev": true, "requires": { "bn.js": "4.11.8", - "elliptic": "6.4.0" + "elliptic": "git+https://github.com/openpgpjs/elliptic.git#0bd7555723b84ac2b747e00c8cea1e64e99b4f4f" } }, "create-hash": { @@ -1624,12 +1846,13 @@ } }, "cross-spawn": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", - "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { "lru-cache": "4.1.1", + "shebang-command": "1.2.0", "which": "1.2.14" }, "dependencies": { @@ -1684,17 +1907,6 @@ "randomfill": "1.0.3" } }, - "cst": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/cst/-/cst-0.4.10.tgz", - "integrity": "sha512-U5ETe1IOjq2h56ZcBE3oe9rT7XryCH6IKgPMv0L7sSk6w29yR3p5egCK0T3BDNHHV95OoUBgXsqiVG+3a900Ag==", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babylon": "6.18.0", - "source-map-support": "0.4.18" - } - }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1704,12 +1916,6 @@ "array-find-index": "1.0.2" } }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", - "dev": true - }, "d": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", @@ -1719,6 +1925,12 @@ "es5-ext": "0.10.37" } }, + "damerau-levenshtein": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz", + "integrity": "sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ=", + "dev": true + }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -1768,12 +1980,6 @@ "type-detect": "4.0.5" } }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true - }, "deep-extend": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", @@ -1786,12 +1992,37 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", "dev": true }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.2.8" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1913,28 +2144,13 @@ "randombytes": "2.0.5" } }, - "dom-serializer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", - "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" - }, - "dependencies": { - "domelementtype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", - "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", - "dev": true - }, - "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", - "dev": true - } + "esutils": "2.0.2" } }, "domain-browser": { @@ -1943,31 +2159,6 @@ "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", "dev": true }, - "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", - "dev": true - }, - "domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", - "dev": true, - "requires": { - "domelementtype": "1.3.0" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "dev": true, - "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" - } - }, "duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -2006,11 +2197,14 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", "dev": true }, + "electron-to-chromium": { + "version": "1.3.32", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.32.tgz", + "integrity": "sha1-EdBoTAhA4APEvoko+KxfNdvCtOY=", + "dev": true + }, "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, + "version": "git+https://github.com/openpgpjs/elliptic.git#0bd7555723b84ac2b747e00c8cea1e64e99b4f4f", "requires": { "bn.js": "4.11.8", "brorand": "1.1.0", @@ -2021,6 +2215,12 @@ "minimalistic-crypto-utils": "1.0.1" } }, + "emoji-regex": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz", + "integrity": "sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ==", + "dev": true + }, "encodeurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", @@ -2035,12 +2235,6 @@ "iconv-lite": "0.4.19" } }, - "entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", - "dev": true - }, "error-ex": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", @@ -2050,6 +2244,30 @@ "is-arrayish": "0.2.1" } }, + "es-abstract": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.10.0.tgz", + "integrity": "sha512-/uh/DhdqIOSkAWifU+8nG78vlQxdLckUdI/sPgy0VhuXi2qJ7T8czBmqIYtLQVpCIFYafChnsRsB5pyb1JdmCQ==", + "dev": true, + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, "es5-ext": { "version": "0.10.37", "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", @@ -2086,10 +2304,9 @@ } }, "es6-promise": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz", - "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng==", - "dev": true + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", + "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==" }, "es6-set": { "version": "0.1.5", @@ -2181,12 +2398,366 @@ "estraverse": "4.2.0" } }, + "eslint": { + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.17.0.tgz", + "integrity": "sha512-AyxBUCANU/o/xC0ijGMKavo5Ls3oK6xykiOITlMdjFjrKOsqLrA7Nf5cnrDgcKrHzBirclAZt63XO7YZlVUPwA==", + "dev": true, + "requires": { + "ajv": "5.5.0", + "babel-code-frame": "6.26.0", + "chalk": "2.3.0", + "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.1.0", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0", + "espree": "3.5.3", + "esquery": "1.0.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "11.3.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.1.0", + "js-yaml": "3.10.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.4.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.2", + "text-table": "0.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globals": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz", + "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==", + "dev": true + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, + "eslint-config-airbnb": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz", + "integrity": "sha512-zLyOhVWhzB/jwbz7IPSbkUuj7X2ox4PHXTcZkEmDqTvd0baJmJyuxlFPDlZOE/Y5bC+HQRaEkT3FoHo9wIdRiw==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "12.1.0" + } + }, + "eslint-config-airbnb-base": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz", + "integrity": "sha512-/vjm0Px5ZCpmJqnjIzcFb9TKZrKWz0gnuG/7Gfkt0Db1ELJR51xkZth+t14rYdqWgX836XbuxtArbIHlVhbLBA==", + "dev": true, + "requires": { + "eslint-restricted-globals": "0.1.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", + "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", + "dev": true, + "requires": { + "debug": "2.6.9", + "resolve": "1.5.0" + }, + "dependencies": { + "resolve": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", + "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==", + "dev": true, + "requires": { + "path-parse": "1.0.5" + } + } + } + }, + "eslint-module-utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", + "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "pkg-dir": "1.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz", + "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==", + "dev": true, + "requires": { + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "0.3.2", + "eslint-module-utils": "2.1.1", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.4", + "read-pkg-up": "2.0.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "eslint-plugin-jsx-a11y": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.3.tgz", + "integrity": "sha1-VFg9GuRCSDFi4EDhPMMYZUZRAOU=", + "dev": true, + "requires": { + "aria-query": "0.7.1", + "array-includes": "3.0.3", + "ast-types-flow": "0.0.7", + "axobject-query": "0.1.0", + "damerau-levenshtein": "1.0.4", + "emoji-regex": "6.5.1", + "jsx-ast-utils": "2.0.1" + } + }, + "eslint-plugin-react": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.6.1.tgz", + "integrity": "sha512-30aMOHWX/DOaaLJVBHz6RMvYM2qy5GH63+y2PLFdIrYe4YLtODFmT3N1YA7ZqUnaBweVbedr4K4cqxOlWAPjIw==", + "dev": true, + "requires": { + "doctrine": "2.1.0", + "has": "1.0.1", + "jsx-ast-utils": "2.0.1", + "prop-types": "15.6.0" + } + }, + "eslint-restricted-globals": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz", + "integrity": "sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc=", + "dev": true + }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, + "espree": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.3.tgz", + "integrity": "sha512-Zy3tAJDORxQZLl2baguiRU1syPERAIg0L+JB2MWorORgTu/CplzvxS9WWA7Xh4+Q+eOQihNs/1o1Xep8cvCxWQ==", + "dev": true, + "requires": { + "acorn": "5.4.1", + "acorn-jsx": "3.0.1" + }, + "dependencies": { + "acorn": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.4.1.tgz", + "integrity": "sha512-XLmq3H/BVvW6/GbxKryGxWORz1ebilSsUDlyC27bXhWGWAZWkGwS6FLHjOlwFXNFoWFQEO/Df4u0YYd0K3BQgQ==", + "dev": true + } + } + }, "esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.0" + } + }, "esrecurse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", @@ -2277,6 +2848,17 @@ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", "dev": true }, + "external-editor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.1.0.tgz", + "integrity": "sha512-E44iT5QVOUJBKij4IIV3uvxuNlbKS38Tw1HiupxEIHPv9qtC2PrDYohbXV5U+1jnfIXttny8gUhj+oZvflFlzA==", + "dev": true, + "requires": { + "chardet": "0.4.2", + "iconv-lite": "0.4.19", + "tmp": "0.0.33" + } + }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -2292,12 +2874,6 @@ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", - "dev": true - }, "fast-deep-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", @@ -2325,6 +2901,29 @@ "websocket-driver": "0.7.0" } }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "dev": true, + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.17" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "dev": true + } + } + }, "fg-lodash": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/fg-lodash/-/fg-lodash-0.0.2.tgz", @@ -2359,6 +2958,16 @@ "object-assign": "4.1.1" } }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, "file-sync-cmp": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz", @@ -2433,6 +3042,18 @@ } } }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -2448,6 +3069,12 @@ "for-in": "1.0.2" } }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -2466,12 +3093,12 @@ } }, "formatio": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz", - "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz", + "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=", "dev": true, "requires": { - "samsam": "1.1.2" + "samsam": "1.3.0" } }, "fresh": { @@ -3396,6 +4023,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gaze": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.2.tgz", @@ -3477,6 +4110,20 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.0.6", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, "globule": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", @@ -3509,12 +4156,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", - "dev": true - }, "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", @@ -3647,17 +4288,6 @@ "file-sync-cmp": "0.1.1" } }, - "grunt-contrib-jshint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", - "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "hooker": "0.2.3", - "jshint": "2.9.5" - } - }, "grunt-contrib-uglify": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/grunt-contrib-uglify/-/grunt-contrib-uglify-3.2.1.tgz", @@ -3716,36 +4346,45 @@ } } }, - "grunt-jscs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/grunt-jscs/-/grunt-jscs-3.0.1.tgz", - "integrity": "sha1-H65Q4+lV3546nZQlrsIqzK4AgJI=", - "dev": true, - "requires": { - "hooker": "0.2.3", - "jscs": "3.0.7", - "lodash": "4.6.1", - "vow": "0.4.17" - }, - "dependencies": { - "lodash": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.6.1.tgz", - "integrity": "sha1-3wDBFkrSNrGDz8OIel6NOMxjy7w=", - "dev": true - } - } - }, "grunt-jsdoc": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.2.0.tgz", - "integrity": "sha512-3/HzvzcG7gxlm4YefR5ELbsUB/bIFCeX3CbUeAANKGMfNUZ2tDQ+Pp0YRb/VWHjyu+v8wG6n1PD8yIjubjEDeg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/grunt-jsdoc/-/grunt-jsdoc-2.2.1.tgz", + "integrity": "sha512-33QZYBYjv2Ph3H2ygqXHn/o0ttfptw1f9QciOTgvzhzUeiPrnvzMNUApTPtw22T6zgReE5FZ1RR58U2wnK/l+w==", "dev": true, "requires": { "cross-spawn": "3.0.1", - "jsdoc": "3.5.5" + "jsdoc": "3.5.5", + "marked": "0.3.12" + }, + "dependencies": { + "cross-spawn": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", + "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", + "dev": true, + "requires": { + "lru-cache": "4.1.1", + "which": "1.2.14" + } + }, + "lru-cache": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", + "integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==", + "dev": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + } } }, + "grunt-keepalive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/grunt-keepalive/-/grunt-keepalive-1.0.0.tgz", + "integrity": "sha1-ZkyOGFoNnqGvQYoswLs/FfL4vzM=", + "dev": true + }, "grunt-known-options": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-1.1.0.tgz", @@ -3858,6 +4497,15 @@ "integrity": "sha1-252c5Z4v5J2id+nbwZXD4Rz7FsI=", "dev": true }, + "gruntify-eslint": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/gruntify-eslint/-/gruntify-eslint-4.0.0.tgz", + "integrity": "sha512-wEa2WjMGVDzQbq1QmOiDX51/CfaAIS5xx1oSKIjfWVLl/fYbV7PtfWsUhuaQrPIy1se4Crpg3kZFZndw02l16g==", + "dev": true, + "requires": { + "eslint": "4.17.0" + } + }, "gzip-size": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-1.0.0.tgz", @@ -4002,12 +4650,6 @@ "ansi-regex": "2.1.1" } }, - "has-color": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz", - "integrity": "sha1-ZxRKUmDDT8PMpnfQQdr1L+e3iy8=", - "dev": true - }, "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", @@ -4027,7 +4669,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", - "dev": true, "requires": { "inherits": "2.0.3", "minimalistic-assert": "1.0.0" @@ -4055,7 +4696,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, "requires": { "hash.js": "1.1.3", "minimalistic-assert": "1.0.0", @@ -4096,45 +4736,6 @@ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", "dev": true }, - "htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", - "dev": true, - "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.3.0", - "domutils": "1.5.1", - "entities": "1.0.0", - "readable-stream": "1.1.14" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "0.0.1", - "string_decoder": "0.10.31" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, "http-errors": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", @@ -4187,12 +4788,6 @@ "extend": "3.0.1" } }, - "i": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", - "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=", - "dev": true - }, "iconv-lite": { "version": "0.4.19", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", @@ -4201,7 +4796,12 @@ "ieee754": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", "dev": true }, "imurmurhash": { @@ -4234,17 +4834,10 @@ "wrappy": "1.0.2" } }, - "inherit": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/inherit/-/inherit-2.2.6.tgz", - "integrity": "sha1-8WFLBshUToEo5CKchjR9tzrZeI0=", - "dev": true - }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -4261,6 +4854,105 @@ "source-map": "0.5.7" } }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.0.0", + "chalk": "2.3.0", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.1.0", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, "insert-module-globals": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", @@ -4391,6 +5083,18 @@ "builtin-modules": "1.1.1" } }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -4454,6 +5158,30 @@ "kind-of": "3.2.2" } }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.1" + } + }, + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "1.0.2" + } + }, "is-posix-bracket": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", @@ -4466,11 +5194,38 @@ "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", "dev": true }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "1.0.1" + } + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -4504,6 +5259,16 @@ "isarray": "1.0.0" } }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "dev": true, + "requires": { + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.3" + } + }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -4606,103 +5371,6 @@ "dev": true, "optional": true }, - "jscs": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/jscs/-/jscs-3.0.7.tgz", - "integrity": "sha1-cUG03/W4bjLQ6Z12S4NnZ8MNIBo=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "cli-table": "0.3.1", - "commander": "2.9.0", - "cst": "0.4.10", - "estraverse": "4.2.0", - "exit": "0.1.2", - "glob": "5.0.15", - "htmlparser2": "3.8.3", - "js-yaml": "3.4.6", - "jscs-jsdoc": "2.0.0", - "jscs-preset-wikimedia": "1.0.0", - "jsonlint": "1.6.2", - "lodash": "3.10.1", - "minimatch": "3.0.4", - "natural-compare": "1.2.2", - "pathval": "0.1.1", - "prompt": "0.2.14", - "reserved-words": "0.1.2", - "resolve": "1.1.7", - "strip-bom": "2.0.0", - "strip-json-comments": "1.0.4", - "to-double-quotes": "2.0.0", - "to-single-quotes": "2.0.1", - "vow": "0.4.17", - "vow-fs": "0.3.6", - "xmlbuilder": "3.1.0" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } - }, - "glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", - "dev": true, - "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "js-yaml": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.4.6.tgz", - "integrity": "sha1-a+GyP2JJ9T0pM3D9TRqqY84bTrA=", - "dev": true, - "requires": { - "argparse": "1.0.9", - "esprima": "2.7.3", - "inherit": "2.2.6" - } - }, - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - }, - "pathval": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-0.1.1.tgz", - "integrity": "sha1-CPkRzcqczllCiA2ngXvAtyO2bYI=", - "dev": true - } - } - }, - "jscs-jsdoc": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jscs-jsdoc/-/jscs-jsdoc-2.0.0.tgz", - "integrity": "sha1-9T684CmqMSW9iCkLpQ1k1FEKSHE=", - "dev": true, - "requires": { - "comment-parser": "0.3.2", - "jsdoctypeparser": "1.2.0" - } - }, - "jscs-preset-wikimedia": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/jscs-preset-wikimedia/-/jscs-preset-wikimedia-1.0.0.tgz", - "integrity": "sha1-//VjNCA4/C6IJre7cwnDrjQG/H4=", - "dev": true - }, "jsdoc": { "version": "3.5.5", "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", @@ -4715,7 +5383,7 @@ "escape-string-regexp": "1.0.5", "js2xmlparser": "3.0.0", "klaw": "2.0.0", - "marked": "0.3.6", + "marked": "0.3.12", "mkdirp": "0.5.1", "requizzle": "0.2.1", "strip-json-comments": "2.0.1", @@ -4728,35 +5396,6 @@ "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==", "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", - "dev": true - } - } - }, - "jsdoctypeparser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/jsdoctypeparser/-/jsdoctypeparser-1.2.0.tgz", - "integrity": "sha1-597cFToRhJ/8UUEUSuhqfvDCU5I=", - "dev": true, - "requires": { - "lodash": "3.10.1" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true } } }, @@ -4766,30 +5405,6 @@ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, - "jshint": { - "version": "2.9.5", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.5.tgz", - "integrity": "sha1-HnJSkVzmgbQIJ+4UJIxG006apiw=", - "dev": true, - "requires": { - "cli": "1.0.1", - "console-browserify": "1.1.0", - "exit": "0.1.2", - "htmlparser2": "3.8.3", - "lodash": "3.7.0", - "minimatch": "3.0.4", - "shelljs": "0.3.0", - "strip-json-comments": "1.0.4" - }, - "dependencies": { - "lodash": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz", - "integrity": "sha1-Nni9irmVBXwHreg27S7wh9qBHUU=", - "dev": true - } - } - }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -4811,6 +5426,12 @@ "jsonify": "0.0.0" } }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -4829,16 +5450,6 @@ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", "dev": true }, - "jsonlint": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/jsonlint/-/jsonlint-1.6.2.tgz", - "integrity": "sha1-VzcEUIX1XrRVxosf9OvAG9UOiDA=", - "dev": true, - "requires": { - "JSV": "4.0.2", - "nomnom": "1.8.1" - } - }, "jsonparse": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", @@ -4857,6 +5468,43 @@ "verror": "1.10.0" } }, + "jsx-ast-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", + "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", + "dev": true, + "requires": { + "array-includes": "3.0.3" + } + }, + "just-extend": { + "version": "1.1.27", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", + "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", + "dev": true + }, + "jwk-to-pem": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-1.2.6.tgz", + "integrity": "sha1-1QfOzkAInFJI4J7GgmaiAwqcYyU=", + "requires": { + "asn1.js": "4.9.2", + "elliptic": "git+https://github.com/openpgpjs/elliptic.git#0bd7555723b84ac2b747e00c8cea1e64e99b4f4f", + "safe-buffer": "5.1.1" + }, + "dependencies": { + "asn1.js": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", + "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + } + } + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -4948,12 +5596,42 @@ "strip-bom": "2.0.0" } }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, "lodash": { "version": "4.17.4", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", "dev": true }, + "lodash.cond": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", + "dev": true + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", @@ -4961,9 +5639,9 @@ "dev": true }, "lolex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz", - "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.3.2.tgz", + "integrity": "sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==", "dev": true }, "longest": { @@ -5007,9 +5685,9 @@ "dev": true }, "marked": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", - "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz", + "integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==", "dev": true }, "maxmin": { @@ -5130,17 +5808,21 @@ "mime-db": "1.30.0" } }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { "version": "3.0.4", @@ -5331,15 +6013,9 @@ "optional": true }, "natural-compare": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.2.2.tgz", - "integrity": "sha1-H5bWDjFBysG20FZTzg2urHY69qo=", - "dev": true - }, - "ncp": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-0.4.2.tgz", - "integrity": "sha1-q8xsvT7C7Spyn/bnwfqPAXhKhXQ=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, "negotiator": { @@ -5348,6 +6024,27 @@ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", "dev": true }, + "nise": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.2.2.tgz", + "integrity": "sha512-rvxf+PSZeCKtP0DgmwMmNf1G3I6X1r4WHiP2H88PlIkOkt7mGqufdokjS8caoHBgZzVx0ee/5ytGcGHbZaUw8w==", + "dev": true, + "requires": { + "formatio": "1.2.0", + "just-extend": "1.1.27", + "lolex": "1.6.0", + "path-to-regexp": "1.7.0", + "text-encoding": "0.6.4" + }, + "dependencies": { + "lolex": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz", + "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=", + "dev": true + } + } + }, "node-fetch": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", @@ -5365,41 +6062,6 @@ "write-file-atomic": "1.3.4" } }, - "nomnom": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz", - "integrity": "sha1-IVH3Ikcrp55Qp2/BJbuMjy5Nwqc=", - "dev": true, - "requires": { - "chalk": "0.4.0", - "underscore": "1.6.0" - }, - "dependencies": { - "ansi-styles": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz", - "integrity": "sha1-yxAt8cVvUSPquLZ817mAJ6AnkXg=", - "dev": true - }, - "chalk": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", - "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", - "dev": true, - "requires": { - "ansi-styles": "1.0.0", - "has-color": "0.1.7", - "strip-ansi": "0.1.1" - } - }, - "strip-ansi": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", - "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", - "dev": true - } - } - }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -5448,6 +6110,12 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, "object.omit": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", @@ -5482,6 +6150,15 @@ "wrappy": "1.0.2" } }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.2.0" + } + }, "opn": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/opn/-/opn-4.0.2.tgz", @@ -5560,6 +6237,30 @@ "shell-quote": "1.6.1" } }, + "p-limit": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "1.2.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", @@ -5586,6 +6287,19 @@ "create-hash": "1.1.3", "evp_bytestokey": "1.0.3", "pbkdf2": "3.0.14" + }, + "dependencies": { + "asn1.js": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", + "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + } } }, "parse-glob": { @@ -5636,12 +6350,41 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "dev": true + }, "path-platform": { "version": "0.11.15", "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=", "dev": true }, + "path-to-regexp": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", + "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "dev": true, + "requires": { + "isarray": "0.0.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + } + } + }, "path-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", @@ -5699,10 +6442,19 @@ "pinkie": "2.0.4" } }, - "pkginfo": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", - "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "1.1.2" + } + }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, "portscanner": { @@ -5754,17 +6506,30 @@ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", "dev": true }, - "prompt": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz", - "integrity": "sha1-V3VPZPVD/XsIRXB8gY7OYY8F/9w=", + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, "requires": { - "pkginfo": "0.4.1", - "read": "1.0.7", - "revalidator": "0.1.8", - "utile": "0.2.1", - "winston": "0.8.3" + "asap": "2.0.6" + } + }, + "prop-types": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", + "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", + "dev": true, + "requires": { + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "proto-list": { @@ -5939,15 +6704,6 @@ } } }, - "read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", - "dev": true, - "requires": { - "mute-stream": "0.0.7" - } - }, "read-only-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", @@ -6176,6 +6932,16 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, "requizzle": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", @@ -6183,26 +6949,38 @@ "dev": true, "requires": { "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } } }, - "reserved-words": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/reserved-words/-/reserved-words-0.1.2.tgz", - "integrity": "sha1-AKCUD5jNUBrqqsMWQR2a3FKzGrE=", - "dev": true - }, "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true }, - "revalidator": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", - "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=", + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", "dev": true }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -6229,22 +7007,44 @@ "inherits": "2.0.3" } }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, "rusha": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.7.tgz", - "integrity": "sha1-MGc7fpX6/g6+H+JN1tlf1gX5Tt4=", + "version": "0.8.12", + "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.12.tgz", + "integrity": "sha1-XYOM4fzosUVnTudx6q1byyV15ks=" + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", "dev": true }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "4.0.8" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "samsam": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz", - "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", + "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", "dev": true }, "sauce-tunnel": { @@ -6333,6 +7133,12 @@ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", "dev": true }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, "setprototypeof": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", @@ -6359,6 +7165,21 @@ "sha.js": "2.4.9" } }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, "shell-quote": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", @@ -6371,12 +7192,6 @@ "jsonify": "0.0.0" } }, - "shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", - "dev": true - }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -6390,15 +7205,35 @@ "dev": true }, "sinon": { - "version": "1.17.7", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz", - "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-4.2.2.tgz", + "integrity": "sha512-BEa593xl+IkIc94nKo0O0LauQC/gQy8Gyv4DkzPwF/9DweC5phr1y+42zibCpn9abfkdHxt9r8AhD0R6u9DE/Q==", "dev": true, "requires": { - "formatio": "1.1.1", - "lolex": "1.3.2", - "samsam": "1.1.2", - "util": "0.10.3" + "diff": "3.3.1", + "formatio": "1.2.0", + "lodash.get": "4.4.2", + "lolex": "2.3.2", + "nise": "1.2.2", + "supports-color": "5.1.0", + "type-detect": "4.0.5" + }, + "dependencies": { + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "supports-color": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.1.0.tgz", + "integrity": "sha512-Ry0AwkoKjDpVKK4sV4h6o3UJmNRbjYm2uXhwfj3J56lMVdvnUNqzQVRztOOMGQ++w1K/TjNDFvpJk0F/LoeBCQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } } }, "slash": { @@ -6407,6 +7242,23 @@ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, "slide": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", @@ -6488,12 +7340,6 @@ "tweetnacl": "0.14.5" } }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", - "dev": true - }, "statuses": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", @@ -6597,9 +7443,9 @@ } }, "strip-json-comments": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", - "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, "subarg": { @@ -6634,12 +7480,106 @@ "acorn": "4.0.13" } }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "5.5.0", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", + "lodash": "4.17.4", + "slice-ansi": "1.0.0", + "string-width": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + } + } + }, "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", "dev": true }, + "text-encoding": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -6722,30 +7662,27 @@ } } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, "to-arraybuffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "dev": true }, - "to-double-quotes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-double-quotes/-/to-double-quotes-2.0.0.tgz", - "integrity": "sha1-qvIx1vqUiUn4GTAburRITYWI5Kc=", - "dev": true - }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", "dev": true }, - "to-single-quotes": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/to-single-quotes/-/to-single-quotes-2.0.1.tgz", - "integrity": "sha1-fMKRUfD18sQZRvEZ9ZMv5VQXASU=", - "dev": true - }, "tough-cookie": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", @@ -6820,6 +7757,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "ua-parser-js": { + "version": "0.7.17", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", + "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==", + "dev": true + }, "uglify-js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.2.0.tgz", @@ -6852,9 +7795,9 @@ "dev": true }, "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", "dev": true }, "underscore-contrib": { @@ -6864,6 +7807,14 @@ "dev": true, "requires": { "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", + "dev": true + } } }, "underscore.string": { @@ -6925,40 +7876,12 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "utile": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/utile/-/utile-0.2.1.tgz", - "integrity": "sha1-kwyI6ZCY1iIINMNWy9mncFItkNc=", - "dev": true, - "requires": { - "async": "0.2.10", - "deep-equal": "1.0.1", - "i": "0.3.6", - "mkdirp": "0.5.1", - "ncp": "0.4.2", - "rimraf": "2.2.8" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - } - } - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", "dev": true }, - "uuid": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", - "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=", - "dev": true - }, "validate-npm-package-license": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", @@ -6989,33 +7912,6 @@ "indexof": "0.0.1" } }, - "vow": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/vow/-/vow-0.4.17.tgz", - "integrity": "sha512-A3/9bWFqf6gT0jLR4/+bT+IPTe1mQf+tdsW6+WI5geP9smAp8Kbbu4R6QQCDKZN/8TSCqTlXVQm12QliB4rHfg==", - "dev": true - }, - "vow-fs": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/vow-fs/-/vow-fs-0.3.6.tgz", - "integrity": "sha1-LUxZviLivyYY3fWXq0uqkjvnIA0=", - "dev": true, - "requires": { - "glob": "7.0.6", - "uuid": "2.0.3", - "vow": "0.4.17", - "vow-queue": "0.4.3" - } - }, - "vow-queue": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/vow-queue/-/vow-queue-0.4.3.tgz", - "integrity": "sha512-/poAKDTFL3zYbeQg7cl4BGcfP4sGgXKrHnRFSKj97dteUFu8oyXMwIcdwu8NSx/RmPGIuYx1Bik/y5vU4H/VKw==", - "dev": true, - "requires": { - "vow": "0.4.17" - } - }, "watchify": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/watchify/-/watchify-3.9.0.tgz", @@ -7093,41 +7989,6 @@ "dev": true, "optional": true }, - "winston": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz", - "integrity": "sha1-ZLar9M0Brcrv1QCTk7HY6L7BnbA=", - "dev": true, - "requires": { - "async": "0.2.10", - "colors": "0.6.2", - "cycle": "1.0.3", - "eyes": "0.1.8", - "isstream": "0.1.2", - "pkginfo": "0.3.1", - "stack-trace": "0.0.10" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - }, - "colors": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", - "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=", - "dev": true - }, - "pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", - "dev": true - } - } - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -7150,6 +8011,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, "write-file-atomic": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz", @@ -7160,23 +8030,6 @@ "slide": "1.1.6" } }, - "xmlbuilder": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-3.1.0.tgz", - "integrity": "sha1-LIaIjy1OrehQ+jjKf3Ij9yCVFuE=", - "dev": true, - "requires": { - "lodash": "3.10.1" - }, - "dependencies": { - "lodash": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", - "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", - "dev": true - } - } - }, "xmlcreate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", diff --git a/package.json b/package.json index 5fe7e38a..dda0cfc1 100644 --- a/package.json +++ b/package.json @@ -71,10 +71,10 @@ "zlibjs": "~0.3.1" }, "dependencies": { + "asmcrypto-lite": "git+https://github.com/openpgpjs/asmcrypto-lite.git", + "asn1.js": "^5.0.0", "bn.js": "^4.11.8", "buffer": "^5.0.8", - "asn1.js": "^5.0.0", - "asmcrypto-lite": "git+https://github.com/openpgpjs/asmcrypto-lite.git", "elliptic": "git+https://github.com/openpgpjs/elliptic.git", "es6-promise": "^4.2.4", "jwk-to-pem": "^1.2.6", diff --git a/src/worker/worker.js b/src/worker/worker.js index 2b25f319..3e52f781 100644 --- a/src/worker/worker.js +++ b/src/worker/worker.js @@ -19,6 +19,11 @@ self.window = {}; // to make UMD bundles work +// ES6 Promise polyfill +if (typeof Promise === 'undefined') { + require('es6-promise').polyfill(); +} + importScripts('openpgp.js'); var openpgp = window.openpgp; diff --git a/test/unittests.js b/test/unittests.js index 2e07de4b..29b8100e 100644 --- a/test/unittests.js +++ b/test/unittests.js @@ -1,3 +1,8 @@ +// ES6 Promise polyfill +if (typeof Promise === 'undefined') { + require('es6-promise').polyfill(); +} + (typeof window !== 'undefined' ? window : global).resolves = function(val) { return new Promise(function(res) { res(val); }); }; From 0b198b5b085bbbaffbc60f95fedbf33b93692d15 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Mon, 5 Feb 2018 15:03:01 -0800 Subject: [PATCH 65/75] add extendedDebugging to saucelabs tests --- Gruntfile.js | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index d759d9c1..bf4836d2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -23,7 +23,7 @@ module.exports = function(grunt) { 'src/packet/**/*.js', 'src/type/**/*.js', 'src/worker/**/*.js', - 'src/*.js', + 'src/*.js' ]; // add more over time ... goal should be 100% coverage var version = grunt.option('release'); @@ -44,14 +44,14 @@ module.exports = function(grunt) { browserify: { openpgp: { files: { - 'dist/openpgp.js': [ './src/index.js' ] + 'dist/openpgp.js': ['./src/index.js'] }, options: { browserifyOptions: { standalone: 'openpgp' }, // Don't bundle these packages with openpgp.js - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem' ], + external: ['crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -62,19 +62,19 @@ module.exports = function(grunt) { presets: ["env"] }] ], - plugin: [ 'browserify-derequire' ] + plugin: ['browserify-derequire'] } }, openpgp_debug: { files: { - 'dist/openpgp_debug.js': [ './src/index.js' ] + 'dist/openpgp_debug.js': ['./src/index.js'] }, options: { browserifyOptions: { debug: true, standalone: 'openpgp' }, - external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem' ], + external: ['crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -85,20 +85,20 @@ module.exports = function(grunt) { presets: ["env"] }] ], - plugin: [ 'browserify-derequire' ] + plugin: ['browserify-derequire'] } }, worker: { files: { - 'dist/openpgp.worker.js': [ './src/worker/worker.js' ] + 'dist/openpgp.worker.js': ['./src/worker/worker.js'] } }, unittests: { files: { - 'test/lib/unittests-bundle.js': [ './test/unittests.js' ] + 'test/lib/unittests-bundle.js': ['./test/unittests.js'] }, options: { - external: [ 'buffer', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp' ] + external: ['buffer', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp'] } } }, @@ -139,8 +139,8 @@ module.exports = function(grunt) { uglify: { openpgp: { files: { - 'dist/openpgp.min.js' : [ 'dist/openpgp.js' ], - 'dist/openpgp.worker.min.js' : [ 'dist/openpgp.worker.js' ] + 'dist/openpgp.min.js' : ['dist/openpgp.js'], + 'dist/openpgp.worker.min.js' : ['dist/openpgp.worker.js'] } }, options: { @@ -177,7 +177,7 @@ module.exports = function(grunt) { src: 'test', options: { root: '.', - timeout: 240000, + timeout: 240000 } } }, @@ -187,7 +187,7 @@ module.exports = function(grunt) { reporter: 'spec', timeout: 120000 }, - src: [ 'test/unittests.js' ] + src: ['test/unittests.js'] } }, copy: { @@ -234,9 +234,10 @@ module.exports = function(grunt) { maxRetries: 3, throttled: 2, pollInterval: 4000, - statusCheckAttempts: 200 + statusCheckAttempts: 200, + extendedDebugging : true } - }, + } }, watch: { src: { @@ -247,7 +248,7 @@ module.exports = function(grunt) { files: ['test/*.js', 'test/crypto/**/*.js', 'test/general/**/*.js', 'test/worker/**/*.js'], tasks: ['browserify:unittests'] } - }, + } }); // Load the plugin(s) From d6cc82027ed41ec0a05ea125e875a5fc1d1dad4a Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Mon, 5 Feb 2018 15:46:18 -0800 Subject: [PATCH 66/75] add extendedDebugging correctly --- Gruntfile.js | 1 - travis.sh | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index bf4836d2..28ac76bb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -235,7 +235,6 @@ module.exports = function(grunt) { throttled: 2, pollInterval: 4000, statusCheckAttempts: 200, - extendedDebugging : true } } }, diff --git a/travis.sh b/travis.sh index 4c3be43f..6c743961 100755 --- a/travis.sh +++ b/travis.sh @@ -17,8 +17,8 @@ elif [[ $OPENPGPJSTEST =~ ^end2end-.* ]]; then declare -a capabilities=( "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Firefox\", \"version\":\"34\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Firefox\", \"version\":\"54\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" - "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"37\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" - "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"59\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" + "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"41\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\", \"extendedDebugging\":true}'" + "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"59\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\", \"extendedDebugging\":true}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Internet Explorer\", \"version\":\"11.103\", \"platform\":\"Windows 10\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"MicrosoftEdge\", \"version\":\"15.15063\", \"platform\":\"Windows 10\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Safari\", \"version\":\"8\", \"platform\":\"OS X 10.10\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" From 1d82046bb82cc56a2a36dddebbf4da0b2ccfca75 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Mon, 5 Feb 2018 16:05:39 -0800 Subject: [PATCH 67/75] revert to Chrome 38 --- travis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/travis.sh b/travis.sh index 6c743961..cc1334f9 100755 --- a/travis.sh +++ b/travis.sh @@ -17,7 +17,7 @@ elif [[ $OPENPGPJSTEST =~ ^end2end-.* ]]; then declare -a capabilities=( "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Firefox\", \"version\":\"34\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Firefox\", \"version\":\"54\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" - "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"41\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\", \"extendedDebugging\":true}'" + "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"38\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\", \"extendedDebugging\":true}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Chrome\", \"version\":\"59\", \"platform\":\"OS X 10.12\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\", \"extendedDebugging\":true}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"Internet Explorer\", \"version\":\"11.103\", \"platform\":\"Windows 10\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" "export SELENIUM_BROWSER_CAPABILITIES='{\"browserName\":\"MicrosoftEdge\", \"version\":\"15.15063\", \"platform\":\"Windows 10\", \"maxDuration\":\"7200\", \"commandTimeout\":\"600\", \"idleTimeout\":\"270\"}'" From 89f86d83d54262911e7599a0874f0cd372d007e3 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Mon, 5 Feb 2018 22:17:41 -0800 Subject: [PATCH 68/75] fix obsolete browser tests, add some polyfills --- Gruntfile.js | 15 +++- npm-shrinkwrap.json | 162 ++++++++++++++++++++++++++++++++++++++++---- package.json | 10 ++- src/openpgp.js | 6 ++ test/unittests.html | 2 - 5 files changed, 174 insertions(+), 21 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 28ac76bb..12a980e3 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -98,7 +98,18 @@ module.exports = function(grunt) { 'test/lib/unittests-bundle.js': ['./test/unittests.js'] }, options: { - external: ['buffer', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp'] + external: ['buffer', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp'], + transform: [ + ["babelify", { + global: true, + plugins: ["transform-async-to-generator", + "syntax-async-functions", + "transform-regenerator", + "transform-remove-strict-mode"], + ignore: ['*.min.js'], + presets: ["env"] + }] + ] } } }, @@ -195,7 +206,7 @@ module.exports = function(grunt) { expand: true, flatten: true, cwd: 'node_modules/', - src: ['mocha/mocha.css', 'mocha/mocha.js', 'chai/chai.js', 'whatwg-fetch/fetch.js'], + src: ['mocha/mocha.css', 'mocha/mocha.js'], dest: 'test/lib/' }, zlib: { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index beaebdb7..f92a1f62 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -270,9 +270,9 @@ "dev": true }, "assertion-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "ast-types-flow": { @@ -801,6 +801,24 @@ "regexpu-core": "2.0.0" } }, + "babel-plugin-transform-es3-member-expression-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es3-member-expression-literals/-/babel-plugin-transform-es3-member-expression-literals-6.22.0.tgz", + "integrity": "sha1-cz00RPPsxBvvjtGmpOCWV7iWnrs=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "babel-plugin-transform-es3-property-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es3-property-literals/-/babel-plugin-transform-es3-property-literals-6.22.0.tgz", + "integrity": "sha1-sgeNWELiKr9A9z6M3pzTcRq9V1g=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, "babel-plugin-transform-exponentiation-operator": { "version": "6.24.1", "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", @@ -821,6 +839,11 @@ "regenerator-transform": "0.10.1" } }, + "babel-plugin-transform-remove-strict-mode": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-strict-mode/-/babel-plugin-transform-remove-strict-mode-0.0.2.tgz", + "integrity": "sha1-kTaFqrlUOfOg7YjliPvV6ZeJBXk=" + }, "babel-plugin-transform-runtime": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz", @@ -847,7 +870,7 @@ "dev": true, "requires": { "babel-runtime": "6.26.0", - "core-js": "2.5.1", + "core-js": "2.5.3", "regenerator-runtime": "0.10.5" }, "dependencies": { @@ -897,6 +920,103 @@ "semver": "5.4.1" } }, + "babel-preset-es2015": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.6.0.tgz", + "integrity": "sha1-iLM+WP7JTG695Y3GXs5dFODsJWg=", + "dev": true, + "requires": { + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-regenerator": "6.26.0" + } + }, + "babel-preset-es2015-mod": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/babel-preset-es2015-mod/-/babel-preset-es2015-mod-6.6.0.tgz", + "integrity": "sha1-4QW2LrfBABCQq4YiUpiQTPkMHo4=", + "dev": true, + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "6.7.7", + "babel-plugin-transform-regenerator": "6.6.5", + "babel-preset-es2015": "6.6.0", + "modify-babel-preset": "2.0.2" + }, + "dependencies": { + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.7.7", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.7.7.tgz", + "integrity": "sha1-+lyiAWYXxNcSEj2M/BV4f8qoPzM=", + "dev": true, + "requires": { + "babel-plugin-transform-strict-mode": "6.24.1", + "babel-runtime": "5.8.38", + "babel-template": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.6.5.tgz", + "integrity": "sha1-B5qYK9VuIjXjHuOxetVK66iY1Oc=", + "dev": true, + "requires": { + "babel-core": "6.26.0", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-plugin-transform-es2015-block-scoping": "6.26.0", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-runtime": "5.8.38", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "private": "0.1.8" + } + }, + "babel-runtime": { + "version": "5.8.38", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.38.tgz", + "integrity": "sha1-HAsC62MxL18If/IEUIJ7QlydTBk=", + "dev": true, + "requires": { + "core-js": "1.2.7" + } + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=", + "dev": true + } + } + }, + "babel-preset-es3": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-es3/-/babel-preset-es3-1.0.1.tgz", + "integrity": "sha1-4I3ZUKFnDauLUKvOqpuT09mszR4=", + "dev": true, + "requires": { + "babel-plugin-transform-es3-member-expression-literals": "6.22.0", + "babel-plugin-transform-es3-property-literals": "6.22.0" + } + }, "babel-register": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", @@ -905,7 +1025,7 @@ "requires": { "babel-core": "6.26.0", "babel-runtime": "6.26.0", - "core-js": "2.5.1", + "core-js": "2.5.3", "home-or-tmp": "2.0.0", "lodash": "4.17.4", "mkdirp": "0.5.1", @@ -918,7 +1038,7 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.1", + "core-js": "2.5.3", "regenerator-runtime": "0.11.0" } }, @@ -1544,7 +1664,7 @@ "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { - "assertion-error": "1.0.2", + "assertion-error": "1.1.0", "check-error": "1.0.2", "deep-eql": "3.0.1", "get-func-name": "2.0.0", @@ -1798,10 +1918,9 @@ "dev": true }, "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.3.tgz", + "integrity": "sha1-isw4NFgk8W2DZbfJtCWRaOjtYD4=" }, "core-util-is": { "version": "1.0.2", @@ -5849,9 +5968,9 @@ } }, "mocha": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", - "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", + "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", "dev": true, "requires": { "browser-stdout": "1.3.0", @@ -5912,6 +6031,15 @@ } } }, + "modify-babel-preset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/modify-babel-preset/-/modify-babel-preset-2.0.2.tgz", + "integrity": "sha1-v6UJZp/kn0IiwM4XG6RO0OgVUec=", + "dev": true, + "requires": { + "require-relative": "0.8.7" + } + }, "module-deps": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", @@ -6932,6 +7060,12 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "require-relative": { + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", + "integrity": "sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=", + "dev": true + }, "require-uncached": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", diff --git a/package.json b/package.json index dda0cfc1..89ecf6a0 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,11 @@ "babel-plugin-transform-runtime": "^6.23.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.6.1", + "babel-preset-es2015-mod": "^6.6.0", + "babel-preset-es3": "^1.0.1", "babelify": "^8.0.0", "browserify-derequire": "^0.9.4", - "chai": "~4.1.2", + "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "eslint": "^4.16.0", "eslint-config-airbnb": "^16.1.0", @@ -65,16 +67,18 @@ "grunt-text-replace": "~0.4.0", "gruntify-eslint": "^4.0.0", "istanbul": "^0.4.5", - "mocha": "^4.0.1", + "mocha": "^4.1.0", "sinon": "^4.2.2", - "whatwg-fetch": "~2.0.3", + "whatwg-fetch": "^2.0.3", "zlibjs": "~0.3.1" }, "dependencies": { "asmcrypto-lite": "git+https://github.com/openpgpjs/asmcrypto-lite.git", "asn1.js": "^5.0.0", + "babel-plugin-transform-remove-strict-mode": "0.0.2", "bn.js": "^4.11.8", "buffer": "^5.0.8", + "core-js": "^2.5.3", "elliptic": "git+https://github.com/openpgpjs/elliptic.git", "es6-promise": "^4.2.4", "jwk-to-pem": "^1.2.6", diff --git a/src/openpgp.js b/src/openpgp.js index 22d331ed..be207d02 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -41,6 +41,12 @@ import config from './config/config'; import util from './util'; import AsyncProxy from './worker/async_proxy'; +// Polyfills +require('core-js/fn/array/fill'); +require('core-js/fn/array/find'); +require('core-js/fn/array/from'); +require('core-js/fn/typed/uint8-array'); +require('core-js/fn/symbol'); ////////////////////////// // // diff --git a/test/unittests.html b/test/unittests.html index 47f16d5d..7c3feb07 100644 --- a/test/unittests.html +++ b/test/unittests.html @@ -11,9 +11,7 @@ - -