From 151f15e2823e78797976cf8a15f8e08fd76e8632 Mon Sep 17 00:00:00 2001 From: larabr Date: Tue, 27 Feb 2024 14:56:07 +0100 Subject: [PATCH] Node: drop asn1.js dependency (#1722) asn1.js is a fairly large lib and was simply needed to handle DER encodings in some NodeCrypto operations. This change replaces the dependency by moving to: - JWT encoding for RSA (support added in Node v15) - a much lighter dependency (eckey-utils) for ECDSA, where JWT cannot be used for now, as Node has yet to add decoding support for Brainpool curves. The change also allows us to drop BN.js as a direct dependency, optimising the BigInteger-related chunking in the lightweight build. --- package-lock.json | 59 +++----- package.json | 5 +- rollup.config.js | 2 +- src/crypto/public_key/elliptic/ecdsa.js | 101 ++++---------- src/crypto/public_key/rsa.js | 172 +++++------------------- test/crypto/validate.js | 17 +-- 6 files changed, 92 insertions(+), 264 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0425d70a..cd610457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "name": "openpgp", "version": "6.0.0-alpha.0", "license": "LGPL-3.0+", - "dependencies": { - "asn1.js": "^5.0.0" - }, "devDependencies": { "@openpgp/asmcrypto.js": "^3.1.0", "@openpgp/jsdoc": "^3.6.11", @@ -28,10 +25,10 @@ "@types/chai": "^4.2.14", "argon2id": "^1.0.1", "benchmark": "^2.1.4", - "bn.js": "^4.11.8", "c8": "^8.0.1", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", + "eckey-utils": "^0.7.14", "eslint": "^8.34.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-base": "^15.0.0", @@ -1775,16 +1772,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/asn1.js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.0.0.tgz", - "integrity": "sha512-Y+FKviD0uyIWWo/xE0XkUl0x1allKFhzEVJ+//2Dgqpy+n+B77MlPNqvyk7Vx50M9XyVzjnRhDqJAEAsyivlbA==", - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -1882,7 +1869,8 @@ "node_modules/bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true }, "node_modules/body-parser": { "version": "1.19.2", @@ -2683,6 +2671,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "node_modules/eckey-utils": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/eckey-utils/-/eckey-utils-0.7.14.tgz", + "integrity": "sha512-s/mENS+mMnJjDSydy0muBQQHMTWJ1nPe8EiphANZrf+lv/1u35aP9WvWHTWqCBJ21blNIurGF7UoLjtaOpoCFw==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4147,7 +4141,8 @@ "node_modules/inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, "node_modules/internal-slot": { "version": "1.0.3", @@ -5216,11 +5211,6 @@ "node": ">=4" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -8589,16 +8579,6 @@ "es-shim-unscopables": "^1.0.0" } }, - "asn1.js": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.0.0.tgz", - "integrity": "sha512-Y+FKviD0uyIWWo/xE0XkUl0x1allKFhzEVJ+//2Dgqpy+n+B77MlPNqvyk7Vx50M9XyVzjnRhDqJAEAsyivlbA==", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -8681,7 +8661,8 @@ "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "dev": true }, "body-parser": { "version": "1.19.2", @@ -9292,6 +9273,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "eckey-utils": { + "version": "0.7.14", + "resolved": "https://registry.npmjs.org/eckey-utils/-/eckey-utils-0.7.14.tgz", + "integrity": "sha512-s/mENS+mMnJjDSydy0muBQQHMTWJ1nPe8EiphANZrf+lv/1u35aP9WvWHTWqCBJ21blNIurGF7UoLjtaOpoCFw==", + "dev": true + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -10420,7 +10407,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, "internal-slot": { "version": "1.0.3", @@ -11255,11 +11243,6 @@ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" - }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", diff --git a/package.json b/package.json index f23b435c..f1ae1e42 100644 --- a/package.json +++ b/package.json @@ -78,10 +78,10 @@ "@types/chai": "^4.2.14", "argon2id": "^1.0.1", "benchmark": "^2.1.4", - "bn.js": "^4.11.8", "c8": "^8.0.1", "chai": "^4.3.7", "chai-as-promised": "^7.1.1", + "eckey-utils": "^0.7.14", "eslint": "^8.34.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-base": "^15.0.0", @@ -106,9 +106,6 @@ "typescript": "^5.3.3", "web-streams-polyfill": "^3.2.0" }, - "dependencies": { - "asn1.js": "^5.0.0" - }, "repository": { "type": "git", "url": "https://github.com/openpgpjs/openpgpjs" diff --git a/rollup.config.js b/rollup.config.js index 22a1b6c5..c29e0e51 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -14,7 +14,7 @@ import { wasm } from '@rollup/plugin-wasm'; // import pkg from './package.json' assert { type: 'json' }; const pkg = JSON.parse(readFileSync('./package.json')); -const nodeDependencies = Object.keys(pkg.dependencies); +const nodeDependencies = Object.keys(pkg.dependencies || {}); const nodeBuiltinModules = builtinModules.concat(['module']); const wasmOptions = { diff --git a/src/crypto/public_key/elliptic/ecdsa.js b/src/crypto/public_key/elliptic/ecdsa.js index 44d63755..35d3c080 100644 --- a/src/crypto/public_key/elliptic/ecdsa.js +++ b/src/crypto/public_key/elliptic/ecdsa.js @@ -24,7 +24,7 @@ import enums from '../../../enums'; import util from '../../../util'; import { getRandomBytes } from '../../random'; import hash from '../../hash'; -import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams } from './oid_curves'; +import { CurveWithOID, webCurves, privateToJWK, rawPublicToJWK, validateStandardParams, nodeCurves } from './oid_curves'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); @@ -63,13 +63,8 @@ export async function sign(oid, hashAlgo, message, publicKey, privateKey, hashed util.printDebugError('Browser did not support signing: ' + err.message); } break; - case 'node': { - const signature = await nodeSign(curve, hashAlgo, message, keyPair); - return { - r: signature.r.toArrayLike(Uint8Array), - s: signature.s.toArrayLike(Uint8Array) - }; - } + case 'node': + return nodeSign(curve, hashAlgo, message, privateKey); } } @@ -247,85 +242,45 @@ async function webVerify(curve, hashAlgo, { r, s }, message, publicKey) { ); } -async function nodeSign(curve, hashAlgo, message, keyPair) { +async function nodeSign(curve, hashAlgo, message, privateKey) { + // JWT encoding cannot be used for now, as Brainpool curves are not supported + const ecKeyUtils = util.nodeRequire('eckey-utils'); + const nodeBuffer = util.getNodeBuffer(); + const { privateKey: derPrivateKey } = ecKeyUtils.generateDer({ + curveName: nodeCurves[curve.name], + privateKey: nodeBuffer.from(privateKey) + }); + const sign = nodeCrypto.createSign(enums.read(enums.hash, hashAlgo)); sign.write(message); sign.end(); - const key = ECPrivateKey.encode({ - version: 1, - parameters: curve.oid, - privateKey: Array.from(keyPair.privateKey), - publicKey: { unused: 0, data: Array.from(keyPair.publicKey) } - }, 'pem', { - label: 'EC PRIVATE KEY' - }); - return ECDSASignature.decode(sign.sign(key), 'der'); + const signature = new Uint8Array(sign.sign({ key: derPrivateKey, format: 'der', type: 'sec1', dsaEncoding: 'ieee-p1363' })); + const len = curve.payloadSize; + + return { + r: signature.subarray(0, len), + s: signature.subarray(len, len << 1) + }; } async function nodeVerify(curve, hashAlgo, { r, s }, message, publicKey) { - const { default: BN } = await import('bn.js'); + const ecKeyUtils = util.nodeRequire('eckey-utils'); + const nodeBuffer = util.getNodeBuffer(); + const { publicKey: derPublicKey } = ecKeyUtils.generateDer({ + curveName: nodeCurves[curve.name], + publicKey: nodeBuffer.from(publicKey) + }); const verify = nodeCrypto.createVerify(enums.read(enums.hash, hashAlgo)); verify.write(message); verify.end(); - const key = SubjectPublicKeyInfo.encode({ - algorithm: { - algorithm: [1, 2, 840, 10045, 2, 1], - parameters: curve.oid - }, - subjectPublicKey: { unused: 0, data: Array.from(publicKey) } - }, 'pem', { - label: 'PUBLIC KEY' - }); - const signature = ECDSASignature.encode({ - r: new BN(r), s: new BN(s) - }, 'der'); + + const signature = util.concatUint8Array([r, s]); try { - return verify.verify(key, signature); + return verify.verify({ key: derPublicKey, format: 'der', type: 'spki', dsaEncoding: 'ieee-p1363' }, signature); } catch (err) { return false; } } - -// Originally written by Owen Smith https://github.com/omsmith -// Adapted on Feb 2018 from https://github.com/Brightspace/node-jwk-to-pem/ - -/* eslint-disable no-invalid-this */ - -const asn1 = nodeCrypto ? util.nodeRequire('asn1.js') : undefined; - -const ECDSASignature = nodeCrypto ? - asn1.define('ECDSASignature', function() { - this.seq().obj( - this.key('r').int(), - this.key('s').int() - ); - }) : undefined; - -const ECPrivateKey = nodeCrypto ? - asn1.define('ECPrivateKey', function() { - this.seq().obj( - this.key('version').int(), - this.key('privateKey').octstr(), - this.key('parameters').explicit(0).optional().any(), - this.key('publicKey').explicit(1).optional().bitstr() - ); - }) : undefined; - -const AlgorithmIdentifier = nodeCrypto ? - asn1.define('AlgorithmIdentifier', function() { - this.seq().obj( - this.key('algorithm').objid(), - this.key('parameters').optional().any() - ); - }) : undefined; - -const SubjectPublicKeyInfo = nodeCrypto ? - asn1.define('SubjectPublicKeyInfo', function() { - this.seq().obj( - this.key('algorithm').use(AlgorithmIdentifier), - this.key('subjectPublicKey').bitstr() - ); - }) : undefined; diff --git a/src/crypto/public_key/rsa.js b/src/crypto/public_key/rsa.js index 6387bc25..79826f23 100644 --- a/src/crypto/public_key/rsa.js +++ b/src/crypto/public_key/rsa.js @@ -28,30 +28,6 @@ import enums from '../../enums'; const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); -const asn1 = nodeCrypto ? util.nodeRequire('asn1.js') : undefined; - -/* eslint-disable no-invalid-this */ -const RSAPrivateKey = nodeCrypto ? asn1.define('RSAPrivateKey', function () { - this.seq().obj( // used for native NodeJS crypto - this.key('version').int(), // 0 - this.key('modulus').int(), // n - this.key('publicExponent').int(), // e - this.key('privateExponent').int(), // d - this.key('prime1').int(), // p - this.key('prime2').int(), // q - this.key('exponent1').int(), // dp - this.key('exponent2').int(), // dq - this.key('coefficient').int() // u - ); -}) : undefined; - -const RSAPublicKey = nodeCrypto ? asn1.define('RSAPubliceKey', function () { - this.seq().obj( // used for native NodeJS crypto - this.key('modulus').int(), // n - this.key('publicExponent').int() // e - ); -}) : undefined; -/* eslint-enable no-invalid-this */ /** Create signature * @param {module:enums.hash} hashAlgo - Hash algorithm @@ -178,47 +154,24 @@ export async function generate(bits, e) { // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33 const jwk = await webCrypto.exportKey('jwk', keyPair.privateKey); // map JWK parameters to corresponding OpenPGP names - return { - n: b64ToUint8Array(jwk.n), - e: e.toUint8Array(), - d: b64ToUint8Array(jwk.d), - // switch p and q - p: b64ToUint8Array(jwk.q), - q: b64ToUint8Array(jwk.p), - // Since p and q are switched in places, u is the inverse of jwk.q - u: b64ToUint8Array(jwk.qi) - }; - } else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) { + return jwkToPrivate(jwk, e); + } else if (util.getNodeCrypto()) { const opts = { modulusLength: bits, publicExponent: e.toNumber(), - publicKeyEncoding: { type: 'pkcs1', format: 'der' }, - privateKeyEncoding: { type: 'pkcs1', format: 'der' } + publicKeyEncoding: { type: 'pkcs1', format: 'jwk' }, + privateKeyEncoding: { type: 'pkcs1', format: 'jwk' } }; - const prv = await new Promise((resolve, reject) => { - nodeCrypto.generateKeyPair('rsa', opts, (err, _, der) => { + const jwk = await new Promise((resolve, reject) => { + nodeCrypto.generateKeyPair('rsa', opts, (err, _, jwkPrivateKey) => { if (err) { reject(err); } else { - resolve(RSAPrivateKey.decode(der, 'der')); + resolve(jwkPrivateKey); } }); }); - /** - * OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`. - * @link https://tools.ietf.org/html/rfc3447#section-3.2 - * @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1 - */ - return { - n: prv.modulus.toArrayLike(Uint8Array), - e: prv.publicExponent.toArrayLike(Uint8Array), - d: prv.privateExponent.toArrayLike(Uint8Array), - // switch p and q - p: prv.prime2.toArrayLike(Uint8Array), - q: prv.prime1.toArrayLike(Uint8Array), - // Since p and q are switched in places, we can keep u as defined by DER - u: prv.coefficient.toArrayLike(Uint8Array) - }; + return jwkToPrivate(jwk, e); } // RSA keygen fallback using 40 iterations of the Miller-Rabin test @@ -332,36 +285,12 @@ async function webSign(hashName, data, n, e, d, p, q, u) { } async function nodeSign(hashAlgo, data, n, e, d, p, q, u) { - const { default: BN } = await import('bn.js'); - const pBNum = new BN(p); - const qBNum = new BN(q); - const dBNum = new BN(d); - const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1) - const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1) const sign = nodeCrypto.createSign(enums.read(enums.hash, hashAlgo)); sign.write(data); sign.end(); - const keyObject = { - version: 0, - modulus: new BN(n), - publicExponent: new BN(e), - privateExponent: new BN(d), - // switch p and q - prime1: new BN(q), - prime2: new BN(p), - // switch dp and dq - exponent1: dq, - exponent2: dp, - coefficient: new BN(u) - }; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects - const der = RSAPrivateKey.encode(keyObject, 'der'); - return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' })); - } - const pem = RSAPrivateKey.encode(keyObject, 'pem', { - label: 'RSA PRIVATE KEY' - }); - return new Uint8Array(sign.sign(pem)); + + const jwk = await privateToJWK(n, e, d, p, q, u); + return new Uint8Array(sign.sign({ key: jwk, format: 'jwk', type: 'pkcs1' })); } async function bnVerify(hashAlgo, s, n, e, hashed) { @@ -388,24 +317,13 @@ async function webVerify(hashName, data, s, n, e) { } async function nodeVerify(hashAlgo, data, s, n, e) { - const { default: BN } = await import('bn.js'); + const jwk = publicToJWK(n, e); + const key = { key: jwk, format: 'jwk', type: 'pkcs1' }; const verify = nodeCrypto.createVerify(enums.read(enums.hash, hashAlgo)); verify.write(data); verify.end(); - const keyObject = { - modulus: new BN(n), - publicExponent: new BN(e) - }; - let key; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects - const der = RSAPublicKey.encode(keyObject, 'der'); - key = { key: der, format: 'der', type: 'pkcs1' }; - } else { - key = RSAPublicKey.encode(keyObject, 'pem', { - label: 'RSA PUBLIC KEY' - }); - } + try { return await verify.verify(key, s); } catch (err) { @@ -414,22 +332,9 @@ async function nodeVerify(hashAlgo, data, s, n, e) { } async function nodeEncrypt(data, n, e) { - const { default: BN } = await import('bn.js'); + const jwk = publicToJWK(n, e); + const key = { key: jwk, format: 'jwk', type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - const keyObject = { - modulus: new BN(n), - publicExponent: new BN(e) - }; - let key; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { - const der = RSAPublicKey.encode(keyObject, 'der'); - key = { key: der, format: 'der', type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } else { - const pem = RSAPublicKey.encode(keyObject, 'pem', { - label: 'RSA PUBLIC KEY' - }); - key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } return new Uint8Array(nodeCrypto.publicEncrypt(key, data)); } @@ -446,36 +351,9 @@ async function bnEncrypt(data, n, e) { } async function nodeDecrypt(data, n, e, d, p, q, u, randomPayload) { - const { default: BN } = await import('bn.js'); + const jwk = await privateToJWK(n, e, d, p, q, u); + const key = { key: jwk, format: 'jwk' , type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - const pBNum = new BN(p); - const qBNum = new BN(q); - const dBNum = new BN(d); - const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1) - const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1) - const keyObject = { - version: 0, - modulus: new BN(n), - publicExponent: new BN(e), - privateExponent: new BN(d), - // switch p and q - prime1: new BN(q), - prime2: new BN(p), - // switch dp and dq - exponent1: dq, - exponent2: dp, - coefficient: new BN(u) - }; - let key; - if (typeof nodeCrypto.createPrivateKey !== 'undefined') { - const der = RSAPrivateKey.encode(keyObject, 'der'); - key = { key: der, format: 'der' , type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } else { - const pem = RSAPrivateKey.encode(keyObject, 'pem', { - label: 'RSA PRIVATE KEY' - }); - key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING }; - } try { return new Uint8Array(nodeCrypto.privateDecrypt(key, data)); } catch (err) { @@ -570,3 +448,17 @@ function publicToJWK(n, e) { ext: true }; } + +/** Convert JWK private key to OpenPGP private key params */ +function jwkToPrivate(jwk, e) { + return { + n: b64ToUint8Array(jwk.n), + e: e.toUint8Array(), + d: b64ToUint8Array(jwk.d), + // switch p and q + p: b64ToUint8Array(jwk.q), + q: b64ToUint8Array(jwk.p), + // Since p and q are switched in places, u is the inverse of jwk.q + u: b64ToUint8Array(jwk.qi) + }; +} diff --git a/test/crypto/validate.js b/test/crypto/validate.js index 6a46d030..e76e4947 100644 --- a/test/crypto/validate.js +++ b/test/crypto/validate.js @@ -1,9 +1,9 @@ -import BN from 'bn.js'; import { use as chaiUse, expect } from 'chai'; import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import chaiUse(chaiAsPromised); import openpgp from '../initOpenpgp.js'; +import util from '../../src/util.js'; const armoredDSAKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- @@ -387,15 +387,16 @@ export default () => { }); it('detect g with small order', async function() { - const keyPacket = await cloneKeyPacket(egKey); - const p = keyPacket.publicParams.p; - const g = keyPacket.publicParams.g; + const BigInteger = await util.getBigInteger(); - const pBN = new BN(p); - const gModP = new BN(g).toRed(new BN.red(pBN)); + const keyPacket = await cloneKeyPacket(egKey); + const { p, g } = keyPacket.publicParams; + + const pBN = new BigInteger(p); + const gBN = new BigInteger(g); // g**(p-1)/2 has order 2 - const gOrd2 = gModP.redPow(pBN.subn(1).shrn(1)); - keyPacket.publicParams.g = gOrd2.toArrayLike(Uint8Array, 'be'); + const gOrd2 = gBN.modExp(pBN.dec().irightShift(new BigInteger(1)), pBN); + keyPacket.publicParams.g = gOrd2.toUint8Array(); await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid'); }); });