This commit is contained in:
Mahrud Sayrafi 2018-02-26 20:01:01 -05:00
parent 378fb85733
commit 3df0997f4d
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
11 changed files with 66 additions and 58 deletions

View File

@ -85,7 +85,8 @@ export default {
} }
break; break;
} }
return { r: r.fromRed(), s: s.fromRed() }; return { r: r.toArrayLike(Uint8Array),
s: s.toArrayLike(Uint8Array) };
}, },
/* /*

View File

@ -167,7 +167,7 @@ Curve.prototype.genKeyPair = async function () {
return new KeyPair(this.curve, keyPair); return new KeyPair(this.curve, keyPair);
}; };
function get(oid_or_name) { function getCurve(oid_or_name) {
let name; let name;
if (OID.prototype.isPrototypeOf(oid_or_name) && if (OID.prototype.isPrototypeOf(oid_or_name) &&
enums.curve[oid_or_name.toHex()]) { enums.curve[oid_or_name.toHex()]) {
@ -184,7 +184,7 @@ function get(oid_or_name) {
} }
async function generate(curve) { async function generate(curve) {
curve = get(curve); curve = getCurve(curve);
const keyPair = await curve.genKeyPair(); const keyPair = await curve.genKeyPair();
return { return {
oid: curve.oid, oid: curve.oid,
@ -202,7 +202,7 @@ function getPreferredHashAlgo(oid) {
export default Curve; export default Curve;
export { export {
curves, webCurves, nodeCurves, get, generate, getPreferredHashAlgo curves, webCurves, nodeCurves, getCurve, generate, getPreferredHashAlgo
}; };

View File

@ -30,7 +30,7 @@
*/ */
import BN from 'bn.js'; import BN from 'bn.js';
import { get as curvesGet } from './curves'; import { getCurve } from './curves';
import aes_kw from '../../aes_kw'; import aes_kw from '../../aes_kw';
import cipher from '../../cipher'; import cipher from '../../cipher';
import hash from '../../hash'; 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) { async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) {
fingerprint = util.hex2Uint8Array(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); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
cipher_algo = enums.read(enums.symmetric, cipher_algo); cipher_algo = enums.read(enums.symmetric, cipher_algo);
const v = await curve.genKeyPair(); 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) { async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) {
fingerprint = util.hex2Uint8Array(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); const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
cipher_algo = enums.read(enums.symmetric, cipher_algo); cipher_algo = enums.read(enums.symmetric, cipher_algo);
V = curve.keyFromPublic(V); V = curve.keyFromPublic(V);

View File

@ -26,7 +26,7 @@
import util from '../../../util'; import util from '../../../util';
import hash from '../../hash'; import hash from '../../hash';
import { get as curvesGet } from './curves'; import { getCurve } from './curves';
/** /**
* Sign a message using the provided key * 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 {enums.hash} hash_algo Hash algorithm used to sign
* @param {Uint8Array} m Message to sign * @param {Uint8Array} m Message to sign
* @param {Uint8Array} d Private key used to sign the message * @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) { async function sign(oid, hash_algo, m, d) {
const curve = curvesGet(oid); const curve = getCurve(oid);
const key = curve.keyFromPrivate(d); 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 * Verifies if a signature is valid for a message
* @param {module:type/oid} oid Elliptic curve object identifier * @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used in the signature * @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} m Message to verify
* @param {Uint8Array} Q Public key used to verify the message * @param {Uint8Array} Q Public key used to verify the message
* @return {Boolean} * @return {Boolean}
*/ */
async function verify(oid, hash_algo, signature, m, Q) { async function verify(oid, hash_algo, signature, m, Q) {
const curve = curvesGet(oid); const curve = getCurve(oid);
const key = curve.keyFromPublic(Q); const key = curve.keyFromPublic(Q);
return key.verify(m, signature, hash_algo); return key.verify(m, signature, hash_algo);
} }

View File

@ -26,41 +26,40 @@
import BN from 'bn.js'; import BN from 'bn.js';
import hash from '../../hash'; import hash from '../../hash';
import { get as curvesGet } from './curves'; import { getCurve } from './curves';
/** /**
* Sign a message using the provided key * Sign a message using the provided key
* @param {module:type/oid} oid Elliptic curve object identifier * @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used to sign * @param {enums.hash} hash_algo Hash algorithm used to sign
* @param {Uint8Array} m Message to sign * @param {Uint8Array} m Message to sign
* @param {BN} d Private key used to sign * @param {Uint8Array} d Private key used to sign
* @return {{R: Array, S: Array}} Signature of the message * @return {{R: Uint8Array,
S: Uint8Array}} Signature of the message
*/ */
async function sign(oid, hash_algo, m, d) { async function sign(oid, hash_algo, m, d) {
const curve = curvesGet(oid); const curve = getCurve(oid);
const key = curve.keyFromSecret(d.toArray('be', 32)); const key = curve.keyFromSecret(d);
const signature = await key.sign(m, hash_algo); const signature = await key.sign(m, hash_algo);
// EdDSA signature params are returned in little-endian format // 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 * Verifies if a signature is valid for a message
* @param {module:type/oid} oid Elliptic curve object identifier * @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used in the signature * @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 {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} * @return {Boolean}
*/ */
async function verify(oid, hash_algo, signature, m, Q) { async function verify(oid, hash_algo, signature, m, Q) {
const curve = curvesGet(oid); const curve = getCurve(oid);
const key = curve.keyFromPublic(Q.toArray('be', 33)); const key = curve.keyFromPublic(Q);
// EdDSA signature params are expected in little-endian format return key.verify(m, signature, hash_algo);
return key.verify(m, {
R: signature.R.toArray('le', 32),
S: signature.S.toArray('le', 32)
}, hash_algo);
} }
export default { sign, verify }; export default { sign, verify };

View File

@ -25,11 +25,11 @@
* @module crypto/public_key/elliptic * @module crypto/public_key/elliptic
*/ */
import { get, generate, getPreferredHashAlgo } from './curves'; import { getCurve, generate, getPreferredHashAlgo } from './curves';
import ecdsa from './ecdsa'; import ecdsa from './ecdsa';
import eddsa from './eddsa'; import eddsa from './eddsa';
import ecdh from './ecdh'; import ecdh from './ecdh';
export default { export default {
ecdh, ecdsa, eddsa, get, generate, getPreferredHashAlgo ecdh, ecdsa, eddsa, getCurve, generate, getPreferredHashAlgo
}; };

View File

@ -55,8 +55,11 @@ export default {
} }
case enums.publicKey.eddsa: { case enums.publicKey.eddsa: {
const oid = pub_MPIs[0]; const oid = pub_MPIs[0];
const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() }; // TODO refactor elliptic to accept Uint8Array
const Q = pub_MPIs[1].toBN(); // 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); return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q);
} }
default: default:
@ -95,8 +98,8 @@ export default {
const x = key_params[4].toBN(); const x = key_params[4].toBN();
const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.r),
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) util.Uint8Array2MPI(signature.s)
]); ]);
} }
case enums.publicKey.elgamal: { case enums.publicKey.elgamal: {
@ -107,17 +110,17 @@ export default {
const d = key_params[2].toUint8Array(); const d = key_params[2].toUint8Array();
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.r),
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) util.Uint8Array2MPI(signature.s)
]); ]);
} }
case enums.publicKey.eddsa: { case enums.publicKey.eddsa: {
const oid = key_params[0]; 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); const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(Uint8Array.from(signature.R)), util.Uint8Array2MPI(signature.R),
util.Uint8Array2MPI(Uint8Array.from(signature.S)) util.Uint8Array2MPI(signature.S)
]); ]);
} }
default: default:

