ECDH, ECDSA, EdDSA are all on BN.js; TODO: ElGamal & type_mpi

This commit is contained in:
Mahrud Sayrafi 2018-02-16 03:44:07 -08:00
parent 9200f026f3
commit 490b1dc0f0
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
11 changed files with 135 additions and 218 deletions

View File

@ -32,7 +32,7 @@
*/ */
import BN from 'bn.js'; 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 publicKey from './public_key';
import cipher from './cipher'; import cipher from './cipher';
import random from './random'; import random from './random';
@ -62,15 +62,15 @@ export default {
* @return {Array<module:type/mpi|module:type/oid|module:type/kdf_params|module:type/ecdh_symkey>} encrypted session key parameters * @return {Array<module:type/mpi|module:type/oid|module:type/kdf_params|module:type/ecdh_symkey>} encrypted session key parameters
*/ */
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
// TODO change algo to return enums
const types = this.getEncSessionKeyParamTypes(algo); const types = this.getEncSessionKeyParamTypes(algo);
return (async function() { return (async function() {
let m;
switch (algo) { switch (algo) {
case 'rsa_encrypt': case 'rsa_encrypt':
case 'rsa_encrypt_sign': { case 'rsa_encrypt_sign': {
const n = publicParams[0].toUint8Array(); const n = publicParams[0].toUint8Array();
const e = publicParams[1].toUint8Array(); const e = publicParams[1].toUint8Array();
m = data.toUint8Array(); const m = data.toUint8Array();
return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]); return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]);
} }
case 'elgamal': { case 'elgamal': {
@ -78,17 +78,15 @@ export default {
const p = publicParams[0].toBigInteger(); const p = publicParams[0].toBigInteger();
const g = publicParams[1].toBigInteger(); const g = publicParams[1].toBigInteger();
const y = publicParams[2].toBigInteger(); const y = publicParams[2].toBigInteger();
m = data.toBigInteger(); const m = data.toBigInteger();
return constructParams(types, elgamal.encrypt(m, g, p, y)); return constructParams(types, elgamal.encrypt(m, g, p, y));
} }
case 'ecdh': { case 'ecdh': {
const { ecdh } = publicKey.elliptic; const oid = publicParams[0];
const curve = publicParams[0];
const kdf_params = publicParams[2]; const kdf_params = publicParams[2];
const R = publicParams[1].toBigInteger(); const Q = publicParams[1].toUint8Array();
const res = await ecdh.encrypt( const res = await publicKey.elliptic.ecdh.encrypt(
curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint);
);
return constructParams(types, [res.V, res.C]); return constructParams(types, [res.V, res.C]);
} }
default: default:
@ -106,9 +104,8 @@ export default {
* @param {String} fingerprint Recipient fingerprint * @param {String} fingerprint Recipient fingerprint
* @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null
*/ */
publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) { publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) {
let p; // TODO change algo to return enums
return new type_mpi(await (async function() { return new type_mpi(await (async function() {
switch (algo) { switch (algo) {
case 'rsa_encrypt_sign': case 'rsa_encrypt_sign':
@ -120,13 +117,9 @@ export default {
const p = keyIntegers[3].toUint8Array(); const p = keyIntegers[3].toUint8Array();
const q = keyIntegers[4].toUint8Array(); const q = keyIntegers[4].toUint8Array();
const u = keyIntegers[5].toUint8Array(); // q^-1 mod p const u = keyIntegers[5].toUint8Array(); // q^-1 mod p
const dd = BigNumber.fromArrayBuffer(d); const dd = new BN(d);
const dp = new Modulus( const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1)
BigNumber.fromArrayBuffer(p).subtract(BigNumber.ONE) const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1)
).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 BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice return new BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice
} }
case 'elgamal': { case 'elgamal': {
@ -134,17 +127,17 @@ export default {
const x = keyIntegers[3].toBigInteger(); const x = keyIntegers[3].toBigInteger();
const c1 = dataIntegers[0].toBigInteger(); const c1 = dataIntegers[0].toBigInteger();
const c2 = dataIntegers[1].toBigInteger(); const c2 = dataIntegers[1].toBigInteger();
p = keyIntegers[0].toBigInteger(); const p = keyIntegers[0].toBigInteger();
return elgamal.decrypt(c1, c2, p, x); return elgamal.decrypt(c1, c2, p, x);
} }
case 'ecdh': { case 'ecdh': {
const { ecdh } = publicKey.elliptic; const oid = keyIntegers[0];
const curve = keyIntegers[0];
const kdf_params = keyIntegers[2]; const kdf_params = keyIntegers[2];
const V = dataIntegers[0].toBigInteger(); const V = dataIntegers[0].toUint8Array();
const C = dataIntegers[1].data; const C = dataIntegers[1].data;
const r = keyIntegers[3].toBigInteger(); const d = keyIntegers[3].toUint8Array();
return ecdh.decrypt(curve.oid, kdf_params.cipher, kdf_params.hash, V, C, r, fingerprint); return publicKey.elliptic.ecdh.decrypt(
oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint);
} }
default: default:
return null; return null;
@ -259,16 +252,17 @@ export default {
}, },
/** Generate algorithm-specific key parameters /** Generate algorithm-specific key parameters
* @param {String} algo The public key algorithm * @param {String} algo The public key algorithm
* @return {Array} The array of parameters * @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)); const types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo));
switch (algo) { switch (algo) {
case 'rsa_encrypt': case 'rsa_encrypt':
case 'rsa_encrypt_sign': case 'rsa_encrypt_sign':
case 'rsa_sign': { case 'rsa_sign': {
//remember "publicKey" refers to the crypto/public_key dir
const rsa = new publicKey.rsa(); const rsa = new publicKey.rsa();
return rsa.generate(bits, "10001").then(function(keyObject) { return rsa.generate(bits, "10001").then(function(keyObject) {
return constructParams( return constructParams(
@ -278,11 +272,11 @@ export default {
} }
case 'ecdsa': case 'ecdsa':
case 'eddsa': 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]); return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]);
}); });
case 'ecdh': 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]); return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]);
}); });
default: default:

View File

@ -26,7 +26,7 @@
*/ */
import { ec as EC, eddsa as EdDSA } from 'elliptic'; import { ec as EC, eddsa as EdDSA } from 'elliptic';
import { KeyPair } from './key'; import KeyPair from './key';
import BigInteger from '../jsbn'; import BigInteger from '../jsbn';
import random from '../../random'; import random from '../../random';
import enums from '../../../enums'; import enums from '../../../enums';
@ -126,7 +126,7 @@ function Curve(name, params) {
throw new Error('Unknown elliptic key type;'); throw new Error('Unknown elliptic key type;');
} }
this.name = name; this.name = name;
this.oid = curves[name].oid; this.oid = new OID(curves[name].oid);
this.hash = params.hash; this.hash = params.hash;
this.cipher = params.cipher; this.cipher = params.cipher;
this.node = params.node && curves[name].node; this.node = params.node && curves[name].node;
@ -202,14 +202,9 @@ function getPreferredHashAlgo(oid) {
return curves[enums.write(enums.curve, oid.toHex())].hash; return curves[enums.write(enums.curve, oid.toHex())].hash;
} }
// TODO convert to export default {...}
module.exports = { module.exports = {
Curve, Curve, curves, webCurves, nodeCurves, get, generate, getPreferredHashAlgo
curves,
webCurves,
nodeCurves,
getPreferredHashAlgo,
generate,
get
}; };

View File

@ -19,10 +19,9 @@
/** /**
* @requires crypto/public_key/elliptic/curves * @requires crypto/public_key/elliptic/curves
* @requires crypto/public_key/jsbn * @requires crypto/aes_kw
* @requires crypto/cipher * @requires crypto/cipher
* @requires crypto/hash * @requires crypto/hash
* @requires crypto/aes_kw
* @requires type/oid * @requires type/oid
* @requires type/kdf_params * @requires type/kdf_params
* @requires enums * @requires enums
@ -30,20 +29,18 @@
* @module crypto/public_key/elliptic/ecdh * @module crypto/public_key/elliptic/ecdh
*/ */
import BN from 'bn.js';
import curves from './curves'; import curves from './curves';
import BigInteger from '../jsbn'; import aes_kw from '../../aes_kw';
import cipher from '../../cipher'; import cipher from '../../cipher';
import hash from '../../hash'; import hash from '../../hash';
import aes_kw from '../../aes_kw';
import type_kdf_params from '../../../type/kdf_params'; import type_kdf_params from '../../../type/kdf_params';
import type_oid from '../../../type/oid'; import type_oid from '../../../type/oid';
import enums from '../../../enums'; import enums from '../../../enums';
import util from '../../../util'; import util from '../../../util';
// Build Param for ECDH algorithm (RFC 6637) // Build Param for ECDH algorithm (RFC 6637)
function buildEcdhParam(public_algo, oid, cipher_algo, hash_algo, fingerprint) { 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]); const kdf_params = new type_kdf_params([hash_algo, cipher_algo]);
return util.concatUint8Array([ return util.concatUint8Array([
oid.write(), oid.write(),
@ -67,26 +64,26 @@ function kdf(hash_algo, X, length, param) {
/** /**
* Encrypt and wrap a session key * Encrypt and wrap a session key
* *
* @param {String} oid OID of the curve to use * @param {module:type/oid} oid Elliptic curve object identifier
* @param {Enums} cipher_algo Symmetric cipher to use * @param {Enums} cipher_algo Symmetric cipher to use
* @param {Enums} hash_algo Hash to use * @param {Enums} hash_algo Hash algorithm to use
* @param {Uint8Array} m Value derived from session key (RFC 6637) * @param {Uint8Array} m Value derived from session key (RFC 6637)
* @param {BigInteger} Q Recipient public key * @param {Uint8Array} Q Recipient public key
* @param {String} fingerprint Recipient fingerprint * @param {String} fingerprint Recipient fingerprint
* @return {{V: BigInteger, C: Uint8Array}} Returns ephemeral key and encoded session key * @return {{V: BN, C: BN}} Returns ephemeral key and encoded session key
*/ */
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 param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
const curve = curves.get(oid); 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); cipher_algo = enums.read(enums.symmetric, cipher_algo);
const v = await curve.genKeyPair(); const v = await curve.genKeyPair();
Q = curve.keyFromPublic(Q.toByteArray()); Q = curve.keyFromPublic(Q);
const S = v.derive(Q); const S = v.derive(Q);
const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); 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.toBytes());
return { return {
V: new BigInteger(util.hexidump(v.getPublic()), 16), V: new BN(v.getPublic()),
C: C 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 * Decrypt and unwrap the value derived from session key
* *
* @param {String} oid Curve OID * @param {module:type/oid} oid Elliptic curve object identifier
* @param {Enums} cipher_algo Symmetric cipher to use * @param {Enums} cipher_algo Symmetric cipher to use
* @param {Enums} hash_algo Hash algorithm to use * @param {Enums} hash_algo Hash algorithm to use
* @param {BigInteger} V Public part of ephemeral key * @param {BN} V Public part of ephemeral key
* @param {Uint8Array} C Encrypted and wrapped value derived from session key * @param {Uint8Array} C Encrypted and wrapped value derived from session key
* @param {BigInteger} d Recipient private key * @param {Uint8Array} d Recipient private key
* @param {String} fingerprint Recipient fingerprint * @param {String} fingerprint Recipient fingerprint
* @return {Uint8Array} Value derived from session * @return {Uint8Array} Value derived from session
*/ */
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 param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
const curve = curves.get(oid); 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); cipher_algo = enums.read(enums.symmetric, cipher_algo);
V = curve.keyFromPublic(V.toByteArray()); V = curve.keyFromPublic(V);
d = curve.keyFromPrivate(d.toByteArray()); d = curve.keyFromPrivate(d);
const S = d.derive(V); const S = d.derive(V);
const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param); 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 = { export default { encrypt, decrypt };
buildEcdhParam: buildEcdhParam,
kdf: kdf,
encrypt: encrypt,
decrypt: decrypt
};

View File

@ -20,7 +20,6 @@
/** /**
* @requires util * @requires util
* @requires crypto/hash * @requires crypto/hash
* @requires crypto/public_key/jsbn
* @requires crypto/public_key/elliptic/curves * @requires crypto/public_key/elliptic/curves
* @module crypto/public_key/elliptic/ecdsa * @module crypto/public_key/elliptic/ecdsa
*/ */
@ -28,44 +27,34 @@
import util from '../../../util'; import util from '../../../util';
import hash from '../../hash'; import hash from '../../hash';
import curves from './curves'; import curves from './curves';
import BigInteger from '../jsbn';
/** /**
* Sign a message using the provided key * Sign a message using the provided key
* @param {String} oid Elliptic curve for the key * @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 {BigInteger} d Private key used to sign * @param {Uint8Array} d Private key used to sign the message
* @return {{r: BigInteger, s: BigInteger}} Signature of the message * @return {{r: BN, s: BN}} Signature of the message
*/ */
async function sign(oid, hash_algo, m, d) { async function sign(oid, hash_algo, m, d) {
const curve = curves.get(oid); const curve = curves.get(oid);
const key = curve.keyFromPrivate(d.toByteArray()); const key = curve.keyFromPrivate(d);
const signature = await key.sign(m, hash_algo); return key.sign(m, hash_algo);
return {
r: new BigInteger(util.hexidump(signature.r.toArray()), 16),
s: new BigInteger(util.hexidump(signature.s.toArray()), 16)
};
} }
/** /**
* Verifies if a signature is valid for a message * Verifies if a signature is valid for a message
* @param {String} oid Elliptic curve for the key * @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: BigInteger, s: BigInteger}} signature Signature to verify * @param {{r: BN, s: BN}} signature Signature to verify
* @param {Uint8Array} m Message to verify * @param {Uint8Array} m Message to verify
* @param {BigInteger} 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 = curves.get(oid); const curve = curves.get(oid);
const key = curve.keyFromPublic(Q.toByteArray()); const key = curve.keyFromPublic(Q);
return key.verify( return key.verify(m, signature, hash_algo);
m, { r: signature.r.toByteArray(), s: signature.s.toByteArray() }, hash_algo
);
} }
module.exports = { export default { sign, verify };
sign: sign,
verify: verify
};

View File

@ -30,45 +30,37 @@ import curves from './curves';
/** /**
* Sign a message using the provided key * Sign a message using the provided key
* @param {String} oid Elliptic curve for the key * @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 {BigInteger} d Private key used to sign * @param {BN} d Private key used to sign
* @return {{R: BN, S: BN}} Signature of the message * @return {{R: Array, S: Array}} Signature of the message
*/ */
async function sign(oid, hash_algo, m, d) { async function sign(oid, hash_algo, m, d) {
const curve = curves.get(oid); 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); 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 { return { R: signature.Rencoded(), S: signature.Sencoded() };
R: new BN(Array.from(signature.Rencoded()).reverse()),
S: new BN(Array.from(signature.Sencoded()).reverse())
};
} }
/** /**
* Verifies if a signature is valid for a message * Verifies if a signature is valid for a message
* @param {String} oid Elliptic curve for the key * @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: BigInteger, S: BigInteger}} signature Signature to verify * @param {{R: BN, S: BN}} signature Signature to verify the message
* @param {Uint8Array} m Message to verify * @param {Uint8Array} m Message to verify
* @param {BigInteger} Q Public key used to verify the message * @param {BN} 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 = curves.get(oid); 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 // 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, { return key.verify(m, {
R: [].concat(R, Array(curve.payloadSize - R.length).fill(0)), R: signature.R.toArray('le', 32),
S: [].concat(S, Array(curve.payloadSize - S.length).fill(0)) S: signature.S.toArray('le', 32)
}, hash_algo); }, hash_algo);
} }
module.exports = { export default { sign, verify };
sign: sign,
verify: verify
};

View File

@ -30,11 +30,6 @@ import ecdsa from './ecdsa';
import eddsa from './eddsa'; import eddsa from './eddsa';
import ecdh from './ecdh'; import ecdh from './ecdh';
module.exports = { export default {
ecdsa: ecdsa, ecdh, ecdsa, eddsa, get, generate, getPreferredHashAlgo
eddsa: eddsa,
ecdh: ecdh,
get: get,
generate: generate,
getPreferredHashAlgo: getPreferredHashAlgo
}; };

View File

@ -29,7 +29,7 @@
* @module crypto/public_key/elliptic/key * @module crypto/public_key/elliptic/key
*/ */
import curves from './curves'; import { webCurves, nodeCurves } from './curves';
import BigInteger from '../jsbn'; import BigInteger from '../jsbn';
import hash from '../../hash'; import hash from '../../hash';
import util from '../../../util'; import util from '../../../util';
@ -37,13 +37,7 @@ import enums from '../../../enums';
import base64 from '../../../encoding/base64'; import base64 from '../../../encoding/base64';
const webCrypto = util.getWebCrypto(); const webCrypto = util.getWebCrypto();
const { webCurves } = curves;
const nodeCrypto = util.getNodeCrypto(); 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 jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined;
const ECDSASignature = nodeCrypto ? const ECDSASignature = nodeCrypto ?
@ -54,16 +48,13 @@ const ECDSASignature = nodeCrypto ?
); );
}) : undefined; }) : undefined;
function KeyPair(curve, options) { export default function KeyPair(curve, options) {
this.curve = curve; this.curve = curve;
this.keyType = curve.curve.type === 'edwards' ? enums.publicKey.eddsa : enums.publicKey.ecdsa; this.keyType = curve.curve.type === 'edwards' ? enums.publicKey.eddsa : enums.publicKey.ecdsa;
this.keyPair = this.curve.keyPair(options); this.keyPair = this.curve.keyPair(options);
} }
KeyPair.prototype.sign = async function (message, hash_algo) { KeyPair.prototype.sign = async function (message, hash_algo) {
if (util.isString(message)) {
message = util.str2Uint8Array(message);
}
if (webCrypto && this.curve.web) { if (webCrypto && this.curve.web) {
// If browser doesn't support a curve, we'll catch it // If browser doesn't support a curve, we'll catch it
try { try {
@ -79,9 +70,6 @@ KeyPair.prototype.sign = async function (message, hash_algo) {
}; };
KeyPair.prototype.verify = async function (message, signature, 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 (webCrypto && this.curve.web) {
// If browser doesn't support a curve, we'll catch it // If browser doesn't support a curve, we'll catch it
try { try {
@ -115,10 +103,6 @@ KeyPair.prototype.getPrivate = function () {
return this.keyPair.getPrivate().toArray(); return this.keyPair.getPrivate().toArray();
}; };
module.exports = {
KeyPair: KeyPair
};
////////////////////////// //////////////////////////
// // // //

View File

@ -22,15 +22,6 @@ export default {
* @return {Boolean} true if signature (sig_data was equal to data over hash) * @return {Boolean} true if signature (sig_data was equal to data over hash)
*/ */
verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { 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) { switch (algo) {
case 1: case 1:
// RSA (Encrypt or Sign) [HAC] // RSA (Encrypt or Sign) [HAC]
@ -38,12 +29,11 @@ export default {
// RSA Encrypt-Only [HAC] // RSA Encrypt-Only [HAC]
case 3: { case 3: {
// RSA Sign-Only [HAC] // RSA Sign-Only [HAC]
const m = msg_MPIs[0].toUint8Array();
const n = publickey_MPIs[0].toUint8Array(); const n = publickey_MPIs[0].toUint8Array();
const k = publickey_MPIs[0].byteLength();
const e = publickey_MPIs[1].toUint8Array(); const e = publickey_MPIs[1].toUint8Array();
m = msg_MPIs[0].toUint8Array();
const EM = RSA_RAW.verify(m, [n, e]); 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; return util.hexidump(EM) === EM2;
} }
case 16: { case 16: {
@ -52,35 +42,27 @@ export default {
} }
case 17: { case 17: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC] // DSA (Digital Signature Algorithm) [FIPS186] [HAC]
const dsa = publicKey.dsa; const r = msg_MPIs[0].toBN();
r = msg_MPIs[0].toBN(); const s = msg_MPIs[1].toBN();
s = msg_MPIs[1].toBN();
const p = publickey_MPIs[0].toBN(); const p = publickey_MPIs[0].toBN();
const q = publickey_MPIs[1].toBN(); const q = publickey_MPIs[1].toBN();
const g = publickey_MPIs[2].toBN(); const g = publickey_MPIs[2].toBN();
const y = publickey_MPIs[3].toBN(); const y = publickey_MPIs[3].toBN();
m = util.str2Uint8Array(data); return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y);
return dsa.verify(hash_algo, r, s, m, p, q, g, y);
} }
case 19: { case 19: {
// ECDSA // ECDSA
const { ecdsa } = publicKey.elliptic; const oid = publickey_MPIs[0];
[curve] = publickey_MPIs; const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
r = msg_MPIs[0].toBigInteger(); const Q = publickey_MPIs[1].toUint8Array();
s = msg_MPIs[1].toBigInteger(); return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q);
m = data;
Q = publickey_MPIs[1].toBigInteger();
return ecdsa.verify(curve.oid, hash_algo, { r: r, s: s }, m, Q);
} }
case 22: { case 22: {
// EdDSA // EdDSA
const { eddsa } = publicKey.elliptic; const oid = publickey_MPIs[0];
[curve] = publickey_MPIs; const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() };
r = msg_MPIs[0].toBigInteger(); const Q = publickey_MPIs[1].toBN();
s = msg_MPIs[1].toBigInteger(); return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q);
m = data;
Q = publickey_MPIs[1].toBigInteger();
return eddsa.verify(curve.oid, hash_algo, { R: r, S: s }, m, Q);
} }
default: default:
throw new Error('Invalid signature algorithm.'); throw new Error('Invalid signature algorithm.');
@ -97,13 +79,6 @@ export default {
*/ */
sign: async function(algo, hash_algo, keyIntegers, data) { sign: async function(algo, hash_algo, keyIntegers, data) {
data = util.Uint8Array2str(data);
let m;
let d;
let curve;
let signature;
switch (algo) { switch (algo) {
case 1: case 1:
// RSA (Encrypt or Sign) [HAC] // RSA (Encrypt or Sign) [HAC]
@ -112,21 +87,21 @@ export default {
case 3: { case 3: {
// RSA Sign-Only [HAC] // RSA Sign-Only [HAC]
const n = keyIntegers[0].toUint8Array(); const n = keyIntegers[0].toUint8Array();
const k = keyIntegers[0].byteLength();
const e = keyIntegers[1].toUint8Array(); const e = keyIntegers[1].toUint8Array();
d = keyIntegers[2].toUint8Array(); const d = keyIntegers[2].toUint8Array();
m = util.hex2Uint8Array('00'+pkcs1.emsa.encode(hash_algo, data, k)); // FIXME remove '00' 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])); return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d]));
} }
case 17: { case 17: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC] // DSA (Digital Signature Algorithm) [FIPS186] [HAC]
const dsa = publicKey.dsa;
const p = keyIntegers[0].toBN(); const p = keyIntegers[0].toBN();
const q = keyIntegers[1].toBN(); const q = keyIntegers[1].toBN();
const g = keyIntegers[2].toBN(); const g = keyIntegers[2].toBN();
const x = keyIntegers[4].toBN(); const x = keyIntegers[4].toBN();
m = util.str2Uint8Array(data); const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x);
signature = dsa.sign(hash_algo, m, g, p, q, x);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)),
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array))
@ -138,23 +113,22 @@ export default {
} }
case 19: { case 19: {
// ECDSA // ECDSA
const { ecdsa } = publicKey.elliptic; const oid = keyIntegers[0];
[curve] = keyIntegers; const d = keyIntegers[2].toUint8Array();
d = keyIntegers[2].toBigInteger(); const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d);
m = data; return util.concatUint8Array([
signature = await ecdsa.sign(curve.oid, hash_algo, m, d); util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)),
return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI()); util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array))
]);
} }
case 22: { case 22: {
// EdDSA // EdDSA
const { eddsa } = publicKey.elliptic; const oid = keyIntegers[0];
[curve] = keyIntegers; const d = keyIntegers[2].toBN();
d = keyIntegers[2].toBigInteger(); const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d);
m = data;
signature = await eddsa.sign(curve.oid, hash_algo, m, d);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(signature.R.toArrayLike(Uint8Array, 'le', 32)), util.Uint8Array2MPI(Uint8Array.from(signature.R)),
util.Uint8Array2MPI(signature.S.toArrayLike(Uint8Array, 'le', 32)) util.Uint8Array2MPI(Uint8Array.from(signature.S))
]); ]);
} }
default: default:

