Implement ECDH using Web Crypto for supported (NIST) curves (#914)
This commit is contained in:
parent
32b4f2bd27
commit
1bd5689d75
|
@ -138,9 +138,10 @@ export default {
|
|||
const kdf_params = 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();
|
||||
return publicKey.elliptic.ecdh.decrypt(
|
||||
oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint);
|
||||
oid, kdf_params.cipher, kdf_params.hash, V, C, Q, d, fingerprint);
|
||||
}
|
||||
default:
|
||||
throw new Error('Invalid public key encryption algorithm.');
|
||||
|
|
|
@ -64,7 +64,8 @@ const curves = {
|
|||
cipher: enums.symmetric.aes128,
|
||||
node: nodeCurves.p256,
|
||||
web: webCurves.p256,
|
||||
payloadSize: 32
|
||||
payloadSize: 32,
|
||||
sharedSize: 256
|
||||
},
|
||||
p384: {
|
||||
oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22],
|
||||
|
@ -73,7 +74,8 @@ const curves = {
|
|||
cipher: enums.symmetric.aes192,
|
||||
node: nodeCurves.p384,
|
||||
web: webCurves.p384,
|
||||
payloadSize: 48
|
||||
payloadSize: 48,
|
||||
sharedSize: 384
|
||||
},
|
||||
p521: {
|
||||
oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23],
|
||||
|
@ -82,7 +84,8 @@ const curves = {
|
|||
cipher: enums.symmetric.aes256,
|
||||
node: nodeCurves.p521,
|
||||
web: webCurves.p521,
|
||||
payloadSize: 66
|
||||
payloadSize: 66,
|
||||
sharedSize: 528
|
||||
},
|
||||
secp256k1: {
|
||||
oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x0A],
|
||||
|
|
|
@ -39,6 +39,8 @@ import type_kdf_params from '../../../type/kdf_params';
|
|||
import enums from '../../../enums';
|
||||
import util from '../../../util';
|
||||
|
||||
const webCrypto = util.getWebCrypto();
|
||||
|
||||
// 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]);
|
||||
|
@ -84,20 +86,28 @@ async function kdf(hash_algo, X, length, param, stripLeading=false, stripTrailin
|
|||
* @async
|
||||
*/
|
||||
async function genPublicEphemeralKey(curve, Q) {
|
||||
if (curve.name === 'curve25519') {
|
||||
const { secretKey: d } = nacl.box.keyPair();
|
||||
const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, d);
|
||||
let { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
||||
publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]);
|
||||
return { publicKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below
|
||||
switch (curve.name) {
|
||||
case 'curve25519': {
|
||||
const { secretKey: d } = nacl.box.keyPair();
|
||||
const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, null, d);
|
||||
let { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
||||
publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]);
|
||||
return { publicKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below
|
||||
}
|
||||
case 'p256':
|
||||
case 'p384':
|
||||
case 'p521': {
|
||||
if (curve.web && util.getWebCrypto()) {
|
||||
try {
|
||||
const result = await webPublicEphemeralKey(curve, Q);
|
||||
return result;
|
||||
} catch (err) {
|
||||
util.print_debug_error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const v = await curve.genKeyPair();
|
||||
Q = curve.keyFromPublic(Q);
|
||||
const publicKey = new Uint8Array(v.getPublic());
|
||||
const S = v.derive(Q);
|
||||
const len = curve.curve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { publicKey, sharedKey };
|
||||
return ellipticPublicEphemeralKey(curve, Q);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,28 +137,37 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) {
|
|||
*
|
||||
* @param {Curve} curve Elliptic curve object
|
||||
* @param {Uint8Array} V Public part of ephemeral key
|
||||
* @param {Uint8Array} Q Recipient public key
|
||||
* @param {Uint8Array} d Recipient private key
|
||||
* @returns {Promise<BN>} Generated ephemeral secret
|
||||
* @async
|
||||
*/
|
||||
async function genPrivateEphemeralKey(curve, V, d) {
|
||||
if (curve.name === 'curve25519') {
|
||||
const one = new BN(1);
|
||||
const mask = one.ushln(255 - 3).sub(one).ushln(3);
|
||||
let secretKey = new BN(d);
|
||||
secretKey = secretKey.or(one.ushln(255 - 1));
|
||||
secretKey = secretKey.and(mask);
|
||||
secretKey = secretKey.toArrayLike(Uint8Array, 'le', 32);
|
||||
const sharedKey = nacl.scalarMult(secretKey, V.subarray(1));
|
||||
return { secretKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below
|
||||
async function genPrivateEphemeralKey(curve, V, Q, d) {
|
||||
switch (curve.name) {
|
||||
case 'curve25519': {
|
||||
const one = new BN(1);
|
||||
const mask = one.ushln(255 - 3).sub(one).ushln(3);
|
||||
let secretKey = new BN(d);
|
||||
secretKey = secretKey.or(one.ushln(255 - 1));
|
||||
secretKey = secretKey.and(mask);
|
||||
secretKey = secretKey.toArrayLike(Uint8Array, 'le', 32);
|
||||
const sharedKey = nacl.scalarMult(secretKey, V.subarray(1));
|
||||
return { secretKey, sharedKey }; // Note: sharedKey is little-endian here, unlike below
|
||||
}
|
||||
case 'p256':
|
||||
case 'p384':
|
||||
case 'p521': {
|
||||
if (curve.web && util.getWebCrypto()) {
|
||||
try {
|
||||
const result = await webPrivateEphemeralKey(curve, V, Q, d);
|
||||
return result;
|
||||
} catch (err) {
|
||||
util.print_debug_error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
V = curve.keyFromPublic(V);
|
||||
d = curve.keyFromPrivate(d);
|
||||
const secretKey = new Uint8Array(d.getPrivate());
|
||||
const S = d.derive(V);
|
||||
const len = curve.curve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { secretKey, sharedKey };
|
||||
return ellipticPrivateEphemeralKey(curve, V, d);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,14 +178,15 @@ async function genPrivateEphemeralKey(curve, V, d) {
|
|||
* @param {module:enums.hash} hash_algo Hash 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
|
||||
* @returns {Promise<BN>} Value derived from session
|
||||
* @async
|
||||
*/
|
||||
async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) {
|
||||
async function decrypt(oid, cipher_algo, hash_algo, V, C, Q, d, fingerprint) {
|
||||
const curve = new Curve(oid);
|
||||
const { sharedKey } = await genPrivateEphemeralKey(curve, V, d);
|
||||
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);
|
||||
let err;
|
||||
|
@ -182,4 +202,187 @@ async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) {
|
|||
throw err;
|
||||
}
|
||||
|
||||
export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf };
|
||||
/**
|
||||
* Generate ECDHE secret from private key and public part of ephemeral key using webCrypto
|
||||
*
|
||||
* @param {Curve} curve Elliptic curve object
|
||||
* @param {Uint8Array} V Public part of ephemeral key
|
||||
* @param {Uint8Array} Q Recipient public key
|
||||
* @param {Uint8Array} d Recipient private key
|
||||
* @returns {Promise<BN>} Generated ephemeral secret
|
||||
* @async
|
||||
*/
|
||||
async function webPrivateEphemeralKey(curve, V, Q, d) {
|
||||
const recipient = privateToJwk(curve.payloadSize, curve.web.web, d, Q);
|
||||
let privateKey = webCrypto.importKey(
|
||||
"jwk",
|
||||
recipient,
|
||||
{
|
||||
name: "ECDH",
|
||||
namedCurve: curve.web.web
|
||||
},
|
||||
true,
|
||||
["deriveKey", "deriveBits"]
|
||||
);
|
||||
const jwk = rawPublicToJwk(curve.payloadSize, curve.web.web, V);
|
||||
let sender = webCrypto.importKey(
|
||||
"jwk",
|
||||
jwk,
|
||||
{
|
||||
name: "ECDH",
|
||||
namedCurve: curve.web.web
|
||||
},
|
||||
true,
|
||||
[]
|
||||
);
|
||||
[privateKey, sender] = await Promise.all([privateKey, sender]);
|
||||
let S = webCrypto.deriveBits(
|
||||
{
|
||||
name: "ECDH",
|
||||
namedCurve: curve.web.web,
|
||||
public: sender
|
||||
},
|
||||
privateKey,
|
||||
curve.web.sharedSize
|
||||
);
|
||||
let secret = webCrypto.exportKey(
|
||||
"jwk",
|
||||
privateKey
|
||||
);
|
||||
[S, secret] = await Promise.all([S, secret]);
|
||||
const sharedKey = new Uint8Array(S);
|
||||
const secretKey = util.b64_to_Uint8Array(secret.d, true);
|
||||
return { secretKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE ephemeral key and secret from public key using webCrypto
|
||||
*
|
||||
* @param {Curve} curve Elliptic curve object
|
||||
* @param {Uint8Array} Q Recipient public key
|
||||
* @returns {Promise<{V: Uint8Array, S: BN}>} Returns public part of ephemeral key and generated ephemeral secret
|
||||
* @async
|
||||
*/
|
||||
async function webPublicEphemeralKey(curve, Q) {
|
||||
const jwk = rawPublicToJwk(curve.payloadSize, curve.web.web, Q);
|
||||
let keyPair = webCrypto.generateKey(
|
||||
{
|
||||
name: "ECDH",
|
||||
namedCurve: curve.web.web
|
||||
},
|
||||
true,
|
||||
["deriveKey", "deriveBits"]
|
||||
);
|
||||
let recipient = webCrypto.importKey(
|
||||
"jwk",
|
||||
jwk,
|
||||
{
|
||||
name: "ECDH",
|
||||
namedCurve: curve.web.web
|
||||
},
|
||||
false,
|
||||
[]
|
||||
);
|
||||
[keyPair, recipient] = await Promise.all([keyPair, recipient]);
|
||||
let s = webCrypto.deriveBits(
|
||||
{
|
||||
name: "ECDH",
|
||||
namedCurve: curve.web.web,
|
||||
public: recipient
|
||||
},
|
||||
keyPair.privateKey,
|
||||
curve.web.sharedSize
|
||||
);
|
||||
let p = webCrypto.exportKey(
|
||||
"jwk",
|
||||
keyPair.publicKey
|
||||
);
|
||||
[s, p] = await Promise.all([s, p]);
|
||||
const sharedKey = new Uint8Array(s);
|
||||
const publicKey = new Uint8Array(jwkToRawPublic(p));
|
||||
return { publicKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE secret from private key and public part of ephemeral key using webCrypto
|
||||
*
|
||||
* @param {Curve} curve Elliptic curve object
|
||||
* @param {Uint8Array} V Public part of ephemeral key
|
||||
* @param {Uint8Array} d Recipient private key
|
||||
* @returns {Promise<BN>} Generated ephemeral secret
|
||||
* @async
|
||||
*/
|
||||
async function ellipticPrivateEphemeralKey(curve, V, d) {
|
||||
V = curve.keyFromPublic(V);
|
||||
d = curve.keyFromPrivate(d);
|
||||
const secretKey = new Uint8Array(d.getPrivate());
|
||||
const S = d.derive(V);
|
||||
const len = curve.curve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { secretKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ECDHE ephemeral key and secret from public key using indutny/elliptic library
|
||||
*
|
||||
* @param {Curve} curve Elliptic curve object
|
||||
* @param {Uint8Array} Q Recipient public key
|
||||
* @returns {Promise<{V: Uint8Array, S: BN}>} Returns public part of ephemeral key and generated ephemeral secret
|
||||
* @async
|
||||
*/
|
||||
async function ellipticPublicEphemeralKey(curve, Q) {
|
||||
const v = await curve.genKeyPair();
|
||||
Q = curve.keyFromPublic(Q);
|
||||
const publicKey = new Uint8Array(v.getPublic());
|
||||
const S = v.derive(Q);
|
||||
const len = curve.curve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { publicKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Integer} payloadSize ec payload size
|
||||
* @param {String} name curve name
|
||||
* @param {Uint8Array} publicKey public key
|
||||
*/
|
||||
function rawPublicToJwk(payloadSize, name, publicKey) {
|
||||
const len = payloadSize;
|
||||
const bufX = publicKey.slice(1, len+1);
|
||||
const bufY = publicKey.slice(len+1, len*2+1);
|
||||
// https://www.rfc-editor.org/rfc/rfc7518.txt
|
||||
const jwKey = {
|
||||
kty: "EC",
|
||||
crv: name,
|
||||
x: util.Uint8Array_to_b64(bufX, true),
|
||||
y: util.Uint8Array_to_b64(bufY, true),
|
||||
ext: true
|
||||
};
|
||||
return jwKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Integer} payloadSize ec payload size
|
||||
* @param {String} name curve name
|
||||
* @param {Uint8Array} publicKey public key
|
||||
* @param {Uint8Array} privateKey private key
|
||||
*/
|
||||
function privateToJwk(payloadSize, name, privateKey, publicKey) {
|
||||
const jwk = rawPublicToJwk(payloadSize, name, publicKey);
|
||||
jwk.d = util.Uint8Array_to_b64(privateKey, true);
|
||||
return jwk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JsonWebKey} jwk key for conversion
|
||||
*/
|
||||
function jwkToRawPublic(jwk) {
|
||||
const bufX = util.b64_to_Uint8Array(jwk.x);
|
||||
const bufY = util.b64_to_Uint8Array(jwk.y);
|
||||
const publicKey = new Uint8Array(bufX.length + bufY.length + 1);
|
||||
publicKey[0] = 0x04;
|
||||
publicKey.set(bufX, 1);
|
||||
publicKey.set(bufY, bufX.length+1);
|
||||
return publicKey;
|
||||
}
|
||||
|
||||
export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, webPrivateEphemeralKey };
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
||||
|
||||
const BN = require('bn.js');
|
||||
const chai = require('chai');
|
||||
|
||||
chai.use(require('chai-as-promised'));
|
||||
|
||||
const expect = chai.expect;
|
||||
|
@ -25,7 +25,8 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
0x6F, 0xAA, 0xE7, 0xFD, 0xC4, 0x7D, 0x89, 0xCC,
|
||||
0x06, 0xCA, 0xFE, 0xAE, 0xCD, 0x0E, 0x9E, 0x62,
|
||||
0x57, 0xA4, 0xC3, 0xE7, 0x5E, 0x69, 0x10, 0xEE,
|
||||
0x67, 0xC2, 0x09, 0xF9, 0xEF, 0xE7, 0x9E, 0x56])
|
||||
0x67, 0xC2, 0x09, 0xF9, 0xEF, 0xE7, 0x9E, 0x56
|
||||
])
|
||||
},
|
||||
p384: {
|
||||
priv: new Uint8Array([
|
||||
|
@ -324,7 +325,7 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
});
|
||||
});
|
||||
describe('ECDH key exchange', function () {
|
||||
const decrypt_message = function (oid, hash, cipher, priv, ephemeral, data, fingerprint) {
|
||||
const decrypt_message = function (oid, hash, cipher, priv, pub, ephemeral, data, fingerprint) {
|
||||
if (openpgp.util.isString(data)) {
|
||||
data = openpgp.util.str_to_Uint8Array(data);
|
||||
} else {
|
||||
|
@ -338,6 +339,7 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
hash,
|
||||
new Uint8Array(ephemeral),
|
||||
data,
|
||||
new Uint8Array(pub),
|
||||
new Uint8Array(priv),
|
||||
new Uint8Array(fingerprint)
|
||||
);
|
||||
|
@ -376,26 +378,26 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
|
||||
it('Invalid curve oid', function (done) {
|
||||
expect(decrypt_message(
|
||||
'', 2, 7, [], [], [], []
|
||||
'', 2, 7, [], [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Not valid curve/).notify(done);
|
||||
});
|
||||
it('Invalid ephemeral key', function (done) {
|
||||
expect(decrypt_message(
|
||||
'secp256k1', 2, 7, [], [], [], []
|
||||
'secp256k1', 2, 7, [], [], [], [], []
|
||||
)).to.be.rejectedWith(Error, /Unknown point format/).notify(done);
|
||||
});
|
||||
it('Invalid elliptic public key', function (done) {
|
||||
expect(decrypt_message(
|
||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_invalid_point, secp256k1_data, []
|
||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_invalid_point, secp256k1_data, []
|
||||
)).to.be.rejectedWith(Error, /Invalid elliptic public key/).notify(done);
|
||||
});
|
||||
it('Invalid key data integrity', function (done) {
|
||||
expect(decrypt_message(
|
||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_data, []
|
||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_point, secp256k1_data, []
|
||||
)).to.be.rejectedWith(Error, /Key Data Integrity failed/).notify(done);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
const Q1 = new Uint8Array([
|
||||
64,
|
||||
48, 226, 162, 114, 194, 194, 67, 214,
|
||||
|
@ -443,10 +445,47 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
);
|
||||
return { V, Z };
|
||||
}
|
||||
async function genPrivateEphemeralKey(curve, V, d, fingerprint) {
|
||||
|
||||
async function genPrivateEphemeralKey(curve, V, Q, d, fingerprint) {
|
||||
const curveObj = new openpgp.crypto.publicKey.elliptic.Curve(curve);
|
||||
const oid = new openpgp.OID(curveObj.oid);
|
||||
const { sharedKey } = await openpgp.crypto.publicKey.elliptic.ecdh.genPrivateEphemeralKey(
|
||||
curveObj, V, Q, d
|
||||
);
|
||||
let cipher_algo = curveObj.cipher;
|
||||
const hash_algo = curveObj.hash;
|
||||
const param = openpgp.crypto.publicKey.elliptic.ecdh.buildEcdhParam(
|
||||
openpgp.enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint
|
||||
);
|
||||
cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo);
|
||||
const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf(
|
||||
hash_algo, sharedKey, openpgp.crypto.cipher[cipher_algo].keySize, param, curveObj, false
|
||||
);
|
||||
return Z;
|
||||
}
|
||||
|
||||
async function genWebPrivateEphemeralKey(curve, V, Q, d, fingerprint) {
|
||||
const curveObj = new openpgp.crypto.publicKey.elliptic.Curve(curve);
|
||||
const oid = new openpgp.OID(curveObj.oid);
|
||||
const { sharedKey } = await openpgp.crypto.publicKey.elliptic.ecdh.webPrivateEphemeralKey(
|
||||
curveObj, V, Q, d
|
||||
);
|
||||
let cipher_algo = curveObj.cipher;
|
||||
const hash_algo = curveObj.hash;
|
||||
const param = openpgp.crypto.publicKey.elliptic.ecdh.buildEcdhParam(
|
||||
openpgp.enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint
|
||||
);
|
||||
cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo);
|
||||
const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf(
|
||||
hash_algo, sharedKey, openpgp.crypto.cipher[cipher_algo].keySize, param, curveObj, false
|
||||
);
|
||||
return Z;
|
||||
}
|
||||
|
||||
async function genEllipticPrivateEphemeralKey(curve, V, Q, d, fingerprint) {
|
||||
const curveObj = new openpgp.crypto.publicKey.elliptic.Curve(curve);
|
||||
const oid = new openpgp.OID(curveObj.oid);
|
||||
const { sharedKey } = await openpgp.crypto.publicKey.elliptic.ecdh.ellipticPrivateEphemeralKey(
|
||||
curveObj, V, d
|
||||
);
|
||||
let cipher_algo = curveObj.cipher;
|
||||
|
@ -460,6 +499,7 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
);
|
||||
return Z;
|
||||
}
|
||||
|
||||
describe('ECDHE key generation', function () {
|
||||
it('Invalid curve', function (done) {
|
||||
expect(genPublicEphemeralKey("secp256k1", Q1, fingerprint1)
|
||||
|
@ -467,24 +507,62 @@ describe('Elliptic Curve Cryptography', function () {
|
|||
});
|
||||
it('Invalid public part of ephemeral key and private key', async function () {
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey("curve25519", Q1, fingerprint1);
|
||||
const ECDHE_Z12 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ1.V, d2, fingerprint1);
|
||||
const ECDHE_Z12 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ1.V, Q2, d2, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z12).join(' ') === Array.from(ECDHE_VZ1.Z).join(' ')).to.be.false;
|
||||
});
|
||||
it('Invalid fingerprint', async function () {
|
||||
const ECDHE_VZ2 = await genPublicEphemeralKey("curve25519", Q2, fingerprint1);
|
||||
const ECDHE_Z2 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ2.V, d2, fingerprint2);
|
||||
const ECDHE_Z2 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ2.V, Q2, d2, fingerprint2);
|
||||
expect(Array.from(ECDHE_Z2).join(' ') === Array.from(ECDHE_VZ2.Z).join(' ')).to.be.false;
|
||||
});
|
||||
it('Different keys', async function () {
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey("curve25519", Q1, fingerprint1);
|
||||
const ECDHE_VZ2 = await genPublicEphemeralKey("curve25519", Q2, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ1.V, d1, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ1.V, Q1, d1, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_VZ2.Z).join(' ')).to.be.false;
|
||||
});
|
||||
it('Successful exchange', async function () {
|
||||
it('Successful exchange curve25519', async function () {
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey("curve25519", Q1, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ1.V, d1, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("curve25519", ECDHE_VZ1.V, Q1, d1, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_VZ1.Z).join(' ')).to.be.true;
|
||||
});
|
||||
});
|
||||
it('Successful exchange NIST P256', async function () {
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey("p256", key_data.p256.pub, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("p256", ECDHE_VZ1.V, key_data.p256.pub, key_data.p256.priv, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_VZ1.Z).join(' ')).to.be.true;
|
||||
});
|
||||
it('Successful exchange NIST P384', async function () {
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey("p384", key_data.p384.pub, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("p384", ECDHE_VZ1.V, key_data.p384.pub, key_data.p384.priv, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_VZ1.Z).join(' ')).to.be.true;
|
||||
});
|
||||
it('Successful exchange NIST P521', async function () {
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey("p521", key_data.p521.pub, fingerprint1);
|
||||
const ECDHE_Z1 = await genPrivateEphemeralKey("p521", ECDHE_VZ1.V, key_data.p521.pub, key_data.p521.priv, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_VZ1.Z).join(' ')).to.be.true;
|
||||
});
|
||||
it('Comparing keys derived using different algorithms', async function () {
|
||||
const names = ["p256", "p384", "p521"];
|
||||
if (!openpgp.util.getWebCrypto()) {
|
||||
this.skip();
|
||||
}
|
||||
return Promise.all(names.map(async function (name) {
|
||||
const curve = new elliptic_curves.Curve(name);
|
||||
try {
|
||||
await window.crypto.subtle.generateKey({
|
||||
name: "ECDSA",
|
||||
namedCurve: curve.web.web
|
||||
}, false, ["sign", "verify"]);
|
||||
} catch(err) {
|
||||
openpgp.util.print_debug_error(err);
|
||||
return;
|
||||
}
|
||||
const ECDHE_VZ1 = await genPublicEphemeralKey(name, key_data[name].pub, fingerprint1);
|
||||
const ECDHE_Z1 = await genEllipticPrivateEphemeralKey(name, ECDHE_VZ1.V, key_data[name].pub, key_data[name].priv, fingerprint1);
|
||||
const ECDHE_Z2 = await genWebPrivateEphemeralKey(name, ECDHE_VZ1.V, key_data[name].pub, key_data[name].priv, fingerprint1);
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_VZ1.Z).join(' ')).to.be.true;
|
||||
expect(Array.from(ECDHE_Z1).join(' ') === Array.from(ECDHE_Z2).join(' ')).to.be.true;
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user