diff --git a/Gruntfile.js b/Gruntfile.js index f463a02d..110e99ac 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -49,7 +49,7 @@ module.exports = function(grunt) { standalone: 'openpgp' }, // Don't bundle these packages with openpgp.js - external: ['crypto', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], + external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -73,7 +73,7 @@ module.exports = function(grunt) { debug: true, standalone: 'openpgp' }, - external: ['crypto', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], + external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], transform: [ ["babelify", { plugins: ["transform-async-to-generator", @@ -208,12 +208,6 @@ module.exports = function(grunt) { src: ['mocha/mocha.css', 'mocha/mocha.js'], dest: 'test/lib/' }, - zlib: { - expand: true, - cwd: 'node_modules/zlibjs/bin/', - src: ['rawdeflate.min.js','rawinflate.min.js','zlib.min.js'], - dest: 'src/compression/' - }, bzip2: { expand: true, cwd: 'node_modules/compressjs/bin/', @@ -316,7 +310,7 @@ 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', 'copy:bzip2', 'browserify', 'version', 'uglify', 'replace_min']); + grunt.registerTask('default', ['clean', 'copy:bzip2', 'browserify', 'version', 'uglify', 'replace_min']); grunt.registerTask('documentation', ['jsdoc']); // Test/Dev tasks grunt.registerTask('test', ['eslint', 'mochaTest']); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 71833d05..ec966dce 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -112,8 +112,7 @@ "amdefine": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true + "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-escapes": { "version": "3.0.0", @@ -1808,6 +1807,23 @@ "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", "dev": true }, + "compressjs": { + "version": "github:openpgpjs/compressjs#ce5ccbede61f075926081e29573c8a79ddf10d88", + "requires": { + "amdefine": "1.0.1", + "commander": "2.8.1" + }, + "dependencies": { + "commander": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", + "requires": { + "graceful-readlink": "1.0.1" + } + } + } + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4129,6 +4145,11 @@ "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=" + }, "growl": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", @@ -6186,8 +6207,7 @@ "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", - "dev": true + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==" }, "parents": { "version": "1.0.1", @@ -7979,12 +7999,6 @@ "requires": { "camelcase": "3.0.0" } - }, - "zlibjs": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", - "integrity": "sha1-UBl+2yihxCymWcyLTmqd3W1ERVQ=", - "dev": true } } } diff --git a/package.json b/package.json index ae99203d..a1d29484 100644 --- a/package.json +++ b/package.json @@ -70,21 +70,21 @@ "istanbul": "^0.4.5", "mocha": "^5.0.0", "sinon": "^4.3.0", - "whatwg-fetch": "^2.0.3", - "zlibjs": "~0.3.1" + "whatwg-fetch": "^2.0.3" }, "dependencies": { "asmcrypto-lite": "github:openpgpjs/asmcrypto-lite", "asn1.js": "^5.0.0", "bn.js": "^4.11.8", "buffer": "^5.0.8", + "compressjs": "github:openpgpjs/compressjs.git", "elliptic": "github:openpgpjs/elliptic.git", "hash.js": "^1.1.3", "jwk-to-pem": "^1.2.6", "node-fetch": "^1.7.3", "node-localstorage": "~1.3.0", - "rusha": "^0.8.12", - "compressjs": "github:openpgpjs/compressjs.git" + "pako": "^1.0.6", + "rusha": "^0.8.12" }, "repository": { "type": "git", diff --git a/src/config/config.js b/src/config/config.js index 1a9fccf8..6f430f97 100644 --- a/src/config/config.js +++ b/src/config/config.js @@ -54,7 +54,7 @@ export default { */ password_collision_check: false, - /** @property {Boolean} use_native Use native Node.js crypto and WebCrypto API's when available */ + /** @property {Boolean} use_native Use native Node.js crypto/zlib and WebCrypto APIs when available */ use_native: true, /** @property {Boolean} Use transferable objects between the Web Worker and main thread */ zero_copy: false, diff --git a/src/crypto/gcm.js b/src/crypto/gcm.js index 9d505150..6cb04d2f 100644 --- a/src/crypto/gcm.js +++ b/src/crypto/gcm.js @@ -22,7 +22,6 @@ import asmCrypto from 'asmcrypto-lite'; import util from '../util.js'; -import config from '../config'; const webCrypto = util.getWebCrypto(); // no GCM support in IE11, Safari 9 const nodeCrypto = util.getNodeCrypto(); @@ -45,9 +44,9 @@ function encrypt(cipher, plaintext, key, iv) { return Promise.reject(new Error('GCM mode supports only AES cipher')); } - if (webCrypto && config.use_native && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (webCrypto && 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 + } else if (nodeCrypto) { // Node crypto library return nodeEncrypt(plaintext, key, iv); } // asm.js fallback return Promise.resolve(asmCrypto.AES_GCM.encrypt(plaintext, key, iv)); @@ -66,9 +65,9 @@ function decrypt(cipher, ciphertext, key, iv) { return Promise.reject(new Error('GCM mode supports only AES cipher')); } - if (webCrypto && config.use_native && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support + if (webCrypto && key.length !== 24) { // WebCrypto (no 192 bit support) see: https://www.chromium.org/blink/webcrypto#TOC-AES-support return webDecrypt(ciphertext, key, iv); - } else if (nodeCrypto && config.use_native) { // Node crypto library + } else if (nodeCrypto) { // Node crypto library return nodeDecrypt(ciphertext, key, iv); } // asm.js fallback return Promise.resolve(asmCrypto.AES_GCM.decrypt(ciphertext, key, iv)); diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 9c8c1b4c..1814f615 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -29,7 +29,6 @@ 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'; import util from '../../../util'; import OID from '../../../type/oid'; @@ -45,7 +44,7 @@ webCurves = { 'p384': 'P-384', 'p521': 'P-521' }; -if (nodeCrypto && config.use_native) { +if (nodeCrypto) { const knownCurves = nodeCrypto.getCurves(); nodeCurves = { 'secp256k1': knownCurves.includes('secp256k1') ? 'secp256k1' : undefined, @@ -149,7 +148,7 @@ Curve.prototype.keyFromPublic = function (pub) { Curve.prototype.genKeyPair = async function () { let keyPair; - if (webCrypto && config.use_native && this.web) { + if (webCrypto && this.web) { // If browser doesn't support a curve, we'll catch it try { keyPair = await webGenKeyPair(this.name); @@ -157,7 +156,7 @@ Curve.prototype.genKeyPair = async function () { } catch (err) { util.print_debug("Browser did not support signing: " + err.message); } - } else if (nodeCrypto && config.use_native && this.node) { + } else if (nodeCrypto && this.node) { keyPair = await nodeGenKeyPair(this.name); 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 7e3861fa..f2d42ba0 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -23,7 +23,6 @@ * @requires crypto/hash * @requires util * @requires enums - * @requires config * @requires encoding/base64 * @requires jwk-to-pem * @requires asn1.js @@ -35,7 +34,6 @@ import BigInteger from '../jsbn'; 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(); @@ -66,14 +64,14 @@ KeyPair.prototype.sign = async function (message, hash_algo) { if (util.isString(message)) { message = util.str2Uint8Array(message); } - if (webCrypto && config.use_native && this.curve.web) { + if (webCrypto && this.curve.web) { // 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) { + } else if (nodeCrypto && this.curve.node) { return nodeSign(this.curve, hash_algo, message, this.keyPair); } const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); @@ -84,14 +82,14 @@ KeyPair.prototype.verify = async function (message, signature, hash_algo) { if (util.isString(message)) { message = util.str2Uint8Array(message); } - if (webCrypto && config.use_native && this.curve.web) { + if (webCrypto && this.curve.web) { // 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) { + } else if (nodeCrypto && this.curve.node) { return nodeVerify(this.curve, hash_algo, signature, message, this.keyPair.getPublic()); } const digest = (typeof hash_algo === 'undefined') ? message : hash.digest(hash_algo, message); diff --git a/src/crypto/random.js b/src/crypto/random.js index 55a4c8a3..0c8f43c2 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -26,6 +26,7 @@ import type_mpi from '../type/mpi.js'; import util from '../util.js'; +// Do not use util.getNodeCrypto because we need this regardless of use_native setting const nodeCrypto = util.detectNode() && require('crypto'); export default { diff --git a/src/packet/compressed.js b/src/packet/compressed.js index f9e40fa3..258c605f 100644 --- a/src/packet/compressed.js +++ b/src/packet/compressed.js @@ -30,13 +30,54 @@ * @module packet/compressed */ +import pako from 'pako'; import enums from '../enums.js'; import util from '../util.js'; -import Zlib from '../compression/zlib.min.js'; -import RawInflate from '../compression/rawinflate.min.js'; -import RawDeflate from '../compression/rawdeflate.min.js'; import Bzip2 from '../compression/bzip2.build.js'; +const nodeZlib = util.getNodeZlib(); +const Buffer = util.getNodeBuffer(); + +function pako_zlib(constructor, options = {}) { + return function(data) { + const obj = new constructor(options); + obj.push(data, true); + return obj.result; + }; +} + +let compress_fns; +let decompress_fns; +if (nodeZlib) { // Use Node native zlib for DEFLATE compression/decompression + compress_fns = { + // eslint-disable-next-line no-sync + zip: nodeZlib.deflateRawSync, + // eslint-disable-next-line no-sync + zlib: nodeZlib.deflateSync, + bzip2: Bzip2.compressFile + }; + + decompress_fns = { + // eslint-disable-next-line no-sync + zip: nodeZlib.inflateRawSync, + // eslint-disable-next-line no-sync + zlib: nodeZlib.inflateSync, + bzip2: Bzip2.decompressFile + }; +} else { // Use JS fallbacks + compress_fns = { + zip: pako_zlib(pako.Deflate, { raw: true }), + zlib: pako_zlib(pako.Deflate), + bzip2: Bzip2.compressFile + }; + + decompress_fns = { + zip: pako_zlib(pako.Inflate, { raw: true }), + zlib: pako_zlib(pako.Inflate), + bzip2: Bzip2.decompressFile + }; +} + /** * @constructor */ @@ -97,65 +138,22 @@ Compressed.prototype.write = function () { * read by read_packet */ Compressed.prototype.decompress = function () { - let decompressed; - let inflate; - switch (this.algorithm) { - case 'uncompressed': - decompressed = this.compressed; - break; - - case 'zip': - inflate = new RawInflate.Zlib.RawInflate(this.compressed); - decompressed = inflate.decompress(); - break; - - case 'zlib': - inflate = new Zlib.Zlib.Inflate(this.compressed); - decompressed = inflate.decompress(); - break; - - case 'bzip2': - decompressed = Bzip2.decompressFile(this.compressed); - break; - - default: - throw new Error("Compression algorithm unknown :" + this.algorithm); + if (!decompress_fns[this.algorithm]) { + throw new Error("Compression algorithm unknown :" + this.algorithm); } - this.packets.read(decompressed); + this.packets.read(decompress_fns[this.algorithm](this.compressed)); }; /** * Compress the packet data (member decompressedData) */ Compressed.prototype.compress = function () { - let deflate; - const uncompressed = this.packets.write(); - switch (this.algorithm) { - case 'uncompressed': - // - Uncompressed - this.compressed = uncompressed; - break; - - case 'zip': - // - ZIP [RFC1951] - deflate = new RawDeflate.Zlib.RawDeflate(uncompressed); - this.compressed = deflate.compress(); - break; - - case 'zlib': - // - ZLIB [RFC1950] - deflate = new Zlib.Zlib.Deflate(uncompressed); - this.compressed = deflate.compress(); - break; - - case 'bzip2': - this.compressed = Bzip2.compressFile(uncompressed); - break; - - default: - throw new Error("Compression algorithm unknown :" + this.type); + if (!compress_fns[this.algorithm]) { + throw new Error("Compression algorithm unknown :" + this.algorithm); } + + this.compressed = compress_fns[this.algorithm](this.packets.write()); }; diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index dfddfaa2..dc43541e 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -28,7 +28,6 @@ * @requires crypto * @requires util * @requires enums - * @requires config * @module packet/sym_encrypted_integrity_protected */ diff --git a/src/signature.js b/src/signature.js index 81f916f2..0b2b4156 100644 --- a/src/signature.js +++ b/src/signature.js @@ -16,7 +16,6 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * @requires config * @requires crypto * @requires encoding/armor * @requires enums diff --git a/src/util.js b/src/util.js index 149f26b5..58a6d0a2 100644 --- a/src/util.js +++ b/src/util.js @@ -587,6 +587,13 @@ export default { // otherwise, it gets replaced with the browserified version // eslint-disable-next-line no-useless-concat, import/no-dynamic-require return require('buf'+'fer').Buffer; - } + }, + getNodeZlib: function() { + if (!this.detectNode() || !config.use_native) { + return; + } + + return require('zlib'); + } };