// GPG4Browsers - An OpenPGP implementation in javascript // Copyright (C) 2011 Recurity Labs GmbH // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // The GPG4Browsers crypto interface /** * Encrypts data using the specified public key multiprecision integers * and the specified algorithm. * @param {Integer} algo Algorithm to be used (See RFC4880 9.1) * @param {openpgp_type_mpi[]} publicMPIs Algorithm dependent multiprecision integers * @param {openpgp_type_mpi} data Data to be encrypted as MPI * @return {openpgp_type_mpi[]} if RSA an openpgp_type_mpi; * if elgamal encryption an array of two openpgp_type_mpi is returned; otherwise null */ function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { var result = (function() { switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); var n = publicMPIs[0].toBigInteger(); var e = publicMPIs[1].toBigInteger(); var m = data.toBigInteger(); return [rsa.encrypt(m,e,n)]; case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] var elgamal = new Elgamal(); var p = publicMPIs[0].toBigInteger(); var g = publicMPIs[1].toBigInteger(); var y = publicMPIs[2].toBigInteger(); var m = data.toBigInteger(); return elgamal.encrypt(m,g,p,y); default: return []; } })(); return result.map(function(bn) { var mpi = new openpgp_type_mpi(); mpi.fromBigInteger(bn); return mpi; }); } /** * 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 {Integer} algo Algorithm to be used (See RFC4880 9.1) * @param {openpgp_type_mpi[]} publicMPIs Algorithm dependent multiprecision integers * of the public key part of the private key * @param {openpgp_type_mpi[]} secretMPIs Algorithm dependent multiprecision integers * of the private key used * @param {openpgp_type_mpi} data Data to be encrypted as MPI * @return {openpgp_type_mpi} returns a big integer containing the decrypted data; otherwise null */ function openpgp_crypto_asymetricDecrypt(algo, keyIntegers, dataIntegers) { var bn = (function() { switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); // 0 and 1 are the public key. var d = keyIntegers[2].toBigInteger(); var p = keyIntegers[3].toBigInteger(); var q = keyIntegers[4].toBigInteger(); var u = keyIntegers[5].toBigInteger(); var m = dataIntegers[0].toBigInteger(); return rsa.decrypt(m, d, p, q, u); case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] var elgamal = new Elgamal(); var x = keyIntegers[3].toBigInteger(); var c1 = dataIntegers[0].toBigInteger(); var c2 = dataIntegers[1].toBigInteger(); var p = keyIntegers[0].toBigInteger(); return elgamal.decrypt(c1,c2,p,x); default: return null; } })(); var result = new openpgp_type_mpi(); result.fromBigInteger(bn); return result; } /** Returns the number of integers comprising the private key of an algorithm * @param {openpgp.publickey} algo The public key algorithm * @return {Integer} The number of integers. */ function openpgp_crypto_getPrivateMpiCount(algo) { if (algo > 0 && algo < 4) { // 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 4; } else if (algo == 16) { // Algorithm-Specific Fields for Elgamal secret keys: // - MPI of Elgamal secret exponent x. return 1; } else if (algo == 17) { // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. return 1; } else return 0; } function openpgp_crypto_getPublicMpiCount(algorithm) { // - A series of multiprecision integers comprising the key material: // Algorithm-Specific Fields for RSA public keys: // - a multiprecision integer (MPI) of RSA public modulus n; // - an MPI of RSA public encryption exponent e. if (algorithm > 0 && algorithm < 4) return 2; // 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). else if (algorithm == 16) return 3; // 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). else if (algorithm == 17) return 4; else return 0; }; /** * generate random byte prefix as string for the specified algorithm * @param {Integer} algo Algorithm to use (see RFC4880 9.2) * @return {String} Random bytes with length equal to the block * size of the cipher */ function openpgp_crypto_getPrefixRandom(algo) { switch(algo) { case 2: case 3: case 4: return openpgp_crypto_getRandomBytes(8); case 7: case 8: case 9: case 10: return openpgp_crypto_getRandomBytes(16); default: return null; } } /** * retrieve the MDC prefixed bytes by decrypting them * @param {Integer} algo Algorithm to use (see RFC4880 9.2) * @param {String} key Key as string. length is depending on the algorithm used * @param {String} data Encrypted data where the prefix is decrypted from * @return {String} Plain text data of the prefixed data */ function openpgp_crypto_MDCSystemBytes(algo, key, data) { util.print_debug_hexstr_dump("openpgp_crypto_symmetricDecrypt:\nencrypteddata:",data); switch(algo) { case 0: // Plaintext or unencrypted data return data; case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) return openpgp_cfb_mdc(desede, 8, key, data, openpgp_cfb); case 3: // CAST5 (128 bit key, as per [RFC2144]) return openpgp_cfb_mdc(cast5_encrypt, 8, key, data); case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] return openpgp_cfb_mdc(BFencrypt, 8, key, data); case 7: // AES with 128-bit key [AES] case 8: // AES with 192-bit key case 9: // AES with 256-bit key return openpgp_cfb_mdc(AESencrypt, 16, keyExpansion(key), data); case 10: return openpgp_cfb_mdc(TFencrypt, 16, key, data); case 1: // IDEA [IDEA] util.print_error(""+ (algo == 1 ? "IDEA Algorithm not implemented" : "Twofish Algorithm not implemented")); return null; default: } return null; } /** * Generating a session key for the specified symmetric algorithm * @param {Integer} algo Algorithm to use (see RFC4880 9.2) * @return {String} Random bytes as a string to be used as a key */ function openpgp_crypto_generateSessionKey(algo) { return openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(algo)); } /** * Get the key length by symmetric algorithm id. * @param {Integer} algo Algorithm to use (see RFC4880 9.2) * @return {String} Random bytes as a string to be used as a key */ function openpgp_crypto_getKeyLength(algo) { switch (algo) { case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) case 8: // AES with 192-bit key return 24; case 3: // CAST5 (128 bit key, as per [RFC2144]) case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] case 7: // AES with 128-bit key [AES] return 16; case 9: // AES with 256-bit key case 10:// Twofish with 256-bit key [TWOFISH] return 32; } return null; } /** * Returns the block length of the specified symmetric encryption algorithm * @param {openpgp.symmetric} algo Symmetric algorithm idenhifier * @return {Integer} The number of bytes in a single block encrypted by the algorithm */ function openpgp_crypto_getBlockLength(algo) { switch (algo) { case 1: // - IDEA [IDEA] case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) case 3: // - CAST5 (128 bit key, as per [RFC2144]) return 8; case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] case 7: // - AES with 128-bit key [AES] case 8: // - AES with 192-bit key case 9: // - AES with 256-bit key return 16; case 10: // - Twofish with 256-bit key [TWOFISH] return 32; default: return 0; } } /** * * @param {Integer} algo public Key algorithm * @param {Integer} hash_algo Hash algorithm * @param {openpgp_type_mpi[]} msg_MPIs Signature multiprecision integers * @param {openpgp_type_mpi[]} publickey_MPIs Public key multiprecision integers * @param {String} data Data on where the signature was computed on. * @return {Boolean} true if signature (sig_data was equal to data over hash) */ function openpgp_crypto_verifySignature(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { var calc_hash = openpgp_crypto_hashData(hash_algo, data); switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); var n = publickey_MPIs[0].toBigInteger(); var e = publickey_MPIs[1].toBigInteger(); var x = msg_MPIs[0].toBigInteger(); var dopublic = rsa.verify(x,e,n); var hash = openpgp_encoding_emsa_pkcs1_decode(hash_algo,dopublic.toMPI().substring(2)); if (hash == -1) { util.print_error("PKCS1 padding in message or key incorrect. Aborting..."); return false; } return hash == calc_hash; case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] util.print_error("signing with Elgamal is not defined in the OpenPGP standard."); return null; case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC] var dsa = new DSA(); var s1 = msg_MPIs[0].toBigInteger(); var s2 = msg_MPIs[1].toBigInteger(); var p = publickey_MPIs[0].toBigInteger(); var q = publickey_MPIs[1].toBigInteger(); var g = publickey_MPIs[2].toBigInteger(); var y = publickey_MPIs[3].toBigInteger(); var m = data; var dopublic = dsa.verify(hash_algo,s1,s2,m,p,q,g,y); return dopublic.compareTo(s1) == 0; default: return null; } } /** * Create a signature on data using the specified algorithm * @param {Integer} hash_algo hash Algorithm to use (See RFC4880 9.4) * @param {Integer} algo Asymmetric cipher algorithm to use (See RFC4880 9.1) * @param {openpgp_type_mpi[]} publicMPIs Public key multiprecision integers * of the private key * @param {openpgp_type_mpi[]} secretMPIs Private key multiprecision * integers which is used to sign the data * @param {String} data Data to be signed * @return {openpgp_type_mpi[]} */ function openpgp_crypto_signData(hash_algo, algo, keyIntegers, data) { switch(algo) { case 1: // RSA (Encrypt or Sign) [HAC] case 2: // RSA Encrypt-Only [HAC] case 3: // RSA Sign-Only [HAC] var rsa = new RSA(); var d = keyIntegers[2].toBigInteger(); var n = keyIntegers[0].toBigInteger(); var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, data, keyIntegers[0].byteLength()); util.print_debug("signing using RSA"); return rsa.sign(m, d, n).toMPI(); case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC] var dsa = new DSA(); util.print_debug("DSA Sign: q size in Bytes:"+keyIntegers[1].getByteLength()); var p = keyIntegers[0].toBigInteger(); var q = keyIntegers[1].toBigInteger(); var g = keyIntegers[2].toBigInteger(); var y = keyIntegers[3].toBigInteger(); var x = keyIntegers[4].toBigInteger(); var m = data; var result = dsa.sign(hash_algo,m, g, p, q, x); util.print_debug("signing using DSA\n result:"+util.hexstrdump(result[0])+"|"+util.hexstrdump(result[1])); return result[0].toString() + result[1].toString(); case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] util.print_debug("signing with Elgamal is not defined in the OpenPGP standard."); return null; default: return null; } } /** * Create a hash on the specified data using the specified algorithm * @param {Integer} algo Hash algorithm type (see RFC4880 9.4) * @param {String} data Data to be hashed * @return {String} hash value */ function openpgp_crypto_hashData(algo, data) { var hash = null; switch(algo) { case 1: // - MD5 [HAC] hash = MD5(data); break; case 2: // - SHA-1 [FIPS180] hash = str_sha1(data); break; case 3: // - RIPE-MD/160 [HAC] hash = RMDstring(data); break; case 8: // - SHA256 [FIPS180] hash = str_sha256(data); break; case 9: // - SHA384 [FIPS180] hash = str_sha384(data); break; case 10:// - SHA512 [FIPS180] hash = str_sha512(data); break; case 11:// - SHA224 [FIPS180] hash = str_sha224(data); default: break; } return hash; } /** * Returns the hash size in bytes of the specified hash algorithm type * @param {Integer} algo Hash algorithm type (See RFC4880 9.4) * @return {Integer} Size in bytes of the resulting hash */ function openpgp_crypto_getHashByteLength(algo) { var hash = null; switch(algo) { case 1: // - MD5 [HAC] return 16; case 2: // - SHA-1 [FIPS180] case 3: // - RIPE-MD/160 [HAC] return 20; case 8: // - SHA256 [FIPS180] return 32; case 9: // - SHA384 [FIPS180] return 48 case 10:// - SHA512 [FIPS180] return 64; case 11:// - SHA224 [FIPS180] return 28; } return null; } /** * Retrieve secure random byte string of the specified length * @param {Integer} length Length in bytes to generate * @return {String} Random byte string */ function openpgp_crypto_getRandomBytes(length) { var result = ''; for (var i = 0; i < length; i++) { result += String.fromCharCode(openpgp_crypto_getSecureRandomOctet()); } return result; } /** * Return a pseudo-random number in the specified range * @param {Integer} from Min of the random number * @param {Integer} to Max of the random number (max 32bit) * @return {Integer} A pseudo random number */ function openpgp_crypto_getPseudoRandom(from, to) { return Math.round(Math.random()*(to-from))+from; } /** * Return a secure random number in the specified range * @param {Integer} from Min of the random number * @param {Integer} to Max of the random number (max 32bit) * @return {Integer} A secure random number */ function openpgp_crypto_getSecureRandom(from, to) { var buf = new Uint32Array(1); window.crypto.getRandomValues(buf); var bits = ((to-from)).toString(2).length; while ((buf[0] & (Math.pow(2, bits) -1)) > (to-from)) window.crypto.getRandomValues(buf); return from+(Math.abs(buf[0] & (Math.pow(2, bits) -1))); } function openpgp_crypto_getSecureRandomOctet() { var buf = new Uint32Array(1); window.crypto.getRandomValues(buf); return buf[0] & 0xFF; } /** * Create a secure random big integer of bits length * @param {Integer} bits Bit length of the MPI to create * @return {BigInteger} Resulting big integer */ function openpgp_crypto_getRandomBigInteger(bits) { if (bits < 0) return null; var numBytes = Math.floor((bits+7)/8); var randomBits = openpgp_crypto_getRandomBytes(numBytes); if (bits % 8 > 0) { randomBits = String.fromCharCode( (Math.pow(2,bits % 8)-1) & randomBits.charCodeAt(0)) + randomBits.substring(1); } return new openpgp_type_mpi().create(randomBits).toBigInteger(); } function openpgp_crypto_getRandomBigIntegerInRange(min, max) { if (max.compareTo(min) <= 0) return; var range = max.subtract(min); var r = openpgp_crypto_getRandomBigInteger(range.bitLength()); while (r > range) { r = openpgp_crypto_getRandomBigInteger(range.bitLength()); } return min.add(r); } //This is a test method to ensure that encryption/decryption with a given 1024bit RSAKey object functions as intended function openpgp_crypto_testRSA(key){ debugger; var rsa = new RSA(); var mpi = new openpgp_type_mpi(); mpi.create(openpgp_encoding_eme_pkcs1_encode('ABABABAB', 128)); var msg = rsa.encrypt(mpi.toBigInteger(),key.ee,key.n); var result = rsa.decrypt(msg, key.d, key.p, key.q, key.u); } /** * @typedef {Object} openpgp_keypair * @property {openpgp_packet_keymaterial} privateKey * @property {openpgp_packet_keymaterial} publicKey */ /** * Calls the necessary crypto functions to generate a keypair. * Called directly by openpgp.js * @param {Integer} keyType Follows OpenPGP algorithm convention. * @param {Integer} numBits Number of bits to make the key to be generated * @return {openpgp_keypair} */ function openpgp_crypto_generateKeyPair(keyType, numBits, passphrase, s2kHash, symmetricEncryptionAlgorithm){ var privKeyPacket; var publicKeyPacket; var d = new Date(); d = d.getTime()/1000; var timePacket = String.fromCharCode(Math.floor(d/0x1000000%0x100)) + String.fromCharCode(Math.floor(d/0x10000%0x100)) + String.fromCharCode(Math.floor(d/0x100%0x100)) + String.fromCharCode(Math.floor(d%0x100)); switch(keyType){ case 1: var rsa = new RSA(); var key = rsa.generate(numBits,"10001"); privKeyPacket = new openpgp_packet_keymaterial().write_private_key(keyType, key, passphrase, s2kHash, symmetricEncryptionAlgorithm, timePacket); publicKeyPacket = new openpgp_packet_keymaterial().write_public_key(keyType, key, timePacket); break; default: util.print_error("Unknown keytype "+keyType) } return {privateKey: privKeyPacket, publicKey: publicKeyPacket}; }