Various quickfixes and cleanups

This commit is contained in:
Mahrud Sayrafi 2018-02-20 13:33:05 -08:00
parent b794956691
commit 605021af3b
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
15 changed files with 229 additions and 227 deletions

View File

@ -436,7 +436,7 @@ function des_removePadding(message, padding) {
// added by Recurity Labs // added by Recurity Labs
function Des(key) { function TripleDES(key) {
this.key = []; this.key = [];
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
@ -458,13 +458,12 @@ function Des(key) {
}; };
} }
Des.keySize = Des.prototype.keySize = 24; TripleDES.keySize = TripleDES.prototype.keySize = 24;
Des.blockSize = Des.prototype.blockSize = 8; TripleDES.blockSize = TripleDES.prototype.blockSize = 8;
// This is "original" DES - Des is actually Triple DES. // This is "original" DES
// This is only exported so we can unit test.
function OriginalDes(key) { function DES(key) {
this.key = key; this.key = key;
this.encrypt = function(block, padding) { this.encrypt = function(block, padding) {
@ -478,9 +477,4 @@ function OriginalDes(key) {
}; };
} }
export default { export default { DES, TripleDES };
/** @static */
des: Des,
/** @static */
originalDes: OriginalDes
};

View File

@ -1,26 +1,27 @@
/** /**
* @requires crypto/cipher/aes * @requires crypto/cipher/aes
* @requires crypto/cipher/blowfish * @requires crypto/cipher/des
* @requires crypto/cipher/cast5 * @requires crypto/cipher/cast5
* @requires crypto/cipher/twofish * @requires crypto/cipher/twofish
* @requires crypto/cipher/blowfish
* @module crypto/cipher * @module crypto/cipher
*/ */
import aes from './aes.js'; import aes from './aes';
import desModule from './des.js'; import des from './des.js';
import cast5 from './cast5.js'; import cast5 from './cast5';
import twofish from './twofish.js'; import twofish from './twofish';
import blowfish from './blowfish.js'; import blowfish from './blowfish';
export default { export default {
/** @see module:crypto/cipher/aes */ /** @see module:crypto/cipher/aes */
aes128: aes(128), aes128: aes(128),
aes192: aes(192), aes192: aes(192),
aes256: aes(256), aes256: aes(256),
/** @see module:crypto/cipher/des.originalDes */ /** @see module:crypto/cipher/des~DES */
des: desModule.originalDes, des: des.DES,
/** @see module:crypto/cipher/des.des */ /** @see module:crypto/cipher/des~TripleDES */
tripledes: desModule.des, tripledes: des.TripleDES,
/** @see module:crypto/cipher/cast5 */ /** @see module:crypto/cipher/cast5 */
cast5: cast5, cast5: cast5,
/** @see module:crypto/cipher/twofish */ /** @see module:crypto/cipher/twofish */

View File

@ -19,7 +19,6 @@
/** /**
* @requires bn.js * @requires bn.js
* @requires asmcrypto.js
* @requires crypto/public_key * @requires crypto/public_key
* @requires crypto/cipher * @requires crypto/cipher
* @requires crypto/random * @requires crypto/random
@ -27,6 +26,7 @@
* @requires type/kdf_params * @requires type/kdf_params
* @requires type/mpi * @requires type/mpi
* @requires type/oid * @requires type/oid
* @requires enums
* @requires util * @requires util
* @module crypto/crypto * @module crypto/crypto
*/ */
@ -39,6 +39,7 @@ import type_ecdh_symkey from '../type/ecdh_symkey';
import type_kdf_params from '../type/kdf_params'; import type_kdf_params from '../type/kdf_params';
import type_mpi from '../type/mpi'; import type_mpi from '../type/mpi';
import type_oid from '../type/oid'; import type_oid from '../type/oid';
import enums from '../enums';
import util from '../util'; import util from '../util';
function constructParams(types, data) { function constructParams(types, data) {
@ -52,39 +53,41 @@ function constructParams(types, data) {
export default { export default {
/** /**
* Encrypts data using the specified public key multiprecision integers * Encrypts data using specified algorithm and public key parameters.
* and the specified algorithm. * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
* @param {module:enums.publicKey} algo Algorithm to be used (See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) * @param {module:enums.publicKey} algo Public key algorithm
* @param {Array<module:type/mpi|module:type/oid|module:type/kdf_params|module:type/ecdh_symkey>} publicParams Algorithm dependent params * @param {Array<module:type/mpi|
* @param {module:type/mpi} data Data to be encrypted as MPI module:type/oid|
* @param {String} fingerprint Recipient fingerprint module:type/kdf_params>} pub_params Algorithm-specific public key parameters
* @return {Array<module:type/mpi|module:type/oid|module:type/kdf_params|module:type/ecdh_symkey>} encrypted session key parameters * @param {module:type/mpi} data Data to be encrypted as MPI
* @param {String} fingerprint Recipient fingerprint
* @return {Array<module:type/mpi|
module:type/ecdh_symkey>} encrypted session key parameters
*/ */
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) {
// TODO change algo to return enums
const types = this.getEncSessionKeyParamTypes(algo); const types = this.getEncSessionKeyParamTypes(algo);
return (async function() { return (async function() {
switch (algo) { switch (algo) {
case 'rsa_encrypt': case enums.publicKey.rsa_encrypt:
case 'rsa_encrypt_sign': { case enums.publicKey.rsa_encrypt_sign: {
const m = data.toUint8Array(); const m = data.toUint8Array();
const n = publicParams[0].toUint8Array(); const n = pub_params[0].toUint8Array();
const e = publicParams[1].toUint8Array(); const e = pub_params[1].toUint8Array();
const res = await publicKey.rsa.encrypt(m, n, e); const res = await publicKey.rsa.encrypt(m, n, e);
return constructParams(types, [new BN(res)]); return constructParams(types, [new BN(res)]);
} }
case 'elgamal': { case enums.publicKey.elgamal: {
const m = data.toBN(); const m = data.toBN();
const p = publicParams[0].toBN(); const p = pub_params[0].toBN();
const g = publicParams[1].toBN(); const g = pub_params[1].toBN();
const y = publicParams[2].toBN(); const y = pub_params[2].toBN();
const res = await publicKey.elgamal.encrypt(m, p, g, y); 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 'ecdh': { case enums.publicKey.ecdh: {
const oid = publicParams[0]; const oid = pub_params[0];
const kdf_params = publicParams[2]; const Q = pub_params[1].toUint8Array();
const Q = publicParams[1].toUint8Array(); const kdf_params = pub_params[2];
const res = await publicKey.elliptic.ecdh.encrypt( const res = await publicKey.elliptic.ecdh.encrypt(
oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint); oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint);
return constructParams(types, [res.V, res.C]); return constructParams(types, [res.V, res.C]);
@ -96,47 +99,50 @@ export default {
}, },
/** /**
* Decrypts data using the specified public key multiprecision integers of the private key, * Decrypts data using specified algorithm and private key parameters.
* the specified secretMPIs of the private key and the specified algorithm. * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
* @param {module:enums.publicKey} algo Algorithm to be used (See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) * @param {module:enums.publicKey} algo Public key algorithm
* @param {Array<module:type/mpi|module:type/oid|module:type/kdf_params>} keyIntegers Algorithm dependent params * @param {Array<module:type/mpi|
* @param {Array<module:type/mpi|module:type/ecdh_symkey>} dataIntegers encrypted session key parameters module:type/oid|
* @param {String} fingerprint Recipient fingerprint module:type/kdf_params>} key_params Algorithm-specific public, private key parameters
* @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null * @param {Array<module:type/mpi|
module:type/ecdh_symkey>}
data_params encrypted session key parameters
* @param {String} fingerprint Recipient fingerprint
* @return {module:type/mpi} An MPI containing the decrypted data
*/ */
publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) { publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) {
// TODO change algo to return enums
return new type_mpi(await (async function() { return new type_mpi(await (async function() {
switch (algo) { switch (algo) {
case 'rsa_encrypt_sign': case enums.publicKey.rsa_encrypt_sign:
case 'rsa_encrypt': { case enums.publicKey.rsa_encrypt: {
const c = dataIntegers[0].toUint8Array(); const c = data_params[0].toUint8Array();
const n = keyIntegers[0].toUint8Array(); // pq const n = key_params[0].toUint8Array(); // pq
const e = keyIntegers[1].toUint8Array(); const e = key_params[1].toUint8Array();
const d = keyIntegers[2].toUint8Array(); // de = 1 mod (p-1)(q-1) const d = key_params[2].toUint8Array(); // de = 1 mod (p-1)(q-1)
const p = keyIntegers[3].toUint8Array(); const p = key_params[3].toUint8Array();
const q = keyIntegers[4].toUint8Array(); const q = key_params[4].toUint8Array();
const u = keyIntegers[5].toUint8Array(); // q^-1 mod p const u = key_params[5].toUint8Array(); // q^-1 mod p
return publicKey.rsa.decrypt(c, n, e, d, p, q, u); return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
} }
case 'elgamal': { case enums.publicKey.elgamal: {
const c1 = dataIntegers[0].toBN(); const c1 = data_params[0].toBN();
const c2 = dataIntegers[1].toBN(); const c2 = data_params[1].toBN();
const p = keyIntegers[0].toBN(); const p = key_params[0].toBN();
const x = keyIntegers[3].toBN(); const x = key_params[3].toBN();
return publicKey.elgamal.decrypt(c1, c2, p, x); return publicKey.elgamal.decrypt(c1, c2, p, x);
} }
case 'ecdh': { case enums.publicKey.ecdh: {
const oid = keyIntegers[0]; const oid = key_params[0];
const kdf_params = keyIntegers[2]; const kdf_params = key_params[2];
const V = dataIntegers[0].toUint8Array(); const V = data_params[0].toUint8Array();
const C = dataIntegers[1].data; const C = data_params[1].data;
const d = keyIntegers[3].toUint8Array(); const d = key_params[3].toUint8Array();
return publicKey.elliptic.ecdh.decrypt( return publicKey.elliptic.ecdh.decrypt(
oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint); oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint);
} }
default: default:
return null; throw new Error('Invalid public key encryption algorithm.');
} }
}())); }()));
}, },
@ -147,31 +153,31 @@ export default {
*/ */
getPrivKeyParamTypes: function(algo) { getPrivKeyParamTypes: function(algo) {
switch (algo) { switch (algo) {
case 'rsa_encrypt': case enums.publicKey.rsa_encrypt:
case 'rsa_encrypt_sign': case enums.publicKey.rsa_encrypt_sign:
case 'rsa_sign': case enums.publicKey.rsa_sign:
// Algorithm-Specific Fields for RSA secret keys: // Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d. // - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p. // - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q). // - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q. // - MPI of u, the multiplicative inverse of p, mod q.
return [type_mpi, type_mpi, type_mpi, type_mpi]; return [type_mpi, type_mpi, type_mpi, type_mpi];
case 'elgamal': case enums.publicKey.elgamal:
// Algorithm-Specific Fields for Elgamal secret keys: // Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x. // - MPI of Elgamal secret exponent x.
return [type_mpi]; return [type_mpi];
case 'dsa': case enums.publicKey.dsa:
// Algorithm-Specific Fields for DSA secret keys: // Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x. // - MPI of DSA secret exponent x.
return [type_mpi]; return [type_mpi];
case 'ecdh': case enums.publicKey.ecdh:
case 'ecdsa': case enums.publicKey.ecdsa:
case 'eddsa': case enums.publicKey.eddsa:
// Algorithm-Specific Fields for ECDSA or ECDH secret keys: // Algorithm-Specific Fields for ECDSA or ECDH secret keys:
// - MPI of an integer representing the secret key. // - MPI of an integer representing the secret key.
return [type_mpi]; return [type_mpi];
default: default:
throw new Error('Unknown algorithm'); throw new Error('Invalid public key encryption algorithm.');
} }
}, },
@ -184,37 +190,37 @@ export default {
// - a multiprecision integer (MPI) of RSA public modulus n; // - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e. // - an MPI of RSA public encryption exponent e.
switch (algo) { switch (algo) {
case 'rsa_encrypt': case enums.publicKey.rsa_encrypt:
case 'rsa_encrypt_sign': case enums.publicKey.rsa_encrypt_sign:
case 'rsa_sign': case enums.publicKey.rsa_sign:
return [type_mpi, type_mpi]; return [type_mpi, type_mpi];
// Algorithm-Specific Fields for Elgamal public keys: // Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p; // - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g; // - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret). // - MPI of Elgamal public key value y (= g**x mod p where x is secret).
case 'elgamal': case enums.publicKey.elgamal:
return [type_mpi, type_mpi, type_mpi]; return [type_mpi, type_mpi, type_mpi];
// Algorithm-Specific Fields for DSA public keys: // Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p; // - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1); // - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g; // - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret). // - MPI of DSA public-key value y (= g**x mod p where x is secret).
case 'dsa': case enums.publicKey.dsa:
return [type_mpi, type_mpi, type_mpi, type_mpi]; return [type_mpi, type_mpi, type_mpi, type_mpi];
// Algorithm-Specific Fields for ECDSA/EdDSA public keys: // Algorithm-Specific Fields for ECDSA/EdDSA public keys:
// - OID of curve; // - OID of curve;
// - MPI of EC point representing public key. // - MPI of EC point representing public key.
case 'ecdsa': case enums.publicKey.ecdsa:
case 'eddsa': case enums.publicKey.eddsa:
return [type_oid, type_mpi]; return [type_oid, type_mpi];
// Algorithm-Specific Fields for ECDH public keys: // Algorithm-Specific Fields for ECDH public keys:
// - OID of curve; // - OID of curve;
// - MPI of EC point representing public key. // - MPI of EC point representing public key.
// - KDF: variable-length field containing KDF parameters. // - KDF: variable-length field containing KDF parameters.
case 'ecdh': case enums.publicKey.ecdh:
return [type_oid, type_mpi, type_kdf_params]; return [type_oid, type_mpi, type_kdf_params];
default: default:
throw new Error('Unknown algorithm.'); throw new Error('Invalid public key encryption algorithm.');
} }
}, },
@ -226,24 +232,24 @@ export default {
switch (algo) { switch (algo) {
// Algorithm-Specific Fields for RSA encrypted session keys: // Algorithm-Specific Fields for RSA encrypted session keys:
// - MPI of RSA encrypted value m**e mod n. // - MPI of RSA encrypted value m**e mod n.
case 'rsa_encrypt': case enums.publicKey.rsa_encrypt:
case 'rsa_encrypt_sign': case enums.publicKey.rsa_encrypt_sign:
return [type_mpi]; return [type_mpi];
// Algorithm-Specific Fields for Elgamal encrypted session keys: // Algorithm-Specific Fields for Elgamal encrypted session keys:
// - MPI of Elgamal value g**k mod p // - MPI of Elgamal value g**k mod p
// - MPI of Elgamal value m * y**k mod p // - MPI of Elgamal value m * y**k mod p
case 'elgamal': case enums.publicKey.elgamal:
return [type_mpi, type_mpi]; return [type_mpi, type_mpi];
// Algorithm-Specific Fields for ECDH encrypted session keys: // Algorithm-Specific Fields for ECDH encrypted session keys:
// - MPI containing the ephemeral key used to establish the shared secret // - MPI containing the ephemeral key used to establish the shared secret
// - ECDH Symmetric Key // - ECDH Symmetric Key
case 'ecdh': case enums.publicKey.ecdh:
return [type_mpi, type_ecdh_symkey]; return [type_mpi, type_ecdh_symkey];
default: default:
throw new Error('Unknown algorithm.'); throw new Error('Invalid public key encryption algorithm.');
} }
}, },
@ -254,35 +260,39 @@ export default {
* @return {Array} The array of parameters * @return {Array} The array of parameters
*/ */
generateParams: function(algo, bits, oid) { generateParams: function(algo, bits, oid) {
const types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo)); const types = [].concat(this.getPubKeyParamTypes(algo), this.getPrivKeyParamTypes(algo));
switch (algo) { switch (algo) {
case 'rsa_encrypt': case enums.publicKey.rsa_encrypt:
case 'rsa_encrypt_sign': case enums.publicKey.rsa_encrypt_sign:
case 'rsa_sign': { case enums.publicKey.rsa_sign: {
return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { return publicKey.rsa.generate(bits, "10001").then(function(keyObject) {
return constructParams( return constructParams(
types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u]
); );
}); });
} }
case 'ecdsa': case enums.publicKey.dsa:
case 'eddsa': case enums.publicKey.elgamal:
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 publicKey.elliptic.generate(oid).then(function (keyObject) {
return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]); return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]);
}); });
case 'ecdh': case enums.publicKey.ecdh:
return publicKey.elliptic.generate(oid).then(function (keyObject) { return publicKey.elliptic.generate(oid).then(function (keyObject) {
return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]);
}); });
default: default:
throw new Error('Unsupported algorithm for key generation.'); throw new Error('Invalid public key encryption algorithm.');
} }
}, },
/** /**
* generate random byte prefix as string for the specified algorithm * Generates a random byte prefix for the specified algorithm
* @param {module:enums.symmetric} algo Algorithm to use (see {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2}) * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @return {Uint8Array} Random bytes with length equal to the block * @param {module:enums.symmetric} algo Symmetric encryption algorithm
* @return {Uint8Array} Random bytes with length equal to the block
* size of the cipher * size of the cipher
*/ */
getPrefixRandom: function(algo) { getPrefixRandom: function(algo) {

View File

@ -15,6 +15,7 @@ import pkcs5 from './pkcs5.js';
import crypto from './crypto.js'; import crypto from './crypto.js';
import aes_kw from './aes_kw.js'; import aes_kw from './aes_kw.js';
// TODO move cfb and gcm to cipher
const mod = { const mod = {
/** @see module:crypto/cipher */ /** @see module:crypto/cipher */
cipher: cipher, cipher: cipher,

View File

@ -48,7 +48,4 @@ function decode(msg) {
throw new Error('Invalid padding'); throw new Error('Invalid padding');
} }
export default { export default { encode, decode };
encode,
decode
};

View File

@ -63,7 +63,6 @@ export default {
// of leftmost bits equal to the number of bits of q. This (possibly // of leftmost bits equal to the number of bits of q. This (possibly
// truncated) hash function result is treated as a number and used // truncated) hash function result is treated as a number and used
// directly in the DSA signature algorithm. // directly in the DSA signature algorithm.
// TODO rewrite getLeftNBits to work with Uint8Arrays
const h = new BN( const h = new BN(
util.str2Uint8Array( util.str2Uint8Array(
util.getLeftNBits( util.getLeftNBits(

View File

@ -1,67 +1,60 @@
/** /**
* @requires asmcrypto.js
* @requires crypto/public_key * @requires crypto/public_key
* @requires crypto/pkcs1 * @requires crypto/pkcs1
* @requires enums
* @requires util * @requires util
* @module crypto/signature * @module crypto/signature
*/ */
// FIXME wrap rsa.js around this
import publicKey from './public_key'; import publicKey from './public_key';
import pkcs1 from './pkcs1'; import pkcs1 from './pkcs1';
import enums from '../enums';
import util from '../util'; import util from '../util';
export default { export default {
/** /**
* * Verifies the signature provided for data using specified algorithms and public key parameters.
* @param {module:enums.publicKey} algo public Key algorithm * See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* @param {module:enums.hash} hash_algo Hash algorithm * and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* @param {Array<module:type/mpi>} msg_MPIs Signature multiprecision integers * for public key and hash algorithms.
* @param {Array<module:type/mpi>} publickey_MPIs Public key multiprecision integers * @param {module:enums.publicKey} algo Public key algorithm
* @param {Uint8Array} data Data on where the signature was computed on. * @param {module:enums.hash} hash_algo Hash algorithm
* @return {Boolean} true if signature (sig_data was equal to data over hash) * @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
* @param {Array<module:type/mpi>} pub_MPIs Algorithm-specific public key parameters
* @param {Uint8Array} data Data for which the signature was created
* @return {Boolean} True if signature is valid
*/ */
verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data) {
switch (algo) { switch (algo) {
case 1: case enums.publicKey.rsa_encrypt_sign:
// RSA (Encrypt or Sign) [HAC] case enums.publicKey.rsa_encrypt:
case 2: case enums.publicKey.rsa_sign: {
// RSA Encrypt-Only [HAC]
case 3: {
// RSA Sign-Only [HAC]
const m = msg_MPIs[0].toUint8Array(); const m = msg_MPIs[0].toUint8Array();
const n = publickey_MPIs[0].toUint8Array(); const n = pub_MPIs[0].toUint8Array();
const e = publickey_MPIs[1].toUint8Array(); const e = pub_MPIs[1].toUint8Array();
const EM = publicKey.rsa.verify(m, n, e); const EM = publicKey.rsa.verify(m, n, e);
const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length);
return util.hexidump(EM) === EM2; return util.hexidump(EM) === EM2;
} }
case 16: { case enums.publicKey.dsa: {
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
throw new Error("signing with Elgamal is not defined in the OpenPGP standard.");
}
case 17: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
const r = msg_MPIs[0].toBN(); const r = msg_MPIs[0].toBN();
const s = msg_MPIs[1].toBN(); const s = msg_MPIs[1].toBN();
const p = publickey_MPIs[0].toBN(); const p = pub_MPIs[0].toBN();
const q = publickey_MPIs[1].toBN(); const q = pub_MPIs[1].toBN();
const g = publickey_MPIs[2].toBN(); const g = pub_MPIs[2].toBN();
const y = publickey_MPIs[3].toBN(); const y = pub_MPIs[3].toBN();
return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y); return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y);
} }
case 19: { case enums.publicKey.ecdsa: {
// ECDSA const oid = pub_MPIs[0];
const oid = publickey_MPIs[0];
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() }; const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
const Q = publickey_MPIs[1].toUint8Array(); const Q = pub_MPIs[1].toUint8Array();
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q); return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q);
} }
case 22: { case enums.publicKey.eddsa: {
// EdDSA const oid = pub_MPIs[0];
const oid = publickey_MPIs[0];
const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() }; const signature = { R: msg_MPIs[0].toBN(), S: msg_MPIs[1].toBN() };
const Q = publickey_MPIs[1].toBN(); const Q = pub_MPIs[1].toBN();
return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q); return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q);
} }
default: default:
@ -70,25 +63,24 @@ export default {
}, },
/** /**
* Create a signature on data using the specified algorithm * Creates a signature on data using specified algorithms and private key parameters.
* @param {module:enums.publicKey} algo Asymmetric cipher algorithm to use (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}
* @param {module:enums.hash} hash_algo hash Algorithm to use (See {@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}
* @param {Array<module:type/mpi>} keyIntegers Public followed by Private key multiprecision algorithm-specific parameters * for public key and hash algorithms.
* @param {Uint8Array} data Data to be signed * @param {module:enums.publicKey} algo Public key algorithm
* @return {Array<module:type/mpi>} * @param {module:enums.hash} hash_algo Hash algorithm
* @param {Array<module:type/mpi>} key_params Algorithm-specific public and private key parameters
* @param {Uint8Array} data Data to be signed
* @return {Uint8Array} Signature
*/ */
sign: async function(algo, hash_algo, keyIntegers, data) { sign: async function(algo, hash_algo, key_params, data) {
switch (algo) { switch (algo) {
case 1: case enums.publicKey.rsa_encrypt_sign:
// RSA (Encrypt or Sign) [HAC] case enums.publicKey.rsa_encrypt:
case 2: case enums.publicKey.rsa_sign: {
// RSA Encrypt-Only [HAC] const n = key_params[0].toUint8Array();
case 3: { const e = key_params[1].toUint8Array();
// RSA Sign-Only [HAC] const d = key_params[2].toUint8Array();
const n = keyIntegers[0].toUint8Array();
const e = keyIntegers[1].toUint8Array();
const d = keyIntegers[2].toUint8Array();
data = util.Uint8Array2str(data); data = util.Uint8Array2str(data);
const m = util.hex2Uint8Array( const m = util.hex2Uint8Array(
'00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00' '00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00'
@ -96,36 +88,32 @@ export default {
const signature = publicKey.rsa.sign(m, n, e, d); const signature = publicKey.rsa.sign(m, n, e, d);
return util.Uint8Array2MPI(signature); return util.Uint8Array2MPI(signature);
} }
case 17: { case enums.publicKey.dsa: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC] const p = key_params[0].toBN();
const p = keyIntegers[0].toBN(); const q = key_params[1].toBN();
const q = keyIntegers[1].toBN(); const g = key_params[2].toBN();
const g = keyIntegers[2].toBN(); const x = key_params[4].toBN();
const x = keyIntegers[4].toBN();
const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)),
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array))
]); ]);
} }
case 16: { case enums.publicKey.elgamal: {
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
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 19: { case enums.publicKey.ecdsa: {
// ECDSA const oid = key_params[0];
const oid = keyIntegers[0]; const d = key_params[2].toUint8Array();
const d = keyIntegers[2].toUint8Array();
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)),
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array))
]); ]);
} }
case 22: { case enums.publicKey.eddsa: {
// EdDSA const oid = key_params[0];
const oid = keyIntegers[0]; const d = key_params[2].toBN();
const d = keyIntegers[2].toBN();
const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d); const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d);
return util.concatUint8Array([ return util.concatUint8Array([
util.Uint8Array2MPI(Uint8Array.from(signature.R)), util.Uint8Array2MPI(Uint8Array.from(signature.R)),

View File

@ -72,13 +72,22 @@ export default {
* @readonly * @readonly
*/ */
publicKey: { publicKey: {
/** RSA (Encrypt or Sign) [HAC] */
rsa_encrypt_sign: 1, rsa_encrypt_sign: 1,
/** RSA (Encrypt only) [HAC] */
rsa_encrypt: 2, rsa_encrypt: 2,
/** RSA (Sign only) [HAC] */
rsa_sign: 3, rsa_sign: 3,
/** Elgamal (Encrypt only) [ELGAMAL] [HAC] */
elgamal: 16, elgamal: 16,
/** DSA (Sign only) [FIPS186] [HAC] */
dsa: 17, dsa: 17,
/** ECDH (Encrypt only) [RFC6637] */
ecdh: 18, ecdh: 18,
/** ECDSA (Sign only) [RFC6637] */
ecdsa: 19, ecdsa: 19,
/** EdDSA (Sign only)
* [{@link https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04|Draft RFC}] */
eddsa: 22 eddsa: 22
}, },

View File

@ -16,21 +16,21 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/** /**
* @requires config
* @requires crypto
* @requires encoding/armor * @requires encoding/armor
* @requires crypto
* @requires packet
* @requires config
* @requires enums * @requires enums
* @requires util * @requires util
* @requires packet
* @module key * @module key
*/ */
import config from './config';
import crypto from './crypto';
import armor from './encoding/armor'; import armor from './encoding/armor';
import crypto from './crypto';
import packet from './packet';
import config from './config';
import enums from './enums'; import enums from './enums';
import util from './util'; import util from './util';
import packet from './packet';
/** /**
* @class * @class
@ -209,7 +209,7 @@ Key.prototype.getKeyIds = function() {
/** /**
* Returns array containing first key packet for given key ID or all key packets in the case of a wildcard ID * Returns array containing first key packet for given key ID or all key packets in the case of a wildcard ID
* @param {type/keyid>} keyIds * @param {type/keyid} keyId
* @return {(module:packet/public_subkey|module:packet/public_key| * @return {(module:packet/public_subkey|module:packet/public_key|
* module:packet/secret_subkey|module:packet/secret_key|null)} * module:packet/secret_subkey|module:packet/secret_key|null)}
*/ */

View File

@ -393,7 +393,7 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords, wild
* Decrypt symmetric session keys with a private key or password. Either a private key or * Decrypt symmetric session keys with a private key or password. Either a private key or
* a password must be specified. * a password must be specified.
* @param {Message} message a message object containing the encrypted session key packets * @param {Message} message a message object containing the encrypted session key packets
* @param {Key|Array<Key} privateKeys (optional) private keys with decrypted secret key data * @param {Key|Array<Key>} privateKeys (optional) private keys with decrypted secret key data
* @param {String|Array<String>} passwords (optional) passwords to decrypt the session key * @param {String|Array<String>} passwords (optional) passwords to decrypt the session key
* @return {Promise<Object|undefined>} Array of decrypted session key, algorithm pairs in form: * @return {Promise<Object|undefined>} Array of decrypted session key, algorithm pairs in form:
* { data:Uint8Array, algorithm:String } * { data:Uint8Array, algorithm:String }

View File

@ -85,8 +85,8 @@ PublicKey.prototype.read = function (bytes) {
// - A one-octet number denoting the public-key algorithm of this key. // - A one-octet number denoting the public-key algorithm of this key.
this.algorithm = enums.read(enums.publicKey, bytes[pos++]); this.algorithm = enums.read(enums.publicKey, bytes[pos++]);
const algo = enums.write(enums.publicKey, this.algorithm);
const types = crypto.getPubKeyParamTypes(this.algorithm); const types = crypto.getPubKeyParamTypes(algo);
this.params = crypto.constructParams(types); this.params = crypto.constructParams(types);
const b = bytes.subarray(pos, bytes.length); const b = bytes.subarray(pos, bytes.length);
@ -123,10 +123,10 @@ PublicKey.prototype.write = function () {
if (this.version === 3) { if (this.version === 3) {
arr.push(util.writeNumber(this.expirationTimeV3, 2)); arr.push(util.writeNumber(this.expirationTimeV3, 2));
} }
arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); // Algorithm-specific params
const algo = enums.write(enums.publicKey, this.algorithm);
const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; const paramCount = crypto.getPubKeyParamTypes(algo).length;
arr.push(new Uint8Array([algo]));
for (let i = 0; i < paramCount; i++) { for (let i = 0; i < paramCount; i++) {
arr.push(this.params[i].write()); arr.push(this.params[i].write());
} }
@ -180,7 +180,8 @@ PublicKey.prototype.getFingerprint = function () {
toHash = this.writeOld(); toHash = this.writeOld();
this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash));
} else if (this.version === 3) { } else if (this.version === 3) {
const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; const algo = enums.write(enums.publicKey, this.algorithm);
const paramCount = crypto.getPubKeyParamTypes(algo).length;
for (let i = 0; i < paramCount; i++) { for (let i = 0; i < paramCount; i++) {
toHash += this.params[i].toString(); toHash += this.params[i].toString();
} }
@ -192,7 +193,7 @@ PublicKey.prototype.getFingerprint = function () {
/** /**
* Returns algorithm information * Returns algorithm information
* @return {Promise<Object} An object of the form {algorithm: String, bits:int, curve:String} * @return {Promise<Object>} An object of the form {algorithm: String, bits:int, curve:String}
*/ */
PublicKey.prototype.getAlgorithmInfo = function () { PublicKey.prototype.getAlgorithmInfo = function () {
const result = {}; const result = {};
@ -209,7 +210,8 @@ PublicKey.prototype.getAlgorithmInfo = function () {
* Fix custom types after cloning * Fix custom types after cloning
*/ */
PublicKey.prototype.postCloneTypeFix = function() { PublicKey.prototype.postCloneTypeFix = function() {
const types = crypto.getPubKeyParamTypes(this.algorithm); const algo = enums.write(enums.publicKey, this.algorithm);
const types = crypto.getPubKeyParamTypes(algo);
for (let i = 0; i < types.length; i++) { for (let i = 0; i < types.length; i++) {
const param = this.params[i]; const param = this.params[i];
this.params[i] = types[i].fromClone(param); this.params[i] = types[i].fromClone(param);

View File

@ -75,7 +75,8 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) {
let i = 10; let i = 10;
const types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const types = crypto.getEncSessionKeyParamTypes(algo);
this.encrypted = crypto.constructParams(types); this.encrypted = crypto.constructParams(types);
for (let j = 0; j < types.length; j++) { for (let j = 0; j < types.length; j++) {
@ -106,18 +107,15 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
data += util.Uint8Array2str(util.writeNumber(checksum, 2)); data += util.Uint8Array2str(util.writeNumber(checksum, 2));
let toEncrypt; let toEncrypt;
if (this.publicKeyAlgorithm === 'ecdh') { const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
if (algo === enums.publicKey.ecdh) {
toEncrypt = new type_mpi(crypto.pkcs5.encode(data)); toEncrypt = new type_mpi(crypto.pkcs5.encode(data));
} else { } else {
toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength())); toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength()));
} }
this.encrypted = await crypto.publicKeyEncrypt( this.encrypted = await crypto.publicKeyEncrypt(
this.publicKeyAlgorithm, algo, key.params, toEncrypt, key.fingerprint);
key.params,
toEncrypt,
key.fingerprint
);
}; };
/** /**
@ -129,21 +127,18 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
* @return {String} The unencrypted session key * @return {String} The unencrypted session key
*/ */
PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
const result = (await crypto.publicKeyDecrypt( const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
this.publicKeyAlgorithm, const result = await crypto.publicKeyDecrypt(
key.params, algo, key.params, this.encrypted, key.fingerprint);
this.encrypted,
key.fingerprint
)).toString();
let checksum; let checksum;
let decoded; let decoded;
if (this.publicKeyAlgorithm === 'ecdh') { if (algo === enums.publicKey.ecdh) {
decoded = crypto.pkcs5.decode(result); decoded = crypto.pkcs5.decode(result.toString());
checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2)));
} else { } else {
decoded = crypto.pkcs1.eme.decode(result); decoded = crypto.pkcs1.eme.decode(result.toString());
checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); checksum = util.readNumber(result.toUint8Array().slice(result.byteLength() - 2));
} }
key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2)); key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2));
@ -161,7 +156,8 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
*/ */
PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() { PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() {
this.publicKeyId = type_keyid.fromClone(this.publicKeyId); this.publicKeyId = type_keyid.fromClone(this.publicKeyId);
const types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm); const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const types = crypto.getEncSessionKeyParamTypes(algo);
for (let i = 0; i < this.encrypted.length; i++) { for (let i = 0; i < this.encrypted.length; i++) {
this.encrypted[i] = types[i].fromClone(this.encrypted[i]); this.encrypted[i] = types[i].fromClone(this.encrypted[i]);
} }

View File

@ -84,7 +84,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
return new Error("Hash mismatch."); return new Error("Hash mismatch.");
} }
const types = crypto.getPrivKeyParamTypes(algorithm); const algo = enums.write(enums.publicKey, algorithm);
const types = crypto.getPrivKeyParamTypes(algo);
const params = crypto.constructParams(types); const params = crypto.constructParams(types);
let p = 0; let p = 0;
@ -100,7 +101,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
function write_cleartext_params(hash_algorithm, algorithm, params) { function write_cleartext_params(hash_algorithm, algorithm, params) {
const arr = []; const arr = [];
const numPublicParams = crypto.getPubKeyParamTypes(algorithm).length; const algo = enums.write(enums.publicKey, algorithm);
const numPublicParams = crypto.getPubKeyParamTypes(algo).length;
for (let i = numPublicParams; i < params.length; i++) { for (let i = numPublicParams; i < params.length; i++) {
arr.push(params[i].write()); arr.push(params[i].write());
@ -269,8 +271,8 @@ SecretKey.prototype.decrypt = function (passphrase) {
SecretKey.prototype.generate = function (bits, curve) { SecretKey.prototype.generate = function (bits, curve) {
const that = this; const that = this;
const algo = enums.write(enums.publicKey, that.algorithm);
return crypto.generateParams(that.algorithm, bits, curve).then(function(params) { return crypto.generateParams(algo, bits, curve).then(function(params) {
that.params = params; that.params = params;
that.isDecrypted = true; that.isDecrypted = true;
}); });
@ -283,7 +285,8 @@ SecretKey.prototype.clearPrivateParams = function () {
if (!this.encrypted) { if (!this.encrypted) {
throw new Error('If secret key is not encrypted, clearing private params is irreversible.'); throw new Error('If secret key is not encrypted, clearing private params is irreversible.');
} }
this.params = this.params.slice(0, crypto.getPubKeyParamTypes(this.algorithm).length); const algo = enums.write(enums.publicKey, this.algorithm);
this.params = this.params.slice(0, crypto.getPubKeyParamTypes(algo).length);
this.isDecrypted = false; this.isDecrypted = false;
}; };
@ -291,7 +294,8 @@ SecretKey.prototype.clearPrivateParams = function () {
* Fix custom types after cloning * Fix custom types after cloning
*/ */
SecretKey.prototype.postCloneTypeFix = function() { SecretKey.prototype.postCloneTypeFix = function() {
const types = crypto.getPubKeyParamTypes(this.algorithm).concat(crypto.getPrivKeyParamTypes(this.algorithm)); const algo = enums.write(enums.publicKey, this.algorithm);
const types = [].concat(crypto.getPubKeyParamTypes(algo), crypto.getPrivKeyParamTypes(algo));
for (let i = 0; i < this.params.length; i++) { for (let i = 0; i < this.params.length; i++) {
const param = this.params[i]; const param = this.params[i];
this.params[i] = types[i].fromClone(param); this.params[i] = types[i].fromClone(param);

View File

@ -446,6 +446,7 @@ export default {
} }
}, },
// TODO rewrite getLeftNBits to work with Uint8Arrays
getLeftNBits: function (string, bitcount) { getLeftNBits: function (string, bitcount) {
const rest = bitcount % 8; const rest = bitcount % 8;
if (rest === 0) { if (rest === 0) {

View File

@ -381,11 +381,11 @@ describe('API functional testing', function() {
const RSAUnencryptedData = new openpgp.MPI(); const RSAUnencryptedData = new openpgp.MPI();
RSAUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); RSAUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength()));
return crypto.publicKeyEncrypt( return crypto.publicKeyEncrypt(
"rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData 1, RSApubMPIs, RSAUnencryptedData
).then(RSAEncryptedData => { ).then(RSAEncryptedData => {
return crypto.publicKeyDecrypt( return crypto.publicKeyDecrypt(
"rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData 1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
).then(data => { ).then(data => {
data = data.write(); data = data.write();
data = util.Uint8Array2str(data.subarray(2, data.length)); data = util.Uint8Array2str(data.subarray(2, data.length));
@ -402,11 +402,11 @@ describe('API functional testing', function() {
ElgamalUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength())); ElgamalUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength()));
return crypto.publicKeyEncrypt( return crypto.publicKeyEncrypt(
"elgamal", ElgamalpubMPIs, ElgamalUnencryptedData 16, ElgamalpubMPIs, ElgamalUnencryptedData
).then(ElgamalEncryptedData => { ).then(ElgamalEncryptedData => {
return crypto.publicKeyDecrypt( return crypto.publicKeyDecrypt(
"elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData 16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData
).then(data => { ).then(data => {
data = data.write(); data = data.write();
data = util.Uint8Array2str(data.subarray(2, data.length)); data = util.Uint8Array2str(data.subarray(2, data.length));