diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 74f91e85..d3bae0d3 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -91,9 +91,9 @@ export default { data = new type_mpi(pkcs5.encode(data)); const oid = pub_params[0]; const Q = pub_params[1].toUint8Array(); - const kdf_params = pub_params[2]; + const kdfParams = pub_params[2]; const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt( - oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint); + oid, kdfParams, data, Q, fingerprint); return constructParams(types, [V, C]); } default: @@ -138,13 +138,13 @@ export default { } case enums.publicKey.ecdh: { const oid = key_params[0]; - const kdf_params = key_params[2]; + const kdfParams = key_params[2]; const V = data_params[0].toUint8Array(); const C = data_params[1].data; const Q = key_params[1].toUint8Array(); const d = key_params[3].toUint8Array(); const result = new type_mpi(await publicKey.elliptic.ecdh.decrypt( - oid, kdf_params.cipher, kdf_params.hash, V, C, Q, d, fingerprint)); + oid, kdfParams, V, C, Q, d, fingerprint)); return pkcs5.decode(result.toString()); } default: @@ -285,7 +285,12 @@ export default { }); case enums.publicKey.ecdh: return publicKey.elliptic.generate(oid).then(function (keyObject) { - return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); + return constructParams(types, [ + keyObject.oid, + keyObject.Q, + { hash: keyObject.hash, cipher: keyObject.cipher }, + keyObject.d + ]); }); default: throw new Error('Invalid public key algorithm.'); diff --git a/src/crypto/public_key/elliptic/ecdh.js b/src/crypto/public_key/elliptic/ecdh.js index 5e61f410..834af7d5 100644 --- a/src/crypto/public_key/elliptic/ecdh.js +++ b/src/crypto/public_key/elliptic/ecdh.js @@ -37,7 +37,6 @@ import aes_kw from '../../aes_kw'; import cipher from '../../cipher'; import random from '../../random'; import hash from '../../hash'; -import type_kdf_params from '../../../type/kdf_params'; import enums from '../../../enums'; import util from '../../../util'; import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey'; @@ -46,12 +45,11 @@ const webCrypto = util.getWebCrypto(); const nodeCrypto = util.getNodeCrypto(); // Build Param for ECDH algorithm (RFC 6637) -function buildEcdhParam(public_algo, oid, cipher_algo, hash_algo, fingerprint) { - const kdf_params = new type_kdf_params([hash_algo, cipher_algo]); +function buildEcdhParam(public_algo, oid, kdfParams, fingerprint) { return util.concatUint8Array([ oid.write(), new Uint8Array([public_algo]), - kdf_params.write(), + kdfParams.write(), util.str_to_Uint8Array("Anonymous Sender "), fingerprint.subarray(0, 20) ]); @@ -117,20 +115,19 @@ async function genPublicEphemeralKey(curve, Q) { * Encrypt and wrap a session key * * @param {module:type/oid} oid Elliptic curve object identifier - * @param {module:enums.symmetric} cipher_algo Symmetric cipher to use - * @param {module:enums.hash} hash_algo Hash algorithm to use + * @param {module:type/kdf_params} kdfParams KDF params including cipher and algorithm to use * @param {module:type/mpi} m Value derived from session key (RFC 6637) * @param {Uint8Array} Q Recipient public key - * @param {String} fingerprint Recipient fingerprint + * @param {Uint8Array} fingerprint Recipient fingerprint * @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>} * @async */ -async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) { +async function encrypt(oid, kdfParams, m, Q, fingerprint) { const curve = new Curve(oid); const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q); - const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); - cipher_algo = enums.read(enums.symmetric, cipher_algo); - const Z = await kdf(hash_algo, sharedKey, cipher[cipher_algo].keySize, param); + const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint); + const cipher_algo = enums.read(enums.symmetric, kdfParams.cipher); + const Z = await kdf(kdfParams.hash, sharedKey, cipher[cipher_algo].keySize, param); const wrappedKey = aes_kw.wrap(Z, m.toString()); return { publicKey, wrappedKey }; } @@ -176,26 +173,25 @@ async function genPrivateEphemeralKey(curve, V, Q, d) { * Decrypt and unwrap the value derived from session key * * @param {module:type/oid} oid Elliptic curve object identifier - * @param {module:enums.symmetric} cipher_algo Symmetric cipher to use - * @param {module:enums.hash} hash_algo Hash algorithm to use + * @param {module:type/kdf_params} kdfParams KDF params including cipher and algorithm to use * @param {Uint8Array} V Public part of ephemeral key * @param {Uint8Array} C Encrypted and wrapped value derived from session key * @param {Uint8Array} Q Recipient public key * @param {Uint8Array} d Recipient private key - * @param {String} fingerprint Recipient fingerprint + * @param {Uint8Array} fingerprint Recipient fingerprint * @returns {Promise} Value derived from session key * @async */ -async function decrypt(oid, cipher_algo, hash_algo, V, C, Q, d, fingerprint) { +async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) { const curve = new Curve(oid); const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d); - const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint); - cipher_algo = enums.read(enums.symmetric, cipher_algo); + const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint); + const cipher_algo = enums.read(enums.symmetric, kdfParams.cipher); let err; for (let i = 0; i < 3; i++) { try { // Work around old go crypto bug and old OpenPGP.js bug, respectively. - const Z = await kdf(hash_algo, sharedKey, cipher[cipher_algo].keySize, param, i === 1, i === 2); + const Z = await kdf(kdfParams.hash, sharedKey, cipher[cipher_algo].keySize, param, i === 1, i === 2); return new BN(aes_kw.unwrap(Z, C)); } catch (e) { err = e; diff --git a/src/type/kdf_params.js b/src/type/kdf_params.js index 7cf1eec7..82253c90 100644 --- a/src/type/kdf_params.js +++ b/src/type/kdf_params.js @@ -27,20 +27,19 @@ * @module type/kdf_params */ -import enums from '../enums.js'; - /** * @constructor * @param {enums.hash} hash Hash algorithm * @param {enums.symmetric} cipher Symmetric algorithm */ function KDFParams(data) { - if (data && data.length === 2) { - this.hash = data[0]; - this.cipher = data[1]; + if (data) { + const { hash, cipher } = data; + this.hash = hash; + this.cipher = cipher; } else { - this.hash = enums.hash.sha1; - this.cipher = enums.symmetric.aes128; + this.hash = null; + this.cipher = null; } } @@ -67,7 +66,8 @@ KDFParams.prototype.write = function () { }; KDFParams.fromClone = function (clone) { - return new KDFParams([clone.hash, clone.cipher]); + const { hash, cipher } = clone; + return new KDFParams({ hash, cipher }); }; export default KDFParams; diff --git a/test/crypto/ecdh.js b/test/crypto/ecdh.js index b6c1968b..75394fd6 100644 --- a/test/crypto/ecdh.js +++ b/test/crypto/ecdh.js @@ -19,8 +19,7 @@ describe('ECDH key exchange @lightweight', function () { const curve = new elliptic_curves.Curve(oid); return elliptic_curves.ecdh.decrypt( new openpgp.OID(curve.oid), - cipher, - hash, + new openpgp.KDFParams({ cipher, hash }), new Uint8Array(ephemeral), data, new Uint8Array(pub), @@ -136,8 +135,9 @@ describe('ECDH key exchange @lightweight', function () { ); let cipher_algo = curveObj.cipher; const hash_algo = curveObj.hash; + const kdfParams = new openpgp.KDFParams({ cipher: cipher_algo, hash: hash_algo }); const param = openpgp.crypto.publicKey.elliptic.ecdh.buildEcdhParam( - openpgp.enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint + openpgp.enums.publicKey.ecdh, oid, kdfParams, fingerprint ); cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo); const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf( @@ -154,8 +154,9 @@ describe('ECDH key exchange @lightweight', function () { ); let cipher_algo = curveObj.cipher; const hash_algo = curveObj.hash; + const kdfParams = new openpgp.KDFParams({ cipher: cipher_algo, hash: hash_algo }); const param = openpgp.crypto.publicKey.elliptic.ecdh.buildEcdhParam( - openpgp.enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint + openpgp.enums.publicKey.ecdh, oid, kdfParams, fingerprint ); cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo); const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf( @@ -186,8 +187,9 @@ describe('ECDH key exchange @lightweight', function () { const sharedKey = result.sharedKey; let cipher_algo = curveObj.cipher; const hash_algo = curveObj.hash; + const kdfParams = new openpgp.KDFParams({ cipher: cipher_algo, hash: hash_algo }); const param = openpgp.crypto.publicKey.elliptic.ecdh.buildEcdhParam( - openpgp.enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint + openpgp.enums.publicKey.ecdh, oid, kdfParams, fingerprint ); cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo); const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf(