From ed4cef102a25062059e68e5d0c78e98bc4571b9a Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 9 Feb 2018 01:06:44 -0800 Subject: [PATCH 01/31] This branch uses the current version of asmcrypto.js --- Gruntfile.js | 2 +- package.json | 9 ++++----- src/crypto/cipher/aes.js | 7 ++++--- src/crypto/gcm.js | 13 +++++++++---- src/crypto/hash/index.js | 10 ++++++---- src/packet/sym_encrypted_integrity_protected.js | 13 +++++++------ 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index f8f210df..091ef0a6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -263,7 +263,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..aca34417 100644 --- a/package.json +++ b/package.json @@ -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": "github:mahrud/asmcrypto.js", "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/cipher/aes.js b/src/crypto/cipher/aes.js index f99cabf6..a1f0423b 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'; // 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/gcm.js b/src/crypto/gcm.js index 6cb04d2f..ed4b1222 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'; +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..8fca5645 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'; 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(); @@ -54,7 +56,7 @@ if (nodeCrypto) { // Use Node native crypto for all hash functions /** @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/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index dc43541e..763384c4 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -25,16 +25,17 @@ * 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'; 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(); @@ -145,7 +146,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 +154,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 } From aee8974ef513a700f358a588bffbe1b88bf19b9f Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 13 Feb 2018 00:38:35 -0800 Subject: [PATCH 02/31] RSA signatures now use asmcrypto.js; various fixes and tweaks --- src/crypto/crypto.js | 10 +++-- src/crypto/pkcs1.js | 12 +++--- src/crypto/public_key/rsa.js | 34 ++++++++--------- src/crypto/signature.js | 47 +++++++++++------------ src/packet/signature.js | 6 +-- src/util.js | 5 ++- test/crypto/crypto.js | 73 ++++++++++++++++++++---------------- test/general/x25519.js | 12 +++--- 8 files changed, 103 insertions(+), 96 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 4cee1f7e..33926c35 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -82,7 +82,9 @@ export default { 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); + const res = await ecdh.encrypt( + curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint + ); return constructParams(types, [res.V, res.C]); } default: @@ -261,7 +263,9 @@ export default { //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]); + return constructParams( + types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] + ); }); } case 'ecdsa': @@ -269,12 +273,10 @@ export default { return publicKey.elliptic.generate(curve).then(function (keyObject) { return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]); }); - case 'ecdh': return publicKey.elliptic.generate(curve).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.'); } diff --git a/src/crypto/pkcs1.js b/src/crypto/pkcs1.js index 1acb15d4..f1c194e6 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,6 +95,7 @@ export default { * @return {String} message, an octet string */ decode: function(EM) { + // FIXME // leading zeros truncated by jsbn if (EM.charCodeAt(0) !== 0) { EM = String.fromCharCode(0) + EM; @@ -158,7 +156,7 @@ export default { PS + String.fromCharCode(0x00) + T; - return new BigInteger(util.hexstrdump(EM), 16); + return util.hexstrdump(EM); } } }; diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 834220e3..c0fc860b 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -18,16 +18,20 @@ // RSA implementation /** + * @requires asmcrypto.js * @requires crypto/public_key/jsbn * @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 { RSA_RAW } from 'asmcrypto.js'; +import BigInteger from './jsbn'; +import random from '../random'; import config from '../../config'; +import util from '../../util'; function SecureRandom() { function nextBytes(byteArray) { @@ -58,20 +62,13 @@ function unblind(t, 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 + * @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 */ function decrypt(m, n, e, d, p, q, u) { @@ -91,6 +88,7 @@ export default function RSA() { t = t.multiply(u).mod(q); } t = t.multiply(p).add(xp); + if (config.rsa_blinding) { t = unblind(t, n); } @@ -111,10 +109,12 @@ export default function RSA() { /* Sign and Verify */ function sign(m, d, n) { return m.modPow(d, n); +// return RSA_RAW.sign(m, [n, e, d]); } function verify(x, e, n) { return x.modPowInt(e, n); +// return RSA_RAW.verify(x, [n, e]); } // "empty" RSA key constructor diff --git a/src/crypto/signature.js b/src/crypto/signature.js index be045470..96a9061c 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,13 +1,15 @@ /** - * @requires util - * @requires crypto/hash - * @requires crypto/pkcs1 + * @requires asmcrypto.js * @requires crypto/public_key + * @requires crypto/pkcs1 + * @requires util * @module crypto/signature */ -import util from '../util'; + +import { RSA_RAW } from 'asmcrypto.js' import publicKey from './public_key'; -import pkcs1 from './pkcs1.js'; +import pkcs1 from './pkcs1'; +import util from '../util'; export default { /** @@ -35,14 +37,13 @@ export default { // RSA Encrypt-Only [HAC] case 3: { // RSA Sign-Only [HAC] - const rsa = new publicKey.rsa(); - const n = publickey_MPIs[0].toBigInteger(); + const n = util.str2Uint8Array(publickey_MPIs[0].toBytes()); 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 e = util.str2Uint8Array(publickey_MPIs[1].toBytes()); + m = msg_MPIs[0].write().slice(2); // FIXME + const EM = RSA_RAW.verify(m, [n, e]); const EM2 = pkcs1.emsa.encode(hash_algo, data, k); - return EM.compareTo(EM2) === 0; + return util.hexidump(EM) === EM2; } case 16: { // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] @@ -88,13 +89,14 @@ 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 {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 {Array} keyIntegers Public followed by Private key multiprecision algorithm-specific parameters * @param {Uint8Array} data Data to be signed * @return {Array} */ - sign: async function(hash_algo, algo, keyIntegers, data) { + sign: async function(algo, hash_algo, keyIntegers, data) { + data = util.Uint8Array2str(data); let m; @@ -109,15 +111,14 @@ export default { // 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() + const n = util.str2Uint8Array(keyIntegers[0].toBytes()); + const k = keyIntegers[0].byteLength(); + const e = util.str2Uint8Array(keyIntegers[1].toBytes()); + d = util.str2Uint8Array(keyIntegers[2].toBytes()); + m = util.hex2Uint8Array( + '00'+pkcs1.emsa.encode(hash_algo, data, k) // FIXME ); - return util.str2Uint8Array(rsa.sign(m, d, n).toMPI()); - } + return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d])); case 17: { // DSA (Digital Signature Algorithm) [FIPS186] [HAC] const dsa = new publicKey.dsa(); @@ -150,10 +151,10 @@ export default { d = keyIntegers[2].toBigInteger(); m = data; signature = await eddsa.sign(curve.oid, hash_algo, m, d); - return new Uint8Array([].concat( + return util.concatUint8Array([ util.Uint8Array2MPI(signature.R.toArrayLike(Uint8Array, 'le', 32)), util.Uint8Array2MPI(signature.S.toArrayLike(Uint8Array, 'le', 32)) - )); + ]); } default: throw new Error('Invalid signature algorithm.'); diff --git a/src/packet/signature.js b/src/packet/signature.js index d1fa0f5b..015672f0 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -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 ); }; @@ -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/util.js b/src/util.js index 12fc4e69..8af4d619 100644 --- a/src/util.js +++ b/src/util.js @@ -326,11 +326,12 @@ export default { * 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 + * @return {Uint8Array} MPI-formatted Uint8Array */ Uint8Array2MPI: function (bin) { const size = (bin.length - 1) * 8 + this.nbits(bin[0]); - return [(size & 0xFF00) >> 8, size & 0xFF].concat(Array.from(bin)); + const prefix = Uint8Array.from([(size & 0xFF00) >> 8, size & 0xFF]); + return this.concatUint8Array([prefix, bin]); }, /** diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 69556544..5be914fe 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -8,6 +8,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, @@ -235,14 +236,15 @@ describe('API functional testing', function() { 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,8 +254,8 @@ 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); const DSAmsgMPIs = []; @@ -261,7 +263,7 @@ describe('API functional testing', function() { 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 +280,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.str2Uint8Array(plaintext), symmKey, resync); + const text = util.Uint8Array2str(crypto.cfb.decrypt(algo, symmKey, symmencData, resync)); expect(text).to.equal(plaintext); }); } @@ -288,16 +290,17 @@ 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 symmencData = crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false); const symmencData2 = asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey); let decrypted = asmCrypto.AES_CFB.decrypt(symmencData, symmKey); - decrypted = decrypted.subarray(openpgp.crypto.cipher[algo].blockSize + 2, decrypted.length); + decrypted = decrypted.subarray(crypto.cipher[algo].blockSize + 2, decrypted.length); expect(util.Uint8Array2str(symmencData)).to.equal(util.Uint8Array2str(symmencData2)); const text = util.Uint8Array2str(decrypted); @@ -309,16 +312,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.str2Uint8Array(plaintext), key, iv + ).then(function(ciphertext) { + return crypto.gcm.decrypt(algo, ciphertext, key, iv); }).then(function(decrypted) { const decryptedStr = util.Uint8Array2str(decrypted); expect(decryptedStr).to.equal(plaintext); - done(); }); }); } @@ -372,40 +376,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')); + it('Asymmetric using RSA with eme_pkcs1 padding', function () { + const symmKey = util.Uint8Array2str(crypto.generateSessionKey('aes256')); const RSAUnencryptedData = new openpgp.MPI(); - RSAUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); - openpgp.crypto.publicKeyEncrypt( + RSAUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); + return crypto.publicKeyEncrypt( "rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData ).then(RSAEncryptedData => { - openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).then(data => { + return crypto.publicKeyDecrypt( + "rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData + ).then(data => { data = data.write(); data = util.Uint8Array2str(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')); + it('Asymmetric using Elgamal with eme_pkcs1 padding', function () { + const symmKey = util.Uint8Array2str(crypto.generateSessionKey('aes256')); const ElgamalUnencryptedData = new openpgp.MPI(); - ElgamalUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); - openpgp.crypto.publicKeyEncrypt( + ElgamalUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); + + return crypto.publicKeyEncrypt( "elgamal", ElgamalpubMPIs, ElgamalUnencryptedData ).then(ElgamalEncryptedData => { - const data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).then(data => { + return crypto.publicKeyDecrypt( + "elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData + ).then(data => { data = data.write(); data = util.Uint8Array2str(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/general/x25519.js b/test/general/x25519.js index 53f4341f..7ed5f337 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -318,7 +318,7 @@ describe('X25519 Cryptography', function () { describe('Ed25519 Test Vectors from RFC8032', function () { // https://tools.ietf.org/html/rfc8032#section-7.1 - const crypto = openpgp.crypto.signature; + const signature = openpgp.crypto.signature; const curve = openpgp.crypto.publicKey.elliptic.get('ed25519'); const util = openpgp.util; function testVector(vector) { @@ -336,12 +336,12 @@ describe('X25519 Cryptography', function () { new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(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.hex2Uint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len)); + expect(util.hex2Uint8Array(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; }) ]); From b126fd5be78bb2aa333dfcb68dca83b17ca50eec Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 13 Feb 2018 14:05:07 -0800 Subject: [PATCH 03/31] RSA encrypt/decrypt use asmcrypto as well TODO: RSA key generation, removing jsbn from dsa, elgamal, mpi, etc. --- src/crypto/crypto.js | 58 ++++++++++++++---------- src/crypto/public_key/elliptic/curves.js | 4 +- src/crypto/public_key/elliptic/ecdh.js | 4 +- src/crypto/public_key/elliptic/ecdsa.js | 6 ++- src/crypto/public_key/elliptic/key.js | 3 +- src/crypto/public_key/rsa.js | 2 + src/crypto/signature.js | 7 +-- 7 files changed, 51 insertions(+), 33 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 33926c35..6ab75b47 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -18,23 +18,28 @@ // The GPG4Browsers crypto interface /** - * @requires crypto/cipher + * @requires asmcrypto.js * @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 util * @module crypto/crypto */ -import random from './random.js'; -import cipher from './cipher'; +import { RSA_RAW, BigNumber, Modulus } from 'asmcrypto.js'; +import BigInteger from './public_key/jsbn'; 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 util from '../util'; function constructParams(types, data) { @@ -63,11 +68,12 @@ export default { 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)]); + const n = util.str2Uint8Array(publicParams[0].toBytes()); + const e = util.str2Uint8Array(publicParams[1].toBytes()); + m = data.write().slice(2); // FIXME + return constructParams(types, [ + new BigInteger(util.hexidump(RSA_RAW.encrypt(m, [n, e])), 16) // FIXME + ]); } case 'elgamal': { const elgamal = new publicKey.elgamal(); @@ -109,17 +115,23 @@ export default { 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); + const c = util.str2Uint8Array(dataIntegers[0].toBytes()); + const n = util.str2Uint8Array(keyIntegers[0].toBytes()); // pq + const e = util.str2Uint8Array(keyIntegers[1].toBytes()); + const d = util.str2Uint8Array(keyIntegers[2].toBytes()); // de = 1 mod (p-1)(q-1) + const p = util.str2Uint8Array(keyIntegers[3].toBytes()); + const q = util.str2Uint8Array(keyIntegers[4].toBytes()); + const u = util.str2Uint8Array(keyIntegers[5].toBytes()); // q^-1 mod p + const dd = BigNumber.fromArrayBuffer(d); + const dp = new Modulus( + BigNumber.fromArrayBuffer(p).subtract(BigNumber.ONE) + ).reduce(dd).toBytes(); // d mod (p-1) + const dq = new Modulus( + BigNumber.fromArrayBuffer(q).subtract(BigNumber.ONE) + ).reduce(dd).toBytes(); // d mod (q-1) + return new BigInteger( + util.hexidump(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)), 16 // FIXME + ); } case 'elgamal': { const elgamal = new publicKey.elgamal(); diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 1814f615..dd9cbcfe 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -191,8 +191,8 @@ async function generate(curve) { const keyPair = await curve.genKeyPair(); return { oid: curve.oid, - Q: new BigInteger(keyPair.getPublic()), - d: new BigInteger(keyPair.getPrivate()), + Q: new BigInteger(util.hexidump(keyPair.getPublic()), 16), + d: new BigInteger(util.hexidump(keyPair.getPrivate()), 16), hash: curve.hash, cipher: curve.cipher }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index fdc97336..54f45928 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -86,7 +86,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); const C = aes_kw.wrap(Z, m.toBytes()); return { - V: new BigInteger(v.getPublic()), + V: new BigInteger(util.hexidump(v.getPublic()), 16), C: C }; } @@ -112,7 +112,7 @@ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { d = curve.keyFromPrivate(d.toByteArray()); const S = d.derive(V); const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); - return new BigInteger(aes_kw.unwrap(Z, C)); + return new BigInteger(util.hexidump(aes_kw.unwrap(Z, C)), 16); } module.exports = { diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index b64d2c69..6bc0d703 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -18,12 +18,14 @@ // Implementation of ECDSA following RFC6637 for Openpgpjs /** + * @requires util * @requires crypto/hash * @requires crypto/public_key/jsbn * @requires crypto/public_key/elliptic/curves * @module crypto/public_key/elliptic/ecdsa */ +import util from '../../../util'; import hash from '../../hash'; import curves from './curves'; import BigInteger from '../jsbn'; @@ -41,8 +43,8 @@ async function sign(oid, hash_algo, m, d) { const key = curve.keyFromPrivate(d.toByteArray()); const signature = await key.sign(m, hash_algo); return { - r: new BigInteger(signature.r.toArray()), - s: new BigInteger(signature.s.toArray()) + r: new BigInteger(util.hexidump(signature.r.toArray()), 16), + s: new BigInteger(util.hexidump(signature.s.toArray()), 16) }; } diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index f2d42ba0..859e968b 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -226,7 +226,8 @@ 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 BigInteger(util.hexidump(r), 16), s: new BigInteger(util.hexidump(s), 16) }, 'der'); const key = jwkToPem( { "kty": "EC", diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index c0fc860b..635b00f6 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -89,6 +89,8 @@ export default function RSA() { } t = t.multiply(p).add(xp); +// var t = RSA.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1) + if (config.rsa_blinding) { t = unblind(t, n); } diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 96a9061c..cc5b6b7c 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -3,10 +3,10 @@ * @requires crypto/public_key * @requires crypto/pkcs1 * @requires util - * @module crypto/signature */ + * @module crypto/signature +*/ - -import { RSA_RAW } from 'asmcrypto.js' +import { RSA_RAW } from 'asmcrypto.js'; import publicKey from './public_key'; import pkcs1 from './pkcs1'; import util from '../util'; @@ -119,6 +119,7 @@ export default { '00'+pkcs1.emsa.encode(hash_algo, data, k) // FIXME ); return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d])); + } case 17: { // DSA (Digital Signature Algorithm) [FIPS186] [HAC] const dsa = new publicKey.dsa(); From 2f3c0a86e9a0aa6bea42938e1f375045f4ce3e8a Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 15 Feb 2018 16:57:28 -0800 Subject: [PATCH 04/31] DSA uses BN.js added toBN for type_mpi --- src/crypto/crypto.js | 1 - src/crypto/public_key/dsa.js | 149 +++++++++++++++++------------------ src/crypto/random.js | 33 ++++++-- src/crypto/signature.js | 40 +++++----- src/type/mpi.js | 6 ++ test/crypto/crypto.js | 2 +- 6 files changed, 128 insertions(+), 103 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 6ab75b47..47873961 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -41,7 +41,6 @@ import type_mpi from '../type/mpi'; import type_oid from '../type/oid'; import util from '../util'; - function constructParams(types, data) { return types.map(function(type, i) { if (data && data[i]) { diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 1fb64979..8d488e38 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -21,108 +21,103 @@ * @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 BN from 'bn.js'; +import hash from '../hash'; import random from '../random.js'; -import hashModule from '../hash'; -import util from '../../util.js'; 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; + // TODO benchmark BN.mont vs BN.red + 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); + // TODO rewrite getLeftNBits to work with Uint8Arrays + const h = new BN( + util.str2Uint8Array( + util.getLeftNBits( + util.Uint8Array2str(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; + k = random.getRandomBN(one, q); + 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.fromRed(), s: s.fromRed()}; + }, - 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.str2Uint8Array( + util.getLeftNBits( + util.Uint8Array2str(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/random.js b/src/crypto/random.js index 0c8f43c2..a36b145c 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'); @@ -107,9 +109,9 @@ export default { let randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes)); if (bits % 8 > 0) { - randomBits = String.fromCharCode(((2 ** (bits % 8)) - 1) & - randomBits.charCodeAt(0)) + - randomBits.substring(1); + randomBits = String.fromCharCode( + ((2 ** (bits % 8)) - 1) & randomBits.charCodeAt(0) + ) + randomBits.substring(1); } const mpi = new type_mpi(randomBits); return mpi.toBigInteger(); @@ -128,6 +130,27 @@ export default { return min.add(r); }, + /** + * Create a secure random MPI in specified range + * @param {module:type/mpi} min Lower bound, included + * @param {module:type/mpi} max Upper bound, excluded + * @return {module:BN} Random MPI + */ + getRandomBN: function(min, max) { + if (max.cmp(min) <= 0) { + throw new Error('Illegal parameter value: max <= min'); + } + + const modulus = max.sub(min); + const length = modulus.byteLength(); + let r = new BN(this.getRandomBytes(length)); + // Using a while loop is necessary to avoid bias + while (r.cmp(modulus) >= 0) { + r = new BN(this.getRandomBytes(length)); + } + return r.iadd(min); + }, + randomBuffer: new RandomBuffer() }; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index cc5b6b7c..0bbbbbd6 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -27,6 +27,7 @@ export default { let s; let Q; let curve; + let signature; data = util.Uint8Array2str(data); @@ -51,16 +52,15 @@ export default { } 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; + const dsa = publicKey.dsa; + r = msg_MPIs[0].toBN(); + s = msg_MPIs[1].toBN(); + const p = publickey_MPIs[0].toBN(); + const q = publickey_MPIs[1].toBN(); + const g = publickey_MPIs[2].toBN(); + const y = publickey_MPIs[3].toBN(); + m = util.str2Uint8Array(data); + return dsa.verify(hash_algo, r, s, m, p, q, g, y); } case 19: { // ECDSA @@ -122,15 +122,17 @@ export default { } 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()); + const dsa = publicKey.dsa; + const p = keyIntegers[0].toBN(); + const q = keyIntegers[1].toBN(); + const g = keyIntegers[2].toBN(); + const x = keyIntegers[4].toBN(); + m = util.str2Uint8Array(data); + signature = dsa.sign(hash_algo, m, g, p, q, x); + return util.concatUint8Array([ + util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), + util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) + ]); } case 16: { // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] diff --git a/src/type/mpi.js b/src/type/mpi.js index 08d7973d..466f6a5f 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -29,11 +29,13 @@ * 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 bn.js * @requires crypto/public_key/jsbn * @requires util * @module type/mpi */ +import BN from 'bn.js'; import BigInteger from '../crypto/public_key/jsbn'; import util from '../util'; @@ -110,6 +112,10 @@ MPI.prototype.toBigInteger = function () { return this.data.clone(); }; +MPI.prototype.toBN = function (bn) { + return new BN(this.write().slice(2)); +}; + MPI.prototype.fromBigInteger = function (bn) { this.data = bn.clone(); }; diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 5be914fe..9979d5a9 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -1,5 +1,5 @@ const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -const asmCrypto = require('asmcrypto-lite'); +const asmCrypto = require('asmcrypto.js'); const chai = require('chai'); chai.use(require('chai-as-promised')); From 9200f026f31734e1d82ffed29a1339a8ac347f1d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 15 Feb 2018 19:09:09 -0800 Subject: [PATCH 05/31] Starting to change MPI --- src/crypto/crypto.js | 31 ++++++++++++++----------------- src/crypto/signature.js | 16 +++++++--------- src/type/mpi.js | 9 +++++++-- 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 47873961..53085d08 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -18,6 +18,7 @@ // The GPG4Browsers crypto interface /** + * @requires bn.js * @requires asmcrypto.js * @requires crypto/public_key * @requires crypto/cipher @@ -30,8 +31,8 @@ * @module crypto/crypto */ +import BN from 'bn.js'; import { RSA_RAW, BigNumber, Modulus } from 'asmcrypto.js'; -import BigInteger from './public_key/jsbn'; import publicKey from './public_key'; import cipher from './cipher'; import random from './random'; @@ -67,12 +68,10 @@ export default { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': { - const n = util.str2Uint8Array(publicParams[0].toBytes()); - const e = util.str2Uint8Array(publicParams[1].toBytes()); - m = data.write().slice(2); // FIXME - return constructParams(types, [ - new BigInteger(util.hexidump(RSA_RAW.encrypt(m, [n, e])), 16) // FIXME - ]); + const n = publicParams[0].toUint8Array(); + const e = publicParams[1].toUint8Array(); + m = data.toUint8Array(); + return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]); } case 'elgamal': { const elgamal = new publicKey.elgamal(); @@ -114,13 +113,13 @@ export default { switch (algo) { case 'rsa_encrypt_sign': case 'rsa_encrypt': { - const c = util.str2Uint8Array(dataIntegers[0].toBytes()); - const n = util.str2Uint8Array(keyIntegers[0].toBytes()); // pq - const e = util.str2Uint8Array(keyIntegers[1].toBytes()); - const d = util.str2Uint8Array(keyIntegers[2].toBytes()); // de = 1 mod (p-1)(q-1) - const p = util.str2Uint8Array(keyIntegers[3].toBytes()); - const q = util.str2Uint8Array(keyIntegers[4].toBytes()); - const u = util.str2Uint8Array(keyIntegers[5].toBytes()); // q^-1 mod p + const c = dataIntegers[0].toUint8Array(); + const n = keyIntegers[0].toUint8Array(); // pq + const e = keyIntegers[1].toUint8Array(); + const d = keyIntegers[2].toUint8Array(); // de = 1 mod (p-1)(q-1) + const p = keyIntegers[3].toUint8Array(); + const q = keyIntegers[4].toUint8Array(); + const u = keyIntegers[5].toUint8Array(); // q^-1 mod p const dd = BigNumber.fromArrayBuffer(d); const dp = new Modulus( BigNumber.fromArrayBuffer(p).subtract(BigNumber.ONE) @@ -128,9 +127,7 @@ export default { const dq = new Modulus( BigNumber.fromArrayBuffer(q).subtract(BigNumber.ONE) ).reduce(dd).toBytes(); // d mod (q-1) - return new BigInteger( - util.hexidump(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)), 16 // FIXME - ); + return new BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice } case 'elgamal': { const elgamal = new publicKey.elgamal(); diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 0bbbbbd6..4136802c 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -38,10 +38,10 @@ export default { // RSA Encrypt-Only [HAC] case 3: { // RSA Sign-Only [HAC] - const n = util.str2Uint8Array(publickey_MPIs[0].toBytes()); + const n = publickey_MPIs[0].toUint8Array(); const k = publickey_MPIs[0].byteLength(); - const e = util.str2Uint8Array(publickey_MPIs[1].toBytes()); - m = msg_MPIs[0].write().slice(2); // FIXME + const e = publickey_MPIs[1].toUint8Array(); + m = msg_MPIs[0].toUint8Array(); const EM = RSA_RAW.verify(m, [n, e]); const EM2 = pkcs1.emsa.encode(hash_algo, data, k); return util.hexidump(EM) === EM2; @@ -111,13 +111,11 @@ export default { // RSA Encrypt-Only [HAC] case 3: { // RSA Sign-Only [HAC] - const n = util.str2Uint8Array(keyIntegers[0].toBytes()); + const n = keyIntegers[0].toUint8Array(); const k = keyIntegers[0].byteLength(); - const e = util.str2Uint8Array(keyIntegers[1].toBytes()); - d = util.str2Uint8Array(keyIntegers[2].toBytes()); - m = util.hex2Uint8Array( - '00'+pkcs1.emsa.encode(hash_algo, data, k) // FIXME - ); + const e = keyIntegers[1].toUint8Array(); + d = keyIntegers[2].toUint8Array(); + m = util.hex2Uint8Array('00'+pkcs1.emsa.encode(hash_algo, data, k)); // FIXME remove '00' return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d])); } case 17: { diff --git a/src/type/mpi.js b/src/type/mpi.js index 466f6a5f..496e5a5c 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -46,6 +46,8 @@ export default function MPI(data) { /** An implementation dependent integer */ if (data instanceof BigInteger) { this.fromBigInteger(data); + } else if (data instanceof BN) { + this.fromBytes(util.Uint8Array2str(data.toArrayLike(Uint8Array))); } else if (util.isString(data)) { this.fromBytes(data); } else { @@ -92,8 +94,11 @@ MPI.prototype.fromBytes = function (bytes) { }; MPI.prototype.toBytes = function () { - const bytes = util.Uint8Array2str(this.write()); - return bytes.substr(2); + return util.Uint8Array2str(this.toUint8Array()); +}; + +MPI.prototype.toUint8Array = function () { + return this.write().slice(2); }; MPI.prototype.byteLength = function () { From 490b1dc0f0434295939b9e59b764896c4ddc1994 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Fri, 16 Feb 2018 03:44:07 -0800 Subject: [PATCH 06/31] ECDH, ECDSA, EdDSA are all on BN.js; TODO: ElGamal & type_mpi --- src/crypto/crypto.js | 56 +++++++-------- src/crypto/public_key/elliptic/curves.js | 13 ++-- src/crypto/public_key/elliptic/ecdh.js | 60 +++++++--------- src/crypto/public_key/elliptic/ecdsa.js | 41 ++++------- src/crypto/public_key/elliptic/eddsa.js | 40 +++++------ src/crypto/public_key/elliptic/index.js | 9 +-- src/crypto/public_key/elliptic/key.js | 20 +----- src/crypto/signature.js | 88 +++++++++--------------- src/type/oid.js | 4 +- test/crypto/elliptic.js | 8 +-- test/general/x25519.js | 14 ++-- 11 files changed, 135 insertions(+), 218 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 53085d08..54fc6ceb 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -32,7 +32,7 @@ */ import BN from 'bn.js'; -import { RSA_RAW, BigNumber, Modulus } from 'asmcrypto.js'; +import { RSA_RAW } from 'asmcrypto.js'; import publicKey from './public_key'; import cipher from './cipher'; import random from './random'; @@ -62,15 +62,15 @@ export default { * @return {Array} encrypted session key parameters */ publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { + // TODO change algo to return enums const types = this.getEncSessionKeyParamTypes(algo); return (async function() { - let m; switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': { const n = publicParams[0].toUint8Array(); const e = publicParams[1].toUint8Array(); - m = data.toUint8Array(); + const m = data.toUint8Array(); return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]); } case 'elgamal': { @@ -78,17 +78,15 @@ export default { const p = publicParams[0].toBigInteger(); const g = publicParams[1].toBigInteger(); const y = publicParams[2].toBigInteger(); - m = data.toBigInteger(); + const m = data.toBigInteger(); return constructParams(types, elgamal.encrypt(m, g, p, y)); } case 'ecdh': { - const { ecdh } = publicKey.elliptic; - const curve = publicParams[0]; + const oid = 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 - ); + const Q = publicParams[1].toUint8Array(); + 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: @@ -106,9 +104,8 @@ export default { * @param {String} fingerprint Recipient fingerprint * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null */ - publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) { - let p; + // TODO change algo to return enums return new type_mpi(await (async function() { switch (algo) { case 'rsa_encrypt_sign': @@ -120,13 +117,9 @@ export default { const p = keyIntegers[3].toUint8Array(); const q = keyIntegers[4].toUint8Array(); const u = keyIntegers[5].toUint8Array(); // q^-1 mod p - const dd = BigNumber.fromArrayBuffer(d); - const dp = new Modulus( - BigNumber.fromArrayBuffer(p).subtract(BigNumber.ONE) - ).reduce(dd).toBytes(); // d mod (p-1) - const dq = new Modulus( - BigNumber.fromArrayBuffer(q).subtract(BigNumber.ONE) - ).reduce(dd).toBytes(); // d mod (q-1) + const dd = new BN(d); + const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1) + const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1) return new BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice } case 'elgamal': { @@ -134,17 +127,17 @@ export default { const x = keyIntegers[3].toBigInteger(); const c1 = dataIntegers[0].toBigInteger(); const c2 = dataIntegers[1].toBigInteger(); - p = keyIntegers[0].toBigInteger(); + const p = keyIntegers[0].toBigInteger(); return elgamal.decrypt(c1, c2, p, x); } case 'ecdh': { - const { ecdh } = publicKey.elliptic; - const curve = keyIntegers[0]; + const oid = keyIntegers[0]; const kdf_params = keyIntegers[2]; - const V = dataIntegers[0].toBigInteger(); + const V = dataIntegers[0].toUint8Array(); 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); + const d = keyIntegers[3].toUint8Array(); + return publicKey.elliptic.ecdh.decrypt( + oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint); } default: return null; @@ -259,16 +252,17 @@ export default { }, /** 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) { + generateParams: function(algo, bits, oid) { const types = this.getPubKeyParamTypes(algo).concat(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( @@ -278,11 +272,11 @@ export default { } case 'ecdsa': case 'eddsa': - return publicKey.elliptic.generate(curve).then(function (keyObject) { + 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) { + return publicKey.elliptic.generate(oid).then(function (keyObject) { return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); }); default: diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index dd9cbcfe..15a3ea8e 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -26,7 +26,7 @@ */ import { ec as EC, eddsa as EdDSA } from 'elliptic'; -import { KeyPair } from './key'; +import KeyPair from './key'; import BigInteger from '../jsbn'; import random from '../../random'; import enums from '../../../enums'; @@ -126,7 +126,7 @@ function Curve(name, params) { throw new Error('Unknown elliptic key type;'); } this.name = name; - this.oid = curves[name].oid; + this.oid = new OID(curves[name].oid); this.hash = params.hash; this.cipher = params.cipher; this.node = params.node && curves[name].node; @@ -202,14 +202,9 @@ function getPreferredHashAlgo(oid) { return curves[enums.write(enums.curve, oid.toHex())].hash; } +// TODO convert to export default {...} module.exports = { - Curve, - curves, - webCurves, - nodeCurves, - getPreferredHashAlgo, - generate, - get + Curve, curves, webCurves, nodeCurves, get, generate, getPreferredHashAlgo }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 54f45928..2bcaeb1b 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,20 +29,18 @@ * @module crypto/public_key/elliptic/ecdh */ +import BN from 'bn.js'; import curves from './curves'; -import BigInteger from '../jsbn'; +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(), @@ -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 {Uint8Array} 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); - const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); const curve = curves.get(oid); + const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); 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()); return { - V: new BigInteger(util.hexidump(v.getPublic()), 16), + 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); - const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); const curve = curves.get(oid); + const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); 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(util.hexidump(aes_kw.unwrap(Z, C)), 16); + 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 6bc0d703..c94ebf45 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -20,7 +20,6 @@ /** * @requires util * @requires crypto/hash - * @requires crypto/public_key/jsbn * @requires crypto/public_key/elliptic/curves * @module crypto/public_key/elliptic/ecdsa */ @@ -28,44 +27,34 @@ import util from '../../../util'; import hash from '../../hash'; import curves from './curves'; -import BigInteger from '../jsbn'; /** * Sign a message using the provided key - * @param {String} oid Elliptic curve for the key - * @param {enums.hash} hash_algo Hash algorithm used to sign - * @param {Uint8Array} m Message to sign - * @param {BigInteger} d Private key used to sign - * @return {{r: BigInteger, s: BigInteger}} Signature of the message + * @param {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: BN, s: BN}} Signature of the message */ async function sign(oid, hash_algo, m, d) { const curve = curves.get(oid); - const key = curve.keyFromPrivate(d.toByteArray()); - const signature = await key.sign(m, hash_algo); - return { - r: new BigInteger(util.hexidump(signature.r.toArray()), 16), - s: new BigInteger(util.hexidump(signature.s.toArray()), 16) - }; + const key = curve.keyFromPrivate(d); + return key.sign(m, hash_algo); } /** * 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: BN, s: BN}} 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 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..9282e327 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -30,45 +30,37 @@ import curves 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 {BN} d Private key used to sign + * @return {{R: Array, S: Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { const curve = curves.get(oid); - const key = curve.keyFromSecret(d.toByteArray()); + const key = curve.keyFromSecret(d.toArray('be', 32)); 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: signature.Rencoded(), S: 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: BN, S: BN}} signature Signature to verify the message + * @param {Uint8Array} m Message to verify + * @param {BN} 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()); + const key = curve.keyFromPublic(Q.toArray('be', 33)); // 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)) + R: signature.R.toArray('le', 32), + S: signature.S.toArray('le', 32) }, 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..9599fc97 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -30,11 +30,6 @@ 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 { + ecdh, ecdsa, eddsa, get, generate, getPreferredHashAlgo }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 859e968b..3d9bc954 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -29,7 +29,7 @@ * @module crypto/public_key/elliptic/key */ -import curves from './curves'; +import { webCurves, nodeCurves } from './curves'; import BigInteger from '../jsbn'; import hash from '../../hash'; import util from '../../../util'; @@ -37,13 +37,7 @@ 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 +48,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 +70,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 +103,6 @@ KeyPair.prototype.getPrivate = function () { return this.keyPair.getPrivate().toArray(); }; -module.exports = { - KeyPair: KeyPair -}; - ////////////////////////// // // diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 4136802c..bb4a10b3 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -22,15 +22,6 @@ export default { * @return {Boolean} true if signature (sig_data was equal to data over hash) */ verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { - let m; - let r; - let s; - let Q; - let curve; - let signature; - - data = util.Uint8Array2str(data); - switch (algo) { case 1: // RSA (Encrypt or Sign) [HAC] @@ -38,12 +29,11 @@ export default { // RSA Encrypt-Only [HAC] case 3: { // RSA Sign-Only [HAC] + const m = msg_MPIs[0].toUint8Array(); const n = publickey_MPIs[0].toUint8Array(); - const k = publickey_MPIs[0].byteLength(); const e = publickey_MPIs[1].toUint8Array(); - m = msg_MPIs[0].toUint8Array(); const EM = RSA_RAW.verify(m, [n, e]); - const EM2 = pkcs1.emsa.encode(hash_algo, data, k); + const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); return util.hexidump(EM) === EM2; } case 16: { @@ -52,35 +42,27 @@ export default { } case 17: { // DSA (Digital Signature Algorithm) [FIPS186] [HAC] - const dsa = publicKey.dsa; - r = msg_MPIs[0].toBN(); - s = msg_MPIs[1].toBN(); + const r = msg_MPIs[0].toBN(); + const s = msg_MPIs[1].toBN(); const p = publickey_MPIs[0].toBN(); const q = publickey_MPIs[1].toBN(); const g = publickey_MPIs[2].toBN(); const y = publickey_MPIs[3].toBN(); - m = util.str2Uint8Array(data); - return dsa.verify(hash_algo, r, s, m, p, q, g, y); + return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y); } 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); + const oid = publickey_MPIs[0]; + const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() }; + const Q = publickey_MPIs[1].toUint8Array(); + return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, 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); + const oid = publickey_MPIs[0]; + const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() }; + const Q = publickey_MPIs[1].toBN(); + return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q); } default: throw new Error('Invalid signature algorithm.'); @@ -97,13 +79,6 @@ export default { */ sign: async function(algo, hash_algo, keyIntegers, data) { - data = util.Uint8Array2str(data); - - let m; - let d; - let curve; - let signature; - switch (algo) { case 1: // RSA (Encrypt or Sign) [HAC] @@ -112,21 +87,21 @@ export default { case 3: { // RSA Sign-Only [HAC] const n = keyIntegers[0].toUint8Array(); - const k = keyIntegers[0].byteLength(); const e = keyIntegers[1].toUint8Array(); - d = keyIntegers[2].toUint8Array(); - m = util.hex2Uint8Array('00'+pkcs1.emsa.encode(hash_algo, data, k)); // FIXME remove '00' + const d = keyIntegers[2].toUint8Array(); + data = util.Uint8Array2str(data); + const m = util.hex2Uint8Array( + '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' + ); return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d])); } case 17: { // DSA (Digital Signature Algorithm) [FIPS186] [HAC] - const dsa = publicKey.dsa; const p = keyIntegers[0].toBN(); const q = keyIntegers[1].toBN(); const g = keyIntegers[2].toBN(); const x = keyIntegers[4].toBN(); - m = util.str2Uint8Array(data); - signature = dsa.sign(hash_algo, m, g, p, q, x); + const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); return util.concatUint8Array([ util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) @@ -138,23 +113,22 @@ export default { } 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()); + const oid = keyIntegers[0]; + const d = keyIntegers[2].toUint8Array(); + const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); + return util.concatUint8Array([ + util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), + util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) + ]); } 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); + const oid = keyIntegers[0]; + const d = keyIntegers[2].toBN(); + const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d); return util.concatUint8Array([ - util.Uint8Array2MPI(signature.R.toArrayLike(Uint8Array, 'le', 32)), - util.Uint8Array2MPI(signature.S.toArrayLike(Uint8Array, 'le', 32)) + util.Uint8Array2MPI(Uint8Array.from(signature.R)), + util.Uint8Array2MPI(Uint8Array.from(signature.S)) ]); } default: diff --git a/src/type/oid.js b/src/type/oid.js index e4ba3e8b..88c75e25 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -31,7 +31,9 @@ module.exports = OID; * @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); diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index ef3b10d1..29adc473 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -9,7 +9,7 @@ const bin2bi = function (bytes) { const mpi = new openpgp.MPI(); bytes = openpgp.util.bin2str(bytes); mpi.fromBytes(bytes); - return mpi.toBigInteger(); + return mpi.toUint8Array(); // FIXME }; describe('Elliptic Curve Cryptography', function () { @@ -316,9 +316,9 @@ describe('Elliptic Curve Cryptography', function () { data = new Uint8Array(data); } return Promise.resolve().then(() => { - const ecdh = elliptic_curves.ecdh; - return ecdh.decrypt( - oid, + const curve = elliptic_curves.get(oid); + return elliptic_curves.ecdh.decrypt( + curve.oid, cipher, hash, bin2bi(ephemeral), diff --git a/test/general/x25519.js b/test/general/x25519.js index 7ed5f337..0e6534c2 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -235,9 +235,9 @@ describe('X25519 Cryptography', function () { 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.params[0].toHex()).to.equal(elliptic.get('ed25519').oid.toHex()); expect(primaryKey.algorithm).to.equal('eddsa'); - expect(subKey.params[0].oid).to.equal(elliptic.get('curve25519').oid); + expect(subKey.params[0].toHex()).to.equal(elliptic.get('curve25519').oid.toHex()); expect(subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid @@ -255,9 +255,9 @@ describe('X25519 Cryptography', function () { }; 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.params[0].toHex()).to.equal(elliptic.get('ed25519').oid.toHex()); 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.params[0].toHex()).to.equal(elliptic.get('curve25519').oid.toHex()); expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid @@ -319,16 +319,16 @@ describe('X25519 Cryptography', function () { describe('Ed25519 Test Vectors from RFC8032', function () { // https://tools.ietf.org/html/rfc8032#section-7.1 const signature = openpgp.crypto.signature; - const curve = openpgp.crypto.publicKey.elliptic.get('ed25519'); + const curve = elliptic.get('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 keyIntegers = [ openpgp.OID.fromClone(curve), - new openpgp.MPI(util.hex2bin(vector.PUBLIC_KEY)), + new openpgp.MPI(util.hex2bin('40'+vector.PUBLIC_KEY)), new openpgp.MPI(util.hex2bin(vector.SECRET_KEY)) ]; const msg_MPIs = [ From e1d85ba682adffade91830731ca226750f4b5f95 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 20 Feb 2018 03:08:42 -0800 Subject: [PATCH 07/31] ElGamal and MPI use bn.js; TODO: RSA Key Generation --- src/crypto/crypto.js | 37 ++++----- src/crypto/public_key/dsa.js | 2 +- src/crypto/public_key/elgamal.js | 62 +++++++------- src/crypto/public_key/elliptic/ecdh.js | 4 +- src/crypto/public_key/rsa.js | 80 +++++++++---------- src/crypto/signature.js | 7 +- src/packet/public_key.js | 2 +- .../public_key_encrypted_session_key.js | 2 +- src/type/mpi.js | 77 ++++++++++-------- test/general/packet.js | 6 +- 10 files changed, 145 insertions(+), 134 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 54fc6ceb..408b4dfe 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -32,7 +32,6 @@ */ import BN from 'bn.js'; -import { RSA_RAW } from 'asmcrypto.js'; import publicKey from './public_key'; import cipher from './cipher'; import random from './random'; @@ -68,18 +67,19 @@ export default { switch (algo) { case 'rsa_encrypt': case 'rsa_encrypt_sign': { + const m = data.toUint8Array(); const n = publicParams[0].toUint8Array(); const e = publicParams[1].toUint8Array(); - const m = data.toUint8Array(); - return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]); + const res = await publicKey.rsa.encrypt(m, n, e); + return constructParams(types, [new BN(res)]); } case 'elgamal': { - const elgamal = new publicKey.elgamal(); - const p = publicParams[0].toBigInteger(); - const g = publicParams[1].toBigInteger(); - const y = publicParams[2].toBigInteger(); - const m = data.toBigInteger(); - return constructParams(types, elgamal.encrypt(m, g, p, y)); + const m = data.toBN(); + const p = publicParams[0].toBN(); + const g = publicParams[1].toBN(); + const y = publicParams[2].toBN(); + const res = await publicKey.elgamal.encrypt(m, p, g, y); + return constructParams(types, [res.c1, res.c2]); } case 'ecdh': { const oid = publicParams[0]; @@ -117,18 +117,14 @@ export default { const p = keyIntegers[3].toUint8Array(); const q = keyIntegers[4].toUint8Array(); const u = keyIntegers[5].toUint8Array(); // q^-1 mod p - const dd = new BN(d); - const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1) - const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1) - return new BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice + 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(); - const p = keyIntegers[0].toBigInteger(); - return elgamal.decrypt(c1, c2, p, x); + const c1 = dataIntegers[0].toBN(); + const c2 = dataIntegers[1].toBN(); + const p = keyIntegers[0].toBN(); + const x = keyIntegers[3].toBN(); + return publicKey.elgamal.decrypt(c1, c2, p, x); } case 'ecdh': { const oid = keyIntegers[0]; @@ -263,8 +259,7 @@ export default { case 'rsa_encrypt': case 'rsa_encrypt_sign': case 'rsa_sign': { - const rsa = new publicKey.rsa(); - return rsa.generate(bits, "10001").then(function(keyObject) { + return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { return constructParams( types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] ); diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 8d488e38..f432681d 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -86,7 +86,7 @@ export default { } break; } - return {r: r.fromRed(), s: s.fromRed()}; + return { r: r.fromRed(), s: s.fromRed() }; }, /* diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index 465dc16a..b81ee2b6 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -18,39 +18,41 @@ // 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 one = new BN(1); + +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); + const k = random.getRandomBN(one, p); + 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/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 2bcaeb1b..8bdd1ba5 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -67,7 +67,7 @@ function kdf(hash_algo, X, length, param) { * @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 {Uint8Array} m Value derived from session key (RFC 6637) + * @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 @@ -81,7 +81,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { 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 BN(v.getPublic()), C: C diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 635b00f6..f7effda3 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -18,6 +18,7 @@ // RSA implementation /** + * @requires bn.js * @requires asmcrypto.js * @requires crypto/public_key/jsbn * @requires crypto/random @@ -27,6 +28,7 @@ */ +import BN from 'bn.js'; import { RSA_RAW } from 'asmcrypto.js'; import BigInteger from './jsbn'; import random from '../random'; @@ -59,7 +61,7 @@ function unblind(t, n) { return t.multiply(unblinder).mod(n); } -export default function RSA() { +export default { /** * This function uses jsbn Big Num library to decrypt RSA * @param m message @@ -71,7 +73,12 @@ export default function RSA() { * @param u RSA u as BigInteger * @return {BigInteger} The decrypted value of the message */ - function decrypt(m, n, e, d, p, q, u) { + decrypt: function(m, n, e, d, p, q, u) { + const dd = new BN(d); + const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1) + const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1) + return new BN(RSA_RAW.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice +/* if (config.rsa_blinding) { m = blind(m, n, e); } @@ -89,53 +96,39 @@ export default function RSA() { } t = t.multiply(p).add(xp); -// var t = RSA.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1) - if (config.rsa_blinding) { t = unblind(t, n); } return t; - } +*/ + }, /** * encrypt message * @param m message as BigInteger - * @param e public MPI part as BigInteger * @param n public MPI part as BigInteger + * @param e public MPI part as BigInteger * @return BigInteger */ - function encrypt(m, e, n) { - return m.modPowInt(e, n); - } + encrypt: function(m, n, e) { +// return m.modPowInt(e, n); + return RSA_RAW.encrypt(m, [n, e]); + }, /* Sign and Verify */ - function sign(m, d, n) { - return m.modPow(d, n); -// return RSA_RAW.sign(m, [n, e, d]); - } + sign: function(m, n, e, d) { +// return m.modPow(d, n); + return RSA_RAW.sign(m, [n, e, d]); + }, - function verify(x, e, n) { - return x.modPowInt(e, n); -// return RSA_RAW.verify(x, [n, e]); - } - - // "empty" RSA key constructor - - 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; - } + verify: function(x, n, e) { +// return x.modPowInt(e, n); + return RSA_RAW.verify(x, [n, e]); + }, // Generate a new random private key B bits long, using public expt E - function generate(B, E) { + generate: function(B, E) { const webCrypto = util.getWebCryptoAll(); // @@ -189,6 +182,20 @@ export default function RSA() { }); } + // "empty" RSA key constructor + + 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; + } + function exportKey(keypair) { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 @@ -263,11 +270,4 @@ export default function RSA() { resolve(key); }); } - - this.encrypt = encrypt; - this.decrypt = decrypt; - this.verify = verify; - this.sign = sign; - this.generate = generate; - this.keyObject = KeyObject; -} +}; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index bb4a10b3..6b7bc82a 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -6,7 +6,7 @@ * @module crypto/signature */ -import { RSA_RAW } from 'asmcrypto.js'; +// FIXME wrap rsa.js around this import publicKey from './public_key'; import pkcs1 from './pkcs1'; import util from '../util'; @@ -32,7 +32,7 @@ export default { const m = msg_MPIs[0].toUint8Array(); const n = publickey_MPIs[0].toUint8Array(); const e = publickey_MPIs[1].toUint8Array(); - const EM = RSA_RAW.verify(m, [n, e]); + const EM = publicKey.rsa.verify(m, n, e); const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); return util.hexidump(EM) === EM2; } @@ -93,7 +93,8 @@ export default { const m = util.hex2Uint8Array( '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' ); - return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d])); + const signature = publicKey.rsa.sign(m, n, e, d); + return util.Uint8Array2MPI(signature); } case 17: { // DSA (Digital Signature Algorithm) [FIPS186] [HAC] diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 58f037d9..87d89720 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -182,7 +182,7 @@ PublicKey.prototype.getFingerprint = function () { } else if (this.version === 3) { const paramCount = crypto.getPubKeyParamTypes(this.algorithm).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))); } diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 2a109974..06f55a17 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -134,7 +134,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { key.params, this.encrypted, key.fingerprint - )).toBytes(); + )).toString(); let checksum; let decoded; diff --git a/src/type/mpi.js b/src/type/mpi.js index 496e5a5c..1620cba0 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -44,12 +44,14 @@ import util from '../util'; */ export default function MPI(data) { /** An implementation dependent integer */ - if (data instanceof BigInteger) { + if (data instanceof BN) { + this.fromBN(data); + } else if (data instanceof BigInteger) { this.fromBigInteger(data); - } else if (data instanceof BN) { - this.fromBytes(util.Uint8Array2str(data.toArrayLike(Uint8Array))); + } else if (util.isUint8Array(data)) { + this.fromUint8Array(data); } else if (util.isString(data)) { - this.fromBytes(data); + this.fromString(data); } else { this.data = null; } @@ -57,8 +59,8 @@ export default function MPI(data) { /** * 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 + * @param {Uint8Array} input Payload of mpi data + * @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian * @return {Integer} Length of data read */ MPI.prototype.read = function (bytes, endian='be') { @@ -81,7 +83,7 @@ MPI.prototype.read = function (bytes, endian='be') { const bytelen = Math.ceil(bits / 8); let payload = bytes.subarray(2, 2 + bytelen); if (endian === 'le') { - payload = new Uint8Array(payload).reverse(); + payload = Uint8Array.from(payload).reverse(); } const raw = util.Uint8Array2str(payload); this.fromBytes(raw); @@ -89,47 +91,58 @@ MPI.prototype.read = function (bytes, endian='be') { 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.Uint8Array2MPI(this.data.toArrayLike(Uint8Array, endian, length)); }; -MPI.prototype.toBytes = function () { - return util.Uint8Array2str(this.toUint8Array()); +MPI.prototype.byteLength = function () { + return this.write().length - 2; }; MPI.prototype.toUint8Array = function () { return this.write().slice(2); }; -MPI.prototype.byteLength = function () { - return this.toBytes().length; +MPI.prototype.fromUint8Array = function (bytes) { + this.data = new BN(bytes); }; -/** - * 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.toString = function () { + return util.Uint8Array2str(this.toUint8Array()); }; -MPI.prototype.toBigInteger = function () { +MPI.prototype.fromString = function (str) { + this.data = new BN(util.str2Uint8Array(str)); +}; + +// TODO remove this +MPI.prototype.fromBytes = MPI.prototype.fromString; + +MPI.prototype.toBN = function () { return this.data.clone(); }; -MPI.prototype.toBN = function (bn) { - return new BN(this.write().slice(2)); -}; - -MPI.prototype.fromBigInteger = function (bn) { +MPI.prototype.fromBN = function (bn) { this.data = bn.clone(); }; -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; +MPI.prototype.toBigInteger = function () { + return new BigInteger(util.hexidump(this.write()), 16); +}; + +MPI.prototype.fromBigInteger = function (bn) { + this.data = new BN(bn.toByteArray()); +}; + +MPI.fromClone = function (clone) { + const bn = new BN(); + clone.data.copy(bn); + return new MPI(bn); }; diff --git a/test/general/packet.js b/test/general/packet.js index 35276a4f..d4a54464 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -171,7 +171,7 @@ 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) { @@ -435,7 +435,7 @@ 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) { @@ -463,7 +463,7 @@ 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) { From 10c49be91da12a02bf4b5112e51ef366acb6e593 Mon Sep 17 00:00:00 2001 From: BafS Date: Tue, 20 Feb 2018 18:27:05 +0100 Subject: [PATCH 08/31] Use ES6 modules for exports --- src/crypto/aes_kw.js | 6 +++--- src/crypto/pkcs5.js | 6 +++--- src/crypto/public_key/elliptic/curves.js | 7 ++++--- src/crypto/public_key/elliptic/ecdh.js | 6 +++--- src/crypto/public_key/elliptic/ecdsa.js | 6 +++--- src/crypto/public_key/elliptic/eddsa.js | 6 +++--- src/type/ecdh_symkey.js | 4 ++-- src/type/kdf_params.js | 4 ++-- src/type/oid.js | 4 ++-- 9 files changed, 25 insertions(+), 24 deletions(-) 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/pkcs5.js b/src/crypto/pkcs5.js index f7e166a4..57d070e6 100644 --- a/src/crypto/pkcs5.js +++ b/src/crypto/pkcs5.js @@ -48,7 +48,7 @@ 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/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 15a3ea8e..cdaaed70 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -202,9 +202,10 @@ function getPreferredHashAlgo(oid) { return curves[enums.write(enums.curve, oid.toHex())].hash; } -// TODO convert to export default {...} -module.exports = { - Curve, curves, webCurves, nodeCurves, get, generate, getPreferredHashAlgo +export default Curve; + +export { + curves, webCurves, nodeCurves, get, generate, getPreferredHashAlgo }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 8bdd1ba5..14217b1a 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -30,7 +30,7 @@ */ import BN from 'bn.js'; -import curves from './curves'; +import { get as curvesGet } from './curves'; import aes_kw from '../../aes_kw'; import cipher from '../../cipher'; import hash from '../../hash'; @@ -74,7 +74,7 @@ function kdf(hash_algo, X, length, param) { */ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); - const curve = curves.get(oid); + const curve = curvesGet(oid); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); cipher_algo = enums.read(enums.symmetric, cipher_algo); const v = await curve.genKeyPair(); @@ -102,7 +102,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { */ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); - const curve = curves.get(oid); + const curve = curvesGet(oid); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); cipher_algo = enums.read(enums.symmetric, cipher_algo); V = curve.keyFromPublic(V); diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index c94ebf45..948ffe59 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -26,7 +26,7 @@ import util from '../../../util'; import hash from '../../hash'; -import curves from './curves'; +import { get as curvesGet } from './curves'; /** * Sign a message using the provided key @@ -37,7 +37,7 @@ import curves from './curves'; * @return {{r: BN, s: BN}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = curves.get(oid); + const curve = curvesGet(oid); const key = curve.keyFromPrivate(d); return key.sign(m, hash_algo); } @@ -52,7 +52,7 @@ async function sign(oid, hash_algo, m, d) { * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - const curve = curves.get(oid); + const curve = curvesGet(oid); const key = curve.keyFromPublic(Q); return key.verify(m, signature, hash_algo); } diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 9282e327..a0fc467d 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -26,7 +26,7 @@ import BN from 'bn.js'; import hash from '../../hash'; -import curves from './curves'; +import { get as curvesGet } from './curves'; /** * Sign a message using the provided key @@ -37,7 +37,7 @@ import curves from './curves'; * @return {{R: Array, S: Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = curves.get(oid); + const curve = curvesGet(oid); const key = curve.keyFromSecret(d.toArray('be', 32)); const signature = await key.sign(m, hash_algo); // EdDSA signature params are returned in little-endian format @@ -54,7 +54,7 @@ async function sign(oid, hash_algo, m, d) { * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - const curve = curves.get(oid); + const curve = curvesGet(oid); const key = curve.keyFromPublic(Q.toArray('be', 33)); // EdDSA signature params are expected in little-endian format return key.verify(m, { diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index ec38430d..1044a459 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -24,8 +24,6 @@ import util from '../util'; -module.exports = ECDHSymmetricKey; - /** * @constructor */ @@ -63,3 +61,5 @@ ECDHSymmetricKey.prototype.read = function (input) { ECDHSymmetricKey.prototype.write = function () { return util.concatUint8Array([new Uint8Array([this.data.length]), this.data]); }; + +export default ECDHSymmetricKey; diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 30bcf469..82598702 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -24,8 +24,6 @@ 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/oid.js b/src/type/oid.js index 88c75e25..23e80d40 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -25,8 +25,6 @@ import util from '../util.js'; -module.exports = OID; - /** * @constructor */ @@ -79,3 +77,5 @@ OID.fromClone = function (clone) { const oid = new OID(clone.oid); return oid; }; + +export default OID; From b794956691387612161001f2b7e620c775b51119 Mon Sep 17 00:00:00 2001 From: BafS Date: Tue, 20 Feb 2018 18:27:57 +0100 Subject: [PATCH 09/31] Update engine field to support Node.js v8+ --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aca34417..33500e0b 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", From 605021af3bbcd4e4184193b706073653cb919d3e Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 20 Feb 2018 13:33:05 -0800 Subject: [PATCH 10/31] Various quickfixes and cleanups --- src/crypto/cipher/des.js | 18 +- src/crypto/cipher/index.js | 21 +- src/crypto/crypto.js | 182 +++++++++--------- src/crypto/index.js | 1 + src/crypto/pkcs5.js | 5 +- src/crypto/public_key/dsa.js | 1 - src/crypto/signature.js | 126 ++++++------ src/enums.js | 9 + src/key.js | 14 +- src/openpgp.js | 2 +- src/packet/public_key.js | 20 +- .../public_key_encrypted_session_key.js | 32 ++- src/packet/secret_key.js | 16 +- src/util.js | 1 + test/crypto/crypto.js | 8 +- 15 files changed, 229 insertions(+), 227 deletions(-) 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 408b4dfe..cfe4ae95 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -19,7 +19,6 @@ /** * @requires bn.js - * @requires asmcrypto.js * @requires crypto/public_key * @requires crypto/cipher * @requires crypto/random @@ -27,6 +26,7 @@ * @requires type/kdf_params * @requires type/mpi * @requires type/oid + * @requires enums * @requires util * @module crypto/crypto */ @@ -39,6 +39,7 @@ 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) { @@ -52,39 +53,41 @@ 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) { - // TODO change algo to return enums + publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) { const types = this.getEncSessionKeyParamTypes(algo); return (async function() { switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': { + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: { const m = data.toUint8Array(); - const n = publicParams[0].toUint8Array(); - const e = publicParams[1].toUint8Array(); + const n = pub_params[0].toUint8Array(); + const e = pub_params[1].toUint8Array(); const res = await publicKey.rsa.encrypt(m, n, e); return constructParams(types, [new BN(res)]); } - case 'elgamal': { + case enums.publicKey.elgamal: { const m = data.toBN(); - const p = publicParams[0].toBN(); - const g = publicParams[1].toBN(); - const y = publicParams[2].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 oid = publicParams[0]; - const kdf_params = publicParams[2]; - const Q = publicParams[1].toUint8Array(); + 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]); @@ -96,47 +99,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) { - // TODO change algo to return enums + 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 c = dataIntegers[0].toUint8Array(); - const n = keyIntegers[0].toUint8Array(); // pq - const e = keyIntegers[1].toUint8Array(); - const d = keyIntegers[2].toUint8Array(); // de = 1 mod (p-1)(q-1) - const p = keyIntegers[3].toUint8Array(); - const q = keyIntegers[4].toUint8Array(); - const u = keyIntegers[5].toUint8Array(); // q^-1 mod p + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: { + const c = data_params[0].toUint8Array(); + const n = key_params[0].toUint8Array(); // pq + const e = key_params[1].toUint8Array(); + const d = key_params[2].toUint8Array(); // de = 1 mod (p-1)(q-1) + const p = key_params[3].toUint8Array(); + const q = key_params[4].toUint8Array(); + const u = key_params[5].toUint8Array(); // q^-1 mod p return publicKey.rsa.decrypt(c, n, e, d, p, q, u); } - case 'elgamal': { - const c1 = dataIntegers[0].toBN(); - const c2 = dataIntegers[1].toBN(); - const p = keyIntegers[0].toBN(); - const x = keyIntegers[3].toBN(); + 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 oid = keyIntegers[0]; - const kdf_params = keyIntegers[2]; - const V = dataIntegers[0].toUint8Array(); - const C = dataIntegers[1].data; - const d = keyIntegers[3].toUint8Array(); + 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.'); } }())); }, @@ -147,31 +153,31 @@ export default { */ getPrivKeyParamTypes: function(algo) { switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.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. return [type_mpi, type_mpi, type_mpi, type_mpi]; - case 'elgamal': + case enums.publicKey.elgamal: // Algorithm-Specific Fields for Elgamal secret keys: // - MPI of Elgamal secret exponent x. return [type_mpi]; - case 'dsa': + case enums.publicKey.dsa: // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. return [type_mpi]; - case 'ecdh': - case 'ecdsa': - case 'eddsa': + case enums.publicKey.ecdh: + case enums.publicKey.ecdsa: + case enums.publicKey.eddsa: // Algorithm-Specific Fields for ECDSA or ECDH secret keys: // - MPI of an integer representing the secret key. return [type_mpi]; default: - throw new Error('Unknown algorithm'); + throw new Error('Invalid public key encryption algorithm.'); } }, @@ -184,37 +190,37 @@ export default { // - 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': + 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': + 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': + 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': + 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': + 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.'); } }, @@ -226,24 +232,24 @@ export default { 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': + 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': + 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': + case enums.publicKey.ecdh: return [type_mpi, type_ecdh_symkey]; default: - throw new Error('Unknown algorithm.'); + throw new Error('Invalid public key encryption algorithm.'); } }, @@ -254,35 +260,39 @@ export default { * @return {Array} The array of parameters */ generateParams: function(algo, bits, oid) { - const types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); + const types = [].concat(this.getPubKeyParamTypes(algo), this.getPrivKeyParamTypes(algo)); switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': { + 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.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] ); }); } - case 'ecdsa': - case 'eddsa': + 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': + 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 encryption 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/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/pkcs5.js b/src/crypto/pkcs5.js index 57d070e6..088d2d89 100644 --- a/src/crypto/pkcs5.js +++ b/src/crypto/pkcs5.js @@ -48,7 +48,4 @@ function decode(msg) { throw new Error('Invalid padding'); } -export default { - encode, - decode -}; +export default { encode, decode }; diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index f432681d..b12f263e 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -63,7 +63,6 @@ export default { // 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. - // TODO rewrite getLeftNBits to work with Uint8Arrays const h = new BN( util.str2Uint8Array( util.getLeftNBits( diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 6b7bc82a..3fd018f6 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,67 +1,60 @@ /** - * @requires asmcrypto.js * @requires crypto/public_key * @requires crypto/pkcs1 + * @requires enums * @requires util * @module crypto/signature */ -// FIXME wrap rsa.js around this import publicKey from './public_key'; 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) { + 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] + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_sign: { const m = msg_MPIs[0].toUint8Array(); - const n = publickey_MPIs[0].toUint8Array(); - const e = publickey_MPIs[1].toUint8Array(); + const n = pub_MPIs[0].toUint8Array(); + const e = pub_MPIs[1].toUint8Array(); const EM = publicKey.rsa.verify(m, n, e); const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); return util.hexidump(EM) === EM2; } - case 16: { - // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] - throw new Error("signing with Elgamal is not defined in the OpenPGP standard."); - } - case 17: { - // DSA (Digital Signature Algorithm) [FIPS186] [HAC] + case enums.publicKey.dsa: { const r = msg_MPIs[0].toBN(); const s = msg_MPIs[1].toBN(); - const p = publickey_MPIs[0].toBN(); - const q = publickey_MPIs[1].toBN(); - const g = publickey_MPIs[2].toBN(); - const y = publickey_MPIs[3].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 19: { - // ECDSA - const oid = publickey_MPIs[0]; + case enums.publicKey.ecdsa: { + const oid = pub_MPIs[0]; const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() }; - const Q = publickey_MPIs[1].toUint8Array(); + const Q = pub_MPIs[1].toUint8Array(); return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q); } - case 22: { - // EdDSA - const oid = publickey_MPIs[0]; + case enums.publicKey.eddsa: { + const oid = pub_MPIs[0]; const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() }; - const Q = publickey_MPIs[1].toBN(); + const Q = pub_MPIs[1].toBN(); return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q); } default: @@ -70,25 +63,24 @@ export default { }, /** - * Create a signature on data using the specified algorithm - * @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 {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 {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(algo, hash_algo, keyIntegers, data) { - + 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 n = keyIntegers[0].toUint8Array(); - const e = keyIntegers[1].toUint8Array(); - const d = keyIntegers[2].toUint8Array(); + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_sign: { + const n = key_params[0].toUint8Array(); + const e = key_params[1].toUint8Array(); + const d = key_params[2].toUint8Array(); data = util.Uint8Array2str(data); const m = util.hex2Uint8Array( '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' @@ -96,36 +88,32 @@ export default { const signature = publicKey.rsa.sign(m, n, e, d); return util.Uint8Array2MPI(signature); } - case 17: { - // DSA (Digital Signature Algorithm) [FIPS186] [HAC] - const p = keyIntegers[0].toBN(); - const q = keyIntegers[1].toBN(); - const g = keyIntegers[2].toBN(); - const x = keyIntegers[4].toBN(); + 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.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) ]); } - 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 oid = keyIntegers[0]; - const d = keyIntegers[2].toUint8Array(); + 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.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) ]); } - case 22: { - // EdDSA - const oid = keyIntegers[0]; - const d = keyIntegers[2].toBN(); + case enums.publicKey.eddsa: { + const oid = key_params[0]; + const d = key_params[2].toBN(); const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d); return util.concatUint8Array([ util.Uint8Array2MPI(Uint8Array.from(signature.R)), diff --git a/src/enums.js b/src/enums.js index d384ad4e..ce331d7f 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 }, diff --git a/src/key.js b/src/key.js index 1f163f74..93e5f2d9 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)} */ 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/public_key.js b/src/packet/public_key.js index 87d89720..a8dbf8a2 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -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()); } @@ -180,7 +180,8 @@ PublicKey.prototype.getFingerprint = function () { toHash = this.writeOld(); this.fingerprint = util.Uint8Array2str(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].toString(); } @@ -192,7 +193,7 @@ PublicKey.prototype.getFingerprint = function () { /** * Returns algorithm information - * @return {Promise} An object of the form {algorithm: String, bits:int, curve:String} */ PublicKey.prototype.getAlgorithmInfo = function () { const 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 06f55a17..f4d5e90c 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -75,7 +75,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++) { @@ -106,18 +107,15 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { data += util.Uint8Array2str(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,21 +127,18 @@ 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 - )).toString(); + 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); + if (algo === enums.publicKey.ecdh) { + decoded = crypto.pkcs5.decode(result.toString()); checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); } else { - decoded = crypto.pkcs1.eme.decode(result); - checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); + 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)); @@ -161,7 +156,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..85ce93d4 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -84,7 +84,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { 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/util.js b/src/util.js index 8af4d619..c03a07e5 100644 --- a/src/util.js +++ b/src/util.js @@ -446,6 +446,7 @@ export default { } }, + // TODO rewrite getLeftNBits to work with Uint8Arrays getLeftNBits: function (string, bitcount) { const rest = bitcount % 8; if (rest === 0) { diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 9979d5a9..36434815 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -381,11 +381,11 @@ describe('API functional testing', function() { const RSAUnencryptedData = new openpgp.MPI(); RSAUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); return crypto.publicKeyEncrypt( - "rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData + 1, RSApubMPIs, RSAUnencryptedData ).then(RSAEncryptedData => { return crypto.publicKeyDecrypt( - "rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData + 1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData ).then(data => { data = data.write(); data = util.Uint8Array2str(data.subarray(2, data.length)); @@ -402,11 +402,11 @@ describe('API functional testing', function() { ElgamalUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); return crypto.publicKeyEncrypt( - "elgamal", ElgamalpubMPIs, ElgamalUnencryptedData + 16, ElgamalpubMPIs, ElgamalUnencryptedData ).then(ElgamalEncryptedData => { return crypto.publicKeyDecrypt( - "elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData + 16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData ).then(data => { data = data.write(); data = util.Uint8Array2str(data.subarray(2, data.length)); From 1812166a539cac85fbe017a86dc61032e8b23bca Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 21 Feb 2018 14:37:57 -0800 Subject: [PATCH 11/31] RSA using asmcrypto with asmcrypto bignum --- src/crypto/crypto.js | 104 ++++---- src/crypto/public_key/dsa.js | 5 +- src/crypto/public_key/elgamal.js | 5 +- src/crypto/public_key/elliptic/curves.js | 17 +- src/crypto/public_key/rsa.js | 322 +++++++++-------------- src/crypto/signature.js | 20 +- src/key.js | 3 + src/packet/signature.js | 2 +- test/general/packet.js | 18 +- 9 files changed, 212 insertions(+), 284 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index cfe4ae95..d4526020 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -18,7 +18,6 @@ // The GPG4Browsers crypto interface /** - * @requires bn.js * @requires crypto/public_key * @requires crypto/cipher * @requires crypto/random @@ -31,7 +30,6 @@ * @module crypto/crypto */ -import BN from 'bn.js'; import publicKey from './public_key'; import cipher from './cipher'; import random from './random'; @@ -70,11 +68,11 @@ export default { switch (algo) { case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_encrypt_sign: { - const m = data.toUint8Array(); - const n = pub_params[0].toUint8Array(); - const e = pub_params[1].toUint8Array(); + 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, [new BN(res)]); + return constructParams(types, [res]); } case enums.publicKey.elgamal: { const m = data.toBN(); @@ -116,13 +114,13 @@ export default { switch (algo) { case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt: { - const c = data_params[0].toUint8Array(); - const n = key_params[0].toUint8Array(); // pq - const e = key_params[1].toUint8Array(); - const d = key_params[2].toUint8Array(); // de = 1 mod (p-1)(q-1) - const p = key_params[3].toUint8Array(); - const q = key_params[4].toUint8Array(); - const u = key_params[5].toUint8Array(); // q^-1 mod p + 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 enums.publicKey.elgamal: { @@ -153,28 +151,28 @@ export default { */ getPrivKeyParamTypes: function(algo) { switch (algo) { + // 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: - // 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. return [type_mpi, type_mpi, type_mpi, type_mpi]; + // Algorithm-Specific Fields for Elgamal secret keys: + // - MPI of Elgamal secret exponent x. case enums.publicKey.elgamal: - // Algorithm-Specific Fields for Elgamal secret keys: - // - MPI of Elgamal secret exponent x. return [type_mpi]; + // Algorithm-Specific Fields for DSA secret keys: + // - MPI of DSA secret exponent x. case enums.publicKey.dsa: - // Algorithm-Specific Fields for DSA secret keys: - // - MPI of DSA secret exponent x. return [type_mpi]; + // 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: - // Algorithm-Specific Fields for ECDSA or ECDH secret keys: - // - MPI of an integer representing the secret key. return [type_mpi]; default: throw new Error('Invalid public key encryption algorithm.'); @@ -186,37 +184,37 @@ 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) { + // 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). + // 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). + // 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. + // 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. + // 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: @@ -230,24 +228,22 @@ export default { */ getEncSessionKeyParamTypes: function(algo) { switch (algo) { - // Algorithm-Specific Fields for RSA encrypted session keys: - // - MPI of RSA encrypted value m**e mod n. + // 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 + // 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 + // 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('Invalid public key encryption algorithm.'); } @@ -267,7 +263,7 @@ export default { case enums.publicKey.rsa_sign: { return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { return constructParams( - types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] + types, [keyObject.n, keyObject.e, keyObject.d, keyObject.p, keyObject.q, keyObject.u] ); }); } diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index b12f263e..77b770db 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -32,7 +32,7 @@ import random from '../random.js'; import config from '../../config'; import util from '../../util'; -const one = new BN(1); +const two = new BN(2); const zero = new BN(0); /* @@ -73,7 +73,8 @@ export default { // signature shall be recalculated. It is extremely unlikely that r = 0 // or s = 0 if signatures are generated properly. while (true) { - k = random.getRandomBN(one, q); + // TODO confirm this range + k = random.getRandomBN(two, q.subn(1)); // returns in [2, q-2] r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q if (zero.cmp(r) === 0) { continue; diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index b81ee2b6..2279620f 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -26,7 +26,7 @@ import BN from 'bn.js'; import random from '../random'; -const one = new BN(1); +const two = new BN(2); export default { /* @@ -38,7 +38,8 @@ export default { const mred = m.toRed(redp); const gred = g.toRed(redp); const yred = y.toRed(redp); - const k = random.getRandomBN(one, p); + // TODO confirm this range + const k = random.getRandomBN(two, p.subn(1)); // returns in [2, p-2] return { c1: gred.redPow(k).fromRed(), c2: yred.redPow(k).redMul(mred).fromRed() diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index cdaaed70..58569ffb 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -37,22 +37,19 @@ 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 = { diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index f7effda3..e6c943a3 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -29,245 +29,181 @@ import BN from 'bn.js'; -import { RSA_RAW } from 'asmcrypto.js'; -import BigInteger from './jsbn'; +import { random as asmcrypto_random, RSA, RSA_RAW } from 'asmcrypto.js'; 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(); - } - } - this.nextBytes = nextBytes; -} - -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); -} +const two = new BN(2); +const zero = new BN(0); export default { - /** - * 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 + /** 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 */ - decrypt: function(m, n, e, d, p, q, u) { - const dd = new BN(d); - const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1) - const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1) - return new BN(RSA_RAW.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice -/* - if (config.rsa_blinding) { - m = blind(m, n, e); - } - 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; -*/ - }, - - /** - * encrypt message - * @param m message as BigInteger - * @param n public MPI part as BigInteger - * @param e public MPI part as BigInteger - * @return BigInteger - */ - encrypt: function(m, n, e) { -// return m.modPowInt(e, n); - return RSA_RAW.encrypt(m, [n, e]); - }, - - /* Sign and Verify */ sign: function(m, n, e, d) { -// return m.modPow(d, n); + m = m.toArrayLike(Uint8Array); + n = n.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + d = d.toArrayLike(Uint8Array); return RSA_RAW.sign(m, [n, e, d]); }, - verify: function(x, n, e) { -// return x.modPowInt(e, n); - return RSA_RAW.verify(x, [n, e]); + /** + * Verify signature + * @param s signature as BN + * @param n public MPI part as BN + * @param e public MPI part as BN + * @return BN + */ + verify: function(s, n, e) { + s = s.toArrayLike(Uint8Array); + n = n.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + return RSA_RAW.verify(s, [n, e]); }, - // Generate a new random private key B bits long, using public expt E + /** + * 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) { + m = m.toArrayLike(Uint8Array); + n = n.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + return RSA_RAW.encrypt(m, [n, e]); + }, - generate: function(B, E) { + /** + * 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) { + let blinder = zero; + let unblinder = zero; + const nred = new BN.red(n); + + config.rsa_blinding = false; // FIXME + if (config.rsa_blinding) { + if (unblinder.bitLength() === n.bitLength()) { + unblinder = unblinder.sqr().mod(n); + } else { + unblinder = random.getRandomBN(two, n); + } + blinder = unblinder.toRed(nred).redInvm().redPow(e).fromRed(); + m = m.mul(blinder).mod(n); + } + + const dq = d.mod(q.subn(1)).toArrayLike(Uint8Array); // d mod (q-1) + const dp = d.mod(p.subn(1)).toArrayLike(Uint8Array); // d mod (p-1) + const nn = n.toArrayLike(Uint8Array); + m = m.toArrayLike(Uint8Array); + e = e.toArrayLike(Uint8Array); + d = d.toArrayLike(Uint8Array); + q = q.toArrayLike(Uint8Array); + p = p.toArrayLike(Uint8Array); + u = u.toArrayLike(Uint8Array); + let result = new BN(RSA_RAW.decrypt(m, [nn, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice + + if (config.rsa_blinding) { + result = result.mul(unblinder).mod(n); + } + + return result; + }, + + /** + * Generate a new random private key B bits long with public exponent E + */ + 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; + const Euint8 = E.toArrayLike(Uint8Array); // get bytes of exponent 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: Euint8, // take three bytes (max 65537) 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 = await webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); + } 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: Euint8, // take three bytes (max 65537) 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); - }); - } - - // "empty" RSA key constructor - - 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; - } - - 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.'); - } - return key; - } + let jwk = await webCrypto.exportKey('jwk', keyPair.privateKey); - 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); + // parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk) + if (jwk instanceof ArrayBuffer) { + jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(key))); + } + + // map JWK parameters to BN + key = {}; + key.n = b64toBN(jwk.n); + key.e = E; + key.d = b64toBN(jwk.d); + key.p = b64toBN(jwk.p); + key.q = b64toBN(jwk.q); key.u = key.p.modInverse(key.q); - - function toBigInteger(base64url) { - const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); - const hex = util.hexstrdump(atob(base64)); - return new BigInteger(hex, 16); - } - return key; } - // - // JS code - // + // TODO use this is ../../encoding/base64.js and ./elliptic/{key,curve}.js + function b64toBN(base64url) { + const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); + const hex = util.hexstrdump(atob(base64)); + return new BN(hex, 16); + } - 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); - }); + // asmcrypto fallback + await asmcrypto_random.seed(await random.getRandomBytes(1024)); // FIXME how much randomness? + key = await RSA.generateKey(B, E.toArrayLike(Uint8Array)); + return { + n: key[0], + e: key[1], + d: key[2], + q: key[3], + p: key[4], + // dq: key[5], + // dp: key[6], + u: key[7] + }; } }; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 3fd018f6..32319a9b 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,4 +1,5 @@ /** + * @requires bn.js * @requires crypto/public_key * @requires crypto/pkcs1 * @requires enums @@ -6,6 +7,7 @@ * @module crypto/signature */ +import BN from 'bn.js'; import publicKey from './public_key'; import pkcs1 from './pkcs1'; import enums from '../enums'; @@ -29,11 +31,11 @@ export default { case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_sign: { - const m = msg_MPIs[0].toUint8Array(); - const n = pub_MPIs[0].toUint8Array(); - const e = pub_MPIs[1].toUint8Array(); + 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.Uint8Array2str(data), n.length); + const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.byteLength()); return util.hexidump(EM) === EM2; } case enums.publicKey.dsa: { @@ -78,13 +80,11 @@ export default { case enums.publicKey.rsa_encrypt_sign: case enums.publicKey.rsa_encrypt: case enums.publicKey.rsa_sign: { - const n = key_params[0].toUint8Array(); - const e = key_params[1].toUint8Array(); - const d = key_params[2].toUint8Array(); + const n = key_params[0].toBN(); + const e = key_params[1].toBN(); + const d = key_params[2].toBN(); data = util.Uint8Array2str(data); - const m = util.hex2Uint8Array( - '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' - ); + const m = new BN(pkcs1.emsa.encode(hash_algo, data, n.byteLength()), 16); const signature = publicKey.rsa.sign(m, n, e, d); return util.Uint8Array2MPI(signature); } diff --git a/src/key.js b/src/key.js index 93e5f2d9..5bf3de9a 100644 --- a/src/key.js +++ b/src/key.js @@ -696,6 +696,7 @@ Key.prototype.verifyPrimaryUser = async function(keys) { return; } const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey }; + // TODO: fix the race condition, this should be a forEach await Promise.all(user.selfCertifications.map(async function(selfCertification) { // skip if certificate is not the most recent if ((selfCertification.isPrimaryUserID && @@ -703,6 +704,7 @@ Key.prototype.verifyPrimaryUser = async function(keys) { (!lastPrimaryUserID && selfCertification.created < lastCreated)) { return; } + // TODO break apart the .verify/isRevoked/isExpired checks // skip if certificates is not valid if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify)) || (selfCertification.revoked || await user.isRevoked(primaryKey, selfCertification)) || @@ -781,6 +783,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 diff --git a/src/packet/signature.js b/src/packet/signature.js index 015672f0..1baf0a7f 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -85,7 +85,7 @@ export default function Signature(date=new Date()) { this.signatureTargetHash = null; this.embeddedSignature = null; - this.verified = false; + this.verified = null; } /** diff --git a/test/general/packet.js b/test/general/packet.js index d4a54464..c614a633 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -176,11 +176,9 @@ describe("Packet", function() { 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(); @@ -439,11 +437,9 @@ describe("Packet", function() { 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; @@ -467,11 +463,9 @@ describe("Packet", function() { 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; From a2868a5c14428658cad75c76396eca83f9c1cccb Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 21 Feb 2018 14:58:50 -0800 Subject: [PATCH 12/31] Begone jsbn.js! I tell you begonegit status --- src/crypto/public_key/dsa.js | 4 +- src/crypto/public_key/elliptic/curves.js | 8 +- src/crypto/public_key/elliptic/key.js | 7 +- src/crypto/public_key/jsbn.js | 1691 ---------------------- src/crypto/public_key/rsa.js | 1 - src/crypto/random.js | 34 - src/type/mpi.js | 12 - src/util.js | 49 +- 8 files changed, 24 insertions(+), 1782 deletions(-) delete mode 100644 src/crypto/public_key/jsbn.js diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 77b770db..eb2b453a 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -18,8 +18,8 @@ // A Digital signature algorithm implementation /** + * @requires bn.js * @requires crypto/hash - * @requires crypto/public_key/jsbn * @requires crypto/random * @requires config * @requires util @@ -28,7 +28,7 @@ import BN from 'bn.js'; import hash from '../hash'; -import random from '../random.js'; +import random from '../random'; import config from '../../config'; import util from '../../util'; diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 58569ffb..ede97dfc 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -18,16 +18,16 @@ // Wrapper of an instance of an Elliptic Curve /** + * @requires bn.js * @requires crypto/public_key/elliptic/key - * @requires crypto/public_key/jsbn * @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 random from '../../random'; import enums from '../../../enums'; import util from '../../../util'; @@ -188,8 +188,8 @@ async function generate(curve) { const keyPair = await curve.genKeyPair(); return { oid: curve.oid, - Q: new BigInteger(util.hexidump(keyPair.getPublic()), 16), - d: new BigInteger(util.hexidump(keyPair.getPrivate()), 16), + Q: new BN(keyPair.getPublic()), + d: new BN(keyPair.getPrivate()), hash: curve.hash, cipher: curve.cipher }; diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index 3d9bc954..e2d6ae66 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -18,8 +18,8 @@ // 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 @@ -29,8 +29,8 @@ * @module crypto/public_key/elliptic/key */ +import BN from 'bn.js'; import { webCurves, nodeCurves } from './curves'; -import BigInteger from '../jsbn'; import hash from '../../hash'; import util from '../../../util'; import enums from '../../../enums'; @@ -210,8 +210,7 @@ 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(util.hexidump(r), 16), s: new BigInteger(util.hexidump(s), 16) }, 'der'); + const signature = ECDSASignature.encode({ r: new BN(r), s: new BN(s) }, 'der'); const key = jwkToPem( { "kty": "EC", 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/rsa.js b/src/crypto/public_key/rsa.js index e6c943a3..0699eaf2 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -20,7 +20,6 @@ /** * @requires bn.js * @requires asmcrypto.js - * @requires crypto/public_key/jsbn * @requires crypto/random * @requires config * @requires util diff --git a/src/crypto/random.js b/src/crypto/random.js index a36b145c..a97ca8c8 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -96,40 +96,6 @@ export default { return buf; }, - /** - * Create a secure random big integer of bits length - * @param {Integer} bits Bit length of the MPI to create - * @return {BigInteger} Resulting big integer - */ - 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) { - 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); - }, - /** * Create a secure random MPI in specified range * @param {module:type/mpi} min Lower bound, included diff --git a/src/type/mpi.js b/src/type/mpi.js index 1620cba0..efa4292c 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -30,13 +30,11 @@ * of the MPI in bits followed by a string of octets that contain the * actual integer. * @requires bn.js - * @requires crypto/public_key/jsbn * @requires util * @module type/mpi */ import BN from 'bn.js'; -import BigInteger from '../crypto/public_key/jsbn'; import util from '../util'; /** @@ -46,8 +44,6 @@ export default function MPI(data) { /** An implementation dependent integer */ if (data instanceof BN) { this.fromBN(data); - } else if (data instanceof BigInteger) { - this.fromBigInteger(data); } else if (util.isUint8Array(data)) { this.fromUint8Array(data); } else if (util.isString(data)) { @@ -133,14 +129,6 @@ MPI.prototype.fromBN = function (bn) { this.data = bn.clone(); }; -MPI.prototype.toBigInteger = function () { - return new BigInteger(util.hexidump(this.write()), 16); -}; - -MPI.prototype.fromBigInteger = function (bn) { - this.data = new BN(bn.toByteArray()); -}; - MPI.fromClone = function (clone) { const bn = new BN(); clone.data.copy(bn); diff --git a/src/util.js b/src/util.js index c03a07e5..dbb4c9e6 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 @@ -539,25 +524,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. */ @@ -600,5 +566,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); } }; From 1b66b9cf60d04d36d79470e27f86f24809b5c398 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Wed, 21 Feb 2018 16:02:19 -0800 Subject: [PATCH 13/31] fix worker tests --- src/type/mpi.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/type/mpi.js b/src/type/mpi.js index efa4292c..b3d98d1d 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -130,6 +130,7 @@ MPI.prototype.fromBN = function (bn) { }; MPI.fromClone = function (clone) { + clone.data.copy = BN.prototype.copy; const bn = new BN(); clone.data.copy(bn); return new MPI(bn); From 8c4fa07dd529c3c89df6f7b686b333d939401c54 Mon Sep 17 00:00:00 2001 From: Bart Butler Date: Wed, 21 Feb 2018 20:02:50 -0800 Subject: [PATCH 14/31] babelify asmcrypto.js --- Gruntfile.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gruntfile.js b/Gruntfile.js index 091ef0a6..ca6abf05 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", From d40e8fe42824ac69818b5d2bdd82032c9f29549c Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 22 Feb 2018 01:30:48 -0800 Subject: [PATCH 15/31] Use asmcrypto.js directly + quickfix --- package.json | 2 +- src/crypto/cipher/aes.js | 2 +- src/crypto/gcm.js | 2 +- src/crypto/hash/index.js | 2 +- src/crypto/public_key/rsa.js | 29 ++++++++++--------- .../sym_encrypted_integrity_protected.js | 2 +- test/crypto/crypto.js | 6 ++-- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 33500e0b..1ed9f141 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "whatwg-fetch": "^2.0.3" }, "dependencies": { - "asmcrypto.js": "github:mahrud/asmcrypto.js", + "asmcrypto.js": "^0.22.0", "asn1.js": "^5.0.0", "bn.js": "^4.11.8", "buffer": "^5.0.8", diff --git a/src/crypto/cipher/aes.js b/src/crypto/cipher/aes.js index a1f0423b..e23d513a 100644 --- a/src/crypto/cipher/aes.js +++ b/src/crypto/cipher/aes.js @@ -3,7 +3,7 @@ * @module crypto/cipher/aes */ -import { AES_ECB } from 'asmcrypto.js'; +import { AES_ECB } from 'asmcrypto.js/src/aes/ecb/exports'; // TODO use webCrypto or nodeCrypto when possible. export default function aes(length) { diff --git a/src/crypto/gcm.js b/src/crypto/gcm.js index ed4b1222..b50325d1 100644 --- a/src/crypto/gcm.js +++ b/src/crypto/gcm.js @@ -24,7 +24,7 @@ * @module crypto/gcm */ -import { AES_GCM } from 'asmcrypto.js'; +import { AES_GCM } from 'asmcrypto.js/src/aes/gcm/exports'; import config from '../config'; import util from '../util'; diff --git a/src/crypto/hash/index.js b/src/crypto/hash/index.js index 8fca5645..e62823b2 100644 --- a/src/crypto/hash/index.js +++ b/src/crypto/hash/index.js @@ -8,7 +8,7 @@ */ import Rusha from 'rusha'; -import { SHA256 } from 'asmcrypto.js'; +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'; diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 0699eaf2..3b935960 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -28,7 +28,9 @@ import BN from 'bn.js'; -import { random as asmcrypto_random, RSA, RSA_RAW } from 'asmcrypto.js'; +import { RSA } from 'asmcrypto.js/src/rsa/exports-keygen'; +import { RSA_RAW } from 'asmcrypto.js/src/rsa/exports-raw'; +import { random as asmcrypto_random } from 'asmcrypto.js/src/random/exports'; import random from '../random'; import config from '../../config'; import util from '../../util'; @@ -137,13 +139,12 @@ export default { if (webCrypto) { let keyPair; let keyGenOpt; - const Euint8 = E.toArrayLike(Uint8Array); // get bytes of exponent 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, // 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' } @@ -154,7 +155,7 @@ export default { keyGenOpt = { name: 'RSA-OAEP', modulusLength: B, // the specified keysize in bits - publicExponent: Euint8, // 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' } @@ -170,7 +171,7 @@ export default { // parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk) if (jwk instanceof ArrayBuffer) { - jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(key))); + jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk))); } // map JWK parameters to BN @@ -180,7 +181,7 @@ export default { key.d = b64toBN(jwk.d); key.p = b64toBN(jwk.p); key.q = b64toBN(jwk.q); - key.u = key.p.modInverse(key.q); + key.u = key.p.invm(key.q); return key; } @@ -195,14 +196,14 @@ export default { await asmcrypto_random.seed(await random.getRandomBytes(1024)); // FIXME how much randomness? key = await RSA.generateKey(B, E.toArrayLike(Uint8Array)); return { - n: key[0], - e: key[1], - d: key[2], - q: key[3], - p: key[4], - // dq: key[5], - // dp: key[6], - u: key[7] + n: new BN(key[0]), + e: new BN(key[1]), + d: new BN(key[2]), + q: new BN(key[3]), + p: new BN(key[4]), + // dq: new BN(key[5]), + // dp: new BN(key[6]), + u: new BN(key[7]) }; } }; diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 763384c4..56d0ce50 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -32,7 +32,7 @@ * @module packet/sym_encrypted_integrity_protected */ -import { AES_CFB } from 'asmcrypto.js'; +import { AES_CFB } from 'asmcrypto.js/src/aes/cfb/exports'; import crypto from '../crypto'; import enums from '../enums'; import util from '../util'; diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 36434815..7d3bca0f 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -1,5 +1,5 @@ const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); -const asmCrypto = require('asmcrypto.js'); +const AES_CFB = require('asmcrypto.js/asmcrypto.all.js').AES_CFB; const chai = require('chai'); chai.use(require('chai-as-promised')); @@ -297,9 +297,9 @@ describe('API functional testing', function() { const prefix = util.concatUint8Array([rndm, repeat]); const symmencData = crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false); - const symmencData2 = asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey); + const symmencData2 = AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey); - let decrypted = asmCrypto.AES_CFB.decrypt(symmencData, symmKey); + let decrypted = AES_CFB.decrypt(symmencData, symmKey); decrypted = decrypted.subarray(crypto.cipher[algo].blockSize + 2, decrypted.length); expect(util.Uint8Array2str(symmencData)).to.equal(util.Uint8Array2str(symmencData2)); From 3b912d2fae95d7c80a722f79453ba4c939b99839 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 22 Feb 2018 14:46:00 -0800 Subject: [PATCH 16/31] Cleaning mpi.js; TODO: store MPI.data as Uint8Array instead of BN Also improved asynchronousity in packet tests --- src/crypto/pkcs1.js | 3 +-- src/crypto/public_key/rsa.js | 2 ++ src/type/mpi.js | 35 ++++++++++++----------------------- test/crypto/crypto.js | 12 ++++++------ test/crypto/elliptic.js | 19 ++++++------------- test/general/packet.js | 25 ++++++++++--------------- 6 files changed, 37 insertions(+), 59 deletions(-) diff --git a/src/crypto/pkcs1.js b/src/crypto/pkcs1.js index f1c194e6..54ac6171 100644 --- a/src/crypto/pkcs1.js +++ b/src/crypto/pkcs1.js @@ -95,8 +95,7 @@ export default { * @return {String} message, an octet string */ decode: function(EM) { - // FIXME - // leading zeros truncated by jsbn + // leading zeros truncated by bn.js if (EM.charCodeAt(0) !== 0) { EM = String.fromCharCode(0) + EM; } diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 3b935960..6427b359 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -129,6 +129,8 @@ export default { /** * 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 */ generate: async function(B, E) { let key; diff --git a/src/type/mpi.js b/src/type/mpi.js index b3d98d1d..03c6f517 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -42,7 +42,7 @@ import util from '../util'; */ export default function MPI(data) { /** An implementation dependent integer */ - if (data instanceof BN) { + if (BN.isBN(data)) { this.fromBN(data); } else if (util.isUint8Array(data)) { this.fromUint8Array(data); @@ -54,35 +54,27 @@ export default function MPI(data) { } /** - * Parsing function for a mpi ({@link https://tools.ietf.org/html/rfc4880#section3.2|RFC 4880 3.2}). - * @param {Uint8Array} input Payload of mpi data - * @param {String} endian Endianness of the payload; 'be' for big-endian or '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); + } else { + bytes = util.copyUint8Array(bytes); } const bits = (bytes[0] << 8) | bytes[1]; - - // 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); + const payload = bytes.subarray(2, 2 + bytelen); + if (endian === 'le') { - payload = Uint8Array.from(payload).reverse(); + payload.reverse(); } - const raw = util.Uint8Array2str(payload); - this.fromBytes(raw); + + this.fromUint8Array(payload); return 2 + bytelen; }; @@ -118,9 +110,6 @@ MPI.prototype.fromString = function (str) { this.data = new BN(util.str2Uint8Array(str)); }; -// TODO remove this -MPI.prototype.fromBytes = MPI.prototype.fromString; - MPI.prototype.toBN = function () { return this.data.clone(); }; diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 7d3bca0f..56dc2a6c 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -378,10 +378,10 @@ describe('API functional testing', function() { it('Asymmetric using RSA with eme_pkcs1 padding', function () { const symmKey = util.Uint8Array2str(crypto.generateSessionKey('aes256')); - const RSAUnencryptedData = new openpgp.MPI(); - RSAUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); + const RSAUnencryptedData = crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength()) + const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData); return crypto.publicKeyEncrypt( - 1, RSApubMPIs, RSAUnencryptedData + 1, RSApubMPIs, RSAUnencryptedMPI ).then(RSAEncryptedData => { return crypto.publicKeyDecrypt( @@ -398,11 +398,11 @@ describe('API functional testing', function() { it('Asymmetric using Elgamal with eme_pkcs1 padding', function () { const symmKey = util.Uint8Array2str(crypto.generateSessionKey('aes256')); - const ElgamalUnencryptedData = new openpgp.MPI(); - ElgamalUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); + const ElgamalUnencryptedData = crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength()); + const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData); return crypto.publicKeyEncrypt( - 16, ElgamalpubMPIs, ElgamalUnencryptedData + 16, ElgamalpubMPIs, ElgamalUnencryptedMPI ).then(ElgamalEncryptedData => { return crypto.publicKeyDecrypt( diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 29adc473..b5bf328d 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -5,13 +5,6 @@ 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.toUint8Array(); // FIXME -}; - describe('Elliptic Curve Cryptography', function () { const elliptic_curves = openpgp.crypto.publicKey.elliptic; const key_data = { @@ -219,9 +212,9 @@ describe('Elliptic Curve Cryptography', function () { return ecdsa.verify( oid, hash, - {r: bin2bi(r), s: bin2bi(s)}, + {r: new Uint8Array(r), s: new Uint8Array(s)}, message, - bin2bi(pub) + new Uint8Array(pub) ); }; const secp256k1_dummy_value = new Uint8Array([ @@ -297,8 +290,8 @@ describe('Elliptic Curve Cryptography', function () { it('Sign and verify message', function () { const curve = elliptic_curves.get('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 => { @@ -321,9 +314,9 @@ describe('Elliptic Curve Cryptography', function () { curve.oid, cipher, hash, - bin2bi(ephemeral), + new Uint8Array(ephemeral), data, - bin2bi(priv), + new Uint8Array(priv), fingerprint ); }); diff --git a/test/general/packet.js b/test/general/packet.js index c614a633..8e924fff 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -204,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' + @@ -239,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' + @@ -304,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'; @@ -346,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' + @@ -367,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(); }); }); From 168a6b0bb8e3f5c40ec57cfe62173df345ec02e5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Thu, 22 Feb 2018 18:27:34 -0800 Subject: [PATCH 17/31] New probabilistic random number generation algorithms; UNTESTED --- src/crypto/public_key/prime.js | 135 +++++++++++++++++++++++++++++++++ src/crypto/public_key/rsa.js | 2 + src/crypto/random.js | 11 +-- src/type/mpi.js | 5 +- 4 files changed, 145 insertions(+), 8 deletions(-) create mode 100644 src/crypto/public_key/prime.js diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js new file mode 100644 index 00000000..75cec019 --- /dev/null +++ b/src/crypto/public_key/prime.js @@ -0,0 +1,135 @@ +// 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 primality testing + +/** + * @requires bn.js + * @requires crypto/random + * @module crypto/public_key/prime + */ + +import BN from 'bn.js'; +import random from '../random'; + +function randomProbablePrime(b) { + let n; + const min = new BN(1).shln(b-1); + do { + n = random.getRandomBN(min, min.shln(1)); + if (n.isEven()) { + n.iaddn(1); // force odd + } + } while (!isProbablePrime(n)); +// this.dAddOffset(2, 0); +// if (this.bitLength() > b) +// this.subTo(BigInteger.ONE.shiftLeft(b - 1), this); +} + +function isProbablePrime(n) { + if (!fermat(n)) { + return false; + } + if (!millerRabin(n)) { + return false; + } + 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. + */ +export function fermat(n, b) { + b = b || new BN(2); + return b.toRed(BN.mont(n)).redPow(n.subn(1)).cmpn(1) === 0; +} + + +// 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 +// TODO check this against jsbn's bnpMillerRabin +// TODO implement fixed base Miller-Rabin; for instance by writing a function that +// picks a number within the given range from a precomputed list of primes. + +/** + * Tests whether n is probably prime or not using the Miller-Rabin test. + * See HAC Remark 4.28. + */ +export function millerRabin(n, k, cb) { + var len = n.bitLength(); + var red = BN.mont(n); + var rone = new BN(1).toRed(red); + + if (!k) + k = Math.max(1, (len / 48) | 0); + + // Find d and s, (n - 1) = (2 ^ s) * d; + var n1 = n.subn(1); + for (var s = 0; !n1.testn(s); s++) {} + var d = n.shrn(s); + + var rn1 = n1.toRed(red); + + var prime = true; + for (; k > 0; k--) { + var a = random.getRandomBN(new BN(2), n1); + if (cb) + cb(a); + + var x = a.toRed(red).redPow(d); + if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) + continue; + + for (var i = 1; i < s; i++) { + x = x.redSqr(); + + if (x.cmp(rone) === 0) + return false; + if (x.cmp(rn1) === 0) + break; + } + + if (i === s) + return false; + } + + return prime; +}; diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 6427b359..2e86b7d5 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -20,6 +20,7 @@ /** * @requires bn.js * @requires asmcrypto.js + * @requires crypto/public_key/prime * @requires crypto/random * @requires config * @requires util @@ -31,6 +32,7 @@ import BN from 'bn.js'; import { RSA } from 'asmcrypto.js/src/rsa/exports-keygen'; import { RSA_RAW } from 'asmcrypto.js/src/rsa/exports-raw'; import { random as asmcrypto_random } from 'asmcrypto.js/src/random/exports'; +import prime from './prime'; import random from '../random'; import config from '../../config'; import util from '../../util'; diff --git a/src/crypto/random.js b/src/crypto/random.js index a97ca8c8..20e2b039 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -97,7 +97,7 @@ export default { }, /** - * Create a secure random MPI in specified range + * 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 @@ -107,18 +107,19 @@ export default { throw new Error('Illegal parameter value: max <= min'); } + let r; const modulus = max.sub(min); const length = modulus.byteLength(); - let r = new BN(this.getRandomBytes(length)); + // Using a while loop is necessary to avoid bias - while (r.cmp(modulus) >= 0) { + do { r = new BN(this.getRandomBytes(length)); - } + } while (r.cmp(modulus) >= 0); + return r.iadd(min); }, randomBuffer: new RandomBuffer() - }; /** diff --git a/src/type/mpi.js b/src/type/mpi.js index 03c6f517..811b9873 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -21,8 +21,7 @@ // - 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. @@ -67,7 +66,7 @@ MPI.prototype.read = function (bytes, endian='be') { } const bits = (bytes[0] << 8) | bytes[1]; - const bytelen = Math.ceil(bits / 8); + const bytelen = (bits + 7) >>> 3; const payload = bytes.subarray(2, 2 + bytelen); if (endian === 'le') { From 9943379cb769064edef5f6cd4911b6457538e4a5 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sat, 24 Feb 2018 22:00:20 -0500 Subject: [PATCH 18/31] Added Native RSA Key Generation --- src/crypto/public_key/index.js | 8 +++--- src/crypto/public_key/prime.js | 43 ++++++++++++++++++----------- src/crypto/public_key/rsa.js | 49 +++++++++++++++++++--------------- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/crypto/public_key/index.js b/src/crypto/public_key/index.js index ae4aac89..384dff82 100644 --- a/src/crypto/public_key/index.js +++ b/src/crypto/public_key/index.js @@ -7,16 +7,18 @@ */ /** @see module:crypto/public_key/rsa */ -import rsa from './rsa.js'; +import rsa from './rsa'; +import prime from './prime'; /** @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, + prime: prime, elgamal: elgamal, elliptic: elliptic, dsa: dsa diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js index 75cec019..a35743b9 100644 --- a/src/crypto/public_key/prime.js +++ b/src/crypto/public_key/prime.js @@ -15,7 +15,7 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// Algorithms for probabilistic primality testing +// Algorithms for probabilistic prime generation /** * @requires bn.js @@ -26,21 +26,32 @@ import BN from 'bn.js'; import random from '../random'; -function randomProbablePrime(b) { - let n; - const min = new BN(1).shln(b-1); - do { - n = random.getRandomBN(min, min.shln(1)); - if (n.isEven()) { - n.iaddn(1); // force odd +export default { + randomProbablePrime, isProbablePrime, fermat, millerRabin +}; + +function randomProbablePrime(bits, e) { + 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)) { + n.iaddn(2); + // If reached the maximum, go back to the minimum. + if (n.bitLength() > bits) { + n = n.mod(min.shln(1)).iadd(min); } - } while (!isProbablePrime(n)); -// this.dAddOffset(2, 0); -// if (this.bitLength() > b) -// this.subTo(BigInteger.ONE.shiftLeft(b - 1), this); + } + return n; } -function isProbablePrime(n) { +function isProbablePrime(n, e) { + if (e && !n.subn(1).gcd(e).eqn(1)) { + return false; + } if (!fermat(n)) { return false; } @@ -54,9 +65,9 @@ function isProbablePrime(n) { * Tests whether n is probably prime or not using Fermat's test with b = 2. * Fails if b^(n-1) mod n === 1. */ -export function fermat(n, b) { +function fermat(n, b) { b = b || new BN(2); - return b.toRed(BN.mont(n)).redPow(n.subn(1)).cmpn(1) === 0; + return b.toRed(BN.mont(n)).redPow(n.subn(1)).fromRed().cmpn(1) === 0; } @@ -93,7 +104,7 @@ export function fermat(n, b) { * Tests whether n is probably prime or not using the Miller-Rabin test. * See HAC Remark 4.28. */ -export function millerRabin(n, k, cb) { +function millerRabin(n, k, cb) { var len = n.bitLength(); var red = BN.mont(n); var rone = new BN(1).toRed(red); diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 2e86b7d5..3792f1ea 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -29,9 +29,7 @@ import BN from 'bn.js'; -import { RSA } from 'asmcrypto.js/src/rsa/exports-keygen'; import { RSA_RAW } from 'asmcrypto.js/src/rsa/exports-raw'; -import { random as asmcrypto_random } from 'asmcrypto.js/src/random/exports'; import prime from './prime'; import random from '../random'; import config from '../../config'; @@ -40,6 +38,13 @@ import util from '../../util'; const two = new BN(2); const zero = new BN(0); +// TODO use this is ../../encoding/base64.js and ./elliptic/{key,curve}.js +function b64toBN(base64url) { + const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); + const hex = util.hexstrdump(atob(base64)); + return new BN(hex, 16); +} + export default { /** Create signature * @param m message as BN @@ -189,25 +194,27 @@ export default { return key; } - // TODO use this is ../../encoding/base64.js and ./elliptic/{key,curve}.js - function b64toBN(base64url) { - const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); - const hex = util.hexstrdump(atob(base64)); - return new BN(hex, 16); - } + while (true) { + let p = prime.randomProbablePrime(B - (B >> 1), E); + let q = prime.randomProbablePrime(B >> 1, E); - // asmcrypto fallback - await asmcrypto_random.seed(await random.getRandomBytes(1024)); // FIXME how much randomness? - key = await RSA.generateKey(B, E.toArrayLike(Uint8Array)); - return { - n: new BN(key[0]), - e: new BN(key[1]), - d: new BN(key[2]), - q: new BN(key[3]), - p: new BN(key[4]), - // dq: new BN(key[5]), - // dp: new BN(key[6]), - u: new BN(key[7]) - }; + if (p.cmp(q) < 0) { + const t = p; + p = q; + q = t; + } + + const phi = p.subn(1).mul(q.subn(1)); + return { + n: p.mul(q), + e: E, + d: E.invm(phi), + q: q, + p: p, + // dq: d.mod(q.subn(1)), + // dp: d.mod(p.subn(1)), + u: p.invm(q) + }; + } } }; From 7a3a75a7df999fcbde3187fa35c86ae25e4c6cba Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 25 Feb 2018 00:11:29 -0500 Subject: [PATCH 19/31] JavaScript RSA using bn.js, without asmcrypto.js --- src/crypto/public_key/dsa.js | 1 - src/crypto/public_key/index.js | 2 - src/crypto/public_key/rsa.js | 72 +++++++++++++++++----------------- 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index eb2b453a..16a1fff0 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -53,7 +53,6 @@ export default { let r; let s; let t; - // TODO benchmark BN.mont vs BN.red const redp = new BN.red(p); const redq = new BN.red(q); const gred = g.toRed(redp); diff --git a/src/crypto/public_key/index.js b/src/crypto/public_key/index.js index 384dff82..927f0c52 100644 --- a/src/crypto/public_key/index.js +++ b/src/crypto/public_key/index.js @@ -8,7 +8,6 @@ /** @see module:crypto/public_key/rsa */ import rsa from './rsa'; -import prime from './prime'; /** @see module:crypto/public_key/elgamal */ import elgamal from './elgamal'; /** @see module:crypto/public_key/elliptic */ @@ -18,7 +17,6 @@ import dsa from './dsa'; export default { rsa: rsa, - prime: prime, elgamal: elgamal, elliptic: elliptic, dsa: dsa diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 3792f1ea..4cf30784 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -29,14 +29,12 @@ import BN from 'bn.js'; -import { RSA_RAW } from 'asmcrypto.js/src/rsa/exports-raw'; import prime from './prime'; import random from '../random'; import config from '../../config'; import util from '../../util'; const two = new BN(2); -const zero = new BN(0); // TODO use this is ../../encoding/base64.js and ./elliptic/{key,curve}.js function b64toBN(base64url) { @@ -54,11 +52,11 @@ export default { * @return BN */ sign: function(m, n, e, d) { - m = m.toArrayLike(Uint8Array); - n = n.toArrayLike(Uint8Array); - e = e.toArrayLike(Uint8Array); - d = d.toArrayLike(Uint8Array); - return RSA_RAW.sign(m, [n, e, d]); + if (n.cmp(m) <= 0) { + throw new Error('Data too large.'); + } + const nred = new BN.red(n); + return m.toRed(nred).redPow(d).toArrayLike(Uint8Array, 'be', n.byteLength()); }, /** @@ -69,10 +67,11 @@ export default { * @return BN */ verify: function(s, n, e) { - s = s.toArrayLike(Uint8Array); - n = n.toArrayLike(Uint8Array); - e = e.toArrayLike(Uint8Array); - return RSA_RAW.verify(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()); }, /** @@ -83,10 +82,11 @@ export default { * @return BN */ encrypt: function(m, n, e) { - m = m.toArrayLike(Uint8Array); - n = n.toArrayLike(Uint8Array); - e = e.toArrayLike(Uint8Array); - return RSA_RAW.encrypt(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()); }, /** @@ -101,37 +101,35 @@ export default { * @return {BN} The decrypted value of the message */ decrypt: function(m, n, e, d, p, q, u) { - let blinder = zero; - let unblinder = zero; + 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); - config.rsa_blinding = false; // FIXME + let blinder; + let unblinder; if (config.rsa_blinding) { - if (unblinder.bitLength() === n.bitLength()) { - unblinder = unblinder.sqr().mod(n); - } else { - unblinder = random.getRandomBN(two, n); - } - blinder = unblinder.toRed(nred).redInvm().redPow(e).fromRed(); - m = m.mul(blinder).mod(n); + unblinder = random.getRandomBN(two, n).toRed(nred); + blinder = unblinder.redInvm().redPow(e); + m = m.toRed(nred).redMul(blinder).fromRed(); } - const dq = d.mod(q.subn(1)).toArrayLike(Uint8Array); // d mod (q-1) - const dp = d.mod(p.subn(1)).toArrayLike(Uint8Array); // d mod (p-1) - const nn = n.toArrayLike(Uint8Array); - m = m.toArrayLike(Uint8Array); - e = e.toArrayLike(Uint8Array); - d = d.toArrayLike(Uint8Array); - q = q.toArrayLike(Uint8Array); - p = p.toArrayLike(Uint8Array); - u = u.toArrayLike(Uint8Array); - let result = new BN(RSA_RAW.decrypt(m, [nn, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice + 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(); + + let result = h.mul(p).add(mp).toRed(nred); if (config.rsa_blinding) { - result = result.mul(unblinder).mod(n); + result = result.redMul(unblinder); } - return result; + return result.toArrayLike(Uint8Array, 'be', n.byteLength()); }, /** From 2e9533582595e1d29fb515075e9eee37babd4948 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Sun, 25 Feb 2018 02:38:51 -0500 Subject: [PATCH 20/31] millerRabin set to 40 iterations; doc fixes --- src/crypto/crypto.js | 2 +- src/crypto/public_key/prime.js | 56 +++++++++++++++++++++++----------- src/crypto/public_key/rsa.js | 20 +++++++----- test/crypto/crypto.js | 7 +---- 4 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index d4526020..72680111 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -280,7 +280,7 @@ export default { return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); }); default: - throw new Error('Invalid public key encryption algorithm.'); + throw new Error('Invalid public key algorithm.'); } }, diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js index a35743b9..6165ee8e 100644 --- a/src/crypto/public_key/prime.js +++ b/src/crypto/public_key/prime.js @@ -15,7 +15,7 @@ // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// Algorithms for probabilistic prime generation +// Algorithms for probabilistic random prime generation /** * @requires bn.js @@ -30,7 +30,14 @@ export default { randomProbablePrime, isProbablePrime, fermat, millerRabin }; -function randomProbablePrime(bits, e) { +/** + * 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)); @@ -38,7 +45,7 @@ function randomProbablePrime(bits, e) { n.iaddn(1); // force odd } - while (!isProbablePrime(n, e)) { + while (!isProbablePrime(n, e, k)) { n.iaddn(2); // If reached the maximum, go back to the minimum. if (n.bitLength() > bits) { @@ -48,14 +55,21 @@ function randomProbablePrime(bits, e) { return n; } -function isProbablePrime(n, e) { +/** + * 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)) { + if (!millerRabin(n, k)) { return false; } return true; @@ -64,6 +78,9 @@ function isProbablePrime(n, e) { /** * 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); @@ -103,33 +120,38 @@ function fermat(n, b) { /** * 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} cb Optional callback function to call with random witnesses + * @return {boolean} */ function millerRabin(n, k, cb) { - var len = n.bitLength(); - var red = BN.mont(n); - var rone = new BN(1).toRed(red); + 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); // Find d and s, (n - 1) = (2 ^ s) * d; - var n1 = n.subn(1); - for (var s = 0; !n1.testn(s); s++) {} - var d = n.shrn(s); + const n1 = n.subn(1); + let s = 0; + while (!n1.testn(s)) { s++; } + const d = n.shrn(s); - var rn1 = n1.toRed(red); + const rn1 = n1.toRed(red); - var prime = true; for (; k > 0; k--) { - var a = random.getRandomBN(new BN(2), n1); + let a = random.getRandomBN(new BN(2), n1); if (cb) cb(a); - var x = a.toRed(red).redPow(d); + let x = a.toRed(red).redPow(d); if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) continue; - for (var i = 1; i < s; i++) { + let i; + for (i = 1; i < s; i++) { x = x.redSqr(); if (x.cmp(rone) === 0) @@ -142,5 +164,5 @@ function millerRabin(n, k, cb) { return false; } - return prime; + return true; }; diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 4cf30784..146a2461 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -19,7 +19,6 @@ /** * @requires bn.js - * @requires asmcrypto.js * @requires crypto/public_key/prime * @requires crypto/random * @requires config @@ -135,7 +134,10 @@ export default { /** * 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 + * @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; @@ -193,13 +195,13 @@ export default { } while (true) { - let p = prime.randomProbablePrime(B - (B >> 1), E); - let q = prime.randomProbablePrime(B >> 1, E); + // 40 iterations of the Miller-Rabin test + // See https://stackoverflow.com/a/6330138 for justification + let p = prime.randomProbablePrime(B - (B >> 1), E, 40); + let q = prime.randomProbablePrime(B >> 1, E, 40); if (p.cmp(q) < 0) { - const t = p; - p = q; - q = t; + [p, q] = [q, p]; } const phi = p.subn(1).mul(q.subn(1)); @@ -214,5 +216,7 @@ export default { u: p.invm(q) }; } - } + }, + + prime: prime }; diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 56dc2a6c..c77ef82b 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 AES_CFB = require('asmcrypto.js/asmcrypto.all.js').AES_CFB; const chai = require('chai'); chai.use(require('chai-as-promised')); @@ -297,11 +296,7 @@ describe('API functional testing', function() { const prefix = util.concatUint8Array([rndm, repeat]); const symmencData = crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false); - const symmencData2 = AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey); - - let decrypted = AES_CFB.decrypt(symmencData, symmKey); - decrypted = decrypted.subarray(crypto.cipher[algo].blockSize + 2, decrypted.length); - expect(util.Uint8Array2str(symmencData)).to.equal(util.Uint8Array2str(symmencData2)); + const decrypted = crypto.cfb.decrypt(algo, symmKey, symmencData, false); const text = util.Uint8Array2str(decrypted); expect(text).to.equal(plaintext); From 378fb857337b9e0f89271d95ede5ce13c6a37a97 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 26 Feb 2018 16:22:08 -0500 Subject: [PATCH 21/31] Optimizes getRandomBN, Brings back IE11 KeyOperation helper --- src/crypto/public_key/rsa.js | 21 +++++++++++++++++++-- src/crypto/random.js | 8 ++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 146a2461..2039374f 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -42,6 +42,21 @@ function b64toBN(base64url) { return new BN(hex, 16); } +// 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); + }; + }); + } + return keyObj; +} + export default { /** Create signature * @param m message as BN @@ -158,7 +173,8 @@ export default { name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify' } }; - keyPair = await webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']); + 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 = { @@ -176,7 +192,8 @@ export default { // export the generated keys as JsonWebKey (JWK) // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 - let jwk = await webCrypto.exportKey('jwk', keyPair.privateKey); + 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) { diff --git a/src/crypto/random.js b/src/crypto/random.js index 20e2b039..8ce6dce4 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -109,11 +109,15 @@ export default { let r; const modulus = max.sub(min); - const length = modulus.byteLength(); + const bits = modulus.bitLength(); + const bytes = modulus.byteLength(); // Using a while loop is necessary to avoid bias do { - r = new BN(this.getRandomBytes(length)); + r = new BN(this.getRandomBytes(bytes)); + if (r.bitLength() > bits) { + r.ishrn(r.bitLength() - bits); + } } while (r.cmp(modulus) >= 0); return r.iadd(min); From 3df0997f4daade6b882f4597b21f7fa2e101af21 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 26 Feb 2018 20:01:01 -0500 Subject: [PATCH 22/31] Cleanups --- src/crypto/public_key/dsa.js | 3 ++- src/crypto/public_key/elliptic/curves.js | 6 ++--- src/crypto/public_key/elliptic/ecdh.js | 6 ++--- src/crypto/public_key/elliptic/ecdsa.js | 16 ++++++++----- src/crypto/public_key/elliptic/eddsa.js | 29 ++++++++++++------------ src/crypto/public_key/elliptic/index.js | 4 ++-- src/crypto/signature.js | 21 +++++++++-------- src/packet/public_key.js | 2 +- src/type/mpi.js | 4 ++-- test/crypto/elliptic.js | 23 ++++++++++--------- test/general/x25519.js | 10 ++++---- 11 files changed, 66 insertions(+), 58 deletions(-) diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index 16a1fff0..b6455e45 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -85,7 +85,8 @@ export default { } break; } - return { r: r.fromRed(), s: s.fromRed() }; + return { r: r.toArrayLike(Uint8Array), + s: s.toArrayLike(Uint8Array) }; }, /* diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index ede97dfc..85f80439 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -167,7 +167,7 @@ Curve.prototype.genKeyPair = async function () { return new KeyPair(this.curve, keyPair); }; -function get(oid_or_name) { +function getCurve(oid_or_name) { let name; if (OID.prototype.isPrototypeOf(oid_or_name) && enums.curve[oid_or_name.toHex()]) { @@ -184,7 +184,7 @@ function get(oid_or_name) { } async function generate(curve) { - curve = get(curve); + curve = getCurve(curve); const keyPair = await curve.genKeyPair(); return { oid: curve.oid, @@ -202,7 +202,7 @@ function getPreferredHashAlgo(oid) { export default Curve; export { - curves, webCurves, nodeCurves, get, generate, getPreferredHashAlgo + curves, webCurves, nodeCurves, getCurve, generate, getPreferredHashAlgo }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 14217b1a..911a6a83 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -30,7 +30,7 @@ */ import BN from 'bn.js'; -import { get as curvesGet } from './curves'; +import { getCurve } from './curves'; import aes_kw from '../../aes_kw'; import cipher from '../../cipher'; import hash from '../../hash'; @@ -74,7 +74,7 @@ function kdf(hash_algo, X, length, param) { */ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); - const curve = curvesGet(oid); + const curve = getCurve(oid); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); cipher_algo = enums.read(enums.symmetric, cipher_algo); const v = await curve.genKeyPair(); @@ -102,7 +102,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { */ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); - const curve = curvesGet(oid); + const curve = getCurve(oid); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); cipher_algo = enums.read(enums.symmetric, cipher_algo); V = curve.keyFromPublic(V); diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 948ffe59..aa8907ba 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -26,7 +26,7 @@ import util from '../../../util'; import hash from '../../hash'; -import { get as curvesGet } from './curves'; +import { getCurve } from './curves'; /** * Sign a message using the provided key @@ -34,25 +34,29 @@ import { get as curvesGet } from './curves'; * @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: BN, s: BN}} Signature of the message + * @return {{r: Uint8Array, + s: Uint8Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = curvesGet(oid); + const curve = getCurve(oid); const key = curve.keyFromPrivate(d); - return key.sign(m, hash_algo); + const signature = await key.sign(m, hash_algo); + return { r: signature.r.toArrayLike(Uint8Array), + s: signature.s.toArrayLike(Uint8Array) }; } /** * Verifies if a signature is valid for a message * @param {module:type/oid} oid Elliptic curve object identifier * @param {enums.hash} hash_algo Hash algorithm used in the signature - * @param {{r: BN, s: BN}} signature Signature to verify + * @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 = curvesGet(oid); + const curve = getCurve(oid); const key = curve.keyFromPublic(Q); return key.verify(m, signature, hash_algo); } diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index a0fc467d..8d908313 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -26,41 +26,40 @@ import BN from 'bn.js'; import hash from '../../hash'; -import { get as curvesGet } from './curves'; +import { getCurve } from './curves'; /** * Sign a message using the provided key * @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 {BN} d Private key used to sign - * @return {{R: Array, S: Array}} Signature of the message + * @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 = curvesGet(oid); - const key = curve.keyFromSecret(d.toArray('be', 32)); + const curve = getCurve(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: signature.Rencoded(), S: signature.Sencoded() }; + return { R: new Uint8Array(signature.Rencoded()), + S: new Uint8Array(signature.Sencoded()) }; } /** * Verifies if a signature is valid for a message * @param {module:type/oid} oid Elliptic curve object identifier * @param {enums.hash} hash_algo Hash algorithm used in the signature - * @param {{R: BN, S: BN}} signature Signature to verify the message + * @param {{R: Uint8Array, + S: Uint8Array}} signature Signature to verify the message * @param {Uint8Array} m Message to verify - * @param {BN} Q Public key used to verify the message + * @param {Uint8Array} Q Public key used to verify the message * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - const curve = curvesGet(oid); - const key = curve.keyFromPublic(Q.toArray('be', 33)); - // EdDSA signature params are expected in little-endian format - return key.verify(m, { - R: signature.R.toArray('le', 32), - S: signature.S.toArray('le', 32) - }, hash_algo); + const curve = getCurve(oid); + const key = curve.keyFromPublic(Q); + return key.verify(m, signature, hash_algo); } export default { sign, verify }; diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index 9599fc97..4c1acb3c 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -25,11 +25,11 @@ * @module crypto/public_key/elliptic */ -import { get, generate, getPreferredHashAlgo } from './curves'; +import { getCurve, generate, getPreferredHashAlgo } from './curves'; import ecdsa from './ecdsa'; import eddsa from './eddsa'; import ecdh from './ecdh'; export default { - ecdh, ecdsa, eddsa, get, generate, getPreferredHashAlgo + ecdh, ecdsa, eddsa, getCurve, generate, getPreferredHashAlgo }; diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 32319a9b..aa33e984 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -55,8 +55,11 @@ export default { } case enums.publicKey.eddsa: { const oid = pub_MPIs[0]; - const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() }; - const Q = pub_MPIs[1].toBN(); + // 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: @@ -95,8 +98,8 @@ export default { const x = key_params[4].toBN(); const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); return util.concatUint8Array([ - util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), - util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) + util.Uint8Array2MPI(signature.r), + util.Uint8Array2MPI(signature.s) ]); } case enums.publicKey.elgamal: { @@ -107,17 +110,17 @@ export default { const d = key_params[2].toUint8Array(); const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); return util.concatUint8Array([ - util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), - util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) + util.Uint8Array2MPI(signature.r), + util.Uint8Array2MPI(signature.s) ]); } case enums.publicKey.eddsa: { const oid = key_params[0]; - const d = key_params[2].toBN(); + 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.Uint8Array2MPI(Uint8Array.from(signature.R)), - util.Uint8Array2MPI(Uint8Array.from(signature.S)) + util.Uint8Array2MPI(signature.R), + util.Uint8Array2MPI(signature.S) ]); } default: diff --git a/src/packet/public_key.js b/src/packet/public_key.js index a8dbf8a2..d7b8ad59 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -201,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 = crypto.publicKey.elliptic.getCurve(this.params[0]).name; } return result; }; diff --git a/src/type/mpi.js b/src/type/mpi.js index 811b9873..f5797200 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -93,8 +93,8 @@ MPI.prototype.byteLength = function () { return this.write().length - 2; }; -MPI.prototype.toUint8Array = function () { - return this.write().slice(2); +MPI.prototype.toUint8Array = function (endian, length) { + return this.write(endian, length).slice(2); }; MPI.prototype.fromUint8Array = function (bytes) { diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index b5bf328d..5af35682 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -1,5 +1,6 @@ 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')); @@ -136,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(elliptic_curves.getCurve(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(elliptic_curves.getCurve(openpgp.util.hex2bin(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 = elliptic_curves.getCurve(name); return curve.genKeyPair().then(keyPair => { expect(keyPair).to.exist; }); @@ -159,7 +160,7 @@ 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 = elliptic_curves.getCurve(name); expect(curve).to.exist; const keyPair = curve.keyFromPrivate(pair.priv); expect(keyPair).to.exist; @@ -170,19 +171,19 @@ describe('Elliptic Curve Cryptography', function () { done(); }); it('Signature verification', function (done) { - const curve = elliptic_curves.get('p256'); + const curve = elliptic_curves.getCurve('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 = elliptic_curves.getCurve('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 = elliptic_curves.getCurve('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); @@ -190,7 +191,7 @@ describe('Elliptic Curve Cryptography', function () { }); }); it('Shared secret generation', function (done) { - const curve = elliptic_curves.get('p256'); + const curve = elliptic_curves.getCurve('p256'); let key1 = curve.keyFromPrivate(key_data.p256.priv); let key2 = curve.keyFromPublic(signature_data.pub); const shared1 = openpgp.util.hexidump(key1.derive(key2)); @@ -212,7 +213,7 @@ describe('Elliptic Curve Cryptography', function () { return ecdsa.verify( oid, hash, - {r: new Uint8Array(r), s: new Uint8Array(s)}, + {r: new BN(r), s: new BN(s)}, message, new Uint8Array(pub) ); @@ -288,7 +289,7 @@ 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 = elliptic_curves.getCurve('p521'); return curve.genKeyPair().then(keyPair => { const keyPublic = new Uint8Array(keyPair.getPublic()); const keyPrivate = new Uint8Array(keyPair.getPrivate()); @@ -309,7 +310,7 @@ describe('Elliptic Curve Cryptography', function () { data = new Uint8Array(data); } return Promise.resolve().then(() => { - const curve = elliptic_curves.get(oid); + const curve = elliptic_curves.getCurve(oid); return elliptic_curves.ecdh.decrypt( curve.oid, cipher, diff --git a/test/general/x25519.js b/test/general/x25519.js index 0e6534c2..ac859525 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -235,9 +235,9 @@ describe('X25519 Cryptography', function () { const hi = firstKey.key; const primaryKey = hi.primaryKey; const subKey = hi.subKeys[0].subKey; - expect(primaryKey.params[0].toHex()).to.equal(elliptic.get('ed25519').oid.toHex()); + expect(primaryKey.params[0].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); expect(primaryKey.algorithm).to.equal('eddsa'); - expect(subKey.params[0].toHex()).to.equal(elliptic.get('curve25519').oid.toHex()); + expect(subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); expect(subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid @@ -255,9 +255,9 @@ describe('X25519 Cryptography', function () { }; return openpgp.generateKey(options).then(function (secondKey) { const bye = secondKey.key; - expect(bye.primaryKey.params[0].toHex()).to.equal(elliptic.get('ed25519').oid.toHex()); + expect(bye.primaryKey.params[0].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); expect(bye.primaryKey.algorithm).to.equal('eddsa'); - expect(bye.subKeys[0].subKey.params[0].toHex()).to.equal(elliptic.get('curve25519').oid.toHex()); + expect(bye.subKeys[0].subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid @@ -319,7 +319,7 @@ describe('X25519 Cryptography', function () { describe('Ed25519 Test Vectors from RFC8032', function () { // https://tools.ietf.org/html/rfc8032#section-7.1 const signature = openpgp.crypto.signature; - const curve = elliptic.get('ed25519'); + const curve = elliptic.getCurve('ed25519'); const util = openpgp.util; function testVector(vector) { const S = curve.keyFromSecret(vector.SECRET_KEY); From d418b4aa40ed812fdfa3e9dc9684fa36198ce15b Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Mon, 26 Feb 2018 22:05:40 -0500 Subject: [PATCH 23/31] MPI stores data as Uint8Array instead of BN --- src/type/mpi.js | 54 +++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/type/mpi.js b/src/type/mpi.js index f5797200..2b33e6d7 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -61,19 +61,13 @@ export default function MPI(data) { MPI.prototype.read = function (bytes, endian='be') { if (util.isString(bytes)) { bytes = util.str2Uint8Array(bytes); - } else { - bytes = util.copyUint8Array(bytes); } const bits = (bytes[0] << 8) | bytes[1]; const bytelen = (bits + 7) >>> 3; const payload = bytes.subarray(2, 2 + bytelen); - if (endian === 'le') { - payload.reverse(); - } - - this.fromUint8Array(payload); + this.fromUint8Array(payload, endian); return 2 + bytelen; }; @@ -86,40 +80,60 @@ MPI.prototype.read = function (bytes, endian='be') { * @return {Uint8Aray} mpi Byte representation */ MPI.prototype.write = function (endian, length) { - return util.Uint8Array2MPI(this.data.toArrayLike(Uint8Array, endian, length)); + return util.Uint8Array2MPI(this.toUint8Array(endian, length)); +}; + +MPI.prototype.bitLength = function () { + return (this.data.length - 1) * 8 + util.nbits(this.data[0]); }; MPI.prototype.byteLength = function () { - return this.write().length - 2; + return this.data.length; }; MPI.prototype.toUint8Array = function (endian, length) { - return this.write(endian, length).slice(2); + 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.fromUint8Array = function (bytes) { - this.data = new BN(bytes); +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.toString = function () { return util.Uint8Array2str(this.toUint8Array()); }; -MPI.prototype.fromString = function (str) { - this.data = new BN(util.str2Uint8Array(str)); +MPI.prototype.fromString = function (str, endian='be') { + this.fromUint8Array(util.str2Uint8Array(str), endian); }; MPI.prototype.toBN = function () { - return this.data.clone(); + return new BN(this.toUint8Array()); }; MPI.prototype.fromBN = function (bn) { - this.data = bn.clone(); + this.data = bn.toArrayLike(Uint8Array); }; MPI.fromClone = function (clone) { - clone.data.copy = BN.prototype.copy; - const bn = new BN(); - clone.data.copy(bn); - return new MPI(bn); + return new MPI(clone.data); }; From b518d27ff00b335c6aacd7e14cb17e5798dcf7f8 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 27 Feb 2018 00:23:51 -0800 Subject: [PATCH 24/31] Added Fixed-Base Miller-Rabin --- src/crypto/public_key/prime.js | 32 +++++++++++++++++++--------- src/crypto/public_key/rsa.js | 38 ++++++++++++++++------------------ 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js index 6165ee8e..ae94402e 100644 --- a/src/crypto/public_key/prime.js +++ b/src/crypto/public_key/prime.js @@ -69,6 +69,9 @@ function isProbablePrime(n, e, k) { 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; } @@ -87,6 +90,17 @@ function fermat(n, b) { 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. @@ -120,12 +134,12 @@ function fermat(n, b) { /** * 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} cb Optional callback function to call with random witnesses + * @param {BN} n Number to test + * @param {Integer} k Optional number of iterations of Miller-Rabin test + * @param {Function} w Optional function to generate potential witnesses * @return {boolean} */ -function millerRabin(n, k, cb) { +function millerRabin(n, k, rand) { const len = n.bitLength(); const red = BN.mont(n); const rone = new BN(1).toRed(red); @@ -133,18 +147,16 @@ function millerRabin(n, k, cb) { if (!k) k = Math.max(1, (len / 48) | 0); - // Find d and s, (n - 1) = (2 ^ s) * d; 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); - const rn1 = n1.toRed(red); - for (; k > 0; k--) { - let a = random.getRandomBN(new BN(2), n1); - if (cb) - cb(a); + let a = rand ? rand() : random.getRandomBN(new BN(2), n1); let x = a.toRed(red).redPow(d); if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 2039374f..48da90e7 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -211,28 +211,26 @@ export default { return key; } - while (true) { - // 40 iterations of the Miller-Rabin test - // See https://stackoverflow.com/a/6330138 for justification - let p = prime.randomProbablePrime(B - (B >> 1), E, 40); - let q = prime.randomProbablePrime(B >> 1, E, 40); + // RSA keygen fallback using 40 iterations of the Miller-Rabin test + // See https://stackoverflow.com/a/6330138 for justification + let p = prime.randomProbablePrime(B - (B >> 1), E, 40); + let q = prime.randomProbablePrime(B >> 1, E, 40); - if (p.cmp(q) < 0) { - [p, q] = [q, p]; - } - - const phi = p.subn(1).mul(q.subn(1)); - return { - n: p.mul(q), - e: E, - d: E.invm(phi), - q: q, - p: p, - // dq: d.mod(q.subn(1)), - // dp: d.mod(p.subn(1)), - u: p.invm(q) - }; + if (p.cmp(q) < 0) { + [p, q] = [q, p]; } + + const phi = p.subn(1).mul(q.subn(1)); + return { + n: p.mul(q), + e: E, + d: E.invm(phi), + q: q, + p: p, + // dq: d.mod(q.subn(1)), + // dp: d.mod(p.subn(1)), + u: p.invm(q) + }; }, prime: prime From d529edfddaf9db022d59f93d002d9517dd017dde Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 27 Feb 2018 13:04:45 -0800 Subject: [PATCH 25/31] Addresses @sanjanarajan's review comments --- src/crypto/public_key/dsa.js | 6 +++--- src/crypto/public_key/elgamal.js | 6 +++--- src/crypto/public_key/prime.js | 17 ++++++++--------- src/crypto/public_key/rsa.js | 5 +++-- src/crypto/random.js | 10 ++++++---- src/key.js | 22 ++++++++++++++-------- 6 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index b6455e45..efffd842 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -32,7 +32,7 @@ import random from '../random'; import config from '../../config'; import util from '../../util'; -const two = new BN(2); +const one = new BN(1); const zero = new BN(0); /* @@ -72,8 +72,8 @@ export default { // signature shall be recalculated. It is extremely unlikely that r = 0 // or s = 0 if signatures are generated properly. while (true) { - // TODO confirm this range - k = random.getRandomBN(two, q.subn(1)); // returns in [2, q-2] + // 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; diff --git a/src/crypto/public_key/elgamal.js b/src/crypto/public_key/elgamal.js index 2279620f..b48402e6 100644 --- a/src/crypto/public_key/elgamal.js +++ b/src/crypto/public_key/elgamal.js @@ -26,7 +26,7 @@ import BN from 'bn.js'; import random from '../random'; -const two = new BN(2); +const zero = new BN(0); export default { /* @@ -38,8 +38,8 @@ export default { const mred = m.toRed(redp); const gred = g.toRed(redp); const yred = y.toRed(redp); - // TODO confirm this range - const k = random.getRandomBN(two, p.subn(1)); // returns in [2, p-2] + // 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() diff --git a/src/crypto/public_key/prime.js b/src/crypto/public_key/prime.js index ae94402e..53339f1a 100644 --- a/src/crypto/public_key/prime.js +++ b/src/crypto/public_key/prime.js @@ -75,6 +75,8 @@ function isProbablePrime(n, e, k) { 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; } @@ -127,16 +129,13 @@ const lowprimes = [ // USE OR OTHER DEALINGS IN THE SOFTWARE. // Adapted on Jan 2018 from version 4.0.1 at https://github.com/indutny/miller-rabin -// TODO check this against jsbn's bnpMillerRabin -// TODO implement fixed base Miller-Rabin; for instance by writing a function that -// picks a number within the given range from a precomputed list of primes. /** * 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} w Optional function to generate potential witnesses + * @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) { @@ -159,16 +158,16 @@ function millerRabin(n, k, rand) { let a = rand ? rand() : random.getRandomBN(new BN(2), n1); let x = a.toRed(red).redPow(d); - if (x.cmp(rone) === 0 || x.cmp(rn1) === 0) + if (x.eq(rone) || x.eq(rn1)) continue; let i; for (i = 1; i < s; i++) { x = x.redSqr(); - if (x.cmp(rone) === 0) + if (x.eq(rone)) return false; - if (x.cmp(rn1) === 0) + if (x.eq(rn1)) break; } diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 48da90e7..51d9a95a 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -213,6 +213,7 @@ export default { // 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); @@ -225,10 +226,10 @@ export default { n: p.mul(q), e: E, d: E.invm(phi), - q: q, p: p, - // dq: d.mod(q.subn(1)), + q: q, // dp: d.mod(p.subn(1)), + // dq: d.mod(q.subn(1)), u: p.invm(q) }; }, diff --git a/src/crypto/random.js b/src/crypto/random.js index 8ce6dce4..99fb4089 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -108,17 +108,19 @@ export default { } let r; - const modulus = max.sub(min); - const bits = modulus.bitLength(); - const bytes = modulus.byteLength(); + const diff = max.sub(min); + const bits = diff.bitLength(); + const bytes = diff.byteLength(); // Using a while loop is necessary to avoid bias + // TODO consider using 64 extra random bits and taking mod + // Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf do { r = new BN(this.getRandomBytes(bytes)); if (r.bitLength() > bits) { r.ishrn(r.bitLength() - bits); } - } while (r.cmp(modulus) >= 0); + } while (r.cmp(diff) >= 0); return r.iadd(min); }, diff --git a/src/key.js b/src/key.js index 5bf3de9a..57c8b8b4 100644 --- a/src/key.js +++ b/src/key.js @@ -696,25 +696,31 @@ Key.prototype.verifyPrimaryUser = async function(keys) { return; } const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey }; - // TODO: fix the race condition, this should be a forEach - 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; } - // TODO break apart the .verify/isRevoked/isExpired checks - // 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) : From a79acf03866a3450db49f524323416a6f517f6bd Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 27 Feb 2018 13:10:52 -0800 Subject: [PATCH 26/31] Loopless getRandomBN using extra random bits --- src/crypto/random.js | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/crypto/random.js b/src/crypto/random.js index 99fb4089..320c70dd 100644 --- a/src/crypto/random.js +++ b/src/crypto/random.js @@ -107,22 +107,14 @@ export default { throw new Error('Illegal parameter value: max <= min'); } - let r; - const diff = max.sub(min); - const bits = diff.bitLength(); - const bytes = diff.byteLength(); + const modulus = max.sub(min); + const bytes = modulus.byteLength(); - // Using a while loop is necessary to avoid bias - // TODO consider using 64 extra random bits and taking mod + // 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 - do { - r = new BN(this.getRandomBytes(bytes)); - if (r.bitLength() > bits) { - r.ishrn(r.bitLength() - bits); - } - } while (r.cmp(diff) >= 0); - - return r.iadd(min); + const r = new BN(this.getRandomBytes(bytes + 8)); + return r.mod(modulus).add(min); }, randomBuffer: new RandomBuffer() From ecc38d0c6e1824a8546b18c7701f36cac423649d Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 27 Feb 2018 16:40:28 -0800 Subject: [PATCH 27/31] Adds worker tests for NIST P-256 and X25519 --- src/type/ecdh_symkey.js | 4 +- test/general/ecc_nist.js | 73 +++++++++++++++ test/general/x25519.js | 191 +++++++++++++++++++++------------------ 3 files changed, 179 insertions(+), 89 deletions(-) diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index ed0e9658..dfbf6a35 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -27,7 +27,7 @@ import util from '../util'; /** * @constructor */ -function ECDHSymmetricKey(data) { +export default function ECDHSymmetricKey(data) { if (typeof data === 'undefined') { data = new Uint8Array([]); } else if (util.isString(data)) { @@ -65,5 +65,3 @@ ECDHSymmetricKey.prototype.write = function () { ECDHSymmetricKey.fromClone = function (clone) { return new ECDHSymmetricKey(clone.data); }; - -export default ECDHSymmetricKey; 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/x25519.js b/test/general/x25519.js index ac859525..2e04c1d8 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,103 +219,120 @@ 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].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); - expect(primaryKey.algorithm).to.equal('eddsa'); - expect(subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); - 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].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); - expect(bye.primaryKey.algorithm).to.equal('eddsa'); - expect(bye.subKeys[0].subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); - 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].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); + expect(primaryKey.algorithm).to.equal('eddsa'); + expect(subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); + 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].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); + expect(bye.primaryKey.algorithm).to.equal('eddsa'); + expect(bye.subKeys[0].subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); + 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 () { From 746d5032e825f09f2bc02a3b2d9b055c2fff5287 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Tue, 27 Feb 2018 17:40:41 -0800 Subject: [PATCH 28/31] Provide our own randomness to elliptic --- src/crypto/public_key/elliptic/curves.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 85f80439..c87c5bec 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -157,8 +157,14 @@ Curve.prototype.genKeyPair = async function () { keyPair = await nodeGenKeyPair(this.name); return new KeyPair(this.curve, keyPair); } + + const options = { + entropy: util.Uint8Array2str(random.getRandomBytes(32)), // 32 = (192 + 64) / 8 + entropyEnc: 'string' + }; + // TODO provide randomness to elliptic here + const r = await this.curve.genKeyPair(options); 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 { From 7eef65926abcff2e7aa875a5ce1554e5d78945ab Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 28 Feb 2018 00:58:50 -0800 Subject: [PATCH 29/31] Simplifies elliptic/curve.js --- src/crypto/public_key/elliptic/curves.js | 74 +++++++++++------------- src/crypto/public_key/elliptic/ecdh.js | 6 +- src/crypto/public_key/elliptic/ecdsa.js | 8 +-- src/crypto/public_key/elliptic/eddsa.js | 6 +- src/crypto/public_key/elliptic/index.js | 4 +- src/packet/public_key.js | 2 +- src/type/oid.js | 19 +++++- test/crypto/elliptic.js | 20 +++---- test/general/x25519.js | 10 ++-- 9 files changed, 77 insertions(+), 72 deletions(-) diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index c87c5bec..002837e4 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -110,25 +110,39 @@ const curves = { } }; -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.hexstrdump(oid_or_name)]) { + this.name = util.hexstrdump(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 = new 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 @@ -149,48 +163,28 @@ 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 options = { - entropy: util.Uint8Array2str(random.getRandomBytes(32)), // 32 = (192 + 64) / 8 - entropyEnc: 'string' - }; - // TODO provide randomness to elliptic here - const r = await this.curve.genKeyPair(options); - 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() }; + if (!keyPair || !keyPair.priv) { + // elliptic fallback + const r = await this.curve.genKeyPair({ entropy: util.Uint8Array2str(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 getCurve(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 = getCurve(curve); + curve = new Curve(curve); const keyPair = await curve.genKeyPair(); return { oid: curve.oid, @@ -205,10 +199,8 @@ function getPreferredHashAlgo(oid) { return curves[enums.write(enums.curve, oid.toHex())].hash; } -export default Curve; - export { - curves, webCurves, nodeCurves, getCurve, generate, getPreferredHashAlgo + curves, webCurves, nodeCurves, generate, getPreferredHashAlgo }; diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 911a6a83..f1fcd886 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -30,7 +30,7 @@ */ import BN from 'bn.js'; -import { getCurve } from './curves'; +import Curve from './curves'; import aes_kw from '../../aes_kw'; import cipher from '../../cipher'; import hash from '../../hash'; @@ -74,7 +74,7 @@ function kdf(hash_algo, X, length, param) { */ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); - const curve = getCurve(oid); + const curve = new Curve(oid); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); cipher_algo = enums.read(enums.symmetric, cipher_algo); const v = await curve.genKeyPair(); @@ -102,7 +102,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { */ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) { fingerprint = util.hex2Uint8Array(fingerprint); - const curve = getCurve(oid); + const curve = new Curve(oid); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); cipher_algo = enums.read(enums.symmetric, cipher_algo); V = curve.keyFromPublic(V); diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index aa8907ba..c9b1549d 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -18,15 +18,13 @@ // Implementation of ECDSA following RFC6637 for Openpgpjs /** - * @requires util * @requires crypto/hash * @requires crypto/public_key/elliptic/curves * @module crypto/public_key/elliptic/ecdsa */ -import util from '../../../util'; import hash from '../../hash'; -import { getCurve } from './curves'; +import Curve from './curves'; /** * Sign a message using the provided key @@ -38,7 +36,7 @@ import { getCurve } from './curves'; s: Uint8Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = getCurve(oid); + const curve = new Curve(oid); const key = curve.keyFromPrivate(d); const signature = await key.sign(m, hash_algo); return { r: signature.r.toArrayLike(Uint8Array), @@ -56,7 +54,7 @@ async function sign(oid, hash_algo, m, d) { * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - const curve = getCurve(oid); + const curve = new Curve(oid); const key = curve.keyFromPublic(Q); return key.verify(m, signature, hash_algo); } diff --git a/src/crypto/public_key/elliptic/eddsa.js b/src/crypto/public_key/elliptic/eddsa.js index 8d908313..4db75099 100644 --- a/src/crypto/public_key/elliptic/eddsa.js +++ b/src/crypto/public_key/elliptic/eddsa.js @@ -26,7 +26,7 @@ import BN from 'bn.js'; import hash from '../../hash'; -import { getCurve } from './curves'; +import Curve from './curves'; /** * Sign a message using the provided key @@ -38,7 +38,7 @@ import { getCurve } from './curves'; S: Uint8Array}} Signature of the message */ async function sign(oid, hash_algo, m, d) { - const curve = getCurve(oid); + 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 @@ -57,7 +57,7 @@ async function sign(oid, hash_algo, m, d) { * @return {Boolean} */ async function verify(oid, hash_algo, signature, m, Q) { - const curve = getCurve(oid); + const curve = new Curve(oid); const key = curve.keyFromPublic(Q); return key.verify(m, signature, hash_algo); } diff --git a/src/crypto/public_key/elliptic/index.js b/src/crypto/public_key/elliptic/index.js index 4c1acb3c..cffe5dc4 100644 --- a/src/crypto/public_key/elliptic/index.js +++ b/src/crypto/public_key/elliptic/index.js @@ -25,11 +25,11 @@ * @module crypto/public_key/elliptic */ -import { getCurve, generate, getPreferredHashAlgo } from './curves'; +import Curve, { generate, getPreferredHashAlgo } from './curves'; import ecdsa from './ecdsa'; import eddsa from './eddsa'; import ecdh from './ecdh'; export default { - ecdh, ecdsa, eddsa, getCurve, generate, getPreferredHashAlgo + Curve, ecdh, ecdsa, eddsa, generate, getPreferredHashAlgo }; diff --git a/src/packet/public_key.js b/src/packet/public_key.js index d7b8ad59..ba51f487 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -201,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.getCurve(this.params[0]).name; + result.curve = this.params[0].getName(); } return result; }; diff --git a/src/type/oid.js b/src/type/oid.js index 23e80d40..a8394874 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -16,14 +16,16 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * Wrapper to an OID value
+ * Wrapper to an OID value *
* An object identifier type from {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. * @requires util + * @requires enums * @module type/oid */ -import util from '../util.js'; +import util from '../util'; +import enums from '../enums'; /** * @constructor @@ -73,6 +75,19 @@ OID.prototype.toHex = function() { return util.hexstrdump(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; diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 5af35682..5f8c4b35 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -137,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.getCurve(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.getCurve(openpgp.util.hex2bin(oid))).to.exist; + expect(new elliptic_curves.Curve(openpgp.util.hex2bin(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.getCurve(name); + const curve = new elliptic_curves.Curve(name); return curve.genKeyPair().then(keyPair => { expect(keyPair).to.exist; }); @@ -160,7 +160,7 @@ 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.getCurve(name); + const curve = new elliptic_curves.Curve(name); expect(curve).to.exist; const keyPair = curve.keyFromPrivate(pair.priv); expect(keyPair).to.exist; @@ -171,19 +171,19 @@ describe('Elliptic Curve Cryptography', function () { done(); }); it('Signature verification', function (done) { - const curve = elliptic_curves.getCurve('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.getCurve('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.getCurve('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); @@ -191,7 +191,7 @@ describe('Elliptic Curve Cryptography', function () { }); }); it('Shared secret generation', function (done) { - const curve = elliptic_curves.getCurve('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)); @@ -289,7 +289,7 @@ describe('Elliptic Curve Cryptography', function () { .to.eventually.be.true.notify(done); }); it('Sign and verify message', function () { - const curve = elliptic_curves.getCurve('p521'); + const curve = new elliptic_curves.Curve('p521'); return curve.genKeyPair().then(keyPair => { const keyPublic = new Uint8Array(keyPair.getPublic()); const keyPrivate = new Uint8Array(keyPair.getPrivate()); @@ -310,7 +310,7 @@ describe('Elliptic Curve Cryptography', function () { data = new Uint8Array(data); } return Promise.resolve().then(() => { - const curve = elliptic_curves.getCurve(oid); + const curve = new elliptic_curves.Curve(oid); return elliptic_curves.ecdh.decrypt( curve.oid, cipher, diff --git a/test/general/x25519.js b/test/general/x25519.js index 2e04c1d8..1d841ee2 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -238,9 +238,9 @@ describe('X25519 Cryptography', function () { const hi = firstKey.key; const primaryKey = hi.primaryKey; const subKey = hi.subKeys[0].subKey; - expect(primaryKey.params[0].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); + expect(primaryKey.params[0].getName()).to.equal("ed25519"); expect(primaryKey.algorithm).to.equal('eddsa'); - expect(subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); + expect(subKey.params[0].getName()).to.equal('curve25519'); expect(subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid @@ -258,9 +258,9 @@ describe('X25519 Cryptography', function () { }; return openpgp.generateKey(options).then(function (secondKey) { const bye = secondKey.key; - expect(bye.primaryKey.params[0].toHex()).to.equal(elliptic.getCurve('ed25519').oid.toHex()); + expect(bye.primaryKey.params[0].getName()).to.equal('ed25519'); expect(bye.primaryKey.algorithm).to.equal('eddsa'); - expect(bye.subKeys[0].subKey.params[0].toHex()).to.equal(elliptic.getCurve('curve25519').oid.toHex()); + expect(bye.subKeys[0].subKey.params[0].getName()).to.equal('curve25519'); expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh'); // Self Certificate is valid @@ -338,7 +338,7 @@ describe('X25519 Cryptography', function () { describe('Ed25519 Test Vectors from RFC8032', function () { // https://tools.ietf.org/html/rfc8032#section-7.1 const signature = openpgp.crypto.signature; - const curve = elliptic.getCurve('ed25519'); + const curve = new elliptic.Curve('ed25519'); const util = openpgp.util; function testVector(vector) { const S = curve.keyFromSecret(vector.SECRET_KEY); From 9275119dbc3f5fab9393c6630e27b09740558789 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 28 Feb 2018 14:13:27 -0800 Subject: [PATCH 30/31] Enables JSDoc Markdown Plugin --- .jsdocrc.js | 3 + Gruntfile.js | 1 + src/crypto/hash/sha.js | 1472 ----------------- src/enums.js | 53 +- src/key.js | 6 +- src/packet/compressed.js | 7 +- src/packet/literal.js | 9 +- src/packet/marker.js | 12 +- src/packet/one_pass_signature.js | 9 +- src/packet/public_key.js | 4 +- .../public_key_encrypted_session_key.js | 7 +- src/packet/secret_key.js | 4 +- src/packet/signature.js | 4 +- .../sym_encrypted_integrity_protected.js | 5 +- src/packet/sym_encrypted_session_key.js | 7 +- src/packet/symmetrically_encrypted.js | 9 +- src/packet/user_attribute.js | 6 +- src/packet/userid.js | 4 +- src/type/ecdh_symkey.js | 4 +- src/type/kdf_params.js | 4 +- src/type/keyid.js | 5 +- src/type/oid.js | 5 +- src/type/s2k.js | 5 +- 23 files changed, 100 insertions(+), 1545 deletions(-) create mode 100644 .jsdocrc.js delete mode 100644 src/crypto/hash/sha.js 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 ca6abf05..913eb952 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -181,6 +181,7 @@ module.exports = function(grunt) { dist: { src: ['README.md', 'src'], options: { + configure: '.jsdocrc.js', destination: 'doc', recurse: true } 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/enums.js b/src/enums.js index ce331d7f..74799d4f 100644 --- a/src/enums.js +++ b/src/enums.js @@ -192,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 @@ -231,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 @@ -240,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 @@ -254,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 @@ -263,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/key.js b/src/key.js index 57c8b8b4..15b68c76 100644 --- a/src/key.js +++ b/src/key.js @@ -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 */ 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..5da18f32 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 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 ba51f487..f2898f61 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 diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index f4d5e90c..41978c7b 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 diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 85ce93d4..b91ec6e3 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 diff --git a/src/packet/signature.js b/src/packet/signature.js index 1baf0a7f..5fa38c08 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 diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 56d0ce50..f54f1bfc 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -16,9 +16,8 @@ // 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 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..0281609c 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 diff --git a/src/packet/userid.js b/src/packet/userid.js index 68ff8958..adf49219 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 diff --git a/src/type/ecdh_symkey.js b/src/type/ecdh_symkey.js index dfbf6a35..28e6e214 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -16,8 +16,8 @@ // 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 */ diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 82598702..d8e60ea4 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -16,8 +16,8 @@ // 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 */ diff --git a/src/type/keyid.js b/src/type/keyid.js index b256a3bb..5214f6ba 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 diff --git a/src/type/oid.js b/src/type/oid.js index a8394874..af488a6d 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -17,8 +17,9 @@ /** * Wrapper to an OID value - *
- * An object identifier type from {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. + * + * An object identifier type from + * {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. * @requires util * @requires enums * @module type/oid diff --git a/src/type/s2k.js b/src/type/s2k.js index 74aa0f60..61d358af 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 From 9e1236c04c189c8e9725482e91348037363b8bd1 Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 28 Feb 2018 15:49:41 -0800 Subject: [PATCH 31/31] Modernizes util.js --- src/crypto/hash/index.js | 4 +- src/crypto/hash/md5.js | 4 +- src/crypto/pkcs1.js | 4 +- src/crypto/public_key/dsa.js | 8 +- src/crypto/public_key/elliptic/curves.js | 34 +- src/crypto/public_key/elliptic/ecdh.js | 6 +- src/crypto/public_key/elliptic/key.js | 32 +- src/crypto/public_key/rsa.js | 20 +- src/crypto/signature.js | 20 +- src/hkp.js | 1 + src/key.js | 4 +- src/message.js | 4 +- src/packet/clone.js | 1 + src/packet/literal.js | 8 +- src/packet/public_key.js | 8 +- .../public_key_encrypted_session_key.js | 8 +- src/packet/secret_key.js | 4 +- src/packet/signature.js | 38 +-- .../sym_encrypted_integrity_protected.js | 4 +- src/packet/user_attribute.js | 4 +- src/packet/userid.js | 4 +- src/type/ecdh_symkey.js | 2 +- src/type/keyid.js | 8 +- src/type/mpi.js | 8 +- src/type/oid.js | 10 +- src/type/s2k.js | 4 +- src/util.js | 307 ++++++++---------- test/crypto/aes_kw.js | 10 +- test/crypto/cipher/aes.js | 22 +- test/crypto/cipher/blowfish.js | 12 +- test/crypto/cipher/cast5.js | 10 +- test/crypto/cipher/des.js | 30 +- test/crypto/cipher/twofish.js | 36 +- test/crypto/crypto.js | 24 +- test/crypto/elliptic.js | 12 +- test/crypto/hash/md5.js | 12 +- test/crypto/hash/ripemd.js | 8 +- test/crypto/hash/sha.js | 20 +- test/general/oid.js | 6 +- test/general/signature.js | 4 +- test/general/x25519.js | 22 +- 41 files changed, 369 insertions(+), 418 deletions(-) diff --git a/src/crypto/hash/index.js b/src/crypto/hash/index.js index e62823b2..2cea7162 100644 --- a/src/crypto/hash/index.js +++ b/src/crypto/hash/index.js @@ -30,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'))); }; } @@ -51,7 +51,7 @@ 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), 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/pkcs1.js b/src/crypto/pkcs1.js index 54ac6171..38dc0671 100644 --- a/src/crypto/pkcs1.js +++ b/src/crypto/pkcs1.js @@ -125,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'); } @@ -155,7 +155,7 @@ export default { PS + String.fromCharCode(0x00) + T; - return util.hexstrdump(EM); + return util.str_to_hex(EM); } } }; diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index efffd842..ee1c5e16 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -63,9 +63,9 @@ export default { // truncated) hash function result is treated as a number and used // directly in the DSA signature algorithm. const h = new BN( - util.str2Uint8Array( + util.str_to_Uint8Array( util.getLeftNBits( - util.Uint8Array2str(hash.digest(hash_algo, m)), q.bitLength()))); + 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 @@ -105,9 +105,9 @@ export default { const redp = new BN.red(p); const redq = new BN.red(q); const h = new BN( - util.str2Uint8Array( + util.str_to_Uint8Array( util.getLeftNBits( - util.Uint8Array2str(hash.digest(hash_algo, m)), q.bitLength()))); + 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"); diff --git a/src/crypto/public_key/elliptic/curves.js b/src/crypto/public_key/elliptic/curves.js index 002837e4..cf72e1a6 100644 --- a/src/crypto/public_key/elliptic/curves.js +++ b/src/crypto/public_key/elliptic/curves.js @@ -19,7 +19,10 @@ /** * @requires bn.js + * @requires elliptic * @requires crypto/public_key/elliptic/key + * @requires crypto/random + * @requires type/oid * @requires enums * @requires util * @module crypto/public_key/elliptic/curve @@ -32,7 +35,6 @@ 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(); @@ -54,7 +56,7 @@ if (nodeCrypto) { 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, @@ -63,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, @@ -72,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, @@ -81,32 +83,32 @@ 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]) } }; @@ -116,8 +118,8 @@ export default function Curve(oid_or_name, params) { 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.hexstrdump(oid_or_name)]) { - this.name = util.hexstrdump(oid_or_name); // by oid string + } 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'); } @@ -172,7 +174,7 @@ Curve.prototype.genKeyPair = async function () { if (!keyPair || !keyPair.priv) { // elliptic fallback - const r = await this.curve.genKeyPair({ entropy: util.Uint8Array2str(random.getRandomBytes(32)) }); + 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() }; @@ -220,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 f1fcd886..315492d8 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -46,7 +46,7 @@ function buildEcdhParam(public_algo, oid, cipher_algo, hash_algo, fingerprint) { oid.write(), new Uint8Array([public_algo]), kdf_params.write(), - util.str2Uint8Array("Anonymous Sender "), + util.str_to_Uint8Array("Anonymous Sender "), fingerprint ]); } @@ -73,7 +73,7 @@ function kdf(hash_algo, X, length, param) { * @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); cipher_algo = enums.read(enums.symmetric, cipher_algo); @@ -101,7 +101,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, 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); cipher_algo = enums.read(enums.symmetric, cipher_algo); diff --git a/src/crypto/public_key/elliptic/key.js b/src/crypto/public_key/elliptic/key.js index e2d6ae66..b728b99b 100644 --- a/src/crypto/public_key/elliptic/key.js +++ b/src/crypto/public_key/elliptic/key.js @@ -23,7 +23,6 @@ * @requires crypto/hash * @requires util * @requires enums - * @requires encoding/base64 * @requires jwk-to-pem * @requires asn1.js * @module crypto/public_key/elliptic/key @@ -34,7 +33,6 @@ 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 nodeCrypto = util.getNodeCrypto(); @@ -118,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" }, @@ -158,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" }, @@ -186,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" }, @@ -215,8 +223,8 @@ async function nodeVerify(curve, hash_algo, { r, s }, message, publicKey) { { "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/rsa.js b/src/crypto/public_key/rsa.js index 51d9a95a..baae76ca 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -26,22 +26,12 @@ * @module crypto/public_key/rsa */ - import BN from 'bn.js'; import prime from './prime'; import random from '../random'; import config from '../../config'; import util from '../../util'; -const two = new BN(2); - -// TODO use this is ../../encoding/base64.js and ./elliptic/{key,curve}.js -function b64toBN(base64url) { - const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/'); - const hex = util.hexstrdump(atob(base64)); - return new BN(hex, 16); -} - // Helper for IE11 KeyOperation objects function promisifyIE11Op(keyObj, err) { if (typeof keyObj.then !== 'function') { // IE11 KeyOperation @@ -127,7 +117,7 @@ export default { let blinder; let unblinder; if (config.rsa_blinding) { - unblinder = random.getRandomBN(two, n).toRed(nred); + unblinder = random.getRandomBN(new BN(2), n).toRed(nred); blinder = unblinder.redInvm().redPow(e); m = m.toRed(nred).redMul(blinder).fromRed(); } @@ -202,11 +192,11 @@ export default { // map JWK parameters to BN key = {}; - key.n = b64toBN(jwk.n); + key.n = new BN(util.b64_to_Uint8Array(jwk.n)); key.e = E; - key.d = b64toBN(jwk.d); - key.p = b64toBN(jwk.p); - key.q = b64toBN(jwk.q); + 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; } diff --git a/src/crypto/signature.js b/src/crypto/signature.js index aa33e984..28ed2d92 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -35,8 +35,8 @@ export default { 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.Uint8Array2str(data), n.byteLength()); - return util.hexidump(EM) === EM2; + const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array_to_str(data), n.byteLength()); + return util.Uint8Array_to_hex(EM) === EM2; } case enums.publicKey.dsa: { const r = msg_MPIs[0].toBN(); @@ -86,10 +86,10 @@ export default { const n = key_params[0].toBN(); const e = key_params[1].toBN(); const d = key_params[2].toBN(); - data = util.Uint8Array2str(data); + 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.Uint8Array2MPI(signature); + return util.Uint8Array_to_MPI(signature); } case enums.publicKey.dsa: { const p = key_params[0].toBN(); @@ -98,8 +98,8 @@ export default { const x = key_params[4].toBN(); const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); return util.concatUint8Array([ - util.Uint8Array2MPI(signature.r), - util.Uint8Array2MPI(signature.s) + util.Uint8Array_to_MPI(signature.r), + util.Uint8Array_to_MPI(signature.s) ]); } case enums.publicKey.elgamal: { @@ -110,8 +110,8 @@ export default { const d = key_params[2].toUint8Array(); const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); return util.concatUint8Array([ - util.Uint8Array2MPI(signature.r), - util.Uint8Array2MPI(signature.s) + util.Uint8Array_to_MPI(signature.r), + util.Uint8Array_to_MPI(signature.s) ]); } case enums.publicKey.eddsa: { @@ -119,8 +119,8 @@ export default { 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.Uint8Array2MPI(signature.R), - util.Uint8Array2MPI(signature.S) + util.Uint8Array_to_MPI(signature.R), + util.Uint8Array_to_MPI(signature.S) ]); } default: 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 15b68c76..cbf7d99c 100644 --- a/src/key.js +++ b/src/key.js @@ -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; @@ -1305,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/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/literal.js b/src/packet/literal.js index 5da18f32..79d08685 100644 --- a/src/packet/literal.js +++ b/src/packet/literal.js @@ -50,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); }; /** @@ -60,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'); }; @@ -114,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)); @@ -129,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/public_key.js b/src/packet/public_key.js index f2898f61..73b7366e 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -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,16 +178,16 @@ 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 algo = enums.write(enums.publicKey, this.algorithm); const paramCount = crypto.getPubKeyParamTypes(algo).length; for (let i = 0; i < paramCount; i++) { 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; }; diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 41978c7b..5342b649 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -103,9 +103,9 @@ 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; const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); @@ -136,13 +136,13 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { let decoded; if (algo === enums.publicKey.ecdh) { decoded = crypto.pkcs5.decode(result.toString()); - checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); + checksum = util.readNumber(util.str_to_Uint8Array(decoded.substr(decoded.length - 2))); } else { 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'); diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index b91ec6e3..dc9d7a50 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -76,9 +76,9 @@ 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."); diff --git a/src/packet/signature.js b/src/packet/signature.js index 5fa38c08..b1449a17 100644 --- a/src/packet/signature.js +++ b/src/packet/signature.js @@ -280,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) { @@ -300,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)); } @@ -459,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; @@ -482,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 @@ -490,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 @@ -498,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 @@ -517,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: diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index f54f1bfc..25a9075d 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -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.'); diff --git a/src/packet/user_attribute.js b/src/packet/user_attribute.js index 0281609c..eaabdcbf 100644 --- a/src/packet/user_attribute.js +++ b/src/packet/user_attribute.js @@ -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 adf49219..95bde6ba 100644 --- a/src/packet/userid.js +++ b/src/packet/userid.js @@ -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 28e6e214..31599aaa 100644 --- a/src/type/ecdh_symkey.js +++ b/src/type/ecdh_symkey.js @@ -31,7 +31,7 @@ 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/keyid.js b/src/type/keyid.js index 5214f6ba..1b75bb9a 100644 --- a/src/type/keyid.js +++ b/src/type/keyid.js @@ -41,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) { @@ -76,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 2b33e6d7..56957c33 100644 --- a/src/type/mpi.js +++ b/src/type/mpi.js @@ -60,7 +60,7 @@ export default function MPI(data) { */ 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]; @@ -80,7 +80,7 @@ MPI.prototype.read = function (bytes, endian='be') { * @return {Uint8Aray} mpi Byte representation */ MPI.prototype.write = function (endian, length) { - return util.Uint8Array2MPI(this.toUint8Array(endian, length)); + return util.Uint8Array_to_MPI(this.toUint8Array(endian, length)); }; MPI.prototype.bitLength = function () { @@ -119,11 +119,11 @@ MPI.prototype.fromUint8Array = function (bytes, endian='be') { }; MPI.prototype.toString = function () { - return util.Uint8Array2str(this.toUint8Array()); + return util.Uint8Array_to_str(this.toUint8Array()); }; MPI.prototype.fromString = function (str, endian='be') { - this.fromUint8Array(util.str2Uint8Array(str), endian); + this.fromUint8Array(util.str_to_Uint8Array(str), endian); }; MPI.prototype.toBN = function () { diff --git a/src/type/oid.js b/src/type/oid.js index af488a6d..fabbbe0e 100644 --- a/src/type/oid.js +++ b/src/type/oid.js @@ -37,9 +37,9 @@ function 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; } @@ -53,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; } } @@ -65,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); }; /** @@ -73,7 +73,7 @@ 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); }; /** diff --git a/src/type/s2k.js b/src/type/s2k.js index 61d358af..1493b18f 100644 --- a/src/type/s2k.js +++ b/src/type/s2k.js @@ -84,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) { @@ -140,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 dbb4c9e6..401690b9 100644 --- a/src/util.js +++ b/src/util.js @@ -99,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 ""; } @@ -145,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)); @@ -155,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; } @@ -184,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 @@ -210,130 +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 {Uint8Array} MPI-formatted Uint8Array - */ - Uint8Array2MPI: 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]); - }, - /** * 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; @@ -347,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'); } @@ -363,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'); } @@ -421,12 +363,12 @@ 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); } }, @@ -442,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 @@ -450,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; @@ -461,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); }, /** 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 c77ef82b..0a59504d 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -231,7 +231,7 @@ 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 () { @@ -256,7 +256,7 @@ describe('API functional testing', function() { 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(); @@ -280,8 +280,8 @@ describe('API functional testing', function() { function testCFB(plaintext, resync) { symmAlgos.forEach(function(algo) { const symmKey = crypto.generateSessionKey(algo); - const symmencData = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, util.str2Uint8Array(plaintext), symmKey, resync); - const text = util.Uint8Array2str(crypto.cfb.decrypt(algo, symmKey, symmencData, resync)); + 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); }); } @@ -295,10 +295,10 @@ describe('API functional testing', function() { const repeat = new Uint8Array([rndm[rndm.length - 2], rndm[rndm.length - 1]]); const prefix = util.concatUint8Array([rndm, repeat]); - const symmencData = crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false); + const symmencData = crypto.cfb.encrypt(rndm, algo, util.str_to_Uint8Array(plaintext), symmKey, false); const decrypted = crypto.cfb.decrypt(algo, symmKey, symmencData, false); - const text = util.Uint8Array2str(decrypted); + const text = util.Uint8Array_to_str(decrypted); expect(text).to.equal(plaintext); } }); @@ -312,11 +312,11 @@ describe('API functional testing', function() { const iv = crypto.random.getRandomValues(new Uint8Array(crypto.gcm.ivLength)); return crypto.gcm.encrypt( - algo, util.str2Uint8Array(plaintext), key, iv + 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); }); }); @@ -372,7 +372,7 @@ describe('API functional testing', function() { }); it('Asymmetric using RSA with eme_pkcs1 padding', function () { - const symmKey = util.Uint8Array2str(crypto.generateSessionKey('aes256')); + 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( @@ -383,7 +383,7 @@ describe('API functional testing', function() { 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 = crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength()); expect(result).to.equal(symmKey); @@ -392,7 +392,7 @@ describe('API functional testing', function() { }); it('Asymmetric using Elgamal with eme_pkcs1 padding', function () { - const symmKey = util.Uint8Array2str(crypto.generateSessionKey('aes256')); + 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); @@ -404,7 +404,7 @@ describe('API functional testing', function() { 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 = crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength()); expect(result).to.equal(symmKey); diff --git a/test/crypto/elliptic.js b/test/crypto/elliptic.js index 5f8c4b35..97f9a2fd 100644 --- a/test/crypto/elliptic.js +++ b/test/crypto/elliptic.js @@ -144,7 +144,7 @@ describe('Elliptic Curve Cryptography', function () { it('Creating curve from oid', function (done) { const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A']; oids.forEach(function (oid) { - expect(new elliptic_curves.Curve(openpgp.util.hex2bin(oid))).to.exist; + expect(new elliptic_curves.Curve(openpgp.util.hex_to_str(oid))).to.exist; }); done(); }); @@ -166,7 +166,7 @@ describe('Elliptic Curve Cryptography', function () { 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(); }); @@ -194,10 +194,10 @@ describe('Elliptic Curve Cryptography', function () { 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(); }); @@ -205,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); } @@ -305,7 +305,7 @@ 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); } 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/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/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 1d841ee2..9233bb4f 100644 --- a/test/general/x25519.js +++ b/test/general/x25519.js @@ -344,21 +344,21 @@ describe('X25519 Cryptography', function () { const S = curve.keyFromSecret(vector.SECRET_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('40'+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([ signature.sign(22, undefined, keyIntegers, data).then(signed => { const len = ((signed[0] << 8| signed[1]) + 7) / 8; - expect(util.hex2Uint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len)); - expect(util.hex2Uint8Array(vector.SIGNATURE.S)).to.deep.eq(signed.slice(4 + len)); + 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)); }), signature.verify(22, undefined, msg_MPIs, keyIntegers, data).then(result => { expect(result).to.be.true; @@ -391,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(''), @@ -408,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(''), @@ -425,7 +425,7 @@ describe('X25519 Cryptography', function () { PUBLIC_KEY: ['278117fc144c72340f67d0f2316e8386', 'ceffbf2b2428c9c51fef7c597f1d426e'].join(''), - MESSAGE: util.hex2bin([ + MESSAGE: util.hex_to_str([ '08b8b2b733424243760fe426a4b54908', '632110a66c2f6591eabd3345e3e4eb98', 'fa6e264bf09efe12ee50f8f54e9f77b1', @@ -507,7 +507,7 @@ describe('X25519 Cryptography', function () { PUBLIC_KEY: ['ec172b93ad5e563bf4932c70e1245034', 'c35467ef2efd4d64ebf819683467e2bf'].join(''), - MESSAGE: util.hex2bin([ + MESSAGE: util.hex_to_str([ 'ddaf35a193617abacc417349ae204131', '12e6fa4e89a97ea20a9eeee64b55d39a', '2192992a274fc1a836ba3c23a3feebbd',