View File

@ -201,7 +201,7 @@ PublicKey.prototype.getAlgorithmInfo = function () {
if (this.params[0] instanceof type_mpi) { if (this.params[0] instanceof type_mpi) {
result.bits = this.params[0].byteLength() * 8; result.bits = this.params[0].byteLength() * 8;
} else { } else {
result.curve = crypto.publicKey.elliptic.get(this.params[0]).name; result.curve = crypto.publicKey.elliptic.getCurve(this.params[0]).name;
} }
return result; return result;
}; };

View File

@ -93,8 +93,8 @@ MPI.prototype.byteLength = function () {
return this.write().length - 2; return this.write().length - 2;
}; };
MPI.prototype.toUint8Array = function () { MPI.prototype.toUint8Array = function (endian, length) {
return this.write().slice(2); return this.write(endian, length).slice(2);
}; };
MPI.prototype.fromUint8Array = function (bytes) { MPI.prototype.fromUint8Array = function (bytes) {

View File

@ -1,5 +1,6 @@
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp'); const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
const BN = require('bn.js');
const chai = require('chai'); const chai = require('chai');
chai.use(require('chai-as-promised')); chai.use(require('chai-as-promised'));
@ -136,21 +137,21 @@ describe('Elliptic Curve Cryptography', function () {
it('Creating curve with name', function (done) { it('Creating curve with name', function (done) {
const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519']; const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519'];
names.forEach(function (name) { names.forEach(function (name) {
expect(elliptic_curves.get(name)).to.exist; expect(elliptic_curves.getCurve(name)).to.exist;
}); });
done(); done();
}); });
it('Creating curve from oid', function (done) { it('Creating curve from oid', function (done) {
const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A']; const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A'];
oids.forEach(function (oid) { 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(); done();
}); });
it('Creating KeyPair', function () { it('Creating KeyPair', function () {
const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519']; const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519'];
return Promise.all(names.map(function (name) { return Promise.all(names.map(function (name) {
const curve = elliptic_curves.get(name); const curve = elliptic_curves.getCurve(name);
return curve.genKeyPair().then(keyPair => { return curve.genKeyPair().then(keyPair => {
expect(keyPair).to.exist; expect(keyPair).to.exist;
}); });
@ -159,7 +160,7 @@ describe('Elliptic Curve Cryptography', function () {
it('Creating KeyPair from data', function (done) { it('Creating KeyPair from data', function (done) {
for (const name in key_data) { for (const name in key_data) {
const pair = key_data[name]; const pair = key_data[name];
const curve = elliptic_curves.get(name); const curve = elliptic_curves.getCurve(name);
expect(curve).to.exist; expect(curve).to.exist;
const keyPair = curve.keyFromPrivate(pair.priv); const keyPair = curve.keyFromPrivate(pair.priv);
expect(keyPair).to.exist; expect(keyPair).to.exist;
@ -170,19 +171,19 @@ describe('Elliptic Curve Cryptography', function () {
done(); done();
}); });
it('Signature verification', 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); const key = curve.keyFromPublic(signature_data.pub);
expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.true; expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.true;
done(); done();
}); });
it('Invalid signature', function (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); const key = curve.keyFromPublic(key_data.p256.pub);
expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.false; expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.false;
done(); done();
}); });
it('Signature generation', function () { it('Signature generation', function () {
const curve = elliptic_curves.get('p256'); const curve = elliptic_curves.getCurve('p256');
let key = curve.keyFromPrivate(key_data.p256.priv); let key = curve.keyFromPrivate(key_data.p256.priv);
return key.sign(signature_data.message, 8).then(signature => { return key.sign(signature_data.message, 8).then(signature => {
key = curve.keyFromPublic(key_data.p256.pub); key = curve.keyFromPublic(key_data.p256.pub);
@ -190,7 +191,7 @@ describe('Elliptic Curve Cryptography', function () {
}); });
}); });
it('Shared secret generation', function (done) { 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 key1 = curve.keyFromPrivate(key_data.p256.priv);
let key2 = curve.keyFromPublic(signature_data.pub); let key2 = curve.keyFromPublic(signature_data.pub);
const shared1 = openpgp.util.hexidump(key1.derive(key2)); const shared1 = openpgp.util.hexidump(key1.derive(key2));
@ -212,7 +213,7 @@ describe('Elliptic Curve Cryptography', function () {
return ecdsa.verify( return ecdsa.verify(
oid, oid,
hash, hash,
{r: new Uint8Array(r), s: new Uint8Array(s)}, {r: new BN(r), s: new BN(s)},
message, message,
new Uint8Array(pub) new Uint8Array(pub)
); );
@ -288,7 +289,7 @@ describe('Elliptic Curve Cryptography', function () {
.to.eventually.be.true.notify(done); .to.eventually.be.true.notify(done);
}); });
it('Sign and verify message', function () { it('Sign and verify message', function () {
const curve = elliptic_curves.get('p521'); const curve = elliptic_curves.getCurve('p521');
return curve.genKeyPair().then(keyPair => { return curve.genKeyPair().then(keyPair => {
const keyPublic = new Uint8Array(keyPair.getPublic()); const keyPublic = new Uint8Array(keyPair.getPublic());
const keyPrivate = new Uint8Array(keyPair.getPrivate()); const keyPrivate = new Uint8Array(keyPair.getPrivate());
@ -309,7 +310,7 @@ describe('Elliptic Curve Cryptography', function () {
data = new Uint8Array(data); data = new Uint8Array(data);
} }
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
const curve = elliptic_curves.get(oid); const curve = elliptic_curves.getCurve(oid);
return elliptic_curves.ecdh.decrypt( return elliptic_curves.ecdh.decrypt(
curve.oid, curve.oid,
cipher, cipher,

View File

@ -235,9 +235,9 @@ describe('X25519 Cryptography', function () {
const hi = firstKey.key; const hi = firstKey.key;
const primaryKey = hi.primaryKey; const primaryKey = hi.primaryKey;
const subKey = hi.subKeys[0].subKey; 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(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'); expect(subKey.algorithm).to.equal('ecdh');
// Self Certificate is valid // Self Certificate is valid
@ -255,9 +255,9 @@ describe('X25519 Cryptography', function () {
}; };
return openpgp.generateKey(options).then(function (secondKey) { return openpgp.generateKey(options).then(function (secondKey) {
const bye = secondKey.key; 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.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'); expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh');
// Self Certificate is valid // Self Certificate is valid
@ -319,7 +319,7 @@ describe('X25519 Cryptography', function () {
describe('Ed25519 Test Vectors from RFC8032', function () { describe('Ed25519 Test Vectors from RFC8032', function () {
// https://tools.ietf.org/html/rfc8032#section-7.1 // https://tools.ietf.org/html/rfc8032#section-7.1
const signature = openpgp.crypto.signature; const signature = openpgp.crypto.signature;
const curve = elliptic.get('ed25519'); const curve = elliptic.getCurve('ed25519');
const util = openpgp.util; const util = openpgp.util;
function testVector(vector) { function testVector(vector) {
const S = curve.keyFromSecret(vector.SECRET_KEY); const S = curve.keyFromSecret(vector.SECRET_KEY);