View File

@ -31,7 +31,9 @@ module.exports = OID;
* @constructor * @constructor
*/ */
function OID(oid) { function OID(oid) {
if (typeof oid === 'undefined') { if (oid instanceof OID) {
oid = oid.oid;
} else if (typeof oid === 'undefined') {
oid = ''; oid = '';
} else if (util.isArray(oid)) { } else if (util.isArray(oid)) {
oid = util.bin2str(oid); oid = util.bin2str(oid);

View File

@ -9,7 +9,7 @@ const bin2bi = function (bytes) {
const mpi = new openpgp.MPI(); const mpi = new openpgp.MPI();
bytes = openpgp.util.bin2str(bytes); bytes = openpgp.util.bin2str(bytes);
mpi.fromBytes(bytes); mpi.fromBytes(bytes);
return mpi.toBigInteger(); return mpi.toUint8Array(); // FIXME
}; };
describe('Elliptic Curve Cryptography', function () { describe('Elliptic Curve Cryptography', function () {
@ -316,9 +316,9 @@ describe('Elliptic Curve Cryptography', function () {
data = new Uint8Array(data); data = new Uint8Array(data);
} }
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
const ecdh = elliptic_curves.ecdh; const curve = elliptic_curves.get(oid);
return ecdh.decrypt( return elliptic_curves.ecdh.decrypt(
oid, curve.oid,
cipher, cipher,
hash, hash,
bin2bi(ephemeral), bin2bi(ephemeral),

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].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(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'); 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].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.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'); expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh');
// Self Certificate is valid // Self Certificate is valid
@ -319,16 +319,16 @@ 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 = openpgp.crypto.publicKey.elliptic.get('ed25519'); const curve = elliptic.get('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);
const P = curve.keyFromPublic(vector.PUBLIC_KEY); const P = curve.keyFromPublic('40'+vector.PUBLIC_KEY);
expect(S.getPublic()).to.deep.equal(P.getPublic()); expect(S.getPublic()).to.deep.equal(P.getPublic());
const data = util.str2Uint8Array(vector.MESSAGE); const data = util.str2Uint8Array(vector.MESSAGE);
const keyIntegers = [ const keyIntegers = [
openpgp.OID.fromClone(curve), 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)) new openpgp.MPI(util.hex2bin(vector.SECRET_KEY))
]; ];
const msg_MPIs = [ const msg_MPIs = [