diff --git a/.jsdocrc.js b/.jsdocrc.js new file mode 100644 index 00000000..f4b88806 --- /dev/null +++ b/.jsdocrc.js @@ -0,0 +1,3 @@ +module.exports = { + plugins: ['plugins/markdown'] +}; diff --git a/Gruntfile.js b/Gruntfile.js index f8f210df..913eb952 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -52,6 +52,8 @@ module.exports = function(grunt) { external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], transform: [ ["babelify", { + global: true, + only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/, // Only babelify asmcrypto in node_modules plugins: ["transform-async-to-generator", "syntax-async-functions", "transform-regenerator", @@ -76,6 +78,8 @@ module.exports = function(grunt) { external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], transform: [ ["babelify", { + global: true, + only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/, // Only babelify asmcrypto in node_modules plugins: ["transform-async-to-generator", "syntax-async-functions", "transform-regenerator", @@ -177,6 +181,7 @@ module.exports = function(grunt) { dist: { src: ['README.md', 'src'], options: { + configure: '.jsdocrc.js', destination: 'doc', recurse: true } @@ -263,7 +268,7 @@ module.exports = function(grunt) { // Load the plugin(s) grunt.loadNpmTasks('grunt-browserify'); - grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-uglify-es'); grunt.loadNpmTasks('grunt-text-replace'); grunt.loadNpmTasks('grunt-jsbeautifier'); grunt.loadNpmTasks('grunt-jsdoc'); diff --git a/package.json b/package.json index a1d29484..1ed9f141 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "license": "LGPL-3.0+", "homepage": "https://openpgpjs.org/", "engines": { - "node": ">=0.8" + "node": ">= 8.0.0" }, "keywords": [ "crypto", @@ -57,11 +57,10 @@ "grunt-contrib-clean": "~1.1.0", "grunt-contrib-connect": "~1.0.2", "grunt-contrib-copy": "~1.0.0", - "grunt-contrib-uglify": "~3.2.1", + "grunt-contrib-uglify-es": "^3.3.0", "grunt-contrib-watch": "^1.0.0", "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", @@ -73,12 +72,12 @@ "whatwg-fetch": "^2.0.3" }, "dependencies": { - "asmcrypto-lite": "github:openpgpjs/asmcrypto-lite", + "asmcrypto.js": "^0.22.0", "asn1.js": "^5.0.0", "bn.js": "^4.11.8", "buffer": "^5.0.8", - "compressjs": "github:openpgpjs/compressjs.git", - "elliptic": "github:openpgpjs/elliptic.git", + "compressjs": "github:openpgpjs/compressjs", + "elliptic": "github:openpgpjs/elliptic", "hash.js": "^1.1.3", "jwk-to-pem": "^1.2.6", "node-fetch": "^1.7.3", diff --git a/src/crypto/aes_kw.js b/src/crypto/aes_kw.js index edf72994..73a69f36 100644 --- a/src/crypto/aes_kw.js +++ b/src/crypto/aes_kw.js @@ -126,7 +126,7 @@ function pack() { return new Uint8Array(buffer); } -module.exports = { - wrap: wrap, - unwrap: unwrap +export default { + wrap, + unwrap }; diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js index f99cabf6..e23d513a 100644 --- a/src/crypto/cipher/aes.js +++ b/src/crypto/cipher/aes.js @@ -1,8 +1,9 @@ /** + * @requires asmcrypto.js * @module crypto/cipher/aes */ -import asmCrypto from 'asmcrypto-lite'; +import { AES_ECB } from 'asmcrypto.js/src/aes/ecb/exports'; // TODO use webCrypto or nodeCrypto when possible. export default function aes(length) { @@ -11,12 +12,12 @@ export default function aes(length) { this.encrypt = function(block) { block = Uint8Array.from(block); - return Array.from(asmCrypto.AES_ECB.encrypt(block, this.key, false)); + return Array.from(AES_ECB.encrypt(block, this.key, false)); }; this.decrypt = function(block) { block = Uint8Array.from(block); - return Array.from(asmCrypto.AES_ECB.decrypt(block, this.key, false)); + return Array.from(AES_ECB.decrypt(block, this.key, false)); }; }; diff --git a/src/crypto/cipher/des.js b/src/crypto/cipher/des.js index b346a7bf..fd9ebab7 100644 --- a/src/crypto/cipher/des.js +++ b/src/crypto/cipher/des.js @@ -436,7 +436,7 @@ function des_removePadding(message, padding) { // added by Recurity Labs -function Des(key) { +function TripleDES(key) { this.key = []; for (let i = 0; i < 3; i++) { @@ -458,13 +458,12 @@ function Des(key) { }; } -Des.keySize = Des.prototype.keySize = 24; -Des.blockSize = Des.prototype.blockSize = 8; +TripleDES.keySize = TripleDES.prototype.keySize = 24; +TripleDES.blockSize = TripleDES.prototype.blockSize = 8; -// This is "original" DES - Des is actually Triple DES. -// This is only exported so we can unit test. +// This is "original" DES -function OriginalDes(key) { +function DES(key) { this.key = key; this.encrypt = function(block, padding) { @@ -478,9 +477,4 @@ function OriginalDes(key) { }; } -export default { - /** @static */ - des: Des, - /** @static */ - originalDes: OriginalDes -}; +export default { DES, TripleDES }; diff --git a/src/crypto/cipher/index.js b/src/crypto/cipher/index.js index 1cf71cff..9b754c04 100644 --- a/src/crypto/cipher/index.js +++ b/src/crypto/cipher/index.js @@ -1,26 +1,27 @@ /** * @requires crypto/cipher/aes - * @requires crypto/cipher/blowfish + * @requires crypto/cipher/des * @requires crypto/cipher/cast5 * @requires crypto/cipher/twofish + * @requires crypto/cipher/blowfish * @module crypto/cipher */ -import aes from './aes.js'; -import desModule from './des.js'; -import cast5 from './cast5.js'; -import twofish from './twofish.js'; -import blowfish from './blowfish.js'; +import aes from './aes'; +import des from './des.js'; +import cast5 from './cast5'; +import twofish from './twofish'; +import blowfish from './blowfish'; export default { /** @see module:crypto/cipher/aes */ aes128: aes(128), aes192: aes(192), aes256: aes(256), - /** @see module:crypto/cipher/des.originalDes */ - des: desModule.originalDes, - /** @see module:crypto/cipher/des.des */ - tripledes: desModule.des, + /** @see module:crypto/cipher/des~DES */ + des: des.DES, + /** @see module:crypto/cipher/des~TripleDES */ + tripledes: des.TripleDES, /** @see module:crypto/cipher/cast5 */ cast5: cast5, /** @see module:crypto/cipher/twofish */ diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 4cee1f7e..72680111 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -18,24 +18,27 @@ // The GPG4Browsers crypto interface /** - * @requires crypto/cipher * @requires crypto/public_key + * @requires crypto/cipher * @requires crypto/random * @requires type/ecdh_symkey * @requires type/kdf_params * @requires type/mpi * @requires type/oid + * @requires enums + * @requires util * @module crypto/crypto */ -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'; - +import cipher from './cipher'; +import random from './random'; +import type_ecdh_symkey from '../type/ecdh_symkey'; +import type_kdf_params from '../type/kdf_params'; +import type_mpi from '../type/mpi'; +import type_oid from '../type/oid'; +import enums from '../enums'; +import util from '../util'; function constructParams(types, data) { return types.map(function(type, i) { @@ -48,41 +51,43 @@ function constructParams(types, data) { 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 https://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 + * Encrypts data using specified algorithm and public key parameters. + * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {Array} pub_params Algorithm-specific public key parameters + * @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) { + publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) { const types = this.getEncSessionKeyParamTypes(algo); return (async function() { - let m; switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': { - const rsa = new publicKey.rsa(); - const n = publicParams[0].toBigInteger(); - const e = publicParams[1].toBigInteger(); - m = data.toBigInteger(); - return constructParams(types, [rsa.encrypt(m, e, n)]); + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: { + const m = data.toBN(); + const n = pub_params[0].toBN(); + const e = pub_params[1].toBN(); + const res = await publicKey.rsa.encrypt(m, n, e); + return constructParams(types, [res]); } - case 'elgamal': { - const elgamal = new publicKey.elgamal(); - const p = publicParams[0].toBigInteger(); - const g = publicParams[1].toBigInteger(); - const y = publicParams[2].toBigInteger(); - m = data.toBigInteger(); - return constructParams(types, elgamal.encrypt(m, g, p, y)); + case enums.publicKey.elgamal: { + const m = data.toBN(); + const p = pub_params[0].toBN(); + const g = pub_params[1].toBN(); + const y = pub_params[2].toBN(); + const res = await publicKey.elgamal.encrypt(m, p, g, y); + return constructParams(types, [res.c1, res.c2]); } - case 'ecdh': { - const { ecdh } = publicKey.elliptic; - const curve = publicParams[0]; - const kdf_params = publicParams[2]; - const R = publicParams[1].toBigInteger(); - const res = await ecdh.encrypt(curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint); + case enums.publicKey.ecdh: { + const oid = pub_params[0]; + const Q = pub_params[1].toUint8Array(); + const kdf_params = pub_params[2]; + const res = await publicKey.elliptic.ecdh.encrypt( + oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint); return constructParams(types, [res.V, res.C]); } default: @@ -92,52 +97,50 @@ 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 https://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 + * Decrypts data using specified algorithm and private key parameters. + * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {Array} key_params Algorithm-specific public, private key parameters + * @param {Array} + data_params encrypted session key parameters + * @param {String} fingerprint Recipient fingerprint + * @return {module:type/mpi} An MPI containing the decrypted data */ - - publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) { - let p; + publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) { return new type_mpi(await (async function() { switch (algo) { - case 'rsa_encrypt_sign': - case 'rsa_encrypt': { - const rsa = new publicKey.rsa(); - // 0 and 1 are the public key. - const n = keyIntegers[0].toBigInteger(); - const e = keyIntegers[1].toBigInteger(); - // 2 to 5 are the private key. - const d = keyIntegers[2].toBigInteger(); - p = keyIntegers[3].toBigInteger(); - const q = keyIntegers[4].toBigInteger(); - const u = keyIntegers[5].toBigInteger(); - const m = dataIntegers[0].toBigInteger(); - return rsa.decrypt(m, n, e, d, p, q, u); + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: { + const c = data_params[0].toBN(); + const n = key_params[0].toBN(); // n = pq + const e = key_params[1].toBN(); + const d = key_params[2].toBN(); // de = 1 mod (p-1)(q-1) + const p = key_params[3].toBN(); + const q = key_params[4].toBN(); + const u = key_params[5].toBN(); // q^-1 mod p + return publicKey.rsa.decrypt(c, n, e, d, p, q, u); } - case 'elgamal': { - const elgamal = new publicKey.elgamal(); - const x = keyIntegers[3].toBigInteger(); - const c1 = dataIntegers[0].toBigInteger(); - const c2 = dataIntegers[1].toBigInteger(); - p = keyIntegers[0].toBigInteger(); - return elgamal.decrypt(c1, c2, p, x); + case enums.publicKey.elgamal: { + const c1 = data_params[0].toBN(); + const c2 = data_params[1].toBN(); + const p = key_params[0].toBN(); + const x = key_params[3].toBN(); + return publicKey.elgamal.decrypt(c1, c2, p, x); } - case 'ecdh': { - const { ecdh } = publicKey.elliptic; - const curve = keyIntegers[0]; - const kdf_params = keyIntegers[2]; - const V = dataIntegers[0].toBigInteger(); - const C = dataIntegers[1].data; - const r = keyIntegers[3].toBigInteger(); - return ecdh.decrypt(curve.oid, kdf_params.cipher, kdf_params.hash, V, C, r, fingerprint); + case enums.publicKey.ecdh: { + const oid = key_params[0]; + const kdf_params = key_params[2]; + const V = data_params[0].toUint8Array(); + const C = data_params[1].data; + const d = key_params[3].toUint8Array(); + return publicKey.elliptic.ecdh.decrypt( + oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint); } default: - return null; + throw new Error('Invalid public key encryption algorithm.'); } }())); }, @@ -148,31 +151,31 @@ export default { */ getPrivKeyParamTypes: function(algo) { switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': - // Algorithm-Specific Fields for RSA secret keys: - // - multiprecision integer (MPI) of RSA secret exponent d. - // - 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. + // Algorithm-Specific Fields for RSA secret keys: + // - multiprecision integer (MPI) of RSA secret exponent d. + // - 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. + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_sign: return [type_mpi, type_mpi, type_mpi, type_mpi]; - case 'elgamal': - // Algorithm-Specific Fields for Elgamal secret keys: - // - MPI of Elgamal secret exponent x. + // Algorithm-Specific Fields for Elgamal secret keys: + // - MPI of Elgamal secret exponent x. + case enums.publicKey.elgamal: return [type_mpi]; - case 'dsa': - // Algorithm-Specific Fields for DSA secret keys: - // - MPI of DSA secret exponent x. + // Algorithm-Specific Fields for DSA secret keys: + // - MPI of DSA secret exponent x. + case enums.publicKey.dsa: 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. + // Algorithm-Specific Fields for ECDSA or ECDH secret keys: + // - MPI of an integer representing the secret key. + case enums.publicKey.ecdh: + case enums.publicKey.ecdsa: + case enums.publicKey.eddsa: return [type_mpi]; default: - throw new Error('Unknown algorithm'); + throw new Error('Invalid public key encryption algorithm.'); } }, @@ -181,41 +184,41 @@ export default { * @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; - // - an MPI of RSA public encryption exponent e. switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': + // Algorithm-Specific Fields for RSA public keys: + // - a multiprecision integer (MPI) of RSA public modulus n; + // - an MPI of RSA public encryption exponent e. + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_sign: 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': + // 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 enums.publicKey.elgamal: 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': + // 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 enums.publicKey.dsa: 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': + // Algorithm-Specific Fields for ECDSA/EdDSA public keys: + // - OID of curve; + // - MPI of EC point representing public key. + case enums.publicKey.ecdsa: + case enums.publicKey.eddsa: 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': + // 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 enums.publicKey.ecdh: return [type_oid, type_mpi, type_kdf_params]; default: - throw new Error('Unknown algorithm.'); + throw new Error('Invalid public key encryption algorithm.'); } }, @@ -225,65 +228,67 @@ export default { */ 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': + // Algorithm-Specific Fields for RSA encrypted session keys: + // - MPI of RSA encrypted value m**e mod n. + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: 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': + // 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 enums.publicKey.elgamal: 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': + // Algorithm-Specific Fields for ECDH encrypted session keys: + // - MPI containing the ephemeral key used to establish the shared secret + // - ECDH Symmetric Key + case enums.publicKey.ecdh: return [type_mpi, type_ecdh_symkey]; - default: - throw new Error('Unknown algorithm.'); + throw new Error('Invalid public key encryption algorithm.'); } }, /** Generate algorithm-specific key parameters - * @param {String} algo The public key algorithm - * @return {Array} The array of parameters + * @param {String} algo The public key algorithm + * @param {Integer} bits Bit length for RSA keys + * @param {module:type/oid} oid Object identifier for ECC keys + * @return {Array} The array of parameters */ - generateParams: function(algo, bits, curve) { - const types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); + generateParams: function(algo, bits, oid) { + const types = [].concat(this.getPubKeyParamTypes(algo), this.getPrivKeyParamTypes(algo)); switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': { - //remember "publicKey" refers to the crypto/public_key dir - const rsa = new publicKey.rsa(); - return rsa.generate(bits, "10001").then(function(keyObject) { - return constructParams(types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u]); + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_sign: { + return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { + return constructParams( + types, [keyObject.n, keyObject.e, keyObject.d, keyObject.p, keyObject.q, keyObject.u] + ); }); } - case 'ecdsa': - case 'eddsa': - return publicKey.elliptic.generate(curve).then(function (keyObject) { + case enums.publicKey.dsa: + case enums.publicKey.elgamal: + throw new Error('Unsupported algorithm for key generation.'); + case enums.publicKey.ecdsa: + case enums.publicKey.eddsa: + return publicKey.elliptic.generate(oid).then(function (keyObject) { return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]); }); - - case 'ecdh': - return publicKey.elliptic.generate(curve).then(function (keyObject) { + case enums.publicKey.ecdh: + return publicKey.elliptic.generate(oid).then(function (keyObject) { return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); }); - default: - throw new Error('Unsupported algorithm for key generation.'); + throw new Error('Invalid public key algorithm.'); } }, /** - * generate random byte prefix as string for the specified algorithm - * @param {module:enums.symmetric} algo Algorithm to use (see {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2}) - * @return {Uint8Array} Random bytes with length equal to the block + * Generates a random byte prefix for the specified algorithm + * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms. + * @param {module:enums.symmetric} algo Symmetric encryption algorithm + * @return {Uint8Array} Random bytes with length equal to the block * size of the cipher */ getPrefixRandom: function(algo) { diff --git a/src/crypto/gcm.js b/src/crypto/gcm.js index 6cb04d2f..b50325d1 100644 --- a/src/crypto/gcm.js +++ b/src/crypto/gcm.js @@ -18,10 +18,15 @@ /** * @fileoverview This module wraps native AES-GCM en/decryption for both * the WebCrypto api as well as node.js' crypto api. + * @requires asmcrypto.js + * @requires config + * @requires util + * @module crypto/gcm */ -import asmCrypto from 'asmcrypto-lite'; -import util from '../util.js'; +import { AES_GCM } from 'asmcrypto.js/src/aes/gcm/exports'; +import config from '../config'; +import util from '../util'; const webCrypto = util.getWebCrypto(); // no GCM support in IE11, Safari 9 const nodeCrypto = util.getNodeCrypto(); @@ -49,7 +54,7 @@ function encrypt(cipher, plaintext, key, iv) { } else if (nodeCrypto) { // Node crypto library return nodeEncrypt(plaintext, key, iv); } // asm.js fallback - return Promise.resolve(asmCrypto.AES_GCM.encrypt(plaintext, key, iv)); + return Promise.resolve(AES_GCM.encrypt(plaintext, key, iv)); } /** @@ -70,7 +75,7 @@ function decrypt(cipher, ciphertext, key, iv) { } else if (nodeCrypto) { // Node crypto library return nodeDecrypt(ciphertext, key, iv); } // asm.js fallback - return Promise.resolve(asmCrypto.AES_GCM.decrypt(ciphertext, key, iv)); + return Promise.resolve(AES_GCM.decrypt(ciphertext, key, iv)); } export default { diff --git a/src/crypto/hash/index.js b/src/crypto/hash/index.js index 7f1455fb..2cea7162 100644 --- a/src/crypto/hash/index.js +++ b/src/crypto/hash/index.js @@ -1,18 +1,20 @@ /** - * @requires crypto/hash/sha + * @requires rusha + * @requires asmcrypto.js + * @requires hash.js * @requires crypto/hash/md5 * @requires util * @module crypto/hash */ import Rusha from 'rusha'; -import asmCrypto from 'asmcrypto-lite'; +import { SHA256 } from 'asmcrypto.js/src/hash/sha256/exports'; import sha224 from 'hash.js/lib/hash/sha/224'; import sha384 from 'hash.js/lib/hash/sha/384'; import sha512 from 'hash.js/lib/hash/sha/512'; import { ripemd160 } from 'hash.js/lib/hash/ripemd'; import md5 from './md5'; -import util from '../../util.js'; +import util from '../../util'; const rusha = new Rusha(); const nodeCrypto = util.getNodeCrypto(); @@ -28,7 +30,7 @@ function node_hash(type) { function hashjs_hash(hash) { return function(data) { - return util.str2Uint8Array(util.hex2bin(hash().update(data).digest('hex'))); + return util.str_to_Uint8Array(util.hex_to_str(hash().update(data).digest('hex'))); }; } @@ -49,12 +51,12 @@ if (nodeCrypto) { // Use Node native crypto for all hash functions md5: md5, /** @see module:rusha */ sha1: function(data) { - return util.str2Uint8Array(util.hex2bin(rusha.digest(data))); + return util.str_to_Uint8Array(util.hex_to_str(rusha.digest(data))); }, /** @see module:hash.js */ sha224: hashjs_hash(sha224), /** @see module:asmcrypto */ - sha256: asmCrypto.SHA256.bytes, + sha256: SHA256.bytes, /** @see module:hash.js */ sha384: hashjs_hash(sha384), // TODO, benchmark this vs asmCrypto's SHA512 diff --git a/src/crypto/hash/md5.js b/src/crypto/hash/md5.js index 6750d41b..a35979b3 100644 --- a/src/crypto/hash/md5.js +++ b/src/crypto/hash/md5.js @@ -24,8 +24,8 @@ import util from '../../util.js'; * @param {String} entree string to hash */ export default function(entree) { - const hex = md5(util.Uint8Array2str(entree)); - const bin = util.str2Uint8Array(util.hex2bin(hex)); + const hex = md5(util.Uint8Array_to_str(entree)); + const bin = util.str_to_Uint8Array(util.hex_to_str(hex)); return bin; } diff --git a/src/crypto/hash/sha.js b/src/crypto/hash/sha.js deleted file mode 100644 index 10997513..00000000 --- a/src/crypto/hash/sha.js +++ /dev/null @@ -1,1472 +0,0 @@ -/** - * @preserve A JavaScript implementation of the SHA family of hashes, as - * defined in FIPS PUB 180-2 as well as the corresponding HMAC implementation - * as defined in FIPS PUB 198a - * - * Copyright Brian Turek 2008-2015 - * Distributed under the BSD License - * See https://caligatio.github.com/jsSHA/ for more information - * - * Several functions taken from Paul Johnston - */ - -/** - * SUPPORTED_ALGS is the stub for a compile flag that will cause pruning of - * functions that are not needed when a limited number of SHA families are - * selected - * - * @define {number} ORed value of SHA variants to be supported - * 1 = SHA-1, 2 = SHA-224/SHA-256, 4 = SHA-384/SHA-512 - */ - -const SUPPORTED_ALGS = 4 | 2 | 1; - -/** - * Int_64 is a object for 2 32-bit numbers emulating a 64-bit number - * - * @private - * @constructor - * @this {Int_64} - * @param {number} msint_32 The most significant 32-bits of a 64-bit number - * @param {number} lsint_32 The least significant 32-bits of a 64-bit number - */ -function Int_64(msint_32, lsint_32) { - this.highOrder = msint_32; - this.lowOrder = lsint_32; -} - -/** - * Convert a string to an array of big-endian words - * - * @private - * @param {string} str String to be converted to binary representation - * @param {string} utfType The Unicode type, UTF8 or UTF16BE, UTF16LE, to - * use to encode the source string - * @return {{value : Array., binLen : number}} Hash list where - * "value" contains the output number array and "binLen" is the binary - * length of "value" - */ -function str2binb(str, utfType) { - const bin = []; - let codePnt; - let binArr = []; - let byteCnt = 0; - let i; - let j; - let offset; - - if (utfType === "UTF8") { - for (i = 0; i < str.length; i += 1) { - codePnt = str.charCodeAt(i); - binArr = []; - - if (codePnt < 0x80) { - binArr.push(codePnt); - } else if (codePnt < 0x800) { - binArr.push(0xC0 | (codePnt >>> 6)); - binArr.push(0x80 | (codePnt & 0x3F)); - } else if ((codePnt < 0xd800) || (codePnt >= 0xe000)) { - binArr.push( - 0xe0 | (codePnt >>> 12), - 0x80 | ((codePnt >>> 6) & 0x3f), - 0x80 | (codePnt & 0x3f) - ); - } else { - i += 1; - codePnt = 0x10000 + (((codePnt & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff)); - binArr.push( - 0xf0 | (codePnt >>> 18), - 0x80 | ((codePnt >>> 12) & 0x3f), - 0x80 | ((codePnt >>> 6) & 0x3f), - 0x80 | (codePnt & 0x3f) - ); - } - - for (j = 0; j < binArr.length; j += 1) { - offset = byteCnt >>> 2; - while (bin.length <= offset) { - bin.push(0); - } - bin[offset] |= binArr[j] << (24 - (8 * (byteCnt % 4))); - byteCnt += 1; - } - } - } else if ((utfType === "UTF16BE") || utfType === "UTF16LE") { - for (i = 0; i < str.length; i += 1) { - codePnt = str.charCodeAt(i); - /* Internally strings are UTF-16BE so only change if UTF-16LE */ - if (utfType === "UTF16LE") { - j = codePnt & 0xFF; - codePnt = (j << 8) | (codePnt >> 8); - } - - offset = byteCnt >>> 2; - while (bin.length <= offset) { - bin.push(0); - } - bin[offset] |= codePnt << (16 - (8 * (byteCnt % 4))); - byteCnt += 2; - } - } - return { "value" : bin, "binLen" : byteCnt * 8 }; -} - -/** - * Convert a hex string to an array of big-endian words - * - * @private - * @param {string} str String to be converted to binary representation - * @return {{value : Array., binLen : number}} Hash list where - * "value" contains the output number array and "binLen" is the binary - * length of "value" - */ -function hex2binb(str) { - const bin = []; - const { length } = str; - let num; - let offset; - - if ((length % 2) !== 0) { - throw new Error('String of HEX type must be in byte increments'); - } - - for (let i = 0; i < length; i += 2) { - num = parseInt(str.substr(i, 2), 16); - if (!Number.isNaN(num)) { - offset = i >>> 3; - while (bin.length <= offset) { - bin.push(0); - } - bin[i >>> 3] |= num << (24 - (4 * (i % 8))); - } else { - throw new Error('String of HEX type contains invalid characters'); - } - } - - return { "value" : bin, "binLen" : length * 4 }; -} - -/** - * Convert a string of raw bytes to an array of big-endian words - * - * @private - * @param {string} str String of raw bytes to be converted to binary representation - * @return {{value : Array., binLen : number}} Hash list where - * "value" contains the output number array and "binLen" is the binary - * length of "value" - */ -function bytes2binb(str) { - const bin = []; - let codePnt; - let offset; - - for (let i = 0; i < str.length; i += 1) { - codePnt = str.charCodeAt(i); - - offset = i >>> 2; - if (bin.length <= offset) { - bin.push(0); - } - bin[offset] |= codePnt << (24 - (8 * (i % 4))); - } - - return { "value" : bin, "binLen" : str.length * 8 }; -} - -/** - * Convert a Uint8Array of raw bytes to an array of big-endian 32-bit words - * - * @private - * @param {Uint8Array} str String of raw bytes to be converted to binary representation - * @return {{value : Array., binLen : number}} Hash list where - * "value" contains the output array and "binLen" is the binary - * length of "value" - */ -function typed2binb(array) { - const bin = []; - let octet; - let offset; - - for (let i = 0; i < array.length; i += 1) { - octet = array[i]; - - offset = i >>> 2; - if (bin.length <= offset) { - bin.push(0); - } - bin[offset] |= octet << (24 - (8 * (i % 4))); - } - - return { "value" : bin, "binLen" : array.length * 8 }; -} - -/** - * Convert a base-64 string to an array of big-endian words - * - * @private - * @param {string} str String to be converted to binary representation - * @return {{value : Array., binLen : number}} Hash list where - * "value" contains the output number array and "binLen" is the binary - * length of "value" - */ -function b642binb(str) { - const retVal = []; - let byteCnt = 0; - let index; - let j; - let tmpInt; - let strPart; - let offset; - const b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - if (str.search(/^[a-zA-Z0-9=+\/]+$/) === -1) { - throw new Error('Invalid character in base-64 string'); - } - const firstEqual = str.indexOf('='); - str = str.replace(/\=/g, ''); - if ((firstEqual !== -1) && (firstEqual < str.length)) { - throw new Error('Invalid \'=\' found in base-64 string'); - } - - for (let i = 0; i < str.length; i += 4) { - strPart = str.substr(i, 4); - tmpInt = 0; - - for (j = 0; j < strPart.length; j += 1) { - index = b64Tab.indexOf(strPart[j]); - tmpInt |= index << (18 - (6 * j)); - } - - for (j = 0; j < strPart.length - 1; j += 1) { - offset = byteCnt >>> 2; - while (retVal.length <= offset) { - retVal.push(0); - } - retVal[offset] |= ((tmpInt >>> (16 - (j * 8))) & 0xFF) << - (24 - (8 * (byteCnt % 4))); - byteCnt += 1; - } - } - - return { "value" : retVal, "binLen" : byteCnt * 8 }; -} - -/** - * Convert an array of big-endian words to a hex string. - * - * @private - * @param {Array.} binarray Array of integers to be converted to - * hexidecimal representation - * @param {{outputUpper : boolean, b64Pad : string}} formatOpts Hash list - * containing validated output formatting options - * @return {string} Hexidecimal representation of the parameter in string - * form - */ -function binb2hex(binarray, formatOpts) { - const hex_tab = "0123456789abcdef"; - let str = ""; - const length = binarray.length * 4; - let srcByte; - - for (let i = 0; i < length; i += 1) { - /* The below is more than a byte but it gets taken care of later */ - srcByte = binarray[i >>> 2] >>> ((3 - (i % 4)) * 8); - str += hex_tab.charAt((srcByte >>> 4) & 0xF) + - hex_tab.charAt(srcByte & 0xF); - } - - return (formatOpts.outputUpper) ? str.toUpperCase() : str; -} - -/** - * Convert an array of big-endian words to a base-64 string - * - * @private - * @param {Array.} binarray Array of integers to be converted to - * base-64 representation - * @param {{outputUpper : boolean, b64Pad : string}} formatOpts Hash list - * containing validated output formatting options - * @return {string} Base-64 encoded representation of the parameter in - * string form - */ -function binb2b64(binarray, formatOpts) { - let str = ""; - const length = binarray.length * 4; - let triplet; - let offset; - let int1; - let int2; - const b64Tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - for (let i = 0; i < length; i += 3) { - offset = (i + 1) >>> 2; - int1 = (binarray.length <= offset) ? 0 : binarray[offset]; - offset = (i + 2) >>> 2; - int2 = (binarray.length <= offset) ? 0 : binarray[offset]; - triplet = (((binarray[i >>> 2] >>> 8 * (3 - i % 4)) & 0xFF) << 16) | - (((int1 >>> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | - ((int2 >>> 8 * (3 - (i + 2) % 4)) & 0xFF); - for (let j = 0; j < 4; j += 1) { - if (i * 8 + j * 6 <= binarray.length * 32) { - str += b64Tab.charAt((triplet >>> 6 * (3 - j)) & 0x3F); - } else { - str += formatOpts.b64Pad; - } - } - } - return str; -} - -/** - * Convert an array of big-endian words to raw bytes string - * - * @private - * @param {Array.} binarray Array of integers to be converted to - * a raw bytes string representation - * @param {!Object} formatOpts Unused Hash list - * @return {string} Raw bytes representation of the parameter in string - * form - */ -function binb2bytes(binarray, formatOpts) { - let str = ""; - const length = binarray.length * 4; - let srcByte; - - for (let i = 0; i < length; i += 1) { - srcByte = (binarray[i >>> 2] >>> ((3 - (i % 4)) * 8)) & 0xFF; - str += String.fromCharCode(srcByte); - } - - return str; -} - -/** - * Convert an array of big-endian words to raw bytes Uint8Array - * - * @private - * @param {Array.} binarray Array of integers to be converted to - * a raw bytes string representation - * @param {!Object} formatOpts Unused Hash list - * @return {Uint8Array} Raw bytes representation of the parameter - */ -function binb2typed(binarray, formatOpts) { - const length = binarray.length * 4; - const arr = new Uint8Array(length); - - for (let i = 0; i < length; i += 1) { - arr[i] = (binarray[i >>> 2] >>> ((3 - (i % 4)) * 8)) & 0xFF; - } - - return arr; -} - -/** - * Validate hash list containing output formatting options, ensuring - * presence of every option or adding the default value - * - * @private - * @param {{outputUpper : boolean, b64Pad : string}|undefined} outputOpts - * Hash list of output formatting options - * @return {{outputUpper : boolean, b64Pad : string}} Validated hash list - * containing output formatting options - */ -function getOutputOpts(outputOpts) { - const retVal = { "outputUpper" : false, "b64Pad" : "=" }; - - try { - if (outputOpts.hasOwnProperty("outputUpper")) { - retVal.outputUpper = outputOpts.outputUpper; - } - - if (outputOpts.hasOwnProperty("b64Pad")) { - retVal.b64Pad = outputOpts.b64Pad; - } - } catch (ignore) {} - - if (typeof (retVal.outputUpper) !== "boolean") { - throw new Error('Invalid outputUpper formatting option'); - } - - if (typeof (retVal.b64Pad) !== "string") { - throw new Error('Invalid b64Pad formatting option'); - } - - return retVal; -} - -/** - * The 32-bit implementation of circular rotate left - * - * @private - * @param {number} x The 32-bit integer argument - * @param {number} n The number of bits to shift - * @return {number} The x shifted circularly by n bits - */ -function rotl_32(x, n) { - return (x << n) | (x >>> (32 - n)); -} - -/** - * The 32-bit implementation of circular rotate right - * - * @private - * @param {number} x The 32-bit integer argument - * @param {number} n The number of bits to shift - * @return {number} The x shifted circularly by n bits - */ -function rotr_32(x, n) { - return (x >>> n) | (x << (32 - n)); -} - -/** - * The 64-bit implementation of circular rotate right - * - * @private - * @param {Int_64} x The 64-bit integer argument - * @param {number} n The number of bits to shift - * @return {Int_64} The x shifted circularly by n bits - */ -function rotr_64(x, n) { - let retVal = null; - const tmp = new Int_64(x.highOrder, x.lowOrder); - - if (n <= 32) { - retVal = new Int_64( - (tmp.highOrder >>> n) | ((tmp.lowOrder << (32 - n)) & 0xFFFFFFFF), - (tmp.lowOrder >>> n) | ((tmp.highOrder << (32 - n)) & 0xFFFFFFFF) - ); - } else { - retVal = new Int_64( - (tmp.lowOrder >>> (n - 32)) | ((tmp.highOrder << (64 - n)) & 0xFFFFFFFF), - (tmp.highOrder >>> (n - 32)) | ((tmp.lowOrder << (64 - n)) & 0xFFFFFFFF) - ); - } - - return retVal; -} - -/** - * The 32-bit implementation of shift right - * - * @private - * @param {number} x The 32-bit integer argument - * @param {number} n The number of bits to shift - * @return {number} The x shifted by n bits - */ -function shr_32(x, n) { - return x >>> n; -} - -/** - * The 64-bit implementation of shift right - * - * @private - * @param {Int_64} x The 64-bit integer argument - * @param {number} n The number of bits to shift - * @return {Int_64} The x shifted by n bits - */ -function shr_64(x, n) { - let retVal = null; - - if (n <= 32) { - retVal = new Int_64( - x.highOrder >>> n, - (x.lowOrder >>> n) | ((x.highOrder << (32 - n)) & 0xFFFFFFFF) - ); - } else { - retVal = new Int_64( - 0, - x.highOrder >>> (n - 32) - ); - } - - return retVal; -} - -/** - * The 32-bit implementation of the NIST specified Parity function - * - * @private - * @param {number} x The first 32-bit integer argument - * @param {number} y The second 32-bit integer argument - * @param {number} z The third 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function parity_32(x, y, z) { - return x ^ y ^ z; -} - -/** - * The 32-bit implementation of the NIST specified Ch function - * - * @private - * @param {number} x The first 32-bit integer argument - * @param {number} y The second 32-bit integer argument - * @param {number} z The third 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function ch_32(x, y, z) { - return (x & y) ^ (~x & z); -} - -/** - * The 64-bit implementation of the NIST specified Ch function - * - * @private - * @param {Int_64} x The first 64-bit integer argument - * @param {Int_64} y The second 64-bit integer argument - * @param {Int_64} z The third 64-bit integer argument - * @return {Int_64} The NIST specified output of the function - */ -function ch_64(x, y, z) { - return new Int_64( - (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder), - (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder) - ); -} - -/** - * The 32-bit implementation of the NIST specified Maj function - * - * @private - * @param {number} x The first 32-bit integer argument - * @param {number} y The second 32-bit integer argument - * @param {number} z The third 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function maj_32(x, y, z) { - return (x & y) ^ (x & z) ^ (y & z); -} - -/** - * The 64-bit implementation of the NIST specified Maj function - * - * @private - * @param {Int_64} x The first 64-bit integer argument - * @param {Int_64} y The second 64-bit integer argument - * @param {Int_64} z The third 64-bit integer argument - * @return {Int_64} The NIST specified output of the function - */ -function maj_64(x, y, z) { - return new Int_64( - (x.highOrder & y.highOrder) ^ - (x.highOrder & z.highOrder) ^ - (y.highOrder & z.highOrder), - (x.lowOrder & y.lowOrder) ^ - (x.lowOrder & z.lowOrder) ^ - (y.lowOrder & z.lowOrder) - ); -} - -/** - * The 32-bit implementation of the NIST specified Sigma0 function - * - * @private - * @param {number} x The 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function sigma0_32(x) { - return rotr_32(x, 2) ^ rotr_32(x, 13) ^ rotr_32(x, 22); -} - -/** - * The 64-bit implementation of the NIST specified Sigma0 function - * - * @private - * @param {Int_64} x The 64-bit integer argument - * @return {Int_64} The NIST specified output of the function - */ -function sigma0_64(x) { - const rotr28 = rotr_64(x, 28); - const rotr34 = rotr_64(x, 34); - const rotr39 = rotr_64(x, 39); - - return new Int_64( - rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder, - rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder - ); -} - -/** - * The 32-bit implementation of the NIST specified Sigma1 function - * - * @private - * @param {number} x The 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function sigma1_32(x) { - return rotr_32(x, 6) ^ rotr_32(x, 11) ^ rotr_32(x, 25); -} - -/** - * The 64-bit implementation of the NIST specified Sigma1 function - * - * @private - * @param {Int_64} x The 64-bit integer argument - * @return {Int_64} The NIST specified output of the function - */ -function sigma1_64(x) { - const rotr14 = rotr_64(x, 14); - const rotr18 = rotr_64(x, 18); - const rotr41 = rotr_64(x, 41); - - return new Int_64( - rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder, - rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder - ); -} - -/** - * The 32-bit implementation of the NIST specified Gamma0 function - * - * @private - * @param {number} x The 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function gamma0_32(x) { - return rotr_32(x, 7) ^ rotr_32(x, 18) ^ shr_32(x, 3); -} - -/** - * The 64-bit implementation of the NIST specified Gamma0 function - * - * @private - * @param {Int_64} x The 64-bit integer argument - * @return {Int_64} The NIST specified output of the function - */ -function gamma0_64(x) { - const rotr1 = rotr_64(x, 1); - const rotr8 = rotr_64(x, 8); - const shr7 = shr_64(x, 7); - - return new Int_64( - rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder, - rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder - ); -} - -/** - * The 32-bit implementation of the NIST specified Gamma1 function - * - * @private - * @param {number} x The 32-bit integer argument - * @return {number} The NIST specified output of the function - */ -function gamma1_32(x) { - return rotr_32(x, 17) ^ rotr_32(x, 19) ^ shr_32(x, 10); -} - -/** - * The 64-bit implementation of the NIST specified Gamma1 function - * - * @private - * @param {Int_64} x The 64-bit integer argument - * @return {Int_64} The NIST specified output of the function - */ -function gamma1_64(x) { - const rotr19 = rotr_64(x, 19); - const rotr61 = rotr_64(x, 61); - const shr6 = shr_64(x, 6); - - return new Int_64( - rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder, - rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder - ); -} - -/** - * Add two 32-bit integers, wrapping at 2^32. This uses 16-bit operations - * internally to work around bugs in some JS interpreters. - * - * @private - * @param {number} a The first 32-bit integer argument to be added - * @param {number} b The second 32-bit integer argument to be added - * @return {number} The sum of a + b - */ -function safeAdd_32_2(a, b) { - const lsw = (a & 0xFFFF) + (b & 0xFFFF); - const msw = (a >>> 16) + (b >>> 16) + (lsw >>> 16); - - return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); -} - -/** - * Add four 32-bit integers, wrapping at 2^32. This uses 16-bit operations - * internally to work around bugs in some JS interpreters. - * - * @private - * @param {number} a The first 32-bit integer argument to be added - * @param {number} b The second 32-bit integer argument to be added - * @param {number} c The third 32-bit integer argument to be added - * @param {number} d The fourth 32-bit integer argument to be added - * @return {number} The sum of a + b + c + d - */ -function safeAdd_32_4(a, b, c, d) { - const lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF); - const msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + - (lsw >>> 16); - - return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); -} - -/** - * Add five 32-bit integers, wrapping at 2^32. This uses 16-bit operations - * internally to work around bugs in some JS interpreters. - * - * @private - * @param {number} a The first 32-bit integer argument to be added - * @param {number} b The second 32-bit integer argument to be added - * @param {number} c The third 32-bit integer argument to be added - * @param {number} d The fourth 32-bit integer argument to be added - * @param {number} e The fifth 32-bit integer argument to be added - * @return {number} The sum of a + b + c + d + e - */ -function safeAdd_32_5(a, b, c, d, e) { - const lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF) + - (e & 0xFFFF); - const msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + - (e >>> 16) + (lsw >>> 16); - - return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); -} - -/** - * Add two 64-bit integers, wrapping at 2^64. This uses 16-bit operations - * internally to work around bugs in some JS interpreters. - * - * @private - * @param {Int_64} x The first 64-bit integer argument to be added - * @param {Int_64} y The second 64-bit integer argument to be added - * @return {Int_64} The sum of x + y - */ -function safeAdd_64_2(x, y) { - let lsw; - let msw; - - lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF); - msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16); - const lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); - - lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16); - msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16); - const highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); - - return new Int_64(highOrder, lowOrder); -} - -/** - * Add four 64-bit integers, wrapping at 2^64. This uses 16-bit operations - * internally to work around bugs in some JS interpreters. - * - * @private - * @param {Int_64} a The first 64-bit integer argument to be added - * @param {Int_64} b The second 64-bit integer argument to be added - * @param {Int_64} c The third 64-bit integer argument to be added - * @param {Int_64} d The fourth 64-bit integer argument to be added - * @return {Int_64} The sum of a + b + c + d - */ -function safeAdd_64_4(a, b, c, d) { - let lsw; - let msw; - - lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + - (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF); - msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + - (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16); - const lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); - - lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + - (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16); - msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + - (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16); - const highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); - - return new Int_64(highOrder, lowOrder); -} - -/** - * Add five 64-bit integers, wrapping at 2^64. This uses 16-bit operations - * internally to work around bugs in some JS interpreters. - * - * @private - * @param {Int_64} a The first 64-bit integer argument to be added - * @param {Int_64} b The second 64-bit integer argument to be added - * @param {Int_64} c The third 64-bit integer argument to be added - * @param {Int_64} d The fourth 64-bit integer argument to be added - * @param {Int_64} e The fourth 64-bit integer argument to be added - * @return {Int_64} The sum of a + b + c + d + e - */ -function safeAdd_64_5(a, b, c, d, e) { - let lsw; - let msw; - - lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + - (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + - (e.lowOrder & 0xFFFF); - msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + - (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + - (lsw >>> 16); - const lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); - - lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + - (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + - (e.highOrder & 0xFFFF) + (msw >>> 16); - msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + - (c.highOrder >>> 16) + (d.highOrder >>> 16) + - (e.highOrder >>> 16) + (lsw >>> 16); - const highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); - - return new Int_64(highOrder, lowOrder); -} - -/** - * Calculates the SHA-1 hash of the string set at instantiation - * - * @private - * @param {Array.} message The binary array representation of the - * string to hash - * @param {number} messageLen The number of bits in the message - * @return {Array.} The array of integers representing the SHA-1 - * hash of message - */ -function coreSHA1(message, messageLen) { - const W = []; - let T; - const ch = ch_32; - const parity = parity_32; - const maj = maj_32; - const rotl = rotl_32; - const safeAdd_2 = safeAdd_32_2; - const safeAdd_5 = safeAdd_32_5; - - const H = [ - 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 - ]; - - const offset = (((messageLen + 65) >>> 9) << 4) + 15; - while (message.length <= offset) { - message.push(0); - } - /* Append '1' at the end of the binary string */ - message[messageLen >>> 5] |= 0x80 << (24 - (messageLen % 32)); - /* Append length of binary string in the position such that the new - length is a multiple of 512. Logic does not work for even multiples - of 512 but there can never be even multiples of 512 */ - message[offset] = messageLen; - - const appendedMessageLength = message.length; - - for (let i = 0; i < appendedMessageLength; i += 16) { - let [a, b, c, d, e] = H; - - for (let t = 0; t < 80; t += 1) { - if (t < 16) { - W[t] = message[t + i]; - } else { - W[t] = rotl(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); - } - - if (t < 20) { - T = safeAdd_5(rotl(a, 5), ch(b, c, d), e, 0x5a827999, W[t]); - } else if (t < 40) { - T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, 0x6ed9eba1, W[t]); - } else if (t < 60) { - T = safeAdd_5(rotl(a, 5), maj(b, c, d), e, 0x8f1bbcdc, W[t]); - } else { - T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, 0xca62c1d6, W[t]); - } - - e = d; - d = c; - c = rotl(b, 30); - b = a; - a = T; - } - - H[0] = safeAdd_2(a, H[0]); - H[1] = safeAdd_2(b, H[1]); - H[2] = safeAdd_2(c, H[2]); - H[3] = safeAdd_2(d, H[3]); - H[4] = safeAdd_2(e, H[4]); - } - - return H; -} - -/** - * Calculates the desired SHA-2 hash of the string set at instantiation - * - * @private - * @param {Array.} message The binary array representation of the - * string to hash - * @param {number} messageLen The number of bits in message - * @param {string} variant The desired SHA-2 variant - * @return {Array.} The array of integers representing the SHA-2 - * hash of message - */ -function coreSHA2(message, messageLen, variant) { - let a; - let b; - let c; - let d; - let e; - let f; - let g; - let h; - let T1; - let T2; - let H; - let numRounds; - let lengthPosition; - let i; - let t; - let binaryStringInc; - let binaryStringMult; - let safeAdd_2; - let safeAdd_4; - let safeAdd_5; - let gamma0; - let gamma1; - let sigma0; - let sigma1; - let ch; - let maj; - let Int; - const W = []; - let int1; - let int2; - let offset; - let retVal; - let K = [ - 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, - 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, - 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, - 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, - 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, - 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, - 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, - 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, - 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, - 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, - 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, - 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, - 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, - 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, - 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, - 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 - ]; - const H_trunc = [ - 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, - 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 - ]; - const H_full = [ - 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, - 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 - ]; - - /* Set up the various function handles and variable for the specific - * variant */ - if ((variant === "SHA-224" || variant === "SHA-256") && - (2 & SUPPORTED_ALGS)) { - /* 32-bit variant */ - numRounds = 64; - lengthPosition = (((messageLen + 65) >>> 9) << 4) + 15; - binaryStringInc = 16; - binaryStringMult = 1; - Int = Number; - safeAdd_2 = safeAdd_32_2; - safeAdd_4 = safeAdd_32_4; - safeAdd_5 = safeAdd_32_5; - gamma0 = gamma0_32; - gamma1 = gamma1_32; - sigma0 = sigma0_32; - sigma1 = sigma1_32; - maj = maj_32; - ch = ch_32; - - if (variant === "SHA-224") { - H = H_trunc; - } else { /* "SHA-256" === variant */ - H = H_full; - } - } else if ((variant === "SHA-384" || variant === "SHA-512") && - (4 & SUPPORTED_ALGS)) { - /* 64-bit variant */ - numRounds = 80; - lengthPosition = (((messageLen + 128) >>> 10) << 5) + 31; - binaryStringInc = 32; - binaryStringMult = 2; - Int = Int_64; - safeAdd_2 = safeAdd_64_2; - safeAdd_4 = safeAdd_64_4; - safeAdd_5 = safeAdd_64_5; - gamma0 = gamma0_64; - gamma1 = gamma1_64; - sigma0 = sigma0_64; - sigma1 = sigma1_64; - maj = maj_64; - ch = ch_64; - - K = [ - new Int(K[0], 0xd728ae22), new Int(K[1], 0x23ef65cd), - new Int(K[2], 0xec4d3b2f), new Int(K[3], 0x8189dbbc), - new Int(K[4], 0xf348b538), new Int(K[5], 0xb605d019), - new Int(K[6], 0xaf194f9b), new Int(K[7], 0xda6d8118), - new Int(K[8], 0xa3030242), new Int(K[9], 0x45706fbe), - new Int(K[10], 0x4ee4b28c), new Int(K[11], 0xd5ffb4e2), - new Int(K[12], 0xf27b896f), new Int(K[13], 0x3b1696b1), - new Int(K[14], 0x25c71235), new Int(K[15], 0xcf692694), - new Int(K[16], 0x9ef14ad2), new Int(K[17], 0x384f25e3), - new Int(K[18], 0x8b8cd5b5), new Int(K[19], 0x77ac9c65), - new Int(K[20], 0x592b0275), new Int(K[21], 0x6ea6e483), - new Int(K[22], 0xbd41fbd4), new Int(K[23], 0x831153b5), - new Int(K[24], 0xee66dfab), new Int(K[25], 0x2db43210), - new Int(K[26], 0x98fb213f), new Int(K[27], 0xbeef0ee4), - new Int(K[28], 0x3da88fc2), new Int(K[29], 0x930aa725), - new Int(K[30], 0xe003826f), new Int(K[31], 0x0a0e6e70), - new Int(K[32], 0x46d22ffc), new Int(K[33], 0x5c26c926), - new Int(K[34], 0x5ac42aed), new Int(K[35], 0x9d95b3df), - new Int(K[36], 0x8baf63de), new Int(K[37], 0x3c77b2a8), - new Int(K[38], 0x47edaee6), new Int(K[39], 0x1482353b), - new Int(K[40], 0x4cf10364), new Int(K[41], 0xbc423001), - new Int(K[42], 0xd0f89791), new Int(K[43], 0x0654be30), - new Int(K[44], 0xd6ef5218), new Int(K[45], 0x5565a910), - new Int(K[46], 0x5771202a), new Int(K[47], 0x32bbd1b8), - new Int(K[48], 0xb8d2d0c8), new Int(K[49], 0x5141ab53), - new Int(K[50], 0xdf8eeb99), new Int(K[51], 0xe19b48a8), - new Int(K[52], 0xc5c95a63), new Int(K[53], 0xe3418acb), - new Int(K[54], 0x7763e373), new Int(K[55], 0xd6b2b8a3), - new Int(K[56], 0x5defb2fc), new Int(K[57], 0x43172f60), - new Int(K[58], 0xa1f0ab72), new Int(K[59], 0x1a6439ec), - new Int(K[60], 0x23631e28), new Int(K[61], 0xde82bde9), - new Int(K[62], 0xb2c67915), new Int(K[63], 0xe372532b), - new Int(0xca273ece, 0xea26619c), new Int(0xd186b8c7, 0x21c0c207), - new Int(0xeada7dd6, 0xcde0eb1e), new Int(0xf57d4f7f, 0xee6ed178), - new Int(0x06f067aa, 0x72176fba), new Int(0x0a637dc5, 0xa2c898a6), - new Int(0x113f9804, 0xbef90dae), new Int(0x1b710b35, 0x131c471b), - new Int(0x28db77f5, 0x23047d84), new Int(0x32caab7b, 0x40c72493), - new Int(0x3c9ebe0a, 0x15c9bebc), new Int(0x431d67c4, 0x9c100d4c), - new Int(0x4cc5d4be, 0xcb3e42b6), new Int(0x597f299c, 0xfc657e2a), - new Int(0x5fcb6fab, 0x3ad6faec), new Int(0x6c44198c, 0x4a475817) - ]; - - if (variant === "SHA-384") { - H = [ - new Int(0xcbbb9d5d, H_trunc[0]), new Int(0x0629a292a, H_trunc[1]), - new Int(0x9159015a, H_trunc[2]), new Int(0x0152fecd8, H_trunc[3]), - new Int(0x67332667, H_trunc[4]), new Int(0x98eb44a87, H_trunc[5]), - new Int(0xdb0c2e0d, H_trunc[6]), new Int(0x047b5481d, H_trunc[7]) - ]; - } else { /* "SHA-512" === variant */ - H = [ - new Int(H_full[0], 0xf3bcc908), new Int(H_full[1], 0x84caa73b), - new Int(H_full[2], 0xfe94f82b), new Int(H_full[3], 0x5f1d36f1), - new Int(H_full[4], 0xade682d1), new Int(H_full[5], 0x2b3e6c1f), - new Int(H_full[6], 0xfb41bd6b), new Int(H_full[7], 0x137e2179) - ]; - } - } else { - throw new Error("Unexpected error in SHA-2 implementation"); - } - - while (message.length <= lengthPosition) { - message.push(0); - } - /* Append '1' at the end of the binary string */ - message[messageLen >>> 5] |= 0x80 << (24 - messageLen % 32); - /* Append length of binary string in the position such that the new - * length is correct */ - message[lengthPosition] = messageLen; - - const appendedMessageLength = message.length; - - for (i = 0; i < appendedMessageLength; i += binaryStringInc) { - [a, b, c, d, e, f, g, h] = H; - - for (t = 0; t < numRounds; t += 1) { - if (t < 16) { - offset = t * binaryStringMult + i; - int1 = (message.length <= offset) ? 0 : message[offset]; - int2 = (message.length <= offset + 1) ? 0 : message[offset + 1]; - /* Bit of a hack - for 32-bit, the second term is ignored */ - W[t] = new Int(int1, int2); - } else { - W[t] = safeAdd_4( - gamma1(W[t - 2]), W[t - 7], - gamma0(W[t - 15]), W[t - 16] - ); - } - - T1 = safeAdd_5(h, sigma1(e), ch(e, f, g), K[t], W[t]); - T2 = safeAdd_2(sigma0(a), maj(a, b, c)); - h = g; - g = f; - f = e; - e = safeAdd_2(d, T1); - d = c; - c = b; - b = a; - a = safeAdd_2(T1, T2); - } - - H[0] = safeAdd_2(a, H[0]); - H[1] = safeAdd_2(b, H[1]); - H[2] = safeAdd_2(c, H[2]); - H[3] = safeAdd_2(d, H[3]); - H[4] = safeAdd_2(e, H[4]); - H[5] = safeAdd_2(f, H[5]); - H[6] = safeAdd_2(g, H[6]); - H[7] = safeAdd_2(h, H[7]); - } - - if ((variant === "SHA-224") && (2 & SUPPORTED_ALGS)) { - retVal = [ - H[0], H[1], H[2], H[3], - H[4], H[5], H[6] - ]; - } else if ((variant === "SHA-256") && (2 & SUPPORTED_ALGS)) { - retVal = H; - } else if ((variant === "SHA-384") && (4 & SUPPORTED_ALGS)) { - retVal = [ - H[0].highOrder, H[0].lowOrder, - H[1].highOrder, H[1].lowOrder, - H[2].highOrder, H[2].lowOrder, - H[3].highOrder, H[3].lowOrder, - H[4].highOrder, H[4].lowOrder, - H[5].highOrder, H[5].lowOrder - ]; - } else if ((variant === "SHA-512") && (4 & SUPPORTED_ALGS)) { - retVal = [ - H[0].highOrder, H[0].lowOrder, - H[1].highOrder, H[1].lowOrder, - H[2].highOrder, H[2].lowOrder, - H[3].highOrder, H[3].lowOrder, - H[4].highOrder, H[4].lowOrder, - H[5].highOrder, H[5].lowOrder, - H[6].highOrder, H[6].lowOrder, - H[7].highOrder, H[7].lowOrder - ]; - } else { /* This should never be reached */ - throw new Error("Unexpected error in SHA-2 implementation"); - } - - return retVal; -} - -/** - * jsSHA is the workhorse of the library. Instantiate it with the string to - * be hashed as the parameter - * - * @constructor - * @this {jsSHA} - * @param {string} srcString The string to be hashed - * @param {string} inputFormat The format of srcString, HEX, ASCII, TEXT, - * B64, or BYTES - * @param {string=} encoding The text encoding to use to encode the source - * string - */ -const jsSHA = function(srcString, inputFormat, encoding) { - let strBinLen = 0; - let strToHash = [0]; - let utfType = ''; - let srcConvertRet = null; - - utfType = encoding || "UTF8"; - - if (!((utfType === "UTF8") || (utfType === "UTF16BE") || (utfType === "UTF16LE"))) { - throw new Error('encoding must be UTF8, UTF16BE, or UTF16LE'); - } - - /* Convert the input string into the correct type */ - if (inputFormat === "HEX") { - if ((srcString.length % 2) !== 0) { - throw new Error('srcString of HEX type must be in byte increments'); - } - srcConvertRet = hex2binb(srcString); - strBinLen = srcConvertRet.binLen; - strToHash = srcConvertRet.value; - } else if ((inputFormat === "TEXT") || (inputFormat === "ASCII")) { - srcConvertRet = str2binb(srcString, utfType); - strBinLen = srcConvertRet.binLen; - strToHash = srcConvertRet.value; - } else if (inputFormat === "B64") { - srcConvertRet = b642binb(srcString); - strBinLen = srcConvertRet.binLen; - strToHash = srcConvertRet.value; - } else if (inputFormat === "BYTES") { - srcConvertRet = bytes2binb(srcString); - strBinLen = srcConvertRet.binLen; - strToHash = srcConvertRet.value; - } else if (inputFormat === "TYPED") { - srcConvertRet = typed2binb(srcString); - strBinLen = srcConvertRet.binLen; - strToHash = srcConvertRet.value; - } else { - throw new Error('inputFormat must be HEX, TEXT, ASCII, B64, BYTES, or TYPED'); - } - - /** - * Returns the desired SHA hash of the string specified at instantiation - * using the specified parameters - * - * @expose - * @param {string} variant The desired SHA variant (SHA-1, SHA-224, - * SHA-256, SHA-384, or SHA-512) - * @param {string} format The desired output formatting (B64, HEX, or BYTES) - * @param {number=} numRounds The number of rounds of hashing to be - * executed - * @param {{outputUpper : boolean, b64Pad : string}=} outputFormatOpts - * Hash list of output formatting options - * @return {string} The string representation of the hash in the format - * specified - */ - this.getHash = function(variant, format, numRounds, outputFormatOpts) { - let formatFunc = null; - let message = strToHash.slice(); - let messageBinLen = strBinLen; - let i; - - /* Need to do argument patching since both numRounds and - outputFormatOpts are optional */ - if (arguments.length === 3) { - if (typeof numRounds !== "number") { - outputFormatOpts = numRounds; - numRounds = 1; - } - } else if (arguments.length === 2) { - numRounds = 1; - } - - /* Validate the numRounds argument */ - if ((numRounds !== parseInt(numRounds, 10)) || (numRounds < 1)) { - throw new Error('numRounds must a integer >= 1'); - } - - /* Validate the output format selection */ - switch (format) { - case "HEX": - formatFunc = binb2hex; - break; - case "B64": - formatFunc = binb2b64; - break; - case "BYTES": - formatFunc = binb2bytes; - break; - case "TYPED": - formatFunc = binb2typed; - break; - default: - throw new Error('format must be HEX, B64, or BYTES'); - } - - if ((variant === "SHA-1") && (1 & SUPPORTED_ALGS)) { - for (i = 0; i < numRounds; i += 1) { - message = coreSHA1(message, messageBinLen); - messageBinLen = 160; - } - } else if ((variant === "SHA-224") && (2 & SUPPORTED_ALGS)) { - for (i = 0; i < numRounds; i += 1) { - message = coreSHA2(message, messageBinLen, variant); - messageBinLen = 224; - } - } else if ((variant === "SHA-256") && (2 & SUPPORTED_ALGS)) { - for (i = 0; i < numRounds; i += 1) { - message = coreSHA2(message, messageBinLen, variant); - messageBinLen = 256; - } - } else if ((variant === "SHA-384") && (4 & SUPPORTED_ALGS)) { - for (i = 0; i < numRounds; i += 1) { - message = coreSHA2(message, messageBinLen, variant); - messageBinLen = 384; - } - } else if ((variant === "SHA-512") && (4 & SUPPORTED_ALGS)) { - for (i = 0; i < numRounds; i += 1) { - message = coreSHA2(message, messageBinLen, variant); - messageBinLen = 512; - } - } else { - throw new Error('Chosen SHA variant is not supported'); - } - - return formatFunc(message, getOutputOpts(outputFormatOpts)); - }; - - /** - * Returns the desired HMAC of the string specified at instantiation - * using the key and variant parameter - * - * @expose - * @param {string} key The key used to calculate the HMAC - * @param {string} inputFormat The format of key, HEX, TEXT, ASCII, - * B64, or BYTES - * @param {string} variant The desired SHA variant (SHA-1, SHA-224, - * SHA-256, SHA-384, or SHA-512) - * @param {string} outputFormat The desired output formatting - * (B64, HEX, or BYTES) - * @param {{outputUpper : boolean, b64Pad : string}=} outputFormatOpts - * associative array of output formatting options - * @return {string} The string representation of the hash in the format - * specified - */ - this.getHMAC = function( - key, inputFormat, variant, outputFormat, - outputFormatOpts - ) { - let formatFunc; - let keyToUse; - let blockByteSize; - let i; - let retVal; - let keyBinLen; - let hashBitSize; - const keyWithIPad = []; - const keyWithOPad = []; - let keyConvertRet = null; - - /* Validate the output format selection */ - switch (outputFormat) { - case "HEX": - formatFunc = binb2hex; - break; - case "B64": - formatFunc = binb2b64; - break; - case "BYTES": - formatFunc = binb2bytes; - break; - default: - throw new Error('outputFormat must be HEX, B64, or BYTES'); - } - - /* Validate the hash variant selection and set needed variables */ - if ((variant === "SHA-1") && (1 & SUPPORTED_ALGS)) { - blockByteSize = 64; - hashBitSize = 160; - } else if ((variant === "SHA-224") && (2 & SUPPORTED_ALGS)) { - blockByteSize = 64; - hashBitSize = 224; - } else if ((variant === "SHA-256") && (2 & SUPPORTED_ALGS)) { - blockByteSize = 64; - hashBitSize = 256; - } else if ((variant === "SHA-384") && (4 & SUPPORTED_ALGS)) { - blockByteSize = 128; - hashBitSize = 384; - } else if ((variant === "SHA-512") && (4 & SUPPORTED_ALGS)) { - blockByteSize = 128; - hashBitSize = 512; - } else { - throw new Error('Chosen SHA variant is not supported'); - } - - /* Validate input format selection */ - if (inputFormat === "HEX") { - keyConvertRet = hex2binb(key); - keyBinLen = keyConvertRet.binLen; - keyToUse = keyConvertRet.value; - } else if ((inputFormat === "TEXT") || (inputFormat === "ASCII")) { - keyConvertRet = str2binb(key, utfType); - keyBinLen = keyConvertRet.binLen; - keyToUse = keyConvertRet.value; - } else if (inputFormat === "B64") { - keyConvertRet = b642binb(key); - keyBinLen = keyConvertRet.binLen; - keyToUse = keyConvertRet.value; - } else if (inputFormat === "BYTES") { - keyConvertRet = bytes2binb(key); - keyBinLen = keyConvertRet.binLen; - keyToUse = keyConvertRet.value; - } else { - throw new Error('inputFormat must be HEX, TEXT, ASCII, B64, or BYTES'); - } - - /* These are used multiple times, calculate and store them */ - const blockBitSize = blockByteSize * 8; - const lastArrayIndex = (blockByteSize / 4) - 1; - - /* Figure out what to do with the key based on its size relative to - * the hash's block size */ - if (blockByteSize < (keyBinLen / 8)) { - if ((variant === "SHA-1") && (1 & SUPPORTED_ALGS)) { - keyToUse = coreSHA1(keyToUse, keyBinLen); - } else if (6 & SUPPORTED_ALGS) { - keyToUse = coreSHA2(keyToUse, keyBinLen, variant); - } else { - throw new Error('Unexpected error in HMAC implementation'); - } - /* For all variants, the block size is bigger than the output - * size so there will never be a useful byte at the end of the - * string */ - while (keyToUse.length <= lastArrayIndex) { - keyToUse.push(0); - } - keyToUse[lastArrayIndex] &= 0xFFFFFF00; - } else if (blockByteSize > (keyBinLen / 8)) { - /* If the blockByteSize is greater than the key length, there - * will always be at LEAST one "useless" byte at the end of the - * string */ - while (keyToUse.length <= lastArrayIndex) { - keyToUse.push(0); - } - keyToUse[lastArrayIndex] &= 0xFFFFFF00; - } - - /* Create ipad and opad */ - for (i = 0; i <= lastArrayIndex; i += 1) { - keyWithIPad[i] = keyToUse[i] ^ 0x36363636; - keyWithOPad[i] = keyToUse[i] ^ 0x5C5C5C5C; - } - - /* Calculate the HMAC */ - if ((variant === "SHA-1") && (1 & SUPPORTED_ALGS)) { - retVal = coreSHA1( - keyWithOPad.concat(coreSHA1( - keyWithIPad.concat(strToHash), - blockBitSize + strBinLen - )), - blockBitSize + hashBitSize - ); - } else if (6 & SUPPORTED_ALGS) { - retVal = coreSHA2( - keyWithOPad.concat(coreSHA2( - keyWithIPad.concat(strToHash), - blockBitSize + strBinLen, - variant - )), - blockBitSize + hashBitSize, variant - ); - } else { - throw new Error('Unexpected error in HMAC implementation'); - } - - return formatFunc(retVal, getOutputOpts(outputFormatOpts)); - }; -}; - -export default { - /** SHA1 hash */ - sha1: function(str) { - const shaObj = new jsSHA(str, "TYPED", "UTF8"); - return shaObj.getHash("SHA-1", "TYPED"); - }, - /** SHA224 hash */ - sha224: function(str) { - const shaObj = new jsSHA(str, "TYPED", "UTF8"); - return shaObj.getHash("SHA-224", "TYPED"); - }, - /** SHA256 hash */ - sha256: function(str) { - const shaObj = new jsSHA(str, "TYPED", "UTF8"); - return shaObj.getHash("SHA-256", "TYPED"); - }, - /** SHA384 hash */ - sha384: function(str) { - const shaObj = new jsSHA(str, "TYPED", "UTF8"); - return shaObj.getHash("SHA-384", "TYPED"); - }, - /** SHA512 hash */ - sha512: function(str) { - const shaObj = new jsSHA(str, "TYPED", "UTF8"); - return shaObj.getHash("SHA-512", "TYPED"); - } -}; diff --git a/src/crypto/index.js b/src/crypto/index.js index a5ced4ed..ad201506 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -15,6 +15,7 @@ import pkcs5 from './pkcs5.js'; import crypto from './crypto.js'; import aes_kw from './aes_kw.js'; +// TODO move cfb and gcm to cipher const mod = { /** @see module:crypto/cipher */ cipher: cipher, diff --git a/src/crypto/pkcs1.js b/src/crypto/pkcs1.js index 1acb15d4..38dc0671 100644 --- a/src/crypto/pkcs1.js +++ b/src/crypto/pkcs1.js @@ -17,18 +17,15 @@ /** * PKCS1 encoding - * @requires crypto/crypto - * @requires crypto/hash - * @requires crypto/public_key/jsbn * @requires crypto/random + * @requires crypto/hash * @requires util * @module crypto/pkcs1 */ -import random from './random.js'; -import util from '../util.js'; -import BigInteger from './public_key/jsbn.js'; +import random from './random'; import hash from './hash'; +import util from '../util'; /** * ASN1 object identifiers for hashes (See {@link https://tools.ietf.org/html/rfc4880#section-5.2.2}) @@ -98,7 +95,7 @@ export default { * @return {String} message, an octet string */ decode: function(EM) { - // leading zeros truncated by jsbn + // leading zeros truncated by bn.js if (EM.charCodeAt(0) !== 0) { EM = String.fromCharCode(0) + EM; } @@ -128,7 +125,7 @@ export default { encode: function(algo, M, emLen) { let i; // Apply the hash function to the message M to produce a hash value H - const H = util.Uint8Array2str(hash.digest(algo, util.str2Uint8Array(M))); + const H = util.Uint8Array_to_str(hash.digest(algo, util.str_to_Uint8Array(M))); if (H.length !== hash.getHashByteLength(algo)) { throw new Error('Invalid hash length'); } @@ -158,7 +155,7 @@ export default { PS + String.fromCharCode(0x00) + T; - return new BigInteger(util.hexstrdump(EM), 16); + return util.str_to_hex(EM); } } }; diff --git a/src/crypto/pkcs5.js b/src/crypto/pkcs5.js index f7e166a4..088d2d89 100644 --- a/src/crypto/pkcs5.js +++ b/src/crypto/pkcs5.js @@ -48,7 +48,4 @@ function decode(msg) { throw new Error('Invalid padding'); } -module.exports = { - encode: encode, - decode: decode -}; +export default { encode, decode }; diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 1fb64979..ee1c5e16 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -18,111 +18,106 @@ // A Digital signature algorithm implementation /** + * @requires bn.js * @requires crypto/hash - * @requires crypto/public_key/jsbn * @requires crypto/random + * @requires config * @requires util * @module crypto/public_key/dsa */ -import BigInteger from './jsbn.js'; -import random from '../random.js'; -import hashModule from '../hash'; -import util from '../../util.js'; +import BN from 'bn.js'; +import hash from '../hash'; +import random from '../random'; import config from '../../config'; +import util from '../../util'; -export default function DSA() { - // s1 = ((g**s) mod p) mod q - // s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q) - function sign(hashalgo, m, g, p, q, x) { +const one = new BN(1); +const zero = new BN(0); + +/* + TODO regarding the hash function, read: + https://tools.ietf.org/html/rfc4880#section-13.6 + https://tools.ietf.org/html/rfc4880#section-14 +*/ + +export default { + /* + * hash_algo is integer + * m is string + * g, p, q, x are all BN + * returns { r: BN, s: BN } + */ + sign: function(hash_algo, m, g, p, q, x) { + let k; + let r; + let s; + let t; + const redp = new BN.red(p); + const redq = new BN.red(q); + const gred = g.toRed(redp); + const xred = x.toRed(redq); // If the output size of the chosen hash is larger than the number of // bits of q, the hash result is truncated to fit by taking the number // of leftmost bits equal to the number of bits of q. This (possibly // truncated) hash function result is treated as a number and used // directly in the DSA signature algorithm. - const hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength()); - const hash = new BigInteger(util.hexstrdump(hashed_data), 16); + const h = new BN( + util.str_to_Uint8Array( + util.getLeftNBits( + util.Uint8Array_to_str(hash.digest(hash_algo, m)), q.bitLength()))); // FIPS-186-4, section 4.6: // The values of r and s shall be checked to determine if r = 0 or s = 0. // If either r = 0 or s = 0, a new value of k shall be generated, and the // signature shall be recalculated. It is extremely unlikely that r = 0 // or s = 0 if signatures are generated properly. - let k; - let s1; - let s2; while (true) { - k = random.getRandomBigIntegerInRange(BigInteger.ONE, q.subtract(BigInteger.ONE)); - s1 = (g.modPow(k, p)).mod(q); - s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q); - if (s1 !== 0 && s2 !== 0) { - break; + // See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf + k = random.getRandomBN(one, q); // returns in [1, q-1] + r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q + if (zero.cmp(r) === 0) { + continue; } + t = h.add(x.mul(r)).toRed(redq); // H(m) + x*r mod q + s = k.toRed(redq).redInvm().redMul(t); // k**-1 * (H(m) + x*r) mod q + if (zero.cmp(s) === 0) { + continue; + } + break; } - const result = []; - result[0] = s1.toMPI(); - result[1] = s2.toMPI(); - return result; - } + return { r: r.toArrayLike(Uint8Array), + s: s.toArrayLike(Uint8Array) }; + }, - function select_hash_algorithm(q) { - const usersetting = config.prefer_hash_algorithm; - /* - * 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash - * 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash - * 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash - * 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash - */ - switch (Math.round(q.bitLength() / 8)) { - case 20: - // 1024 bit - if (usersetting !== 2 && - usersetting > 11 && - usersetting !== 10 && - usersetting < 8) { - return 2; // prefer sha1 - } - return usersetting; - case 28: - // 2048 bit - if (usersetting > 11 && - usersetting < 8) { - return 11; - } - return usersetting; - case 32: - // 4096 bit // prefer sha224 - if (usersetting > 10 && - usersetting < 8) { - return 8; // prefer sha256 - } - return usersetting; - default: - util.print_debug("DSA select hash algorithm: returning null for an unknown length of q"); - return null; - } - } - this.select_hash_algorithm = select_hash_algorithm; - - function verify(hashalgo, s1, s2, m, p, q, g, y) { - const hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength()); - const hash = new BigInteger(util.hexstrdump(hashed_data), 16); - if (BigInteger.ZERO.compareTo(s1) >= 0 || - s1.compareTo(q) >= 0 || - BigInteger.ZERO.compareTo(s2) >= 0 || - s2.compareTo(q) >= 0) { + /* + * hash_algo is integer + * r, s are both BN + * m is string + * p, q, g, y are all BN + * returns BN + */ + verify: function(hash_algo, r, s, m, p, q, g, y) { + if (zero.ucmp(r) >= 0 || r.ucmp(q) >= 0 || + zero.ucmp(s) >= 0 || s.ucmp(q) >= 0) { util.print_debug("invalid DSA Signature"); return null; } - const w = s2.modInverse(q); - if (BigInteger.ZERO.compareTo(w) === 0) { + const redp = new BN.red(p); + const redq = new BN.red(q); + const h = new BN( + util.str_to_Uint8Array( + util.getLeftNBits( + util.Uint8Array_to_str(hash.digest(hash_algo, m)), q.bitLength()))); + const w = s.toRed(redq).redInvm(); // s**-1 mod q + if (zero.cmp(w) === 0) { util.print_debug("invalid DSA Signature"); return null; } - const u1 = hash.multiply(w).mod(q); - const u2 = s1.multiply(w).mod(q); - return g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q); + const u1 = h.toRed(redq).redMul(w); // H(m) * w mod q + const u2 = r.toRed(redq).redMul(w); // r * w mod q + const t1 = g.toRed(redp).redPow(u1.fromRed()); // g**u1 mod p + const t2 = y.toRed(redp).redPow(u2.fromRed()); // y**u2 mod p + const v = t1.redMul(t2).fromRed().mod(q); // (g**u1 * y**u2 mod p) mod q + return v.cmp(r) === 0; } - - this.sign = sign; - this.verify = verify; -} +}; diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index 465dc16a..b48402e6 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -18,39 +18,42 @@ // ElGamal implementation /** - * @requires crypto/public_key/jsbn + * @requires bn.js * @requires crypto/random - * @requires util * @module crypto/public_key/elgamal */ -import BigInteger from './jsbn.js'; -import random from '../random.js'; -import util from '../../util.js'; +import BN from 'bn.js'; +import random from '../random'; -export default function Elgamal() { - function encrypt(m, g, p, y) { - // choose k in {2,...,p-2} - const pMinus2 = p.subtract(BigInteger.TWO); - let k = random.getRandomBigIntegerInRange(BigInteger.ONE, pMinus2); - k = k.mod(pMinus2).add(BigInteger.ONE); - const c = []; - c[0] = g.modPow(k, p); - c[1] = y.modPow(k, p).multiply(m).mod(p); - return c; +const zero = new BN(0); + +export default { + /* + * m, p, g, y are all BN + * returns { c1: BN, c2: BN } + */ + encrypt: function(m, p, g, y) { + const redp = new BN.red(p); + const mred = m.toRed(redp); + const gred = g.toRed(redp); + const yred = y.toRed(redp); + // See Section 11.5 here: https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf + const k = random.getRandomBN(zero, p); // returns in [0, p-1] + return { + c1: gred.redPow(k).fromRed(), + c2: yred.redPow(k).redMul(mred).fromRed() + }; + }, + + /* + * c1, c2, p, x are all BN + * returns BN + */ + decrypt: function(c1, c2, p, x) { + const redp = new BN.red(p); + const c1red = c1.toRed(redp); + const c2red = c2.toRed(redp); + return c1red.redPow(x).redInvm().redMul(c2red).fromRed(); } - - function decrypt(c1, c2, p, x) { - util.print_debug("Elgamal Decrypt:\nc1:" + util.hexstrdump(c1.toMPI()) + "\n" + - "c2:" + util.hexstrdump(c2.toMPI()) + "\n" + - "p:" + util.hexstrdump(p.toMPI()) + "\n" + - "x:" + util.hexstrdump(x.toMPI())); - return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p); - //var c = c1.pow(x).modInverse(p); // c0^-a mod p - //return c.multiply(c2).mod(p); - } - - // signing and signature verification using Elgamal is not required by OpenPGP. - this.encrypt = encrypt; - this.decrypt = decrypt; -} +}; diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 1814f615..cf72e1a6 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -18,46 +18,45 @@ // Wrapper of an instance of an Elliptic Curve /** + * @requires bn.js + * @requires elliptic * @requires crypto/public_key/elliptic/key - * @requires crypto/public_key/jsbn + * @requires crypto/random + * @requires type/oid * @requires enums * @requires util * @module crypto/public_key/elliptic/curve */ +import BN from 'bn.js'; import { ec as EC, eddsa as EdDSA } from 'elliptic'; -import { KeyPair } from './key'; -import BigInteger from '../jsbn'; +import KeyPair from './key'; import random from '../../random'; 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(); -let webCurves = {}; -let nodeCurves = {}; -webCurves = { +const nodeCurves = {}; +const webCurves = { 'p256': 'P-256', 'p384': 'P-384', 'p521': 'P-521' }; if (nodeCrypto) { const 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 - }; + nodeCurves.secp256k1 = knownCurves.includes('secp256k1') ? 'secp256k1' : undefined; + nodeCurves.p256 = knownCurves.includes('prime256v1') ? 'prime256v1' : undefined; + nodeCurves.p384 = knownCurves.includes('secp384r1') ? 'secp384r1' : undefined; + nodeCurves.p521 = knownCurves.includes('secp521r1') ? 'secp521r1' : undefined; + // TODO add more here } const curves = { p256: { - oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), + oid: util.Uint8Array_to_str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), keyType: enums.publicKey.ecdsa, hash: enums.hash.sha256, cipher: enums.symmetric.aes128, @@ -66,7 +65,7 @@ const curves = { payloadSize: 32 }, p384: { - oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]), + oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x22]), keyType: enums.publicKey.ecdsa, hash: enums.hash.sha384, cipher: enums.symmetric.aes192, @@ -75,7 +74,7 @@ const curves = { payloadSize: 48 }, p521: { - oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]), + oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x23]), keyType: enums.publicKey.ecdsa, hash: enums.hash.sha512, cipher: enums.symmetric.aes256, @@ -84,54 +83,68 @@ const curves = { payloadSize: 66 }, secp256k1: { - oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]), + oid: util.Uint8Array_to_str([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]), + oid: util.Uint8Array_to_str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), keyType: enums.publicKey.eddsa, hash: enums.hash.sha512, payloadSize: 32 }, curve25519: { - oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), + oid: util.Uint8Array_to_str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), 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]) + oid: util.Uint8Array_to_str([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]) + oid: util.Uint8Array_to_str([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]) + oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D]) } }; -function Curve(name, params) { +export default function Curve(oid_or_name, params) { + if (OID.prototype.isPrototypeOf(oid_or_name) && + enums.curve[oid_or_name.toHex()]) { + this.name = oid_or_name.toHex(); // by curve OID + } else if (enums.curve[oid_or_name]) { + this.name = oid_or_name; // by curve name + } else if (enums.curve[util.str_to_hex(oid_or_name)]) { + this.name = util.str_to_hex(oid_or_name); // by oid string + } else { + throw new Error('Not valid curve'); + } + this.name = enums.write(enums.curve, this.name); + this.oid = new OID(curves[this.name].oid); + + params = params || curves[this.name]; + this.keyType = params.keyType; switch (this.keyType) { case enums.publicKey.eddsa: - this.curve = new EdDSA(name); + this.curve = new EdDSA(this.name); break; case enums.publicKey.ecdsa: - this.curve = new EC(name); + this.curve = new EC(this.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; - this.node = params.node && curves[name].node; - this.web = params.web && curves[name].web; - this.payloadSize = curves[name].payloadSize; + this.node = params.node && curves[this.name].node; + this.web = params.web && curves[this.name].web; + this.payloadSize = curves[this.name].payloadSize; } Curve.prototype.keyFromPrivate = function (priv) { // Not for ed25519 @@ -152,47 +165,33 @@ Curve.prototype.genKeyPair = async function () { // 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 && this.node) { keyPair = await nodeGenKeyPair(this.name); - return new KeyPair(this.curve, keyPair); } - const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; - const 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() }; + + if (!keyPair || !keyPair.priv) { + // elliptic fallback + const r = await this.curve.genKeyPair({ entropy: util.Uint8Array_to_str(random.getRandomBytes(32)) }); + const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; + if (this.keyType === enums.publicKey.eddsa) { + keyPair = { secret: r.getSecret() }; + } else { + keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() }; + } } return new KeyPair(this.curve, keyPair); }; -function get(oid_or_name) { - let 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]); - } - throw new Error('Not valid curve'); -} - async function generate(curve) { - curve = get(curve); + curve = new Curve(curve); const keyPair = await curve.genKeyPair(); return { oid: curve.oid, - Q: new BigInteger(keyPair.getPublic()), - d: new BigInteger(keyPair.getPrivate()), + Q: new BN(keyPair.getPublic()), + d: new BN(keyPair.getPrivate()), hash: curve.hash, cipher: curve.cipher }; @@ -202,14 +201,8 @@ function getPreferredHashAlgo(oid) { return curves[enums.write(enums.curve, oid.toHex())].hash; } -module.exports = { - Curve, - curves, - webCurves, - nodeCurves, - getPreferredHashAlgo, - generate, - get +export { + curves, webCurves, nodeCurves, generate, getPreferredHashAlgo }; @@ -229,10 +222,10 @@ async function webGenKeyPair(name) { return { pub: { - x: base64.decode(publicKey.x, true), - y: base64.decode(publicKey.y, true) + x: util.b64_to_Uint8Array(publicKey.x, true), + y: util.b64_to_Uint8Array(publicKey.y, true) }, - priv: base64.decode(privateKey.d, true) + priv: util.b64_to_Uint8Array(privateKey.d, true) }; } diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index fdc97336..315492d8 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -19,10 +19,9 @@ /** * @requires crypto/public_key/elliptic/curves - * @requires crypto/public_key/jsbn + * @requires crypto/aes_kw * @requires crypto/cipher * @requires crypto/hash - * @requires crypto/aes_kw * @requires type/oid * @requires type/kdf_params * @requires enums @@ -30,26 +29,24 @@ * @module crypto/public_key/elliptic/ecdh */ -import curves from './curves'; -import BigInteger from '../jsbn'; +import BN from 'bn.js'; +import Curve from './curves'; +import aes_kw from '../../aes_kw'; import cipher from '../../cipher'; import hash from '../../hash'; -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) 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]); return util.concatUint8Array([ oid.write(), new Uint8Array([public_algo]), kdf_params.write(), - util.str2Uint8Array("Anonymous Sender "), + util.str_to_Uint8Array("Anonymous Sender "), fingerprint ]); } @@ -67,26 +64,26 @@ function kdf(hash_algo, X, length, param) { /** * Encrypt and wrap a session key * - * @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) - * @param {BigInteger} Q Recipient public key - * @param {String} fingerprint Recipient fingerprint - * @return {{V: BigInteger, C: Uint8Array}} Returns ephemeral key and encoded session key + * @param {module:type/oid} oid Elliptic curve object identifier + * @param {Enums} cipher_algo Symmetric cipher to use + * @param {Enums} hash_algo Hash algorithm to use + * @param {module:type/mpi} m Value derived from session key (RFC 6637) + * @param {Uint8Array} Q Recipient public key + * @param {String} fingerprint Recipient fingerprint + * @return {{V: BN, C: BN}} Returns ephemeral key and encoded session key */ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { - fingerprint = util.hex2Uint8Array(fingerprint); + fingerprint = util.hex_to_Uint8Array(fingerprint); + const curve = new Curve(oid); 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 = await curve.genKeyPair(); - Q = curve.keyFromPublic(Q.toByteArray()); + Q = curve.keyFromPublic(Q); const S = v.derive(Q); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - const C = aes_kw.wrap(Z, m.toBytes()); + const C = aes_kw.wrap(Z, m.toString()); return { - V: new BigInteger(v.getPublic()), + V: new BN(v.getPublic()), C: C }; } @@ -94,30 +91,25 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { /** * Decrypt and unwrap the value derived from session key * - * @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 - * @param {Uint8Array} C Encrypted and wrapped value derived from session key - * @param {BigInteger} d Recipient private key - * @param {String} fingerprint Recipient fingerprint - * @return {Uint8Array} Value derived from session + * @param {module:type/oid} oid Elliptic curve object identifier + * @param {Enums} cipher_algo Symmetric cipher to use + * @param {Enums} hash_algo Hash algorithm to use + * @param {BN} V Public part of ephemeral key + * @param {Uint8Array} C Encrypted and wrapped value derived from session key + * @param {Uint8Array} d Recipient private key + * @param {String} fingerprint Recipient fingerprint + * @return {Uint8Array} Value derived from session */ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { - fingerprint = util.hex2Uint8Array(fingerprint); + fingerprint = util.hex_to_Uint8Array(fingerprint); + const curve = new Curve(oid); 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()); - d = curve.keyFromPrivate(d.toByteArray()); + V = curve.keyFromPublic(V); + d = curve.keyFromPrivate(d); const S = d.derive(V); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - return new BigInteger(aes_kw.unwrap(Z, C)); + return new BN(aes_kw.unwrap(Z, C)); } -module.exports = { - buildEcdhParam: buildEcdhParam, - kdf: kdf, - encrypt: encrypt, - decrypt: decrypt -}; +export default { encrypt, decrypt }; diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index b64d2c69..c9b1549d 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -19,51 +19,44 @@ /** * @requires crypto/hash - * @requires crypto/public_key/jsbn * @requires crypto/public_key/elliptic/curves * @module crypto/public_key/elliptic/ecdsa */ import hash from '../../hash'; -import curves from './curves'; -import BigInteger from '../jsbn'; +import Curve from './curves'; /** * 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 {module:type/oid} oid Elliptic curve object identifier + * @param {enums.hash} hash_algo Hash algorithm used to sign + * @param {Uint8Array} m Message to sign + * @param {Uint8Array} d Private key used to sign the message + * @return {{r: Uint8Array, + s: Uint8Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = curves.get(oid); - const key = curve.keyFromPrivate(d.toByteArray()); + const curve = new Curve(oid); + const key = curve.keyFromPrivate(d); const signature = await key.sign(m, hash_algo); - return { - r: new BigInteger(signature.r.toArray()), - s: new BigInteger(signature.s.toArray()) - }; + return { r: signature.r.toArrayLike(Uint8Array), + s: signature.s.toArrayLike(Uint8Array) }; } /** * 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 + * @param {module:type/oid} oid Elliptic curve object identifier + * @param {enums.hash} hash_algo Hash algorithm used in the signature + * @param {{r: Uint8Array, + s: Uint8Array}} signature Signature to verify + * @param {Uint8Array} m Message to verify + * @param {Uint8Array} Q Public key used to verify the message * @return {Boolean} */ async 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 - ); + const curve = new Curve(oid); + const key = curve.keyFromPublic(Q); + return key.verify(m, signature, hash_algo); } -module.exports = { - sign: sign, - verify: verify -}; +export default { sign, verify }; diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 6145b7cd..4db75099 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -26,49 +26,40 @@ import BN from 'bn.js'; import hash from '../../hash'; -import curves from './curves'; +import Curve from './curves'; /** * 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: BN, S: BN}} Signature of the message + * @param {module:type/oid} oid Elliptic curve object identifier + * @param {enums.hash} hash_algo Hash algorithm used to sign + * @param {Uint8Array} m Message to sign + * @param {Uint8Array} d Private key used to sign + * @return {{R: Uint8Array, + S: Uint8Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = curves.get(oid); - const key = curve.keyFromSecret(d.toByteArray()); + const curve = new Curve(oid); + const key = curve.keyFromSecret(d); const signature = await key.sign(m, hash_algo); // EdDSA signature params are returned in little-endian format - return { - R: new BN(Array.from(signature.Rencoded()).reverse()), - S: new BN(Array.from(signature.Sencoded()).reverse()) - }; + return { R: new Uint8Array(signature.Rencoded()), + S: new Uint8Array(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 + * @param {module:type/oid} oid Elliptic curve object identifier + * @param {enums.hash} hash_algo Hash algorithm used in the signature + * @param {{R: Uint8Array, + S: Uint8Array}} signature Signature to verify the message + * @param {Uint8Array} m Message to verify + * @param {Uint8Array} Q Public key used to verify the message * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - const curve = curves.get(oid); - const key = curve.keyFromPublic(Q.toByteArray()); - // EdDSA signature params are expected in little-endian format - const R = Array.from(signature.R.toByteArray()).reverse(); - const S = Array.from(signature.S.toByteArray()).reverse(); - return key.verify(m, { - R: [].concat(R, Array(curve.payloadSize - R.length).fill(0)), - S: [].concat(S, Array(curve.payloadSize - S.length).fill(0)) - }, hash_algo); + const curve = new Curve(oid); + const key = curve.keyFromPublic(Q); + return key.verify(m, signature, hash_algo); } -module.exports = { - sign: sign, - verify: verify -}; +export default { sign, verify }; diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index 35bfdb83..cffe5dc4 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -25,16 +25,11 @@ * @module crypto/public_key/elliptic */ -import { get, generate, getPreferredHashAlgo } from './curves'; +import Curve, { generate, getPreferredHashAlgo } from './curves'; import ecdsa from './ecdsa'; import eddsa from './eddsa'; import ecdh from './ecdh'; -module.exports = { - ecdsa: ecdsa, - eddsa: eddsa, - ecdh: ecdh, - get: get, - generate: generate, - getPreferredHashAlgo: getPreferredHashAlgo +export default { + Curve, ecdh, ecdsa, eddsa, generate, getPreferredHashAlgo }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index f2d42ba0..b728b99b 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -18,32 +18,24 @@ // Wrapper for a KeyPair of an Elliptic Curve /** + * @requires bn.js * @requires crypto/public_key/elliptic/curves - * @requires crypto/public_key/jsbn * @requires crypto/hash * @requires util * @requires enums - * @requires encoding/base64 * @requires jwk-to-pem * @requires asn1.js * @module crypto/public_key/elliptic/key */ -import curves from './curves'; -import BigInteger from '../jsbn'; +import BN from 'bn.js'; +import { webCurves, nodeCurves } from './curves'; import hash from '../../hash'; import util from '../../../util'; import enums from '../../../enums'; -import base64 from '../../../encoding/base64'; const webCrypto = util.getWebCrypto(); -const { webCurves } = curves; const nodeCrypto = util.getNodeCrypto(); -const { nodeCurves } = curves; - -// const webCrypto = util.getWebCrypto(); -// const nodeCrypto = util.getNodeCrypto(); -// const { webCurves, nodeCurves } = curves; const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined; const ECDSASignature = nodeCrypto ? @@ -54,16 +46,13 @@ const ECDSASignature = nodeCrypto ? ); }) : undefined; -function KeyPair(curve, options) { +export default 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); } KeyPair.prototype.sign = async function (message, hash_algo) { - if (util.isString(message)) { - message = util.str2Uint8Array(message); - } if (webCrypto && this.curve.web) { // If browser doesn't support a curve, we'll catch it try { @@ -79,9 +68,6 @@ KeyPair.prototype.sign = async function (message, hash_algo) { }; KeyPair.prototype.verify = async function (message, signature, hash_algo) { - if (util.isString(message)) { - message = util.str2Uint8Array(message); - } if (webCrypto && this.curve.web) { // If browser doesn't support a curve, we'll catch it try { @@ -115,10 +101,6 @@ KeyPair.prototype.getPrivate = function () { return this.keyPair.getPrivate().toArray(); }; -module.exports = { - KeyPair: KeyPair -}; - ////////////////////////// // // @@ -134,9 +116,9 @@ async function webSign(curve, hash_algo, message, keyPair) { { "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), + "x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), true), + "y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), true), + "d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray('be', l)), true), "use": "sig", "kid": "ECDSA Private Key" }, @@ -174,8 +156,8 @@ async function webVerify(curve, hash_algo, { r, s }, message, publicKey) { { "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), + "x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray('be', l)), true), + "y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray('be', l)), true), "use": "sig", "kid": "ECDSA Public Key" }, @@ -202,13 +184,23 @@ async function webVerify(curve, hash_algo, { r, s }, message, publicKey) { async function nodeSign(curve, hash_algo, message, keyPair) { + console.log({ + "kty": "EC", + "crv": webCurves[curve.name], + "x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray())), + "y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray())), + "d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray())), + "use": "sig", + "kid": "ECDSA Private Key" + }); + 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())), + "x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray())), + "y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray())), + "d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray())), "use": "sig", "kid": "ECDSA Private Key" }, @@ -226,13 +218,13 @@ async function nodeSign(curve, hash_algo, message, keyPair) { } async function nodeVerify(curve, hash_algo, { r, s }, message, publicKey) { - const signature = ECDSASignature.encode({ r: new BigInteger(r), s: new BigInteger(s) }, 'der'); + const 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())), + "x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray())), + "y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray())), "use": "sig", "kid": "ECDSA Public Key" }, diff --git a/src/crypto/public_key/index.js b/src/crypto/public_key/index.js index ae4aac89..927f0c52 100644 --- a/src/crypto/public_key/index.js +++ b/src/crypto/public_key/index.js @@ -7,13 +7,13 @@ */ /** @see module:crypto/public_key/rsa */ -import rsa from './rsa.js'; +import rsa from './rsa'; /** @see module:crypto/public_key/elgamal */ -import elgamal from './elgamal.js'; +import elgamal from './elgamal'; /** @see module:crypto/public_key/elliptic */ import elliptic from './elliptic'; /** @see module:crypto/public_key/dsa */ -import dsa from './dsa.js'; +import dsa from './dsa'; export default { rsa: rsa, diff --git a/src/crypto/public_key/jsbn.js b/src/crypto/public_key/jsbn.js deleted file mode 100644 index bd550d6d..00000000 --- a/src/crypto/public_key/jsbn.js +++ /dev/null @@ -1,1691 +0,0 @@ -/* - * Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU) - * All Rights Reserved. - * - * Modified by Recurity Labs GmbH - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, - * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * - * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF - * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * In addition, the following condition applies: - * - * All redistributions must retain an intact copy of this copyright notice - * and disclaimer. - */ - -/* eslint-disable no-mixed-operators */ -/* eslint-disable no-eq-null */ -/* eslint-disable eqeqeq */ -/* eslint-disable no-cond-assign */ -/* eslint-disable one-var */ - -/** - * @requires util - * @module crypto/public_key/jsbn - */ - -import util from '../../util'; - -// Basic JavaScript BN library - subset useful for RSA encryption. - -// Bits per digit -let dbits; - -// JavaScript engine analysis -const canary = 0xdeadbeefcafe; -const j_lm = ((canary & 0xffffff) == 0xefcafe); - -// (public) Constructor - -export default function BigInteger(a, b, c) { - if (a != null) { if (typeof a === "number") this.fromNumber(a, b, c); - else if (b == null && !util.isString(a)) this.fromString(a, 256); - else this.fromString(a, b); } -} - -// return new, unset BigInteger - -function nbi() { - return new BigInteger(null); -} - -// am: Compute w_j += (x*this_i), propagate carries, -// c is initial carry, returns final carry. -// c < 3*dvalue, x < 2*dvalue, this_i < dvalue -// We need to select the fastest one that works in this environment. - -// am1: use a single mult and divide to get the high bits, -// max digit bits should be 26 because -// max internal value = 2*dvalue^2-2*dvalue (< 2^53) - -function am1(i, x, w, j, c, n) { - while (--n >= 0) { - const v = x * this[i++] + w[j] + c; - c = Math.floor(v / 0x4000000); - w[j++] = v & 0x3ffffff; - } - return c; -} -// am2 avoids a big mult-and-extract completely. -// Max digit bits should be <= 30 because we do bitwise ops -// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) - -function am2(i, x, w, j, c, n) { - const xl = x & 0x7fff; - const xh = x >> 15; - while (--n >= 0) { - let l = this[i] & 0x7fff; - const h = this[i++] >> 15; - const m = xh * l + h * xl; - l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff); - c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30); - w[j++] = l & 0x3fffffff; - } - return c; -} -// Alternately, set max digit bits to 28 since some -// browsers slow down when dealing with 32-bit numbers. - -function am3(i, x, w, j, c, n) { - const xl = x & 0x3fff; - const xh = x >> 14; - while (--n >= 0) { - let l = this[i] & 0x3fff; - const h = this[i++] >> 14; - const m = xh * l + h * xl; - l = xl * l + ((m & 0x3fff) << 14) + w[j] + c; - c = (l >> 28) + (m >> 14) + xh * h; - w[j++] = l & 0xfffffff; - } - return c; -} -/*if(j_lm && (navigator != undefined && - navigator.appName == "Microsoft Internet Explorer")) { - BigInteger.prototype.am = am2; - dbits = 30; -} -else if(j_lm && (navigator != undefined && navigator.appName != "Netscape")) {*/ -BigInteger.prototype.am = am1; -dbits = 26; // eslint-disable-line -/*} -else { // Mozilla/Netscape seems to prefer am3 - BigInteger.prototype.am = am3; - dbits = 28; -}*/ - -BigInteger.prototype.DB = dbits; -BigInteger.prototype.DM = ((1 << dbits) - 1); -BigInteger.prototype.DV = (1 << dbits); - -const BI_FP = 52; -BigInteger.prototype.FV = (2 ** BI_FP); -BigInteger.prototype.F1 = BI_FP - dbits; -BigInteger.prototype.F2 = 2 * dbits - BI_FP; - -// Digit conversions -const BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"; -const BI_RC = new Array(); -let rr; -let vv; -rr = "0".charCodeAt(0); -for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv; -rr = "a".charCodeAt(0); -for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; -rr = "A".charCodeAt(0); -for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv; - -function int2char(n) { - return BI_RM.charAt(n); -} - -function intAt(s, i) { - const c = BI_RC[s.charCodeAt(i)]; - return (c == null) ? -1 : c; -} - -// (protected) copy this to r - -function bnpCopyTo(r) { - for (let i = this.t - 1; i >= 0; --i) r[i] = this[i]; - r.t = this.t; - r.s = this.s; -} - -// (protected) set from integer value x, -DV <= x < DV - -function bnpFromInt(x) { - this.t = 1; - this.s = (x < 0) ? -1 : 0; - if (x > 0) this[0] = x; - else if (x < -1) this[0] = x + this.DV; - else this.t = 0; -} - -// return bigint initialized to value - -function nbv(i) { - const r = nbi(); - r.fromInt(i); - return r; -} - -// (protected) set from string and radix - -function bnpFromString(s, b) { - let k; - if (b == 16) k = 4; - else if (b == 8) k = 3; - else if (b == 256) k = 8; // byte array - else if (b == 2) k = 1; - else if (b == 32) k = 5; - else if (b == 4) k = 2; - else { - this.fromRadix(s, b); - return; - } - this.t = 0; - this.s = 0; - let i = s.length; - let mi = false; - let sh = 0; - while (--i >= 0) { - const x = (k == 8) ? s[i] & 0xff : intAt(s, i); - if (x < 0) { - if (s.charAt(i) == "-") mi = true; - continue; - } - mi = false; - if (sh == 0) this[this.t++] = x; - else if (sh + k > this.DB) { - this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh; - this[this.t++] = (x >> (this.DB - sh)); - } else this[this.t - 1] |= x << sh; - sh += k; - if (sh >= this.DB) sh -= this.DB; - } - if (k == 8 && (s[0] & 0x80) != 0) { - this.s = -1; - if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh; - } - this.clamp(); - if (mi) BigInteger.ZERO.subTo(this, this); -} - -// (protected) clamp off excess high words - -function bnpClamp() { - const c = this.s & this.DM; - while (this.t > 0 && this[this.t - 1] == c)--this.t; -} - -// (public) return string representation in given radix - -function bnToString(b) { - if (this.s < 0) return "-" + this.negate().toString(b); - let k; - if (b == 16) k = 4; - else if (b == 8) k = 3; - else if (b == 2) k = 1; - else if (b == 32) k = 5; - else if (b == 4) k = 2; - else return this.toRadix(b); - const km = (1 << k) - 1; - let d = false; - let m = false; - let r = ""; - let i = this.t; - let p = this.DB - (i * this.DB) % k; - if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) > 0) { - m = true; - r = int2char(d); - } - while (i >= 0) { - if (p < k) { - d = (this[i] & ((1 << p) - 1)) << (k - p); - d |= this[--i] >> (p += this.DB - k); - } else { - d = (this[i] >> (p -= k)) & km; - if (p <= 0) { - p += this.DB; - --i; - } - } - if (d > 0) m = true; - if (m) r += int2char(d); - } - } - return m ? r : "0"; -} - -// (public) -this - -function bnNegate() { - const r = nbi(); - BigInteger.ZERO.subTo(this, r); - return r; -} - -// (public) |this| - -function bnAbs() { - return (this.s < 0) ? this.negate() : this; -} - -// (public) return + if this > a, - if this < a, 0 if equal - -function bnCompareTo(a) { - let r = this.s - a.s; - if (r != 0) return r; - let i = this.t; - r = i - a.t; - if (r != 0) return (this.s < 0) ? -r : r; - while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r; - return 0; -} - -// returns bit length of the integer x - -function nbits(x) { - let r = 1; - let t; - if ((t = x >>> 16) != 0) { - x = t; - r += 16; - } - if ((t = x >> 8) != 0) { - x = t; - r += 8; - } - if ((t = x >> 4) != 0) { - x = t; - r += 4; - } - if ((t = x >> 2) != 0) { - x = t; - r += 2; - } - if ((t = x >> 1) != 0) { - x = t; - r += 1; - } - return r; -} - -// (public) return the number of bits in "this" - -function bnBitLength() { - if (this.t <= 0) return 0; - return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM)); -} - -// (protected) r = this << n*DB - -function bnpDLShiftTo(n, r) { - let i; - for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i]; - for (i = n - 1; i >= 0; --i) r[i] = 0; - r.t = this.t + n; - r.s = this.s; -} - -// (protected) r = this >> n*DB - -function bnpDRShiftTo(n, r) { - for (let i = n; i < this.t; ++i) r[i - n] = this[i]; - r.t = Math.max(this.t - n, 0); - r.s = this.s; -} - -// (protected) r = this << n - -function bnpLShiftTo(n, r) { - const bs = n % this.DB; - const cbs = this.DB - bs; - const bm = (1 << cbs) - 1; - const ds = Math.floor(n / this.DB); - let c = (this.s << bs) & this.DM; - let i; - for (i = this.t - 1; i >= 0; --i) { - r[i + ds + 1] = (this[i] >> cbs) | c; - c = (this[i] & bm) << bs; - } - for (i = ds - 1; i >= 0; --i) r[i] = 0; - r[ds] = c; - r.t = this.t + ds + 1; - r.s = this.s; - r.clamp(); -} - -// (protected) r = this >> n - -function bnpRShiftTo(n, r) { - r.s = this.s; - const ds = Math.floor(n / this.DB); - if (ds >= this.t) { - r.t = 0; - return; - } - const bs = n % this.DB; - const cbs = this.DB - bs; - const bm = (1 << bs) - 1; - r[0] = this[ds] >> bs; - for (let i = ds + 1; i < this.t; ++i) { - r[i - ds - 1] |= (this[i] & bm) << cbs; - r[i - ds] = this[i] >> bs; - } - if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs; - r.t = this.t - ds; - r.clamp(); -} - -// (protected) r = this - a - -function bnpSubTo(a, r) { - let i = 0; - let c = 0; - const m = Math.min(a.t, this.t); - while (i < m) { - c += this[i] - a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - if (a.t < this.t) { - c -= a.s; - while (i < this.t) { - c += this[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += this.s; - } else { - c += this.s; - while (i < a.t) { - c -= a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c -= a.s; - } - r.s = (c < 0) ? -1 : 0; - if (c < -1) r[i++] = this.DV + c; - else if (c > 0) r[i++] = c; - r.t = i; - r.clamp(); -} - -// (protected) r = this * a, r != this,a (HAC 14.12) -// "this" should be the larger one if appropriate. - -function bnpMultiplyTo(a, r) { - const x = this.abs(); - const y = a.abs(); - let i = x.t; - r.t = i + y.t; - while (--i >= 0) r[i] = 0; - for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t); - r.s = 0; - r.clamp(); - if (this.s != a.s) BigInteger.ZERO.subTo(r, r); -} - -// (protected) r = this^2, r != this (HAC 14.16) - -function bnpSquareTo(r) { - const x = this.abs(); - let i = r.t = 2 * x.t; - while (--i >= 0) r[i] = 0; - for (i = 0; i < x.t - 1; ++i) { - const c = x.am(i, x[i], r, 2 * i, 0, 1); - if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) { - r[i + x.t] -= x.DV; - r[i + x.t + 1] = 1; - } - } - if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1); - r.s = 0; - r.clamp(); -} - -// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) -// r != q, this != m. q or r may be null. - -function bnpDivRemTo(m, q, r) { - const pm = m.abs(); - if (pm.t <= 0) return; - const pt = this.abs(); - if (pt.t < pm.t) { - if (q != null) q.fromInt(0); - if (r != null) this.copyTo(r); - return; - } - if (r == null) r = nbi(); - const y = nbi(); - const ts = this.s; - const ms = m.s; - const nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus - if (nsh > 0) { - pm.lShiftTo(nsh, y); - pt.lShiftTo(nsh, r); - } else { - pm.copyTo(y); - pt.copyTo(r); - } - const ys = y.t; - const y0 = y[ys - 1]; - if (y0 == 0) return; - const yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0); - const d1 = this.FV / yt; - const d2 = (1 << this.F1) / yt; - const e = 1 << this.F2; - let i = r.t; - let j = i - ys; - const t = (q == null) ? nbi() : q; - y.dlShiftTo(j, t); - if (r.compareTo(t) >= 0) { - r[r.t++] = 1; - r.subTo(t, r); - } - BigInteger.ONE.dlShiftTo(ys, t); - t.subTo(y, y); // "negative" y so we can replace sub with am later - while (y.t < ys) y[y.t++] = 0; - while (--j >= 0) { - // Estimate quotient digit - let qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2); - if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out - y.dlShiftTo(j, t); - r.subTo(t, r); - while (r[i] < --qd) r.subTo(t, r); - } - } - if (q != null) { - r.drShiftTo(ys, q); - if (ts != ms) BigInteger.ZERO.subTo(q, q); - } - r.t = ys; - r.clamp(); - if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder - if (ts < 0) BigInteger.ZERO.subTo(r, r); -} - -// (public) this mod a - -function bnMod(a) { - const r = nbi(); - this.abs().divRemTo(a, null, r); - if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r); - return r; -} - -// Modular reduction using "classic" algorithm - -function Classic(m) { - this.m = m; -} - -function cConvert(x) { - if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); - else return x; -} - -function cRevert(x) { - return x; -} - -function cReduce(x) { - x.divRemTo(this.m, null, x); -} - -function cMulTo(x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -} - -function cSqrTo(x, r) { - x.squareTo(r); - this.reduce(r); -} - -Classic.prototype.convert = cConvert; -Classic.prototype.revert = cRevert; -Classic.prototype.reduce = cReduce; -Classic.prototype.mulTo = cMulTo; -Classic.prototype.sqrTo = cSqrTo; - -// (protected) return "-1/this % 2^DB"; useful for Mont. reduction -// justification: -// xy == 1 (mod m) -// xy = 1+km -// xy(2-xy) = (1+km)(1-km) -// x[y(2-xy)] = 1-k^2m^2 -// x[y(2-xy)] == 1 (mod m^2) -// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 -// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. -// JS multiply "overflows" differently from C/C++, so care is needed here. - -function bnpInvDigit() { - if (this.t < 1) return 0; - const x = this[0]; - if ((x & 1) == 0) return 0; - let y = x & 3; // y == 1/x mod 2^2 - y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4 - y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8 - y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16 - // last step - calculate inverse mod DV directly; - // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints - y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits - // we really want the negative inverse, and -DV < y < DV - return (y > 0) ? this.DV - y : -y; -} - -// Montgomery reduction - -function Montgomery(m) { - this.m = m; - this.mp = m.invDigit(); - this.mpl = this.mp & 0x7fff; - this.mph = this.mp >> 15; - this.um = (1 << (m.DB - 15)) - 1; - this.mt2 = 2 * m.t; -} - -// xR mod m - -function montConvert(x) { - const r = nbi(); - x.abs().dlShiftTo(this.m.t, r); - r.divRemTo(this.m, null, r); - if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r); - return r; -} - -// x/R mod m - -function montRevert(x) { - const r = nbi(); - x.copyTo(r); - this.reduce(r); - return r; -} - -// x = x/R mod m (HAC 14.32) - -function montReduce(x) { - while (x.t <= this.mt2) // pad x so am has enough room later - { x[x.t++] = 0; } - for (let i = 0; i < this.m.t; ++i) { - // faster way of calculating u0 = x[i]*mp mod DV - let j = x[i] & 0x7fff; - const u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM; - // use am to combine the multiply-shift-add into one call - j = i + this.m.t; - x[j] += this.m.am(0, u0, x, i, 0, this.m.t); - // propagate carry - while (x[j] >= x.DV) { - x[j] -= x.DV; - x[++j]++; - } - } - x.clamp(); - x.drShiftTo(this.m.t, x); - if (x.compareTo(this.m) >= 0) x.subTo(this.m, x); -} - -// r = "x^2/R mod m"; x != r - -function montSqrTo(x, r) { - x.squareTo(r); - this.reduce(r); -} - -// r = "xy/R mod m"; x,y != r - -function montMulTo(x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -} - -Montgomery.prototype.convert = montConvert; -Montgomery.prototype.revert = montRevert; -Montgomery.prototype.reduce = montReduce; -Montgomery.prototype.mulTo = montMulTo; -Montgomery.prototype.sqrTo = montSqrTo; - -// (protected) true iff this is even - -function bnpIsEven() { - return ((this.t > 0) ? (this[0] & 1) : this.s) == 0; -} - -// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) - -function bnpExp(e, z) { - if (e > 0xffffffff || e < 1) return BigInteger.ONE; - let r = nbi(); - let r2 = nbi(); - const g = z.convert(this); - let i = nbits(e) - 1; - g.copyTo(r); - while (--i >= 0) { - z.sqrTo(r, r2); - if ((e & (1 << i)) > 0) z.mulTo(r2, g, r); - else { - const t = r; - r = r2; - r2 = t; - } - } - return z.revert(r); -} - -// (public) this^e % m, 0 <= e < 2^32 - -function bnModPowInt(e, m) { - let z; - if (e < 256 || m.isEven()) z = new Classic(m); - else z = new Montgomery(m); - return this.exp(e, z); -} - -// protected -BigInteger.prototype.copyTo = bnpCopyTo; -BigInteger.prototype.fromInt = bnpFromInt; -BigInteger.prototype.fromString = bnpFromString; -BigInteger.prototype.clamp = bnpClamp; -BigInteger.prototype.dlShiftTo = bnpDLShiftTo; -BigInteger.prototype.drShiftTo = bnpDRShiftTo; -BigInteger.prototype.lShiftTo = bnpLShiftTo; -BigInteger.prototype.rShiftTo = bnpRShiftTo; -BigInteger.prototype.subTo = bnpSubTo; -BigInteger.prototype.multiplyTo = bnpMultiplyTo; -BigInteger.prototype.squareTo = bnpSquareTo; -BigInteger.prototype.divRemTo = bnpDivRemTo; -BigInteger.prototype.invDigit = bnpInvDigit; -BigInteger.prototype.isEven = bnpIsEven; -BigInteger.prototype.exp = bnpExp; - -// public -BigInteger.prototype.toString = bnToString; -BigInteger.prototype.negate = bnNegate; -BigInteger.prototype.abs = bnAbs; -BigInteger.prototype.compareTo = bnCompareTo; -BigInteger.prototype.bitLength = bnBitLength; -BigInteger.prototype.mod = bnMod; -BigInteger.prototype.modPowInt = bnModPowInt; - -// "constants" -BigInteger.ZERO = nbv(0); -BigInteger.ONE = nbv(1); -BigInteger.TWO = nbv(2); - - -/* - * Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU) - * All Rights Reserved. - * - * Modified by Recurity Labs GmbH - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, - * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY - * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. - * - * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER - * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF - * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT - * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * In addition, the following condition applies: - * - * All redistributions must retain an intact copy of this copyright notice - * and disclaimer. - */ - - -// Extended JavaScript BN functions, required for RSA private ops. - -// Version 1.1: new BigInteger("0", 10) returns "proper" zero -// Version 1.2: square() API, isProbablePrime fix - -// (public) -function bnClone() { - const r = nbi(); - this.copyTo(r); - return r; -} - -// (public) return value as integer - -function bnIntValue() { - if (this.s < 0) { - if (this.t == 1) return this[0] - this.DV; - else if (this.t == 0) return -1; - } else if (this.t == 1) return this[0]; - else if (this.t == 0) return 0; - // assumes 16 < DB < 32 - return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0]; -} - -// (public) return value as byte - -function bnByteValue() { - return (this.t == 0) ? this.s : (this[0] << 24) >> 24; -} - -// (public) return value as short (assumes DB>=16) - -function bnShortValue() { - return (this.t == 0) ? this.s : (this[0] << 16) >> 16; -} - -// (protected) return x s.t. r^x < DV - -function bnpChunkSize(r) { - return Math.floor(Math.LN2 * this.DB / Math.log(r)); -} - -// (public) 0 if this == 0, 1 if this > 0 - -function bnSigNum() { - if (this.s < 0) return -1; - else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; - else return 1; -} - -// (protected) convert to radix string - -function bnpToRadix(b) { - if (b == null) b = 10; - if (this.signum() == 0 || b < 2 || b > 36) return "0"; - const cs = this.chunkSize(b); - const a = (b ** cs); - const d = nbv(a); - const y = nbi(); - const z = nbi(); - let r = ""; - this.divRemTo(d, y, z); - while (y.signum() > 0) { - r = (a + z.intValue()).toString(b).substr(1) + r; - y.divRemTo(d, y, z); - } - return z.intValue().toString(b) + r; -} - -// (protected) convert from radix string - -function bnpFromRadix(s, b) { - this.fromInt(0); - if (b == null) b = 10; - const cs = this.chunkSize(b); - const d = (b ** cs); - let mi = false; - let j = 0; - let w = 0; - for (let i = 0; i < s.length; ++i) { - const x = intAt(s, i); - if (x < 0) { - if (s.charAt(i) == "-" && this.signum() == 0) mi = true; - continue; - } - w = b * w + x; - if (++j >= cs) { - this.dMultiply(d); - this.dAddOffset(w, 0); - j = 0; - w = 0; - } - } - if (j > 0) { - this.dMultiply((b ** j)); - this.dAddOffset(w, 0); - } - if (mi) BigInteger.ZERO.subTo(this, this); -} - -// (protected) alternate constructor - -function bnpFromNumber(a, b, c) { - if (typeof b === "number") { - // new BigInteger(int,int,RNG) - if (a < 2) this.fromInt(1); - else { - this.fromNumber(a, c); - if (!this.testBit(a - 1)) // force MSB set - { this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this); } - if (this.isEven()) this.dAddOffset(1, 0); // force odd - while (!this.isProbablePrime(b)) { - this.dAddOffset(2, 0); - if (this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a - 1), this); - } - } - } else { - // new BigInteger(int,RNG) - const x = new Array(); - const t = a & 7; - x.length = (a >> 3) + 1; - b.nextBytes(x); - if (t > 0) x[0] &= ((1 << t) - 1); - else x[0] = 0; - this.fromString(x, 256); - } -} - -// (public) convert to bigendian byte array - -function bnToByteArray() { - let i = this.t; - const r = new Array(); - r[0] = this.s; - let p = this.DB - (i * this.DB) % 8; - let d = 0; - let k = 0; - if (i-- > 0) { - if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p) r[k++] = d | (this.s << (this.DB - p)); - while (i >= 0) { - if (p < 8) { - d = (this[i] & ((1 << p) - 1)) << (8 - p); - d |= this[--i] >> (p += this.DB - 8); - } else { - d = (this[i] >> (p -= 8)) & 0xff; - if (p <= 0) { - p += this.DB; - --i; - } - } - //if((d&0x80) != 0) d |= -256; - //if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; - if (k > 0 || d != this.s) r[k++] = d; - } - } - return r; -} - -function bnEquals(a) { - return (this.compareTo(a) == 0); -} - -function bnMin(a) { - return (this.compareTo(a) < 0) ? this : a; -} - -function bnMax(a) { - return (this.compareTo(a) > 0) ? this : a; -} - -// (protected) r = this op a (bitwise) - -function bnpBitwiseTo(a, op, r) { - let i = Math.min(a.t, this.t); - let f = i; - const m = i; - for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]); - if (a.t < this.t) { - f = a.s & this.DM; - for (i = m; i < this.t; ++i) r[i] = op(this[i], f); - r.t = this.t; - } else { - f = this.s & this.DM; - for (i = m; i < a.t; ++i) r[i] = op(f, a[i]); - r.t = a.t; - } - r.s = op(this.s, a.s); - r.clamp(); -} - -// (public) this & a - -function op_and(x, y) { - return x & y; -} - -function bnAnd(a) { - const r = nbi(); - this.bitwiseTo(a, op_and, r); - return r; -} - -// (public) this | a - -function op_or(x, y) { - return x | y; -} - -function bnOr(a) { - const r = nbi(); - this.bitwiseTo(a, op_or, r); - return r; -} - -// (public) this ^ a - -function op_xor(x, y) { - return x ^ y; -} - -function bnXor(a) { - const r = nbi(); - this.bitwiseTo(a, op_xor, r); - return r; -} - -// (public) this & ~a - -function op_andnot(x, y) { - return x & ~y; -} - -function bnAndNot(a) { - const r = nbi(); - this.bitwiseTo(a, op_andnot, r); - return r; -} - -// (public) ~this - -function bnNot() { - const r = nbi(); - for (let i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i]; - r.t = this.t; - r.s = ~this.s; - return r; -} - -// (public) this << n - -function bnShiftLeft(n) { - const r = nbi(); - if (n < 0) this.rShiftTo(-n, r); - else this.lShiftTo(n, r); - return r; -} - -// (public) this >> n - -function bnShiftRight(n) { - const r = nbi(); - if (n < 0) this.lShiftTo(-n, r); - else this.rShiftTo(n, r); - return r; -} - -// return index of lowest 1-bit in x, x < 2^31 - -function lbit(x) { - if (x == 0) return -1; - let r = 0; - if ((x & 0xffff) == 0) { - x >>= 16; - r += 16; - } - if ((x & 0xff) == 0) { - x >>= 8; - r += 8; - } - if ((x & 0xf) == 0) { - x >>= 4; - r += 4; - } - if ((x & 3) == 0) { - x >>= 2; - r += 2; - } - if ((x & 1) == 0)++r; - return r; -} - -// (public) returns index of lowest 1-bit (or -1 if none) - -function bnGetLowestSetBit() { - for (let i = 0; i < this.t; ++i) if (this[i] != 0) return i * this.DB + lbit(this[i]); - if (this.s < 0) return this.t * this.DB; - return -1; -} - -// return number of 1 bits in x - -function cbit(x) { - let r = 0; - while (x != 0) { - x &= x - 1; - ++r; - } - return r; -} - -// (public) return number of set bits - -function bnBitCount() { - let r = 0; - const x = this.s & this.DM; - for (let i = 0; i < this.t; ++i) r += cbit(this[i] ^ x); - return r; -} - -// (public) true iff nth bit is set - -function bnTestBit(n) { - const j = Math.floor(n / this.DB); - if (j >= this.t) return (this.s != 0); - return ((this[j] & (1 << (n % this.DB))) != 0); -} - -// (protected) this op (1<>= this.DB; - } - if (a.t < this.t) { - c += a.s; - while (i < this.t) { - c += this[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += this.s; - } else { - c += this.s; - while (i < a.t) { - c += a[i]; - r[i++] = c & this.DM; - c >>= this.DB; - } - c += a.s; - } - r.s = (c < 0) ? -1 : 0; - if (c > 0) r[i++] = c; - else if (c < -1) r[i++] = this.DV + c; - r.t = i; - r.clamp(); -} - -// (public) this + a - -function bnAdd(a) { - const r = nbi(); - this.addTo(a, r); - return r; -} - -// (public) this - a - -function bnSubtract(a) { - const r = nbi(); - this.subTo(a, r); - return r; -} - -// (public) this * a - -function bnMultiply(a) { - const r = nbi(); - this.multiplyTo(a, r); - return r; -} - -// (public) this^2 - -function bnSquare() { - const r = nbi(); - this.squareTo(r); - return r; -} - -// (public) this / a - -function bnDivide(a) { - const r = nbi(); - this.divRemTo(a, r, null); - return r; -} - -// (public) this % a - -function bnRemainder(a) { - const r = nbi(); - this.divRemTo(a, null, r); - return r; -} - -// (public) [this/a,this%a] - -function bnDivideAndRemainder(a) { - const q = nbi(); - const r = nbi(); - this.divRemTo(a, q, r); - return new Array(q, r); -} - -// (protected) this *= n, this >= 0, 1 < n < DV - -function bnpDMultiply(n) { - this[this.t] = this.am(0, n - 1, this, 0, 0, this.t); - ++this.t; - this.clamp(); -} - -// (protected) this += n << w words, this >= 0 - -function bnpDAddOffset(n, w) { - if (n == 0) return; - while (this.t <= w) this[this.t++] = 0; - this[w] += n; - while (this[w] >= this.DV) { - this[w] -= this.DV; - if (++w >= this.t) this[this.t++] = 0; - ++this[w]; - } -} - -// A "null" reducer - -function NullExp() {} - -function nNop(x) { - return x; -} - -function nMulTo(x, y, r) { - x.multiplyTo(y, r); -} - -function nSqrTo(x, r) { - x.squareTo(r); -} - -NullExp.prototype.convert = nNop; -NullExp.prototype.revert = nNop; -NullExp.prototype.mulTo = nMulTo; -NullExp.prototype.sqrTo = nSqrTo; - -// (public) this^e - -function bnPow(e) { - return this.exp(e, new NullExp()); -} - -// (protected) r = lower n words of "this * a", a.t <= n -// "this" should be the larger one if appropriate. - -function bnpMultiplyLowerTo(a, n, r) { - let i = Math.min(this.t + a.t, n); - r.s = 0; // assumes a,this >= 0 - r.t = i; - while (i > 0) r[--i] = 0; - let j; - for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t); - for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i); - r.clamp(); -} - -// (protected) r = "this * a" without lower n words, n > 0 -// "this" should be the larger one if appropriate. - -function bnpMultiplyUpperTo(a, n, r) { - --n; - let i = r.t = this.t + a.t - n; - r.s = 0; // assumes a,this >= 0 - while (--i >= 0) r[i] = 0; - for (i = Math.max(n - this.t, 0); i < a.t; ++i) r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n); - r.clamp(); - r.drShiftTo(1, r); -} - -// Barrett modular reduction - -function Barrett(m) { - // setup Barrett - this.r2 = nbi(); - this.q3 = nbi(); - BigInteger.ONE.dlShiftTo(2 * m.t, this.r2); - this.mu = this.r2.divide(m); - this.m = m; -} - -function barrettConvert(x) { - if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m); - else if (x.compareTo(this.m) < 0) return x; - else { - const r = nbi(); - x.copyTo(r); - this.reduce(r); - return r; - } -} - -function barrettRevert(x) { - return x; -} - -// x = x mod m (HAC 14.42) - -function barrettReduce(x) { - x.drShiftTo(this.m.t - 1, this.r2); - if (x.t > this.m.t + 1) { - x.t = this.m.t + 1; - x.clamp(); - } - this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3); - this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2); - while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1); - x.subTo(this.r2, x); - while (x.compareTo(this.m) >= 0) x.subTo(this.m, x); -} - -// r = x^2 mod m; x != r - -function barrettSqrTo(x, r) { - x.squareTo(r); - this.reduce(r); -} - -// r = x*y mod m; x,y != r - -function barrettMulTo(x, y, r) { - x.multiplyTo(y, r); - this.reduce(r); -} - -Barrett.prototype.convert = barrettConvert; -Barrett.prototype.revert = barrettRevert; -Barrett.prototype.reduce = barrettReduce; -Barrett.prototype.mulTo = barrettMulTo; -Barrett.prototype.sqrTo = barrettSqrTo; - -// (public) this^e % m (HAC 14.85) - -function bnModPow(e, m) { - let i = e.bitLength(); - let k, r = nbv(1); - let z; - if (i <= 0) return r; - else if (i < 18) k = 1; - else if (i < 48) k = 3; - else if (i < 144) k = 4; - else if (i < 768) k = 5; - else k = 6; - if (i < 8) z = new Classic(m); - else if (m.isEven()) z = new Barrett(m); - else z = new Montgomery(m); - - // precomputation - const g = new Array(); - let n = 3; - const k1 = k - 1; - const km = (1 << k) - 1; - g[1] = z.convert(this); - if (k > 1) { - const g2 = nbi(); - z.sqrTo(g[1], g2); - while (n <= km) { - g[n] = nbi(); - z.mulTo(g2, g[n - 2], g[n]); - n += 2; - } - } - - let j = e.t - 1; - let w, is1 = true; - let r2 = nbi(); - let t; - i = nbits(e[j]) - 1; - while (j >= 0) { - if (i >= k1) w = (e[j] >> (i - k1)) & km; - else { - w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i); - if (j > 0) w |= e[j - 1] >> (this.DB + i - k1); - } - - n = k; - while ((w & 1) == 0) { - w >>= 1; - --n; - } - if ((i -= n) < 0) { - i += this.DB; - --j; - } - if (is1) { // ret == 1, don't bother squaring or multiplying it - g[w].copyTo(r); - is1 = false; - } else { - while (n > 1) { - z.sqrTo(r, r2); - z.sqrTo(r2, r); - n -= 2; - } - if (n > 0) z.sqrTo(r, r2); - else { - t = r; - r = r2; - r2 = t; - } - z.mulTo(r2, g[w], r); - } - - while (j >= 0 && (e[j] & (1 << i)) == 0) { - z.sqrTo(r, r2); - t = r; - r = r2; - r2 = t; - if (--i < 0) { - i = this.DB - 1; - --j; - } - } - } - return z.revert(r); -} - -// (public) gcd(this,a) (HAC 14.54) - -function bnGCD(a) { - let x = (this.s < 0) ? this.negate() : this.clone(); - let y = (a.s < 0) ? a.negate() : a.clone(); - if (x.compareTo(y) < 0) { - const t = x; - x = y; - y = t; - } - let i = x.getLowestSetBit(), - g = y.getLowestSetBit(); - if (g < 0) return x; - if (i < g) g = i; - if (g > 0) { - x.rShiftTo(g, x); - y.rShiftTo(g, y); - } - while (x.signum() > 0) { - if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x); - if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y); - if (x.compareTo(y) >= 0) { - x.subTo(y, x); - x.rShiftTo(1, x); - } else { - y.subTo(x, y); - y.rShiftTo(1, y); - } - } - if (g > 0) y.lShiftTo(g, y); - return y; -} - -// (protected) this % n, n < 2^26 - -function bnpModInt(n) { - if (n <= 0) return 0; - const d = this.DV % n; - let r = (this.s < 0) ? n - 1 : 0; - if (this.t > 0) { if (d == 0) r = this[0] % n; - else for (let i = this.t - 1; i >= 0; --i) r = (d * r + this[i]) % n; } - return r; -} - -// (public) 1/this % m (HAC 14.61) - -function bnModInverse(m) { - const ac = m.isEven(); - if ((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; - const u = m.clone(); - const v = this.clone(); - const a = nbv(1); - const b = nbv(0); - const c = nbv(0); - const d = nbv(1); - while (u.signum() != 0) { - while (u.isEven()) { - u.rShiftTo(1, u); - if (ac) { - if (!a.isEven() || !b.isEven()) { - a.addTo(this, a); - b.subTo(m, b); - } - a.rShiftTo(1, a); - } else if (!b.isEven()) b.subTo(m, b); - b.rShiftTo(1, b); - } - while (v.isEven()) { - v.rShiftTo(1, v); - if (ac) { - if (!c.isEven() || !d.isEven()) { - c.addTo(this, c); - d.subTo(m, d); - } - c.rShiftTo(1, c); - } else if (!d.isEven()) d.subTo(m, d); - d.rShiftTo(1, d); - } - if (u.compareTo(v) >= 0) { - u.subTo(v, u); - if (ac) a.subTo(c, a); - b.subTo(d, b); - } else { - v.subTo(u, v); - if (ac) c.subTo(a, c); - d.subTo(b, d); - } - } - if (v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; - if (d.compareTo(m) >= 0) return d.subtract(m); - if (d.signum() < 0) d.addTo(m, d); - else return d; - if (d.signum() < 0) return d.add(m); - else return d; -} - -const lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, - 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, - 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, - 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, - 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, - 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, - 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, - 977, 983, 991, 997]; -const lplim = (1 << 26) / lowprimes[lowprimes.length - 1]; - -// (public) test primality with certainty >= 1-.5^t - -function bnIsProbablePrime(t) { - let i; - const x = this.abs(); - if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) { - for (i = 0; i < lowprimes.length; ++i) if (x[0] == lowprimes[i]) return true; - return false; - } - if (x.isEven()) return false; - i = 1; - while (i < lowprimes.length) { - let m = lowprimes[i]; - let j = i + 1; - while (j < lowprimes.length && m < lplim) m *= lowprimes[j++]; - m = x.modInt(m); - while (i < j) if (m % lowprimes[i++] == 0) return false; - } - return x.millerRabin(t); -} - -/* added by Recurity Labs */ - -/* eslint-disable no-redeclare */ -function nbits(x) { - let n = 1; - let t; - if ((t = x >>> 16) != 0) { - x = t; - n += 16; - } - if ((t = x >> 8) != 0) { - x = t; - n += 8; - } - if ((t = x >> 4) != 0) { - x = t; - n += 4; - } - if ((t = x >> 2) != 0) { - x = t; - n += 2; - } - if ((t = x >> 1) != 0) { - x = t; - n += 1; - } - return n; -} - -function bnToMPI() { - const ba = this.toByteArray(); - const size = (ba.length - 1) * 8 + nbits(ba[0]); - let result = ""; - result += String.fromCharCode((size & 0xFF00) >> 8); - result += String.fromCharCode(size & 0xFF); - result += util.bin2str(ba); - return result; -} -/* END of addition */ - -// (protected) true if probably prime (HAC 4.24, Miller-Rabin) -function bnpMillerRabin(t) { - const n1 = this.subtract(BigInteger.ONE); - const k = n1.getLowestSetBit(); - if (k <= 0) return false; - const r = n1.shiftRight(k); - t = (t + 1) >> 1; - if (t > lowprimes.length) t = lowprimes.length; - const a = nbi(); - let j; - const bases = []; - for (let i = 0; i < t; ++i) { - //Pick bases at random, instead of starting at 2 - for (;;) { - j = lowprimes[Math.floor(Math.random() * lowprimes.length)]; - if (bases.indexOf(j) == -1) break; - } - bases.push(j); - a.fromInt(j); - let y = a.modPow(r, this); - if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { - let j = 1; - while (j++ < k && y.compareTo(n1) != 0) { - y = y.modPowInt(2, this); - if (y.compareTo(BigInteger.ONE) == 0) return false; - } - if (y.compareTo(n1) != 0) return false; - } - } - return true; -} - -// protected -BigInteger.prototype.chunkSize = bnpChunkSize; -BigInteger.prototype.toRadix = bnpToRadix; -BigInteger.prototype.fromRadix = bnpFromRadix; -BigInteger.prototype.fromNumber = bnpFromNumber; -BigInteger.prototype.bitwiseTo = bnpBitwiseTo; -BigInteger.prototype.changeBit = bnpChangeBit; -BigInteger.prototype.addTo = bnpAddTo; -BigInteger.prototype.dMultiply = bnpDMultiply; -BigInteger.prototype.dAddOffset = bnpDAddOffset; -BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; -BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; -BigInteger.prototype.modInt = bnpModInt; -BigInteger.prototype.millerRabin = bnpMillerRabin; - -// public -BigInteger.prototype.clone = bnClone; -BigInteger.prototype.intValue = bnIntValue; -BigInteger.prototype.byteValue = bnByteValue; -BigInteger.prototype.shortValue = bnShortValue; -BigInteger.prototype.signum = bnSigNum; -BigInteger.prototype.toByteArray = bnToByteArray; -BigInteger.prototype.equals = bnEquals; -BigInteger.prototype.min = bnMin; -BigInteger.prototype.max = bnMax; -BigInteger.prototype.and = bnAnd; -BigInteger.prototype.or = bnOr; -BigInteger.prototype.xor = bnXor; -BigInteger.prototype.andNot = bnAndNot; -BigInteger.prototype.not = bnNot; -BigInteger.prototype.shiftLeft = bnShiftLeft; -BigInteger.prototype.shiftRight = bnShiftRight; -BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; -BigInteger.prototype.bitCount = bnBitCount; -BigInteger.prototype.testBit = bnTestBit; -BigInteger.prototype.setBit = bnSetBit; -BigInteger.prototype.clearBit = bnClearBit; -BigInteger.prototype.flipBit = bnFlipBit; -BigInteger.prototype.add = bnAdd; -BigInteger.prototype.subtract = bnSubtract; -BigInteger.prototype.multiply = bnMultiply; -BigInteger.prototype.divide = bnDivide; -BigInteger.prototype.remainder = bnRemainder; -BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; -BigInteger.prototype.modPow = bnModPow; -BigInteger.prototype.modInverse = bnModInverse; -BigInteger.prototype.pow = bnPow; -BigInteger.prototype.gcd = bnGCD; -BigInteger.prototype.isProbablePrime = bnIsProbablePrime; -BigInteger.prototype.toMPI = bnToMPI; - -// JSBN-specific extension -BigInteger.prototype.square = bnSquare; diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js new file mode 100644 index 00000000..53339f1a --- /dev/null +++ b/src/crypto/public_key/prime.js @@ -0,0 +1,179 @@ +// OpenPGP.js - An OpenPGP implementation in javascript +// 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 +// 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 + +// Algorithms for probabilistic random prime generation + +/** + * @requires bn.js + * @requires crypto/random + * @module crypto/public_key/prime + */ + +import BN from 'bn.js'; +import random from '../random'; + +export default { + randomProbablePrime, isProbablePrime, fermat, millerRabin +}; + +/** + * Probabilistic random number generator + * @param {Integer} bits Bit length of the prime + * @param {BN} e Optional RSA exponent to check against the prime + * @param {Integer} k Optional number of iterations of Miller-Rabin test + * @return BN + */ +function randomProbablePrime(bits, e, k) { + const min = new BN(1).shln(bits - 1); + + let n = random.getRandomBN(min, min.shln(1)); + if (n.isEven()) { + n.iaddn(1); // force odd + } + + while (!isProbablePrime(n, e, k)) { + n.iaddn(2); + // If reached the maximum, go back to the minimum. + if (n.bitLength() > bits) { + n = n.mod(min.shln(1)).iadd(min); + } + } + return n; +} + +/** + * Probabilistic primality testing + * @param {BN} n Number to test + * @param {BN} e Optional RSA exponent to check against the prime + * @param {Integer} k Optional number of iterations of Miller-Rabin test + * @return {boolean} + */ +function isProbablePrime(n, e, k) { + if (e && !n.subn(1).gcd(e).eqn(1)) { + return false; + } + if (!fermat(n)) { + return false; + } + if (!millerRabin(n, k, () => new BN(lowprimes[Math.random() * lowprimes.length | 0]))) { + return false; + } + if (!millerRabin(n, k)) { + return false; + } + // TODO implement the Lucas test + // See Section C.3.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf + return true; +} + +/** + * Tests whether n is probably prime or not using Fermat's test with b = 2. + * Fails if b^(n-1) mod n === 1. + * @param {BN} n Number to test + * @param {Integer} b Optional Fermat test base + * @return {boolean} + */ +function fermat(n, b) { + b = b || new BN(2); + return b.toRed(BN.mont(n)).redPow(n.subn(1)).fromRed().cmpn(1) === 0; +} + +const lowprimes = [ + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997]; + + +// Miller-Rabin - Miller Rabin algorithm for primality test +// Copyright Fedor Indutny, 2014. +// +// This software is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Adapted on Jan 2018 from version 4.0.1 at https://github.com/indutny/miller-rabin + +/** + * Tests whether n is probably prime or not using the Miller-Rabin test. + * See HAC Remark 4.28. + * @param {BN} n Number to test + * @param {Integer} k Optional number of iterations of Miller-Rabin test + * @param {Function} rand Optional function to generate potential witnesses + * @return {boolean} + */ +function millerRabin(n, k, rand) { + const len = n.bitLength(); + const red = BN.mont(n); + const rone = new BN(1).toRed(red); + + if (!k) + k = Math.max(1, (len / 48) | 0); + + const n1 = n.subn(1); + const rn1 = n1.toRed(red); + + // Find d and s, (n - 1) = (2 ^ s) * d; + let s = 0; + while (!n1.testn(s)) { s++; } + const d = n.shrn(s); + + for (; k > 0; k--) { + let a = rand ? rand() : random.getRandomBN(new BN(2), n1); + + let x = a.toRed(red).redPow(d); + if (x.eq(rone) || x.eq(rn1)) + continue; + + let i; + for (i = 1; i < s; i++) { + x = x.redSqr(); + + if (x.eq(rone)) + return false; + if (x.eq(rn1)) + break; + } + + if (i === s) + return false; + } + + return true; +}; diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 834220e3..baae76ca 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -18,254 +18,211 @@ // RSA implementation /** - * @requires crypto/public_key/jsbn + * @requires bn.js + * @requires crypto/public_key/prime * @requires crypto/random + * @requires config * @requires util * @module crypto/public_key/rsa */ -import BigInteger from './jsbn.js'; -import util from '../../util.js'; -import random from '../random.js'; +import BN from 'bn.js'; +import prime from './prime'; +import random from '../random'; import config from '../../config'; +import util from '../../util'; -function SecureRandom() { - function nextBytes(byteArray) { - for (let n = 0; n < byteArray.length; n++) { - byteArray[n] = random.getSecureRandomOctet(); - } +// Helper for IE11 KeyOperation objects +function promisifyIE11Op(keyObj, err) { + if (typeof keyObj.then !== 'function') { // IE11 KeyOperation + return new Promise(function(resolve, reject) { + keyObj.onerror = function () { + reject(new Error(err)); + }; + keyObj.oncomplete = function (e) { + resolve(e.target.result); + }; + }); } - this.nextBytes = nextBytes; + return keyObj; } -let blinder = BigInteger.ZERO; -let unblinder = BigInteger.ZERO; - -function blind(m, n, e) { - if (unblinder.bitLength() === n.bitLength()) { - unblinder = unblinder.square().mod(n); - } else { - unblinder = random.getRandomBigIntegerInRange(BigInteger.TWO, n); - } - blinder = unblinder.modInverse(n).modPow(e, n); - return m.multiply(blinder).mod(n); -} - -function unblind(t, n) { - return t.multiply(unblinder).mod(n); -} - -export default function RSA() { - /** - * This function uses jsbn Big Num library to decrypt RSA - * @param m - * message - * @param n - * RSA public modulus n as BigInteger - * @param e - * RSA public exponent as BigInteger - * @param d - * RSA d as BigInteger - * @param p - * RSA p as BigInteger - * @param q - * RSA q as BigInteger - * @param u - * RSA u as BigInteger - * @return {BigInteger} The decrypted value of the message +export default { + /** Create signature + * @param m message as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @param d private MPI part as BN + * @return BN */ - function decrypt(m, n, e, d, p, q, u) { - if (config.rsa_blinding) { - m = blind(m, n, e); + sign: function(m, n, e, d) { + if (n.cmp(m) <= 0) { + throw new Error('Data too large.'); } - const xp = m.mod(p).modPow(d.mod(p.subtract(BigInteger.ONE)), p); - const xq = m.mod(q).modPow(d.mod(q.subtract(BigInteger.ONE)), q); - util.print_debug("rsa.js decrypt\nxpn:" + util.hexstrdump(xp.toMPI()) + "\nxqn:" + util.hexstrdump(xq.toMPI())); - - let t = xq.subtract(xp); - if (t[0] === 0) { - t = xp.subtract(xq); - t = t.multiply(u).mod(q); - t = q.subtract(t); - } else { - t = t.multiply(u).mod(q); - } - t = t.multiply(p).add(xp); - if (config.rsa_blinding) { - t = unblind(t, n); - } - return t; - } + const nred = new BN.red(n); + return m.toRed(nred).redPow(d).toArrayLike(Uint8Array, 'be', n.byteLength()); + }, /** - * encrypt message - * @param m message as BigInteger - * @param e public MPI part as BigInteger - * @param n public MPI part as BigInteger - * @return BigInteger + * Verify signature + * @param s signature as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @return BN */ - function encrypt(m, e, n) { - return m.modPowInt(e, n); - } + verify: function(s, n, e) { + if (n.cmp(s) <= 0) { + throw new Error('Data too large.'); + } + const nred = new BN.red(n); + return s.toRed(nred).redPow(e).toArrayLike(Uint8Array, 'be', n.byteLength()); + }, - /* Sign and Verify */ - function sign(m, d, n) { - return m.modPow(d, n); - } + /** + * Encrypt message + * @param m message as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @return BN + */ + encrypt: function(m, n, e) { + if (n.cmp(m) <= 0) { + throw new Error('Data too large.'); + } + const nred = new BN.red(n); + return m.toRed(nred).redPow(e).toArrayLike(Uint8Array, 'be', n.byteLength()); + }, - function verify(x, e, n) { - return x.modPowInt(e, n); - } + /** + * Decrypt RSA message + * @param m message as BN + * @param n RSA public modulus n as BN + * @param e RSA public exponent as BN + * @param d RSA d as BN + * @param p RSA p as BN + * @param q RSA q as BN + * @param u RSA u as BN + * @return {BN} The decrypted value of the message + */ + decrypt: function(m, n, e, d, p, q, u) { + if (n.cmp(m) <= 0) { + throw new Error('Data too large.'); + } + const dq = d.mod(q.subn(1)); // d mod (q-1) + const dp = d.mod(p.subn(1)); // d mod (p-1) + const pred = new BN.red(p); + const qred = new BN.red(q); + const nred = new BN.red(n); - // "empty" RSA key constructor + let blinder; + let unblinder; + if (config.rsa_blinding) { + unblinder = random.getRandomBN(new BN(2), n).toRed(nred); + blinder = unblinder.redInvm().redPow(e); + m = m.toRed(nred).redMul(blinder).fromRed(); + } - function KeyObject() { - this.n = null; - this.e = 0; - this.ee = null; - this.d = null; - this.p = null; - this.q = null; - this.dmp1 = null; - this.dmq1 = null; - this.u = null; - } + const mp = m.toRed(pred).redPow(dp); + const mq = m.toRed(qred).redPow(dq); + const t = mq.redSub(mp.fromRed().toRed(qred)); + const h = u.toRed(qred).redMul(t).fromRed(); - // Generate a new random private key B bits long, using public expt E + let result = h.mul(p).add(mp).toRed(nred); - function generate(B, E) { + if (config.rsa_blinding) { + result = result.redMul(unblinder); + } + + return result.toArrayLike(Uint8Array, 'be', n.byteLength()); + }, + + /** + * Generate a new random private key B bits long with public exponent E + * @param {Integer} B RSA bit length + * @param {String} E RSA public exponent in hex string + * @return {{n: BN, e: BN, d: BN, + p: BN, q: BN, u: BN}} RSA public modulus, RSA public exponent, RSA private exponent, + RSA private prime p, RSA private prime q, u = q ** -1 mod p + */ + generate: async function(B, E) { + let key; + E = new BN(E, 16); const webCrypto = util.getWebCryptoAll(); - // // Native RSA keygen using Web Crypto - // - if (webCrypto) { - const Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent - const Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent + let keyPair; let keyGenOpt; - - let keys; if ((window.crypto && window.crypto.subtle) || window.msCrypto) { // current standard spec keyGenOpt = { name: 'RSASSA-PKCS1-v1_5', modulusLength: B, // the specified keysize in bits - publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537) + publicExponent: E.toArrayLike(Uint8Array), // take three bytes (max 65537) for exponent hash: { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - - keys = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); - if (typeof keys.then !== 'function') { // IE11 KeyOperation - keys = util.promisifyIE11Op(keys, 'Error generating RSA key pair.'); - } - } - else if (window.crypto && window.crypto.webkitSubtle) { + keyPair = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); + keyPair = await promisifyIE11Op(keyPair, 'Error generating RSA key pair.'); + } else if (window.crypto && window.crypto.webkitSubtle) { // outdated spec implemented by old Webkit keyGenOpt = { name: 'RSA-OAEP', modulusLength: B, // the specified keysize in bits - publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537) + publicExponent: E.toArrayLike(Uint8Array), // take three bytes (max 65537) for exponent hash: { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - keys = webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']); - } - else { + keyPair = await webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']); + } else { throw new Error('Unknown WebCrypto implementation'); } - return keys.then(exportKey).then(function(key) { - if (key instanceof ArrayBuffer) { - // parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk) - return decodeKey(JSON.parse(String.fromCharCode.apply(null, new Uint8Array(key)))); - } - return decodeKey(key); - }); - } - - function exportKey(keypair) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 - let key = webCrypto.exportKey('jwk', keypair.privateKey); - if (typeof key.then !== 'function') { // IE11 KeyOperation - key = util.promisifyIE11Op(key, 'Error exporting RSA key pair.'); + let jwk = webCrypto.exportKey('jwk', keyPair.privateKey); + jwk = await promisifyIE11Op(jwk, 'Error exporting RSA key pair.'); + + // parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk) + if (jwk instanceof ArrayBuffer) { + jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk))); } + + // map JWK parameters to BN + key = {}; + key.n = new BN(util.b64_to_Uint8Array(jwk.n)); + key.e = E; + key.d = new BN(util.b64_to_Uint8Array(jwk.d)); + key.p = new BN(util.b64_to_Uint8Array(jwk.p)); + key.q = new BN(util.b64_to_Uint8Array(jwk.q)); + key.u = key.p.invm(key.q); return key; } - function decodeKey(jwk) { - // map JWK parameters to local BigInteger type system - const key = new KeyObject(); - key.n = toBigInteger(jwk.n); - key.ee = new BigInteger(E, 16); - key.d = toBigInteger(jwk.d); - key.p = toBigInteger(jwk.p); - key.q = toBigInteger(jwk.q); - key.u = key.p.modInverse(key.q); + // RSA keygen fallback using 40 iterations of the Miller-Rabin test + // See https://stackoverflow.com/a/6330138 for justification + // Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST + let p = prime.randomProbablePrime(B - (B >> 1), E, 40); + let q = prime.randomProbablePrime(B >> 1, E, 40); - function toBigInteger(base64url) { - const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); - const hex = util.hexstrdump(atob(base64)); - return new BigInteger(hex, 16); - } - - return key; + if (p.cmp(q) < 0) { + [p, q] = [q, p]; } - // - // JS code - // + const phi = p.subn(1).mul(q.subn(1)); + return { + n: p.mul(q), + e: E, + d: E.invm(phi), + p: p, + q: q, + // dp: d.mod(p.subn(1)), + // dq: d.mod(q.subn(1)), + u: p.invm(q) + }; + }, - return new Promise(function(resolve) { - const key = new KeyObject(); - const rng = new SecureRandom(); - const qs = B >> 1; - key.e = parseInt(E, 16); - key.ee = new BigInteger(E, 16); - - for (;;) { - for (;;) { - key.p = new BigInteger(B - qs, 1, rng); - if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) { - break; - } - } - for (;;) { - key.q = new BigInteger(qs, 1, rng); - if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) { - break; - } - } - if (key.p.compareTo(key.q) <= 0) { - const t = key.p; - key.p = key.q; - key.q = t; - } - const p1 = key.p.subtract(BigInteger.ONE); - const q1 = key.q.subtract(BigInteger.ONE); - const phi = p1.multiply(q1); - if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) { - key.n = key.p.multiply(key.q); - key.d = key.ee.modInverse(phi); - key.dmp1 = key.d.mod(p1); - key.dmq1 = key.d.mod(q1); - key.u = key.p.modInverse(key.q); - break; - } - } - - resolve(key); - }); - } - - this.encrypt = encrypt; - this.decrypt = decrypt; - this.verify = verify; - this.sign = sign; - this.generate = generate; - this.keyObject = KeyObject; -} + prime: prime +}; diff --git a/src/crypto/random.js b/src/crypto/random.js index 0c8f43c2..320c70dd 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -18,13 +18,15 @@ // The GPG4Browsers crypto interface /** + * @requires bn.js * @requires type/mpi * @requires util * @module crypto/random */ -import type_mpi from '../type/mpi.js'; -import util from '../util.js'; +import BN from 'bn.js'; +import type_mpi from '../type/mpi'; +import util from '../util'; // Do not use util.getNodeCrypto because we need this regardless of use_native setting const nodeCrypto = util.detectNode() && require('crypto'); @@ -95,41 +97,27 @@ export default { }, /** - * Create a secure random big integer of bits length - * @param {Integer} bits Bit length of the MPI to create - * @return {BigInteger} Resulting big integer + * Create a secure random MPI that is greater than or equal to min and less than max. + * @param {module:type/mpi} min Lower bound, included + * @param {module:type/mpi} max Upper bound, excluded + * @return {module:BN} Random MPI */ - getRandomBigInteger: function(bits) { - if (bits < 1) { - throw new Error('Illegal parameter value: bits < 1'); - } - const numBytes = Math.floor((bits + 7) / 8); - - let randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes)); - if (bits % 8 > 0) { - randomBits = String.fromCharCode(((2 ** (bits % 8)) - 1) & - randomBits.charCodeAt(0)) + - randomBits.substring(1); - } - const mpi = new type_mpi(randomBits); - return mpi.toBigInteger(); - }, - - getRandomBigIntegerInRange: function(min, max) { - if (max.compareTo(min) <= 0) { + getRandomBN: function(min, max) { + if (max.cmp(min) <= 0) { throw new Error('Illegal parameter value: max <= min'); } - const range = max.subtract(min); - let r = this.getRandomBigInteger(range.bitLength()); - while (r.compareTo(range) > 0) { - r = this.getRandomBigInteger(range.bitLength()); - } - return min.add(r); + const modulus = max.sub(min); + const bytes = modulus.byteLength(); + + // Using a while loop is necessary to avoid bias introduced by the mod operation. + // However, we request 64 extra random bits so that the bias is negligible. + // Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf + const r = new BN(this.getRandomBytes(bytes + 8)); + return r.mod(modulus).add(min); }, randomBuffer: new RandomBuffer() - }; /** diff --git a/src/crypto/signature.js b/src/crypto/signature.js index be045470..28ed2d92 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,85 +1,66 @@ /** - * @requires util - * @requires crypto/hash - * @requires crypto/pkcs1 + * @requires bn.js * @requires crypto/public_key - * @module crypto/signature */ + * @requires crypto/pkcs1 + * @requires enums + * @requires util + * @module crypto/signature +*/ -import util from '../util'; +import BN from 'bn.js'; import publicKey from './public_key'; -import pkcs1 from './pkcs1.js'; +import pkcs1 from './pkcs1'; +import enums from '../enums'; +import util from '../util'; export default { /** - * - * @param {module:enums.publicKey} algo public Key algorithm - * @param {module:enums.hash} hash_algo Hash algorithm - * @param {Array} msg_MPIs Signature multiprecision integers - * @param {Array} publickey_MPIs Public key multiprecision integers - * @param {Uint8Array} data Data on where the signature was computed on. - * @return {Boolean} true if signature (sig_data was equal to data over hash) + * Verifies the signature provided for data using specified algorithms and public key parameters. + * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} + * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4} + * for public key and hash algorithms. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {module:enums.hash} hash_algo Hash algorithm + * @param {Array} msg_MPIs Algorithm-specific signature parameters + * @param {Array} pub_MPIs Algorithm-specific public key parameters + * @param {Uint8Array} data Data for which the signature was created + * @return {Boolean} True if signature is valid */ - verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { - let m; - let r; - let s; - let Q; - let curve; - - data = util.Uint8Array2str(data); - + verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data) { switch (algo) { - case 1: - // RSA (Encrypt or Sign) [HAC] - case 2: - // RSA Encrypt-Only [HAC] - case 3: { - // RSA Sign-Only [HAC] - const rsa = new publicKey.rsa(); - const n = publickey_MPIs[0].toBigInteger(); - const k = publickey_MPIs[0].byteLength(); - const e = publickey_MPIs[1].toBigInteger(); - m = msg_MPIs[0].toBigInteger(); - const EM = rsa.verify(m, e, n); - const EM2 = pkcs1.emsa.encode(hash_algo, data, k); - return EM.compareTo(EM2) === 0; + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_sign: { + const m = msg_MPIs[0].toBN(); + const n = pub_MPIs[0].toBN(); + const e = pub_MPIs[1].toBN(); + const EM = publicKey.rsa.verify(m, n, e); + const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array_to_str(data), n.byteLength()); + return util.Uint8Array_to_hex(EM) === EM2; } - case 16: { - // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] - throw new Error("signing with Elgamal is not defined in the OpenPGP standard."); + case enums.publicKey.dsa: { + const r = msg_MPIs[0].toBN(); + const s = msg_MPIs[1].toBN(); + const p = pub_MPIs[0].toBN(); + const q = pub_MPIs[1].toBN(); + const g = pub_MPIs[2].toBN(); + const y = pub_MPIs[3].toBN(); + return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y); } - case 17: { - // DSA (Digital Signature Algorithm) [FIPS186] [HAC] - const dsa = new publicKey.dsa(); - const s1 = msg_MPIs[0].toBigInteger(); - const s2 = msg_MPIs[1].toBigInteger(); - const p = publickey_MPIs[0].toBigInteger(); - const q = publickey_MPIs[1].toBigInteger(); - const g = publickey_MPIs[2].toBigInteger(); - const y = publickey_MPIs[3].toBigInteger(); - m = data; - const dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y); - return dopublic.compareTo(s1) === 0; + case enums.publicKey.ecdsa: { + const oid = pub_MPIs[0]; + const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() }; + const Q = pub_MPIs[1].toUint8Array(); + return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q); } - case 19: { - // ECDSA - const { ecdsa } = publicKey.elliptic; - [curve] = publickey_MPIs; - r = msg_MPIs[0].toBigInteger(); - s = msg_MPIs[1].toBigInteger(); - m = data; - 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; - [curve] = publickey_MPIs; - 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); + case enums.publicKey.eddsa: { + const oid = pub_MPIs[0]; + // TODO refactor elliptic to accept Uint8Array + // EdDSA signature params are expected in little-endian format + const signature = { R: Array.from(msg_MPIs[0].toUint8Array('le', 32)), + S: Array.from(msg_MPIs[1].toUint8Array('le', 32)) }; + const Q = Array.from(pub_MPIs[1].toUint8Array('be', 33)); + return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q); } default: throw new Error('Invalid signature algorithm.'); @@ -87,73 +68,60 @@ export default { }, /** - * Create a signature on data using the specified algorithm - * @param {module:enums.hash} hash_algo hash Algorithm to use (See {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}) - * @param {module:enums.publicKey} algo Asymmetric cipher algorithm to use (See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) - * @param {Array} keyIntegers Public followed by Private key multiprecision algorithm-specific parameters - * @param {Uint8Array} data Data to be signed - * @return {Array} + * Creates a signature on data using specified algorithms and private key parameters. + * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} + * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4} + * for public key and hash algorithms. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {module:enums.hash} hash_algo Hash algorithm + * @param {Array} key_params Algorithm-specific public and private key parameters + * @param {Uint8Array} data Data to be signed + * @return {Uint8Array} Signature */ - sign: async function(hash_algo, algo, keyIntegers, data) { - data = util.Uint8Array2str(data); - - let m; - let d; - let curve; - let signature; - + sign: async function(algo, hash_algo, key_params, data) { switch (algo) { - case 1: - // RSA (Encrypt or Sign) [HAC] - case 2: - // RSA Encrypt-Only [HAC] - case 3: { - // RSA Sign-Only [HAC] - const rsa = new publicKey.rsa(); - d = keyIntegers[2].toBigInteger(); - const n = keyIntegers[0].toBigInteger(); - m = pkcs1.emsa.encode( - hash_algo, - data, keyIntegers[0].byteLength() - ); - return util.str2Uint8Array(rsa.sign(m, d, n).toMPI()); + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_sign: { + const n = key_params[0].toBN(); + const e = key_params[1].toBN(); + const d = key_params[2].toBN(); + data = util.Uint8Array_to_str(data); + const m = new BN(pkcs1.emsa.encode(hash_algo, data, n.byteLength()), 16); + const signature = publicKey.rsa.sign(m, n, e, d); + return util.Uint8Array_to_MPI(signature); } - case 17: { - // DSA (Digital Signature Algorithm) [FIPS186] [HAC] - const dsa = new publicKey.dsa(); - - const p = keyIntegers[0].toBigInteger(); - const q = keyIntegers[1].toBigInteger(); - const g = keyIntegers[2].toBigInteger(); - const x = keyIntegers[4].toBigInteger(); - m = data; - const result = dsa.sign(hash_algo, m, g, p, q, x); - return util.str2Uint8Array(result[0].toString() + result[1].toString()); + case enums.publicKey.dsa: { + const p = key_params[0].toBN(); + const q = key_params[1].toBN(); + const g = key_params[2].toBN(); + const x = key_params[4].toBN(); + const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); + return util.concatUint8Array([ + util.Uint8Array_to_MPI(signature.r), + util.Uint8Array_to_MPI(signature.s) + ]); } - case 16: { - // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + case enums.publicKey.elgamal: { throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.'); } - case 19: { - // ECDSA - const { ecdsa } = publicKey.elliptic; - [curve] = keyIntegers; - d = keyIntegers[2].toBigInteger(); - m = data; - signature = await ecdsa.sign(curve.oid, hash_algo, m, d); - return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); + case enums.publicKey.ecdsa: { + const oid = key_params[0]; + const d = key_params[2].toUint8Array(); + const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); + return util.concatUint8Array([ + util.Uint8Array_to_MPI(signature.r), + util.Uint8Array_to_MPI(signature.s) + ]); } - case 22: { - // EdDSA - const { eddsa } = publicKey.elliptic; - [curve] = keyIntegers; - d = keyIntegers[2].toBigInteger(); - m = data; - signature = await eddsa.sign(curve.oid, hash_algo, m, d); - return new Uint8Array([].concat( - util.Uint8Array2MPI(signature.R.toArrayLike(Uint8Array, 'le', 32)), - util.Uint8Array2MPI(signature.S.toArrayLike(Uint8Array, 'le', 32)) - )); + case enums.publicKey.eddsa: { + const oid = key_params[0]; + const d = Array.from(key_params[2].toUint8Array('be', 32)); + const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d); + return util.concatUint8Array([ + util.Uint8Array_to_MPI(signature.R), + util.Uint8Array_to_MPI(signature.S) + ]); } default: throw new Error('Invalid signature algorithm.'); diff --git a/src/enums.js b/src/enums.js index d384ad4e..74799d4f 100644 --- a/src/enums.js +++ b/src/enums.js @@ -72,13 +72,22 @@ export default { * @readonly */ publicKey: { + /** RSA (Encrypt or Sign) [HAC] */ rsa_encrypt_sign: 1, + /** RSA (Encrypt only) [HAC] */ rsa_encrypt: 2, + /** RSA (Sign only) [HAC] */ rsa_sign: 3, + /** Elgamal (Encrypt only) [ELGAMAL] [HAC] */ elgamal: 16, + /** DSA (Sign only) [FIPS186] [HAC] */ dsa: 17, + /** ECDH (Encrypt only) [RFC6637] */ ecdh: 18, + /** ECDSA (Sign only) [RFC6637] */ ecdsa: 19, + /** EdDSA (Sign only) + * [{@link https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04|Draft RFC}] */ eddsa: 22 }, @@ -183,37 +192,44 @@ export default { signature: { /** 0x00: Signature of a binary document. */ binary: 0, - /** 0x01: Signature of a canonical text document.
+ /** 0x01: Signature of a canonical text document. + * * Canonicalyzing the document by converting line endings. */ text: 1, - /** 0x02: Standalone signature.
+ /** 0x02: Standalone signature. + * * This signature is a signature of only its own subpacket contents. * It is calculated identically to a signature over a zero-lengh * binary document. Note that it doesn't make sense to have a V3 * standalone signature. */ standalone: 2, - /** 0x10: Generic certification of a User ID and Public-Key packet.
+ /** 0x10: Generic certification of a User ID and Public-Key packet. + * * The issuer of this certification does not make any particular * assertion as to how well the certifier has checked that the owner * of the key is in fact the person described by the User ID. */ cert_generic: 16, - /** 0x11: Persona certification of a User ID and Public-Key packet.
+ /** 0x11: Persona certification of a User ID and Public-Key packet. + * * The issuer of this certification has not done any verification of * the claim that the owner of this key is the User ID specified. */ cert_persona: 17, - /** 0x12: Casual certification of a User ID and Public-Key packet.
+ /** 0x12: Casual certification of a User ID and Public-Key packet. + * * The issuer of this certification has done some casual * verification of the claim of identity. */ cert_casual: 18, - /** 0x13: Positive certification of a User ID and Public-Key packet.
+ /** 0x13: Positive certification of a User ID and Public-Key packet. + * * The issuer of this certification has done substantial - * verification of the claim of identity.
- *
+ * verification of the claim of identity. + * * Most OpenPGP implementations make their "key signatures" as 0x10 * certifications. Some implementations can issue 0x11-0x13 * certifications, but few differentiate between the types. */ cert_positive: 19, - /** 0x30: Certification revocation signature
+ /** 0x30: Certification revocation signature + * * This signature revokes an earlier User ID certification signature * (signature class 0x10 through 0x13) or direct-key signature * (0x1F). It should be issued by the same key that issued the @@ -222,7 +238,8 @@ export default { * revokes, and should have a later creation date than that * certificate. */ cert_revocation: 48, - /** 0x18: Subkey Binding Signature
+ /** 0x18: Subkey Binding Signature + * * This signature is a statement by the top-level signing key that * indicates that it owns the subkey. This signature is calculated * directly on the primary key and subkey, and not on any User ID or @@ -231,12 +248,13 @@ export default { * contains a 0x19 signature made by the signing subkey on the * primary key and subkey. */ subkey_binding: 24, - /** 0x19: Primary Key Binding Signature
+ /** 0x19: Primary Key Binding Signature + * * This signature is a statement by a signing subkey, indicating * that it is owned by the primary key and subkey. This signature * is calculated the same way as a 0x18 signature: directly on the - * primary key and subkey, and not on any User ID or other packets.
- *
+ * primary key and subkey, and not on any User ID or other packets. + * * When a signature is made over a key, the hash data starts with the * octet 0x99, followed by a two-octet length of the key, and then body * of the key packet. (Note that this is an old-style packet header for @@ -245,7 +263,8 @@ export default { * the subkey using the same format as the main key (also using 0x99 as * the first octet). */ key_binding: 25, - /** 0x1F: Signature directly on a key
+ /** 0x1F: Signature directly on a key + * * This signature is calculated directly on a key. It binds the * information in the Signature subpackets to the key, and is * appropriate to be used for subpackets that provide information @@ -254,27 +273,30 @@ export default { * about the key itself, rather than the binding between a key and a * name. */ key: 31, - /** 0x20: Key revocation signature
+ /** 0x20: Key revocation signature + * * The signature is calculated directly on the key being revoked. A * revoked key is not to be used. Only revocation signatures by the * key being revoked, or by an authorized revocation key, should be * considered valid revocation signatures.a */ key_revocation: 32, - /** 0x28: Subkey revocation signature
+ /** 0x28: Subkey revocation signature + * * The signature is calculated directly on the subkey being revoked. * A revoked subkey is not to be used. Only revocation signatures * by the top-level signature key that is bound to this subkey, or * by an authorized revocation key, should be considered valid - * revocation signatures.
- *
+ * revocation signatures. + * * Key revocation signatures (types 0x20 and 0x28) * hash only the key being revoked. */ subkey_revocation: 40, - /** 0x40: Timestamp signature.
+ /** 0x40: Timestamp signature. * This signature is only meaningful for the timestamp contained in * it. */ timestamp: 64, - /** 0x50: Third-Party Confirmation signature.
+ /** 0x50: Third-Party Confirmation signature. + * * This signature is a signature over some other OpenPGP Signature * packet(s). It is analogous to a notary seal on the signed data. * A third-party signature SHOULD include Signature Target diff --git a/src/hkp.js b/src/hkp.js index 2c248113..18e3ab68 100644 --- a/src/hkp.js +++ b/src/hkp.js @@ -18,6 +18,7 @@ /** * @fileoverview This class implements a client for the OpenPGP HTTP Keyserver Protocol (HKP) * in order to lookup and upload keys on standard public key servers. + * @module hkp */ import config from './config'; diff --git a/src/key.js b/src/key.js index 1f163f74..cbf7d99c 100644 --- a/src/key.js +++ b/src/key.js @@ -16,21 +16,21 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * @requires config - * @requires crypto * @requires encoding/armor + * @requires crypto + * @requires packet + * @requires config * @requires enums * @requires util - * @requires packet * @module key */ -import config from './config'; -import crypto from './crypto'; import armor from './encoding/armor'; +import crypto from './crypto'; +import packet from './packet'; +import config from './config'; import enums from './enums'; import util from './util'; -import packet from './packet'; /** * @class @@ -209,7 +209,7 @@ Key.prototype.getKeyIds = function() { /** * Returns array containing first key packet for given key ID or all key packets in the case of a wildcard ID - * @param {type/keyid>} keyIds + * @param {type/keyid} keyId * @return {(module:packet/public_subkey|module:packet/public_key| * module:packet/secret_subkey|module:packet/secret_key|null)} */ @@ -235,7 +235,7 @@ Key.prototype.getUserIds = function() { const userids = []; for (let i = 0; i < this.users.length; i++) { if (this.users[i].userId) { - userids.push(util.Uint8Array2str(this.users[i].userId.write())); + userids.push(util.Uint8Array_to_str(this.users[i].userId.write())); } } return userids; @@ -514,8 +514,10 @@ function getExpirationTime(keyPacket, selfCertificate, defaultValue=null) { /** * Returns primary user and most significant (latest valid) self signature - * - if multiple users are marked as primary users returns the one with the latest self signature - * - if no primary user is found returns the user with the latest self signature + * - if multiple primary users exist, returns the one with the latest self signature + * - otherwise, returns the user with the latest self signature + * + * NOTE: call verifyPrimaryUser before calling this function. * @param {Date} date use the given date for verification instead of the current time * @return {{user: Array, selfCertificate: Array}|null} The primary user and the self signature */ @@ -696,23 +698,31 @@ Key.prototype.verifyPrimaryUser = async function(keys) { return; } const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey }; - await Promise.all(user.selfCertifications.map(async function(selfCertification) { + // TODO replace when Promise.forEach is implemented + for (let i = 0; i < user.selfCertifications.length; i++) { + const selfCertification = user.selfCertifications[i]; // 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()) { + // skip if certificates is invalid, revoked, or expired + // eslint-disable-next-line no-await-in-loop + if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify))) { + return; + } + // eslint-disable-next-line no-await-in-loop + if (selfCertification.revoked || await user.isRevoked(primaryKey, selfCertification)) { + return; + } + if (selfCertification.isExpired()) { return; } lastPrimaryUserID = selfCertification.isPrimaryUserID; lastCreated = selfCertification.created; primaryUsers.push(user); - })); + } })); const user = primaryUsers.pop(); const results = !user ? [] : keys ? await user.verifyAllCertifications(primaryKey, keys) : @@ -781,6 +791,7 @@ User.prototype.toPacketlist = function() { * @return {Boolean} True if the certificate is revoked */ User.prototype.isRevoked = async function(primaryKey, certificate, key) { + certificate.revoked = null; if (this.revocationCertifications) { const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey }; // TODO clarify OpenPGP's behavior given an expired revocation signature @@ -1294,7 +1305,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) { await Promise.all(options.userIds.map(async function(userId, index) { const userIdPacket = new packet.Userid(); - userIdPacket.read(util.str2Uint8Array(userId)); + userIdPacket.read(util.str_to_Uint8Array(userId)); const dataToSign = {}; dataToSign.userid = userIdPacket; diff --git a/src/message.js b/src/message.js index 57320ecb..ed142024 100644 --- a/src/message.js +++ b/src/message.js @@ -188,7 +188,7 @@ Message.prototype.decryptSessionKeys = function(privateKeys, passwords) { if (keyPackets.length > 1) { const seen = {}; keyPackets = keyPackets.filter(function(item) { - const k = item.sessionKeyAlgorithm + util.Uint8Array2str(item.sessionKey); + const k = item.sessionKeyAlgorithm + util.Uint8Array_to_str(item.sessionKey); if (seen.hasOwnProperty(k)) { return false; } @@ -622,7 +622,7 @@ export function read(input) { */ export function readSignedContent(content, detachedSignature) { const literalDataPacket = new packet.Literal(); - literalDataPacket.setBytes(util.str2Uint8Array(content), enums.read(enums.literal, enums.literal.binary)); + literalDataPacket.setBytes(util.str_to_Uint8Array(content), enums.read(enums.literal, enums.literal.binary)); const packetlist = new packet.List(); packetlist.push(literalDataPacket); const input = armor.decode(detachedSignature).data; diff --git a/src/openpgp.js b/src/openpgp.js index 5142b520..bbe9f658 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -393,7 +393,7 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords, wild * Decrypt symmetric session keys with a private key or password. Either a private key or * a password must be specified. * @param {Message} message a message object containing the encrypted session key packets - * @param {Key|Array} privateKeys (optional) private keys with decrypted secret key data * @param {String|Array} passwords (optional) passwords to decrypt the session key * @return {Promise} Array of decrypted session key, algorithm pairs in form: * { data:Uint8Array, algorithm:String } diff --git a/src/packet/clone.js b/src/packet/clone.js index b6969003..83cb9221 100644 --- a/src/packet/clone.js +++ b/src/packet/clone.js @@ -19,6 +19,7 @@ * @fileoverview This module implements packet list cloning required to * pass certain object types between the web worker and main thread using * the structured cloning algorithm. + * @module packet/clone */ import { Key } from '../key'; diff --git a/src/packet/compressed.js b/src/packet/compressed.js index a726a103..4a3ee47d 100644 --- a/src/packet/compressed.js +++ b/src/packet/compressed.js @@ -16,9 +16,10 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Compressed Data Packet (Tag 8)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.6|RFC4880 5.6}: The Compressed Data packet contains compressed data. Typically, + * Implementation of the Compressed Data Packet (Tag 8) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.6|RFC4880 5.6}: + * The Compressed Data packet contains compressed data. Typically, * this packet is found as the contents of an encrypted packet, or following * a Signature or One-Pass Signature packet, and contains a literal data packet. * @requires compression/zlib diff --git a/src/packet/literal.js b/src/packet/literal.js index 5ff1bf64..79d08685 100644 --- a/src/packet/literal.js +++ b/src/packet/literal.js @@ -16,10 +16,11 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Literal Data Packet (Tag 11)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.9|RFC4880 5.9}: A Literal Data packet contains the body of a message; data that - * is not to be further interpreted. + * Implementation of the Literal Data Packet (Tag 11) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.9|RFC4880 5.9}: + * A Literal Data packet contains the body of a message; data that is not to be + * further interpreted. * @requires enums * @requires util * @module packet/literal @@ -49,7 +50,7 @@ Literal.prototype.setText = function(text) { // normalize EOL to \r\n text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n'); // encode UTF8 - this.data = this.format === 'utf8' ? util.str2Uint8Array(util.encode_utf8(text)) : util.str2Uint8Array(text); + this.data = this.format === 'utf8' ? util.str_to_Uint8Array(util.encode_utf8(text)) : util.str_to_Uint8Array(text); }; /** @@ -59,7 +60,7 @@ Literal.prototype.setText = function(text) { */ Literal.prototype.getText = function() { // decode UTF8 - const text = util.decode_utf8(util.Uint8Array2str(this.data)); + const text = util.decode_utf8(util.Uint8Array_to_str(this.data)); // normalize EOL to \n return text.replace(/\r\n/g, '\n'); }; @@ -113,7 +114,7 @@ Literal.prototype.read = function(bytes) { const format = enums.read(enums.literal, bytes[0]); const filename_len = bytes[1]; - this.filename = util.decode_utf8(util.Uint8Array2str(bytes.subarray(2, 2 + filename_len))); + this.filename = util.decode_utf8(util.Uint8Array_to_str(bytes.subarray(2, 2 + filename_len))); this.date = util.readDate(bytes.subarray(2 + filename_len, 2 + filename_len + 4)); @@ -128,7 +129,7 @@ Literal.prototype.read = function(bytes) { * @return {Uint8Array} Uint8Array representation of the packet */ Literal.prototype.write = function() { - const filename = util.str2Uint8Array(util.encode_utf8(this.filename)); + const filename = util.str_to_Uint8Array(util.encode_utf8(this.filename)); const filename_length = new Uint8Array([filename.length]); const format = new Uint8Array([enums.write(enums.literal, this.format)]); diff --git a/src/packet/marker.js b/src/packet/marker.js index 1c008a38..f77ce0c4 100644 --- a/src/packet/marker.js +++ b/src/packet/marker.js @@ -15,15 +15,15 @@ // 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 the strange "Marker packet" (Tag 10)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.8|RFC4880 5.8}: An experimental version of PGP used this packet as the Literal + * Implementation of the strange "Marker packet" (Tag 10) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.8|RFC4880 5.8}: + * An experimental version of PGP used this packet as the Literal * packet, but no released version of PGP generated Literal packets with this * tag. With PGP 5.x, this packet has been reassigned and is reserved for use as - * the Marker packet.
- *
+ * the Marker packet. + * * Such a packet MUST be ignored when received. * @requires enums * @module packet/marker diff --git a/src/packet/one_pass_signature.js b/src/packet/one_pass_signature.js index b3dc74f6..7facea19 100644 --- a/src/packet/one_pass_signature.js +++ b/src/packet/one_pass_signature.js @@ -16,14 +16,15 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the One-Pass Signature Packets (Tag 4)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.4|RFC4880 5.4}: The One-Pass Signature packet precedes the signed data and contains + * Implementation of the One-Pass Signature Packets (Tag 4) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.4|RFC4880 5.4}: + * The One-Pass Signature packet precedes the signed data and contains * enough information to allow the receiver to begin calculating any * hashes needed to verify the signature. It allows the Signature * packet to be placed at the end of the message, so that the signer * can compute the entire signed message in one pass. -* @requires util + * @requires util * @requires enums * @requires type/keyid * @module packet/one_pass_signature diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 58f037d9..73b7366e 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -16,8 +16,8 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Key Material Packet (Tag 5,6,7,14)
- *
+ * Implementation of the Key Material Packet (Tag 5,6,7,14) + * * {@link https://tools.ietf.org/html/rfc4880#section-5.5|RFC4480 5.5}: * A key material packet contains all the information about a public or * private key. There are four variants of this packet type, and two @@ -85,8 +85,8 @@ 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++]); - - const types = crypto.getPubKeyParamTypes(this.algorithm); + const algo = enums.write(enums.publicKey, this.algorithm); + const types = crypto.getPubKeyParamTypes(algo); this.params = crypto.constructParams(types); const b = bytes.subarray(pos, bytes.length); @@ -123,10 +123,10 @@ PublicKey.prototype.write = function () { if (this.version === 3) { arr.push(util.writeNumber(this.expirationTimeV3, 2)); } - arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); - - const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; - + // Algorithm-specific params + const algo = enums.write(enums.publicKey, this.algorithm); + const paramCount = crypto.getPubKeyParamTypes(algo).length; + arr.push(new Uint8Array([algo])); for (let i = 0; i < paramCount; i++) { arr.push(this.params[i].write()); } @@ -159,7 +159,7 @@ PublicKey.prototype.getKeyId = function () { } this.keyid = new type_keyid(); if (this.version === 4) { - this.keyid.read(util.str2Uint8Array(util.hex2bin(this.getFingerprint()).substr(12, 8))); + this.keyid.read(util.str_to_Uint8Array(util.hex_to_str(this.getFingerprint()).substr(12, 8))); } else if (this.version === 3) { const arr = this.params[0].write(); this.keyid.read(arr.subarray(arr.length - 8, arr.length)); @@ -178,21 +178,22 @@ PublicKey.prototype.getFingerprint = function () { let toHash = ''; if (this.version === 4) { toHash = this.writeOld(); - this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); + this.fingerprint = util.Uint8Array_to_str(crypto.hash.sha1(toHash)); } else if (this.version === 3) { - const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; + const algo = enums.write(enums.publicKey, this.algorithm); + const paramCount = crypto.getPubKeyParamTypes(algo).length; for (let i = 0; i < paramCount; i++) { - toHash += this.params[i].toBytes(); + toHash += this.params[i].toString(); } - this.fingerprint = util.Uint8Array2str(crypto.hash.md5(util.str2Uint8Array(toHash))); + this.fingerprint = util.Uint8Array_to_str(crypto.hash.md5(util.str_to_Uint8Array(toHash))); } - this.fingerprint = util.hexstrdump(this.fingerprint); + this.fingerprint = util.str_to_hex(this.fingerprint); return this.fingerprint; }; /** * Returns algorithm information - * @return {Promise} An object of the form {algorithm: String, bits:int, curve:String} */ PublicKey.prototype.getAlgorithmInfo = function () { const result = {}; @@ -200,7 +201,7 @@ PublicKey.prototype.getAlgorithmInfo = function () { if (this.params[0] instanceof type_mpi) { result.bits = this.params[0].byteLength() * 8; } else { - result.curve = crypto.publicKey.elliptic.get(this.params[0]).name; + result.curve = this.params[0].getName(); } return result; }; @@ -209,7 +210,8 @@ PublicKey.prototype.getAlgorithmInfo = function () { * Fix custom types after cloning */ PublicKey.prototype.postCloneTypeFix = function() { - const types = crypto.getPubKeyParamTypes(this.algorithm); + const algo = enums.write(enums.publicKey, this.algorithm); + const types = crypto.getPubKeyParamTypes(algo); for (let i = 0; i < types.length; i++) { const param = this.params[i]; this.params[i] = types[i].fromClone(param); diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 2a109974..5342b649 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -16,9 +16,10 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Public-Key Encrypted Session Key Packets (Tag 1)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: A Public-Key Encrypted Session Key packet holds the session key + * Public-Key Encrypted Session Key Packets (Tag 1) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: + * A Public-Key Encrypted Session Key packet holds the session key * used to encrypt a message. Zero or more Public-Key Encrypted Session Key * packets and/or Symmetric-Key Encrypted Session Key packets may precede a * Symmetrically Encrypted Data Packet, which holds an encrypted message. The @@ -75,7 +76,8 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { let i = 10; - const types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); + const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); + const types = crypto.getEncSessionKeyParamTypes(algo); this.encrypted = crypto.constructParams(types); for (let j = 0; j < types.length; j++) { @@ -101,23 +103,20 @@ PublicKeyEncryptedSessionKey.prototype.write = function () { PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { let data = String.fromCharCode(enums.write(enums.symmetric, this.sessionKeyAlgorithm)); - data += util.Uint8Array2str(this.sessionKey); + data += util.Uint8Array_to_str(this.sessionKey); const checksum = util.calc_checksum(this.sessionKey); - data += util.Uint8Array2str(util.writeNumber(checksum, 2)); + data += util.Uint8Array_to_str(util.writeNumber(checksum, 2)); let toEncrypt; - if (this.publicKeyAlgorithm === 'ecdh') { + const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); + if (algo === enums.publicKey.ecdh) { toEncrypt = new type_mpi(crypto.pkcs5.encode(data)); } else { toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength())); } this.encrypted = await crypto.publicKeyEncrypt( - this.publicKeyAlgorithm, - key.params, - toEncrypt, - key.fingerprint - ); + algo, key.params, toEncrypt, key.fingerprint); }; /** @@ -129,24 +128,21 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { * @return {String} The unencrypted session key */ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { - const result = (await crypto.publicKeyDecrypt( - this.publicKeyAlgorithm, - key.params, - this.encrypted, - key.fingerprint - )).toBytes(); + const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); + const result = await crypto.publicKeyDecrypt( + algo, key.params, this.encrypted, key.fingerprint); let checksum; let decoded; - if (this.publicKeyAlgorithm === 'ecdh') { - decoded = crypto.pkcs5.decode(result); - checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); + if (algo === enums.publicKey.ecdh) { + decoded = crypto.pkcs5.decode(result.toString()); + checksum = util.readNumber(util.str_to_Uint8Array(decoded.substr(decoded.length - 2))); } else { - decoded = crypto.pkcs1.eme.decode(result); - checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); + decoded = crypto.pkcs1.eme.decode(result.toString()); + checksum = util.readNumber(result.toUint8Array().slice(result.byteLength() - 2)); } - key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2)); + key = util.str_to_Uint8Array(decoded.substring(1, decoded.length - 2)); if (checksum !== util.calc_checksum(key)) { throw new Error('Checksum mismatch'); @@ -161,7 +157,8 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { */ PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() { this.publicKeyId = type_keyid.fromClone(this.publicKeyId); - const types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); + const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); + const types = crypto.getEncSessionKeyParamTypes(algo); for (let i = 0; i < this.encrypted.length; i++) { this.encrypted[i] = types[i].fromClone(this.encrypted[i]); } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 1323d8eb..dc9d7a50 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -16,8 +16,8 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Key Material Packet (Tag 5,6,7,14)
- *
+ * Implementation of the Key Material Packet (Tag 5,6,7,14) + * * {@link https://tools.ietf.org/html/rfc4880#section-5.5|RFC4480 5.5}: * A key material packet contains all the information about a public or * private key. There are four variants of this packet type, and two @@ -76,15 +76,16 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { const hashlen = get_hash_len(hash_algorithm); const hashfn = get_hash_fn(hash_algorithm); - const hashtext = util.Uint8Array2str(cleartext.subarray(cleartext.length - hashlen, cleartext.length)); + const hashtext = util.Uint8Array_to_str(cleartext.subarray(cleartext.length - hashlen, cleartext.length)); cleartext = cleartext.subarray(0, cleartext.length - hashlen); - const hash = util.Uint8Array2str(hashfn(cleartext)); + const hash = util.Uint8Array_to_str(hashfn(cleartext)); if (hash !== hashtext) { return new Error("Hash mismatch."); } - const types = crypto.getPrivKeyParamTypes(algorithm); + const algo = enums.write(enums.publicKey, algorithm); + const types = crypto.getPrivKeyParamTypes(algo); const params = crypto.constructParams(types); let p = 0; @@ -100,7 +101,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { function write_cleartext_params(hash_algorithm, algorithm, params) { const arr = []; - const numPublicParams = crypto.getPubKeyParamTypes(algorithm).length; + const algo = enums.write(enums.publicKey, algorithm); + const numPublicParams = crypto.getPubKeyParamTypes(algo).length; for (let i = numPublicParams; i < params.length; i++) { arr.push(params[i].write()); @@ -269,8 +271,8 @@ SecretKey.prototype.decrypt = function (passphrase) { SecretKey.prototype.generate = function (bits, curve) { const that = this; - - return crypto.generateParams(that.algorithm, bits, curve).then(function(params) { + const algo = enums.write(enums.publicKey, that.algorithm); + return crypto.generateParams(algo, bits, curve).then(function(params) { that.params = params; that.isDecrypted = true; }); @@ -283,7 +285,8 @@ SecretKey.prototype.clearPrivateParams = function () { if (!this.encrypted) { 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); + const algo = enums.write(enums.publicKey, this.algorithm); + this.params = this.params.slice(0, crypto.getPubKeyParamTypes(algo).length); this.isDecrypted = false; }; @@ -291,7 +294,8 @@ SecretKey.prototype.clearPrivateParams = function () { * Fix custom types after cloning */ SecretKey.prototype.postCloneTypeFix = function() { - const types = crypto.getPubKeyParamTypes(this.algorithm).concat(crypto.getPrivKeyParamTypes(this.algorithm)); + const algo = enums.write(enums.publicKey, this.algorithm); + const types = [].concat(crypto.getPubKeyParamTypes(algo), crypto.getPrivKeyParamTypes(algo)); for (let i = 0; i < this.params.length; i++) { const param = this.params[i]; this.params[i] = types[i].fromClone(param); diff --git a/src/packet/signature.js b/src/packet/signature.js index d1fa0f5b..b1449a17 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -16,8 +16,8 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Signature Packet (Tag 2)
- *
+ * Implementation of the Signature Packet (Tag 2) + * * {@link https://tools.ietf.org/html/rfc4880#section-5.2|RFC4480 5.2}: * A Signature packet describes a binding between some public key and * some data. The most common signatures are a signature of a file or a @@ -85,7 +85,7 @@ export default function Signature(date=new Date()) { this.signatureTargetHash = null; this.embeddedSignature = null; - this.verified = false; + this.verified = null; } /** @@ -245,8 +245,7 @@ Signature.prototype.sign = async function (key, data) { this.signedHashValue = hash.subarray(0, 2); this.signature = await crypto.signature.sign( - hashAlgorithm, - publicKeyAlgorithm, key.params, toHash + publicKeyAlgorithm, hashAlgorithm, key.params, toHash ); }; @@ -281,7 +280,7 @@ Signature.prototype.write_all_sub_packets = function () { arr.push(write_sub_packet(sub.key_expiration_time, util.writeNumber(this.keyExpirationTime, 4))); } if (this.preferredSymmetricAlgorithms !== null) { - bytes = util.str2Uint8Array(util.bin2str(this.preferredSymmetricAlgorithms)); + bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredSymmetricAlgorithms)); arr.push(write_sub_packet(sub.preferred_symmetric_algorithms, bytes)); } if (this.revocationKeyClass !== null) { @@ -301,51 +300,51 @@ Signature.prototype.write_all_sub_packets = function () { bytes.push(util.writeNumber(name.length, 2)); // 2 octets of value length bytes.push(util.writeNumber(value.length, 2)); - bytes.push(util.str2Uint8Array(name + value)); + bytes.push(util.str_to_Uint8Array(name + value)); bytes = util.concatUint8Array(bytes); arr.push(write_sub_packet(sub.notation_data, bytes)); } } } if (this.preferredHashAlgorithms !== null) { - bytes = util.str2Uint8Array(util.bin2str(this.preferredHashAlgorithms)); + bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredHashAlgorithms)); arr.push(write_sub_packet(sub.preferred_hash_algorithms, bytes)); } if (this.preferredCompressionAlgorithms !== null) { - bytes = util.str2Uint8Array(util.bin2str(this.preferredCompressionAlgorithms)); + bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredCompressionAlgorithms)); arr.push(write_sub_packet(sub.preferred_compression_algorithms, bytes)); } if (this.keyServerPreferences !== null) { - bytes = util.str2Uint8Array(util.bin2str(this.keyServerPreferences)); + bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.keyServerPreferences)); arr.push(write_sub_packet(sub.key_server_preferences, bytes)); } if (this.preferredKeyServer !== null) { - arr.push(write_sub_packet(sub.preferred_key_server, util.str2Uint8Array(this.preferredKeyServer))); + arr.push(write_sub_packet(sub.preferred_key_server, util.str_to_Uint8Array(this.preferredKeyServer))); } if (this.isPrimaryUserID !== null) { arr.push(write_sub_packet(sub.primary_user_id, new Uint8Array([this.isPrimaryUserID ? 1 : 0]))); } if (this.policyURI !== null) { - arr.push(write_sub_packet(sub.policy_uri, util.str2Uint8Array(this.policyURI))); + arr.push(write_sub_packet(sub.policy_uri, util.str_to_Uint8Array(this.policyURI))); } if (this.keyFlags !== null) { - bytes = util.str2Uint8Array(util.bin2str(this.keyFlags)); + bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.keyFlags)); arr.push(write_sub_packet(sub.key_flags, bytes)); } if (this.signersUserId !== null) { - arr.push(write_sub_packet(sub.signers_user_id, util.str2Uint8Array(this.signersUserId))); + arr.push(write_sub_packet(sub.signers_user_id, util.str_to_Uint8Array(this.signersUserId))); } if (this.reasonForRevocationFlag !== null) { - bytes = util.str2Uint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString); + bytes = util.str_to_Uint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString); arr.push(write_sub_packet(sub.reason_for_revocation, bytes)); } if (this.features !== null) { - bytes = util.str2Uint8Array(util.bin2str(this.features)); + bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.features)); arr.push(write_sub_packet(sub.features, bytes)); } if (this.signatureTargetPublicKeyAlgorithm !== null) { bytes = [new Uint8Array([this.signatureTargetPublicKeyAlgorithm, this.signatureTargetHashAlgorithm])]; - bytes.push(util.str2Uint8Array(this.signatureTargetHash)); + bytes.push(util.str_to_Uint8Array(this.signatureTargetHash)); bytes = util.concatUint8Array(bytes); arr.push(write_sub_packet(sub.signature_target, bytes)); } @@ -460,8 +459,8 @@ Signature.prototype.read_sub_packet = function (bytes) { const n = util.readNumber(bytes.subarray(mypos, mypos + 2)); mypos += 2; - const name = util.Uint8Array2str(bytes.subarray(mypos, mypos + m)); - const value = util.Uint8Array2str(bytes.subarray(mypos + m, mypos + m + n)); + const name = util.Uint8Array_to_str(bytes.subarray(mypos, mypos + m)); + const value = util.Uint8Array_to_str(bytes.subarray(mypos + m, mypos + m + n)); this.notation = this.notation || {}; this.notation[name] = value; @@ -483,7 +482,7 @@ Signature.prototype.read_sub_packet = function (bytes) { break; case 24: // Preferred Key Server - this.preferredKeyServer = util.Uint8Array2str(bytes.subarray(mypos, bytes.length)); + this.preferredKeyServer = util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); break; case 25: // Primary User ID @@ -491,7 +490,7 @@ Signature.prototype.read_sub_packet = function (bytes) { break; case 26: // Policy URI - this.policyURI = util.Uint8Array2str(bytes.subarray(mypos, bytes.length)); + this.policyURI = util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); break; case 27: // Key Flags @@ -499,12 +498,12 @@ Signature.prototype.read_sub_packet = function (bytes) { break; case 28: // Signer's User ID - this.signersUserId += util.Uint8Array2str(bytes.subarray(mypos, bytes.length)); + this.signersUserId += util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); break; case 29: // Reason for Revocation this.reasonForRevocationFlag = bytes[mypos++]; - this.reasonForRevocationString = util.Uint8Array2str(bytes.subarray(mypos, bytes.length)); + this.reasonForRevocationString = util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length)); break; case 30: // Features @@ -518,7 +517,7 @@ Signature.prototype.read_sub_packet = function (bytes) { const len = crypto.getHashByteLength(this.signatureTargetHashAlgorithm); - this.signatureTargetHash = util.Uint8Array2str(bytes.subarray(mypos, mypos + len)); + this.signatureTargetHash = util.Uint8Array_to_str(bytes.subarray(mypos, mypos + len)); break; } case 32: @@ -652,8 +651,7 @@ Signature.prototype.verify = async function (key, data) { } this.verified = await crypto.signature.verify( - publicKeyAlgorithm, - hashAlgorithm, mpi, key.params, + publicKeyAlgorithm, hashAlgorithm, mpi, key.params, util.concatUint8Array([bytes, this.signatureData, trailer]) ); diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index dc43541e..25a9075d 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -16,25 +16,25 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Sym. Encrypted Integrity Protected Data - * Packet (Tag 18)
- *
+ * Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) + * * {@link https://tools.ietf.org/html/rfc4880#section-5.13|RFC4880 5.13}: * The Symmetrically Encrypted Integrity Protected Data packet is * a variant of the Symmetrically Encrypted Data packet. It is a new feature * created for OpenPGP that addresses the problem of detecting a modification to * encrypted data. It is used in combination with a Modification Detection Code * packet. + * @requires asmcrypto.js * @requires crypto - * @requires util * @requires enums + * @requires util * @module packet/sym_encrypted_integrity_protected */ -import asmCrypto from 'asmcrypto-lite'; -import util from '../util.js'; +import { AES_CFB } from 'asmcrypto.js/src/aes/cfb/exports'; import crypto from '../crypto'; -import enums from '../enums.js'; +import enums from '../enums'; +import util from '../util'; const nodeCrypto = util.getNodeCrypto(); const Buffer = util.getNodeBuffer(); @@ -121,8 +121,8 @@ SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm const prefix = crypto.cfb.mdc(sessionKeyAlgorithm, key, this.encrypted); const bytes = decrypted.subarray(0, decrypted.length - 20); const tohash = util.concatUint8Array([prefix, bytes]); - this.hash = util.Uint8Array2str(crypto.hash.sha1(tohash)); - const mdc = util.Uint8Array2str(decrypted.subarray(decrypted.length - 20, decrypted.length)); + this.hash = util.Uint8Array_to_str(crypto.hash.sha1(tohash)); + const mdc = util.Uint8Array_to_str(decrypted.subarray(decrypted.length - 20, decrypted.length)); if (this.hash !== mdc) { throw new Error('Modification detected.'); @@ -145,7 +145,7 @@ function aesEncrypt(algo, prefix, pt, key) { if (nodeCrypto) { // Node crypto library. return nodeEncrypt(algo, prefix, pt, key); } // asm.js fallback - return asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, pt]), key); + return AES_CFB.encrypt(util.concatUint8Array([prefix, pt]), key); } function aesDecrypt(algo, ct, key) { @@ -153,7 +153,7 @@ function aesDecrypt(algo, ct, key) { if (nodeCrypto) { // Node crypto library. pt = nodeDecrypt(algo, ct, key); } else { // asm.js fallback - pt = asmCrypto.AES_CFB.decrypt(ct, key); + pt = AES_CFB.decrypt(ct, key); } return pt.subarray(crypto.cipher[algo].blockSize + 2, pt.length); // Remove random prefix } diff --git a/src/packet/sym_encrypted_session_key.js b/src/packet/sym_encrypted_session_key.js index 3ef36fde..fa6d25ab 100644 --- a/src/packet/sym_encrypted_session_key.js +++ b/src/packet/sym_encrypted_session_key.js @@ -16,9 +16,10 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Public-Key Encrypted Session Key Packets (Tag 1)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: A Public-Key Encrypted Session Key packet holds the session key + * Public-Key Encrypted Session Key Packets (Tag 1) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: + * A Public-Key Encrypted Session Key packet holds the session key * used to encrypt a message. Zero or more Public-Key Encrypted Session Key * packets and/or Symmetric-Key Encrypted Session Key packets may precede a * Symmetrically Encrypted Data Packet, which holds an encrypted message. The diff --git a/src/packet/symmetrically_encrypted.js b/src/packet/symmetrically_encrypted.js index cb3efbb6..210ef532 100644 --- a/src/packet/symmetrically_encrypted.js +++ b/src/packet/symmetrically_encrypted.js @@ -16,10 +16,11 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the Symmetrically Encrypted Data Packet (Tag 9)
- *
- * {@link https://tools.ietf.org/html/rfc4880#section-5.7|RFC4880 5.7}: The Symmetrically Encrypted Data packet contains data encrypted - * with a symmetric-key algorithm. When it has been decrypted, it contains other + * Implementation of the Symmetrically Encrypted Data Packet (Tag 9) + * + * {@link https://tools.ietf.org/html/rfc4880#section-5.7|RFC4880 5.7}: + * The Symmetrically Encrypted Data packet contains data encrypted with a + * symmetric-key algorithm. When it has been decrypted, it contains other * packets (usually a literal data packet or compressed data packet, but in * theory other Symmetrically Encrypted Data packets or sequences of packets * that form whole OpenPGP messages). diff --git a/src/packet/user_attribute.js b/src/packet/user_attribute.js index d671153f..eaabdcbf 100644 --- a/src/packet/user_attribute.js +++ b/src/packet/user_attribute.js @@ -16,15 +16,15 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the User Attribute Packet (Tag 17)
- *
+ * Implementation of the User Attribute Packet (Tag 17) + * * The User Attribute packet is a variation of the User ID packet. It * is capable of storing more types of data than the User ID packet, * which is limited to text. Like the User ID packet, a User Attribute * packet may be certified by the key owner ("self-signed") or any other * key owner who cares to certify it. Except as noted, a User Attribute * packet may be used anywhere that a User ID packet may be used. - *
+ * * While User Attribute packets are not a required part of the OpenPGP * standard, implementations SHOULD provide at least enough * compatibility to properly handle a certification signature on the @@ -58,7 +58,7 @@ UserAttribute.prototype.read = function(bytes) { const len = packet.readSimpleLength(bytes.subarray(i, bytes.length)); i += len.offset; - this.attributes.push(util.Uint8Array2str(bytes.subarray(i, i + len.len))); + this.attributes.push(util.Uint8Array_to_str(bytes.subarray(i, i + len.len))); i += len.len; } }; @@ -71,7 +71,7 @@ UserAttribute.prototype.write = function() { const arr = []; for (let i = 0; i < this.attributes.length; i++) { arr.push(packet.writeSimpleLength(this.attributes[i].length)); - arr.push(util.str2Uint8Array(this.attributes[i])); + arr.push(util.str_to_Uint8Array(this.attributes[i])); } return util.concatUint8Array(arr); }; diff --git a/src/packet/userid.js b/src/packet/userid.js index 68ff8958..95bde6ba 100644 --- a/src/packet/userid.js +++ b/src/packet/userid.js @@ -16,8 +16,8 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the User ID Packet (Tag 13)
- *
+ * Implementation of the User ID Packet (Tag 13) + * * A User ID packet consists of UTF-8 text that is intended to represent * the name and email address of the key holder. By convention, it * includes an RFC 2822 [RFC2822] mail name-addr, but there are no @@ -48,7 +48,7 @@ export default function Userid() { * @param {Uint8Array} input payload of a tag 13 packet */ Userid.prototype.read = function (bytes) { - this.userid = util.decode_utf8(util.Uint8Array2str(bytes)); + this.userid = util.decode_utf8(util.Uint8Array_to_str(bytes)); }; /** @@ -56,5 +56,5 @@ Userid.prototype.read = function (bytes) { * @return {Uint8Array} binary representation */ Userid.prototype.write = function () { - return util.str2Uint8Array(util.encode_utf8(this.userid)); + return util.str_to_Uint8Array(util.encode_utf8(this.userid)); }; diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index 87f9cf3e..31599aaa 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -16,24 +16,22 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Encoded symmetric key for ECDH
- *
+ * Encoded symmetric key for ECDH + * * @requires util * @module type/ecdh_symkey */ import util from '../util'; -module.exports = ECDHSymmetricKey; - /** * @constructor */ -function ECDHSymmetricKey(data) { +export default function ECDHSymmetricKey(data) { if (typeof data === 'undefined') { data = new Uint8Array([]); } else if (util.isString(data)) { - data = util.str2Uint8Array(data); + data = util.str_to_Uint8Array(data); } else { data = new Uint8Array(data); } diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 30bcf469..d8e60ea4 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -16,16 +16,14 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of type KDF parameters RFC 6637
- *
+ * Implementation of type KDF parameters RFC 6637 + * * @requires enums * @module type/kdf_params */ import enums from '../enums.js'; -module.exports = KDFParams; - /** * @constructor * @param {enums.hash} hash Hash algorithm @@ -66,3 +64,5 @@ KDFParams.prototype.write = function () { KDFParams.fromClone = function (clone) { return new KDFParams(clone.hash, clone.cipher); }; + +export default KDFParams; diff --git a/src/type/keyid.js b/src/type/keyid.js index b256a3bb..1b75bb9a 100644 --- a/src/type/keyid.js +++ b/src/type/keyid.js @@ -16,8 +16,9 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of type key id ({@link https://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3})
- *
+ * Implementation of type key id + * + * {@link https://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3}: * A Key ID is an eight-octet scalar that identifies a key. * Implementations SHOULD NOT assume that Key IDs are unique. The * section "Enhanced Key Formats" below describes how Key IDs are @@ -40,15 +41,15 @@ export default function Keyid() { * @param {Uint8Array} input Input to read the key id from */ Keyid.prototype.read = function(bytes) { - this.bytes = util.Uint8Array2str(bytes.subarray(0, 8)); + this.bytes = util.Uint8Array_to_str(bytes.subarray(0, 8)); }; Keyid.prototype.write = function() { - return util.str2Uint8Array(this.bytes); + return util.str_to_Uint8Array(this.bytes); }; Keyid.prototype.toHex = function() { - return util.hexstrdump(this.bytes); + return util.str_to_hex(this.bytes); }; Keyid.prototype.equals = function(keyid) { @@ -75,7 +76,7 @@ Keyid.fromClone = function (clone) { Keyid.fromId = function (hex) { const keyid = new Keyid(); - keyid.read(util.str2Uint8Array(util.hex2bin(hex))); + keyid.read(util.str_to_Uint8Array(util.hex_to_str(hex))); return keyid; }; diff --git a/src/type/mpi.js b/src/type/mpi.js index 08d7973d..56957c33 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -21,20 +21,19 @@ // - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8) /** - * Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2})
- *
+ * Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2}) * Multiprecision integers (also called MPIs) are unsigned integers used * to hold large integers such as the ones used in cryptographic * calculations. * An MPI consists of two pieces: a two-octet scalar that is the length * of the MPI in bits followed by a string of octets that contain the * actual integer. - * @requires crypto/public_key/jsbn + * @requires bn.js * @requires util * @module type/mpi */ -import BigInteger from '../crypto/public_key/jsbn'; +import BN from 'bn.js'; import util from '../util'; /** @@ -42,83 +41,99 @@ import util from '../util'; */ export default function MPI(data) { /** An implementation dependent integer */ - if (data instanceof BigInteger) { - this.fromBigInteger(data); + if (BN.isBN(data)) { + this.fromBN(data); + } else if (util.isUint8Array(data)) { + this.fromUint8Array(data); } else if (util.isString(data)) { - this.fromBytes(data); + this.fromString(data); } else { this.data = null; } } /** - * Parsing function for a mpi ({@link https://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 + * Parsing function for a MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC 4880 3.2}). + * @param {Uint8Array} input Payload of MPI data + * @param {String} endian Endianness of the data; 'be' for big-endian or 'le' for little-endian + * @return {Integer} Length of data read */ MPI.prototype.read = function (bytes, endian='be') { if (util.isString(bytes)) { - bytes = util.str2Uint8Array(bytes); + bytes = util.str_to_Uint8Array(bytes); } const bits = (bytes[0] << 8) | bytes[1]; + const bytelen = (bits + 7) >>> 3; + const payload = bytes.subarray(2, 2 + bytelen); - // Additional rules: - // - // The size of an MPI is ((MPI.length + 7) / 8) + 2 octets. - // - // The length field of an MPI describes the length starting from its - // most significant non-zero bit. Thus, the MPI [00 02 01] is not - // formed correctly. It should be [00 01 01]. - - // TODO: Verification of this size method! This size calculation as - // specified above is not applicable in JavaScript - const bytelen = Math.ceil(bits / 8); - let payload = bytes.subarray(2, 2 + bytelen); - if (endian === 'le') { - payload = new Uint8Array(payload).reverse(); - } - const raw = util.Uint8Array2str(payload); - this.fromBytes(raw); + this.fromUint8Array(payload, endian); return 2 + bytelen; }; -MPI.prototype.fromBytes = function (bytes) { - this.data = new BigInteger(util.hexstrdump(bytes), 16); +/** + * Converts the mpi object to a bytes as specified in + * {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2} + * @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian + * @param {Integer} length Length of the data part of the MPI + * @return {Uint8Aray} mpi Byte representation + */ +MPI.prototype.write = function (endian, length) { + return util.Uint8Array_to_MPI(this.toUint8Array(endian, length)); }; -MPI.prototype.toBytes = function () { - const bytes = util.Uint8Array2str(this.write()); - return bytes.substr(2); +MPI.prototype.bitLength = function () { + return (this.data.length - 1) * 8 + util.nbits(this.data[0]); }; MPI.prototype.byteLength = function () { - return this.toBytes().length; + return this.data.length; }; -/** - * Converts the mpi object to a bytes as specified in {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2} - * @return {Uint8Aray} mpi Byte representation - */ -MPI.prototype.write = function () { - return util.str2Uint8Array(this.data.toMPI()); +MPI.prototype.toUint8Array = function (endian, length) { + endian = endian || 'be'; + length = length || this.data.length; + + const payload = new Uint8Array(length); + const start = length - this.data.length; + if (start < 0) { + throw new Error('Payload is too large.'); + } + + payload.set(this.data, start); + if (endian === 'le') { + payload.reverse(); + } + + return payload; }; -MPI.prototype.toBigInteger = function () { - return this.data.clone(); +MPI.prototype.fromUint8Array = function (bytes, endian='be') { + this.data = new Uint8Array(bytes.length); + this.data.set(bytes); + + if (endian === 'le') { + this.data.reverse(); + } }; -MPI.prototype.fromBigInteger = function (bn) { - this.data = bn.clone(); +MPI.prototype.toString = function () { + return util.Uint8Array_to_str(this.toUint8Array()); +}; + +MPI.prototype.fromString = function (str, endian='be') { + this.fromUint8Array(util.str_to_Uint8Array(str), endian); +}; + +MPI.prototype.toBN = function () { + return new BN(this.toUint8Array()); +}; + +MPI.prototype.fromBN = function (bn) { + this.data = bn.toArrayLike(Uint8Array); }; MPI.fromClone = function (clone) { - clone.data.copyTo = BigInteger.prototype.copyTo; - const bn = new BigInteger(); - clone.data.copyTo(bn); - const mpi = new MPI(); - mpi.data = bn; - return mpi; + return new MPI(clone.data); }; diff --git a/src/type/oid.js b/src/type/oid.js index e4ba3e8b..fabbbe0e 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -16,27 +16,30 @@ // 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}. + * Wrapper to an OID value + * + * An object identifier type from + * {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. * @requires util + * @requires enums * @module type/oid */ -import util from '../util.js'; - -module.exports = OID; +import util from '../util'; +import enums from '../enums'; /** * @constructor */ function OID(oid) { - if (typeof oid === 'undefined') { + if (oid instanceof OID) { + oid = oid.oid; + } else if (typeof oid === 'undefined') { oid = ''; } else if (util.isArray(oid)) { - oid = util.bin2str(oid); + oid = util.Uint8Array_to_str(oid); } else if (util.isUint8Array(oid)) { - oid = util.Uint8Array2str(oid); + oid = util.Uint8Array_to_str(oid); } this.oid = oid; } @@ -50,7 +53,7 @@ OID.prototype.read = function (input) { if (input.length >= 1) { const length = input[0]; if (input.length >= 1+length) { - this.oid = util.Uint8Array2str(input.subarray(1, 1+length)); + this.oid = util.Uint8Array_to_str(input.subarray(1, 1+length)); return 1+this.oid.length; } } @@ -62,7 +65,7 @@ OID.prototype.read = function (input) { * @return {Uint8Array} Array with the serialized value the OID */ OID.prototype.write = function () { - return util.str2Uint8Array(String.fromCharCode(this.oid.length)+this.oid); + return util.str_to_Uint8Array(String.fromCharCode(this.oid.length)+this.oid); }; /** @@ -70,10 +73,25 @@ OID.prototype.write = function () { * @return {string} String with the hex value of the OID */ OID.prototype.toHex = function() { - return util.hexstrdump(this.oid); + return util.str_to_hex(this.oid); +}; + +/** + * If a known curve object identifier, return the canonical name of the curve + * @return {string} String with the canonical name of the curve + */ +OID.prototype.getName = function() { + const hex = this.toHex(); + if (enums.curve[hex]) { + return enums.write(enums.curve, hex); + } else { + throw new Error('Unknown curve object identifier.'); + } }; OID.fromClone = function (clone) { const oid = new OID(clone.oid); return oid; }; + +export default OID; diff --git a/src/type/s2k.js b/src/type/s2k.js index 74aa0f60..1493b18f 100644 --- a/src/type/s2k.js +++ b/src/type/s2k.js @@ -16,8 +16,9 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Implementation of the String-to-key specifier ({@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7})
- *
+ * Implementation of the String-to-key specifier + * + * {@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7}: * String-to-key (S2K) specifiers are used to convert passphrase strings * into symmetric-key encryption/decryption keys. They are used in two * places, currently: to encrypt the secret part of private keys in the @@ -83,7 +84,7 @@ S2K.prototype.read = function (bytes) { break; case 'gnu': - if (util.Uint8Array2str(bytes.subarray(i, 3)) === "GNU") { + if (util.Uint8Array_to_str(bytes.subarray(i, 3)) === "GNU") { i += 3; // GNU const gnuExtType = 1000 + bytes[i++]; if (gnuExtType === 1001) { @@ -139,7 +140,7 @@ S2K.prototype.write = function () { * hashAlgorithm hash length */ S2K.prototype.produce_key = function (passphrase, numBytes) { - passphrase = util.str2Uint8Array(util.encode_utf8(passphrase)); + passphrase = util.str_to_Uint8Array(util.encode_utf8(passphrase)); function round(prefix, s2k) { const algorithm = enums.write(enums.hash, s2k.algorithm); diff --git a/src/util.js b/src/util.js index 12fc4e69..401690b9 100644 --- a/src/util.js +++ b/src/util.js @@ -37,21 +37,6 @@ export default { return Uint8Array.prototype.isPrototypeOf(data); }, - isEmailAddress: function(data) { - 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,}|xn--[a-zA-Z\-0-9]+)))$/; - return re.test(data); - }, - - isUserId: function(data) { - if (!this.isString(data)) { - return false; - } - return /$/.test(data); - }, - /** * Get transferable objects to pass buffers with zero copy (similar to "pass by reference" in C++) * See: https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage @@ -114,32 +99,12 @@ export default { return time === null ? time : new Date(Math.floor(+time / 1000) * 1000); }, - hexdump: function (str) { - const r = []; - const e = str.length; - let c = 0; - let h; - let i = 0; - while (c < e) { - h = str.charCodeAt(c++).toString(16); - while (h.length < 2) { - h = "0" + h; - } - r.push(" " + h); - i++; - if (i % 32 === 0) { - r.push("\n "); - } - } - return r.join(''); - }, - /** - * Create hexstring from a binary + * Create hex string from a binary * @param {String} str String to convert * @return {String} String containing the hexadecimal values */ - hexstrdump: function (str) { + str_to_hex: function (str) { if (str === null) { return ""; } @@ -160,9 +125,9 @@ export default { /** * Create binary string from a hex encoded string * @param {String} str Hex string to convert - * @return {String} String containing the binary values + * @return {String} */ - hex2bin: function (hex) { + hex_to_str: function (hex) { let str = ''; for (let i = 0; i < hex.length; i += 2) { str += String.fromCharCode(parseInt(hex.substr(i, 2), 16)); @@ -170,27 +135,68 @@ export default { return str; }, + /** + * Convert a Uint8Array to an MPI-formatted Uint8Array. + * Note: the output is **not** an MPI object. + * @see {@link module:type/mpi/MPI.fromUint8Array} + * @see {@link module:type/mpi/MPI.toUint8Array} + * @param {Uint8Array} bin An array of 8-bit integers to convert + * @return {Uint8Array} MPI-formatted Uint8Array + */ + Uint8Array_to_MPI: function (bin) { + const size = (bin.length - 1) * 8 + this.nbits(bin[0]); + const prefix = Uint8Array.from([(size & 0xFF00) >> 8, size & 0xFF]); + return this.concatUint8Array([prefix, bin]); + }, - hex2Uint8Array: function (hex) { - const result = new Uint8Array(hex.length/2); - for (let k=0; k> 1); + for (let k = 0; k < hex.length >> 1; k++) { + result[k] = parseInt(hex.substr(k << 1, 2), 16); } return result; }, /** - * Creating a hex string from an binary array of integers (0..255) - * @param {String} str Array of bytes to convert + * Convert an array of 8-bit integers to a hex string + * @param {Uint8Array} bytes Array of 8-bit integers to convert * @return {String} Hexadecimal representation of the array */ - hexidump: function (str) { + Uint8Array_to_hex: function (bytes) { const r = []; - const e = str.length; + const e = bytes.length; let c = 0; let h; while (c < e) { - h = str[c++].toString(16); + h = bytes[c++].toString(16); while (h.length < 2) { h = "0" + h; } @@ -199,6 +205,39 @@ export default { return r.join(''); }, + /** + * Convert a string to an array of 8-bit integers + * @param {String} str String to convert + * @return {Uint8Array} An array of 8-bit integers + */ + str_to_Uint8Array: function (str) { + if (!this.isString(str)) { + throw new Error('str_to_Uint8Array: Data must be in the form of a string'); + } + + const result = new Uint8Array(str.length); + for (let i = 0; i < str.length; i++) { + result[i] = str.charCodeAt(i); + } + return result; + }, + + /** + * Convert an array of 8-bit integers to a string + * @param {Uint8Array} bytes An array of 8-bit integers to convert + * @return {String} String representation of the array + */ + Uint8Array_to_str: function (bytes) { + bytes = new Uint8Array(bytes); + const result = []; + const bs = 1 << 14; + const j = bytes.length; + + for (let i = 0; i < j; i += bs) { + result.push(String.fromCharCode.apply(String, bytes.subarray(i, i+bs < j ? i+bs : j))); + } + return result.join(''); + }, /** * Convert a native javascript string to a string of utf8 bytes @@ -225,129 +264,20 @@ export default { } }, - /** - * Convert an array of integers(0.255) to a string - * @param {Array} bin An array of (binary) integers to convert - * @return {String} The string representation of the array - */ - bin2str: function (bin) { - const result = []; - for (let i = 0; i < bin.length; i++) { - result[i] = String.fromCharCode(bin[i]); - } - return result.join(''); - }, - - /** - * Convert a string to an array of integers(0.255) - * @param {String} str String to convert - * @return {Array} An array of (binary) integers - */ - str2bin: function (str) { - const result = []; - for (let i = 0; i < str.length; i++) { - result[i] = str.charCodeAt(i); - } - return result; - }, - - - /** - * Convert a string to a Uint8Array - * @param {String} str String to convert - * @return {Uint8Array} The array of (binary) integers - */ - str2Uint8Array: function (str) { - if (typeof str !== 'string' && !String.prototype.isPrototypeOf(str)) { - throw new Error('str2Uint8Array: Data must be in the form of a string'); - } - - const result = new Uint8Array(str.length); - for (let i = 0; i < str.length; i++) { - result[i] = str.charCodeAt(i); - } - return result; - }, - - /** - * Convert a Uint8Array to a string. This currently functions - * the same as bin2str. - * @function module:util.Uint8Array2str - * @param {Uint8Array} bin An array of (binary) integers to convert - * @return {String} String representation of the array - */ - Uint8Array2str: function (bin) { - if (!Uint8Array.prototype.isPrototypeOf(bin)) { - throw new Error('Uint8Array2str: Data must be in the form of a Uint8Array'); - } - - const result = []; - const bs = 16384; - const j = bin.length; - - for (let i = 0; i < j; i += bs) { - result.push(String.fromCharCode.apply(String, bin.subarray(i, i+bs < j ? i+bs : j))); - } - return result.join(''); - }, - - // returns bit length of the integer x - nbits: function (x) { - let r = 1; - let 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) { - const 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 * @param {Array} Array of Uint8Arrays to concatenate * @return {Uint8array} Concatenated array */ concatUint8Array: function (arrays) { let totalLength = 0; - arrays.forEach(function (element) { - if (!Uint8Array.prototype.isPrototypeOf(element)) { + for (let i = 0; i < arrays.length; i++) { + if (!this.isUint8Array(arrays[i])) { throw new Error('concatUint8Array: Data must be in the form of a Uint8Array'); } - totalLength += element.length; - }); + totalLength += arrays[i].length; + } const result = new Uint8Array(totalLength); let pos = 0; @@ -361,12 +291,11 @@ export default { /** * Deep copy Uint8Array - * @function module:util.copyUint8Array * @param {Uint8Array} Array to copy * @return {Uint8Array} new Uint8Array */ copyUint8Array: function (array) { - if (!Uint8Array.prototype.isPrototypeOf(array)) { + if (!this.isUint8Array(array)) { throw new Error('Data must be in the form of a Uint8Array'); } @@ -377,13 +306,12 @@ export default { /** * Check Uint8Array equality - * @function module:util.equalsUint8Array * @param {Uint8Array} first array * @param {Uint8Array} second array * @return {Boolean} equality */ equalsUint8Array: function (array1, array2) { - if (!Uint8Array.prototype.isPrototypeOf(array1) || !Uint8Array.prototype.isPrototypeOf(array2)) { + if (!this.isUint8Array(array1) || !this.isUint8Array(array2)) { throw new Error('Data must be in the form of a Uint8Array'); } @@ -435,16 +363,17 @@ export default { * Helper function to print a debug message. Debug * messages are only printed if * @link module:config/config.debug is set to true. - * Different than print_debug because will call hexstrdump iff necessary. + * Different than print_debug because will call str_to_hex iff necessary. * @param {String} str String of the debug message */ print_debug_hexstr_dump: function (str, strToHex) { if (config.debug) { - str += this.hexstrdump(strToHex); + str += this.str_to_hex(strToHex); console.log(str); } }, + // TODO rewrite getLeftNBits to work with Uint8Arrays getLeftNBits: function (string, bitcount) { const rest = bitcount % 8; if (rest === 0) { @@ -455,6 +384,37 @@ export default { return this.shiftRight(result, 8 - rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF); }, + // returns bit length of the integer x + nbits: function (x) { + let r = 1; + let 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; + }, + /** * Shifting a string to n bits right * @param {String} value The string to shift @@ -463,7 +423,7 @@ export default { * @return {String} Resulting string. */ shiftRight: function (value, bitcount) { - const temp = this.str2bin(value); + const temp = this.str_to_Uint8Array(value); if (bitcount % 8 !== 0) { for (let i = temp.length - 1; i >= 0; i--) { temp[i] >>= bitcount % 8; @@ -474,31 +434,7 @@ export default { } else { return value; } - return this.bin2str(temp); - }, - - /** - * Return the algorithm type as string - * @return {String} String representing the message type - */ - get_hashAlgorithmString: function (algo) { - switch (algo) { - case 1: - return "MD5"; - case 2: - return "SHA1"; - case 3: - return "RIPEMD160"; - case 8: - return "SHA256"; - case 9: - return "SHA384"; - case 10: - return "SHA512"; - case 11: - return "SHA224"; - } - return "unknown"; + return this.Uint8Array_to_str(temp); }, /** @@ -537,25 +473,6 @@ export default { } }, - /** - * Converts an IE11 web crypto api result to a promise. - * This is required since IE11 implements an old version of the - * Web Crypto specification that does not use promises. - * @param {Object} cryptoOp The return value of an IE11 web cryptro api call - * @param {String} errmsg An error message for a specific operation - * @return {Promise} The resulting Promise - */ - promisifyIE11Op: function(cryptoOp, errmsg) { - return new Promise(function(resolve, reject) { - cryptoOp.onerror = function () { - reject(new Error(errmsg)); - }; - cryptoOp.oncomplete = function (e) { - resolve(e.target.result); - }; - }); - }, - /** * Detect Node.js runtime. */ @@ -598,5 +515,20 @@ export default { } return require('zlib'); + }, + + isEmailAddress: function(data) { + 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,}|xn--[a-zA-Z\-0-9]+)))$/; + return re.test(data); + }, + + isUserId: function(data) { + if (!this.isString(data)) { + return false; + } + return /$/.test(data); } }; diff --git a/test/crypto/aes_kw.js b/test/crypto/aes_kw.js index 3627da0a..4e048dcf 100644 --- a/test/crypto/aes_kw.js +++ b/test/crypto/aes_kw.js @@ -44,13 +44,13 @@ describe('AES Key Wrap and Unwrap', function () { test_vectors.forEach(function(test) { it(test[0], function(done) { - const kek = openpgp.util.hex2Uint8Array(test[1]); + const kek = openpgp.util.hex_to_Uint8Array(test[1]); const input = test[2].replace(/\s/g, ""); - const input_bin = openpgp.util.hex2bin(input); + const input_bin = openpgp.util.hex_to_str(input); const output = test[3].replace(/\s/g, ""); - const output_bin = openpgp.util.hex2bin(output); - 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); + const output_bin = openpgp.util.hex_to_str(output); + expect(openpgp.util.Uint8Array_to_hex(openpgp.crypto.aes_kw.wrap(kek, input_bin)).toUpperCase()).to.equal(output); + expect(openpgp.util.Uint8Array_to_hex(openpgp.crypto.aes_kw.unwrap(kek, output_bin)).toUpperCase()).to.equal(input); done(); }); }); diff --git a/test/crypto/cipher/aes.js b/test/crypto/cipher/aes.js index c7324383..806f7114 100644 --- a/test/crypto/cipher/aes.js +++ b/test/crypto/cipher/aes.js @@ -9,9 +9,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function function test_aes(input, key, output) { const aes = new openpgp.crypto.cipher.aes128(key); - const result = util.bin2str(aes.encrypt(new Uint8Array(input))); + const result = util.Uint8Array_to_str(aes.encrypt(new Uint8Array(input))); - return util.hexstrdump(result) === util.hexstrdump(util.bin2str(output)); + return util.str_to_hex(result) === util.str_to_hex(util.Uint8Array_to_str(output)); } const testvectors128 = [[[0x00,0x01,0x02,0x03,0x05,0x06,0x07,0x08,0x0A,0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x12],[0x50,0x68,0x12,0xA4,0x5F,0x08,0xC8,0x89,0xB9,0x7F,0x59,0x80,0x03,0x8B,0x83,0x59],[0xD8,0xF5,0x32,0x53,0x82,0x89,0xEF,0x7D,0x06,0xB5,0x06,0xA4,0xFD,0x5B,0xE9,0xC9]], @@ -65,9 +65,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function it('128 bit key', function (done) { for (let i = 0; i < testvectors128.length; i++) { const res = test_aes(testvectors128[i][1],testvectors128[i][0],testvectors128[i][2]); - expect(res, 'block ' + util.hexidump(testvectors128[i][1]) + - ' and key '+util.hexidump(testvectors128[i][0]) + - ' should be '+util.hexidump(testvectors128[i][2])).to.be.true; + expect(res, 'block ' + util.Uint8Array_to_hex(testvectors128[i][1]) + + ' and key '+util.Uint8Array_to_hex(testvectors128[i][0]) + + ' should be '+util.Uint8Array_to_hex(testvectors128[i][2])).to.be.true; } done(); }); @@ -75,9 +75,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function it('192 bit key', function (done) { for (let i = 0; i < testvectors192.length; i++) { const res = test_aes(testvectors192[i][1],testvectors192[i][0],testvectors192[i][2]); - expect(res, 'block ' + util.hexidump(testvectors192[i][1]) + - ' and key ' + util.hexidump(testvectors192[i][0])+ - ' should be ' + util.hexidump(testvectors192[i][2])).to.be.true; + expect(res, 'block ' + util.Uint8Array_to_hex(testvectors192[i][1]) + + ' and key ' + util.Uint8Array_to_hex(testvectors192[i][0])+ + ' should be ' + util.Uint8Array_to_hex(testvectors192[i][2])).to.be.true; } done(); }); @@ -85,9 +85,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function it('256 bit key', function (done) { for (let i = 0; i < testvectors256.length; i++) { const res = test_aes(testvectors256[i][1],testvectors256[i][0],testvectors256[i][2]); - expect(res, 'block ' + util.hexidump(testvectors256[i][1]) + - ' and key ' + util.hexidump(testvectors256[i][0]) + - ' should be ' + util.hexidump(testvectors256[i][2])).to.be.true; + expect(res, 'block ' + util.Uint8Array_to_hex(testvectors256[i][1]) + + ' and key ' + util.Uint8Array_to_hex(testvectors256[i][0]) + + ' should be ' + util.Uint8Array_to_hex(testvectors256[i][2])).to.be.true; } done(); }); diff --git a/test/crypto/cipher/blowfish.js b/test/crypto/cipher/blowfish.js index 384738f3..7a30f52d 100644 --- a/test/crypto/cipher/blowfish.js +++ b/test/crypto/cipher/blowfish.js @@ -8,10 +8,10 @@ const { expect } = chai; it('Blowfish cipher test with test vectors from https://www.schneier.com/code/vectors.txt', function(done) { function test_bf(input, key, output) { - const blowfish = new openpgp.crypto.cipher.blowfish(util.bin2str(key)); - const result = util.bin2str(blowfish.encrypt(input)); + const blowfish = new openpgp.crypto.cipher.blowfish(util.Uint8Array_to_str(key)); + const result = util.Uint8Array_to_str(blowfish.encrypt(input)); - return (util.hexstrdump(result) === util.hexstrdump(util.bin2str(output))); + return (util.str_to_hex(result) === util.str_to_hex(util.Uint8Array_to_str(output))); } const testvectors = [[[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x4E,0xF9,0x97,0x45,0x61,0x98,0xDD,0x78]], @@ -51,9 +51,9 @@ it('Blowfish cipher test with test vectors from https://www.schneier.com/code/ve for (let i = 0; i < testvectors.length; i++) { const res = test_bf(testvectors[i][1],testvectors[i][0],testvectors[i][2]); - expect(res, 'vector '+ i + '" with block ' + util.hexidump(testvectors[i][0])+ - ' and key ' + util.hexidump(testvectors[i][1]) + - ' should be ' + util.hexidump(testvectors[i][2]), false); + expect(res, 'vector '+ i + '" with block ' + util.Uint8Array_to_hex(testvectors[i][0])+ + ' and key ' + util.Uint8Array_to_hex(testvectors[i][1]) + + ' should be ' + util.Uint8Array_to_hex(testvectors[i][2]), false); } done(); }); diff --git a/test/crypto/cipher/cast5.js b/test/crypto/cipher/cast5.js index 4cbf82c4..37437903 100644 --- a/test/crypto/cipher/cast5.js +++ b/test/crypto/cipher/cast5.js @@ -8,18 +8,18 @@ const { expect } = chai; it('CAST-128 cipher test with test vectors from RFC2144', function (done) { function test_cast(input, key, output) { const cast5 = new openpgp.crypto.cipher.cast5(key); - const result = util.bin2str(cast5.encrypt(input)); + const result = util.Uint8Array_to_str(cast5.encrypt(input)); - return util.hexstrdump(result) === util.hexstrdump(util.bin2str(output)); + return util.str_to_hex(result) === util.str_to_hex(util.Uint8Array_to_str(output)); } const testvectors = [[[0x01,0x23,0x45,0x67,0x12,0x34,0x56,0x78,0x23,0x45,0x67,0x89,0x34,0x56,0x78,0x9A],[0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF],[0x23,0x8B,0x4F,0xE5,0x84,0x7E,0x44,0xB2]]]; for (let i = 0; i < testvectors.length; i++) { const res = test_cast(testvectors[i][1],testvectors[i][0],testvectors[i][2]); - expect(res, 'vector with block ' + util.hexidump(testvectors[i][0]) + - ' and key ' + util.hexidump(testvectors[i][1]) + - ' should be ' + util.hexidump(testvectors[i][2])).to.be.true; + expect(res, 'vector with block ' + util.Uint8Array_to_hex(testvectors[i][0]) + + ' and key ' + util.Uint8Array_to_hex(testvectors[i][1]) + + ' should be ' + util.Uint8Array_to_hex(testvectors[i][2])).to.be.true; } done(); }); diff --git a/test/crypto/cipher/des.js b/test/crypto/cipher/des.js index 5b418293..9d8c0b79 100644 --- a/test/crypto/cipher/des.js +++ b/test/crypto/cipher/des.js @@ -77,12 +77,12 @@ describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', fu for (let i = 0; i < testvectors.length; i++) { const des = new openpgp.crypto.cipher.tripledes(key); - const encr = util.bin2str(des.encrypt(testvectors[i][0], key)); + const encr = util.Uint8Array_to_str(des.encrypt(testvectors[i][0], key)); - expect(encr, 'vector with block ' + util.hexidump(testvectors[i][0]) + - ' and key ' + util.hexstrdump(util.Uint8Array2str(key)) + - ' should be ' + util.hexidump(testvectors[i][1]) + - ' != ' + util.hexidump(encr)).to.be.equal(util.bin2str(testvectors[i][1])); + expect(encr, 'vector with block ' + util.Uint8Array_to_hex(testvectors[i][0]) + + ' and key ' + util.str_to_hex(util.Uint8Array_to_str(key)) + + ' should be ' + util.Uint8Array_to_hex(testvectors[i][1]) + + ' != ' + util.Uint8Array_to_hex(encr)).to.be.equal(util.Uint8Array_to_str(testvectors[i][1])); } done(); }); @@ -124,18 +124,18 @@ describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', fu const encrypted = des.encrypt(thisVectorSet[i][0], padding); const decrypted = des.decrypt(encrypted, padding); - expect(util.bin2str(encrypted), 'vector with block [' + util.hexidump(thisVectorSet[i][0]) + - '] and key [' + util.hexstrdump(util.Uint8Array2str(key)) + + expect(util.Uint8Array_to_str(encrypted), 'vector with block [' + util.Uint8Array_to_hex(thisVectorSet[i][0]) + + '] and key [' + util.str_to_hex(util.Uint8Array_to_str(key)) + '] and padding [' + padding + - '] should be ' + util.hexidump(thisVectorSet[i][1]) + - ' - Actually [' + util.hexidump(encrypted) + - ']').to.equal(util.bin2str(thisVectorSet[i][1])); - expect(util.bin2str(decrypted), 'vector with block [' + util.hexidump(thisVectorSet[i][0]) + - '] and key [' + util.hexstrdump(util.Uint8Array2str(key)) + + '] should be ' + util.Uint8Array_to_hex(thisVectorSet[i][1]) + + ' - Actually [' + util.Uint8Array_to_hex(encrypted) + + ']').to.equal(util.Uint8Array_to_str(thisVectorSet[i][1])); + expect(util.Uint8Array_to_str(decrypted), 'vector with block [' + util.Uint8Array_to_hex(thisVectorSet[i][0]) + + '] and key [' + util.str_to_hex(util.Uint8Array_to_str(key)) + '] and padding [' + padding + - '] should be ' + util.hexidump(thisVectorSet[i][0]) + - ' - Actually [' + util.hexidump(decrypted) + - ']').to.equal(util.bin2str(thisVectorSet[i][0])); + '] should be ' + util.Uint8Array_to_hex(thisVectorSet[i][0]) + + ' - Actually [' + util.Uint8Array_to_hex(decrypted) + + ']').to.equal(util.Uint8Array_to_str(thisVectorSet[i][0])); } } done(); diff --git a/test/crypto/cipher/twofish.js b/test/crypto/cipher/twofish.js index 157e64cf..2f1f2e54 100644 --- a/test/crypto/cipher/twofish.js +++ b/test/crypto/cipher/twofish.js @@ -7,7 +7,7 @@ const { expect } = chai; it('Twofish with test vectors from https://www.schneier.com/code/ecb_ival.txt', function(done) { function TFencrypt(block, key) { - const tf = new openpgp.crypto.cipher.twofish(util.str2Uint8Array(key)); + const tf = new openpgp.crypto.cipher.twofish(util.str_to_Uint8Array(key)); return tf.encrypt(block); } @@ -36,36 +36,36 @@ it('Twofish with test vectors from https://www.schneier.com/code/ecb_ival.txt', if (i === 0) { blk = start_short; - key = util.bin2str(start); + key = util.Uint8Array_to_str(start); ct = testvectors[0]; - res = util.bin2str(TFencrypt(blk,key)); - exp = util.bin2str(ct); + res = util.Uint8Array_to_str(TFencrypt(blk,key)); + exp = util.Uint8Array_to_str(ct); } else if (i === 1) { blk = testvectors[0]; - key = util.bin2str(start); + key = util.Uint8Array_to_str(start); ct = testvectors[1]; - res = util.bin2str(TFencrypt(blk,key)); - exp = util.bin2str(ct); + res = util.Uint8Array_to_str(TFencrypt(blk,key)); + exp = util.Uint8Array_to_str(ct); } else if (i === 2) { blk = testvectors[i-1]; - key = util.bin2str(testvectors[i-2].concat(start_short)); + key = util.Uint8Array_to_str(testvectors[i-2].concat(start_short)); ct = testvectors[i]; - res = util.bin2str(TFencrypt(blk,key)); - exp = util.bin2str(ct); + res = util.Uint8Array_to_str(TFencrypt(blk,key)); + exp = util.Uint8Array_to_str(ct); } else if (i < 10 || i > 46) { blk = testvectors[i-1]; - key = util.bin2str(testvectors[i-2].concat(testvectors[i-3])); + key = util.Uint8Array_to_str(testvectors[i-2].concat(testvectors[i-3])); ct = testvectors[i]; - res = util.bin2str(TFencrypt(blk,key)); - exp = util.bin2str(ct); + res = util.Uint8Array_to_str(TFencrypt(blk,key)); + exp = util.Uint8Array_to_str(ct); } else { - testvectors[i] = TFencrypt(testvectors[i-1],util.bin2str(testvectors[i-2].concat(testvectors[i-3]))); + testvectors[i] = TFencrypt(testvectors[i-1],util.Uint8Array_to_str(testvectors[i-2].concat(testvectors[i-3]))); continue; } - expect(res, 'vector with block ' + util.hexidump(blk) + - ' with key ' + util.hexstrdump(key) + - ' should be ' + util.hexidump(ct) + - ' but is ' + util.hexidump(TFencrypt(blk,key))).to.equal(exp); + expect(res, 'vector with block ' + util.Uint8Array_to_hex(blk) + + ' with key ' + util.str_to_hex(key) + + ' should be ' + util.Uint8Array_to_hex(ct) + + ' but is ' + util.Uint8Array_to_hex(TFencrypt(blk,key))).to.equal(exp); } done(); }); diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 69556544..0a59504d 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -1,5 +1,4 @@ const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -const asmCrypto = require('asmcrypto-lite'); const chai = require('chai'); chai.use(require('chai-as-promised')); @@ -8,6 +7,7 @@ const expect = chai.expect; describe('API functional testing', function() { const util = openpgp.util; + const crypto = openpgp.crypto; const RSApubMPIstrs = [ new Uint8Array([0x08,0x00,0xac,0x15,0xb3,0xd6,0xd2,0x0f,0xf0,0x7a,0xdd,0x21,0xb7, 0xbf,0x61,0xfa,0xca,0x93,0x86,0xc8,0x55,0x5a,0x4b,0xa6,0xa4,0x1a, @@ -231,18 +231,19 @@ describe('API functional testing', function() { ElgamalpubMPIs[i].read(ElgamalpubMPIstrs[i]); } - const data = util.str2Uint8Array("foobar"); + const data = util.str_to_Uint8Array("foobar"); describe('Sign and verify', function () { it('RSA', function () { + // FIXME //Originally we passed public and secret MPI separately, now they are joined. Is this what we want to do long term? // RSA - return openpgp.crypto.signature.sign( - 2, 1, RSApubMPIs.concat(RSAsecMPIs), data + return crypto.signature.sign( + 1, 2, RSApubMPIs.concat(RSAsecMPIs), data ).then(RSAsignedData => { const RSAsignedDataMPI = new openpgp.MPI(); RSAsignedDataMPI.read(RSAsignedData); - return openpgp.crypto.signature.verify( + return crypto.signature.verify( 1, 2, [RSAsignedDataMPI], RSApubMPIs, data ).then(success => { return expect(success).to.be.true; @@ -252,16 +253,16 @@ describe('API functional testing', function() { it('DSA', function () { // DSA - return openpgp.crypto.signature.sign( - 2, 17, DSApubMPIs.concat(DSAsecMPIs), data + return crypto.signature.sign( + 17, 2, DSApubMPIs.concat(DSAsecMPIs), data ).then(DSAsignedData => { - DSAsignedData = util.Uint8Array2str(DSAsignedData); + DSAsignedData = util.Uint8Array_to_str(DSAsignedData); const 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( + return crypto.signature.verify( 17, 2, DSAmsgMPIs, DSApubMPIs, data ).then(success => { return expect(success).to.be.true; @@ -278,9 +279,9 @@ describe('API functional testing', function() { function testCFB(plaintext, resync) { symmAlgos.forEach(function(algo) { - const symmKey = openpgp.crypto.generateSessionKey(algo); - const symmencData = openpgp.crypto.cfb.encrypt(openpgp.crypto.getPrefixRandom(algo), algo, util.str2Uint8Array(plaintext), symmKey, resync); - const text = util.Uint8Array2str(openpgp.crypto.cfb.decrypt(algo, symmKey, symmencData, resync)); + const symmKey = crypto.generateSessionKey(algo); + const symmencData = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, util.str_to_Uint8Array(plaintext), symmKey, resync); + const text = util.Uint8Array_to_str(crypto.cfb.decrypt(algo, symmKey, symmencData, resync)); expect(text).to.equal(plaintext); }); } @@ -288,19 +289,16 @@ describe('API functional testing', function() { function testAESCFB(plaintext) { symmAlgos.forEach(function(algo) { if(algo.substr(0,3) === 'aes') { - const symmKey = openpgp.crypto.generateSessionKey(algo); - const rndm = openpgp.crypto.getPrefixRandom(algo); + const symmKey = crypto.generateSessionKey(algo); + const rndm = crypto.getPrefixRandom(algo); + const repeat = new Uint8Array([rndm[rndm.length - 2], rndm[rndm.length - 1]]); const prefix = util.concatUint8Array([rndm, repeat]); - const symmencData = openpgp.crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false); - const symmencData2 = asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey); + const symmencData = crypto.cfb.encrypt(rndm, algo, util.str_to_Uint8Array(plaintext), symmKey, false); + const decrypted = crypto.cfb.decrypt(algo, symmKey, symmencData, false); - let decrypted = asmCrypto.AES_CFB.decrypt(symmencData, symmKey); - decrypted = decrypted.subarray(openpgp.crypto.cipher[algo].blockSize + 2, decrypted.length); - expect(util.Uint8Array2str(symmencData)).to.equal(util.Uint8Array2str(symmencData2)); - - const text = util.Uint8Array2str(decrypted); + const text = util.Uint8Array_to_str(decrypted); expect(text).to.equal(plaintext); } }); @@ -309,16 +307,17 @@ describe('API functional testing', function() { function testAESGCM(plaintext) { symmAlgos.forEach(function(algo) { if(algo.substr(0,3) === 'aes') { - it(algo, function(done) { - const key = openpgp.crypto.generateSessionKey(algo); - const iv = openpgp.crypto.random.getRandomValues(new Uint8Array(openpgp.crypto.gcm.ivLength)); + it(algo, function() { + const key = crypto.generateSessionKey(algo); + const iv = crypto.random.getRandomValues(new Uint8Array(crypto.gcm.ivLength)); - openpgp.crypto.gcm.encrypt(algo, util.str2Uint8Array(plaintext), key, iv).then(function(ciphertext) { - return openpgp.crypto.gcm.decrypt(algo, ciphertext, key, iv); + return crypto.gcm.encrypt( + algo, util.str_to_Uint8Array(plaintext), key, iv + ).then(function(ciphertext) { + return crypto.gcm.decrypt(algo, ciphertext, key, iv); }).then(function(decrypted) { - const decryptedStr = util.Uint8Array2str(decrypted); + const decryptedStr = util.Uint8Array_to_str(decrypted); expect(decryptedStr).to.equal(plaintext); - done(); }); }); } @@ -372,40 +371,43 @@ describe('API functional testing', function() { testAESGCM("12345678901234567890123456789012345678901234567890"); }); - it('Asymmetric using RSA with eme_pkcs1 padding', function (done) { - const symmKey = util.Uint8Array2str(openpgp.crypto.generateSessionKey('aes256')); - const RSAUnencryptedData = new openpgp.MPI(); - RSAUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); - openpgp.crypto.publicKeyEncrypt( - "rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData + it('Asymmetric using RSA with eme_pkcs1 padding', function () { + const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256')); + const RSAUnencryptedData = crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength()) + const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData); + return crypto.publicKeyEncrypt( + 1, RSApubMPIs, RSAUnencryptedMPI ).then(RSAEncryptedData => { - openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).then(data => { + return crypto.publicKeyDecrypt( + 1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData + ).then(data => { data = data.write(); - data = util.Uint8Array2str(data.subarray(2, data.length)); + data = util.Uint8Array_to_str(data.subarray(2, data.length)); - const result = openpgp.crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); + const result = crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); expect(result).to.equal(symmKey); - done(); }); }); }); - it('Asymmetric using Elgamal with eme_pkcs1 padding', function (done) { - const symmKey = util.Uint8Array2str(openpgp.crypto.generateSessionKey('aes256')); - const ElgamalUnencryptedData = new openpgp.MPI(); - ElgamalUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); - openpgp.crypto.publicKeyEncrypt( - "elgamal", ElgamalpubMPIs, ElgamalUnencryptedData + it('Asymmetric using Elgamal with eme_pkcs1 padding', function () { + const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256')); + const ElgamalUnencryptedData = crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength()); + const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData); + + return crypto.publicKeyEncrypt( + 16, ElgamalpubMPIs, ElgamalUnencryptedMPI ).then(ElgamalEncryptedData => { - const data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).then(data => { + return crypto.publicKeyDecrypt( + 16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData + ).then(data => { data = data.write(); - data = util.Uint8Array2str(data.subarray(2, data.length)); + data = util.Uint8Array_to_str(data.subarray(2, data.length)); - const result = openpgp.crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength()); + const result = 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 ef3b10d1..97f9a2fd 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -1,17 +1,11 @@ const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); +const BN = require('bn.js'); const chai = require('chai'); chai.use(require('chai-as-promised')); const expect = chai.expect; -const bin2bi = function (bytes) { - const mpi = new openpgp.MPI(); - bytes = openpgp.util.bin2str(bytes); - mpi.fromBytes(bytes); - return mpi.toBigInteger(); -}; - describe('Elliptic Curve Cryptography', function () { const elliptic_curves = openpgp.crypto.publicKey.elliptic; const key_data = { @@ -143,21 +137,21 @@ describe('Elliptic Curve Cryptography', function () { it('Creating curve with name', function (done) { const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519']; names.forEach(function (name) { - expect(elliptic_curves.get(name)).to.exist; + expect(new elliptic_curves.Curve(name)).to.exist; }); done(); }); it('Creating curve from oid', function (done) { const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A']; oids.forEach(function (oid) { - expect(elliptic_curves.get(openpgp.util.hex2bin(oid))).to.exist; + expect(new elliptic_curves.Curve(openpgp.util.hex_to_str(oid))).to.exist; }); done(); }); it('Creating KeyPair', function () { const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519']; return Promise.all(names.map(function (name) { - const curve = elliptic_curves.get(name); + const curve = new elliptic_curves.Curve(name); return curve.genKeyPair().then(keyPair => { expect(keyPair).to.exist; }); @@ -166,30 +160,30 @@ describe('Elliptic Curve Cryptography', function () { it('Creating KeyPair from data', function (done) { for (const name in key_data) { const pair = key_data[name]; - const curve = elliptic_curves.get(name); + const curve = new elliptic_curves.Curve(name); expect(curve).to.exist; const keyPair = curve.keyFromPrivate(pair.priv); expect(keyPair).to.exist; const pub = keyPair.getPublic(); expect(pub).to.exist; - expect(openpgp.util.hexidump(pub)).to.equal(openpgp.util.hexidump(pair.pub)); + expect(openpgp.util.Uint8Array_to_hex(pub)).to.equal(openpgp.util.Uint8Array_to_hex(pair.pub)); } done(); }); it('Signature verification', function (done) { - const curve = elliptic_curves.get('p256'); + const curve = new elliptic_curves.Curve('p256'); const key = curve.keyFromPublic(signature_data.pub); expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.true; done(); }); it('Invalid signature', function (done) { - const curve = elliptic_curves.get('p256'); + const curve = new elliptic_curves.Curve('p256'); const key = curve.keyFromPublic(key_data.p256.pub); expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.false; done(); }); it('Signature generation', function () { - const curve = elliptic_curves.get('p256'); + const curve = new elliptic_curves.Curve('p256'); let key = curve.keyFromPrivate(key_data.p256.priv); return key.sign(signature_data.message, 8).then(signature => { key = curve.keyFromPublic(key_data.p256.pub); @@ -197,13 +191,13 @@ describe('Elliptic Curve Cryptography', function () { }); }); it('Shared secret generation', function (done) { - const curve = elliptic_curves.get('p256'); + const curve = new elliptic_curves.Curve('p256'); let key1 = curve.keyFromPrivate(key_data.p256.priv); let key2 = curve.keyFromPublic(signature_data.pub); - const shared1 = openpgp.util.hexidump(key1.derive(key2)); + const shared1 = openpgp.util.Uint8Array_to_hex(key1.derive(key2)); key1 = curve.keyFromPublic(key_data.p256.pub); key2 = curve.keyFromPrivate(signature_data.priv); - const shared2 = openpgp.util.hexidump(key2.derive(key1)); + const shared2 = openpgp.util.Uint8Array_to_hex(key2.derive(key1)); expect(shared1).to.equal(shared2); done(); }); @@ -211,7 +205,7 @@ describe('Elliptic Curve Cryptography', function () { describe('ECDSA signature', function () { const verify_signature = function (oid, hash, r, s, message, pub) { if (openpgp.util.isString(message)) { - message = openpgp.util.str2Uint8Array(message); + message = openpgp.util.str_to_Uint8Array(message); } else if (!openpgp.util.isUint8Array(message)) { message = new Uint8Array(message); } @@ -219,9 +213,9 @@ describe('Elliptic Curve Cryptography', function () { return ecdsa.verify( oid, hash, - {r: bin2bi(r), s: bin2bi(s)}, + {r: new BN(r), s: new BN(s)}, message, - bin2bi(pub) + new Uint8Array(pub) ); }; const secp256k1_dummy_value = new Uint8Array([ @@ -295,10 +289,10 @@ describe('Elliptic Curve Cryptography', function () { .to.eventually.be.true.notify(done); }); it('Sign and verify message', function () { - const curve = elliptic_curves.get('p521'); + const curve = new elliptic_curves.Curve('p521'); return curve.genKeyPair().then(keyPair => { - const keyPublic = bin2bi(keyPair.getPublic()); - const keyPrivate = bin2bi(keyPair.getPrivate()); + const keyPublic = new Uint8Array(keyPair.getPublic()); + const keyPrivate = new Uint8Array(keyPair.getPrivate()); const oid = curve.oid; const message = p384_message; return elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => { @@ -311,19 +305,19 @@ describe('Elliptic Curve Cryptography', function () { describe('ECDH key exchange', function () { const decrypt_message = function (oid, hash, cipher, priv, ephemeral, data, fingerprint) { if (openpgp.util.isString(data)) { - data = openpgp.util.str2Uint8Array(data); + data = openpgp.util.str_to_Uint8Array(data); } else { data = new Uint8Array(data); } return Promise.resolve().then(() => { - const ecdh = elliptic_curves.ecdh; - return ecdh.decrypt( - oid, + const curve = new elliptic_curves.Curve(oid); + return elliptic_curves.ecdh.decrypt( + curve.oid, cipher, hash, - bin2bi(ephemeral), + new Uint8Array(ephemeral), data, - bin2bi(priv), + new Uint8Array(priv), fingerprint ); }); diff --git a/test/crypto/hash/md5.js b/test/crypto/hash/md5.js index ecf01f2d..f7aa028a 100644 --- a/test/crypto/hash/md5.js +++ b/test/crypto/hash/md5.js @@ -7,11 +7,11 @@ const MD5 = openpgp.crypto.hash.md5; const { expect } = chai; it('MD5 with test vectors from RFC 1321', function(done) { - expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array(''))), 'MD5("") = d41d8cd98f00b204e9800998ecf8427e')).to.equal('d41d8cd98f00b204e9800998ecf8427e'); - expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('abc'))), 'MD5("a") = 0cc175b9c0f1b6a831c399e269772661')).to.equal('900150983cd24fb0d6963f7d28e17f72'); - expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('message digest'))), 'MD5("message digest") = f96b697d7cb7938d525a2f31aaf161d0')).to.equal('f96b697d7cb7938d525a2f31aaf161d0'); - expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('abcdefghijklmnopqrstuvwxyz'))), 'MD5("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b')).to.equal('c3fcd3d76192e4007dfb496cca67e13b'); - expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'))), 'MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f')).to.equal('d174ab98d277d9f5a5611c2c9f419d9f'); - expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('12345678901234567890123456789012345678901234567890123456789012345678901234567890'))), 'MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a')).to.equal('57edf4a22be3c955ac49da2e2107b67a'); + expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array(''))), 'MD5("") = d41d8cd98f00b204e9800998ecf8427e')).to.equal('d41d8cd98f00b204e9800998ecf8427e'); + expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('abc'))), 'MD5("a") = 0cc175b9c0f1b6a831c399e269772661')).to.equal('900150983cd24fb0d6963f7d28e17f72'); + expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('message digest'))), 'MD5("message digest") = f96b697d7cb7938d525a2f31aaf161d0')).to.equal('f96b697d7cb7938d525a2f31aaf161d0'); + expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('abcdefghijklmnopqrstuvwxyz'))), 'MD5("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b')).to.equal('c3fcd3d76192e4007dfb496cca67e13b'); + expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'))), 'MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f')).to.equal('d174ab98d277d9f5a5611c2c9f419d9f'); + expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('12345678901234567890123456789012345678901234567890123456789012345678901234567890'))), 'MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a')).to.equal('57edf4a22be3c955ac49da2e2107b67a'); done(); }); diff --git a/test/crypto/hash/ripemd.js b/test/crypto/hash/ripemd.js index 2cb94e00..7ed9c559 100644 --- a/test/crypto/hash/ripemd.js +++ b/test/crypto/hash/ripemd.js @@ -7,9 +7,9 @@ const RMDstring = openpgp.crypto.hash.ripemd; const { expect } = chai; it("RIPE-MD 160 bits with test vectors from https://homes.esat.kuleuven.be/~bosselae/ripemd160.html", function(done) { - expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array(''))), 'RMDstring("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31')).to.equal('9c1185a5c5e9fc54612808977ee8f548b2258d31'); - expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array('a'))), 'RMDstring("a") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe')).to.equal('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe'); - expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array('abc'))), 'RMDstring("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc')).to.equal('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc'); - expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array('message digest'))), 'RMDstring("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36')).to.equal('5d0689ef49d2fae572b881b123a85ffa21595f36'); + expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array(''))), 'RMDstring("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31')).to.equal('9c1185a5c5e9fc54612808977ee8f548b2258d31'); + expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array('a'))), 'RMDstring("a") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe')).to.equal('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe'); + expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array('abc'))), 'RMDstring("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc')).to.equal('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc'); + expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array('message digest'))), 'RMDstring("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36')).to.equal('5d0689ef49d2fae572b881b123a85ffa21595f36'); done(); }); diff --git a/test/crypto/hash/sha.js b/test/crypto/hash/sha.js index e782b822..5e1fb2c2 100644 --- a/test/crypto/hash/sha.js +++ b/test/crypto/hash/sha.js @@ -7,15 +7,15 @@ const { hash } = openpgp.crypto; const { expect } = chai; it('SHA* with test vectors from NIST FIPS 180-2', function(done) { - expect(util.hexstrdump(util.Uint8Array2str(hash.sha1(util.str2Uint8Array('abc'))), 'hash.sha1("abc") = a9993e364706816aba3e25717850c26c9cd0d89d')).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha1(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983e441c3bd26ebaae4aa1f95129e5e54670f1')).to.equal('84983e441c3bd26ebaae4aa1f95129e5e54670f1'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha224(util.str2Uint8Array('abc'))), 'hash.sha224("abc") = 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7')).to.equal('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha224(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525')).to.equal('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha256(util.str2Uint8Array('abc'))), 'hash.sha256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')).to.equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha256(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1')).to.equal('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha384(util.str2Uint8Array('abc'))), 'hash.sha384("abc") = cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')).to.equal('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha384(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b')).to.equal('3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha512(util.str2Uint8Array('abc'))), 'hash.sha512("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')).to.equal('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'); - expect(util.hexstrdump(util.Uint8Array2str(hash.sha512(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445')).to.equal('204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha1(util.str_to_Uint8Array('abc'))), 'hash.sha1("abc") = a9993e364706816aba3e25717850c26c9cd0d89d')).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha1(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983e441c3bd26ebaae4aa1f95129e5e54670f1')).to.equal('84983e441c3bd26ebaae4aa1f95129e5e54670f1'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha224(util.str_to_Uint8Array('abc'))), 'hash.sha224("abc") = 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7')).to.equal('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha224(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525')).to.equal('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha256(util.str_to_Uint8Array('abc'))), 'hash.sha256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')).to.equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha256(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1')).to.equal('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha384(util.str_to_Uint8Array('abc'))), 'hash.sha384("abc") = cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')).to.equal('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha384(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b')).to.equal('3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha512(util.str_to_Uint8Array('abc'))), 'hash.sha512("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')).to.equal('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f'); + expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha512(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445')).to.equal('204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445'); done(); }); diff --git a/test/general/ecc_nist.js b/test/general/ecc_nist.js index 2b75c685..38350161 100644 --- a/test/general/ecc_nist.js +++ b/test/general/ecc_nist.js @@ -1,3 +1,5 @@ +/* globals tryTests: true */ + const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); const chai = require('chai'); @@ -234,4 +236,75 @@ describe('Elliptic Curve Cryptography', function () { expect(key.publicKeyArmored).to.exist; }); }); + + function omnibus() { + it('Omnibus NIST P-256 Test', function () { + const options = { userIds: {name: "Hi", email: "hi@hel.lo"}, curve: "p256" }; + return openpgp.generateKey(options).then(function (firstKey) { + const hi = firstKey.key; + const pubHi = hi.toPublic(); + + const options = { userIds: { name: "Bye", email: "bye@good.bye" }, curve: "p256" }; + return openpgp.generateKey(options).then(function (secondKey) { + const bye = secondKey.key; + const pubBye = bye.toPublic(); + + return Promise.all([ + // Signing message + openpgp.sign( + { data: 'Hi, this is me, Hi!', privateKeys: hi } + ).then(signed => { + const msg = openpgp.cleartext.readArmored(signed.data); + // Verifying signed message + return Promise.all([ + openpgp.verify( + { message: msg, publicKeys: pubHi } + ).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: pubHi, + 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: [pubBye], + privateKeys: [hi] } + ).then(encrypted => { + const msg = openpgp.message.readArmored(encrypted.data); + // Decrypting and verifying + return openpgp.decrypt( + { message: msg, + privateKeys: bye, + publicKeys: [pubHi] } + ).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; + }); + }) + ]); + }); + }); + }); + } + + omnibus(); + + tryTests('ECC Worker Tests', omnibus, { + if: typeof window !== 'undefined' && window.Worker, + before: function() { + openpgp.initWorker({ path:'../dist/openpgp.worker.js' }); + }, + beforeEach: function() { + openpgp.config.use_native = true; + }, + after: function() { + openpgp.destroyWorker(); + } + }); + + // TODO find test vectors }); diff --git a/test/general/oid.js b/test/general/oid.js index f08c0d79..47b4ec73 100644 --- a/test/general/oid.js +++ b/test/general/oid.js @@ -14,7 +14,7 @@ describe('Oid tests', function() { 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)); + expect(oid.oid).to.equal(openpgp.util.Uint8Array_to_str(data)); }); }); it('Reading and writing', function() { @@ -25,12 +25,12 @@ describe('Oid tests', function() { 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))); + expect(oid.oid).to.equal(openpgp.util.Uint8Array_to_str(data.subarray(1))); const 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))); + expect(openpgp.util.Uint8Array_to_str(result.subarray(1))).to.equal(openpgp.util.Uint8Array_to_str(data.subarray(1))); }); }); }); diff --git a/test/general/packet.js b/test/general/packet.js index 35276a4f..8e924fff 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -171,16 +171,14 @@ describe("Packet", function() { }); it('Public key encrypted symmetric key packet', function() { - const rsa = new openpgp.crypto.publicKey.rsa(); + const rsa = openpgp.crypto.publicKey.rsa; const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { - let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; + let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; mpi = mpi.map(function(k) { - const mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + return new openpgp.MPI(k); }); const enc = new openpgp.packet.PublicKeyEncryptedSessionKey(); @@ -206,7 +204,7 @@ describe("Packet", function() { }); }); - it('Secret key packet (reading, unencrypted)', function(done) { + it('Secret key packet (reading, unencrypted)', function() { const armored_key = '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + @@ -241,17 +239,14 @@ describe("Packet", function() { enc.sessionKeyAlgorithm = 'aes256'; enc.publicKeyId.bytes = '12345678'; - enc.encrypt(key).then(() => { - - enc.decrypt(key).then(() => { - + return enc.encrypt(key).then(() => { + return enc.decrypt(key).then(() => { expect(stringify(enc.sessionKey)).to.equal(stringify(secret)); - done(); }); }); }); - it('Public key encrypted packet (reading, GPG)', function(done) { + it('Public key encrypted packet (reading, GPG)', function() { const armored_key = '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + @@ -306,17 +301,16 @@ describe("Packet", function() { const msg = new openpgp.packet.List(); msg.read(openpgp.armor.decode(armored_msg).data); - msg[0].decrypt(key).then(() => { - msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); + return msg[0].decrypt(key).then(() => { + return msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); const text = stringify(msg[1].packets[0].packets[0].data); expect(text).to.equal('Hello world!'); - done(); }); }); - it('Sym encrypted session key reading/writing', function(done) { + it('Sym. encrypted session key reading/writing', function(done) { const passphrase = 'hello'; const algo = 'aes256'; @@ -348,7 +342,7 @@ describe("Packet", function() { done(); }); - it('Secret key encryption/decryption test', function(done) { + it('Secret key encryption/decryption test', function() { const armored_msg = '-----BEGIN PGP MESSAGE-----\n' + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + @@ -369,13 +363,12 @@ describe("Packet", function() { const msg = new openpgp.packet.List(); msg.read(openpgp.armor.decode(armored_msg).data); - msg[0].decrypt(key).then(() => { - msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); + return msg[0].decrypt(key).then(() => { + return msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey); const text = stringify(msg[1].packets[0].packets[0].data); expect(text).to.equal('Hello world!'); - done(); }); }); @@ -435,15 +428,13 @@ describe("Packet", function() { const key = new openpgp.packet.List(); key.push(new openpgp.packet.SecretKey()); - const rsa = new openpgp.crypto.publicKey.rsa(); + const rsa = openpgp.crypto.publicKey.rsa; const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { - let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; + let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; mpi = mpi.map(function(k) { - const mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + return new openpgp.MPI(k); }); key[0].params = mpi; @@ -463,15 +454,13 @@ describe("Packet", function() { it('Writing and verification of a signature packet.', function() { const key = new openpgp.packet.SecretKey(); - const rsa = new openpgp.crypto.publicKey.rsa(); + const rsa = openpgp.crypto.publicKey.rsa; const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys return rsa.generate(keySize, "10001").then(function(mpiGen) { - let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; + let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u]; mpi = mpi.map(function(k) { - const mpi = new openpgp.MPI(); - mpi.fromBigInteger(k); - return mpi; + return new openpgp.MPI(k); }); key.params = mpi; diff --git a/test/general/signature.js b/test/general/signature.js index d61e3023..3e1adb87 100644 --- a/test/general/signature.js +++ b/test/general/signature.js @@ -603,7 +603,7 @@ describe("Signature", function() { }); it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', function() { - const plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말'); + const plaintext = openpgp.util.str_to_Uint8Array('short message\nnext line\n한국어/조선말'); const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; privKey.primaryKey.decrypt('hello world'); @@ -623,7 +623,7 @@ describe("Signature", function() { }); it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', function() { - const plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말'); + const plaintext = openpgp.util.str_to_Uint8Array('short message\nnext line\n한국어/조선말'); const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0]; const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0]; privKey.primaryKey.decrypt('hello world'); diff --git a/test/general/x25519.js b/test/general/x25519.js index 53f4341f..9233bb4f 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -147,6 +147,8 @@ describe('X25519 Cryptography', function () { done(); }); + // This test is slow because the keys are generated by GPG2, which + // by default chooses a larger number for S2K iterations than we do. it('Load private key', function (done) { load_priv_key('light'); load_priv_key('night'); @@ -217,131 +219,148 @@ describe('X25519 Cryptography', function () { }); }); - // TODO generate, export, then reimport key and validate - it('Omnibus Ed25519/Curve25519 Test', function () { - const 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; - - const hi = firstKey.key; - const primaryKey = hi.primaryKey; - const 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 - const user = hi.users[0]; - expect(user.selfCertifications[0].verify( - primaryKey, { userid: user.userId, key: primaryKey } - )).to.eventually.be.true; - expect(user.verifyCertificate( - primaryKey, user.selfCertifications[0], [hi.toPublic()] - )).to.eventually.equal(openpgp.enums.keyStatus.valid); - + // TODO export, then reimport key and validate + function omnibus() { + it('Omnibus Ed25519/Curve25519 Test', function () { const options = { - userIds: { name: "Bye", email: "bye@good.bye" }, - curve: "curve25519" + userIds: {name: "Hi", email: "hi@hel.lo"}, + curve: "ed25519" }; - return openpgp.generateKey(options).then(function (secondKey) { - const 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'); + 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; + + const hi = firstKey.key; + const primaryKey = hi.primaryKey; + const subKey = hi.subKeys[0].subKey; + expect(primaryKey.params[0].getName()).to.equal("ed25519"); + expect(primaryKey.algorithm).to.equal('eddsa'); + expect(subKey.params[0].getName()).to.equal('curve25519'); + expect(subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid - const user = bye.users[0]; + const user = hi.users[0]; expect(user.selfCertifications[0].verify( - bye.primaryKey, { userid: user.userId, key: bye.primaryKey } + primaryKey, { userid: user.userId, key: primaryKey } )).to.eventually.be.true; expect(user.verifyCertificate( - bye.primaryKey, user.selfCertifications[0], [bye.toPublic()] + primaryKey, user.selfCertifications[0], [hi.toPublic()] )).to.eventually.equal(openpgp.enums.keyStatus.valid); - 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 => { - const 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 => { - const msg = openpgp.message.readArmored(encrypted.data); - // Decrypting and verifying - return openpgp.decrypt( - { message: msg, - privateKeys: 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; - }); - }) - ]); + const options = { + userIds: { name: "Bye", email: "bye@good.bye" }, + curve: "curve25519" + }; + return openpgp.generateKey(options).then(function (secondKey) { + const bye = secondKey.key; + expect(bye.primaryKey.params[0].getName()).to.equal('ed25519'); + expect(bye.primaryKey.algorithm).to.equal('eddsa'); + expect(bye.subKeys[0].subKey.params[0].getName()).to.equal('curve25519'); + expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh'); + + // Self Certificate is valid + const user = bye.users[0]; + expect(user.selfCertifications[0].verify( + bye.primaryKey, { userid: user.userId, key: bye.primaryKey } + )).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! + 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 => { + const 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 => { + const msg = openpgp.message.readArmored(encrypted.data); + // Decrypting and verifying + return openpgp.decrypt( + { message: msg, + privateKeys: 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; + }); + }) + ]); + }); }); }); + } + + omnibus(); + + tryTests('X25519 Worker Tests', omnibus, { + if: typeof window !== 'undefined' && window.Worker, + before: function() { + openpgp.initWorker({ path:'../dist/openpgp.worker.js' }); + }, + beforeEach: function() { + openpgp.config.use_native = true; + }, + after: function() { + openpgp.destroyWorker(); + } }); 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 signature = openpgp.crypto.signature; + const curve = new elliptic.Curve('ed25519'); const util = openpgp.util; function testVector(vector) { const S = curve.keyFromSecret(vector.SECRET_KEY); - const P = curve.keyFromPublic(vector.PUBLIC_KEY); + const P = curve.keyFromPublic('40'+vector.PUBLIC_KEY); expect(S.getPublic()).to.deep.equal(P.getPublic()); - const data = util.str2Uint8Array(vector.MESSAGE); + const data = util.str_to_Uint8Array(vector.MESSAGE); const keyIntegers = [ openpgp.OID.fromClone(curve), - new openpgp.MPI(util.hex2bin(vector.PUBLIC_KEY)), - new openpgp.MPI(util.hex2bin(vector.SECRET_KEY)) + new openpgp.MPI(util.hex_to_str('40'+vector.PUBLIC_KEY)), + new openpgp.MPI(util.hex_to_str(vector.SECRET_KEY)) ]; const msg_MPIs = [ - new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(vector.SIGNATURE.R).reverse())), - new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(vector.SIGNATURE.S).reverse())) + new openpgp.MPI(util.Uint8Array_to_str(util.hex_to_Uint8Array(vector.SIGNATURE.R).reverse())), + new openpgp.MPI(util.Uint8Array_to_str(util.hex_to_Uint8Array(vector.SIGNATURE.S).reverse())) ]; return Promise.all([ - crypto.sign(undefined, 22, keyIntegers, data).then(signature => { - const 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)); + signature.sign(22, undefined, keyIntegers, data).then(signed => { + const len = ((signed[0] << 8| signed[1]) + 7) / 8; + expect(util.hex_to_Uint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len)); + expect(util.hex_to_Uint8Array(vector.SIGNATURE.S)).to.deep.eq(signed.slice(4 + len)); }), - crypto.verify(22, undefined, msg_MPIs, keyIntegers, data).then(result => { + signature.verify(22, undefined, msg_MPIs, keyIntegers, data).then(result => { expect(result).to.be.true; }) ]); @@ -372,7 +391,7 @@ describe('X25519 Cryptography', function () { PUBLIC_KEY: ['3d4017c3e843895a92b70aa74d1b7ebc', '9c982ccf2ec4968cc0cd55f12af4660c'].join(''), - MESSAGE: util.hex2bin('72'), + MESSAGE: util.hex_to_str('72'), SIGNATURE: { R: ['92a009a9f0d4cab8720e820b5f642540', 'a2b27b5416503f8fb3762223ebdb69da'].join(''), @@ -389,7 +408,7 @@ describe('X25519 Cryptography', function () { PUBLIC_KEY: ['fc51cd8e6218a1a38da47ed00230f058', '0816ed13ba3303ac5deb911548908025'].join(''), - MESSAGE: util.hex2bin('af82'), + MESSAGE: util.hex_to_str('af82'), SIGNATURE: { R: ['6291d657deec24024827e69c3abe01a3', '0ce548a284743a445e3680d7db5ac3ac'].join(''), @@ -406,7 +425,7 @@ describe('X25519 Cryptography', function () { PUBLIC_KEY: ['278117fc144c72340f67d0f2316e8386', 'ceffbf2b2428c9c51fef7c597f1d426e'].join(''), - MESSAGE: util.hex2bin([ + MESSAGE: util.hex_to_str([ '08b8b2b733424243760fe426a4b54908', '632110a66c2f6591eabd3345e3e4eb98', 'fa6e264bf09efe12ee50f8f54e9f77b1', @@ -488,7 +507,7 @@ describe('X25519 Cryptography', function () { PUBLIC_KEY: ['ec172b93ad5e563bf4932c70e1245034', 'c35467ef2efd4d64ebf819683467e2bf'].join(''), - MESSAGE: util.hex2bin([ + MESSAGE: util.hex_to_str([ 'ddaf35a193617abacc417349ae204131', '12e6fa4e89a97ea20a9eeee64b55d39a', '2192992a274fc1a836ba3c23a3feebbd',