diff --git a/src/crypto/cipher/des.js b/src/crypto/cipher/des.js index b346a7bf..fd9ebab7 100644 --- a/src/crypto/cipher/des.js +++ b/src/crypto/cipher/des.js @@ -436,7 +436,7 @@ function des_removePadding(message, padding) { // added by Recurity Labs -function Des(key) { +function TripleDES(key) { this.key = []; for (let i = 0; i < 3; i++) { @@ -458,13 +458,12 @@ function Des(key) { }; } -Des.keySize = Des.prototype.keySize = 24; -Des.blockSize = Des.prototype.blockSize = 8; +TripleDES.keySize = TripleDES.prototype.keySize = 24; +TripleDES.blockSize = TripleDES.prototype.blockSize = 8; -// This is "original" DES - Des is actually Triple DES. -// This is only exported so we can unit test. +// This is "original" DES -function OriginalDes(key) { +function DES(key) { this.key = key; this.encrypt = function(block, padding) { @@ -478,9 +477,4 @@ function OriginalDes(key) { }; } -export default { - /** @static */ - des: Des, - /** @static */ - originalDes: OriginalDes -}; +export default { DES, TripleDES }; diff --git a/src/crypto/cipher/index.js b/src/crypto/cipher/index.js index 1cf71cff..9b754c04 100644 --- a/src/crypto/cipher/index.js +++ b/src/crypto/cipher/index.js @@ -1,26 +1,27 @@ /** * @requires crypto/cipher/aes - * @requires crypto/cipher/blowfish + * @requires crypto/cipher/des * @requires crypto/cipher/cast5 * @requires crypto/cipher/twofish + * @requires crypto/cipher/blowfish * @module crypto/cipher */ -import aes from './aes.js'; -import desModule from './des.js'; -import cast5 from './cast5.js'; -import twofish from './twofish.js'; -import blowfish from './blowfish.js'; +import aes from './aes'; +import des from './des.js'; +import cast5 from './cast5'; +import twofish from './twofish'; +import blowfish from './blowfish'; export default { /** @see module:crypto/cipher/aes */ aes128: aes(128), aes192: aes(192), aes256: aes(256), - /** @see module:crypto/cipher/des.originalDes */ - des: desModule.originalDes, - /** @see module:crypto/cipher/des.des */ - tripledes: desModule.des, + /** @see module:crypto/cipher/des~DES */ + des: des.DES, + /** @see module:crypto/cipher/des~TripleDES */ + tripledes: des.TripleDES, /** @see module:crypto/cipher/cast5 */ cast5: cast5, /** @see module:crypto/cipher/twofish */ diff --git a/src/crypto/crypto.js b/src/crypto/crypto.js index 408b4dfe..cfe4ae95 100644 --- a/src/crypto/crypto.js +++ b/src/crypto/crypto.js @@ -19,7 +19,6 @@ /** * @requires bn.js - * @requires asmcrypto.js * @requires crypto/public_key * @requires crypto/cipher * @requires crypto/random @@ -27,6 +26,7 @@ * @requires type/kdf_params * @requires type/mpi * @requires type/oid + * @requires enums * @requires util * @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_mpi from '../type/mpi'; import type_oid from '../type/oid'; +import enums from '../enums'; import util from '../util'; function constructParams(types, data) { @@ -52,39 +53,41 @@ function constructParams(types, data) { export default { /** - * Encrypts data using the specified public key multiprecision integers - * and the specified algorithm. - * @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 {Array} publicParams Algorithm dependent params - * @param {module:type/mpi} data Data to be encrypted as MPI - * @param {String} fingerprint Recipient fingerprint - * @return {Array} encrypted session 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. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {Array} pub_params Algorithm-specific public key parameters + * @param {module:type/mpi} data Data to be encrypted as MPI + * @param {String} fingerprint Recipient fingerprint + * @return {Array} encrypted session key parameters */ - publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { - // TODO change algo to return enums + publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) { const types = this.getEncSessionKeyParamTypes(algo); return (async function() { switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': { + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: { const m = data.toUint8Array(); - const n = publicParams[0].toUint8Array(); - const e = publicParams[1].toUint8Array(); + const n = pub_params[0].toUint8Array(); + const e = pub_params[1].toUint8Array(); const res = await publicKey.rsa.encrypt(m, n, e); return constructParams(types, [new BN(res)]); } - case 'elgamal': { + case enums.publicKey.elgamal: { const m = data.toBN(); - const p = publicParams[0].toBN(); - const g = publicParams[1].toBN(); - const y = publicParams[2].toBN(); + const p = pub_params[0].toBN(); + const g = pub_params[1].toBN(); + const y = pub_params[2].toBN(); const res = await publicKey.elgamal.encrypt(m, p, g, y); return constructParams(types, [res.c1, res.c2]); } - case 'ecdh': { - const oid = publicParams[0]; - const kdf_params = publicParams[2]; - const Q = publicParams[1].toUint8Array(); + case enums.publicKey.ecdh: { + const oid = pub_params[0]; + const Q = pub_params[1].toUint8Array(); + const kdf_params = pub_params[2]; const res = await publicKey.elliptic.ecdh.encrypt( oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint); 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, - * the specified secretMPIs of the private key and the specified algorithm. - * @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 {Array} keyIntegers Algorithm dependent params - * @param {Array} dataIntegers encrypted session key parameters - * @param {String} fingerprint Recipient fingerprint - * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null + * Decrypts data using specified algorithm and private key parameters. + * 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 {Array} key_params Algorithm-specific public, private key parameters + * @param {Array} + 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) { - // TODO change algo to return enums + publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) { return new type_mpi(await (async function() { switch (algo) { - case 'rsa_encrypt_sign': - case 'rsa_encrypt': { - const c = dataIntegers[0].toUint8Array(); - const n = keyIntegers[0].toUint8Array(); // pq - const e = keyIntegers[1].toUint8Array(); - const d = keyIntegers[2].toUint8Array(); // de = 1 mod (p-1)(q-1) - const p = keyIntegers[3].toUint8Array(); - const q = keyIntegers[4].toUint8Array(); - const u = keyIntegers[5].toUint8Array(); // q^-1 mod p + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: { + const c = data_params[0].toUint8Array(); + const n = key_params[0].toUint8Array(); // pq + const e = key_params[1].toUint8Array(); + 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(); // q^-1 mod p return publicKey.rsa.decrypt(c, n, e, d, p, q, u); } - case 'elgamal': { - const c1 = dataIntegers[0].toBN(); - const c2 = dataIntegers[1].toBN(); - const p = keyIntegers[0].toBN(); - const x = keyIntegers[3].toBN(); + case enums.publicKey.elgamal: { + const c1 = data_params[0].toBN(); + const c2 = data_params[1].toBN(); + const p = key_params[0].toBN(); + const x = key_params[3].toBN(); return publicKey.elgamal.decrypt(c1, c2, p, x); } - case 'ecdh': { - const oid = keyIntegers[0]; - const kdf_params = keyIntegers[2]; - const V = dataIntegers[0].toUint8Array(); - const C = dataIntegers[1].data; - const d = keyIntegers[3].toUint8Array(); + case enums.publicKey.ecdh: { + const oid = key_params[0]; + const kdf_params = key_params[2]; + const V = data_params[0].toUint8Array(); + const C = data_params[1].data; + const d = key_params[3].toUint8Array(); return publicKey.elliptic.ecdh.decrypt( oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint); } default: - return null; + throw new Error('Invalid public key encryption algorithm.'); } }())); }, @@ -147,31 +153,31 @@ export default { */ getPrivKeyParamTypes: function(algo) { switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_sign: // 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. return [type_mpi, type_mpi, type_mpi, type_mpi]; - case 'elgamal': + case enums.publicKey.elgamal: // Algorithm-Specific Fields for Elgamal secret keys: // - MPI of Elgamal secret exponent x. return [type_mpi]; - case 'dsa': + case enums.publicKey.dsa: // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. return [type_mpi]; - case 'ecdh': - case 'ecdsa': - case 'eddsa': + case enums.publicKey.ecdh: + case enums.publicKey.ecdsa: + case enums.publicKey.eddsa: // Algorithm-Specific Fields for ECDSA or ECDH secret keys: // - MPI of an integer representing the secret key. return [type_mpi]; 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; // - an MPI of RSA public encryption exponent e. switch (algo) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_sign: return [type_mpi, type_mpi]; // Algorithm-Specific Fields for Elgamal public keys: // - MPI of Elgamal prime p; // - MPI of Elgamal group generator g; // - 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]; // 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 'dsa': + case enums.publicKey.dsa: return [type_mpi, type_mpi, type_mpi, type_mpi]; // Algorithm-Specific Fields for ECDSA/EdDSA public keys: // - OID of curve; // - MPI of EC point representing public key. - case 'ecdsa': - case 'eddsa': + case enums.publicKey.ecdsa: + case enums.publicKey.eddsa: return [type_oid, type_mpi]; // Algorithm-Specific Fields for ECDH public keys: // - OID of curve; // - MPI of EC point representing public key. // - KDF: variable-length field containing KDF parameters. - case 'ecdh': + case enums.publicKey.ecdh: return [type_oid, type_mpi, type_kdf_params]; default: - throw new Error('Unknown algorithm.'); + throw new Error('Invalid public key encryption algorithm.'); } }, @@ -226,24 +232,24 @@ export default { switch (algo) { // Algorithm-Specific Fields for RSA encrypted session keys: // - MPI of RSA encrypted value m**e mod n. - case 'rsa_encrypt': - case 'rsa_encrypt_sign': + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: return [type_mpi]; // Algorithm-Specific Fields for Elgamal encrypted session keys: // - MPI of Elgamal value g**k mod p // - MPI of Elgamal value m * y**k mod p - case 'elgamal': + case enums.publicKey.elgamal: return [type_mpi, type_mpi]; // Algorithm-Specific Fields for ECDH encrypted session keys: // - MPI containing the ephemeral key used to establish the shared secret // - ECDH Symmetric Key - case 'ecdh': + case enums.publicKey.ecdh: return [type_mpi, type_ecdh_symkey]; 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 */ 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) { - case 'rsa_encrypt': - case 'rsa_encrypt_sign': - case 'rsa_sign': { + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_sign: { return publicKey.rsa.generate(bits, "10001").then(function(keyObject) { return constructParams( types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u] ); }); } - case 'ecdsa': - case 'eddsa': + case enums.publicKey.dsa: + 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 constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]); }); - case 'ecdh': + case enums.publicKey.ecdh: return publicKey.elliptic.generate(oid).then(function (keyObject) { return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]); }); 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 - * @param {module:enums.symmetric} algo Algorithm to use (see {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2}) - * @return {Uint8Array} Random bytes with length equal to the block + * Generates a random byte prefix for the specified algorithm + * See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms. + * @param {module:enums.symmetric} algo Symmetric encryption algorithm + * @return {Uint8Array} Random bytes with length equal to the block * size of the cipher */ getPrefixRandom: function(algo) { diff --git a/src/crypto/index.js b/src/crypto/index.js index a5ced4ed..ad201506 100644 --- a/src/crypto/index.js +++ b/src/crypto/index.js @@ -15,6 +15,7 @@ import pkcs5 from './pkcs5.js'; import crypto from './crypto.js'; import aes_kw from './aes_kw.js'; +// TODO move cfb and gcm to cipher const mod = { /** @see module:crypto/cipher */ cipher: cipher, diff --git a/src/crypto/pkcs5.js b/src/crypto/pkcs5.js index 57d070e6..088d2d89 100644 --- a/src/crypto/pkcs5.js +++ b/src/crypto/pkcs5.js @@ -48,7 +48,4 @@ function decode(msg) { throw new Error('Invalid padding'); } -export default { - encode, - decode -}; +export default { encode, decode }; diff --git a/src/crypto/public_key/dsa.js b/src/crypto/public_key/dsa.js index f432681d..b12f263e 100644 --- a/src/crypto/public_key/dsa.js +++ b/src/crypto/public_key/dsa.js @@ -63,7 +63,6 @@ export default { // of leftmost bits equal to the number of bits of q. This (possibly // truncated) hash function result is treated as a number and used // directly in the DSA signature algorithm. - // TODO rewrite getLeftNBits to work with Uint8Arrays const h = new BN( util.str2Uint8Array( util.getLeftNBits( diff --git a/src/crypto/signature.js b/src/crypto/signature.js index 6b7bc82a..3fd018f6 100644 --- a/src/crypto/signature.js +++ b/src/crypto/signature.js @@ -1,67 +1,60 @@ /** - * @requires asmcrypto.js * @requires crypto/public_key * @requires crypto/pkcs1 + * @requires enums * @requires util * @module crypto/signature */ -// FIXME wrap rsa.js around this import publicKey from './public_key'; import pkcs1 from './pkcs1'; +import enums from '../enums'; import util from '../util'; export default { /** - * - * @param {module:enums.publicKey} algo public Key algorithm - * @param {module:enums.hash} hash_algo Hash algorithm - * @param {Array} msg_MPIs Signature multiprecision integers - * @param {Array} publickey_MPIs Public key multiprecision integers - * @param {Uint8Array} data Data on where the signature was computed on. - * @return {Boolean} true if signature (sig_data was equal to data over hash) + * Verifies the signature provided for data using specified algorithms and public key parameters. + * 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} + * for public key and hash algorithms. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {module:enums.hash} hash_algo Hash algorithm + * @param {Array} msg_MPIs Algorithm-specific signature parameters + * @param {Array} 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) { - case 1: - // RSA (Encrypt or Sign) [HAC] - case 2: - // RSA Encrypt-Only [HAC] - case 3: { - // RSA Sign-Only [HAC] + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_sign: { const m = msg_MPIs[0].toUint8Array(); - const n = publickey_MPIs[0].toUint8Array(); - const e = publickey_MPIs[1].toUint8Array(); + const n = pub_MPIs[0].toUint8Array(); + const e = pub_MPIs[1].toUint8Array(); const EM = publicKey.rsa.verify(m, n, e); const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length); return util.hexidump(EM) === EM2; } - case 16: { - // 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] + case enums.publicKey.dsa: { const r = msg_MPIs[0].toBN(); const s = msg_MPIs[1].toBN(); - const p = publickey_MPIs[0].toBN(); - const q = publickey_MPIs[1].toBN(); - const g = publickey_MPIs[2].toBN(); - const y = publickey_MPIs[3].toBN(); + const p = pub_MPIs[0].toBN(); + const q = pub_MPIs[1].toBN(); + const g = pub_MPIs[2].toBN(); + const y = pub_MPIs[3].toBN(); return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y); } - case 19: { - // ECDSA - const oid = publickey_MPIs[0]; + case enums.publicKey.ecdsa: { + const oid = pub_MPIs[0]; 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); } - case 22: { - // EdDSA - const oid = publickey_MPIs[0]; + case enums.publicKey.eddsa: { + const oid = pub_MPIs[0]; 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); } default: @@ -70,25 +63,24 @@ export default { }, /** - * Create a signature on data using the specified algorithm - * @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}) - * @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}) - * @param {Array} keyIntegers Public followed by Private key multiprecision algorithm-specific parameters - * @param {Uint8Array} data Data to be signed - * @return {Array} + * Creates a signature on data using specified algorithms and private key parameters. + * 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} + * for public key and hash algorithms. + * @param {module:enums.publicKey} algo Public key algorithm + * @param {module:enums.hash} hash_algo Hash algorithm + * @param {Array} 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) { - case 1: - // RSA (Encrypt or Sign) [HAC] - case 2: - // RSA Encrypt-Only [HAC] - case 3: { - // RSA Sign-Only [HAC] - const n = keyIntegers[0].toUint8Array(); - const e = keyIntegers[1].toUint8Array(); - const d = keyIntegers[2].toUint8Array(); + case enums.publicKey.rsa_encrypt_sign: + case enums.publicKey.rsa_encrypt: + case enums.publicKey.rsa_sign: { + const n = key_params[0].toUint8Array(); + const e = key_params[1].toUint8Array(); + const d = key_params[2].toUint8Array(); data = util.Uint8Array2str(data); const m = util.hex2Uint8Array( '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); return util.Uint8Array2MPI(signature); } - case 17: { - // DSA (Digital Signature Algorithm) [FIPS186] [HAC] - const p = keyIntegers[0].toBN(); - const q = keyIntegers[1].toBN(); - const g = keyIntegers[2].toBN(); - const x = keyIntegers[4].toBN(); + case enums.publicKey.dsa: { + const p = key_params[0].toBN(); + const q = key_params[1].toBN(); + const g = key_params[2].toBN(); + const x = key_params[4].toBN(); const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x); return util.concatUint8Array([ util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) ]); } - case 16: { - // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + case enums.publicKey.elgamal: { throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.'); } - case 19: { - // ECDSA - const oid = keyIntegers[0]; - const d = keyIntegers[2].toUint8Array(); + case enums.publicKey.ecdsa: { + const oid = key_params[0]; + const d = key_params[2].toUint8Array(); const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d); return util.concatUint8Array([ util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)), util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array)) ]); } - case 22: { - // EdDSA - const oid = keyIntegers[0]; - const d = keyIntegers[2].toBN(); + case enums.publicKey.eddsa: { + const oid = key_params[0]; + const d = key_params[2].toBN(); const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d); return util.concatUint8Array([ util.Uint8Array2MPI(Uint8Array.from(signature.R)), diff --git a/src/enums.js b/src/enums.js index d384ad4e..ce331d7f 100644 --- a/src/enums.js +++ b/src/enums.js @@ -72,13 +72,22 @@ export default { * @readonly */ publicKey: { + /** RSA (Encrypt or Sign) [HAC] */ rsa_encrypt_sign: 1, + /** RSA (Encrypt only) [HAC] */ rsa_encrypt: 2, + /** RSA (Sign only) [HAC] */ rsa_sign: 3, + /** Elgamal (Encrypt only) [ELGAMAL] [HAC] */ elgamal: 16, + /** DSA (Sign only) [FIPS186] [HAC] */ dsa: 17, + /** ECDH (Encrypt only) [RFC6637] */ ecdh: 18, + /** ECDSA (Sign only) [RFC6637] */ ecdsa: 19, + /** EdDSA (Sign only) + * [{@link https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04|Draft RFC}] */ eddsa: 22 }, diff --git a/src/key.js b/src/key.js index 1f163f74..93e5f2d9 100644 --- a/src/key.js +++ b/src/key.js @@ -16,21 +16,21 @@ // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /** - * @requires config - * @requires crypto * @requires encoding/armor + * @requires crypto + * @requires packet + * @requires config * @requires enums * @requires util - * @requires packet * @module key */ -import config from './config'; -import crypto from './crypto'; import armor from './encoding/armor'; +import crypto from './crypto'; +import packet from './packet'; +import config from './config'; import enums from './enums'; import util from './util'; -import packet from './packet'; /** * @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 - * @param {type/keyid>} keyIds + * @param {type/keyid} keyId * @return {(module:packet/public_subkey|module:packet/public_key| * module:packet/secret_subkey|module:packet/secret_key|null)} */ diff --git a/src/openpgp.js b/src/openpgp.js index 5142b520..bbe9f658 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -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 * a password must be specified. * @param {Message} message a message object containing the encrypted session key packets - * @param {Key|Array} privateKeys (optional) private keys with decrypted secret key data * @param {String|Array} passwords (optional) passwords to decrypt the session key * @return {Promise} Array of decrypted session key, algorithm pairs in form: * { data:Uint8Array, algorithm:String } diff --git a/src/packet/public_key.js b/src/packet/public_key.js index 87d89720..a8dbf8a2 100644 --- a/src/packet/public_key.js +++ b/src/packet/public_key.js @@ -85,8 +85,8 @@ PublicKey.prototype.read = function (bytes) { // - A one-octet number denoting the public-key algorithm of this key. this.algorithm = enums.read(enums.publicKey, bytes[pos++]); - - const types = crypto.getPubKeyParamTypes(this.algorithm); + const algo = enums.write(enums.publicKey, this.algorithm); + const types = crypto.getPubKeyParamTypes(algo); this.params = crypto.constructParams(types); const b = bytes.subarray(pos, bytes.length); @@ -123,10 +123,10 @@ PublicKey.prototype.write = function () { if (this.version === 3) { arr.push(util.writeNumber(this.expirationTimeV3, 2)); } - arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)])); - - const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length; - + // Algorithm-specific params + const algo = enums.write(enums.publicKey, this.algorithm); + const paramCount = crypto.getPubKeyParamTypes(algo).length; + arr.push(new Uint8Array([algo])); for (let i = 0; i < paramCount; i++) { arr.push(this.params[i].write()); } @@ -180,7 +180,8 @@ PublicKey.prototype.getFingerprint = function () { toHash = this.writeOld(); this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash)); } 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++) { toHash += this.params[i].toString(); } @@ -192,7 +193,7 @@ PublicKey.prototype.getFingerprint = function () { /** * Returns algorithm information - * @return {Promise} An object of the form {algorithm: String, bits:int, curve:String} */ PublicKey.prototype.getAlgorithmInfo = function () { const result = {}; @@ -209,7 +210,8 @@ PublicKey.prototype.getAlgorithmInfo = function () { * Fix custom types after cloning */ 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++) { const param = this.params[i]; this.params[i] = types[i].fromClone(param); diff --git a/src/packet/public_key_encrypted_session_key.js b/src/packet/public_key_encrypted_session_key.js index 06f55a17..f4d5e90c 100644 --- a/src/packet/public_key_encrypted_session_key.js +++ b/src/packet/public_key_encrypted_session_key.js @@ -75,7 +75,8 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) { 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); 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)); 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)); } else { toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength())); } this.encrypted = await crypto.publicKeyEncrypt( - this.publicKeyAlgorithm, - key.params, - toEncrypt, - key.fingerprint - ); + algo, key.params, toEncrypt, key.fingerprint); }; /** @@ -129,21 +127,18 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) { * @return {String} The unencrypted session key */ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { - const result = (await crypto.publicKeyDecrypt( - this.publicKeyAlgorithm, - key.params, - this.encrypted, - key.fingerprint - )).toString(); + const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm); + const result = await crypto.publicKeyDecrypt( + algo, key.params, this.encrypted, key.fingerprint); let checksum; let decoded; - if (this.publicKeyAlgorithm === 'ecdh') { - decoded = crypto.pkcs5.decode(result); + if (algo === enums.publicKey.ecdh) { + decoded = crypto.pkcs5.decode(result.toString()); checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2))); } else { - decoded = crypto.pkcs1.eme.decode(result); - checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2))); + decoded = crypto.pkcs1.eme.decode(result.toString()); + checksum = util.readNumber(result.toUint8Array().slice(result.byteLength() - 2)); } key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2)); @@ -161,7 +156,8 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) { */ PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() { 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++) { this.encrypted[i] = types[i].fromClone(this.encrypted[i]); } diff --git a/src/packet/secret_key.js b/src/packet/secret_key.js index 1323d8eb..85ce93d4 100644 --- a/src/packet/secret_key.js +++ b/src/packet/secret_key.js @@ -84,7 +84,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { 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); let p = 0; @@ -100,7 +101,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) { function write_cleartext_params(hash_algorithm, algorithm, params) { 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++) { arr.push(params[i].write()); @@ -269,8 +271,8 @@ SecretKey.prototype.decrypt = function (passphrase) { SecretKey.prototype.generate = function (bits, curve) { const that = this; - - return crypto.generateParams(that.algorithm, bits, curve).then(function(params) { + const algo = enums.write(enums.publicKey, that.algorithm); + return crypto.generateParams(algo, bits, curve).then(function(params) { that.params = params; that.isDecrypted = true; }); @@ -283,7 +285,8 @@ SecretKey.prototype.clearPrivateParams = function () { if (!this.encrypted) { 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; }; @@ -291,7 +294,8 @@ SecretKey.prototype.clearPrivateParams = function () { * Fix custom types after cloning */ 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++) { const param = this.params[i]; this.params[i] = types[i].fromClone(param); diff --git a/src/util.js b/src/util.js index 8af4d619..c03a07e5 100644 --- a/src/util.js +++ b/src/util.js @@ -446,6 +446,7 @@ export default { } }, + // TODO rewrite getLeftNBits to work with Uint8Arrays getLeftNBits: function (string, bitcount) { const rest = bitcount % 8; if (rest === 0) { diff --git a/test/crypto/crypto.js b/test/crypto/crypto.js index 9979d5a9..36434815 100644 --- a/test/crypto/crypto.js +++ b/test/crypto/crypto.js @@ -381,11 +381,11 @@ describe('API functional testing', function() { const RSAUnencryptedData = new openpgp.MPI(); RSAUnencryptedData.fromBytes(crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())); return crypto.publicKeyEncrypt( - "rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData + 1, RSApubMPIs, RSAUnencryptedData ).then(RSAEncryptedData => { return crypto.publicKeyDecrypt( - "rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData + 1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData ).then(data => { data = data.write(); 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())); return crypto.publicKeyEncrypt( - "elgamal", ElgamalpubMPIs, ElgamalUnencryptedData + 16, ElgamalpubMPIs, ElgamalUnencryptedData ).then(ElgamalEncryptedData => { return crypto.publicKeyDecrypt( - "elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData + 16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData ).then(data => { data = data.write(); data = util.Uint8Array2str(data.subarray(2, data.length));