From 7eef65926abcff2e7aa875a5ce1554e5d78945ab Mon Sep 17 00:00:00 2001 From: Mahrud Sayrafi Date: Wed, 28 Feb 2018 00:58:50 -0800 Subject: [PATCH] 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);