Store named key params in key objects (#1141)
- Store private and public params separately and by name in objects, instead of as an array - Do not keep params in MPI form, but convert them to Uint8Arrays when generating/parsing the key - Modify low-level crypto functions to always accept and return Uint8Arrays instead of BigIntegers - Move PKCS1 padding to lower level functions
This commit is contained in:
parent
8854b097b4
commit
3a75eadaa0
|
@ -36,13 +36,12 @@ import publicKey from './public_key';
|
||||||
import cipher from './cipher';
|
import cipher from './cipher';
|
||||||
import random from './random';
|
import random from './random';
|
||||||
import type_ecdh_symkey from '../type/ecdh_symkey';
|
import type_ecdh_symkey from '../type/ecdh_symkey';
|
||||||
import type_kdf_params from '../type/kdf_params';
|
import KDFParams from '../type/kdf_params';
|
||||||
import type_mpi from '../type/mpi';
|
import type_mpi from '../type/mpi';
|
||||||
import type_oid from '../type/oid';
|
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
import pkcs1 from './pkcs1';
|
import OID from '../type/oid';
|
||||||
import pkcs5 from './pkcs5';
|
import Curve from './public_key/elliptic/curves';
|
||||||
|
|
||||||
function constructParams(types, data) {
|
function constructParams(types, data) {
|
||||||
return types.map(function(type, i) {
|
return types.map(function(type, i) {
|
||||||
|
@ -57,40 +56,30 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Encrypts data using specified algorithm and public key parameters.
|
* Encrypts data using specified algorithm and public key parameters.
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
|
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
|
||||||
* @param {module:enums.publicKey} algo Public key algorithm
|
* @param {module:enums.publicKey} algo Public key algorithm
|
||||||
* @param {Array<module:type/mpi|
|
* @param {Object} pubParams Algorithm-specific public key parameters
|
||||||
module:type/oid|
|
* @param {Uint8Array} data Data to be encrypted
|
||||||
module:type/kdf_params>} pub_params Algorithm-specific public key parameters
|
* @param {Uint8Array} fingerprint Recipient fingerprint
|
||||||
* @param {Uint8Array} data Data to be encrypted
|
|
||||||
* @param {Uint8Array} fingerprint Recipient fingerprint
|
|
||||||
* @returns {Array<module:type/mpi|
|
* @returns {Array<module:type/mpi|
|
||||||
* module:type/ecdh_symkey>} encrypted session key parameters
|
* module:type/ecdh_symkey>} encrypted session key parameters
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) {
|
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
|
||||||
const types = this.getEncSessionKeyParamTypes(algo);
|
const types = this.getEncSessionKeyParamTypes(algo);
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncryptSign: {
|
case enums.publicKey.rsaEncryptSign: {
|
||||||
const n = pub_params[0].toUint8Array();
|
const { n, e } = publicParams;
|
||||||
const e = pub_params[1].toUint8Array();
|
|
||||||
const res = await publicKey.rsa.encrypt(data, n, e);
|
const res = await publicKey.rsa.encrypt(data, n, e);
|
||||||
return constructParams(types, [res]);
|
return constructParams(types, [res]);
|
||||||
}
|
}
|
||||||
case enums.publicKey.elgamal: {
|
case enums.publicKey.elgamal: {
|
||||||
data = new type_mpi(await pkcs1.eme.encode(data, pub_params[0].byteLength()));
|
const { p, g, y } = publicParams;
|
||||||
const m = await data.toBigInteger();
|
const res = await publicKey.elgamal.encrypt(data, p, g, y);
|
||||||
const p = await pub_params[0].toBigInteger();
|
|
||||||
const g = await pub_params[1].toBigInteger();
|
|
||||||
const y = await pub_params[2].toBigInteger();
|
|
||||||
const res = await publicKey.elgamal.encrypt(m, p, g, y);
|
|
||||||
return constructParams(types, [res.c1, res.c2]);
|
return constructParams(types, [res.c1, res.c2]);
|
||||||
}
|
}
|
||||||
case enums.publicKey.ecdh: {
|
case enums.publicKey.ecdh: {
|
||||||
data = new type_mpi(pkcs5.encode(data));
|
const { oid, Q, kdfParams } = publicParams;
|
||||||
const oid = pub_params[0];
|
|
||||||
const Q = pub_params[1].toUint8Array();
|
|
||||||
const kdfParams = pub_params[2];
|
|
||||||
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
|
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
|
||||||
oid, kdfParams, data, Q, fingerprint);
|
oid, kdfParams, data, Q, fingerprint);
|
||||||
return constructParams(types, [V, C]);
|
return constructParams(types, [V, C]);
|
||||||
|
@ -104,125 +93,133 @@ export default {
|
||||||
* Decrypts data using specified algorithm and private key parameters.
|
* Decrypts data using specified algorithm and private key parameters.
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
|
* See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
|
||||||
* @param {module:enums.publicKey} algo Public key algorithm
|
* @param {module:enums.publicKey} algo Public key algorithm
|
||||||
* @param {Array<module:type/mpi|
|
* @param {Object} publicKeyParams Algorithm-specific public key parameters
|
||||||
module:type/oid|
|
* @param {Object} privateKeyParams Algorithm-specific private key parameters
|
||||||
module:type/kdf_params>} key_params Algorithm-specific public, private key parameters
|
|
||||||
* @param {Array<module:type/mpi|
|
* @param {Array<module:type/mpi|
|
||||||
module:type/ecdh_symkey>}
|
module:type/ecdh_symkey>}
|
||||||
data_params encrypted session key parameters
|
data_params encrypted session key parameters
|
||||||
* @param {String} fingerprint Recipient fingerprint
|
* @param {Uint8Array} fingerprint Recipient fingerprint
|
||||||
* @returns {String} String containing the decrypted data
|
* @returns {Uint8Array} decrypted data
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) {
|
publicKeyDecrypt: async function(algo, publicKeyParams, privateKeyParams, data_params, fingerprint) {
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaEncrypt: {
|
case enums.publicKey.rsaEncrypt: {
|
||||||
const c = data_params[0].toUint8Array();
|
const c = data_params[0].toUint8Array();
|
||||||
const n = key_params[0].toUint8Array(); // n = pq
|
const { n, e } = publicKeyParams;
|
||||||
const e = key_params[1].toUint8Array();
|
const { d, p, q, u } = privateKeyParams;
|
||||||
const d = key_params[2].toUint8Array(); // de = 1 mod (p-1)(q-1)
|
|
||||||
const p = key_params[3].toUint8Array();
|
|
||||||
const q = key_params[4].toUint8Array();
|
|
||||||
const u = key_params[5].toUint8Array(); // p^-1 mod q
|
|
||||||
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
|
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
|
||||||
}
|
}
|
||||||
case enums.publicKey.elgamal: {
|
case enums.publicKey.elgamal: {
|
||||||
const c1 = await data_params[0].toBigInteger();
|
const c1 = data_params[0].toUint8Array();
|
||||||
const c2 = await data_params[1].toBigInteger();
|
const c2 = data_params[1].toUint8Array();
|
||||||
const p = await key_params[0].toBigInteger();
|
const p = publicKeyParams.p;
|
||||||
const x = await key_params[3].toBigInteger();
|
const x = privateKeyParams.x;
|
||||||
const result = new type_mpi(await publicKey.elgamal.decrypt(c1, c2, p, x));
|
return publicKey.elgamal.decrypt(c1, c2, p, x);
|
||||||
return pkcs1.eme.decode(result.toUint8Array('be', p.byteLength()));
|
|
||||||
}
|
}
|
||||||
case enums.publicKey.ecdh: {
|
case enums.publicKey.ecdh: {
|
||||||
const oid = key_params[0];
|
const { oid, Q, kdfParams } = publicKeyParams;
|
||||||
const kdfParams = key_params[2];
|
const { d } = privateKeyParams;
|
||||||
const V = data_params[0].toUint8Array();
|
const V = data_params[0].toUint8Array();
|
||||||
const C = data_params[1].data;
|
const C = data_params[1].data;
|
||||||
const Q = key_params[1].toUint8Array();
|
return publicKey.elliptic.ecdh.decrypt(
|
||||||
const d = key_params[3].toUint8Array();
|
oid, kdfParams, V, C, Q, d, fingerprint);
|
||||||
const result = new type_mpi(await publicKey.elliptic.ecdh.decrypt(
|
|
||||||
oid, kdfParams, V, C, Q, d, fingerprint));
|
|
||||||
return pkcs5.decode(result.toUint8Array());
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid public key encryption algorithm.');
|
throw new Error('Invalid public key encryption algorithm.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Returns the types comprising the private key of an algorithm
|
/**
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
* Parse public key material in binary form to get the key parameters
|
||||||
* @returns {Array<Object>} The array of types
|
* @param {module:enums.publicKey} algo The key algorithm
|
||||||
|
* @param {Uint8Array} bytes The key material to parse
|
||||||
|
* @returns {Object} key parameters referenced by name
|
||||||
|
* @returns { read: Number, publicParams: Object } number of read bytes plus key parameters referenced by name
|
||||||
*/
|
*/
|
||||||
getPrivKeyParamTypes: function(algo) {
|
parsePublicKeyParams: function(algo, bytes) {
|
||||||
|
let read = 0;
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
// Algorithm-Specific Fields for RSA secret keys:
|
|
||||||
// - multiprecision integer (MPI) of RSA secret exponent d.
|
|
||||||
// - MPI of RSA secret prime value p.
|
|
||||||
// - MPI of RSA secret prime value q (p < q).
|
|
||||||
// - MPI of u, the multiplicative inverse of p, mod q.
|
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaSign:
|
case enums.publicKey.rsaSign: {
|
||||||
return [type_mpi, type_mpi, type_mpi, type_mpi];
|
let read = 0;
|
||||||
// Algorithm-Specific Fields for Elgamal secret keys:
|
const n = util.readMPI(bytes.subarray(read)); read += n.length + 2;
|
||||||
// - MPI of Elgamal secret exponent x.
|
const e = util.readMPI(bytes.subarray(read)); read += e.length + 2;
|
||||||
case enums.publicKey.elgamal:
|
return { read, publicParams: { n, e } };
|
||||||
return [type_mpi];
|
}
|
||||||
// Algorithm-Specific Fields for DSA secret keys:
|
case enums.publicKey.dsa: {
|
||||||
// - MPI of DSA secret exponent x.
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
||||||
case enums.publicKey.dsa:
|
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
|
||||||
return [type_mpi];
|
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
|
||||||
// Algorithm-Specific Fields for ECDSA or ECDH secret keys:
|
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
|
||||||
// - MPI of an integer representing the secret key.
|
return { read, publicParams: { p, q, g, y } };
|
||||||
case enums.publicKey.ecdh:
|
}
|
||||||
case enums.publicKey.ecdsa:
|
case enums.publicKey.elgamal: {
|
||||||
case enums.publicKey.eddsa:
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
||||||
return [type_mpi];
|
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
|
||||||
|
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
|
||||||
|
return { read, publicParams: { p, g, y } };
|
||||||
|
}
|
||||||
|
case enums.publicKey.ecdsa: {
|
||||||
|
const oid = new OID(); read += oid.read(bytes);
|
||||||
|
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
|
||||||
|
return { read: read, publicParams: { oid, Q } };
|
||||||
|
}
|
||||||
|
case enums.publicKey.eddsa: {
|
||||||
|
const oid = new OID(); read += oid.read(bytes);
|
||||||
|
let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
|
||||||
|
Q = util.padToLength(Q, 33);
|
||||||
|
return { read: read, publicParams: { oid, Q } };
|
||||||
|
}
|
||||||
|
case enums.publicKey.ecdh: {
|
||||||
|
const oid = new OID(); read += oid.read(bytes);
|
||||||
|
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
|
||||||
|
const kdfParams = new KDFParams(); read += kdfParams.read(bytes.subarray(read));
|
||||||
|
return { read: read, publicParams: { oid, Q, kdfParams } };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid public key encryption algorithm.');
|
throw new Error('Invalid public key encryption algorithm.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Returns the types comprising the public key of an algorithm
|
/**
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
* Parse private key material in binary form to get the key parameters
|
||||||
* @returns {Array<Object>} The array of types
|
* @param {module:enums.publicKey} algo The key algorithm
|
||||||
|
* @param {Uint8Array} bytes The key material to parse
|
||||||
|
* @param {Object} publicParams (ECC only) public params, needed to format some private params
|
||||||
|
* @returns { read: Number, privateParams: Object } number of read bytes plus the key parameters referenced by name
|
||||||
*/
|
*/
|
||||||
getPubKeyParamTypes: function(algo) {
|
parsePrivateKeyParams: function(algo, bytes, publicParams) {
|
||||||
|
let read = 0;
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
// Algorithm-Specific Fields for RSA public keys:
|
|
||||||
// - a multiprecision integer (MPI) of RSA public modulus n;
|
|
||||||
// - an MPI of RSA public encryption exponent e.
|
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaSign:
|
case enums.publicKey.rsaSign: {
|
||||||
return [type_mpi, type_mpi];
|
const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
|
||||||
// Algorithm-Specific Fields for Elgamal public keys:
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
||||||
// - MPI of Elgamal prime p;
|
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
|
||||||
// - MPI of Elgamal group generator g;
|
const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
|
||||||
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
|
return { read, privateParams: { d, p, q, u } };
|
||||||
case enums.publicKey.elgamal:
|
}
|
||||||
return [type_mpi, type_mpi, type_mpi];
|
|
||||||
// Algorithm-Specific Fields for DSA public keys:
|
|
||||||
// - MPI of DSA prime p;
|
|
||||||
// - MPI of DSA group order q (q is a prime divisor of p-1);
|
|
||||||
// - MPI of DSA group generator g;
|
|
||||||
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
|
|
||||||
case enums.publicKey.dsa:
|
case enums.publicKey.dsa:
|
||||||
return [type_mpi, type_mpi, type_mpi, type_mpi];
|
case enums.publicKey.elgamal: {
|
||||||
// Algorithm-Specific Fields for ECDSA/EdDSA public keys:
|
const x = util.readMPI(bytes.subarray(read)); read += x.length + 2;
|
||||||
// - OID of curve;
|
return { read, privateParams: { x } };
|
||||||
// - MPI of EC point representing public key.
|
}
|
||||||
case enums.publicKey.ecdsa:
|
case enums.publicKey.ecdsa:
|
||||||
case enums.publicKey.eddsa:
|
case enums.publicKey.ecdh: {
|
||||||
return [type_oid, type_mpi];
|
const curve = new Curve(publicParams.oid);
|
||||||
// Algorithm-Specific Fields for ECDH public keys:
|
let d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
|
||||||
// - OID of curve;
|
d = util.padToLength(d, curve.payloadSize);
|
||||||
// - MPI of EC point representing public key.
|
return { read, privateParams: { d } };
|
||||||
// - KDF: variable-length field containing KDF parameters.
|
}
|
||||||
case enums.publicKey.ecdh:
|
case enums.publicKey.eddsa: {
|
||||||
return [type_oid, type_mpi, type_kdf_params];
|
let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2;
|
||||||
|
seed = util.padToLength(seed, 32);
|
||||||
|
return { read, privateParams: { seed } };
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid public key encryption algorithm.');
|
throw new Error('Invalid public key encryption algorithm.');
|
||||||
}
|
}
|
||||||
|
@ -255,42 +252,60 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Generate algorithm-specific key parameters
|
/**
|
||||||
|
* Convert params to MPI and serializes them in the proper order
|
||||||
|
* @param {module:enums.publicKey} algo The public key algorithm
|
||||||
|
* @param {Object} params The key parameters indexed by name
|
||||||
|
* @returns {Uint8Array} The array containing the MPIs
|
||||||
|
*/
|
||||||
|
serializeKeyParams: function(algo, params) {
|
||||||
|
const orderedParams = Object.keys(params).map(name => {
|
||||||
|
const param = params[name];
|
||||||
|
return util.isUint8Array(param) ? util.uint8ArrayToMpi(param) : param.write();
|
||||||
|
});
|
||||||
|
return util.concatUint8Array(orderedParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate algorithm-specific key parameters
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
* @param {module:enums.publicKey} algo The public key algorithm
|
||||||
* @param {Integer} bits Bit length for RSA keys
|
* @param {Integer} bits Bit length for RSA keys
|
||||||
* @param {module:type/oid} oid Object identifier for ECC keys
|
* @param {module:type/oid} oid Object identifier for ECC keys
|
||||||
* @returns {Array} The array of parameters
|
* @returns { publicParams, privateParams: {Object} } The parameters referenced by name
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
generateParams: function(algo, bits, oid) {
|
generateParams: function(algo, bits, oid) {
|
||||||
const types = [].concat(this.getPubKeyParamTypes(algo), this.getPrivKeyParamTypes(algo));
|
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaSign: {
|
case enums.publicKey.rsaSign: {
|
||||||
return publicKey.rsa.generate(bits, 65537).then(function(keyObject) {
|
return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({
|
||||||
return constructParams(
|
privateParams: { d, p, q, u },
|
||||||
types, [keyObject.n, keyObject.e, keyObject.d, keyObject.p, keyObject.q, keyObject.u]
|
publicParams: { n, e }
|
||||||
);
|
}));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
case enums.publicKey.ecdsa:
|
||||||
|
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
|
||||||
|
privateParams: { d: secret },
|
||||||
|
publicParams: { oid: new OID(oid), Q }
|
||||||
|
}));
|
||||||
|
case enums.publicKey.eddsa:
|
||||||
|
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
|
||||||
|
privateParams: { seed: secret },
|
||||||
|
publicParams: { oid: new OID(oid), Q }
|
||||||
|
}));
|
||||||
|
case enums.publicKey.ecdh:
|
||||||
|
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret, hash, cipher }) => ({
|
||||||
|
privateParams: { d: secret },
|
||||||
|
publicParams: {
|
||||||
|
oid: new OID(oid),
|
||||||
|
Q,
|
||||||
|
kdfParams: new KDFParams({ hash, cipher })
|
||||||
|
}
|
||||||
|
}));
|
||||||
case enums.publicKey.dsa:
|
case enums.publicKey.dsa:
|
||||||
case enums.publicKey.elgamal:
|
case enums.publicKey.elgamal:
|
||||||
throw new Error('Unsupported algorithm for key generation.');
|
throw new Error('Unsupported algorithm for key generation.');
|
||||||
case enums.publicKey.ecdsa:
|
|
||||||
case enums.publicKey.eddsa:
|
|
||||||
return publicKey.elliptic.generate(oid).then(function (keyObject) {
|
|
||||||
return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]);
|
|
||||||
});
|
|
||||||
case enums.publicKey.ecdh:
|
|
||||||
return publicKey.elliptic.generate(oid).then(function (keyObject) {
|
|
||||||
return constructParams(types, [
|
|
||||||
keyObject.oid,
|
|
||||||
keyObject.Q,
|
|
||||||
{ hash: keyObject.hash, cipher: keyObject.cipher },
|
|
||||||
keyObject.d
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
default:
|
default:
|
||||||
throw new Error('Invalid public key algorithm.');
|
throw new Error('Invalid public key algorithm.');
|
||||||
}
|
}
|
||||||
|
@ -299,65 +314,43 @@ export default {
|
||||||
/**
|
/**
|
||||||
* Validate algorithm-specific key parameters
|
* Validate algorithm-specific key parameters
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
* @param {module:enums.publicKey} algo The public key algorithm
|
||||||
* @param {Array} params The array of parameters
|
* @param {Object} publicParams Algorithm-specific public key parameters
|
||||||
* @returns {Promise<Boolean>} whether the parameters are valid
|
* @param {Object} privateParams Algorithm-specific private key parameters
|
||||||
|
* @returns {Promise<Boolean>} whether the parameters are valid
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
validateParams: async function(algo, params) {
|
validateParams: async function(algo, publicParams, privateParams) {
|
||||||
|
if (!publicParams || !privateParams) {
|
||||||
|
throw new Error('Missing key parameters');
|
||||||
|
}
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaSign: {
|
case enums.publicKey.rsaSign: {
|
||||||
if (params.length < 6) {
|
const { n, e } = publicParams;
|
||||||
throw new Error('Missing key parameters');
|
const { d, p, q, u } = privateParams;
|
||||||
}
|
|
||||||
const n = params[0].toUint8Array();
|
|
||||||
const e = params[1].toUint8Array();
|
|
||||||
const d = params[2].toUint8Array();
|
|
||||||
const p = params[3].toUint8Array();
|
|
||||||
const q = params[4].toUint8Array();
|
|
||||||
const u = params[5].toUint8Array();
|
|
||||||
return publicKey.rsa.validateParams(n, e, d, p, q, u);
|
return publicKey.rsa.validateParams(n, e, d, p, q, u);
|
||||||
}
|
}
|
||||||
case enums.publicKey.dsa: {
|
case enums.publicKey.dsa: {
|
||||||
if (params.length < 5) {
|
const { p, q, g, y } = publicParams;
|
||||||
throw new Error('Missing key parameters');
|
const { x } = privateParams;
|
||||||
}
|
|
||||||
const p = params[0].toUint8Array();
|
|
||||||
const q = params[1].toUint8Array();
|
|
||||||
const g = params[2].toUint8Array();
|
|
||||||
const y = params[3].toUint8Array();
|
|
||||||
const x = params[4].toUint8Array();
|
|
||||||
return publicKey.dsa.validateParams(p, q, g, y, x);
|
return publicKey.dsa.validateParams(p, q, g, y, x);
|
||||||
}
|
}
|
||||||
case enums.publicKey.elgamal: {
|
case enums.publicKey.elgamal: {
|
||||||
if (params.length < 4) {
|
const { p, g, y } = publicParams;
|
||||||
throw new Error('Missing key parameters');
|
const { x } = privateParams;
|
||||||
}
|
|
||||||
const p = params[0].toUint8Array();
|
|
||||||
const g = params[1].toUint8Array();
|
|
||||||
const y = params[2].toUint8Array();
|
|
||||||
const x = params[3].toUint8Array();
|
|
||||||
return publicKey.elgamal.validateParams(p, g, y, x);
|
return publicKey.elgamal.validateParams(p, g, y, x);
|
||||||
}
|
}
|
||||||
case enums.publicKey.ecdsa:
|
case enums.publicKey.ecdsa:
|
||||||
case enums.publicKey.ecdh: {
|
case enums.publicKey.ecdh: {
|
||||||
const expectedLen = algo === enums.publicKey.ecdh ? 3 : 2;
|
|
||||||
if (params.length < expectedLen) {
|
|
||||||
throw new Error('Missing key parameters');
|
|
||||||
}
|
|
||||||
|
|
||||||
const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
|
const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
|
||||||
const { oid, Q, d } = algoModule.parseParams(params);
|
const { oid, Q } = publicParams;
|
||||||
|
const { d } = privateParams;
|
||||||
return algoModule.validateParams(oid, Q, d);
|
return algoModule.validateParams(oid, Q, d);
|
||||||
}
|
}
|
||||||
case enums.publicKey.eddsa: {
|
case enums.publicKey.eddsa: {
|
||||||
const expectedLen = 3;
|
const { oid, Q } = publicParams;
|
||||||
if (params.length < expectedLen) {
|
const { seed } = privateParams;
|
||||||
throw new Error('Missing key parameters');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { oid, Q, seed } = publicKey.elliptic.eddsa.parseParams(params);
|
|
||||||
return publicKey.elliptic.eddsa.validateParams(oid, Q, seed);
|
return publicKey.elliptic.eddsa.validateParams(oid, Q, seed);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -36,16 +36,21 @@ export default {
|
||||||
* DSA Sign function
|
* DSA Sign function
|
||||||
* @param {Integer} hash_algo
|
* @param {Integer} hash_algo
|
||||||
* @param {Uint8Array} hashed
|
* @param {Uint8Array} hashed
|
||||||
* @param {BigInteger} g
|
* @param {Uint8Array} g
|
||||||
* @param {BigInteger} p
|
* @param {Uint8Array} p
|
||||||
* @param {BigInteger} q
|
* @param {Uint8Array} q
|
||||||
* @param {BigInteger} x
|
* @param {Uint8Array} x
|
||||||
* @returns {{ r: BigInteger, s: BigInteger }}
|
* @returns {{ r: Uint8Array, s: Uint8Array }}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
sign: async function(hash_algo, hashed, g, p, q, x) {
|
sign: async function(hash_algo, hashed, g, p, q, x) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
const one = new BigInteger(1);
|
const one = new BigInteger(1);
|
||||||
|
p = new BigInteger(p);
|
||||||
|
q = new BigInteger(q);
|
||||||
|
g = new BigInteger(g);
|
||||||
|
x = new BigInteger(x);
|
||||||
|
|
||||||
let k;
|
let k;
|
||||||
let r;
|
let r;
|
||||||
let s;
|
let s;
|
||||||
|
@ -87,30 +92,37 @@ export default {
|
||||||
/**
|
/**
|
||||||
* DSA Verify function
|
* DSA Verify function
|
||||||
* @param {Integer} hash_algo
|
* @param {Integer} hash_algo
|
||||||
* @param {BigInteger} r
|
* @param {Uint8Array} r
|
||||||
* @param {BigInteger} s
|
* @param {Uint8Array} s
|
||||||
* @param {Uint8Array} hashed
|
* @param {Uint8Array} hashed
|
||||||
* @param {BigInteger} g
|
* @param {Uint8Array} g
|
||||||
* @param {BigInteger} p
|
* @param {Uint8Array} p
|
||||||
* @param {BigInteger} q
|
* @param {Uint8Array} q
|
||||||
* @param {BigInteger} y
|
* @param {Uint8Array} y
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
verify: async function(hash_algo, r, s, hashed, g, p, q, y) {
|
verify: async function(hash_algo, r, s, hashed, g, p, q, y) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
const zero = new BigInteger(0);
|
const zero = new BigInteger(0);
|
||||||
|
r = new BigInteger(r);
|
||||||
|
s = new BigInteger(s);
|
||||||
|
|
||||||
|
p = new BigInteger(p);
|
||||||
|
q = new BigInteger(q);
|
||||||
|
g = new BigInteger(g);
|
||||||
|
y = new BigInteger(y);
|
||||||
|
|
||||||
if (r.lte(zero) || r.gte(q) ||
|
if (r.lte(zero) || r.gte(q) ||
|
||||||
s.lte(zero) || s.gte(q)) {
|
s.lte(zero) || s.gte(q)) {
|
||||||
util.printDebug("invalid DSA Signature");
|
util.printDebug("invalid DSA Signature");
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
|
const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
|
||||||
const w = s.modInv(q); // s**-1 mod q
|
const w = s.modInv(q); // s**-1 mod q
|
||||||
if (w.isZero()) {
|
if (w.isZero()) {
|
||||||
util.printDebug("invalid DSA Signature");
|
util.printDebug("invalid DSA Signature");
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g = g.mod(p);
|
g = g.mod(p);
|
||||||
|
|
|
@ -24,38 +24,55 @@
|
||||||
|
|
||||||
import util from '../../util';
|
import util from '../../util';
|
||||||
import random from '../random';
|
import random from '../random';
|
||||||
|
import pkcs1 from '../pkcs1';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/**
|
/**
|
||||||
* ElGamal Encryption function
|
* ElGamal Encryption function
|
||||||
* @param {BigInteger} m
|
* Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)
|
||||||
* @param {BigInteger} p
|
* @param {Uint8Array} data to be padded and encrypted
|
||||||
* @param {BigInteger} g
|
* @param {Uint8Array} p
|
||||||
* @param {BigInteger} y
|
* @param {Uint8Array} g
|
||||||
* @returns {{ c1: BigInteger, c2: BigInteger }}
|
* @param {Uint8Array} y
|
||||||
|
* @returns {{ c1: Uint8Array, c2: Uint8Array }}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
encrypt: async function(m, p, g, y) {
|
encrypt: async function(data, p, g, y) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
// See Section 11.5 here: https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf
|
p = new BigInteger(p);
|
||||||
const k = await random.getRandomBigInteger(new BigInteger(0), p); // returns in [0, p-1]
|
g = new BigInteger(g);
|
||||||
|
y = new BigInteger(y);
|
||||||
|
|
||||||
|
const padded = await pkcs1.eme.encode(data, p.byteLength());
|
||||||
|
const m = new BigInteger(padded);
|
||||||
|
|
||||||
|
// OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
|
||||||
|
// hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2]
|
||||||
|
const k = await random.getRandomBigInteger(new BigInteger(1), p.dec());
|
||||||
return {
|
return {
|
||||||
c1: g.modExp(k, p),
|
c1: g.modExp(k, p).toUint8Array(),
|
||||||
c2: y.modExp(k, p).imul(m).imod(p)
|
c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ElGamal Encryption function
|
* ElGamal Encryption function
|
||||||
* @param {BigInteger} c1
|
* @param {Uint8Array} c1
|
||||||
* @param {BigInteger} c2
|
* @param {Uint8Array} c2
|
||||||
* @param {BigInteger} p
|
* @param {Uint8Array} p
|
||||||
* @param {BigInteger} x
|
* @param {Uint8Array} x
|
||||||
* @returns BigInteger
|
* @returns {Uint8Array} unpadded message
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
decrypt: async function(c1, c2, p, x) {
|
decrypt: async function(c1, c2, p, x) {
|
||||||
return c1.modExp(x, p).modInv(p).imul(c2).imod(p);
|
const BigInteger = await util.getBigInteger();
|
||||||
|
c1 = new BigInteger(c1);
|
||||||
|
c2 = new BigInteger(c2);
|
||||||
|
p = new BigInteger(p);
|
||||||
|
x = new BigInteger(x);
|
||||||
|
|
||||||
|
const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
|
||||||
|
return pkcs1.eme.decode(padded.toUint8Array('be', p.byteLength()));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -214,10 +214,12 @@ async function generate(curve) {
|
||||||
|
|
||||||
curve = new Curve(curve);
|
curve = new Curve(curve);
|
||||||
const keyPair = await curve.genKeyPair();
|
const keyPair = await curve.genKeyPair();
|
||||||
|
const Q = new BigInteger(keyPair.publicKey).toUint8Array();
|
||||||
|
const secret = new BigInteger(keyPair.privateKey).toUint8Array('be', curve.payloadSize);
|
||||||
return {
|
return {
|
||||||
oid: curve.oid,
|
oid: curve.oid,
|
||||||
Q: new BigInteger(keyPair.publicKey),
|
Q,
|
||||||
d: new BigInteger(keyPair.privateKey),
|
secret,
|
||||||
hash: curve.hash,
|
hash: curve.hash,
|
||||||
cipher: curve.cipher
|
cipher: curve.cipher
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,6 +37,8 @@ import random from '../../random';
|
||||||
import hash from '../../hash';
|
import hash from '../../hash';
|
||||||
import enums from '../../../enums';
|
import enums from '../../../enums';
|
||||||
import util from '../../../util';
|
import util from '../../../util';
|
||||||
|
import pkcs5 from '../../pkcs5';
|
||||||
|
import MPI from '../../../type/mpi';
|
||||||
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
|
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
|
||||||
|
|
||||||
const webCrypto = util.getWebCrypto();
|
const webCrypto = util.getWebCrypto();
|
||||||
|
@ -65,31 +67,6 @@ function buildEcdhParam(public_algo, oid, kdfParams, fingerprint) {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses MPI params and returns them as byte arrays of fixed length
|
|
||||||
* @param {Array} params key parameters
|
|
||||||
* @returns {Object} parameters in the form
|
|
||||||
* { oid, kdfParams, d: Uint8Array, Q: Uint8Array }
|
|
||||||
*/
|
|
||||||
function parseParams(params) {
|
|
||||||
if (params.length < 3 || params.length > 4) {
|
|
||||||
throw new Error('Unexpected number of parameters');
|
|
||||||
}
|
|
||||||
|
|
||||||
const oid = params[0];
|
|
||||||
const curve = new Curve(oid);
|
|
||||||
const parsedParams = { oid };
|
|
||||||
// The public point never has leading zeros, as it is prefixed by 0x40 or 0x04
|
|
||||||
parsedParams.Q = params[1].toUint8Array();
|
|
||||||
parsedParams.kdfParams = params[2];
|
|
||||||
|
|
||||||
if (params.length === 4) {
|
|
||||||
parsedParams.d = params[3].toUint8Array('be', curve.payloadSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Key Derivation Function (RFC 6637)
|
// Key Derivation Function (RFC 6637)
|
||||||
async function kdf(hash_algo, X, length, param, stripLeading = false, stripTrailing = false) {
|
async function kdf(hash_algo, X, length, param, stripLeading = false, stripTrailing = false) {
|
||||||
// Note: X is little endian for Curve25519, big-endian for all others.
|
// Note: X is little endian for Curve25519, big-endian for all others.
|
||||||
|
@ -151,13 +128,15 @@ async function genPublicEphemeralKey(curve, Q) {
|
||||||
*
|
*
|
||||||
* @param {module:type/oid} oid Elliptic curve object identifier
|
* @param {module:type/oid} oid Elliptic curve object identifier
|
||||||
* @param {module:type/kdf_params} kdfParams KDF params including cipher and 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} data Unpadded session key data
|
||||||
* @param {Uint8Array} Q Recipient public key
|
* @param {Uint8Array} Q Recipient public key
|
||||||
* @param {Uint8Array} fingerprint Recipient fingerprint
|
* @param {Uint8Array} fingerprint Recipient fingerprint
|
||||||
* @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
|
* @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function encrypt(oid, kdfParams, m, Q, fingerprint) {
|
async function encrypt(oid, kdfParams, data, Q, fingerprint) {
|
||||||
|
const m = new MPI(pkcs5.encode(data));
|
||||||
|
|
||||||
const curve = new Curve(oid);
|
const curve = new Curve(oid);
|
||||||
const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q);
|
const { publicKey, sharedKey } = await genPublicEphemeralKey(curve, Q);
|
||||||
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
|
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
|
||||||
|
@ -214,12 +193,10 @@ async function genPrivateEphemeralKey(curve, V, Q, d) {
|
||||||
* @param {Uint8Array} Q Recipient public key
|
* @param {Uint8Array} Q Recipient public key
|
||||||
* @param {Uint8Array} d Recipient private key
|
* @param {Uint8Array} d Recipient private key
|
||||||
* @param {Uint8Array} fingerprint Recipient fingerprint
|
* @param {Uint8Array} fingerprint Recipient fingerprint
|
||||||
* @returns {Promise<BigInteger>} Value derived from session key
|
* @returns {Promise<Uint8Array>} Value derived from session key
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
|
async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
|
||||||
const BigInteger = await util.getBigInteger();
|
|
||||||
|
|
||||||
const curve = new Curve(oid);
|
const curve = new Curve(oid);
|
||||||
const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
|
const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
|
||||||
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
|
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
|
||||||
|
@ -229,7 +206,7 @@ async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
|
||||||
try {
|
try {
|
||||||
// Work around old go crypto bug and old OpenPGP.js bug, respectively.
|
// Work around old go crypto bug and old OpenPGP.js bug, respectively.
|
||||||
const Z = await kdf(kdfParams.hash, 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 BigInteger(aes_kw.unwrap(Z, C));
|
return pkcs5.decode(aes_kw.unwrap(Z, C));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
err = e;
|
err = e;
|
||||||
}
|
}
|
||||||
|
@ -411,4 +388,4 @@ async function nodePublicEphemeralKey(curve, Q) {
|
||||||
return { publicKey, sharedKey };
|
return { publicKey, sharedKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, webPrivateEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, nodePublicEphemeralKey, nodePrivateEphemeralKey, validateParams, parseParams };
|
export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, webPrivateEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, nodePublicEphemeralKey, nodePrivateEphemeralKey, validateParams };
|
||||||
|
|
|
@ -152,31 +152,7 @@ async function validateParams(oid, Q, d) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export default { sign, verify, ellipticVerify, ellipticSign, validateParams };
|
||||||
* Parses MPI params and returns them as byte arrays of fixed length
|
|
||||||
* @param {Array} params key parameters
|
|
||||||
* @returns {Object} parameters in the form
|
|
||||||
* { oid, d: Uint8Array, Q: Uint8Array }
|
|
||||||
*/
|
|
||||||
function parseParams(params) {
|
|
||||||
if (params.length < 2 || params.length > 3) {
|
|
||||||
throw new Error('Unexpected number of parameters');
|
|
||||||
}
|
|
||||||
|
|
||||||
const oid = params[0];
|
|
||||||
const curve = new Curve(oid);
|
|
||||||
const parsedParams = { oid };
|
|
||||||
// The public point never has leading zeros, as it is prefixed by 0x40 or 0x04
|
|
||||||
parsedParams.Q = params[1].toUint8Array();
|
|
||||||
if (params.length === 3) {
|
|
||||||
parsedParams.d = params[2].toUint8Array('be', curve.payloadSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default { sign, verify, ellipticVerify, ellipticSign, validateParams, parseParams };
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
|
@ -92,28 +92,4 @@ async function validateParams(oid, Q, k) {
|
||||||
return util.equalsUint8Array(Q, dG);
|
return util.equalsUint8Array(Q, dG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export default { sign, verify, validateParams };
|
||||||
* Parses MPI params and returns them as byte arrays of fixed length
|
|
||||||
* @param {Array} params key parameters
|
|
||||||
* @returns {Object} parameters in the form
|
|
||||||
* { oid, seed: Uint8Array, Q: Uint8Array }
|
|
||||||
*/
|
|
||||||
function parseParams(params) {
|
|
||||||
if (params.length < 2 || params.length > 3) {
|
|
||||||
throw new Error('Unexpected number of parameters');
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedParams = {
|
|
||||||
oid: params[0],
|
|
||||||
Q: params[1].toUint8Array('be', 33)
|
|
||||||
};
|
|
||||||
|
|
||||||
if (params.length === 3) {
|
|
||||||
parsedParams.seed = params[2].toUint8Array('be', 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default { sign, verify, validateParams, parseParams };
|
|
||||||
|
|
|
@ -171,14 +171,13 @@ export default {
|
||||||
* @param {Integer} bits RSA bit length
|
* @param {Integer} bits RSA bit length
|
||||||
* @param {Integer} e RSA public exponent
|
* @param {Integer} e RSA public exponent
|
||||||
* @returns {{n, e, d,
|
* @returns {{n, e, d,
|
||||||
* p, q ,u: BigInteger}} RSA public modulus, RSA public exponent, RSA private exponent,
|
* p, q ,u: Uint8Array}} RSA public modulus, RSA public exponent, RSA private exponent,
|
||||||
* RSA private prime p, RSA private prime q, u = q ** 1 mod p
|
* RSA private prime p, RSA private prime q, u = p ** -1 mod q
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
generate: async function(bits, e) {
|
generate: async function(bits, e) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
|
|
||||||
let key;
|
|
||||||
e = new BigInteger(e);
|
e = new BigInteger(e);
|
||||||
|
|
||||||
// Native RSA keygen using Web Crypto
|
// Native RSA keygen using Web Crypto
|
||||||
|
@ -221,17 +220,17 @@ export default {
|
||||||
if (jwk instanceof ArrayBuffer) {
|
if (jwk instanceof ArrayBuffer) {
|
||||||
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
|
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
|
||||||
}
|
}
|
||||||
// map JWK parameters to BN
|
// map JWK parameters to corresponding OpenPGP names
|
||||||
key = {};
|
return {
|
||||||
key.n = new BigInteger(util.b64ToUint8Array(jwk.n));
|
n: util.b64ToUint8Array(jwk.n),
|
||||||
key.e = e;
|
e: e.toUint8Array(),
|
||||||
key.d = new BigInteger(util.b64ToUint8Array(jwk.d));
|
d: util.b64ToUint8Array(jwk.d),
|
||||||
// switch p and q
|
// switch p and q
|
||||||
key.p = new BigInteger(util.b64ToUint8Array(jwk.q));
|
p: util.b64ToUint8Array(jwk.q),
|
||||||
key.q = new BigInteger(util.b64ToUint8Array(jwk.p));
|
q: util.b64ToUint8Array(jwk.p),
|
||||||
// Since p and q are switched in places, we could keep u
|
// Since p and q are switched in places, u is the inverse of jwk.q
|
||||||
key.u = new BigInteger(util.b64ToUint8Array(jwk.qi));
|
u: util.b64ToUint8Array(jwk.qi)
|
||||||
return key;
|
};
|
||||||
} else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
|
} else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
|
||||||
const opts = {
|
const opts = {
|
||||||
modulusLength: bits,
|
modulusLength: bits,
|
||||||
|
@ -246,19 +245,20 @@ export default {
|
||||||
resolve(RSAPrivateKey.decode(der, 'der'));
|
resolve(RSAPrivateKey.decode(der, 'der'));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
/** PGP spec differs from DER spec, DER: `(inverse of q) mod p`, PGP: `(inverse of p) mod q`.
|
/**
|
||||||
|
* OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`.
|
||||||
* @link https://tools.ietf.org/html/rfc3447#section-3.2
|
* @link https://tools.ietf.org/html/rfc3447#section-3.2
|
||||||
* @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
|
* @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
|
||||||
*/
|
*/
|
||||||
return {
|
return {
|
||||||
n: prv.modulus,
|
n: prv.modulus.toArrayLike(Uint8Array),
|
||||||
e: prv.publicExponent,
|
e: prv.publicExponent.toArrayLike(Uint8Array),
|
||||||
d: prv.privateExponent,
|
d: prv.privateExponent.toArrayLike(Uint8Array),
|
||||||
// switch p and q
|
// switch p and q
|
||||||
p: prv.prime2,
|
p: prv.prime2.toArrayLike(Uint8Array),
|
||||||
q: prv.prime1,
|
q: prv.prime1.toArrayLike(Uint8Array),
|
||||||
// Since p and q are switched in places, we could keep u
|
// Since p and q are switched in places, we can keep u as defined by DER
|
||||||
u: prv.coefficient // PGP type of u
|
u: prv.coefficient.toArrayLike(Uint8Array)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,17 +271,16 @@ export default {
|
||||||
if (q.lt(p)) {
|
if (q.lt(p)) {
|
||||||
[p, q] = [q, p];
|
[p, q] = [q, p];
|
||||||
}
|
}
|
||||||
|
|
||||||
const phi = p.dec().imul(q.dec());
|
const phi = p.dec().imul(q.dec());
|
||||||
return {
|
return {
|
||||||
n: p.mul(q),
|
n: p.mul(q).toUint8Array(),
|
||||||
e,
|
e: e.toUint8Array(),
|
||||||
d: e.modInv(phi),
|
d: e.modInv(phi).toUint8Array(),
|
||||||
p: p,
|
p: p.toUint8Array(),
|
||||||
q: q,
|
q: q.toUint8Array(),
|
||||||
// dp: d.mod(p.subn(1)),
|
// dp: d.mod(p.subn(1)),
|
||||||
// dq: d.mod(q.subn(1)),
|
// dq: d.mod(q.subn(1)),
|
||||||
u: p.modInv(q)
|
u: p.modInv(q).toUint8Array()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -18,45 +18,37 @@ export default {
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
|
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
|
||||||
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
|
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
|
||||||
* for public key and hash algorithms.
|
* for public key and hash algorithms.
|
||||||
* @param {module:enums.publicKey} algo Public key algorithm
|
* @param {module:enums.publicKey} algo Public key algorithm
|
||||||
* @param {module:enums.hash} hash_algo Hash algorithm
|
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||||
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
|
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
|
||||||
* @param {Array<module:type/mpi>} pub_MPIs Algorithm-specific public key parameters
|
* @param {Object} publicParams Algorithm-specific public key parameters
|
||||||
* @param {Uint8Array} data Data for which the signature was created
|
* @param {Uint8Array} data Data for which the signature was created
|
||||||
* @param {Uint8Array} hashed The hashed data
|
* @param {Uint8Array} hashed The hashed data
|
||||||
* @returns {Boolean} True if signature is valid
|
* @returns {Boolean} True if signature is valid
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data, hashed) {
|
verify: async function(algo, hash_algo, msg_MPIs, publicParams, data, hashed) {
|
||||||
const types = crypto.getPubKeyParamTypes(algo);
|
|
||||||
if (pub_MPIs.length < types.length) {
|
|
||||||
throw new Error('Missing public key parameters');
|
|
||||||
}
|
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaSign: {
|
case enums.publicKey.rsaSign: {
|
||||||
const n = pub_MPIs[0].toUint8Array();
|
const { n, e } = publicParams;
|
||||||
const e = pub_MPIs[1].toUint8Array();
|
|
||||||
const m = msg_MPIs[0].toUint8Array('be', n.length);
|
const m = msg_MPIs[0].toUint8Array('be', n.length);
|
||||||
return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
|
return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
|
||||||
}
|
}
|
||||||
case enums.publicKey.dsa: {
|
case enums.publicKey.dsa: {
|
||||||
const r = await msg_MPIs[0].toBigInteger();
|
const r = await msg_MPIs[0].toUint8Array();
|
||||||
const s = await msg_MPIs[1].toBigInteger();
|
const s = await msg_MPIs[1].toUint8Array();
|
||||||
const p = await pub_MPIs[0].toBigInteger();
|
const { g, p, q, y } = publicParams;
|
||||||
const q = await pub_MPIs[1].toBigInteger();
|
|
||||||
const g = await pub_MPIs[2].toBigInteger();
|
|
||||||
const y = await pub_MPIs[3].toBigInteger();
|
|
||||||
return publicKey.dsa.verify(hash_algo, r, s, hashed, g, p, q, y);
|
return publicKey.dsa.verify(hash_algo, r, s, hashed, g, p, q, y);
|
||||||
}
|
}
|
||||||
case enums.publicKey.ecdsa: {
|
case enums.publicKey.ecdsa: {
|
||||||
const { oid, Q } = publicKey.elliptic.ecdsa.parseParams(pub_MPIs);
|
const { oid, Q } = publicParams;
|
||||||
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
|
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
|
||||||
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q, hashed);
|
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q, hashed);
|
||||||
}
|
}
|
||||||
case enums.publicKey.eddsa: {
|
case enums.publicKey.eddsa: {
|
||||||
const { oid, Q } = publicKey.elliptic.eddsa.parseParams(pub_MPIs);
|
const { oid, Q } = publicParams;
|
||||||
// EdDSA signature params are expected in little-endian format
|
// EdDSA signature params are expected in little-endian format
|
||||||
const signature = {
|
const signature = {
|
||||||
R: msg_MPIs[0].toUint8Array('le', 32),
|
R: msg_MPIs[0].toUint8Array('le', 32),
|
||||||
|
@ -74,37 +66,31 @@ export default {
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
|
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
|
||||||
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
|
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
|
||||||
* for public key and hash algorithms.
|
* for public key and hash algorithms.
|
||||||
* @param {module:enums.publicKey} algo Public key algorithm
|
* @param {module:enums.publicKey} algo Public key algorithm
|
||||||
* @param {module:enums.hash} hash_algo Hash algorithm
|
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||||
* @param {Array<module:type/mpi>} key_params Algorithm-specific public and private key parameters
|
* @param {Object} publicKeyParams Algorithm-specific public and private key parameters
|
||||||
* @param {Uint8Array} data Data to be signed
|
* @param {Object} privateKeyParams Algorithm-specific public and private key parameters
|
||||||
* @param {Uint8Array} hashed The hashed data
|
* @param {Uint8Array} data Data to be signed
|
||||||
* @returns {Uint8Array} Signature
|
* @param {Uint8Array} hashed The hashed data
|
||||||
|
* @returns {Uint8Array} Signature
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
sign: async function(algo, hash_algo, key_params, data, hashed) {
|
sign: async function(algo, hash_algo, publicKeyParams, privateKeyParams, data, hashed) {
|
||||||
const types = [].concat(crypto.getPubKeyParamTypes(algo), crypto.getPrivKeyParamTypes(algo));
|
if (!publicKeyParams || !privateKeyParams) {
|
||||||
if (key_params.length < types.length) {
|
throw new Error('Missing key parameters');
|
||||||
throw new Error('Missing private key parameters');
|
|
||||||
}
|
}
|
||||||
switch (algo) {
|
switch (algo) {
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaSign: {
|
case enums.publicKey.rsaSign: {
|
||||||
const n = key_params[0].toUint8Array();
|
const { n, e } = publicKeyParams;
|
||||||
const e = key_params[1].toUint8Array();
|
const { d, p, q, u } = privateKeyParams;
|
||||||
const d = key_params[2].toUint8Array();
|
|
||||||
const p = key_params[3].toUint8Array();
|
|
||||||
const q = key_params[4].toUint8Array();
|
|
||||||
const u = key_params[5].toUint8Array();
|
|
||||||
const signature = await publicKey.rsa.sign(hash_algo, data, n, e, d, p, q, u, hashed);
|
const signature = await publicKey.rsa.sign(hash_algo, data, n, e, d, p, q, u, hashed);
|
||||||
return util.uint8ArrayToMpi(signature);
|
return util.uint8ArrayToMpi(signature);
|
||||||
}
|
}
|
||||||
case enums.publicKey.dsa: {
|
case enums.publicKey.dsa: {
|
||||||
const p = await key_params[0].toBigInteger();
|
const { g, p, q } = publicKeyParams;
|
||||||
const q = await key_params[1].toBigInteger();
|
const { x } = privateKeyParams;
|
||||||
const g = await key_params[2].toBigInteger();
|
|
||||||
const x = await key_params[4].toBigInteger();
|
|
||||||
const signature = await publicKey.dsa.sign(hash_algo, hashed, g, p, q, x);
|
const signature = await publicKey.dsa.sign(hash_algo, hashed, g, p, q, x);
|
||||||
return util.concatUint8Array([
|
return util.concatUint8Array([
|
||||||
util.uint8ArrayToMpi(signature.r),
|
util.uint8ArrayToMpi(signature.r),
|
||||||
|
@ -115,7 +101,8 @@ export default {
|
||||||
throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
|
throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
|
||||||
}
|
}
|
||||||
case enums.publicKey.ecdsa: {
|
case enums.publicKey.ecdsa: {
|
||||||
const { oid, Q, d } = publicKey.elliptic.ecdsa.parseParams(key_params);
|
const { oid, Q } = publicKeyParams;
|
||||||
|
const { d } = privateKeyParams;
|
||||||
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, Q, d, hashed);
|
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, Q, d, hashed);
|
||||||
return util.concatUint8Array([
|
return util.concatUint8Array([
|
||||||
util.uint8ArrayToMpi(signature.r),
|
util.uint8ArrayToMpi(signature.r),
|
||||||
|
@ -123,7 +110,8 @@ export default {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
case enums.publicKey.eddsa: {
|
case enums.publicKey.eddsa: {
|
||||||
const { oid, Q, seed } = publicKey.elliptic.eddsa.parseParams(key_params);
|
const { oid, Q } = publicKeyParams;
|
||||||
|
const { seed } = privateKeyParams;
|
||||||
const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, Q, seed, hashed);
|
const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, Q, seed, hashed);
|
||||||
return util.concatUint8Array([
|
return util.concatUint8Array([
|
||||||
util.uint8ArrayToMpi(signature.R),
|
util.uint8ArrayToMpi(signature.R),
|
||||||
|
|
|
@ -151,7 +151,7 @@ export async function getPreferredHashAlgo(key, keyPacket, date = new Date(), us
|
||||||
case 'ecdh':
|
case 'ecdh':
|
||||||
case 'ecdsa':
|
case 'ecdsa':
|
||||||
case 'eddsa':
|
case 'eddsa':
|
||||||
pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(keyPacket.params[0]);
|
pref_algo = crypto.publicKey.elliptic.getPreferredHashAlgo(keyPacket.publicParams.oid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?
|
return crypto.hash.getHashByteLength(hash_algo) <= crypto.hash.getHashByteLength(pref_algo) ?
|
||||||
|
|
|
@ -29,7 +29,6 @@
|
||||||
import { Sha1 } from 'asmcrypto.js/dist_es8/hash/sha1/sha1';
|
import { Sha1 } from 'asmcrypto.js/dist_es8/hash/sha1/sha1';
|
||||||
import { Sha256 } from 'asmcrypto.js/dist_es8/hash/sha256/sha256';
|
import { Sha256 } from 'asmcrypto.js/dist_es8/hash/sha256/sha256';
|
||||||
import type_keyid from '../type/keyid';
|
import type_keyid from '../type/keyid';
|
||||||
import type_mpi from '../type/mpi';
|
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import crypto from '../crypto';
|
import crypto from '../crypto';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
|
@ -70,10 +69,10 @@ class PublicKeyPacket {
|
||||||
*/
|
*/
|
||||||
this.algorithm = null;
|
this.algorithm = null;
|
||||||
/**
|
/**
|
||||||
* Algorithm specific params
|
* Algorithm specific public params
|
||||||
* @type {Array<Object>}
|
* @type {Object}
|
||||||
*/
|
*/
|
||||||
this.params = [];
|
this.publicParams = null;
|
||||||
/**
|
/**
|
||||||
* Time until expiration in days (V3 only)
|
* Time until expiration in days (V3 only)
|
||||||
* @type {Integer}
|
* @type {Integer}
|
||||||
|
@ -116,16 +115,13 @@ class PublicKeyPacket {
|
||||||
pos += 4;
|
pos += 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// - A series of values comprising the key material. This is
|
// - A series of values comprising the key material.
|
||||||
// algorithm-specific and described in section XXXX.
|
try {
|
||||||
const types = crypto.getPubKeyParamTypes(algo);
|
const { read, publicParams } = crypto.parsePublicKeyParams(algo, bytes.subarray(pos));
|
||||||
this.params = crypto.constructParams(types);
|
this.publicParams = publicParams;
|
||||||
|
pos += read;
|
||||||
for (let i = 0; i < types.length && pos < bytes.length; i++) {
|
} catch (err) {
|
||||||
pos += this.params[i].read(bytes.subarray(pos, bytes.length));
|
throw new Error('Error reading MPIs');
|
||||||
if (pos > bytes.length) {
|
|
||||||
throw new Error('Error reading MPI @:' + pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pos;
|
return pos;
|
||||||
|
@ -134,9 +130,8 @@ class PublicKeyPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as write_private_key, but has less information because of
|
* Creates an OpenPGP public key packet for the given key.
|
||||||
* public key.
|
* @returns {Uint8Array} Bytes encoding the public key OpenPGP packet
|
||||||
* @returns {Uint8Array} OpenPGP packet body contents,
|
|
||||||
*/
|
*/
|
||||||
write() {
|
write() {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
|
@ -147,8 +142,7 @@ class PublicKeyPacket {
|
||||||
const algo = enums.write(enums.publicKey, this.algorithm);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
arr.push(new Uint8Array([algo]));
|
arr.push(new Uint8Array([algo]));
|
||||||
|
|
||||||
const paramCount = crypto.getPubKeyParamTypes(algo).length;
|
const params = crypto.serializeKeyParams(algo, this.publicParams);
|
||||||
const params = util.concatUint8Array(this.params.slice(0, paramCount).map(param => param.write()));
|
|
||||||
if (this.version === 5) {
|
if (this.version === 5) {
|
||||||
// A four-octet scalar octet count for the following key material
|
// A four-octet scalar octet count for the following key material
|
||||||
arr.push(util.writeNumber(params.length, 4));
|
arr.push(util.writeNumber(params.length, 4));
|
||||||
|
@ -243,11 +237,11 @@ class PublicKeyPacket {
|
||||||
getAlgorithmInfo() {
|
getAlgorithmInfo() {
|
||||||
const result = {};
|
const result = {};
|
||||||
result.algorithm = this.algorithm;
|
result.algorithm = this.algorithm;
|
||||||
if (this.params[0] instanceof type_mpi) {
|
if (this.publicParams.n) {
|
||||||
result.rsaBits = this.params[0].byteLength() * 8;
|
result.rsaBits = this.publicParams.n.length * 8;
|
||||||
result.bits = result.rsaBits; // Deprecated.
|
result.bits = result.rsaBits; // Deprecated.
|
||||||
} else {
|
} else {
|
||||||
result.curve = this.params[0].getName();
|
result.curve = this.publicParams.oid.getName();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ class PublicKeyEncryptedSessionKeyPacket {
|
||||||
]);
|
]);
|
||||||
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
||||||
this.encrypted = await crypto.publicKeyEncrypt(
|
this.encrypted = await crypto.publicKeyEncrypt(
|
||||||
algo, key.params, data, key.getFingerprintBytes());
|
algo, key.publicParams, data, key.getFingerprintBytes());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ class PublicKeyEncryptedSessionKeyPacket {
|
||||||
if (algo !== keyAlgo) {
|
if (algo !== keyAlgo) {
|
||||||
throw new Error('Decryption error');
|
throw new Error('Decryption error');
|
||||||
}
|
}
|
||||||
const decoded = await crypto.publicKeyDecrypt(algo, key.params, this.encrypted, key.getFingerprintBytes());
|
const decoded = await crypto.publicKeyDecrypt(algo, key.publicParams, key.privateParams, this.encrypted, key.getFingerprintBytes());
|
||||||
const checksum = decoded.subarray(decoded.length - 2);
|
const checksum = decoded.subarray(decoded.length - 2);
|
||||||
const sessionKey = decoded.subarray(1, decoded.length - 2);
|
const sessionKey = decoded.subarray(1, decoded.length - 2);
|
||||||
if (!util.equalsUint8Array(checksum, util.writeChecksum(sessionKey))) {
|
if (!util.equalsUint8Array(checksum, util.writeChecksum(sessionKey))) {
|
||||||
|
|
|
@ -73,6 +73,11 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
* @type {String}
|
* @type {String}
|
||||||
*/
|
*/
|
||||||
this.aead = null;
|
this.aead = null;
|
||||||
|
/**
|
||||||
|
* Decrypted private parameters, referenced by name
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
this.privateParams = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5.5.3. Secret-Key Packet Formats
|
// 5.5.3. Secret-Key Packet Formats
|
||||||
|
@ -154,8 +159,13 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) {
|
if (!util.equalsUint8Array(util.writeChecksum(cleartext), this.keyMaterial.subarray(-2))) {
|
||||||
throw new Error('Key checksum mismatch');
|
throw new Error('Key checksum mismatch');
|
||||||
}
|
}
|
||||||
const privParams = parse_cleartext_params(cleartext, this.algorithm);
|
try {
|
||||||
this.params = this.params.concat(privParams);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
|
const { privateParams } = crypto.parsePrivateKeyParams(algo, cleartext, this.publicParams);
|
||||||
|
this.privateParams = privateParams;
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Error reading MPIs');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,7 +210,8 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
|
|
||||||
if (!this.isDummy()) {
|
if (!this.isDummy()) {
|
||||||
if (!this.s2k_usage) {
|
if (!this.s2k_usage) {
|
||||||
const cleartextParams = write_cleartext_params(this.params, this.algorithm);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
|
const cleartextParams = crypto.serializeKeyParams(algo, this.privateParams);
|
||||||
this.keyMaterial = util.concatUint8Array([
|
this.keyMaterial = util.concatUint8Array([
|
||||||
cleartextParams,
|
cleartextParams,
|
||||||
util.writeChecksum(cleartextParams)
|
util.writeChecksum(cleartextParams)
|
||||||
|
@ -282,7 +293,8 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
|
|
||||||
this.s2k = new type_s2k();
|
this.s2k = new type_s2k();
|
||||||
this.s2k.salt = await crypto.random.getRandomBytes(8);
|
this.s2k.salt = await crypto.random.getRandomBytes(8);
|
||||||
const cleartext = write_cleartext_params(this.params, this.algorithm);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
|
const cleartext = crypto.serializeKeyParams(algo, this.privateParams);
|
||||||
this.symmetric = 'aes256';
|
this.symmetric = 'aes256';
|
||||||
const key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric);
|
const key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric);
|
||||||
const blockLen = crypto.cipher[this.symmetric].blockSize;
|
const blockLen = crypto.cipher[this.symmetric].blockSize;
|
||||||
|
@ -354,8 +366,13 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const privParams = parse_cleartext_params(cleartext, this.algorithm);
|
try {
|
||||||
this.params = this.params.concat(privParams);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
|
const { privateParams } = crypto.parsePrivateKeyParams(algo, cleartext, this.publicParams);
|
||||||
|
this.privateParams = privateParams;
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Error reading MPIs');
|
||||||
|
}
|
||||||
this.isEncrypted = false;
|
this.isEncrypted = false;
|
||||||
this.keyMaterial = null;
|
this.keyMaterial = null;
|
||||||
this.s2k_usage = 0;
|
this.s2k_usage = 0;
|
||||||
|
@ -378,7 +395,14 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
}
|
}
|
||||||
|
|
||||||
const algo = enums.write(enums.publicKey, this.algorithm);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
const validParams = await crypto.validateParams(algo, this.params);
|
|
||||||
|
let validParams;
|
||||||
|
try {
|
||||||
|
// this can throw if some parameters are undefined
|
||||||
|
validParams = await crypto.validateParams(algo, this.publicParams, this.privateParams);
|
||||||
|
} catch (_) {
|
||||||
|
validParams = false;
|
||||||
|
}
|
||||||
if (!validParams) {
|
if (!validParams) {
|
||||||
throw new Error('Key is invalid');
|
throw new Error('Key is invalid');
|
||||||
}
|
}
|
||||||
|
@ -387,7 +411,9 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
|
|
||||||
async generate(bits, curve) {
|
async generate(bits, curve) {
|
||||||
const algo = enums.write(enums.publicKey, this.algorithm);
|
const algo = enums.write(enums.publicKey, this.algorithm);
|
||||||
this.params = await crypto.generateParams(algo, bits, curve);
|
const { privateParams, publicParams } = await crypto.generateParams(algo, bits, curve);
|
||||||
|
this.privateParams = privateParams;
|
||||||
|
this.publicParams = publicParams;
|
||||||
this.isEncrypted = false;
|
this.isEncrypted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,47 +426,16 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const algo = enums.write(enums.publicKey, this.algorithm);
|
Object.keys(this.privateParams).forEach(name => {
|
||||||
const publicParamCount = crypto.getPubKeyParamTypes(algo).length;
|
const param = this.privateParams[name];
|
||||||
this.params.slice(publicParamCount).forEach(param => {
|
param.fill(0);
|
||||||
param.data.fill(0);
|
delete this.privateParams[name];
|
||||||
});
|
});
|
||||||
this.params.length = publicParamCount;
|
this.privateParams = null;
|
||||||
this.isEncrypted = true;
|
this.isEncrypted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function
|
|
||||||
|
|
||||||
function parse_cleartext_params(cleartext, algorithm) {
|
|
||||||
const algo = enums.write(enums.publicKey, algorithm);
|
|
||||||
const types = crypto.getPrivKeyParamTypes(algo);
|
|
||||||
const params = crypto.constructParams(types);
|
|
||||||
let p = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < types.length && p < cleartext.length; i++) {
|
|
||||||
p += params[i].read(cleartext.subarray(p, cleartext.length));
|
|
||||||
if (p > cleartext.length) {
|
|
||||||
throw new Error('Error reading param @:' + p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
function write_cleartext_params(params, algorithm) {
|
|
||||||
const arr = [];
|
|
||||||
const algo = enums.write(enums.publicKey, algorithm);
|
|
||||||
const numPublicParams = crypto.getPubKeyParamTypes(algo).length;
|
|
||||||
|
|
||||||
for (let i = numPublicParams; i < params.length; i++) {
|
|
||||||
arr.push(params[i].write());
|
|
||||||
}
|
|
||||||
|
|
||||||
return util.concatUint8Array(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function produceEncryptionKey(s2k, passphrase, algorithm) {
|
async function produceEncryptionKey(s2k, passphrase, algorithm) {
|
||||||
return s2k.produce_key(
|
return s2k.produce_key(
|
||||||
passphrase,
|
passphrase,
|
||||||
|
|
|
@ -177,9 +177,8 @@ class SignaturePacket {
|
||||||
const hash = await this.hash(signatureType, data, toHash, detached);
|
const hash = await this.hash(signatureType, data, toHash, detached);
|
||||||
|
|
||||||
this.signedHashValue = stream.slice(stream.clone(hash), 0, 2);
|
this.signedHashValue = stream.slice(stream.clone(hash), 0, 2);
|
||||||
const params = key.params;
|
|
||||||
const signed = async () => crypto.signature.sign(
|
const signed = async () => crypto.signature.sign(
|
||||||
publicKeyAlgorithm, hashAlgorithm, params, toHash, await stream.readToEnd(hash)
|
publicKeyAlgorithm, hashAlgorithm, key.publicParams, key.privateParams, toHash, await stream.readToEnd(hash)
|
||||||
);
|
);
|
||||||
if (streaming) {
|
if (streaming) {
|
||||||
this.signature = stream.fromAsync(signed);
|
this.signature = stream.fromAsync(signed);
|
||||||
|
@ -714,7 +713,7 @@ class SignaturePacket {
|
||||||
i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian);
|
i += mpi[j].read(this.signature.subarray(i, this.signature.length), endian);
|
||||||
}
|
}
|
||||||
const verified = await crypto.signature.verify(
|
const verified = await crypto.signature.verify(
|
||||||
publicKeyAlgorithm, hashAlgorithm, mpi, key.params,
|
publicKeyAlgorithm, hashAlgorithm, mpi, key.publicParams,
|
||||||
toHash, hash
|
toHash, hash
|
||||||
);
|
);
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
|
|
25
src/util.js
25
src/util.js
|
@ -167,6 +167,31 @@ export default {
|
||||||
return str;
|
return str;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read one MPI from bytes in input
|
||||||
|
* @param {Uint8Array} bytes input data to parse
|
||||||
|
* @returns {Uint8Array} parsed MPI
|
||||||
|
*/
|
||||||
|
readMPI: function (bytes) {
|
||||||
|
const bits = (bytes[0] << 8) | bytes[1];
|
||||||
|
const bytelen = (bits + 7) >>> 3;
|
||||||
|
return bytes.subarray(2, 2 + bytelen);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pad Uint8Array to length by adding 0x0 bytes
|
||||||
|
* @param {Uint8Array} bytes data to pad
|
||||||
|
* @param {Number} length padded length
|
||||||
|
* @param {'be'|'le'} endianess endianess of input data
|
||||||
|
* @return {Uint8Array} padded bytes
|
||||||
|
*/
|
||||||
|
padToLength(bytes, length, endianess = 'be') {
|
||||||
|
const padded = new Uint8Array(length);
|
||||||
|
const offset = (endianess === 'be') ? 0 : (length - bytes.length);
|
||||||
|
padded.set(bytes, offset);
|
||||||
|
return padded;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a Uint8Array to an MPI-formatted Uint8Array.
|
* Convert a Uint8Array to an MPI-formatted Uint8Array.
|
||||||
* Note: the output is **not** an MPI object.
|
* Note: the output is **not** an MPI object.
|
||||||
|
|
|
@ -8,7 +8,7 @@ const expect = chai.expect;
|
||||||
module.exports = () => describe('API functional testing', function() {
|
module.exports = () => describe('API functional testing', function() {
|
||||||
const util = openpgp.util;
|
const util = openpgp.util;
|
||||||
const crypto = openpgp.crypto;
|
const crypto = openpgp.crypto;
|
||||||
const RSApubMPIstrs = [
|
const RSAPublicKeyMaterial = util.concatUint8Array([
|
||||||
new Uint8Array([0x08,0x00,0xac,0x15,0xb3,0xd6,0xd2,0x0f,0xf0,0x7a,0xdd,0x21,0xb7,
|
new Uint8Array([0x08,0x00,0xac,0x15,0xb3,0xd6,0xd2,0x0f,0xf0,0x7a,0xdd,0x21,0xb7,
|
||||||
0xbf,0x61,0xfa,0xca,0x93,0x86,0xc8,0x55,0x5a,0x4b,0xa6,0xa4,0x1a,
|
0xbf,0x61,0xfa,0xca,0x93,0x86,0xc8,0x55,0x5a,0x4b,0xa6,0xa4,0x1a,
|
||||||
0x60,0xa2,0x3a,0x37,0x06,0x08,0xd8,0x15,0x8e,0x85,0x45,0xaa,0xb7,
|
0x60,0xa2,0x3a,0x37,0x06,0x08,0xd8,0x15,0x8e,0x85,0x45,0xaa,0xb7,
|
||||||
|
@ -30,8 +30,8 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
0xee,0xc9,0xa4,0xcd,0x15,0xdc,0x1b,0x8d,0x64,0xc1,0x36,0x17,0xc4,
|
0xee,0xc9,0xa4,0xcd,0x15,0xdc,0x1b,0x8d,0x64,0xc1,0x36,0x17,0xc4,
|
||||||
0x8d,0x5e,0x99,0x7a,0x5b,0x9f,0x39,0xd0,0x00,0x6e,0xf9]),
|
0x8d,0x5e,0x99,0x7a,0x5b,0x9f,0x39,0xd0,0x00,0x6e,0xf9]),
|
||||||
new Uint8Array([0x00,0x11,0x01,0x00,0x01])
|
new Uint8Array([0x00,0x11,0x01,0x00,0x01])
|
||||||
];
|
]);
|
||||||
const RSAsecMPIstrs = [
|
const RSAPrivateKeyMaterial = util.concatUint8Array([
|
||||||
new Uint8Array([0x07,0xfe,0x23,0xff,0xce,0x45,0x6c,0x60,0x65,0x40,0x6e,0xae,0x35,
|
new Uint8Array([0x07,0xfe,0x23,0xff,0xce,0x45,0x6c,0x60,0x65,0x40,0x6e,0xae,0x35,
|
||||||
0x10,0x56,0x60,0xee,0xab,0xfa,0x10,0x42,0xba,0xc7,0x04,0xaf,0x63,
|
0x10,0x56,0x60,0xee,0xab,0xfa,0x10,0x42,0xba,0xc7,0x04,0xaf,0x63,
|
||||||
0xcd,0x3f,0x62,0xca,0x4b,0xfa,0xe1,0xa9,0x70,0xcd,0x34,0x8b,0xc8,
|
0xcd,0x3f,0x62,0xca,0x4b,0xfa,0xe1,0xa9,0x70,0xcd,0x34,0x8b,0xc8,
|
||||||
|
@ -82,9 +82,9 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
0x51,0xe0,0x22,0xf0,0xff,0xa7,0x42,0xd4,0xde,0x0b,0x47,0x8f,0x2b,
|
0x51,0xe0,0x22,0xf0,0xff,0xa7,0x42,0xd4,0xde,0x0b,0x47,0x8f,0x2b,
|
||||||
0xf5,0x4d,0x04,0x32,0x91,0x89,0x4b,0x0e,0x05,0x8d,0x70,0xf9,0xbb,
|
0xf5,0x4d,0x04,0x32,0x91,0x89,0x4b,0x0e,0x05,0x8d,0x70,0xf9,0xbb,
|
||||||
0xe7,0xd6,0x76,0xea,0x0e,0x1a,0x90,0x30,0xf5,0x98,0x01,0xc5,0x73])
|
0xe7,0xd6,0x76,0xea,0x0e,0x1a,0x90,0x30,0xf5,0x98,0x01,0xc5,0x73])
|
||||||
];
|
]);
|
||||||
|
|
||||||
const DSApubMPIstrs = [
|
const DSAPublicKeyMaterial = util.concatUint8Array([
|
||||||
new Uint8Array([0x08,0x00,0xa8,0x85,0x5c,0x28,0x05,0x94,0x03,0xbe,0x07,0x6c,0x13,0x3e,0x65,
|
new Uint8Array([0x08,0x00,0xa8,0x85,0x5c,0x28,0x05,0x94,0x03,0xbe,0x07,0x6c,0x13,0x3e,0x65,
|
||||||
0xfb,0xb5,0xe1,0x99,0x7c,0xfa,0x84,0xe3,0xac,0x47,0xa5,0xc4,0x46,0xd8,0x5f,
|
0xfb,0xb5,0xe1,0x99,0x7c,0xfa,0x84,0xe3,0xac,0x47,0xa5,0xc4,0x46,0xd8,0x5f,
|
||||||
0x44,0xe9,0xc1,0x6b,0x69,0xf7,0x10,0x76,0x49,0xa7,0x25,0x85,0xf4,0x1b,0xed,
|
0x44,0xe9,0xc1,0x6b,0x69,0xf7,0x10,0x76,0x49,0xa7,0x25,0x85,0xf4,0x1b,0xed,
|
||||||
|
@ -142,14 +142,14 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
0x67,0x8d,0x9d,0x14,0xb6,0x9d,0x32,0x82,0xd0,0xb5,0xc6,0x57,0xf0,0x91,0xd9,
|
0x67,0x8d,0x9d,0x14,0xb6,0x9d,0x32,0x82,0xd0,0xb5,0xc6,0x57,0xf0,0x91,0xd9,
|
||||||
0xc3,0x26,0xae,0x9f,0xa9,0x67,0x49,0x96,0x5c,0x07,0x3e,0x47,0x5c,0xed,0x60,
|
0xc3,0x26,0xae,0x9f,0xa9,0x67,0x49,0x96,0x5c,0x07,0x3e,0x47,0x5c,0xed,0x60,
|
||||||
0x07,0xac,0x6a])
|
0x07,0xac,0x6a])
|
||||||
];
|
]);
|
||||||
const DSAsecMPIstrs = [
|
const DSAPrivateKeyMaterial = util.concatUint8Array([
|
||||||
new Uint8Array([0x01,0x00,0x9b,0x58,0xa8,0xf4,0x04,0xb1,0xd5,0x14,0x09,0xe1,0xe1,0xa1,0x8a,
|
new Uint8Array([0x01,0x00,0x9b,0x58,0xa8,0xf4,0x04,0xb1,0xd5,0x14,0x09,0xe1,0xe1,0xa1,0x8a,
|
||||||
0x0b,0xa3,0xc3,0xa3,0x66,0xaa,0x27,0x99,0x50,0x1c,0x4d,0xba,0x24,0xee,0xdf,
|
0x0b,0xa3,0xc3,0xa3,0x66,0xaa,0x27,0x99,0x50,0x1c,0x4d,0xba,0x24,0xee,0xdf,
|
||||||
0xdf,0xb8,0x8e,0x8e])
|
0xdf,0xb8,0x8e,0x8e])
|
||||||
];
|
]);
|
||||||
|
|
||||||
const ElgamalpubMPIstrs = [
|
const elGamalPublicKeyMaterial = util.concatUint8Array([
|
||||||
new Uint8Array([0x08,0x00,0xea,0xcc,0xbe,0xe2,0xe4,0x5a,0x51,0x18,0x93,0xa1,0x12,0x2f,0x00,
|
new Uint8Array([0x08,0x00,0xea,0xcc,0xbe,0xe2,0xe4,0x5a,0x51,0x18,0x93,0xa1,0x12,0x2f,0x00,
|
||||||
0x99,0x42,0xd8,0x5c,0x1c,0x2f,0xb6,0x3c,0xd9,0x94,0x61,0xb4,0x55,0x8d,0x4e,
|
0x99,0x42,0xd8,0x5c,0x1c,0x2f,0xb6,0x3c,0xd9,0x94,0x61,0xb4,0x55,0x8d,0x4e,
|
||||||
0x73,0xe6,0x69,0xbc,0x1d,0x33,0xe3,0x2d,0x91,0x23,0x69,0x95,0x98,0xd7,0x18,
|
0x73,0xe6,0x69,0xbc,0x1d,0x33,0xe3,0x2d,0x91,0x23,0x69,0x95,0x98,0xd7,0x18,
|
||||||
|
@ -187,64 +187,37 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
0xda,0xba,0x19,0xf3,0xcb,0x10,0xa0,0x6b,0xd0,0x2d,0xbe,0x40,0x42,0x7b,0x9b,
|
0xda,0xba,0x19,0xf3,0xcb,0x10,0xa0,0x6b,0xd0,0x2d,0xbe,0x40,0x42,0x7b,0x9b,
|
||||||
0x15,0xa4,0x2d,0xec,0xcf,0x09,0xd6,0xe3,0x92,0xc3,0x8d,0x65,0x6b,0x60,0x97,
|
0x15,0xa4,0x2d,0xec,0xcf,0x09,0xd6,0xe3,0x92,0xc3,0x8d,0x65,0x6b,0x60,0x97,
|
||||||
0xda,0x6b,0xca])
|
0xda,0x6b,0xca])
|
||||||
];
|
]);
|
||||||
|
|
||||||
const ElgamalsecMPIstrs = [
|
const elGamalPrivateKeyMaterial = util.concatUint8Array([
|
||||||
new Uint8Array([0x01,0x52,0x02,0x80,0x87,0xf6,0xe4,0x49,0xd7,0x2e,0x3e,0xfe,0x60,0xb9,0xa3,
|
new Uint8Array([0x01,0x52,0x02,0x80,0x87,0xf6,0xe4,0x49,0xd7,0x2e,0x3e,0xfe,0x60,0xb9,0xa3,
|
||||||
0x2a,0xf0,0x67,0x58,0xe9,0xf6,0x47,0x83,0xde,0x7e,0xfb,0xbb,0xbd,0xdf,0x48,
|
0x2a,0xf0,0x67,0x58,0xe9,0xf6,0x47,0x83,0xde,0x7e,0xfb,0xbb,0xbd,0xdf,0x48,
|
||||||
0x12,0x1b,0x06,0x7d,0x13,0xbc,0x3b,0x49,0xf9,0x86,0xd4,0x53,0xed,0x2d,0x68])
|
0x12,0x1b,0x06,0x7d,0x13,0xbc,0x3b,0x49,0xf9,0x86,0xd4,0x53,0xed,0x2d,0x68])
|
||||||
];
|
]);
|
||||||
|
|
||||||
const RSApubMPIs = [];
|
const algoRSA = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
let i;
|
const RSAPublicParams = crypto.parsePublicKeyParams(algoRSA, RSAPublicKeyMaterial).publicParams;
|
||||||
for (i = 0; i < 2; i++) {
|
const RSAPrivateParams = crypto.parsePrivateKeyParams(algoRSA, RSAPrivateKeyMaterial).privateParams;
|
||||||
RSApubMPIs[i] = new openpgp.MPI();
|
|
||||||
RSApubMPIs[i].read(RSApubMPIstrs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const RSAsecMPIs = [];
|
const algoDSA = openpgp.enums.publicKey.dsa;
|
||||||
for (i = 0; i < 4; i++) {
|
const DSAPublicParams = crypto.parsePublicKeyParams(algoDSA, DSAPublicKeyMaterial).publicParams;
|
||||||
RSAsecMPIs[i] = new openpgp.MPI();
|
const DSAPrivateParams = crypto.parsePrivateKeyParams(algoDSA, DSAPrivateKeyMaterial).privateParams;
|
||||||
RSAsecMPIs[i].read(RSAsecMPIstrs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DSAsecMPIs = [];
|
const algoElGamal = openpgp.enums.publicKey.elgamal;
|
||||||
for (i = 0; i < 1; i++) {
|
const elGamalPublicParams = crypto.parsePublicKeyParams(algoElGamal, elGamalPublicKeyMaterial).publicParams;
|
||||||
DSAsecMPIs[i] = new openpgp.MPI();
|
const elGamalPrivateParams = crypto.parsePrivateKeyParams(algoElGamal, elGamalPrivateKeyMaterial).privateParams;
|
||||||
DSAsecMPIs[i].read(DSAsecMPIstrs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DSApubMPIs = [];
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
DSApubMPIs[i] = new openpgp.MPI();
|
|
||||||
DSApubMPIs[i].read(DSApubMPIstrs[i]);
|
|
||||||
}
|
|
||||||
const ElgamalsecMPIs = [];
|
|
||||||
for (i = 0; i < 1; i++) {
|
|
||||||
ElgamalsecMPIs[i] = new openpgp.MPI();
|
|
||||||
ElgamalsecMPIs[i].read(ElgamalsecMPIstrs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ElgamalpubMPIs = [];
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
ElgamalpubMPIs[i] = new openpgp.MPI();
|
|
||||||
ElgamalpubMPIs[i].read(ElgamalpubMPIstrs[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = util.strToUint8Array("foobar");
|
const data = util.strToUint8Array("foobar");
|
||||||
|
|
||||||
describe('Sign and verify', function () {
|
describe('Sign and verify', function () {
|
||||||
it('RSA', async function () {
|
it('RSA', async function () {
|
||||||
// FIXME
|
|
||||||
//Originally we passed public and secret MPI separately, now they are joined. Is this what we want to do long term?
|
|
||||||
// RSA
|
|
||||||
return crypto.signature.sign(
|
return crypto.signature.sign(
|
||||||
1, 2, RSApubMPIs.concat(RSAsecMPIs), data, await crypto.hash.digest(2, data)
|
1, 2, RSAPublicParams, RSAPrivateParams, data, await crypto.hash.digest(2, data)
|
||||||
).then(async RSAsignedData => {
|
).then(async RSAsignedData => {
|
||||||
const RSAsignedDataMPI = new openpgp.MPI();
|
const RSAsignedDataMPI = new openpgp.MPI();
|
||||||
RSAsignedDataMPI.read(RSAsignedData);
|
RSAsignedDataMPI.read(RSAsignedData);
|
||||||
return crypto.signature.verify(
|
return crypto.signature.verify(
|
||||||
1, 2, [RSAsignedDataMPI], RSApubMPIs, data, await crypto.hash.digest(2, data)
|
1, 2, [RSAsignedDataMPI], RSAPublicParams, data, await crypto.hash.digest(2, data)
|
||||||
).then(success => {
|
).then(success => {
|
||||||
return expect(success).to.be.true;
|
return expect(success).to.be.true;
|
||||||
});
|
});
|
||||||
|
@ -252,9 +225,8 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('DSA', async function () {
|
it('DSA', async function () {
|
||||||
// DSA
|
|
||||||
return crypto.signature.sign(
|
return crypto.signature.sign(
|
||||||
17, 2, DSApubMPIs.concat(DSAsecMPIs), data, await crypto.hash.digest(2, data)
|
17, 2, DSAPublicParams, DSAPrivateParams, data, await crypto.hash.digest(2, data)
|
||||||
).then(async DSAsignedData => {
|
).then(async DSAsignedData => {
|
||||||
DSAsignedData = util.uint8ArrayToStr(DSAsignedData);
|
DSAsignedData = util.uint8ArrayToStr(DSAsignedData);
|
||||||
const DSAmsgMPIs = [];
|
const DSAmsgMPIs = [];
|
||||||
|
@ -263,7 +235,7 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
DSAmsgMPIs[0].read(DSAsignedData.substring(0,34));
|
DSAmsgMPIs[0].read(DSAsignedData.substring(0,34));
|
||||||
DSAmsgMPIs[1].read(DSAsignedData.substring(34,68));
|
DSAmsgMPIs[1].read(DSAsignedData.substring(34,68));
|
||||||
return crypto.signature.verify(
|
return crypto.signature.verify(
|
||||||
17, 2, DSAmsgMPIs, DSApubMPIs, data, await crypto.hash.digest(2, data)
|
17, 2, DSAmsgMPIs, DSAPublicParams, data, await crypto.hash.digest(2, data)
|
||||||
).then(success => {
|
).then(success => {
|
||||||
return expect(success).to.be.true;
|
return expect(success).to.be.true;
|
||||||
});
|
});
|
||||||
|
@ -356,9 +328,9 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
|
|
||||||
it('Asymmetric using RSA with eme_pkcs1 padding', async function () {
|
it('Asymmetric using RSA with eme_pkcs1 padding', async function () {
|
||||||
const symmKey = await crypto.generateSessionKey('aes256');
|
const symmKey = await crypto.generateSessionKey('aes256');
|
||||||
return crypto.publicKeyEncrypt(1, RSApubMPIs, symmKey).then(RSAEncryptedData => {
|
return crypto.publicKeyEncrypt(algoRSA, RSAPublicParams, symmKey).then(RSAEncryptedData => {
|
||||||
return crypto.publicKeyDecrypt(
|
return crypto.publicKeyDecrypt(
|
||||||
1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
|
algoRSA, RSAPublicParams, RSAPrivateParams, RSAEncryptedData
|
||||||
).then(data => {
|
).then(data => {
|
||||||
expect(data).to.deep.equal(symmKey);
|
expect(data).to.deep.equal(symmKey);
|
||||||
});
|
});
|
||||||
|
@ -367,9 +339,9 @@ module.exports = () => describe('API functional testing', function() {
|
||||||
|
|
||||||
it('Asymmetric using Elgamal with eme_pkcs1 padding', async function () {
|
it('Asymmetric using Elgamal with eme_pkcs1 padding', async function () {
|
||||||
const symmKey = await crypto.generateSessionKey('aes256');
|
const symmKey = await crypto.generateSessionKey('aes256');
|
||||||
return crypto.publicKeyEncrypt(16, ElgamalpubMPIs, symmKey).then(ElgamalEncryptedData => {
|
return crypto.publicKeyEncrypt(algoElGamal, elGamalPublicParams, symmKey).then(ElgamalEncryptedData => {
|
||||||
return crypto.publicKeyDecrypt(
|
return crypto.publicKeyDecrypt(
|
||||||
16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData
|
algoElGamal, elGamalPublicParams, elGamalPrivateParams, ElgamalEncryptedData
|
||||||
).then(data => {
|
).then(data => {
|
||||||
expect(data).to.deep.equal(symmKey);
|
expect(data).to.deep.equal(symmKey);
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,16 +22,11 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
|
|
||||||
it('sign and verify using generated key params', async function() {
|
it('sign and verify using generated key params', async function() {
|
||||||
const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
|
const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, 'sha256');
|
const hash_algo = openpgp.enums.write(openpgp.enums.hash, 'sha256');
|
||||||
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
const hashed = await openpgp.crypto.hash.digest(hash_algo, message);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const signature = await openpgp.crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
|
const signature = await openpgp.crypto.publicKey.rsa.sign(hash_algo, message, n, e, d, p, q, u, hashed);
|
||||||
expect(signature).to.exist;
|
expect(signature).to.exist;
|
||||||
const verify = await openpgp.crypto.publicKey.rsa.verify(hash_algo, message, signature, n, e, hashed);
|
const verify = await openpgp.crypto.publicKey.rsa.verify(hash_algo, message, signature, n, e, hashed);
|
||||||
|
@ -40,13 +35,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
|
|
||||||
it('encrypt and decrypt using generated key params', async function() {
|
it('encrypt and decrypt using generated key params', async function() {
|
||||||
const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
|
const bits = openpgp.util.getWebCryptoAll() ? 2048 : 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const message = await openpgp.crypto.generateSessionKey('aes256');
|
const message = await openpgp.crypto.generateSessionKey('aes256');
|
||||||
const encrypted = await openpgp.crypto.publicKey.rsa.encrypt(message, n, e);
|
const encrypted = await openpgp.crypto.publicKey.rsa.encrypt(message, n, e);
|
||||||
const result = new openpgp.MPI(encrypted);
|
const result = new openpgp.MPI(encrypted);
|
||||||
|
@ -59,13 +49,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
const bits = 1024;
|
const bits = 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const message = await openpgp.crypto.generateSessionKey('aes256');
|
const message = await openpgp.crypto.generateSessionKey('aes256');
|
||||||
const encryptedBn = await openpgp.crypto.publicKey.rsa.bnEncrypt(message, n, e);
|
const encryptedBn = await openpgp.crypto.publicKey.rsa.bnEncrypt(message, n, e);
|
||||||
const decrypted1 = await openpgp.crypto.publicKey.rsa.nodeDecrypt(encryptedBn, n, e, d, p, q, u);
|
const decrypted1 = await openpgp.crypto.publicKey.rsa.nodeDecrypt(encryptedBn, n, e, d, p, q, u);
|
||||||
|
@ -80,13 +65,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||||
const hashName = 'sha256';
|
const hashName = 'sha256';
|
||||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||||
|
@ -107,13 +87,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||||
const hashName = 'sha256';
|
const hashName = 'sha256';
|
||||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||||
|
@ -137,13 +112,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
const bits = 1024;
|
const bits = 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||||
const hashName = 'sha256';
|
const hashName = 'sha256';
|
||||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||||
|
@ -158,13 +128,8 @@ module.exports = () => (!native ? describe.skip : describe)('basic RSA cryptogra
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
const bits = openpgp.util.getWebCrypto() ? 2048 : 1024;
|
||||||
const keyParams = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
const { publicParams, privateParams } = await openpgp.crypto.generateParams(openpgp.enums.publicKey.rsaSign, bits);
|
||||||
const n = keyParams[0].toUint8Array();
|
const { n, e, d, p, q, u } = { ...publicParams, ...privateParams };
|
||||||
const e = keyParams[1].toUint8Array();
|
|
||||||
const d = keyParams[2].toUint8Array();
|
|
||||||
const p = keyParams[3].toUint8Array();
|
|
||||||
const q = keyParams[4].toUint8Array();
|
|
||||||
const u = keyParams[5].toUint8Array();
|
|
||||||
const message = await openpgp.crypto.random.getRandomBytes(64);
|
const message = await openpgp.crypto.random.getRandomBytes(64);
|
||||||
const hashName = 'sha256';
|
const hashName = 'sha256';
|
||||||
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
const hash_algo = openpgp.enums.write(openpgp.enums.hash, hashName);
|
||||||
|
|
|
@ -93,12 +93,12 @@ module.exports = () => {
|
||||||
|
|
||||||
it('detect invalid edDSA Q', async function() {
|
it('detect invalid edDSA Q', async function() {
|
||||||
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
|
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
|
||||||
const Q = eddsaKeyPacket.params[1];
|
const Q = eddsaKeyPacket.publicParams.Q;
|
||||||
Q.data[0]++;
|
Q[0]++;
|
||||||
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
const infQ = new Uint8Array(Q.data.length);
|
const infQ = new Uint8Array(Q.length);
|
||||||
eddsaKeyPacket.params[1] = new openpgp.MPI(infQ);
|
eddsaKeyPacket.publicParams.Q = infQ;
|
||||||
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -114,86 +114,78 @@ module.exports = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('EdDSA params are not valid for ECDH', async function() {
|
it('EdDSA params are not valid for ECDH', async function() {
|
||||||
const oid = eddsaKey.keyPacket.params[0];
|
const { oid, Q } = eddsaKey.keyPacket.publicParams;
|
||||||
const Q = eddsaKey.keyPacket.params[1];
|
const { seed } = eddsaKey.keyPacket.privateParams;
|
||||||
const seed = eddsaKey.keyPacket.params[2];
|
|
||||||
|
|
||||||
const ecdhKeyPacket = cloneKeyPacket(ecdhKey);
|
const ecdhKeyPacket = cloneKeyPacket(ecdhKey);
|
||||||
const ecdhOID = ecdhKeyPacket.params[0];
|
const ecdhOID = ecdhKeyPacket.publicParams.oid;
|
||||||
|
|
||||||
ecdhKeyPacket.params[0] = oid;
|
ecdhKeyPacket.publicParams.oid = oid;
|
||||||
await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
ecdhKeyPacket.params[0] = ecdhOID;
|
ecdhKeyPacket.publicParams.oid = ecdhOID;
|
||||||
ecdhKeyPacket.params[1] = Q;
|
ecdhKeyPacket.publicParams.Q = Q;
|
||||||
ecdhKeyPacket.params[3] = seed;
|
ecdhKeyPacket.privateParams.d = seed;
|
||||||
await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(ecdhKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('EdDSA params are not valid for EcDSA', async function() {
|
it('EdDSA params are not valid for EcDSA', async function() {
|
||||||
const oid = eddsaKey.keyPacket.params[0];
|
const { oid, Q } = eddsaKey.keyPacket.publicParams;
|
||||||
const Q = eddsaKey.keyPacket.params[1];
|
const { seed } = eddsaKey.keyPacket.privateParams;
|
||||||
const seed = eddsaKey.keyPacket.params[2];
|
|
||||||
|
|
||||||
const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
|
const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
|
||||||
const ecdsaOID = ecdsaKeyPacket.params[0];
|
const ecdsaOID = ecdsaKeyPacket.publicParams.oid;
|
||||||
ecdsaKeyPacket.params[0] = oid;
|
ecdsaKeyPacket.publicParams.oid = oid;
|
||||||
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
ecdsaKeyPacket.params[0] = ecdsaOID;
|
ecdsaKeyPacket.publicParams.oid = ecdsaOID;
|
||||||
ecdsaKeyPacket.params[1] = Q;
|
ecdsaKeyPacket.publicParams.Q = Q;
|
||||||
ecdsaKeyPacket.params[2] = seed;
|
ecdsaKeyPacket.privateParams.d = seed;
|
||||||
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ECDH x25519 params are not valid for EcDSA', async function() {
|
it('ECDH x25519 params are not valid for EcDSA', async function() {
|
||||||
const ecdh25519KeyPacket = ecdhKey.keyPacket;
|
const { oid, Q } = ecdhKey.keyPacket.publicParams;
|
||||||
const oid = ecdh25519KeyPacket.params[0];
|
const { d } = ecdhKey.keyPacket.privateParams;
|
||||||
const Q = ecdh25519KeyPacket.params[1];
|
|
||||||
const d = ecdh25519KeyPacket.params[3];
|
|
||||||
|
|
||||||
const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
|
const ecdsaKeyPacket = cloneKeyPacket(ecdsaKey);
|
||||||
ecdsaKeyPacket.params[0] = oid;
|
ecdsaKeyPacket.publicParams.oid = oid;
|
||||||
ecdsaKeyPacket.params[1] = Q;
|
ecdsaKeyPacket.publicParams.Q = Q;
|
||||||
ecdsaKeyPacket.params[2] = d;
|
ecdsaKeyPacket.privateParams.d = d;
|
||||||
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(ecdsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('EcDSA params are not valid for EdDSA', async function() {
|
it('EcDSA params are not valid for EdDSA', async function() {
|
||||||
const oid = ecdsaKey.keyPacket.params[0];
|
const { oid, Q } = ecdsaKey.keyPacket.publicParams;
|
||||||
const Q = ecdsaKey.keyPacket.params[1];
|
const { d } = ecdsaKey.keyPacket.privateParams;
|
||||||
const d = ecdsaKey.keyPacket.params[2];
|
|
||||||
|
|
||||||
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
|
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
|
||||||
const eddsaOID = eddsaKeyPacket.params[0];
|
const eddsaOID = eddsaKeyPacket.publicParams.oid;
|
||||||
eddsaKeyPacket.params[0] = oid;
|
eddsaKeyPacket.publicParams.oid = oid;
|
||||||
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
eddsaKeyPacket.params[0] = eddsaOID;
|
eddsaKeyPacket.publicParams.oid = eddsaOID;
|
||||||
eddsaKeyPacket.params[1] = Q;
|
eddsaKeyPacket.publicParams.Q = Q;
|
||||||
eddsaKeyPacket.params[2] = d;
|
eddsaKeyPacket.privateParams.seed = d;
|
||||||
await expect(eddsaKeyPacket.validate()).to.be.rejected;
|
await expect(eddsaKeyPacket.validate()).to.be.rejected;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ECDH x25519 params are not valid for EdDSA', async function() {
|
it('ECDH x25519 params are not valid for EdDSA', async function() {
|
||||||
const ecdh25519KeyPacket = ecdhKey.keyPacket;
|
const { oid, Q } = ecdhKey.keyPacket.publicParams;
|
||||||
const oid = ecdh25519KeyPacket.params[0];
|
const { d } = ecdhKey.keyPacket.privateParams;
|
||||||
const Q = ecdh25519KeyPacket.params[1];
|
|
||||||
const d = ecdh25519KeyPacket.params[3];
|
|
||||||
|
|
||||||
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
|
const eddsaKeyPacket = cloneKeyPacket(eddsaKey);
|
||||||
const eddsaOID = eddsaKeyPacket.params[0];
|
const eddsaOID = eddsaKeyPacket.publicParams.oid;
|
||||||
eddsaKeyPacket.params[0] = oid;
|
eddsaKeyPacket.publicParams.oid = oid;
|
||||||
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
eddsaKeyPacket.params[0] = eddsaOID;
|
eddsaKeyPacket.publicParams.oid = eddsaOID;
|
||||||
eddsaKeyPacket.params[1] = Q;
|
eddsaKeyPacket.publicParams.Q = Q;
|
||||||
eddsaKeyPacket.params[2] = d;
|
eddsaKeyPacket.privateParams.seed = d;
|
||||||
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(eddsaKeyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const curves = ['curve25519', 'p256', 'p384', 'p521', 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
|
const curves = ['curve25519', 'p256', 'p384', 'p521', 'secp256k1', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1'];
|
||||||
curves.forEach(curve => {
|
curves.forEach(curve => {
|
||||||
describe(`ECC ${curve} parameter validation`, () => {
|
describe(`ECC ${curve} parameter validation`, () => {
|
||||||
|
@ -216,12 +208,12 @@ module.exports = () => {
|
||||||
|
|
||||||
it('detect invalid EcDSA Q', async function() {
|
it('detect invalid EcDSA Q', async function() {
|
||||||
const keyPacket = cloneKeyPacket(ecdsaKey);
|
const keyPacket = cloneKeyPacket(ecdsaKey);
|
||||||
const Q = keyPacket.params[1];
|
const Q = keyPacket.publicParams.Q;
|
||||||
Q.data[0]++;
|
Q[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
const infQ = new Uint8Array(Q.data.length);
|
const infQ = new Uint8Array(Q.length);
|
||||||
keyPacket.params[1] = new openpgp.MPI(infQ);
|
keyPacket.publicParams.Q = infQ;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -232,12 +224,12 @@ module.exports = () => {
|
||||||
|
|
||||||
it('detect invalid ECDH Q', async function() {
|
it('detect invalid ECDH Q', async function() {
|
||||||
const keyPacket = cloneKeyPacket(ecdhKey);
|
const keyPacket = cloneKeyPacket(ecdhKey);
|
||||||
const Q = keyPacket.params[1];
|
const Q = keyPacket.publicParams.Q;
|
||||||
Q.data[16]++;
|
Q[16]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
const infQ = new Uint8Array(Q.data.length);
|
const infQ = new Uint8Array(Q.length);
|
||||||
keyPacket.params[1] = new openpgp.MPI(infQ);
|
keyPacket.publicParams.Q = infQ;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -255,15 +247,15 @@ module.exports = () => {
|
||||||
|
|
||||||
it('detect invalid RSA n', async function() {
|
it('detect invalid RSA n', async function() {
|
||||||
const keyPacket = cloneKeyPacket(rsaKey);
|
const keyPacket = cloneKeyPacket(rsaKey);
|
||||||
const n = keyPacket.params[0];
|
const n = keyPacket.publicParams.n;
|
||||||
n.data[0]++;
|
n[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid RSA e', async function() {
|
it('detect invalid RSA e', async function() {
|
||||||
const keyPacket = cloneKeyPacket(rsaKey);
|
const keyPacket = cloneKeyPacket(rsaKey);
|
||||||
const e = keyPacket.params[1];
|
const e = keyPacket.publicParams.e;
|
||||||
e.data[0]++;
|
e[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -280,28 +272,27 @@ module.exports = () => {
|
||||||
|
|
||||||
it('detect invalid DSA p', async function() {
|
it('detect invalid DSA p', async function() {
|
||||||
const keyPacket = cloneKeyPacket(dsaKey);
|
const keyPacket = cloneKeyPacket(dsaKey);
|
||||||
const p = keyPacket.params[0];
|
const p = keyPacket.publicParams.p;
|
||||||
p.data[0]++;
|
p[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid DSA y', async function() {
|
it('detect invalid DSA y', async function() {
|
||||||
const keyPacket = cloneKeyPacket(dsaKey);
|
const keyPacket = cloneKeyPacket(dsaKey);
|
||||||
const y = keyPacket.params[3];
|
const y = keyPacket.publicParams.y;
|
||||||
|
|
||||||
y.data[0]++;
|
y[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid DSA g', async function() {
|
it('detect invalid DSA g', async function() {
|
||||||
const keyPacket = cloneKeyPacket(dsaKey);
|
const keyPacket = cloneKeyPacket(dsaKey);
|
||||||
const g = keyPacket.params[2];
|
const g = keyPacket.publicParams.g;
|
||||||
|
|
||||||
g.data[0]++;
|
g[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
const gOne = new openpgp.MPI(new Uint8Array([1]));
|
keyPacket.publicParams.g = new Uint8Array([1]);
|
||||||
keyPacket.params[2] = gOne;
|
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -318,40 +309,39 @@ module.exports = () => {
|
||||||
|
|
||||||
it('detect invalid p', async function() {
|
it('detect invalid p', async function() {
|
||||||
const keyPacket = cloneKeyPacket(egKey);
|
const keyPacket = cloneKeyPacket(egKey);
|
||||||
const p = keyPacket.params[0];
|
const p = keyPacket.publicParams.p;
|
||||||
p.data[0]++;
|
p[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid y', async function() {
|
it('detect invalid y', async function() {
|
||||||
const keyPacket = cloneKeyPacket(egKey);
|
const keyPacket = cloneKeyPacket(egKey);
|
||||||
const y = keyPacket.params[2];
|
const y = keyPacket.publicParams.y;
|
||||||
y.data[0]++;
|
y[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect invalid g', async function() {
|
it('detect invalid g', async function() {
|
||||||
const keyPacket = cloneKeyPacket(egKey);
|
const keyPacket = cloneKeyPacket(egKey);
|
||||||
const g = keyPacket.params[1];
|
const g = keyPacket.publicParams.g;
|
||||||
|
|
||||||
g.data[0]++;
|
g[0]++;
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
|
|
||||||
const gOne = new openpgp.MPI(new Uint8Array([1]));
|
keyPacket.publicParams.g = new Uint8Array([1]);
|
||||||
keyPacket.params[1] = gOne;
|
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('detect g with small order', async function() {
|
it('detect g with small order', async function() {
|
||||||
const keyPacket = cloneKeyPacket(egKey);
|
const keyPacket = cloneKeyPacket(egKey);
|
||||||
const p = keyPacket.params[0].toUint8Array();
|
const p = keyPacket.publicParams.p;
|
||||||
const g = keyPacket.params[1].toUint8Array();
|
const g = keyPacket.publicParams.g;
|
||||||
|
|
||||||
const pBN = new BN(p);
|
const pBN = new BN(p);
|
||||||
const gModP = new BN(g).toRed(new BN.red(pBN));
|
const gModP = new BN(g).toRed(new BN.red(pBN));
|
||||||
// g**(p-1)/2 has order 2
|
// g**(p-1)/2 has order 2
|
||||||
const gOrd2 = gModP.redPow(pBN.subn(1).shrn(1));
|
const gOrd2 = gModP.redPow(pBN.subn(1).shrn(1));
|
||||||
keyPacket.params[1] = new openpgp.MPI(gOrd2.toArrayLike(Uint8Array, 'be'));
|
keyPacket.publicParams.g = gOrd2.toArrayLike(Uint8Array, 'be');
|
||||||
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(keyPacket.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2584,6 +2584,17 @@ function versionSpecificTests() {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = () => describe('Key', function() {
|
module.exports = () => describe('Key', function() {
|
||||||
|
async function deepCopyKeyParams(params) {
|
||||||
|
const paramsCopy = {};
|
||||||
|
Object.keys(params).forEach(name => {
|
||||||
|
const param = params[name];
|
||||||
|
const copy = new Uint8Array(param.length);
|
||||||
|
copy.set(param);
|
||||||
|
paramsCopy[name] = copy;
|
||||||
|
});
|
||||||
|
return paramsCopy;
|
||||||
|
}
|
||||||
|
|
||||||
let rsaGenStub;
|
let rsaGenStub;
|
||||||
let v5KeysVal;
|
let v5KeysVal;
|
||||||
let aeadProtectVal;
|
let aeadProtectVal;
|
||||||
|
@ -2594,8 +2605,9 @@ module.exports = () => describe('Key', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
// We fake the generation function to speed up the tests
|
||||||
rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
|
rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
|
||||||
rsaGenStub.callsFake(N => rsaGenValue[N]);
|
rsaGenStub.callsFake(async N => deepCopyKeyParams(await rsaGenValue[N]));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -2882,7 +2894,7 @@ module.exports = () => describe('Key', function() {
|
||||||
key.primaryKey.makeDummy();
|
key.primaryKey.makeDummy();
|
||||||
expect(key.primaryKey.isDummy()).to.be.true;
|
expect(key.primaryKey.isDummy()).to.be.true;
|
||||||
await key.validate();
|
await key.validate();
|
||||||
await expect(openpgp.reformatKey({ privateKey: key, userIds: 'test2 <b@a.com>' })).to.be.rejectedWith(/Missing private key parameters/);
|
await expect(openpgp.reformatKey({ privateKey: key, userIds: 'test2 <b@a.com>' })).to.be.rejectedWith(/Missing key parameters/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('makeDummy() - subkeys of the converted key can still sign', async function() {
|
it('makeDummy() - subkeys of the converted key can still sign', async function() {
|
||||||
|
@ -2905,25 +2917,28 @@ module.exports = () => describe('Key', function() {
|
||||||
const key = await openpgp.key.readArmored(priv_key_rsa);
|
const key = await openpgp.key.readArmored(priv_key_rsa);
|
||||||
await key.decrypt('hello world');
|
await key.decrypt('hello world');
|
||||||
const signingKeyPacket = key.subKeys[0].keyPacket;
|
const signingKeyPacket = key.subKeys[0].keyPacket;
|
||||||
const params = signingKeyPacket.params;
|
const privateParams = signingKeyPacket.privateParams;
|
||||||
await key.clearPrivateParams();
|
await key.clearPrivateParams();
|
||||||
key.primaryKey.isEncrypted = false;
|
key.primaryKey.isEncrypted = false;
|
||||||
key.primaryKey.params = params;
|
key.primaryKey.privateParams = privateParams;
|
||||||
key.subKeys[0].keyPacket.isEncrypted = false;
|
key.subKeys[0].keyPacket.isEncrypted = false;
|
||||||
key.subKeys[0].keyPacket.params = params;
|
key.subKeys[0].keyPacket.privateParams = privateParams;
|
||||||
await expect(key.validate()).to.be.rejectedWith('Missing key parameters');
|
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clearPrivateParams() - detect that private key parameters were zeroed out', async function() {
|
it('clearPrivateParams() - detect that private key parameters were zeroed out', async function() {
|
||||||
const key = await openpgp.key.readArmored(priv_key_rsa);
|
const key = await openpgp.key.readArmored(priv_key_rsa);
|
||||||
await key.decrypt('hello world');
|
await key.decrypt('hello world');
|
||||||
const signingKeyPacket = key.subKeys[0].keyPacket;
|
const signingKeyPacket = key.subKeys[0].keyPacket;
|
||||||
const params = signingKeyPacket.params.slice();
|
const privateParams = {};
|
||||||
|
Object.entries(signingKeyPacket.privateParams).forEach(([name, value]) => {
|
||||||
|
privateParams[name] = value;
|
||||||
|
});
|
||||||
await key.clearPrivateParams();
|
await key.clearPrivateParams();
|
||||||
key.primaryKey.isEncrypted = false;
|
key.primaryKey.isEncrypted = false;
|
||||||
key.primaryKey.params = params;
|
key.primaryKey.privateParams = privateParams;
|
||||||
key.subKeys[0].keyPacket.isEncrypted = false;
|
key.subKeys[0].keyPacket.isEncrypted = false;
|
||||||
key.subKeys[0].keyPacket.params = params;
|
key.subKeys[0].keyPacket.privateParams = privateParams;
|
||||||
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
await expect(key.validate()).to.be.rejectedWith('Key is invalid');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3398,9 +3413,9 @@ VYGdb3eNlV8CfoEC
|
||||||
const subKey = newPrivateKey.subKeys[total];
|
const subKey = newPrivateKey.subKeys[total];
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
const subkeyN = subKey.keyPacket.params[0];
|
const subkeyN = subKey.keyPacket.publicParams.n;
|
||||||
const pkN = privateKey.primaryKey.params[0];
|
const pkN = privateKey.primaryKey.publicParams.n;
|
||||||
expect(subkeyN.byteLength()).to.be.equal(rsaBits ? (rsaBits / 8) : pkN.byteLength());
|
expect(subkeyN.length).to.be.equal(rsaBits ? (rsaBits / 8) : pkN.length);
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('rsaEncryptSign');
|
||||||
expect(subKey.getAlgorithmInfo().rsaBits).to.be.equal(rsaBits || privateKey.getAlgorithmInfo().rsaBits);
|
expect(subKey.getAlgorithmInfo().rsaBits).to.be.equal(rsaBits || privateKey.getAlgorithmInfo().rsaBits);
|
||||||
await subKey.verify(newPrivateKey.primaryKey);
|
await subKey.verify(newPrivateKey.primaryKey);
|
||||||
|
@ -3446,8 +3461,8 @@ VYGdb3eNlV8CfoEC
|
||||||
expect(subKey1.getKeyId().toHex()).to.be.equal(subKey.getKeyId().toHex());
|
expect(subKey1.getKeyId().toHex()).to.be.equal(subKey.getKeyId().toHex());
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
const subkeyOid = subKey.keyPacket.params[0];
|
const subkeyOid = subKey.keyPacket.publicParams.oid;
|
||||||
const pkOid = privateKey.primaryKey.params[0];
|
const pkOid = privateKey.primaryKey.publicParams.oid;
|
||||||
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||||
await subKey.verify(privateKey.primaryKey);
|
await subKey.verify(privateKey.primaryKey);
|
||||||
|
@ -3464,7 +3479,7 @@ VYGdb3eNlV8CfoEC
|
||||||
const subKey = newPrivateKey.subKeys[total];
|
const subKey = newPrivateKey.subKeys[total];
|
||||||
expect(subKey).to.exist;
|
expect(subKey).to.exist;
|
||||||
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
expect(newPrivateKey.subKeys.length).to.be.equal(total + 1);
|
||||||
expect(subKey.keyPacket.params[0].getName()).to.be.equal(openpgp.enums.curve.curve25519);
|
expect(subKey.keyPacket.publicParams.oid.getName()).to.be.equal(openpgp.enums.curve.curve25519);
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdh');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('ecdh');
|
||||||
await subKey.verify(privateKey.primaryKey);
|
await subKey.verify(privateKey.primaryKey);
|
||||||
});
|
});
|
||||||
|
@ -3479,8 +3494,8 @@ VYGdb3eNlV8CfoEC
|
||||||
const armoredKey = newPrivateKey.armor();
|
const armoredKey = newPrivateKey.armor();
|
||||||
newPrivateKey = await openpgp.key.readArmored(armoredKey);
|
newPrivateKey = await openpgp.key.readArmored(armoredKey);
|
||||||
const subKey = newPrivateKey.subKeys[total];
|
const subKey = newPrivateKey.subKeys[total];
|
||||||
const subkeyOid = subKey.keyPacket.params[0];
|
const subkeyOid = subKey.keyPacket.publicParams.oid;
|
||||||
const pkOid = newPrivateKey.primaryKey.params[0];
|
const pkOid = newPrivateKey.primaryKey.publicParams.oid;
|
||||||
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
expect(subkeyOid.getName()).to.be.equal(pkOid.getName());
|
||||||
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
expect(subKey.getAlgorithmInfo().algorithm).to.be.equal('eddsa');
|
||||||
await subKey.verify(newPrivateKey.primaryKey);
|
await subKey.verify(newPrivateKey.primaryKey);
|
||||||
|
|
|
@ -549,13 +549,24 @@ function withCompression(tests) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = () => describe('OpenPGP.js public api tests', function() {
|
module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
|
async function deepCopyKeyParams(params) {
|
||||||
|
const paramsCopy = {};
|
||||||
|
Object.keys(params).forEach(name => {
|
||||||
|
const param = params[name];
|
||||||
|
const copy = new Uint8Array(param.length);
|
||||||
|
copy.set(param);
|
||||||
|
paramsCopy[name] = copy;
|
||||||
|
});
|
||||||
|
return paramsCopy;
|
||||||
|
}
|
||||||
|
|
||||||
let rsaGenStub;
|
let rsaGenStub;
|
||||||
let rsaGenValue = openpgp.crypto.publicKey.rsa.generate(openpgp.util.getWebCryptoAll() ? 2048 : 512, 65537);
|
const rsaGenValue = openpgp.crypto.publicKey.rsa.generate(openpgp.util.getWebCryptoAll() ? 2048 : 512, 65537);
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
// We fake the generation function to speed up the tests
|
||||||
rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
|
rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
|
||||||
rsaGenStub.returns(rsaGenValue);
|
rsaGenStub.returns(async () => deepCopyKeyParams(await rsaGenValue()));
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function() {
|
afterEach(function() {
|
||||||
|
@ -2329,7 +2340,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() {
|
||||||
const pubKeyDE = await openpgp.key.readArmored(pub_key_de);
|
const pubKeyDE = await openpgp.key.readArmored(pub_key_de);
|
||||||
const privKeyDE = await openpgp.key.readArmored(priv_key_de);
|
const privKeyDE = await openpgp.key.readArmored(priv_key_de);
|
||||||
// corrupt the public key params
|
// corrupt the public key params
|
||||||
privKeyDE.subKeys[0].keyPacket.params[0].data[0]++;
|
privKeyDE.subKeys[0].keyPacket.publicParams.p[0]++;
|
||||||
// validation will not check the decryption subkey and will succeed
|
// validation will not check the decryption subkey and will succeed
|
||||||
await privKeyDE.decrypt(passphrase);
|
await privKeyDE.decrypt(passphrase);
|
||||||
const encrypted = await openpgp.encrypt({
|
const encrypted = await openpgp.encrypt({
|
||||||
|
|
|
@ -6,6 +6,7 @@ chai.use(require('chai-as-promised'));
|
||||||
|
|
||||||
const { expect } = chai;
|
const { expect } = chai;
|
||||||
const input = require('./testInputs.js');
|
const input = require('./testInputs.js');
|
||||||
|
const { PacketList } = require('../../dist/node/openpgp.min');
|
||||||
|
|
||||||
function stringify(array) {
|
function stringify(array) {
|
||||||
if (openpgp.util.isStream(array)) {
|
if (openpgp.util.isStream(array)) {
|
||||||
|
@ -315,16 +316,10 @@ module.exports = () => describe("Packet", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Public key encrypted symmetric key packet', function() {
|
it('Public key encrypted symmetric key packet', function() {
|
||||||
const rsa = openpgp.crypto.publicKey.rsa;
|
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
return rsa.generate(keySize, 65537).then(function(mpiGen) {
|
return openpgp.crypto.generateParams(rsa, keySize, 65537).then(function({ publicParams, privateParams }) {
|
||||||
|
|
||||||
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
|
|
||||||
mpi = mpi.map(function(k) {
|
|
||||||
return new openpgp.MPI(k);
|
|
||||||
});
|
|
||||||
|
|
||||||
const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket();
|
const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket();
|
||||||
const msg = new openpgp.PacketList();
|
const msg = new openpgp.PacketList();
|
||||||
const msg2 = new openpgp.PacketList();
|
const msg2 = new openpgp.PacketList();
|
||||||
|
@ -333,14 +328,12 @@ module.exports = () => describe("Packet", function() {
|
||||||
enc.publicKeyAlgorithm = 'rsaEncryptSign';
|
enc.publicKeyAlgorithm = 'rsaEncryptSign';
|
||||||
enc.sessionKeyAlgorithm = 'aes256';
|
enc.sessionKeyAlgorithm = 'aes256';
|
||||||
enc.publicKeyId.bytes = '12345678';
|
enc.publicKeyId.bytes = '12345678';
|
||||||
return enc.encrypt({ params: mpi, getFingerprintBytes() {} }).then(async () => {
|
return enc.encrypt({ publicParams, getFingerprintBytes() {} }).then(async () => {
|
||||||
|
|
||||||
msg.push(enc);
|
msg.push(enc);
|
||||||
|
|
||||||
await msg2.read(msg.write(), openpgp);
|
await msg2.read(msg.write(), openpgp);
|
||||||
|
|
||||||
return msg2[0].decrypt({ algorithm: 'rsaEncryptSign', params: mpi, getFingerprintBytes() {} }).then(() => {
|
return msg2[0].decrypt({ algorithm: 'rsaEncryptSign', publicParams, privateParams, getFingerprintBytes() {} }).then(() => {
|
||||||
|
|
||||||
expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey));
|
expect(stringify(msg2[0].sessionKey)).to.equal(stringify(enc.sessionKey));
|
||||||
expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm);
|
expect(msg2[0].sessionKeyAlgorithm).to.equal(enc.sessionKeyAlgorithm);
|
||||||
});
|
});
|
||||||
|
@ -841,82 +834,67 @@ V+HOQJQxXJkVRYa3QrFUehiMzTeqqMdgC6ZqJy7+
|
||||||
expect(rawNotations[1].humanReadable).to.equal(true);
|
expect(rawNotations[1].humanReadable).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Writing and encryption of a secret key packet.', function() {
|
it('Writing and encryption of a secret key packet (AEAD)', async function() {
|
||||||
const key = new openpgp.PacketList();
|
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
key.push(new openpgp.SecretKeyPacket());
|
|
||||||
|
|
||||||
const rsa = openpgp.crypto.publicKey.rsa;
|
|
||||||
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
const { privateParams, publicParams } = await openpgp.crypto.generateParams(rsa, keySize, 65537);
|
||||||
|
|
||||||
return rsa.generate(keySize, 65537).then(async function(mpiGen) {
|
const secretKeyPacket = new openpgp.SecretKeyPacket();
|
||||||
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
|
secretKeyPacket.privateParams = privateParams;
|
||||||
mpi = mpi.map(function(k) {
|
secretKeyPacket.publicParams = publicParams;
|
||||||
return new openpgp.MPI(k);
|
secretKeyPacket.algorithm = "rsaSign";
|
||||||
});
|
secretKeyPacket.isEncrypted = false;
|
||||||
|
await secretKeyPacket.encrypt('hello');
|
||||||
|
|
||||||
key[0].params = mpi;
|
const raw = new openpgp.PacketList();
|
||||||
key[0].algorithm = "rsaSign";
|
raw.push(secretKeyPacket);
|
||||||
key[0].isEncrypted = false;
|
const packetList = new openpgp.PacketList();
|
||||||
await key[0].encrypt('hello');
|
await packetList.read(raw.write(), openpgp);
|
||||||
|
const secretKeyPacket2 = packetList[0];
|
||||||
|
await secretKeyPacket2.decrypt('hello');
|
||||||
|
|
||||||
const raw = key.write();
|
expect(secretKeyPacket2.privateParams).to.deep.equal(secretKeyPacket.privateParams);
|
||||||
|
expect(secretKeyPacket2.publicParams).to.deep.equal(secretKeyPacket.publicParams);
|
||||||
const key2 = new openpgp.PacketList();
|
|
||||||
await key2.read(raw, openpgp);
|
|
||||||
await key2[0].decrypt('hello');
|
|
||||||
|
|
||||||
expect(key[0].params.toString()).to.equal(key2[0].params.toString());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Writing and encryption of a secret key packet. (AEAD)', async function() {
|
it('Writing and encryption of a secret key packet (CFB)', async function() {
|
||||||
let aeadProtectVal = openpgp.config.aeadProtect;
|
const aeadProtectVal = openpgp.config.aeadProtect;
|
||||||
openpgp.config.aeadProtect = true;
|
openpgp.config.aeadProtect = false;
|
||||||
|
|
||||||
const key = new openpgp.PacketList();
|
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
key.push(new openpgp.SecretKeyPacket());
|
|
||||||
|
|
||||||
const rsa = openpgp.crypto.publicKey.rsa;
|
|
||||||
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const mpiGen = await rsa.generate(keySize, 65537);
|
const { privateParams, publicParams } = await openpgp.crypto.generateParams(rsa, keySize, 65537);
|
||||||
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
|
const secretKeyPacket = new openpgp.SecretKeyPacket();
|
||||||
mpi = mpi.map(function(k) {
|
secretKeyPacket.privateParams = privateParams;
|
||||||
return new openpgp.MPI(k);
|
secretKeyPacket.publicParams = publicParams;
|
||||||
});
|
secretKeyPacket.algorithm = "rsaSign";
|
||||||
|
secretKeyPacket.isEncrypted = false;
|
||||||
|
await secretKeyPacket.encrypt('hello');
|
||||||
|
|
||||||
key[0].params = mpi;
|
const raw = new openpgp.PacketList();
|
||||||
key[0].algorithm = "rsaSign";
|
raw.push(secretKeyPacket);
|
||||||
key[0].isEncrypted = false;
|
const packetList = new openpgp.PacketList();
|
||||||
await key[0].encrypt('hello');
|
await packetList.read(raw.write(), openpgp);
|
||||||
|
const secretKeyPacket2 = packetList[0];
|
||||||
const raw = key.write();
|
await secretKeyPacket2.decrypt('hello');
|
||||||
|
|
||||||
const key2 = new openpgp.PacketList();
|
|
||||||
await key2.read(raw, openpgp);
|
|
||||||
await key2[0].decrypt('hello');
|
|
||||||
|
|
||||||
expect(key[0].params.toString()).to.equal(key2[0].params.toString());
|
|
||||||
} finally {
|
} finally {
|
||||||
openpgp.config.aeadProtect = aeadProtectVal;
|
openpgp.config.aeadProtect = aeadProtectVal;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Writing and verification of a signature packet.', function() {
|
it('Writing and verification of a signature packet', function() {
|
||||||
const key = new openpgp.SecretKeyPacket();
|
const key = new openpgp.SecretKeyPacket();
|
||||||
|
|
||||||
const rsa = openpgp.crypto.publicKey.rsa;
|
const rsa = openpgp.enums.publicKey.rsaEncryptSign;
|
||||||
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
|
||||||
return rsa.generate(keySize, 65537).then(function(mpiGen) {
|
return openpgp.crypto.generateParams(rsa, keySize, 65537).then(function({ privateParams, publicParams }) {
|
||||||
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
|
|
||||||
mpi = mpi.map(function(k) {
|
|
||||||
return new openpgp.MPI(k);
|
|
||||||
});
|
|
||||||
const testText = input.createSomeMessage();
|
const testText = input.createSomeMessage();
|
||||||
|
|
||||||
key.params = mpi;
|
key.publicParams = publicParams;
|
||||||
|
key.privateParams = privateParams;
|
||||||
key.algorithm = "rsaSign";
|
key.algorithm = "rsaSign";
|
||||||
|
|
||||||
const signed = new openpgp.PacketList();
|
const signed = new openpgp.PacketList();
|
||||||
|
|
|
@ -877,9 +877,9 @@ hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
|
||||||
expect(msg.signatures).to.have.length(1);
|
expect(msg.signatures).to.have.length(1);
|
||||||
expect(msg.signatures[0].valid).to.be.true;
|
expect(msg.signatures[0].valid).to.be.true;
|
||||||
expect(msg.signatures[0].signature.packets.length).to.equal(1);
|
expect(msg.signatures[0].signature.packets.length).to.equal(1);
|
||||||
await expect(openpgp.sign({ message: openpgp.message.fromText('test'), privateKeys: [priv_key_gnupg_ext] })).to.eventually.be.rejectedWith('Missing private key parameters');
|
await expect(openpgp.sign({ message: openpgp.message.fromText('test'), privateKeys: [priv_key_gnupg_ext] })).to.eventually.be.rejectedWith(/Missing key parameters/);
|
||||||
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith('Missing private key parameters');
|
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext })).to.eventually.be.rejectedWith(/Missing key parameters/);
|
||||||
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith('Missing private key parameters');
|
await expect(openpgp.reformatKey({ userIds: { name: 'test' }, privateKey: priv_key_gnupg_ext_2, passphrase: 'test' })).to.eventually.be.rejectedWith(/Missing key parameters/);
|
||||||
await priv_key_gnupg_ext.encrypt("abcd");
|
await priv_key_gnupg_ext.encrypt("abcd");
|
||||||
expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
|
expect(priv_key_gnupg_ext.isDecrypted()).to.be.false;
|
||||||
const primaryKey_packet2 = priv_key_gnupg_ext.primaryKey.write();
|
const primaryKey_packet2 = priv_key_gnupg_ext.primaryKey.write();
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a 64 character long javascript string out of the whole utf-8 range.
|
* Generates a 64 character long javascript string out of the whole utf-8 range.
|
||||||
*/
|
*/
|
||||||
function createSomeMessage(){
|
function createSomeMessage(){
|
||||||
let arr = [];
|
const arr = [];
|
||||||
for (let i = 0; i < 30; i++) {
|
for (let i = 0; i < 30; i++) {
|
||||||
arr.push(Math.floor(Math.random() * 10174) + 1);
|
arr.push(Math.floor(Math.random() * 10174) + 1);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
arr.push(0x1F600 + Math.floor(Math.random() * (0x1F64F - 0x1F600)) + 1);
|
arr.push(0x1F600 + Math.floor(Math.random() * (0x1F64F - 0x1F600)) + 1);
|
||||||
}
|
}
|
||||||
return ' \t' + String.fromCodePoint(...arr).replace(/\r/g, '\n') + ' \t\n한국어/조선말';
|
return ' \t' + String.fromCodePoint(...arr).replace(/\r/g, '\n') + ' \t\n한국어/조선말';
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createSomeMessage: createSomeMessage
|
createSomeMessage: createSomeMessage
|
||||||
};
|
};
|
||||||
|
|
|
@ -219,22 +219,24 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
|
||||||
const { publicKey } = openpgp.crypto.publicKey.nacl.sign.keyPair.fromSeed(openpgp.util.hexToUint8Array(vector.SECRET_KEY));
|
const { publicKey } = openpgp.crypto.publicKey.nacl.sign.keyPair.fromSeed(openpgp.util.hexToUint8Array(vector.SECRET_KEY));
|
||||||
expect(publicKey).to.deep.equal(openpgp.util.hexToUint8Array(vector.PUBLIC_KEY));
|
expect(publicKey).to.deep.equal(openpgp.util.hexToUint8Array(vector.PUBLIC_KEY));
|
||||||
const data = util.strToUint8Array(vector.MESSAGE);
|
const data = util.strToUint8Array(vector.MESSAGE);
|
||||||
const keyIntegers = [
|
const privateParams = {
|
||||||
new openpgp.OID(curve.oid),
|
seed: util.hexToUint8Array(vector.SECRET_KEY)
|
||||||
new openpgp.MPI(util.hexToStr('40'+vector.PUBLIC_KEY)),
|
};
|
||||||
new openpgp.MPI(util.hexToStr(vector.SECRET_KEY))
|
const publicParams = {
|
||||||
];
|
oid: new openpgp.OID(curve.oid),
|
||||||
|
Q: util.hexToUint8Array('40' + vector.PUBLIC_KEY)
|
||||||
|
};
|
||||||
const msg_MPIs = [
|
const msg_MPIs = [
|
||||||
new openpgp.MPI(util.uint8ArrayToStr(util.hexToUint8Array(vector.SIGNATURE.R).reverse())),
|
new openpgp.MPI(util.uint8ArrayToStr(util.hexToUint8Array(vector.SIGNATURE.R).reverse())),
|
||||||
new openpgp.MPI(util.uint8ArrayToStr(util.hexToUint8Array(vector.SIGNATURE.S).reverse()))
|
new openpgp.MPI(util.uint8ArrayToStr(util.hexToUint8Array(vector.SIGNATURE.S).reverse()))
|
||||||
];
|
];
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
signature.sign(22, undefined, keyIntegers, undefined, data).then(signed => {
|
signature.sign(22, undefined, publicParams, privateParams, undefined, data).then(signed => {
|
||||||
const len = ((signed[0] << 8| signed[1]) + 7) / 8;
|
const len = (((signed[0] << 8) | signed[1]) + 7) / 8;
|
||||||
expect(util.hexToUint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len));
|
expect(util.hexToUint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len));
|
||||||
expect(util.hexToUint8Array(vector.SIGNATURE.S)).to.deep.eq(signed.slice(4 + len));
|
expect(util.hexToUint8Array(vector.SIGNATURE.S)).to.deep.eq(signed.slice(4 + len));
|
||||||
}),
|
}),
|
||||||
signature.verify(22, undefined, msg_MPIs, keyIntegers, undefined, data).then(result => {
|
signature.verify(22, undefined, msg_MPIs, publicParams, undefined, data).then(result => {
|
||||||
expect(result).to.be.true;
|
expect(result).to.be.true;
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
@ -242,63 +244,44 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
|
||||||
|
|
||||||
it('Signature of empty string', function () {
|
it('Signature of empty string', function () {
|
||||||
return testVector({
|
return testVector({
|
||||||
SECRET_KEY:
|
SECRET_KEY: '9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60',
|
||||||
['9d61b19deffd5a60ba844af492ec2cc4',
|
PUBLIC_KEY: 'd75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a',
|
||||||
'4449c5697b326919703bac031cae7f60'].join(''),
|
|
||||||
PUBLIC_KEY:
|
|
||||||
['d75a980182b10ab7d54bfed3c964073a',
|
|
||||||
'0ee172f3daa62325af021a68f707511a'].join(''),
|
|
||||||
MESSAGE: '',
|
MESSAGE: '',
|
||||||
SIGNATURE:
|
SIGNATURE: {
|
||||||
{ R: ['e5564300c360ac729086e2cc806e828a',
|
R: 'e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155',
|
||||||
'84877f1eb8e5d974d873e06522490155'].join(''),
|
S: '5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b'
|
||||||
S: ['5fb8821590a33bacc61e39701cf9b46b',
|
}
|
||||||
'd25bf5f0595bbe24655141438e7a100b'].join('') }
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Signature of single byte', function () {
|
it('Signature of single byte', function () {
|
||||||
return testVector({
|
return testVector({
|
||||||
SECRET_KEY:
|
SECRET_KEY: '4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb',
|
||||||
['4ccd089b28ff96da9db6c346ec114e0f',
|
PUBLIC_KEY: '3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c',
|
||||||
'5b8a319f35aba624da8cf6ed4fb8a6fb'].join(''),
|
|
||||||
PUBLIC_KEY:
|
|
||||||
['3d4017c3e843895a92b70aa74d1b7ebc',
|
|
||||||
'9c982ccf2ec4968cc0cd55f12af4660c'].join(''),
|
|
||||||
MESSAGE: util.hexToStr('72'),
|
MESSAGE: util.hexToStr('72'),
|
||||||
SIGNATURE:
|
SIGNATURE: {
|
||||||
{ R: ['92a009a9f0d4cab8720e820b5f642540',
|
R: '92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da',
|
||||||
'a2b27b5416503f8fb3762223ebdb69da'].join(''),
|
S: '085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00'
|
||||||
S: ['085ac1e43e15996e458f3613d0f11d8c',
|
}
|
||||||
'387b2eaeb4302aeeb00d291612bb0c00'].join('') }
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Signature of two bytes', function () {
|
it('Signature of two bytes', function () {
|
||||||
return testVector({
|
return testVector({
|
||||||
SECRET_KEY:
|
SECRET_KEY: 'c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7',
|
||||||
['c5aa8df43f9f837bedb7442f31dcb7b1',
|
PUBLIC_KEY: 'fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025',
|
||||||
'66d38535076f094b85ce3a2e0b4458f7'].join(''),
|
|
||||||
PUBLIC_KEY:
|
|
||||||
['fc51cd8e6218a1a38da47ed00230f058',
|
|
||||||
'0816ed13ba3303ac5deb911548908025'].join(''),
|
|
||||||
MESSAGE: util.hexToStr('af82'),
|
MESSAGE: util.hexToStr('af82'),
|
||||||
SIGNATURE:
|
SIGNATURE: {
|
||||||
{ R: ['6291d657deec24024827e69c3abe01a3',
|
R: '6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac',
|
||||||
'0ce548a284743a445e3680d7db5ac3ac'].join(''),
|
S: '18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a'
|
||||||
S: ['18ff9b538d16f290ae67f760984dc659',
|
}
|
||||||
'4a7c15e9716ed28dc027beceea1ec40a'].join('') }
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Signature of 1023 bytes', function () {
|
it('Signature of 1023 bytes', function () {
|
||||||
return testVector({
|
return testVector({
|
||||||
SECRET_KEY:
|
SECRET_KEY: 'f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5',
|
||||||
['f5e5767cf153319517630f226876b86c',
|
PUBLIC_KEY: '278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e',
|
||||||
'8160cc583bc013744c6bf255f5cc0ee5'].join(''),
|
|
||||||
PUBLIC_KEY:
|
|
||||||
['278117fc144c72340f67d0f2316e8386',
|
|
||||||
'ceffbf2b2428c9c51fef7c597f1d426e'].join(''),
|
|
||||||
MESSAGE: util.hexToStr([
|
MESSAGE: util.hexToStr([
|
||||||
'08b8b2b733424243760fe426a4b54908',
|
'08b8b2b733424243760fe426a4b54908',
|
||||||
'632110a66c2f6591eabd3345e3e4eb98',
|
'632110a66c2f6591eabd3345e3e4eb98',
|
||||||
|
@ -365,39 +348,32 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
|
||||||
'0618983f8741c5ef68d3a101e8a3b8ca',
|
'0618983f8741c5ef68d3a101e8a3b8ca',
|
||||||
'c60c905c15fc910840b94c00a0b9d0'
|
'c60c905c15fc910840b94c00a0b9d0'
|
||||||
].join('')),
|
].join('')),
|
||||||
SIGNATURE:
|
SIGNATURE: {
|
||||||
{ R: ['0aab4c900501b3e24d7cdf4663326a3a',
|
R: '0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350',
|
||||||
'87df5e4843b2cbdb67cbf6e460fec350'].join(''),
|
S: 'aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03'
|
||||||
S: ['aa5371b1508f9f4528ecea23c436d94b',
|
}
|
||||||
'5e8fcd4f681e30a6ac00a9704a188a03'].join('') }
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Signature of SHA(abc)', function () {
|
it('Signature of SHA(abc)', function () {
|
||||||
return testVector({
|
return testVector({
|
||||||
SECRET_KEY:
|
SECRET_KEY: '833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42',
|
||||||
['833fe62409237b9d62ec77587520911e',
|
PUBLIC_KEY: 'ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf',
|
||||||
'9a759cec1d19755b7da901b96dca3d42'].join(''),
|
|
||||||
PUBLIC_KEY:
|
|
||||||
['ec172b93ad5e563bf4932c70e1245034',
|
|
||||||
'c35467ef2efd4d64ebf819683467e2bf'].join(''),
|
|
||||||
MESSAGE: util.hexToStr([
|
MESSAGE: util.hexToStr([
|
||||||
'ddaf35a193617abacc417349ae204131',
|
'ddaf35a193617abacc417349ae204131',
|
||||||
'12e6fa4e89a97ea20a9eeee64b55d39a',
|
'12e6fa4e89a97ea20a9eeee64b55d39a',
|
||||||
'2192992a274fc1a836ba3c23a3feebbd',
|
'2192992a274fc1a836ba3c23a3feebbd',
|
||||||
'454d4423643ce80e2a9ac94fa54ca49f'
|
'454d4423643ce80e2a9ac94fa54ca49f'
|
||||||
].join('')),
|
].join('')),
|
||||||
SIGNATURE:
|
SIGNATURE: {
|
||||||
{ R: ['dc2a4459e7369633a52b1bf277839a00',
|
R: 'dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589',
|
||||||
'201009a3efbf3ecb69bea2186c26b589'].join(''),
|
S: '09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704'
|
||||||
S: ['09351fc9ac90b3ecfdfbc7c66431e030',
|
}
|
||||||
'3dca179c138ac17ad9bef1177331a704'].join('') }
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
/* TODO how does GPG2 accept this?
|
it('Should handle little-endian parameters in EdDSA', async function () {
|
||||||
it('Should handle little-endian parameters in EdDSA', function () {
|
|
||||||
const pubKey = [
|
const pubKey = [
|
||||||
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
|
||||||
'Version: OpenPGP.js v3.0.0',
|
'Version: OpenPGP.js v3.0.0',
|
||||||
|
@ -412,19 +388,18 @@ module.exports = () => (openpgp.config.ci ? describe.skip : describe)('X25519 Cr
|
||||||
'FQIbDAAAhNQBAKmy4gPorjbwTwy5usylHttP28XnTdaGkZ1E7Rc3G9luAQCs',
|
'FQIbDAAAhNQBAKmy4gPorjbwTwy5usylHttP28XnTdaGkZ1E7Rc3G9luAQCs',
|
||||||
'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==',
|
'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==',
|
||||||
'=xeG/',
|
'=xeG/',
|
||||||
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
|
'-----END PGP PUBLIC KEY BLOCK-----'
|
||||||
|
].join('\n');
|
||||||
const hi = await openpgp.key.readArmored(pubKey);
|
const hi = await openpgp.key.readArmored(pubKey);
|
||||||
const results = hi.getPrimaryUser();
|
const results = await hi.getPrimaryUser();
|
||||||
|
// console.log(results);
|
||||||
expect(results).to.exist;
|
expect(results).to.exist;
|
||||||
expect(results.user).to.exist;
|
expect(results.user).to.exist;
|
||||||
const user = results.user;
|
const user = results.user;
|
||||||
expect(user.selfCertifications[0].verify(
|
|
||||||
hi.primaryKey, {userId: user.userId, key: hi.primaryKey}
|
|
||||||
)).to.eventually.be.true;
|
|
||||||
await user.verifyCertificate(
|
await user.verifyCertificate(
|
||||||
hi.primaryKey, user.selfCertifications[0], [hi]
|
hi.primaryKey, user.selfCertifications[0], [hi]
|
||||||
);
|
);
|
||||||
}); */
|
});
|
||||||
|
|
||||||
describe('X25519 Omnibus Tests', omnibus);
|
describe('X25519 Omnibus Tests', omnibus);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user