mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-03-30 15:08:32 +00:00
Clean up ECDH API
This commit is contained in:
parent
ca0322bbea
commit
e637e75891
@ -69,36 +69,34 @@ export default {
|
||||
*/
|
||||
publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) {
|
||||
const types = this.getEncSessionKeyParamTypes(algo);
|
||||
return (async function() {
|
||||
switch (algo) {
|
||||
case enums.publicKey.rsa_encrypt:
|
||||
case enums.publicKey.rsa_encrypt_sign: {
|
||||
const m = data.toBN();
|
||||
const n = pub_params[0].toBN();
|
||||
const e = pub_params[1].toBN();
|
||||
const res = await publicKey.rsa.encrypt(m, n, e);
|
||||
return constructParams(types, [res]);
|
||||
}
|
||||
case enums.publicKey.elgamal: {
|
||||
const m = data.toBN();
|
||||
const p = pub_params[0].toBN();
|
||||
const g = pub_params[1].toBN();
|
||||
const y = pub_params[2].toBN();
|
||||
const res = await publicKey.elgamal.encrypt(m, p, g, y);
|
||||
return constructParams(types, [res.c1, res.c2]);
|
||||
}
|
||||
case enums.publicKey.ecdh: {
|
||||
const oid = pub_params[0];
|
||||
const Q = pub_params[1].toUint8Array();
|
||||
const kdf_params = pub_params[2];
|
||||
const { V, C } = await publicKey.elliptic.ecdh.encrypt(
|
||||
oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint);
|
||||
return constructParams(types, [new BN(V), C]);
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
switch (algo) {
|
||||
case enums.publicKey.rsa_encrypt:
|
||||
case enums.publicKey.rsa_encrypt_sign: {
|
||||
const m = data.toBN();
|
||||
const n = pub_params[0].toBN();
|
||||
const e = pub_params[1].toBN();
|
||||
const res = await publicKey.rsa.encrypt(m, n, e);
|
||||
return constructParams(types, [res]);
|
||||
}
|
||||
}());
|
||||
case enums.publicKey.elgamal: {
|
||||
const m = data.toBN();
|
||||
const p = pub_params[0].toBN();
|
||||
const g = pub_params[1].toBN();
|
||||
const y = pub_params[2].toBN();
|
||||
const res = await publicKey.elgamal.encrypt(m, p, g, y);
|
||||
return constructParams(types, [res.c1, res.c2]);
|
||||
}
|
||||
case enums.publicKey.ecdh: {
|
||||
const oid = pub_params[0];
|
||||
const Q = pub_params[1].toUint8Array();
|
||||
const kdf_params = pub_params[2];
|
||||
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
|
||||
oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint);
|
||||
return constructParams(types, [new BN(V), C]);
|
||||
}
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -112,43 +110,41 @@ export default {
|
||||
module:type/ecdh_symkey>}
|
||||
data_params encrypted session key parameters
|
||||
* @param {String} fingerprint Recipient fingerprint
|
||||
* @returns {module:type/mpi} An MPI containing the decrypted data
|
||||
* @returns {BN} A BN containing the decrypted data
|
||||
* @async
|
||||
*/
|
||||
publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) {
|
||||
return new type_mpi(await (async function() {
|
||||
switch (algo) {
|
||||
case enums.publicKey.rsa_encrypt_sign:
|
||||
case enums.publicKey.rsa_encrypt: {
|
||||
const c = data_params[0].toBN();
|
||||
const n = key_params[0].toBN(); // n = pq
|
||||
const e = key_params[1].toBN();
|
||||
const d = key_params[2].toBN(); // de = 1 mod (p-1)(q-1)
|
||||
const p = key_params[3].toBN();
|
||||
const q = key_params[4].toBN();
|
||||
const u = key_params[5].toBN(); // q^-1 mod p
|
||||
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
|
||||
}
|
||||
case enums.publicKey.elgamal: {
|
||||
const c1 = data_params[0].toBN();
|
||||
const c2 = data_params[1].toBN();
|
||||
const p = key_params[0].toBN();
|
||||
const x = key_params[3].toBN();
|
||||
return publicKey.elgamal.decrypt(c1, c2, p, x);
|
||||
}
|
||||
case enums.publicKey.ecdh: {
|
||||
const oid = key_params[0];
|
||||
const kdf_params = key_params[2];
|
||||
const V = data_params[0].toUint8Array();
|
||||
const C = data_params[1].data;
|
||||
const d = key_params[3].toUint8Array();
|
||||
return publicKey.elliptic.ecdh.decrypt(
|
||||
oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint);
|
||||
}
|
||||
default:
|
||||
throw new Error('Invalid public key encryption algorithm.');
|
||||
switch (algo) {
|
||||
case enums.publicKey.rsa_encrypt_sign:
|
||||
case enums.publicKey.rsa_encrypt: {
|
||||
const c = data_params[0].toBN();
|
||||
const n = key_params[0].toBN(); // n = pq
|
||||
const e = key_params[1].toBN();
|
||||
const d = key_params[2].toBN(); // de = 1 mod (p-1)(q-1)
|
||||
const p = key_params[3].toBN();
|
||||
const q = key_params[4].toBN();
|
||||
const u = key_params[5].toBN(); // q^-1 mod p
|
||||
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
|
||||
}
|
||||
}()));
|
||||
case enums.publicKey.elgamal: {
|
||||
const c1 = data_params[0].toBN();
|
||||
const c2 = data_params[1].toBN();
|
||||
const p = key_params[0].toBN();
|
||||
const x = key_params[3].toBN();
|
||||
return publicKey.elgamal.decrypt(c1, c2, p, x);
|
||||
}
|
||||
case enums.publicKey.ecdh: {
|
||||
const oid = key_params[0];
|
||||
const kdf_params = key_params[2];
|
||||
const V = data_params[0].toUint8Array();
|
||||
const C = data_params[1].data;
|
||||
const d = key_params[3].toUint8Array();
|
||||
return publicKey.elliptic.ecdh.decrypt(
|
||||
oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint);
|
||||
}
|
||||
default:
|
||||
throw new Error('Invalid public key encryption algorithm.');
|
||||
}
|
||||
},
|
||||
|
||||
/** Returns the types comprising the private key of an algorithm
|
||||
|
@ -52,13 +52,10 @@ function buildEcdhParam(public_algo, oid, cipher_algo, hash_algo, fingerprint) {
|
||||
}
|
||||
|
||||
// Key Derivation Function (RFC 6637)
|
||||
async function kdf(hash_algo, S, length, param, curve, stripLeading=false, stripTrailing=false) {
|
||||
const len = curve.curve.curve.p.byteLength();
|
||||
// Note: this is not ideal, but the RFC's are unclear
|
||||
async function kdf(hash_algo, X, length, param, stripLeading=false, stripTrailing=false) {
|
||||
// Note: X is little endian for Curve25519, big-endian for all others.
|
||||
// This is not ideal, but the RFC's are unclear
|
||||
// https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-02#appendix-B
|
||||
let X = curve.curve.curve.type === 'mont' ?
|
||||
S.toArrayLike(Uint8Array, 'le', len) :
|
||||
S.toArrayLike(Uint8Array, 'be', len);
|
||||
let i;
|
||||
if (stripLeading) {
|
||||
// Work around old go crypto bug
|
||||
@ -88,23 +85,19 @@ async function kdf(hash_algo, S, length, param, curve, stripLeading=false, strip
|
||||
*/
|
||||
async function genPublicEphemeralKey(curve, Q) {
|
||||
if (curve.name === 'curve25519') {
|
||||
const { secretKey } = nacl.box.keyPair();
|
||||
const one = curve.curve.curve.one;
|
||||
const mask = one.ushln(255 - 3).sub(one).ushln(3);
|
||||
let priv = new BN(secretKey);
|
||||
priv = priv.or(one.ushln(255 - 1));
|
||||
priv = priv.and(mask);
|
||||
priv = priv.toArrayLike(Uint8Array, 'le', 32);
|
||||
const S = nacl.scalarMult(priv, Q.subarray(1));
|
||||
const { publicKey } = nacl.box.keyPair.fromSecretKey(priv);
|
||||
const ret = { V: util.concatUint8Array([new Uint8Array([0x40]), publicKey]), S: new BN(S, 'le') };
|
||||
return ret;
|
||||
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
|
||||
}
|
||||
const v = await curve.genKeyPair();
|
||||
Q = curve.keyFromPublic(Q);
|
||||
const V = new Uint8Array(v.getPublic());
|
||||
const publicKey = new Uint8Array(v.getPublic());
|
||||
const S = v.derive(Q);
|
||||
return { V, S };
|
||||
const len = curve.curve.curve.p.byteLength();
|
||||
const sharedKey = S.toArrayLike(Uint8Array, 'be', len);
|
||||
return { publicKey, sharedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,12 +114,12 @@ async function genPublicEphemeralKey(curve, Q) {
|
||||
*/
|
||||
async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) {
|
||||
const curve = new Curve(oid);
|
||||
const { V, S } = await genPublicEphemeralKey(curve, Q);
|
||||
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, S, cipher[cipher_algo].keySize, param, curve);
|
||||
const C = aes_kw.wrap(Z, m.toString());
|
||||
return { V, C };
|
||||
const Z = await kdf(hash_algo, sharedKey, cipher[cipher_algo].keySize, param);
|
||||
const wrappedKey = aes_kw.wrap(Z, m.toString());
|
||||
return { publicKey, wrappedKey };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,15 +135,20 @@ async function genPrivateEphemeralKey(curve, V, d) {
|
||||
if (curve.name === 'curve25519') {
|
||||
const one = curve.curve.curve.one;
|
||||
const mask = one.ushln(255 - 3).sub(one).ushln(3);
|
||||
let priv = new BN(d);
|
||||
priv = priv.or(one.ushln(255 - 1));
|
||||
priv = priv.and(mask);
|
||||
const S = nacl.scalarMult(priv.toArrayLike(Uint8Array, 'le', 32), V.subarray(1));
|
||||
return new BN(S, 'le');
|
||||
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
|
||||
}
|
||||
V = curve.keyFromPublic(V);
|
||||
d = curve.keyFromPrivate(d);
|
||||
return d.derive(V);
|
||||
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 };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,14 +166,14 @@ async function genPrivateEphemeralKey(curve, V, d) {
|
||||
*/
|
||||
async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) {
|
||||
const curve = new Curve(oid);
|
||||
const S = await genPrivateEphemeralKey(curve, V, d);
|
||||
const { sharedKey } = await genPrivateEphemeralKey(curve, V, d);
|
||||
const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
|
||||
cipher_algo = enums.read(enums.symmetric, cipher_algo);
|
||||
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, S, cipher[cipher_algo].keySize, param, curve, i === 1, i === 2);
|
||||
const Z = await kdf(hash_algo, sharedKey, cipher[cipher_algo].keySize, param, i === 1, i === 2);
|
||||
return new BN(aes_kw.unwrap(Z, C));
|
||||
} catch (e) {
|
||||
err = e;
|
||||
|
@ -137,8 +137,8 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
|
||||
*/
|
||||
PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
|
||||
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
||||
const result = await crypto.publicKeyDecrypt(
|
||||
algo, key.params, this.encrypted, key.getFingerprintBytes());
|
||||
const result = new type_mpi(await crypto.publicKeyDecrypt(
|
||||
algo, key.params, this.encrypted, key.getFingerprintBytes()));
|
||||
|
||||
let checksum;
|
||||
let decoded;
|
||||
|
@ -364,7 +364,7 @@ describe('API functional testing', function() {
|
||||
return crypto.publicKeyDecrypt(
|
||||
1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
|
||||
).then(data => {
|
||||
data = data.write();
|
||||
data = new openpgp.MPI(data).write();
|
||||
data = util.Uint8Array_to_str(data.subarray(2, data.length));
|
||||
|
||||
const result = crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength());
|
||||
@ -383,7 +383,7 @@ describe('API functional testing', function() {
|
||||
return crypto.publicKeyDecrypt(
|
||||
16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData
|
||||
).then(data => {
|
||||
data = data.write();
|
||||
data = new openpgp.MPI(data).write();
|
||||
data = util.Uint8Array_to_str(data.subarray(2, data.length));
|
||||
|
||||
const result = crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength());
|
||||
|
@ -429,7 +429,7 @@ describe('Elliptic Curve Cryptography', function () {
|
||||
async function genPublicEphemeralKey(curve, Q, fingerprint) {
|
||||
const curveObj = new openpgp.crypto.publicKey.elliptic.Curve(curve);
|
||||
const oid = new openpgp.OID(curveObj.oid);
|
||||
const { V, S } = await openpgp.crypto.publicKey.elliptic.ecdh.genPublicEphemeralKey(
|
||||
const { publicKey: V, sharedKey } = await openpgp.crypto.publicKey.elliptic.ecdh.genPublicEphemeralKey(
|
||||
curveObj, Q
|
||||
);
|
||||
let cipher_algo = curveObj.cipher;
|
||||
@ -439,14 +439,14 @@ describe('Elliptic Curve Cryptography', function () {
|
||||
);
|
||||
cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo);
|
||||
const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf(
|
||||
hash_algo, S, openpgp.crypto.cipher[cipher_algo].keySize, param, curveObj, false
|
||||
hash_algo, sharedKey, openpgp.crypto.cipher[cipher_algo].keySize, param, curveObj, false
|
||||
);
|
||||
return { V, Z };
|
||||
}
|
||||
async function genPrivateEphemeralKey(curve, V, d, fingerprint) {
|
||||
const curveObj = new openpgp.crypto.publicKey.elliptic.Curve(curve);
|
||||
const oid = new openpgp.OID(curveObj.oid);
|
||||
const S = await openpgp.crypto.publicKey.elliptic.ecdh.genPrivateEphemeralKey(
|
||||
const { sharedKey } = await openpgp.crypto.publicKey.elliptic.ecdh.genPrivateEphemeralKey(
|
||||
curveObj, V, d
|
||||
);
|
||||
let cipher_algo = curveObj.cipher;
|
||||
@ -456,7 +456,7 @@ describe('Elliptic Curve Cryptography', function () {
|
||||
);
|
||||
cipher_algo = openpgp.enums.read(openpgp.enums.symmetric, cipher_algo);
|
||||
const Z = await openpgp.crypto.publicKey.elliptic.ecdh.kdf(
|
||||
hash_algo, S, openpgp.crypto.cipher[cipher_algo].keySize, param, curveObj, false
|
||||
hash_algo, sharedKey, openpgp.crypto.cipher[cipher_algo].keySize, param, curveObj, false
|
||||
);
|
||||
return Z;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user