From 6fe166fa87a7f5cc850085d2a5bf766e9ca705a4 Mon Sep 17 00:00:00 2001 From: Michal Kolodziej Date: Thu, 25 Apr 2013 17:28:43 +0200 Subject: [PATCH] Reading of unencrypted secret key packets seems to be working. --- resources/openpgp.js | 1662 +++++++++-------- resources/openpgp.min.js | 58 +- src/ciphers/openpgp.cfb.js | 1 - src/ciphers/openpgp.crypto.js | 46 + src/packet/packet.js | 2 +- src/packet/packetlist.js | 1 - src/packet/public_key.js | 213 +++ ...gp.packet.keymaterial.js => secret_key.js} | 661 ++----- src/packet/signature.js | 35 + .../sym_encrypted_integrity_protected.js | 7 +- test/general/packet.js | 63 +- test/index.html | 1 - 12 files changed, 1405 insertions(+), 1345 deletions(-) create mode 100644 src/packet/public_key.js rename src/packet/{openpgp.packet.keymaterial.js => secret_key.js} (50%) create mode 100644 src/packet/signature.js diff --git a/resources/openpgp.js b/resources/openpgp.js index 92261658..6ee0cf0a 100644 --- a/resources/openpgp.js +++ b/resources/openpgp.js @@ -3594,7 +3594,6 @@ function openpgp_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, text.push(String.fromCharCode(ablock[i]^iblock[i])); } } - } return text.join(''); @@ -3741,6 +3740,30 @@ function openpgp_crypto_asymetricDecrypt(algo, publicMPIs, secretMPIs, dataMPIs) 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; +} + /** * generate random byte prefix as string for the specified algorithm * @param {Integer} algo Algorithm to use (see RFC4880 9.2) @@ -3824,6 +3847,28 @@ function openpgp_crypto_getKeyLength(algo) { 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 @@ -7404,7 +7449,7 @@ function openpgp_config() { keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" }; - this.versionstring ="OpenPGP.js v.1.20130424"; + this.versionstring ="OpenPGP.js v.1.20130425"; this.commentstring ="http://openpgpjs.org"; /** * Reads the config out of the HTML5 local storage @@ -9858,825 +9903,6 @@ function openpgp_packet_encryptedsessionkey() { // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -/** - * @class - * @classdesc Implementation of the Key Material Packet (Tag 5,6,7,14) - * - * RFC4480 5.5: - * A key material packet contains all the information about a public or - * private key. There are four variants of this packet type, and two - * major versions. Consequently, this section is complex. - */ -function openpgp_packet_keymaterial() { - // members: - this.publicKeyAlgorithm = null; - this.tagType = null; - this.creationTime = null; - this.version = null; - this.expiration = null;// V3 - this.MPIs = null; - this.secMPIs = null; - this.publicKey = null; - this.symmetricEncryptionAlgorithm = null; - this.s2kUsageConventions = null; - this.IVLength = null; - this.encryptedMPIData = null; - this.hasUnencryptedSecretKeyData = null; - this.checksum = null; - this.parentNode = null; - this.subKeySignature = null; - this.subKeyRevocationSignature = null; - - // 5.5.1. Key Packet Variants - - // 5.5.1.3. Secret-Key Packet (Tag 5) - /** - * This function reads the payload of a secret key packet (Tag 5) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Intefer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag5(input, position, len) { - this.tagType = 5; - this.read_priv_key(input, position, len); - return this; - } - - // 5.5.1.1. Public-Key Packet (Tag 6) - /** - * This function reads the payload of a public key packet (Tag 6) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag6(input, position, len) { - // A Public-Key packet starts a series of packets that forms an OpenPGP - // key (sometimes called an OpenPGP certificate). - this.tagType = 6; - this.packetLength = len; - this.read_pub_key(input, position,len); - - return this; - } - - // 5.5.1.4. Secret-Subkey Packet (Tag 7) - /** - * This function reads the payload of a secret key sub packet (Tag 7) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag7(input, position, len) { - this.tagType = 7; - this.packetLength = len; - return this.read_priv_key(input, position, len); - } - - // 5.5.1.2. Public-Subkey Packet (Tag 14) - /** - * This function reads the payload of a public key sub packet (Tag 14) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag14(input, position, len) { - this.subKeySignature = null; - this.subKeyRevocationSignature = new Array(); - this.tagType = 14; - this.packetLength = len; - this.read_pub_key(input, position,len); - return this; - } - - /** - * Internal Parser for public keys as specified in RFC 4880 section - * 5.5.2 Public-Key Packet Formats - * called by read_tag<num> - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {Object} This object with attributes set by the parser - */ - function read_pub_key(input, position, len) { - var mypos = position; - // A one-octet version number (3 or 4). - this.version = input[mypos++].charCodeAt(); - if (this.version == 3) { - // A four-octet number denoting the time that the key was created. - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()))*1000); - - // - A two-octet number denoting the time in days that this key is - // valid. If this number is zero, then it does not expire. - this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt(); - - // - A one-octet number denoting the public-key algorithm of this key. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); - var mpicount = 0; - // - 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 (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) - mpicount = 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 (this.publicKeyAlgorithm == 16) - mpicount = 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 (this.publicKeyAlgorithm == 17) - mpicount = 4; - - this.MPIs = new Array(); - for (var i = 0; i < mpicount; i++) { - this.MPIs[i] = new openpgp_type_mpi(); - if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && - !this.packetLength < (mypos-position)) { - mypos += this.MPIs[i].packetLength; - } else { - util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos); - } - } - this.packetLength = mypos-position; - } else if (this.version == 4) { - // - A four-octet number denoting the time that the key was created. - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()))*1000); - - // - A one-octet number denoting the public-key algorithm of this key. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); - var mpicount = 0; - // - 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 (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) - mpicount = 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 (this.publicKeyAlgorithm == 16) - mpicount = 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 (this.publicKeyAlgorithm == 17) - mpicount = 4; - - this.MPIs = new Array(); - var i = 0; - for (var i = 0; i < mpicount; i++) { - this.MPIs[i] = new openpgp_type_mpi(); - if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && - !this.packetLength < (mypos-position)) { - mypos += this.MPIs[i].packetLength; - } else { - util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos); - } - } - this.packetLength = mypos-position; - } else { - return null; - } - this.data = input.substring(position, mypos); - this.packetdata = input.substring(position, mypos); - return this; - } - - // 5.5.3. Secret-Key Packet Formats - - /** - * Internal parser for private keys as specified in RFC 4880 section 5.5.3 - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {Object} This object with attributes set by the parser - */ - function read_priv_key(input,position, len) { - // - A Public-Key or Public-Subkey packet, as described above. - this.publicKey = new openpgp_packet_keymaterial(); - if (this.publicKey.read_pub_key(input,position, len) == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input[position].charCodeAt()+" "+position+" "+len+"\n Aborting here..."); - return null; - } - this.publicKey.header = openpgp_packet.write_old_packet_header(6,this.publicKey.packetLength); - // this.publicKey.header = String.fromCharCode(0x99) + String.fromCharCode(this.publicKey.packetLength >> 8 & 0xFF)+String.fromCharCode(this.publicKey.packetLength & 0xFF); - var mypos = position + this.publicKey.data.length; - this.packetLength = len; - - // - One octet indicating string-to-key usage conventions. Zero - // indicates that the secret-key data is not encrypted. 255 or 254 - // indicates that a string-to-key specifier is being given. Any - // other value is a symmetric-key encryption algorithm identifier. - this.s2kUsageConventions = input[mypos++].charCodeAt(); - - if (this.s2kUsageConventions == 0) - this.hasUnencryptedSecretKeyData = true; - - // - [Optional] If string-to-key usage octet was 255 or 254, a one- - // octet symmetric encryption algorithm. - if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { - this.symmetricEncryptionAlgorithm = input[mypos++].charCodeAt(); - } - - // - [Optional] If string-to-key usage octet was 255 or 254, a - // string-to-key specifier. The length of the string-to-key - // specifier is implied by its type, as described above. - if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { - this.s2k = new openpgp_type_s2k(); - this.s2k.read(input, mypos); - mypos +=this.s2k.s2kLength; - } - - // - [Optional] If secret data is encrypted (string-to-key usage octet - // not zero), an Initial Vector (IV) of the same length as the - // cipher's block size. - this.symkeylength = 0; - if (this.s2kUsageConventions != 0 && this.s2kUsageConventions != 255 && - this.s2kUsageConventions != 254) { - this.symmetricEncryptionAlgorithm = this.s2kUsageConventions; - } - if (this.s2kUsageConventions != 0 && this.s2k.type != 1001) { - this.hasIV = true; - switch (this.symmetricEncryptionAlgorithm) { - case 1: // - IDEA [IDEA] - util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encrytryption algorithim: IDEA is not implemented"); - return null; - case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) - case 3: // - CAST5 (128 bit key, as per [RFC2144]) - this.IVLength = 8; - break; - 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 - this.IVLength = 16; - break; - case 10: // - Twofish with 256-bit key [TWOFISH] - this.IVLength = 32; - break; - case 5: // - Reserved - case 6: // - Reserved - default: - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm); - return null; - } - mypos++; - this.IV = input.substring(mypos, mypos+this.IVLength); - mypos += this.IVLength; - } - // - Plain or encrypted multiprecision integers comprising the secret - // key data. These algorithm-specific fields are as described - // below. - - // s2k type 1001 corresponds to GPG specific extension without primary key secrets - // http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment - if (this.s2kUsageConventions != 0 && this.s2k.type == 1001) { - this.secMPIs = null; - this.encryptedMPIData = null; - } else if (!this.hasUnencryptedSecretKeyData) { - this.encryptedMPIData = input.substring(mypos, len); - mypos += this.encryptedMPIData.length; - } else { - if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 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. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[0].packetLength; - this.secMPIs[1] = new openpgp_type_mpi(); - this.secMPIs[1].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[1].packetLength; - this.secMPIs[2] = new openpgp_type_mpi(); - this.secMPIs[2].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[2].packetLength; - this.secMPIs[3] = new openpgp_type_mpi(); - this.secMPIs[3].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[3].packetLength; - } else if (this.publicKey.publicKeyAlgorithm == 16) { - // Algorithm-Specific Fields for Elgamal secret keys: - // - MPI of Elgamal secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[0].packetLength; - } else if (this.publicKey.publicKeyAlgorithm == 17) { - // Algorithm-Specific Fields for DSA secret keys: - // - MPI of DSA secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[0].packetLength; - } - // checksum because s2k usage convention is 0 - this.checksum = new Array(); - this.checksum[0] = input[mypos++].charCodeAt(); - this.checksum[1] = input[mypos++].charCodeAt(); - } - return this; - } - - - /** - * Decrypts the private key MPIs which are needed to use the key. - * openpgp_packet_keymaterial.hasUnencryptedSecretKeyData should be - * false otherwise - * a call to this function is not needed - * - * @param {String} str_passphrase The passphrase for this private key - * as string - * @return {Boolean} True if the passphrase was correct; false if not - */ - function decryptSecretMPIs(str_passphrase) { - if (this.hasUnencryptedSecretKeyData) - return this.secMPIs; - // creating a key out of the passphrase - var key = this.s2k.produce_key(str_passphrase); - var cleartextMPIs = ""; - switch (this.symmetricEncryptionAlgorithm) { - case 1: // - IDEA [IDEA] - util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encryption algorithim: IDEA is not implemented"); - return false; - case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) - cleartextMPIs = normal_cfb_decrypt(function(block, key) { - return des(key, block,1,null,0); - }, this.IVLength, key, this.encryptedMPIData, this.IV); - break; - case 3: // - CAST5 (128 bit key, as per [RFC2144]) - cleartextMPIs = normal_cfb_decrypt(function(block, key) { - var cast5 = new openpgp_symenc_cast5(); - cast5.setKey(key); - return cast5.encrypt(util.str2bin(block)); - }, this.IVLength, util.str2bin(key.substring(0,16)), this.encryptedMPIData, this.IV); - break; - case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] - cleartextMPIs = normal_cfb_decrypt(function(block, key) { - var blowfish = new Blowfish(key); - return blowfish.encrypt(block); - }, this.IVLength, key, this.encryptedMPIData, this.IV); - break; - case 7: // - AES with 128-bit key [AES] - case 8: // - AES with 192-bit key - case 9: // - AES with 256-bit key - var numBytes = 16; - //This is a weird way to achieve this. If's within a switch is probably not ideal. - if(this.symmetricEncryptionAlgorithm == 8){ - numBytes = 24; - key = this.s2k.produce_key(str_passphrase,numBytes); - } - if(this.symmetricEncryptionAlgorithm == 9){ - numBytes = 32; - key = this.s2k.produce_key(str_passphrase,numBytes); - } - cleartextMPIs = normal_cfb_decrypt(function(block,key){ - return AESencrypt(util.str2bin(block),key); - }, - this.IVLength, keyExpansion(key.substring(0,numBytes)), this.encryptedMPIData, this.IV); - break; - case 10: // - Twofish with 256-bit key [TWOFISH] - util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented"); - return false; - case 5: // - Reserved - case 6: // - Reserved - default: - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm); - return false; - } - - if (cleartextMPIs == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+"cleartextMPIs was null"); - return false; - } - - var cleartextMPIslength = cleartextMPIs.length; - - if (this.s2kUsageConventions == 254 && - str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) == - cleartextMPIs.substring(cleartextMPIs.length - 20)) { - cleartextMPIslength -= 20; - } else if (this.s2kUsageConventions != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) == - (cleartextMPIs.charCodeAt(cleartextMPIs.length -2) << 8 | cleartextMPIs.charCodeAt(cleartextMPIs.length -1))) { - cleartextMPIslength -= 2; - } else { - return false; - } - - if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 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. - var mypos = 0; - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength); - mypos += this.secMPIs[0].packetLength; - this.secMPIs[1] = new openpgp_type_mpi(); - this.secMPIs[1].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); - mypos += this.secMPIs[1].packetLength; - this.secMPIs[2] = new openpgp_type_mpi(); - this.secMPIs[2].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); - mypos += this.secMPIs[2].packetLength; - this.secMPIs[3] = new openpgp_type_mpi(); - this.secMPIs[3].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); - mypos += this.secMPIs[3].packetLength; - } else if (this.publicKey.publicKeyAlgorithm == 16) { - // Algorithm-Specific Fields for Elgamal secret keys: - // - MPI of Elgamal secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIs); - } else if (this.publicKey.publicKeyAlgorithm == 17) { - // Algorithm-Specific Fields for DSA secret keys: - // - MPI of DSA secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength); - } - return true; - } - - /** - * Generates Debug output - * @return String which gives some information about the keymaterial - */ - function toString() { - var result = ""; - switch (this.tagType) { - case 6: - result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+ - ' length: '+this.packetLength+'\n'+ - ' version: '+this.version+'\n'+ - ' creation time: '+this.creationTime+'\n'+ - ' expiration time: '+this.expiration+'\n'+ - ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; - break; - case 14: - result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+ - ' length: '+this.packetLength+'\n'+ - ' version: '+this.version+'\n'+ - ' creation time: '+this.creationTime+'\n'+ - ' expiration time: '+this.expiration+'\n'+ - ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; - break; - case 5: - result +='5.5.1.3. Secret-Key Packet (Tag 5)\n'+ - ' length: '+this.packetLength+'\n'+ - ' version: '+this.publicKey.version+'\n'+ - ' creation time: '+this.publicKey.creationTime+'\n'+ - ' expiration time: '+this.publicKey.expiration+'\n'+ - ' publicKeyAlgorithm: '+this.publicKey.publicKeyAlgorithm+'\n'; - break; - case 7: - result += '5.5.1.4. Secret-Subkey Packet (Tag 7)\n'+ - ' length: '+this.packetLength+'\n'+ - ' version[1]: '+(this.version == 4)+'\n'+ - ' creationtime[4]: '+this.creationTime+'\n'+ - ' expiration[2]: '+this.expiration+'\n'+ - ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; - break; - default: - result += 'unknown key material packet\n'; - } - if (this.MPIs != null) { - result += "Public Key MPIs:\n"; - for (var i = 0; i < this.MPIs.length; i++) { - result += this.MPIs[i].toString(); - } - } - if (this.publicKey != null && this.publicKey.MPIs != null) { - result += "Public Key MPIs:\n"; - for (var i = 0; i < this.publicKey.MPIs.length; i++) { - result += this.publicKey.MPIs[i].toString(); - } - } - if (this.secMPIs != null) { - result += "Secret Key MPIs:\n"; - for (var i = 0; i < this.secMPIs.length; i++) { - result += this.secMPIs[i].toString(); - } - } - - if (this.subKeySignature != null) - result += "subKey Signature:\n"+this.subKeySignature.toString(); - - if (this.subKeyRevocationSignature != null ) - result += "subKey Revocation Signature:\n"+this.subKeyRevocationSignature.toString(); - return result; - } - - /** - * Continue parsing packets belonging to the key material such as signatures - * @param {Object} parent_node The parent object - * @param {String} input Input string to read the packet(s) from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet(s) or remaining length of input - * @return {Integer} Length of nodes read - */ - function read_nodes(parent_node, input, position, len) { - this.parentNode = parent_node; - if (this.tagType == 14) { // public sub-key packet - var pos = position; - var result = null; - while (input.length != pos) { - var l = input.length - pos; - result = openpgp_packet.read_packet(input, pos, l); - if (result == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_pub]parsing ends here @:' + pos + " l:" + l); - break; - } else { - - switch (result.tagType) { - case 2: // Signature Packet certification signature - if (result.signatureType == 24) { // subkey binding signature - this.subKeySignature = result; - pos += result.packetLength + result.headerLength; - break; - } else if (result.signatureType == 40) { // subkey revocation signature - this.subKeyRevocationSignature[this.subKeyRevocationSignature.length] = result; - pos += result.packetLength + result.headerLength; - break; - } else { - util.print_error("openpgp.packet.keymaterial.js\nunknown signature:"+result.toString()); - } - - default: - this.data = input; - this.position = position - this.parentNode.packetLength; - this.len = pos - position; - return this.len; - break; - } - } - } - this.data = input; - this.position = position - this.parentNode.packetLength; - this.len = pos - position; - return this.len; - } else if (this.tagType == 7) { // private sub-key packet - var pos = position; - while (input.length != pos) { - var result = openpgp_packet.read_packet(input, pos, len - (pos - position)); - if (result == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_priv] parsing ends here @:' + pos); - break; - } else { - switch (result.tagType) { - case 2: // Signature Packet certification signature - if (result.signatureType == 24) // subkey embedded signature - this.subKeySignature = result; - else if (result.signatureType == 40) // subkey revocation signature - this.subKeyRevocationSignature[this.subKeyRevocationSignature.length] = result; - pos += result.packetLength + result.headerLength; - break; - default: - this.data = input; - this.position = position - this.parentNode.packetLength; - this.len = pos - position; - return this.len; - } - } - } - this.data = input; - this.position = position - this.parentNode.packetLength; - this.len = pos - position; - return this.len; - } else { - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown parent node for a key material packet "+parent_node.tagType); - } - } - - /** - * Checks the validity for usage of this (sub)key - * @return {Integer} 0 = bad key, 1 = expired, 2 = revoked, 3 = valid - */ - function verifyKey() { - if (this.tagType == 14) { - if (this.subKeySignature == null) { - return 0; - } - if (this.subKeySignature.version == 4 && - this.subKeySignature.keyNeverExpires != null && - !this.subKeySignature.keyNeverExpires && - new Date((this.subKeySignature.keyExpirationTime*1000)+ this.creationTime.getTime()) < new Date()) { - return 1; - } - var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+ - String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata; - if (!this.subKeySignature.verify(hashdata,this.parentNode)) { - return 0; - } - for (var i = 0; i < this.subKeyRevocationSignature.length; i++) { - if (this.getKeyId() == this.subKeyRevocationSignature[i].keyId){ - return 2; - } - } - } - return 3; - } - - /** - * Calculates the key id of they key - * @return {String} A 8 byte key id - */ - function getKeyId() { - if (this.version == 4) { - var f = this.getFingerprint(); - return f.substring(12,20); - } else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) { - var key_id = this.MPIs[0].substring((this.MPIs[0].mpiByteLength-8)); - util.print_debug("openpgp.msg.publickey read_nodes:\n"+"V3 key ID: "+key_id); - return key_id; - } - } - - /** - * Calculates the fingerprint of the key - * @return {String} A string containing the fingerprint - */ - function getFingerprint() { - if (this.version == 4) { - tohash = String.fromCharCode(0x99)+ String.fromCharCode(((this.packetdata.length) >> 8) & 0xFF) - + String.fromCharCode((this.packetdata.length) & 0xFF)+this.packetdata; - util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm); - return str_sha1(tohash, tohash.length); - } else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) { - return MD5(this.MPIs[0].MPI); - } - } - - /* - * Creates an OpenPGP key packet for the given key. much - * TODO in regards to s2k, subkeys. - * @param {Integer} keyType Follows the OpenPGP algorithm standard, - * IE 1 corresponds to RSA. - * @param {RSA.keyObject} key - * @param password - * @param s2kHash - * @param symmetricEncryptionAlgorithm - * @param timePacket - * @return {Object} {body: [string]OpenPGP packet body contents, - header: [string] OpenPGP packet header, string: [string] header+body} - */ - function write_private_key(keyType, key, password, s2kHash, symmetricEncryptionAlgorithm, timePacket){ - this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm; - var tag = 5; - var body = String.fromCharCode(4); - body += timePacket; - switch(keyType){ - case 1: - body += String.fromCharCode(keyType);//public key algo - body += key.n.toMPI(); - body += key.ee.toMPI(); - var algorithmStart = body.length; - //below shows ske/s2k - if(password){ - body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1 - //if s2k == 255,254 then 1 octet symmetric encryption algo - body += String.fromCharCode(this.symmetricEncryptionAlgorithm); - //if s2k == 255,254 then s2k specifier - body += String.fromCharCode(3); //s2k salt+iter - body += String.fromCharCode(s2kHash); - //8 octet salt value - //1 octet count - var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); - var sha1Hash = str_sha1(cleartextMPIs); - util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash); - var salt = openpgp_crypto_getRandomBytes(8); - util.print_debug_hexstr_dump('write_private_key Salt: ',salt); - body += salt; - var c = 96; //c of 96 translates to count of 65536 - body += String.fromCharCode(c); - util.print_debug('write_private_key c: '+ c); - var s2k = new openpgp_type_s2k(); - var hashKey = s2k.write(3, s2kHash, password, salt, c); - //if s2k, IV of same length as cipher's block - switch(this.symmetricEncryptionAlgorithm){ - case 3: - this.IVLength = 8; - this.IV = openpgp_crypto_getRandomBytes(this.IVLength); - ciphertextMPIs = normal_cfb_encrypt(function(block, key) { - var cast5 = new openpgp_symenc_cast5(); - cast5.setKey(key); - return cast5.encrypt(util.str2bin(block)); - }, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV); - body += this.IV + ciphertextMPIs; - break; - case 7: - case 8: - case 9: - this.IVLength = 16; - this.IV = openpgp_crypto_getRandomBytes(this.IVLength); - ciphertextMPIs = normal_cfb_encrypt(AESencrypt, - this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV); - body += this.IV + ciphertextMPIs; - break; - } - } - else{ - body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k - body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); - var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI()); - body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 - util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum); - } - break; - default : - body = ""; - util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); - } - var header = openpgp_packet.write_packet_header(tag,body.length); - return {string: header+body , header: header, body: body}; - } - - /* - * Same as write_private_key, but has less information because of - * public key. - * @param {Integer} keyType Follows the OpenPGP algorithm standard, - * IE 1 corresponds to RSA. - * @param {RSA.keyObject} key - * @param timePacket - * @return {Object} {body: [string]OpenPGP packet body contents, - * header: [string] OpenPGP packet header, string: [string] header+body} - */ - function write_public_key(keyType, key, timePacket){ - var tag = 6; - var body = String.fromCharCode(4); - body += timePacket; - switch(keyType){ - case 1: - body += String.fromCharCode(1);//public key algo - body += key.n.toMPI(); - body += key.ee.toMPI(); - break; - default: - util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); - } - var header = openpgp_packet.write_packet_header(tag,body.length); - return {string: header+body , header: header, body: body}; - } - - - this.read_tag5 = read_tag5; - this.read_tag6 = read_tag6; - this.read_tag7 = read_tag7; - this.read_tag14 = read_tag14; - this.toString = toString; - this.read_pub_key = read_pub_key; - this.read_priv_key = read_priv_key; - this.decryptSecretMPIs = decryptSecretMPIs; - this.read_nodes = read_nodes; - this.verifyKey = verifyKey; - this.getKeyId = getKeyId; - this.getFingerprint = getFingerprint; - this.write_private_key = write_private_key; - this.write_public_key = write_public_key; -} -// 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 - /** * @class * @classdesc Implementation of the strange "Marker packet" (Tag 10) @@ -12361,7 +11587,7 @@ function _openpgp_packet() { return { packet: result, - offset: mypos + packet_length + offset: mypos + real_packet_length }; } @@ -12426,7 +11652,6 @@ function openpgp_packetlist() { * @param {openpgp_bytearray} An array of bytes. */ this.read = function(bytes) { - this.packets = []; var i = 0; while(i < bytes.length) { @@ -12680,6 +11905,790 @@ function openpgp_packet_public_key_encrypted_session_key() { // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +/** + * @class + * @classdesc Implementation of the Key Material Packet (Tag 5,6,7,14) + * + * RFC4480 5.5: + * A key material packet contains all the information about a public or + * private key. There are four variants of this packet type, and two + * major versions. Consequently, this section is complex. + */ +function openpgp_packet_public_key() { + // members: + this.tag = 6; + this.version = 4; + this.expiration = null; + this.created = null; + this.mpi = []; + this.algorithm = openpgp.publickey.rsa_sign; + + + var public_mpis = function(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; + }; + + + /** + * Internal Parser for public keys as specified in RFC 4880 section + * 5.5.2 Public-Key Packet Formats + * called by read_tag<num> + * @param {String} input Input string to read the packet from + * @param {Integer} position Start position for the parser + * @param {Integer} len Length of the packet or remaining length of input + * @return {Object} This object with attributes set by the parser + */ + this.read = function(bytes) { + // A one-octet version number (3 or 4). + this.version = bytes[0].charCodeAt(); + + if (this.version == 3) { + /* + // A four-octet number denoting the time that the key was created. + this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | + (input[mypos++].charCodeAt() << 16) | + (input[mypos++].charCodeAt() << 8) | + (input[mypos++].charCodeAt()))*1000); + + // - A two-octet number denoting the time in days that this key is + // valid. If this number is zero, then it does not expire. + this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt(); + + // - A one-octet number denoting the public-key algorithm of this key. + this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + var mpicount = 0; + // - 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 (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) + mpicount = 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 (this.publicKeyAlgorithm == 16) + mpicount = 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 (this.publicKeyAlgorithm == 17) + mpicount = 4; + + this.MPIs = new Array(); + for (var i = 0; i < mpicount; i++) { + this.MPIs[i] = new openpgp_type_mpi(); + if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && + !this.packetLength < (mypos-position)) { + mypos += this.MPIs[i].packetLength; + } else { + util.print_error("openpgp.packet.keymaterial.js\n"+ + 'error reading MPI @:'+mypos); + } + } + this.packetLength = mypos-position; + */ + } else if (this.version == 4) { + // - A four-octet number denoting the time that the key was created. + var timeb = bytes.substr(1, 4); + + this.created= new Date(( + (timeb[0].charCodeAt() << 24) | + (timeb[1].charCodeAt() << 16) | + (timeb[2].charCodeAt() << 8) | + (timeb[3].charCodeAt())) * 1000); + + // - A one-octet number denoting the public-key algorithm of this key. + this.algorithm = bytes[5].charCodeAt(); + + var mpicount = public_mpis(this.algorithm); + this.mpi = []; + + var bmpi = bytes.substr(6); + var p = 0; + + for (var i = 0; + i < mpicount && p < bmpi.length; + i++) { + + this.mpi[i] = new openpgp_type_mpi(); + + p += this.mpi[i].read(bmpi.substr(p)) + + if(p > bmpi.length) + util.print_error("openpgp.packet.keymaterial.js\n" + +'error reading MPI @:'+p); + } + + return p + 6; + } else { + util.print_error('Unknown packet version'); + } + } + + /* + * Same as write_private_key, but has less information because of + * public key. + * @param {Integer} keyType Follows the OpenPGP algorithm standard, + * IE 1 corresponds to RSA. + * @param {RSA.keyObject} key + * @param timePacket + * @return {Object} {body: [string]OpenPGP packet body contents, + * header: [string] OpenPGP packet header, string: [string] header+body} + */ + this.write = function() { + var result = String.fromCharCode(4); + result += '0000'; + result += String.fromCharCode(this.algorithm); + + for(var i in this.mpi) { + result += this.mpi[i].write(); + } + + return result; + } + + + /** + * Generates Debug output + * @return String which gives some information about the keymaterial + */ + this.toString = function() { + var result = ""; + switch (this.tag) { + case 6: + result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + case 14: + result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + } + } + + +} +// 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 + +/** + * @class + * @classdesc Implementation of the Key Material Packet (Tag 5,6,7,14) + * + * RFC4480 5.5: + * A key material packet contains all the information about a public or + * private key. There are four variants of this packet type, and two + * major versions. Consequently, this section is complex. + */ +function openpgp_packet_secret_key() { + this.tag = 5; + this.public_key = new openpgp_packet_public_key(); + this.mpi = []; + this.symmetric_algorithm = openpgp.symmetric.plaintext; + this.s2k = null; + this.checksum_algorithm = openpgp.hash.sha1; + this.encrypted = null; + this.iv = null; + + + // 5.5.3. Secret-Key Packet Formats + + /** + * Internal parser for private keys as specified in RFC 4880 section 5.5.3 + * @param {String} bytes Input string to read the packet from + * @param {Integer} position Start position for the parser + * @param {Integer} len Length of the packet or remaining length of bytes + * @return {Object} This object with attributes set by the parser + */ + this.read = function(bytes) { + // - A Public-Key or Public-Subkey packet, as described above. + var len = this.public_key.read(bytes); + + bytes = bytes.substr(len); + + + // - One octet indicating string-to-key usage conventions. Zero + // indicates that the secret-key data is not encrypted. 255 or 254 + // indicates that a string-to-key specifier is being given. Any + // other value is a symmetric-key encryption algorithm identifier. + var s2k_usage = bytes[0].charCodeAt(); + + var i = 1; + + // - [Optional] If string-to-key usage octet was 255 or 254, a one- + // octet symmetric encryption algorithm. + if (s2k_usage == 255 || s2k_usage == 254) { + this.symmetric_algorithm = bytes[i++].charCodeAt(); + + // - [Optional] If string-to-key usage octet was 255 or 254, a + // string-to-key specifier. The length of the string-to-key + // specifier is implied by its type, as described above. + this.s2k = new openpgp_type_s2k(); + this.s2k.read(bytes, i); + i += this.s2k.s2kLength; + } + + // - [Optional] If secret data is encrypted (string-to-key usage octet + // not zero), an Initial Vector (IV) of the same length as the + // cipher's block size. + + if (s2k_usage != 0 && s2k_usage != 255 && + s2k_usage != 254) { + this.symmetric_algorithm = s2k_usage; + } + + if (s2k_usage != 0 && this.s2k.type != 1001) { + this.iv = bytes.substr(i, + openpgp_crypto_getBlockLength(this.symmetric_algorithm)); + + i += this.iv.length; + } + + // - Plain or encrypted multiprecision integers comprising the secret + // key data. These algorithm-specific fields are as described + // below. + + // s2k type 1001 corresponds to GPG specific extension without primary key secrets + // http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment + if (s2k_usage != 0 && this.s2k.type == 1001) { + this.mpi = null; + this.encrypted = null; + + } else if (s2k_usage != 0) { + this.encrypted = bytes.substr(i); + + } else { + var mpis = openpgp_crypto_getPrivateMpiCount(this.public_key.algorithm); + this.mpi = []; + + for(var j = 0; j < 4; j++) { + this.mpi[j] = new openpgp_type_mpi(); + i += this.mpi[j].read(bytes.substr(i)); + } + + // checksum because s2k usage convention is 0 + this.checksum = []; + this.checksum[0] = bytes[i++].charCodeAt(); + this.checksum[1] = bytes[i++].charCodeAt(); + } + } + + /* + * Creates an OpenPGP key packet for the given key. much + * TODO in regards to s2k, subkeys. + * @param {Integer} keyType Follows the OpenPGP algorithm standard, + * IE 1 corresponds to RSA. + * @param {RSA.keyObject} key + * @param password + * @param s2kHash + * @param symmetricEncryptionAlgorithm + * @param timePacket + * @return {Object} {body: [string]OpenPGP packet body contents, + header: [string] OpenPGP packet header, string: [string] header+body} + */ + this.write = function() { + + var body = String.fromCharCode(4); + body += timePacket; + switch(keyType){ + case 1: + body += String.fromCharCode(keyType);//public key algo + body += key.n.toMPI(); + body += key.ee.toMPI(); + var algorithmStart = body.length; + //below shows ske/s2k + if(password){ + body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1 + //if s2k == 255,254 then 1 octet symmetric encryption algo + body += String.fromCharCode(this.symmetric_algorithm); + //if s2k == 255,254 then s2k specifier + body += String.fromCharCode(3); //s2k salt+iter + body += String.fromCharCode(s2kHash); + //8 octet salt value + //1 octet count + var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var sha1Hash = str_sha1(cleartextMPIs); + util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash); + var salt = openpgp_crypto_getRandomBytes(8); + util.print_debug_hexstr_dump('write_private_key Salt: ',salt); + body += salt; + var c = 96; //c of 96 translates to count of 65536 + body += String.fromCharCode(c); + util.print_debug('write_private_key c: '+ c); + var s2k = new openpgp_type_s2k(); + var hashKey = s2k.write(3, s2kHash, password, salt, c); + //if s2k, IV of same length as cipher's block + switch(this.symmetric_algorithm){ + case 3: + this.IVLength = 8; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + case 7: + case 8: + case 9: + this.IVLength = 16; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(AESencrypt, + this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + } + } + else{ + body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k + body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI()); + body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 + util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum); + } + break; + default : + body = ""; + util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); + } + var header = openpgp_packet.write_packet_header(tag,body.length); + return {string: header+body , header: header, body: body}; + } + + /** + * Decrypts the private key MPIs which are needed to use the key. + * openpgp_packet_keymaterial.hasUnencryptedSecretKeyData should be + * false otherwise + * a call to this function is not needed + * + * @param {String} str_passphrase The passphrase for this private key + * as string + * @return {Boolean} True if the passphrase was correct; false if not + */ + this.decrypt = function(passphrase) { + if (this.encrypted == null) + return; + + // creating a key out of the passphrase + var key = this.s2k.produce_key(passphrase); + + var cleartextMPIs = ""; + + switch (this.symmetric_algorithm) { + case 1: // - IDEA [IDEA] + util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encryption algorithim: IDEA is not implemented"); + return false; + case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + cleartextMPIs = normal_cfb_decrypt(function(block, key) { + return des(key, block,1,null,0); + }, this.IVLength, key, this.encrypted, this.IV); + break; + case 3: // - CAST5 (128 bit key, as per [RFC2144]) + cleartextMPIs = normal_cfb_decrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, this.IVLength, util.str2bin(key.substring(0,16)), this.encrypted, this.IV); + break; + case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] + cleartextMPIs = normal_cfb_decrypt(function(block, key) { + var blowfish = new Blowfish(key); + return blowfish.encrypt(block); + }, this.IVLength, key, this.encrypted, this.IV); + break; + case 7: // - AES with 128-bit key [AES] + case 8: // - AES with 192-bit key + case 9: // - AES with 256-bit key + var numBytes = 16; + //This is a weird way to achieve this. If's within a switch is probably not ideal. + if(this.symmetric_algorithm == 8){ + numBytes = 24; + key = this.s2k.produce_key(str_passphrase,numBytes); + } + if(this.symmetric_algorithm == 9){ + numBytes = 32; + key = this.s2k.produce_key(str_passphrase,numBytes); + } + cleartextMPIs = normal_cfb_decrypt(function(block,key){ + return AESencrypt(util.str2bin(block),key); + }, + this.IVLength, keyExpansion(key.substring(0,numBytes)), this.encrypted, this.IV); + break; + case 10: // - Twofish with 256-bit key [TWOFISH] + util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented"); + return false; + case 5: // - Reserved + case 6: // - Reserved + default: + util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetric_algorithm); + return false; + } + + if (cleartextMPIs == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+"cleartextMPIs was null"); + return false; + } + + var cleartextMPIslength = cleartextMPIs.length; + + if (s2k_usage == 254 && + str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) == + cleartextMPIs.substring(cleartextMPIs.length - 20)) { + cleartextMPIslength -= 20; + } else if (s2k_usage != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) == + (cleartextMPIs.charCodeAt(cleartextMPIs.length -2) << 8 | cleartextMPIs.charCodeAt(cleartextMPIs.length -1))) { + cleartextMPIslength -= 2; + } else { + return false; + } + + if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 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. + var i = 0; + this.mpi = new Array(); + this.mpi[0] = new openpgp_type_mpi(); + this.mpi[0].read(cleartextMPIs, 0, cleartextMPIslength); + i += this.mpi[0].packetLength; + this.mpi[1] = new openpgp_type_mpi(); + this.mpi[1].read(cleartextMPIs, i, cleartextMPIslength-i); + i += this.mpi[1].packetLength; + this.mpi[2] = new openpgp_type_mpi(); + this.mpi[2].read(cleartextMPIs, i, cleartextMPIslength-i); + i += this.mpi[2].packetLength; + this.mpi[3] = new openpgp_type_mpi(); + this.mpi[3].read(cleartextMPIs, i, cleartextMPIslength-i); + i += this.mpi[3].packetLength; + } else if (this.publicKey.publicKeyAlgorithm == 16) { + // Algorithm-Specific Fields for Elgamal secret keys: + // - MPI of Elgamal secret exponent x. + this.mpi = new Array(); + this.mpi[0] = new openpgp_type_mpi(); + this.mpi[0].read(cleartextMPIs, 0, cleartextMPIs); + } else if (this.publicKey.publicKeyAlgorithm == 17) { + // Algorithm-Specific Fields for DSA secret keys: + // - MPI of DSA secret exponent x. + this.mpi = new Array(); + this.mpi[0] = new openpgp_type_mpi(); + this.mpi[0].read(cleartextMPIs, 0, cleartextMPIslength); + } + return true; + } + + /** + * Generates Debug output + * @return String which gives some information about the keymaterial + */ + function toString() { + var result = ""; + switch (this.tagType) { + case 6: + result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + case 14: + result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + case 5: + result +='5.5.1.3. Secret-Key Packet (Tag 5)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.publicKey.version+'\n'+ + ' creation time: '+this.publicKey.creationTime+'\n'+ + ' expiration time: '+this.publicKey.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKey.publicKeyAlgorithm+'\n'; + break; + case 7: + result += '5.5.1.4. Secret-Subkey Packet (Tag 7)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version[1]: '+(this.version == 4)+'\n'+ + ' creationtime[4]: '+this.creationTime+'\n'+ + ' expiration[2]: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + default: + result += 'unknown key material packet\n'; + } + if (this.MPIs != null) { + result += "Public Key MPIs:\n"; + for (var i = 0; i < this.MPIs.length; i++) { + result += this.MPIs[i].toString(); + } + } + if (this.publicKey != null && this.publicKey.MPIs != null) { + result += "Public Key MPIs:\n"; + for (var i = 0; i < this.publicKey.MPIs.length; i++) { + result += this.publicKey.MPIs[i].toString(); + } + } + if (this.mpi != null) { + result += "Secret Key MPIs:\n"; + for (var i = 0; i < this.mpi.length; i++) { + result += this.mpi[i].toString(); + } + } + + if (this.subKeySignature != null) + result += "subKey Signature:\n"+this.subKeySignature.toString(); + + if (this.subKeyRevocationSignature != null ) + result += "subKey Revocation Signature:\n"+this.subKeyRevocationSignature.toString(); + return result; + } + + /** + * Continue parsing packets belonging to the key material such as signatures + * @param {Object} parent_node The parent object + * @param {String} bytes Input string to read the packet(s) from + * @param {Integer} position Start position for the parser + * @param {Integer} len Length of the packet(s) or remaining length of bytes + * @return {Integer} Length of nodes read + */ + function read_nodes(parent_node, bytes, position, len) { + this.parentNode = parent_node; + if (this.tagType == 14) { // public sub-key packet + var pos = position; + var result = null; + while (bytes.length != pos) { + var l = bytes.length - pos; + result = openpgp_packet.read_packet(bytes, pos, l); + if (result == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_pub]parsing ends here @:' + pos + " l:" + l); + break; + } else { + + switch (result.tagType) { + case 2: // Signature Packet certification signature + if (result.signatureType == 24) { // subkey binding signature + this.subKeySignature = result; + pos += result.packetLength + result.headerLength; + break; + } else if (result.signatureType == 40) { // subkey revocation signature + this.subKeyRevocationSignature[this.subKeyRevocationSignature.length] = result; + pos += result.packetLength + result.headerLength; + break; + } else { + util.print_error("openpgp.packet.keymaterial.js\nunknown signature:"+result.toString()); + } + + default: + this.data = bytes; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + break; + } + } + } + this.data = bytes; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + } else if (this.tagType == 7) { // private sub-key packet + var pos = position; + while (bytes.length != pos) { + var result = openpgp_packet.read_packet(bytes, pos, len - (pos - position)); + if (result == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_priv] parsing ends here @:' + pos); + break; + } else { + switch (result.tagType) { + case 2: // Signature Packet certification signature + if (result.signatureType == 24) // subkey embedded signature + this.subKeySignature = result; + else if (result.signatureType == 40) // subkey revocation signature + this.subKeyRevocationSignature[this.subKeyRevocationSignature.length] = result; + pos += result.packetLength + result.headerLength; + break; + default: + this.data = bytes; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + } + } + } + this.data = bytes; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + } else { + util.print_error("openpgp.packet.keymaterial.js\n"+"unknown parent node for a key material packet "+parent_node.tagType); + } + } + + /** + * Checks the validity for usage of this (sub)key + * @return {Integer} 0 = bad key, 1 = expired, 2 = revoked, 3 = valid + */ + function verifyKey() { + if (this.tagType == 14) { + if (this.subKeySignature == null) { + return 0; + } + if (this.subKeySignature.version == 4 && + this.subKeySignature.keyNeverExpires != null && + !this.subKeySignature.keyNeverExpires && + new Date((this.subKeySignature.keyExpirationTime*1000)+ this.creationTime.getTime()) < new Date()) { + return 1; + } + var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+ + String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata; + if (!this.subKeySignature.verify(hashdata,this.parentNode)) { + return 0; + } + for (var i = 0; i < this.subKeyRevocationSignature.length; i++) { + if (this.getKeyId() == this.subKeyRevocationSignature[i].keyId){ + return 2; + } + } + } + return 3; + } + + /** + * Calculates the key id of they key + * @return {String} A 8 byte key id + */ + function getKeyId() { + if (this.version == 4) { + var f = this.getFingerprint(); + return f.substring(12,20); + } else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) { + var key_id = this.MPIs[0].substring((this.MPIs[0].mpiByteLength-8)); + util.print_debug("openpgp.msg.publickey read_nodes:\n"+"V3 key ID: "+key_id); + return key_id; + } + } + + /** + * Calculates the fingerprint of the key + * @return {String} A string containing the fingerprint + */ + function getFingerprint() { + if (this.version == 4) { + tohash = String.fromCharCode(0x99)+ String.fromCharCode(((this.packetdata.length) >> 8) & 0xFF) + + String.fromCharCode((this.packetdata.length) & 0xFF)+this.packetdata; + util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm); + return str_sha1(tohash, tohash.length); + } else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) { + return MD5(this.MPIs[0].MPI); + } + } + + +} +// 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 + +/** + * @class + * @classdesc Implementation of the Signature Packet (Tag 2) + * + * RFC4480 5.2: + * A Signature packet describes a binding between some public key and + * some data. The most common signatures are a signature of a file or a + * block of text, and a signature that is a certification of a User ID. + */ +function openpgp_packet_signature() { + this.tag = 2; + + this.write = function() { + + } + + this.read = function() {} +} +// 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 + /** * @class * @classdesc Implementation of the Sym. Encrypted Integrity Protected Data @@ -12780,17 +12789,16 @@ function openpgp_packet_sym_encrypted_integrity_protected() { util.print_debug_hexstr_dump("calc hash = ", this.hash); - - this.packets.read(decrypted.substr(0, decrypted.length - 22)); - var mdc = decrypted.substr(decrypted.length - 20, 20); if(this.hash != mdc) { - this.packets = null; + this.packets = new openpgp_packetlist(); util.print_error("Decryption stopped: discovered a " + "modification of encrypted data."); return; } + else + this.packets.read(decrypted.substr(0, decrypted.length - 22)); } this.toString = function() { diff --git a/resources/openpgp.min.js b/resources/openpgp.min.js index 778c952c..f74a6dda 100644 --- a/resources/openpgp.min.js +++ b/resources/openpgp.min.js @@ -109,9 +109,9 @@ function normal_cfb_encrypt(b,a,c,d,e){for(var f="",f="",g=0,h=[],j=[],f=e.subst function normal_cfb_decrypt(b,a,c,d,e){var f="",g=0,h=[];if(null==e)for(e=0;ea*g;){for(var j=b(f,c),f=d.substring(g*a+0,g*a+a+0),e=0;eb?4:16==b?1:17==b?1:0}function openpgp_crypto_getPrefixRandom(b){switch(b){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}} function openpgp_crypto_MDCSystemBytes(b,a,c){util.print_debug_hexstr_dump("openpgp_crypto_symmetricDecrypt:\nencrypteddata:",c);switch(b){case 0:return c;case 2:return openpgp_cfb_mdc(desede,8,a,c,openpgp_cfb);case 3:return openpgp_cfb_mdc(cast5_encrypt,8,a,c);case 4:return openpgp_cfb_mdc(BFencrypt,8,a,c);case 7:case 8:case 9:return openpgp_cfb_mdc(AESencrypt,16,keyExpansion(a),c);case 10:return openpgp_cfb_mdc(TFencrypt,16,a,c);case 1:util.print_error(""+(1==b?"IDEA Algorithm not implemented": -"Twofish Algorithm not implemented"))}return null}function openpgp_crypto_generateSessionKey(b){return openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(b))}function openpgp_crypto_getKeyLength(b){switch(b){case 2:case 8:return 24;case 3:case 4:case 7:return 16;case 9:case 10:return 32}return null} +"Twofish Algorithm not implemented"))}return null}function openpgp_crypto_generateSessionKey(b){return openpgp_crypto_getRandomBytes(openpgp_crypto_getKeyLength(b))}function openpgp_crypto_getKeyLength(b){switch(b){case 2:case 8:return 24;case 3:case 4:case 7:return 16;case 9:case 10:return 32}return null}function openpgp_crypto_getBlockLength(b){switch(b){case 1:case 2:case 3:return 8;case 4:case 7:case 8:case 9:return 16;case 10:return 32;default:return 0}} function openpgp_crypto_verifySignature(b,a,c,d,e){var f=openpgp_crypto_hashData(a,e);switch(b){case 1:case 2:case 3:e=new RSA;b=d[0].toBigInteger();d=d[1].toBigInteger();c=c[0].toBigInteger();d=e.verify(c,d,b);a=openpgp_encoding_emsa_pkcs1_decode(a,d.toMPI().substring(2));return-1==a?(util.print_error("PKCS1 padding in message or key incorrect. Aborting..."),!1):a==f;case 16:return util.print_error("signing with Elgamal is not defined in the OpenPGP standard."),null;case 17:var b=new DSA,f=c[0].toBigInteger(), c=c[1].toBigInteger(),g=d[0].toBigInteger(),h=d[1].toBigInteger(),j=d[2].toBigInteger(),d=d[3].toBigInteger(),d=b.verify(a,f,c,e,g,h,j,d);return 0==d.compareTo(f);default:return null}} function openpgp_crypto_signData(b,a,c,d,e){switch(a){case 1:case 2:case 3:var a=new RSA,d=d[0].toBigInteger(),f=c[0].toBigInteger(),b=openpgp_encoding_emsa_pkcs1_encode(b,e,c[0].mpiByteLength);util.print_debug("signing using RSA");return a.sign(b,d,f).toMPI();case 17:a=new DSA;util.print_debug("DSA Sign: q size in Bytes:"+c[1].getByteLength());var f=c[0].toBigInteger(),g=c[1].toBigInteger(),h=c[2].toBigInteger();c[3].toBigInteger();c=d[0].toBigInteger();b=a.sign(b,e,h,f,g,c);util.print_debug("signing using DSA\n result:"+ @@ -285,7 +285,7 @@ JXG.Util.asciiCharCodeAt=function(b,a){var c=b.charCodeAt(a);if(255d?(a.push(String.fromCharCode(d)),c++):191d?(e=b.charCodeAt(c+1),a.push(String.fromCharCode((d&31)<<6|e&63)),c+=2):(e=b.charCodeAt(c+1),f=b.charCodeAt(c+2),a.push(String.fromCharCode((d&15)<<12|(e&63)<<6|f&63)),c+=3);return a.join("")}; JXG.Util.genUUID=function(){for(var b="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""),a=Array(36),c=0,d,e=0;36>e;e++)8==e||13==e||18==e||23==e?a[e]="-":14==e?a[e]="4":(2>=c&&(c=33554432+16777216*Math.random()|0),d=c&15,c>>=4,a[e]=b[19==e?d&3|8:d]);return a.join("")}; -function openpgp_config(){this.config=null;this.default_config={prefer_hash_algorithm:8,encryption_cipher:9,compression:1,show_version:!0,show_comment:!0,integrity_protect:!0,composition_behavior:0,keyserver:"keyserver.linux.it"};this.versionstring="OpenPGP.js v.1.20130424";this.commentstring="http://openpgpjs.org";this.debug=!1;this.read=function(){var b=JSON.parse(window.localStorage.getItem("config"));null==b?(this.config=this.default_config,this.write()):this.config=b};this.write=function(){window.localStorage.setItem("config", +function openpgp_config(){this.config=null;this.default_config={prefer_hash_algorithm:8,encryption_cipher:9,compression:1,show_version:!0,show_comment:!0,integrity_protect:!0,composition_behavior:0,keyserver:"keyserver.linux.it"};this.versionstring="OpenPGP.js v.1.20130425";this.commentstring="http://openpgpjs.org";this.debug=!1;this.read=function(){var b=JSON.parse(window.localStorage.getItem("config"));null==b?(this.config=this.default_config,this.write()):this.config=b};this.write=function(){window.localStorage.setItem("config", JSON.stringify(this.config))}}var b64s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";function s2r(b){var a,c,d,e="",f=0,g=0,h=b.length;for(d=0;d>2&63),a=(c&3)<<4):1==g?(e+=b64s.charAt(a|c>>4&15),a=(c&15)<<2):2==g&&(e+=b64s.charAt(a|c>>6&3),f+=1,0==f%60&&(e+="\n"),e+=b64s.charAt(c&63)),f+=1,0==f%60&&(e+="\n"),g+=1,3==g&&(g=0);0>6-e&255)),e=e+2&7,f=a<>8&255),d=d+String.fromCharCode(e&255),f=f+b+String.fromCharCode(c),b=new openpgp_type_mpi,a=openpgp_crypto_asymetricEncrypt(c,a,b.create(openpgp_encoding_eme_pkcs1_encode(d,a[0].mpiByteLength))),c=0;cthis.publicKeyAlgorithm?d=2:16==this.publicKeyAlgorithm?d=3:17==this.publicKeyAlgorithm&&(d=4);this.MPIs=[];for(var e=0;ethis.publicKeyAlgorithm?d=2:16==this.publicKeyAlgorithm?d=3:17==this.publicKeyAlgorithm&&(d=4);this.MPIs=[];for(e=0;ethis.publicKey.publicKeyAlgorithm)this.secMPIs=[],this.secMPIs[0]=new openpgp_type_mpi,this.secMPIs[0].read(b,d,c-2-(d-a)),d+=this.secMPIs[0].packetLength,this.secMPIs[1]=new openpgp_type_mpi,this.secMPIs[1].read(b,d,c-2-(d-a)),d+=this.secMPIs[1].packetLength,this.secMPIs[2]=new openpgp_type_mpi,this.secMPIs[2].read(b,d,c-2-(d-a)),d+=this.secMPIs[2].packetLength,this.secMPIs[3]=new openpgp_type_mpi,this.secMPIs[3].read(b,d,c-2-(d-a)),d+=this.secMPIs[3].packetLength;else if(16==this.publicKey.publicKeyAlgorithm)this.secMPIs= -[],this.secMPIs[0]=new openpgp_type_mpi,this.secMPIs[0].read(b,d,c-2-(d-a)),d+=this.secMPIs[0].packetLength;else if(17==this.publicKey.publicKeyAlgorithm)this.secMPIs=[],this.secMPIs[0]=new openpgp_type_mpi,this.secMPIs[0].read(b,d,c-2-(d-a)),d+=this.secMPIs[0].packetLength;this.checksum=[];this.checksum[0]=b[d++].charCodeAt();this.checksum[1]=b[d++].charCodeAt()}else this.encryptedMPIData=b.substring(d,c);return this};this.decryptSecretMPIs=function(b){if(this.hasUnencryptedSecretKeyData)return this.secMPIs; -var a=this.s2k.produce_key(b),c="";switch(this.symmetricEncryptionAlgorithm){case 1:return util.print_error("openpgp.packet.keymaterial.js\nsymmetric encryption algorithim: IDEA is not implemented"),!1;case 2:c=normal_cfb_decrypt(function(a,b){return des(b,a,1,null,0)},this.IVLength,a,this.encryptedMPIData,this.IV);break;case 3:c=normal_cfb_decrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},this.IVLength,util.str2bin(a.substring(0,16)),this.encryptedMPIData, -this.IV);break;case 4:c=normal_cfb_decrypt(function(a,b){return(new Blowfish(b)).encrypt(a)},this.IVLength,a,this.encryptedMPIData,this.IV);break;case 7:case 8:case 9:c=16;8==this.symmetricEncryptionAlgorithm&&(c=24,a=this.s2k.produce_key(b,c));9==this.symmetricEncryptionAlgorithm&&(c=32,a=this.s2k.produce_key(b,c));c=normal_cfb_decrypt(function(a,b){return AESencrypt(util.str2bin(a),b)},this.IVLength,keyExpansion(a.substring(0,c)),this.encryptedMPIData,this.IV);break;case 10:return util.print_error("openpgp.packet.keymaterial.js\nKey material is encrypted with twofish: not implemented"), -!1;default:return util.print_error("openpgp.packet.keymaterial.js\nunknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm),!1}if(null==c)return util.print_error("openpgp.packet.keymaterial.js\ncleartextMPIs was null"),!1;b=c.length;if(254==this.s2kUsageConventions&&str_sha1(c.substring(0,c.length-20))==c.substring(c.length-20))b-=20;else if(254!=this.s2kUsageConventions&&util.calc_checksum(c.substring(0,c.length-2))==(c.charCodeAt(c.length-2)<<8|c.charCodeAt(c.length-1)))b-= -2;else return!1;if(0this.publicKey.publicKeyAlgorithm)a=0,this.secMPIs=[],this.secMPIs[0]=new openpgp_type_mpi,this.secMPIs[0].read(c,0,b),a+=this.secMPIs[0].packetLength,this.secMPIs[1]=new openpgp_type_mpi,this.secMPIs[1].read(c,a,b-a),a+=this.secMPIs[1].packetLength,this.secMPIs[2]=new openpgp_type_mpi,this.secMPIs[2].read(c,a,b-a),a+=this.secMPIs[2].packetLength,this.secMPIs[3]=new openpgp_type_mpi,this.secMPIs[3].read(c,a,b-a),a+=this.secMPIs[3].packetLength; -else if(16==this.publicKey.publicKeyAlgorithm)this.secMPIs=[],this.secMPIs[0]=new openpgp_type_mpi,this.secMPIs[0].read(c,0,c);else if(17==this.publicKey.publicKeyAlgorithm)this.secMPIs=[],this.secMPIs[0]=new openpgp_type_mpi,this.secMPIs[0].read(c,0,b);return!0};this.read_nodes=function(b,a,c,d){this.parentNode=b;if(14==this.tagType){for(var b=c,e=null;a.length!=b;)if(d=a.length-b,e=openpgp_packet.read_packet(a,b,d),null==e){util.print_error("openpgp.packet.keymaterial.js\n[user_keymat_pub]parsing ends here @:"+ -b+" l:"+d);break}else switch(e.tagType){case 2:if(24==e.signatureType){this.subKeySignature=e;b+=e.packetLength+e.headerLength;break}else if(40==e.signatureType){this.subKeyRevocationSignature[this.subKeyRevocationSignature.length]=e;b+=e.packetLength+e.headerLength;break}else util.print_error("openpgp.packet.keymaterial.js\nunknown signature:"+e.toString());default:return this.data=a,this.position=c-this.parentNode.packetLength,this.len=b-c}this.data=a;this.position=c-this.parentNode.packetLength; -return this.len=b-c}if(7==this.tagType){for(b=c;a.length!=b;)if(e=openpgp_packet.read_packet(a,b,d-(b-c)),null==e){util.print_error("openpgp.packet.keymaterial.js\n[user_keymat_priv] parsing ends here @:"+b);break}else switch(e.tagType){case 2:24==e.signatureType?this.subKeySignature=e:40==e.signatureType&&(this.subKeyRevocationSignature[this.subKeyRevocationSignature.length]=e);b+=e.packetLength+e.headerLength;break;default:return this.data=a,this.position=c-this.parentNode.packetLength,this.len= -b-c}this.data=a;this.position=c-this.parentNode.packetLength;return this.len=b-c}util.print_error("openpgp.packet.keymaterial.js\nunknown parent node for a key material packet "+b.tagType)};this.verifyKey=function(){if(14==this.tagType){if(null==this.subKeySignature)return 0;if(4==this.subKeySignature.version&&null!=this.subKeySignature.keyNeverExpires&&!this.subKeySignature.keyNeverExpires&&new Date(1E3*this.subKeySignature.keyExpirationTime+this.creationTime.getTime())this.publicKeyAlgorithm){var b=this.MPIs[0].substring(this.MPIs[0].mpiByteLength- -8);util.print_debug("openpgp.msg.publickey read_nodes:\nV3 key ID: "+b);return b}};this.getFingerprint=function(){if(4==this.version)return tohash=String.fromCharCode(153)+String.fromCharCode(this.packetdata.length>>8&255)+String.fromCharCode(this.packetdata.length&255)+this.packetdata,util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm),str_sha1(tohash,tohash.length);if(3==this.version&&0this.publicKeyAlgorithm)return MD5(this.MPIs[0].MPI)};this.write_private_key=function(b,a,c,d,e,f){this.symmetricEncryptionAlgorithm=e;e=String.fromCharCode(4);e+=f;switch(b){case 1:e+=String.fromCharCode(b);e+=a.n.toMPI();e+=a.ee.toMPI();if(c)switch(e+=String.fromCharCode(254),e+=String.fromCharCode(this.symmetricEncryptionAlgorithm),e+=String.fromCharCode(3),e+=String.fromCharCode(d),b=a.d.toMPI()+a.p.toMPI()+a.q.toMPI()+a.u.toMPI(),a=str_sha1(b),util.print_debug_hexstr_dump("write_private_key sha1: ", -a),f=openpgp_crypto_getRandomBytes(8),util.print_debug_hexstr_dump("write_private_key Salt: ",f),e=e+f+String.fromCharCode(96),util.print_debug("write_private_key c: 96"),c=(new openpgp_type_s2k).write(3,d,c,f,96),this.symmetricEncryptionAlgorithm){case 3:this.IVLength=8;this.IV=openpgp_crypto_getRandomBytes(this.IVLength);ciphertextMPIs=normal_cfb_encrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},this.IVLength,util.str2bin(c.substring(0,16)),b+a, -this.IV);e+=this.IV+ciphertextMPIs;break;case 7:case 8:case 9:this.IVLength=16,this.IV=openpgp_crypto_getRandomBytes(this.IVLength),ciphertextMPIs=normal_cfb_encrypt(AESencrypt,this.IVLength,c,b+a,this.IV),e+=this.IV+ciphertextMPIs}else e+=String.fromCharCode(0),e+=a.d.toMPI()+a.p.toMPI()+a.q.toMPI()+a.u.toMPI(),c=util.calc_checksum(a.d.toMPI()+a.p.toMPI()+a.q.toMPI()+a.u.toMPI()),e+=String.fromCharCode(c/256)+String.fromCharCode(c%256),util.print_debug_hexstr_dump("write_private_key basic checksum: "+ -c);break;default:e="",util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+b)}c=openpgp_packet.write_packet_header(5,e.length);return{string:c+e,header:c,body:e}};this.write_public_key=function(b,a,c){var d=String.fromCharCode(4),d=d+c;switch(b){case 1:d+=String.fromCharCode(1);d+=a.n.toMPI();d+=a.ee.toMPI();break;default:util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+b)}b=openpgp_packet.write_packet_header(6,d.length); -return{string:b+d,header:b,body:d}}}function openpgp_packet_marker(){this.tagType=10;this.read_packet=function(b,a){this.packetLength=3;return 80==b[a].charCodeAt()&&71==b[a+1].charCodeAt()&&80==b[a+2].charCodeAt()?this:null};this.toString=function(){return'5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)\n packet reads: "PGP"\n'}} +b.encryptedData.decrypt(d,c):b.encryptedData.decrypt_sym(d,c)}if(3==this.tagType)return util.print_error("Symmetric encrypted sessionkey is not supported!"),null}}function openpgp_packet_marker(){this.tagType=10;this.read_packet=function(b,a){this.packetLength=3;return 80==b[a].charCodeAt()&&71==b[a+1].charCodeAt()&&80==b[a+2].charCodeAt()?this:null};this.toString=function(){return'5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)\n packet reads: "PGP"\n'}} function openpgp_packet_onepasssignature(){this.tagType=4;this.flags=this.signingKeyId=this.publicKeyAlgorithm=this.hashAlgorithm=this.type=this.version=null;this.read_packet=read_packet;this.toString=function(){return"5.4. One-Pass Signature Packets (Tag 4)\n length: "+this.packetLength+"\n type: "+this.type+"\n keyID: "+this.signingKeyId.toString()+"\n hashA: "+this.hashAlgorithm+"\n pubKeyA:"+this.publicKeyAlgorithm+"\n flags: "+this.flags+"\n version:"+this.version+ "\n"};this.write_packet=write_packet} function openpgp_packet_signature(){function b(a,b){var d;d=""+openpgp_packet.encode_length(b.length+1);d+=String.fromCharCode(a);return d+b}this.tagType=2;this.embeddedSignature=this.signatureTargetHash=this.signatureTargetHashAlgorithm=this.signatureTargetPublicKeyAlgorithm=this.reasonForRevocationString=this.reasonForRevocationFlag=this.signersUserId=this.keyFlags=this.policyURI=this.isPrimaryUserID=this.preferredKeyServer=this.keyServerPreferences=this.preferredCompressionAlgorithms=this.preferredHashAlgorithms= @@ -439,18 +411,30 @@ this.publicKeyPacket.data+a,e.verify(f,d))){result[c]=e.keyId==b.getKeyId()?6:3; function _openpgp_packet(){function b(a){var b="";192>a?b+=String.fromCharCode(a):191a?(b+=String.fromCharCode((a-192>>8)+192),b+=String.fromCharCode(a-192&255)):(b+=String.fromCharCode(255),b+=String.fromCharCode(a>>24&255),b+=String.fromCharCode(a>>16&255),b+=String.fromCharCode(a>>8&255),b+=String.fromCharCode(a&255));return b}this.encode_length=b;this.write_old_packet_header=function(a,b){var d="";256>b?(d+=String.fromCharCode(128|a<<2),d+=String.fromCharCode(b)):(65536>b?(d+=String.fromCharCode(a<< 2|129),d+=String.fromCharCode(b>>8)):(d+=String.fromCharCode(a<<2|130),d+=String.fromCharCode(b>>24&255),d+=String.fromCharCode(b>>16&255),d+=String.fromCharCode(b>>8&255)),d+=String.fromCharCode(b&255));return d};this.write_packet_header=function(a,c){var d;d=""+String.fromCharCode(192|a);return d+=b(c)};this.read_packet=function(a,b,d){if(null==a||a.length<=b||2>a.substring(b).length||0==(a[b].charCodeAt()&128))return util.print_error("Error during parsing. This message / key is probably not containing a valid OpenPGP format."), null;var e=-1,f=-1,f=0;0!=(a[b].charCodeAt()&64)&&(f=1);var g;f?e=a[b].charCodeAt()&63:(e=(a[b].charCodeAt()&63)>>2,g=a[b].charCodeAt()&3);b++;var h=null,j=-1;if(f)if(192>a[b].charCodeAt())d=a[b++].charCodeAt(),util.print_debug("1 byte length:"+d);else if(192<=a[b].charCodeAt()&&224>a[b].charCodeAt())d=(a[b++].charCodeAt()-192<<8)+a[b++].charCodeAt()+192,util.print_debug("2 byte length:"+d);else if(223a[b].charCodeAt()){d=1<<(a[b++].charCodeAt()&31);util.print_debug("4 byte length:"+ -d);g=b+d;for(h=a.substring(b,b+d);;)if(192>a[g].charCodeAt()){j=a[g++].charCodeAt();d+=j;h+=a.substring(g,g+j);g+=j;break}else if(192<=a[g].charCodeAt()&&224>a[g].charCodeAt()){j=(a[g++].charCodeAt()-192<<8)+a[g++].charCodeAt()+192;d+=j;h+=a.substring(g,g+j);g+=j;break}else if(223a[g].charCodeAt())j=1<<(a[g++].charCodeAt()&31),d+=j,h+=a.substring(g,g+j),g+=j;else{g++;j=a[g++].charCodeAt()<<24|a[g++].charCodeAt()<<16|a[g++].charCodeAt()<<8|a[g++].charCodeAt();h+=a.substring(g, -g+j);d+=j;g+=j;break}j=g}else b++,d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();else switch(g){case 0:d=a[b++].charCodeAt();break;case 1:d=a[b++].charCodeAt()<<8|a[b++].charCodeAt();break;case 2:d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();break}-1==j&&(j=d);null==h&&(h=a.substring(b,b+j));var a={},k;for(k in this.type)a[this.type[k]]=k;k="openpgp_packet_"+a[e];a=window[k];if(void 0==a)throw k;k=new a;k.read(h); -return{packet:k,offset:b+d}};this.type={reserved:0,public_key_encrypted_session_key:1,signature:2,sym_encrypted_session_key:3,one_pass_signature:4,secret_key:5,public_key:6,secret_subkey:7,compressed:8,symmetrically_encrypted:9,marker:10,literal:11,trust:12,userid:13,public_subkey:14,user_attribute:17,sym_encrypted_integrity_protected:18,modification_detection_code:19}}var openpgp_packet=new _openpgp_packet; -function openpgp_packetlist(){this.length=0;this.read=function(b){this.packets=[];for(var a=0;aa[j].charCodeAt()){g=a[j++].charCodeAt();d+=g;h+=a.substring(j,j+g);j+=g;break}else if(192<=a[j].charCodeAt()&&224>a[j].charCodeAt()){g=(a[j++].charCodeAt()-192<<8)+a[j++].charCodeAt()+192;d+=g;h+=a.substring(j,j+g);j+=g;break}else if(223a[j].charCodeAt())g=1<<(a[j++].charCodeAt()&31),d+=g,h+=a.substring(j,j+g),j+=g;else{j++;g=a[j++].charCodeAt()<<24|a[j++].charCodeAt()<<16|a[j++].charCodeAt()<<8|a[j++].charCodeAt();h+=a.substring(j, +j+g);d+=g;j+=g;break}}else b++,d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();else switch(g){case 0:d=a[b++].charCodeAt();break;case 1:d=a[b++].charCodeAt()<<8|a[b++].charCodeAt();break;case 2:d=a[b++].charCodeAt()<<24|a[b++].charCodeAt()<<16|a[b++].charCodeAt()<<8|a[b++].charCodeAt();break}-1==j&&(j=d);null==h&&(h=a.substring(b,b+j));var a={},k;for(k in this.type)a[this.type[k]]=k;k="openpgp_packet_"+a[e];a=window[k];if(void 0==a)throw k;k=new a;k.read(h); +return{packet:k,offset:b+j}};this.type={reserved:0,public_key_encrypted_session_key:1,signature:2,sym_encrypted_session_key:3,one_pass_signature:4,secret_key:5,public_key:6,secret_subkey:7,compressed:8,symmetrically_encrypted:9,marker:10,literal:11,trust:12,userid:13,public_subkey:14,user_attribute:17,sym_encrypted_integrity_protected:18,modification_detection_code:19}}var openpgp_packet=new _openpgp_packet; +function openpgp_packetlist(){this.length=0;this.read=function(b){for(var a=0;ab.length)return util.print_error("openpgp.packet.encryptedsessionkey.js\ninvalid length"),null;this.version=b[0].charCodeAt();this.public_key_id.read_packet(b,1);this.public_key_algorithm=b[9].charCodeAt(); var a=10;switch(this.public_key_algorithm){case openpgp.publickey.rsa_encrypt:case openpgp.publickey.rsa_encrypt_sign:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;this.encrypted[0].read(b.substr(a));break;case openpgp.publickey.elgamal:this.encrypted=[];this.encrypted[0]=new openpgp_type_mpi;a+=this.encrypted[0].read(b.substr(a));this.encrypted[1]=new openpgp_type_mpi;this.encrypted[1].read(b.substr(a));break;default:util.print_error("openpgp.packet.encryptedsessionkey.js\nunknown public key packet algorithm type "+ this.public_key_algorithm)}};this.write=function(){for(var b=String.fromCharCode(this.version),b=b+this.public_key_id.bytes,b=b+String.fromCharCode(this.public_key_algorithm),a=0;a>8&255),a=a+String.fromCharCode(c&255),c=new openpgp_type_mpi;c.fromBytes(openpgp_encoding_eme_pkcs1_encode(a, b[0].byteLength()));this.encrypted=openpgp_crypto_asymetricEncrypt(this.public_key_algorithm,b,c)};this.decrypt=function(b,a){var c=openpgp_crypto_asymetricDecrypt(this.public_key_algorithm,b,a,this.encrypted).toBytes(),d=(c.charCodeAt(c.length-2)<<8)+c.charCodeAt(c.length-1),c=openpgp_encoding_eme_pkcs1_decode(c,b[0].byteLength()),e=c.substring(1,c.length-2);d!=util.calc_checksum(e)?util.print_error("Checksum mismatch"):(this.symmetric_key=e,this.symmetric_algorithm=c.charCodeAt(0))};this.toString= function(){for(var b="5.1. Public-Key Encrypted Session Key Packets (Tag 1)\n KeyId: "+this.keyId.toString()+"\n length: "+this.packetLength+"\n version:"+this.version+"\n pubAlgUs:"+this.publicKeyAlgorithmUsed+"\n",a=0;athis.algorithm?2:16==this.algorithm?3:17==this.algorithm?4:0;this.mpi= +[];for(var b=b.substr(6),c=0,d=0;db.length&&util.print_error("openpgp.packet.keymaterial.js\nerror reading MPI @:"+c);return c+6}util.print_error("Unknown packet version")}};this.write=function(){var b=String.fromCharCode(4),b=b+"0000"+String.fromCharCode(this.algorithm),a;for(a in this.mpi)b+=this.mpi[a].write();return b};this.toString=function(){}} +function openpgp_packet_secret_key(){this.tag=5;this.public_key=new openpgp_packet_public_key;this.mpi=[];this.symmetric_algorithm=openpgp.symmetric.plaintext;this.s2k=null;this.checksum_algorithm=openpgp.hash.sha1;this.iv=this.encrypted=null;this.read=function(b){var a=this.public_key.read(b),b=b.substr(a),c=b[0].charCodeAt(),a=1;if(255==c||254==c)this.symmetric_algorithm=b[a++].charCodeAt(),this.s2k=new openpgp_type_s2k,this.s2k.read(b,a),a+=this.s2k.s2kLength;if(0!=c&&255!=c&&254!=c)this.symmetric_algorithm= +c;if(0!=c&&1001!=this.s2k.type)this.iv=b.substr(a,openpgp_crypto_getBlockLength(this.symmetric_algorithm)),a+=this.iv.length;if(0!=c&&1001==this.s2k.type)this.encrypted=this.mpi=null;else if(0!=c)this.encrypted=b.substr(a);else{openpgp_crypto_getPrivateMpiCount(this.public_key.algorithm);this.mpi=[];for(c=0;4>c;c++)this.mpi[c]=new openpgp_type_mpi,a+=this.mpi[c].read(b.substr(a));this.checksum=[];this.checksum[0]=b[a++].charCodeAt();this.checksum[1]=b[a++].charCodeAt()}};this.write=function(){var b= +String.fromCharCode(4),b=b+timePacket;switch(keyType){case 1:b+=String.fromCharCode(keyType);b+=key.n.toMPI();b+=key.ee.toMPI();if(password){var b=b+String.fromCharCode(254),b=b+String.fromCharCode(this.symmetric_algorithm),b=b+String.fromCharCode(3),b=b+String.fromCharCode(s2kHash),a=key.d.toMPI()+key.p.toMPI()+key.q.toMPI()+key.u.toMPI(),c=str_sha1(a);util.print_debug_hexstr_dump("write_private_key sha1: ",c);var d=openpgp_crypto_getRandomBytes(8);util.print_debug_hexstr_dump("write_private_key Salt: ", +d);b=b+d+String.fromCharCode(96);util.print_debug("write_private_key c: 96");d=(new openpgp_type_s2k).write(3,s2kHash,password,d,96);switch(this.symmetric_algorithm){case 3:this.IVLength=8;this.IV=openpgp_crypto_getRandomBytes(this.IVLength);ciphertextMPIs=normal_cfb_encrypt(function(a,b){var c=new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},this.IVLength,util.str2bin(d.substring(0,16)),a+c,this.IV);b+=this.IV+ciphertextMPIs;break;case 7:case 8:case 9:this.IVLength=16,this.IV= +openpgp_crypto_getRandomBytes(this.IVLength),ciphertextMPIs=normal_cfb_encrypt(AESencrypt,this.IVLength,d,a+c,this.IV),b+=this.IV+ciphertextMPIs}}else b+=String.fromCharCode(0),b+=key.d.toMPI()+key.p.toMPI()+key.q.toMPI()+key.u.toMPI(),a=util.calc_checksum(key.d.toMPI()+key.p.toMPI()+key.q.toMPI()+key.u.toMPI()),b+=String.fromCharCode(a/256)+String.fromCharCode(a%256),util.print_debug_hexstr_dump("write_private_key basic checksum: "+a);break;default:b="",util.print_error("openpgp.packet.keymaterial.js\nerror writing private key, unknown type :"+ +keyType)}a=openpgp_packet.write_packet_header(tag,b.length);return{string:a+b,header:a,body:b}};this.decrypt=function(b){if(null!=this.encrypted){var a=this.s2k.produce_key(b),b="";switch(this.symmetric_algorithm){case 1:return util.print_error("openpgp.packet.keymaterial.js\nsymmetric encryption algorithim: IDEA is not implemented"),!1;case 2:b=normal_cfb_decrypt(function(a,b){return des(b,a,1,null,0)},this.IVLength,a,this.encrypted,this.IV);break;case 3:b=normal_cfb_decrypt(function(a,b){var c= +new openpgp_symenc_cast5;c.setKey(b);return c.encrypt(util.str2bin(a))},this.IVLength,util.str2bin(a.substring(0,16)),this.encrypted,this.IV);break;case 4:b=normal_cfb_decrypt(function(a,b){return(new Blowfish(b)).encrypt(a)},this.IVLength,a,this.encrypted,this.IV);break;case 7:case 8:case 9:b=16;8==this.symmetric_algorithm&&(b=24,a=this.s2k.produce_key(str_passphrase,b));9==this.symmetric_algorithm&&(b=32,a=this.s2k.produce_key(str_passphrase,b));b=normal_cfb_decrypt(function(a,b){return AESencrypt(util.str2bin(a), +b)},this.IVLength,keyExpansion(a.substring(0,b)),this.encrypted,this.IV);break;case 10:return util.print_error("openpgp.packet.keymaterial.js\nKey material is encrypted with twofish: not implemented"),!1;default:return util.print_error("openpgp.packet.keymaterial.js\nunknown encryption algorithm for secret key :"+this.symmetric_algorithm),!1}if(null==b)return util.print_error("openpgp.packet.keymaterial.js\ncleartextMPIs was null"),!1;a=b.length;if(254==s2k_usage&&str_sha1(b.substring(0,b.length- +20))==b.substring(b.length-20))a-=20;else if(254!=s2k_usage&&util.calc_checksum(b.substring(0,b.length-2))==(b.charCodeAt(b.length-2)<<8|b.charCodeAt(b.length-1)))a-=2;else return!1;if(0this.publicKey.publicKeyAlgorithm){var c=0;this.mpi=[];this.mpi[0]=new openpgp_type_mpi;this.mpi[0].read(b,0,a);c+=this.mpi[0].packetLength;this.mpi[1]=new openpgp_type_mpi;this.mpi[1].read(b,c,a-c);c+=this.mpi[1].packetLength;this.mpi[2]=new openpgp_type_mpi;this.mpi[2].read(b, +c,a-c);c+=this.mpi[2].packetLength;this.mpi[3]=new openpgp_type_mpi;this.mpi[3].read(b,c,a-c);c+=this.mpi[3].packetLength}else if(16==this.publicKey.publicKeyAlgorithm)this.mpi=[],this.mpi[0]=new openpgp_type_mpi,this.mpi[0].read(b,0,b);else if(17==this.publicKey.publicKeyAlgorithm)this.mpi=[],this.mpi[0]=new openpgp_type_mpi,this.mpi[0].read(b,0,a);return!0}}}function openpgp_packet_signature(){this.tag=2;this.write=function(){};this.read=function(){}} function openpgp_packet_sym_encrypted_integrity_protected(){this.tag=18;this.version=1;this.encrypted=null;this.modification=!1;this.packets=new openpgp_packetlist;this.read=function(b){this.version=b[0].charCodeAt();if(1!=this.version)return util.print_error("openpgp.packet.encryptedintegrityprotecteddata.js\nunknown encrypted integrity protected data packet version: "+this.version+"hex:"+util.hexstrdump(b)),null;this.encrypted=b.substr(1)};this.write=function(){return String.fromCharCode(this.version)+ this.encrypted};this.encrypt=function(b,a){var c=this.packets.write(),d=openpgp_crypto_getPrefixRandom(b),e=d+d.charAt(d.length-2)+d.charAt(d.length-1),c=c+String.fromCharCode(211),c=c+String.fromCharCode(20);util.print_debug_hexstr_dump("data to be hashed:",e+c);c+=str_sha1(e+c);util.print_debug_hexstr_dump("hash:",c.substring(c.length-20,c.length));this.encrypted=openpgp_crypto_symmetricEncrypt(d,b,a,c,!1).substring(0,e.length+c.length)};this.decrypt=function(b,a){var c=openpgp_crypto_symmetricDecrypt(b, -a,this.encrypted,!1);this.hash=str_sha1(openpgp_crypto_MDCSystemBytes(b,a,this.encrypted)+c.substring(0,c.length-20));util.print_debug_hexstr_dump("calc hash = ",this.hash);this.packets.read(c.substr(0,c.length-22));if(this.hash!=c.substr(c.length-20,20))this.packets=null,util.print_error("Decryption stopped: discovered a modification of encrypted data.")};this.toString=function(){var b="";openpgp.config.debug&&(b=" data: Bytes ["+util.hexstrdump(this.encrypted)+"]");return"5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n\n version: "+ +a,this.encrypted,!1);this.hash=str_sha1(openpgp_crypto_MDCSystemBytes(b,a,this.encrypted)+c.substring(0,c.length-20));util.print_debug_hexstr_dump("calc hash = ",this.hash);this.hash!=c.substr(c.length-20,20)?(this.packets=new openpgp_packetlist,util.print_error("Decryption stopped: discovered a modification of encrypted data.")):this.packets.read(c.substr(0,c.length-22))};this.toString=function(){var b="";openpgp.config.debug&&(b=" data: Bytes ["+util.hexstrdump(this.encrypted)+"]");return"5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n\n version: "+ this.version+"\n"+b}} function openpgp_packet_sym_encrypted_session_key(){this.tag=3;this.algorithm=this.private_algorithm=openpgp.symmetric.plaintext;this.encrypted=null;this.s2k=new openpgp_type_s2k;this.read=function(b){this.version=b[0].charCodeAt();this.private_algorithm=b[1].charCodeAt();this.s2k.read(b,2);var a=this.s2k.length+2;if(a 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; +} + /** * generate random byte prefix as string for the specified algorithm * @param {Integer} algo Algorithm to use (see RFC4880 9.2) @@ -181,6 +205,28 @@ function openpgp_crypto_getKeyLength(algo) { 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 diff --git a/src/packet/packet.js b/src/packet/packet.js index be603492..5b88f24f 100644 --- a/src/packet/packet.js +++ b/src/packet/packet.js @@ -277,7 +277,7 @@ function _openpgp_packet() { return { packet: result, - offset: mypos + packet_length + offset: mypos + real_packet_length }; } diff --git a/src/packet/packetlist.js b/src/packet/packetlist.js index df3c1ca7..4c3f0a56 100644 --- a/src/packet/packetlist.js +++ b/src/packet/packetlist.js @@ -16,7 +16,6 @@ function openpgp_packetlist() { * @param {openpgp_bytearray} An array of bytes. */ this.read = function(bytes) { - this.packets = []; var i = 0; while(i < bytes.length) { diff --git a/src/packet/public_key.js b/src/packet/public_key.js new file mode 100644 index 00000000..f4cdc46e --- /dev/null +++ b/src/packet/public_key.js @@ -0,0 +1,213 @@ +// 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 + +/** + * @class + * @classdesc Implementation of the Key Material Packet (Tag 5,6,7,14) + * + * RFC4480 5.5: + * A key material packet contains all the information about a public or + * private key. There are four variants of this packet type, and two + * major versions. Consequently, this section is complex. + */ +function openpgp_packet_public_key() { + // members: + this.tag = 6; + this.version = 4; + this.expiration = null; + this.created = null; + this.mpi = []; + this.algorithm = openpgp.publickey.rsa_sign; + + + var public_mpis = function(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; + }; + + + /** + * Internal Parser for public keys as specified in RFC 4880 section + * 5.5.2 Public-Key Packet Formats + * called by read_tag<num> + * @param {String} input Input string to read the packet from + * @param {Integer} position Start position for the parser + * @param {Integer} len Length of the packet or remaining length of input + * @return {Object} This object with attributes set by the parser + */ + this.read = function(bytes) { + // A one-octet version number (3 or 4). + this.version = bytes[0].charCodeAt(); + + if (this.version == 3) { + /* + // A four-octet number denoting the time that the key was created. + this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | + (input[mypos++].charCodeAt() << 16) | + (input[mypos++].charCodeAt() << 8) | + (input[mypos++].charCodeAt()))*1000); + + // - A two-octet number denoting the time in days that this key is + // valid. If this number is zero, then it does not expire. + this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt(); + + // - A one-octet number denoting the public-key algorithm of this key. + this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + var mpicount = 0; + // - 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 (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) + mpicount = 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 (this.publicKeyAlgorithm == 16) + mpicount = 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 (this.publicKeyAlgorithm == 17) + mpicount = 4; + + this.MPIs = new Array(); + for (var i = 0; i < mpicount; i++) { + this.MPIs[i] = new openpgp_type_mpi(); + if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && + !this.packetLength < (mypos-position)) { + mypos += this.MPIs[i].packetLength; + } else { + util.print_error("openpgp.packet.keymaterial.js\n"+ + 'error reading MPI @:'+mypos); + } + } + this.packetLength = mypos-position; + */ + } else if (this.version == 4) { + // - A four-octet number denoting the time that the key was created. + var timeb = bytes.substr(1, 4); + + this.created= new Date(( + (timeb[0].charCodeAt() << 24) | + (timeb[1].charCodeAt() << 16) | + (timeb[2].charCodeAt() << 8) | + (timeb[3].charCodeAt())) * 1000); + + // - A one-octet number denoting the public-key algorithm of this key. + this.algorithm = bytes[5].charCodeAt(); + + var mpicount = public_mpis(this.algorithm); + this.mpi = []; + + var bmpi = bytes.substr(6); + var p = 0; + + for (var i = 0; + i < mpicount && p < bmpi.length; + i++) { + + this.mpi[i] = new openpgp_type_mpi(); + + p += this.mpi[i].read(bmpi.substr(p)) + + if(p > bmpi.length) + util.print_error("openpgp.packet.keymaterial.js\n" + +'error reading MPI @:'+p); + } + + return p + 6; + } else { + util.print_error('Unknown packet version'); + } + } + + /* + * Same as write_private_key, but has less information because of + * public key. + * @param {Integer} keyType Follows the OpenPGP algorithm standard, + * IE 1 corresponds to RSA. + * @param {RSA.keyObject} key + * @param timePacket + * @return {Object} {body: [string]OpenPGP packet body contents, + * header: [string] OpenPGP packet header, string: [string] header+body} + */ + this.write = function() { + var result = String.fromCharCode(4); + result += '0000'; + result += String.fromCharCode(this.algorithm); + + for(var i in this.mpi) { + result += this.mpi[i].write(); + } + + return result; + } + + + /** + * Generates Debug output + * @return String which gives some information about the keymaterial + */ + this.toString = function() { + var result = ""; + switch (this.tag) { + case 6: + result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + case 14: + result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + } + } + + +} diff --git a/src/packet/openpgp.packet.keymaterial.js b/src/packet/secret_key.js similarity index 50% rename from src/packet/openpgp.packet.keymaterial.js rename to src/packet/secret_key.js index e52fb879..94ec44a4 100644 --- a/src/packet/openpgp.packet.keymaterial.js +++ b/src/packet/secret_key.js @@ -24,342 +24,181 @@ * private key. There are four variants of this packet type, and two * major versions. Consequently, this section is complex. */ -function openpgp_packet_keymaterial() { - // members: - this.publicKeyAlgorithm = null; - this.tagType = null; - this.creationTime = null; - this.version = null; - this.expiration = null;// V3 - this.MPIs = null; - this.secMPIs = null; - this.publicKey = null; - this.symmetricEncryptionAlgorithm = null; - this.s2kUsageConventions = null; - this.IVLength = null; - this.encryptedMPIData = null; - this.hasUnencryptedSecretKeyData = null; - this.checksum = null; - this.parentNode = null; - this.subKeySignature = null; - this.subKeyRevocationSignature = null; +function openpgp_packet_secret_key() { + this.tag = 5; + this.public_key = new openpgp_packet_public_key(); + this.mpi = []; + this.symmetric_algorithm = openpgp.symmetric.plaintext; + this.s2k = null; + this.checksum_algorithm = openpgp.hash.sha1; + this.encrypted = null; + this.iv = null; - // 5.5.1. Key Packet Variants - - // 5.5.1.3. Secret-Key Packet (Tag 5) - /** - * This function reads the payload of a secret key packet (Tag 5) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Intefer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag5(input, position, len) { - this.tagType = 5; - this.read_priv_key(input, position, len); - return this; - } - - // 5.5.1.1. Public-Key Packet (Tag 6) - /** - * This function reads the payload of a public key packet (Tag 6) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag6(input, position, len) { - // A Public-Key packet starts a series of packets that forms an OpenPGP - // key (sometimes called an OpenPGP certificate). - this.tagType = 6; - this.packetLength = len; - this.read_pub_key(input, position,len); - - return this; - } - - // 5.5.1.4. Secret-Subkey Packet (Tag 7) - /** - * This function reads the payload of a secret key sub packet (Tag 7) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag7(input, position, len) { - this.tagType = 7; - this.packetLength = len; - return this.read_priv_key(input, position, len); - } - - // 5.5.1.2. Public-Subkey Packet (Tag 14) - /** - * This function reads the payload of a public key sub packet (Tag 14) - * and initializes the openpgp_packet_keymaterial - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {openpgp_packet_keymaterial} - */ - function read_tag14(input, position, len) { - this.subKeySignature = null; - this.subKeyRevocationSignature = new Array(); - this.tagType = 14; - this.packetLength = len; - this.read_pub_key(input, position,len); - return this; - } - - /** - * Internal Parser for public keys as specified in RFC 4880 section - * 5.5.2 Public-Key Packet Formats - * called by read_tag<num> - * @param {String} input Input string to read the packet from - * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input - * @return {Object} This object with attributes set by the parser - */ - function read_pub_key(input, position, len) { - var mypos = position; - // A one-octet version number (3 or 4). - this.version = input[mypos++].charCodeAt(); - if (this.version == 3) { - // A four-octet number denoting the time that the key was created. - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()))*1000); - - // - A two-octet number denoting the time in days that this key is - // valid. If this number is zero, then it does not expire. - this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt(); - - // - A one-octet number denoting the public-key algorithm of this key. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); - var mpicount = 0; - // - 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 (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) - mpicount = 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 (this.publicKeyAlgorithm == 16) - mpicount = 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 (this.publicKeyAlgorithm == 17) - mpicount = 4; - - this.MPIs = new Array(); - for (var i = 0; i < mpicount; i++) { - this.MPIs[i] = new openpgp_type_mpi(); - if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && - !this.packetLength < (mypos-position)) { - mypos += this.MPIs[i].packetLength; - } else { - util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos); - } - } - this.packetLength = mypos-position; - } else if (this.version == 4) { - // - A four-octet number denoting the time that the key was created. - this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | - (input[mypos++].charCodeAt() << 16) | - (input[mypos++].charCodeAt() << 8) | - (input[mypos++].charCodeAt()))*1000); - - // - A one-octet number denoting the public-key algorithm of this key. - this.publicKeyAlgorithm = input[mypos++].charCodeAt(); - var mpicount = 0; - // - 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 (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) - mpicount = 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 (this.publicKeyAlgorithm == 16) - mpicount = 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 (this.publicKeyAlgorithm == 17) - mpicount = 4; - - this.MPIs = new Array(); - var i = 0; - for (var i = 0; i < mpicount; i++) { - this.MPIs[i] = new openpgp_type_mpi(); - if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && - !this.packetLength < (mypos-position)) { - mypos += this.MPIs[i].packetLength; - } else { - util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos); - } - } - this.packetLength = mypos-position; - } else { - return null; - } - this.data = input.substring(position, mypos); - this.packetdata = input.substring(position, mypos); - return this; - } // 5.5.3. Secret-Key Packet Formats /** * Internal parser for private keys as specified in RFC 4880 section 5.5.3 - * @param {String} input Input string to read the packet from + * @param {String} bytes Input string to read the packet from * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet or remaining length of input + * @param {Integer} len Length of the packet or remaining length of bytes * @return {Object} This object with attributes set by the parser */ - function read_priv_key(input,position, len) { + this.read = function(bytes) { // - A Public-Key or Public-Subkey packet, as described above. - this.publicKey = new openpgp_packet_keymaterial(); - if (this.publicKey.read_pub_key(input,position, len) == null) { - util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input[position].charCodeAt()+" "+position+" "+len+"\n Aborting here..."); - return null; - } - this.publicKey.header = openpgp_packet.write_old_packet_header(6,this.publicKey.packetLength); - // this.publicKey.header = String.fromCharCode(0x99) + String.fromCharCode(this.publicKey.packetLength >> 8 & 0xFF)+String.fromCharCode(this.publicKey.packetLength & 0xFF); - var mypos = position + this.publicKey.data.length; - this.packetLength = len; + var len = this.public_key.read(bytes); + + bytes = bytes.substr(len); + // - One octet indicating string-to-key usage conventions. Zero // indicates that the secret-key data is not encrypted. 255 or 254 // indicates that a string-to-key specifier is being given. Any // other value is a symmetric-key encryption algorithm identifier. - this.s2kUsageConventions = input[mypos++].charCodeAt(); + var s2k_usage = bytes[0].charCodeAt(); - if (this.s2kUsageConventions == 0) - this.hasUnencryptedSecretKeyData = true; - + var i = 1; + // - [Optional] If string-to-key usage octet was 255 or 254, a one- // octet symmetric encryption algorithm. - if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { - this.symmetricEncryptionAlgorithm = input[mypos++].charCodeAt(); - } + if (s2k_usage == 255 || s2k_usage == 254) { + this.symmetric_algorithm = bytes[i++].charCodeAt(); - // - [Optional] If string-to-key usage octet was 255 or 254, a - // string-to-key specifier. The length of the string-to-key - // specifier is implied by its type, as described above. - if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { + // - [Optional] If string-to-key usage octet was 255 or 254, a + // string-to-key specifier. The length of the string-to-key + // specifier is implied by its type, as described above. this.s2k = new openpgp_type_s2k(); - this.s2k.read(input, mypos); - mypos +=this.s2k.s2kLength; + this.s2k.read(bytes, i); + i += this.s2k.s2kLength; } // - [Optional] If secret data is encrypted (string-to-key usage octet // not zero), an Initial Vector (IV) of the same length as the // cipher's block size. - this.symkeylength = 0; - if (this.s2kUsageConventions != 0 && this.s2kUsageConventions != 255 && - this.s2kUsageConventions != 254) { - this.symmetricEncryptionAlgorithm = this.s2kUsageConventions; + + if (s2k_usage != 0 && s2k_usage != 255 && + s2k_usage != 254) { + this.symmetric_algorithm = s2k_usage; } - if (this.s2kUsageConventions != 0 && this.s2k.type != 1001) { - this.hasIV = true; - switch (this.symmetricEncryptionAlgorithm) { - case 1: // - IDEA [IDEA] - util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encrytryption algorithim: IDEA is not implemented"); - return null; - case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) - case 3: // - CAST5 (128 bit key, as per [RFC2144]) - this.IVLength = 8; - break; - 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 - this.IVLength = 16; - break; - case 10: // - Twofish with 256-bit key [TWOFISH] - this.IVLength = 32; - break; - case 5: // - Reserved - case 6: // - Reserved - default: - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm); - return null; - } - mypos++; - this.IV = input.substring(mypos, mypos+this.IVLength); - mypos += this.IVLength; + + if (s2k_usage != 0 && this.s2k.type != 1001) { + this.iv = bytes.substr(i, + openpgp_crypto_getBlockLength(this.symmetric_algorithm)); + + i += this.iv.length; } + // - Plain or encrypted multiprecision integers comprising the secret // key data. These algorithm-specific fields are as described // below. // s2k type 1001 corresponds to GPG specific extension without primary key secrets // http://www.gnupg.org/faq/GnuPG-FAQ.html#how-can-i-use-gnupg-in-an-automated-environment - if (this.s2kUsageConventions != 0 && this.s2k.type == 1001) { - this.secMPIs = null; - this.encryptedMPIData = null; - } else if (!this.hasUnencryptedSecretKeyData) { - this.encryptedMPIData = input.substring(mypos, len); - mypos += this.encryptedMPIData.length; + if (s2k_usage != 0 && this.s2k.type == 1001) { + this.mpi = null; + this.encrypted = null; + + } else if (s2k_usage != 0) { + this.encrypted = bytes.substr(i); + } else { - if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 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. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[0].packetLength; - this.secMPIs[1] = new openpgp_type_mpi(); - this.secMPIs[1].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[1].packetLength; - this.secMPIs[2] = new openpgp_type_mpi(); - this.secMPIs[2].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[2].packetLength; - this.secMPIs[3] = new openpgp_type_mpi(); - this.secMPIs[3].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[3].packetLength; - } else if (this.publicKey.publicKeyAlgorithm == 16) { - // Algorithm-Specific Fields for Elgamal secret keys: - // - MPI of Elgamal secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[0].packetLength; - } else if (this.publicKey.publicKeyAlgorithm == 17) { - // Algorithm-Specific Fields for DSA secret keys: - // - MPI of DSA secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); - mypos += this.secMPIs[0].packetLength; - } + var mpis = openpgp_crypto_getPrivateMpiCount(this.public_key.algorithm); + this.mpi = []; + + for(var j = 0; j < 4; j++) { + this.mpi[j] = new openpgp_type_mpi(); + i += this.mpi[j].read(bytes.substr(i)); + } + // checksum because s2k usage convention is 0 - this.checksum = new Array(); - this.checksum[0] = input[mypos++].charCodeAt(); - this.checksum[1] = input[mypos++].charCodeAt(); + this.checksum = []; + this.checksum[0] = bytes[i++].charCodeAt(); + this.checksum[1] = bytes[i++].charCodeAt(); } - return this; } + /* + * Creates an OpenPGP key packet for the given key. much + * TODO in regards to s2k, subkeys. + * @param {Integer} keyType Follows the OpenPGP algorithm standard, + * IE 1 corresponds to RSA. + * @param {RSA.keyObject} key + * @param password + * @param s2kHash + * @param symmetricEncryptionAlgorithm + * @param timePacket + * @return {Object} {body: [string]OpenPGP packet body contents, + header: [string] OpenPGP packet header, string: [string] header+body} + */ + this.write = function() { + + var body = String.fromCharCode(4); + body += timePacket; + switch(keyType){ + case 1: + body += String.fromCharCode(keyType);//public key algo + body += key.n.toMPI(); + body += key.ee.toMPI(); + var algorithmStart = body.length; + //below shows ske/s2k + if(password){ + body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1 + //if s2k == 255,254 then 1 octet symmetric encryption algo + body += String.fromCharCode(this.symmetric_algorithm); + //if s2k == 255,254 then s2k specifier + body += String.fromCharCode(3); //s2k salt+iter + body += String.fromCharCode(s2kHash); + //8 octet salt value + //1 octet count + var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var sha1Hash = str_sha1(cleartextMPIs); + util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash); + var salt = openpgp_crypto_getRandomBytes(8); + util.print_debug_hexstr_dump('write_private_key Salt: ',salt); + body += salt; + var c = 96; //c of 96 translates to count of 65536 + body += String.fromCharCode(c); + util.print_debug('write_private_key c: '+ c); + var s2k = new openpgp_type_s2k(); + var hashKey = s2k.write(3, s2kHash, password, salt, c); + //if s2k, IV of same length as cipher's block + switch(this.symmetric_algorithm){ + case 3: + this.IVLength = 8; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + case 7: + case 8: + case 9: + this.IVLength = 16; + this.IV = openpgp_crypto_getRandomBytes(this.IVLength); + ciphertextMPIs = normal_cfb_encrypt(AESencrypt, + this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV); + body += this.IV + ciphertextMPIs; + break; + } + } + else{ + body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k + body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); + var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI()); + body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 + util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum); + } + break; + default : + body = ""; + util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); + } + var header = openpgp_packet.write_packet_header(tag,body.length); + return {string: header+body , header: header, body: body}; + } /** * Decrypts the private key MPIs which are needed to use the key. @@ -371,51 +210,54 @@ function openpgp_packet_keymaterial() { * as string * @return {Boolean} True if the passphrase was correct; false if not */ - function decryptSecretMPIs(str_passphrase) { - if (this.hasUnencryptedSecretKeyData) - return this.secMPIs; + this.decrypt = function(passphrase) { + if (this.encrypted == null) + return; + // creating a key out of the passphrase - var key = this.s2k.produce_key(str_passphrase); + var key = this.s2k.produce_key(passphrase); + var cleartextMPIs = ""; - switch (this.symmetricEncryptionAlgorithm) { + + switch (this.symmetric_algorithm) { case 1: // - IDEA [IDEA] util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encryption algorithim: IDEA is not implemented"); return false; case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) cleartextMPIs = normal_cfb_decrypt(function(block, key) { return des(key, block,1,null,0); - }, this.IVLength, key, this.encryptedMPIData, this.IV); + }, this.IVLength, key, this.encrypted, this.IV); break; case 3: // - CAST5 (128 bit key, as per [RFC2144]) cleartextMPIs = normal_cfb_decrypt(function(block, key) { var cast5 = new openpgp_symenc_cast5(); cast5.setKey(key); return cast5.encrypt(util.str2bin(block)); - }, this.IVLength, util.str2bin(key.substring(0,16)), this.encryptedMPIData, this.IV); + }, this.IVLength, util.str2bin(key.substring(0,16)), this.encrypted, this.IV); break; case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] cleartextMPIs = normal_cfb_decrypt(function(block, key) { var blowfish = new Blowfish(key); return blowfish.encrypt(block); - }, this.IVLength, key, this.encryptedMPIData, this.IV); + }, this.IVLength, key, this.encrypted, this.IV); break; case 7: // - AES with 128-bit key [AES] case 8: // - AES with 192-bit key case 9: // - AES with 256-bit key var numBytes = 16; //This is a weird way to achieve this. If's within a switch is probably not ideal. - if(this.symmetricEncryptionAlgorithm == 8){ + if(this.symmetric_algorithm == 8){ numBytes = 24; key = this.s2k.produce_key(str_passphrase,numBytes); } - if(this.symmetricEncryptionAlgorithm == 9){ + if(this.symmetric_algorithm == 9){ numBytes = 32; key = this.s2k.produce_key(str_passphrase,numBytes); } cleartextMPIs = normal_cfb_decrypt(function(block,key){ return AESencrypt(util.str2bin(block),key); }, - this.IVLength, keyExpansion(key.substring(0,numBytes)), this.encryptedMPIData, this.IV); + this.IVLength, keyExpansion(key.substring(0,numBytes)), this.encrypted, this.IV); break; case 10: // - Twofish with 256-bit key [TWOFISH] util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented"); @@ -423,7 +265,7 @@ function openpgp_packet_keymaterial() { case 5: // - Reserved case 6: // - Reserved default: - util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm); + util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetric_algorithm); return false; } @@ -434,11 +276,11 @@ function openpgp_packet_keymaterial() { var cleartextMPIslength = cleartextMPIs.length; - if (this.s2kUsageConventions == 254 && + if (s2k_usage == 254 && str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) == cleartextMPIs.substring(cleartextMPIs.length - 20)) { cleartextMPIslength -= 20; - } else if (this.s2kUsageConventions != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) == + } else if (s2k_usage != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) == (cleartextMPIs.charCodeAt(cleartextMPIs.length -2) << 8 | cleartextMPIs.charCodeAt(cleartextMPIs.length -1))) { cleartextMPIslength -= 2; } else { @@ -451,32 +293,32 @@ function openpgp_packet_keymaterial() { // - 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. - var mypos = 0; - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength); - mypos += this.secMPIs[0].packetLength; - this.secMPIs[1] = new openpgp_type_mpi(); - this.secMPIs[1].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); - mypos += this.secMPIs[1].packetLength; - this.secMPIs[2] = new openpgp_type_mpi(); - this.secMPIs[2].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); - mypos += this.secMPIs[2].packetLength; - this.secMPIs[3] = new openpgp_type_mpi(); - this.secMPIs[3].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); - mypos += this.secMPIs[3].packetLength; + var i = 0; + this.mpi = new Array(); + this.mpi[0] = new openpgp_type_mpi(); + this.mpi[0].read(cleartextMPIs, 0, cleartextMPIslength); + i += this.mpi[0].packetLength; + this.mpi[1] = new openpgp_type_mpi(); + this.mpi[1].read(cleartextMPIs, i, cleartextMPIslength-i); + i += this.mpi[1].packetLength; + this.mpi[2] = new openpgp_type_mpi(); + this.mpi[2].read(cleartextMPIs, i, cleartextMPIslength-i); + i += this.mpi[2].packetLength; + this.mpi[3] = new openpgp_type_mpi(); + this.mpi[3].read(cleartextMPIs, i, cleartextMPIslength-i); + i += this.mpi[3].packetLength; } else if (this.publicKey.publicKeyAlgorithm == 16) { // Algorithm-Specific Fields for Elgamal secret keys: // - MPI of Elgamal secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIs); + this.mpi = new Array(); + this.mpi[0] = new openpgp_type_mpi(); + this.mpi[0].read(cleartextMPIs, 0, cleartextMPIs); } else if (this.publicKey.publicKeyAlgorithm == 17) { // Algorithm-Specific Fields for DSA secret keys: // - MPI of DSA secret exponent x. - this.secMPIs = new Array(); - this.secMPIs[0] = new openpgp_type_mpi(); - this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength); + this.mpi = new Array(); + this.mpi[0] = new openpgp_type_mpi(); + this.mpi[0].read(cleartextMPIs, 0, cleartextMPIslength); } return true; } @@ -535,10 +377,10 @@ function openpgp_packet_keymaterial() { result += this.publicKey.MPIs[i].toString(); } } - if (this.secMPIs != null) { + if (this.mpi != null) { result += "Secret Key MPIs:\n"; - for (var i = 0; i < this.secMPIs.length; i++) { - result += this.secMPIs[i].toString(); + for (var i = 0; i < this.mpi.length; i++) { + result += this.mpi[i].toString(); } } @@ -553,19 +395,19 @@ function openpgp_packet_keymaterial() { /** * Continue parsing packets belonging to the key material such as signatures * @param {Object} parent_node The parent object - * @param {String} input Input string to read the packet(s) from + * @param {String} bytes Input string to read the packet(s) from * @param {Integer} position Start position for the parser - * @param {Integer} len Length of the packet(s) or remaining length of input + * @param {Integer} len Length of the packet(s) or remaining length of bytes * @return {Integer} Length of nodes read */ - function read_nodes(parent_node, input, position, len) { + function read_nodes(parent_node, bytes, position, len) { this.parentNode = parent_node; if (this.tagType == 14) { // public sub-key packet var pos = position; var result = null; - while (input.length != pos) { - var l = input.length - pos; - result = openpgp_packet.read_packet(input, pos, l); + while (bytes.length != pos) { + var l = bytes.length - pos; + result = openpgp_packet.read_packet(bytes, pos, l); if (result == null) { util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_pub]parsing ends here @:' + pos + " l:" + l); break; @@ -586,7 +428,7 @@ function openpgp_packet_keymaterial() { } default: - this.data = input; + this.data = bytes; this.position = position - this.parentNode.packetLength; this.len = pos - position; return this.len; @@ -594,14 +436,14 @@ function openpgp_packet_keymaterial() { } } } - this.data = input; + this.data = bytes; this.position = position - this.parentNode.packetLength; this.len = pos - position; return this.len; } else if (this.tagType == 7) { // private sub-key packet var pos = position; - while (input.length != pos) { - var result = openpgp_packet.read_packet(input, pos, len - (pos - position)); + while (bytes.length != pos) { + var result = openpgp_packet.read_packet(bytes, pos, len - (pos - position)); if (result == null) { util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_priv] parsing ends here @:' + pos); break; @@ -615,14 +457,14 @@ function openpgp_packet_keymaterial() { pos += result.packetLength + result.headerLength; break; default: - this.data = input; + this.data = bytes; this.position = position - this.parentNode.packetLength; this.len = pos - position; return this.len; } } } - this.data = input; + this.data = bytes; this.position = position - this.parentNode.packetLength; this.len = pos - position; return this.len; @@ -690,130 +532,5 @@ function openpgp_packet_keymaterial() { } } - /* - * Creates an OpenPGP key packet for the given key. much - * TODO in regards to s2k, subkeys. - * @param {Integer} keyType Follows the OpenPGP algorithm standard, - * IE 1 corresponds to RSA. - * @param {RSA.keyObject} key - * @param password - * @param s2kHash - * @param symmetricEncryptionAlgorithm - * @param timePacket - * @return {Object} {body: [string]OpenPGP packet body contents, - header: [string] OpenPGP packet header, string: [string] header+body} - */ - function write_private_key(keyType, key, password, s2kHash, symmetricEncryptionAlgorithm, timePacket){ - this.symmetricEncryptionAlgorithm = symmetricEncryptionAlgorithm; - var tag = 5; - var body = String.fromCharCode(4); - body += timePacket; - switch(keyType){ - case 1: - body += String.fromCharCode(keyType);//public key algo - body += key.n.toMPI(); - body += key.ee.toMPI(); - var algorithmStart = body.length; - //below shows ske/s2k - if(password){ - body += String.fromCharCode(254); //octet of 254 indicates s2k with SHA1 - //if s2k == 255,254 then 1 octet symmetric encryption algo - body += String.fromCharCode(this.symmetricEncryptionAlgorithm); - //if s2k == 255,254 then s2k specifier - body += String.fromCharCode(3); //s2k salt+iter - body += String.fromCharCode(s2kHash); - //8 octet salt value - //1 octet count - var cleartextMPIs = key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); - var sha1Hash = str_sha1(cleartextMPIs); - util.print_debug_hexstr_dump('write_private_key sha1: ',sha1Hash); - var salt = openpgp_crypto_getRandomBytes(8); - util.print_debug_hexstr_dump('write_private_key Salt: ',salt); - body += salt; - var c = 96; //c of 96 translates to count of 65536 - body += String.fromCharCode(c); - util.print_debug('write_private_key c: '+ c); - var s2k = new openpgp_type_s2k(); - var hashKey = s2k.write(3, s2kHash, password, salt, c); - //if s2k, IV of same length as cipher's block - switch(this.symmetricEncryptionAlgorithm){ - case 3: - this.IVLength = 8; - this.IV = openpgp_crypto_getRandomBytes(this.IVLength); - ciphertextMPIs = normal_cfb_encrypt(function(block, key) { - var cast5 = new openpgp_symenc_cast5(); - cast5.setKey(key); - return cast5.encrypt(util.str2bin(block)); - }, this.IVLength, util.str2bin(hashKey.substring(0,16)), cleartextMPIs + sha1Hash, this.IV); - body += this.IV + ciphertextMPIs; - break; - case 7: - case 8: - case 9: - this.IVLength = 16; - this.IV = openpgp_crypto_getRandomBytes(this.IVLength); - ciphertextMPIs = normal_cfb_encrypt(AESencrypt, - this.IVLength, hashKey, cleartextMPIs + sha1Hash, this.IV); - body += this.IV + ciphertextMPIs; - break; - } - } - else{ - body += String.fromCharCode(0);//1 octet -- s2k, 0 for no s2k - body += key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI(); - var checksum = util.calc_checksum(key.d.toMPI() + key.p.toMPI() + key.q.toMPI() + key.u.toMPI()); - body += String.fromCharCode(checksum/0x100) + String.fromCharCode(checksum%0x100);//DEPRECATED:s2k == 0, 255: 2 octet checksum, sum all octets%65536 - util.print_debug_hexstr_dump('write_private_key basic checksum: '+ checksum); - } - break; - default : - body = ""; - util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); - } - var header = openpgp_packet.write_packet_header(tag,body.length); - return {string: header+body , header: header, body: body}; - } - /* - * Same as write_private_key, but has less information because of - * public key. - * @param {Integer} keyType Follows the OpenPGP algorithm standard, - * IE 1 corresponds to RSA. - * @param {RSA.keyObject} key - * @param timePacket - * @return {Object} {body: [string]OpenPGP packet body contents, - * header: [string] OpenPGP packet header, string: [string] header+body} - */ - function write_public_key(keyType, key, timePacket){ - var tag = 6; - var body = String.fromCharCode(4); - body += timePacket; - switch(keyType){ - case 1: - body += String.fromCharCode(1);//public key algo - body += key.n.toMPI(); - body += key.ee.toMPI(); - break; - default: - util.print_error("openpgp.packet.keymaterial.js\n"+'error writing private key, unknown type :'+keyType); - } - var header = openpgp_packet.write_packet_header(tag,body.length); - return {string: header+body , header: header, body: body}; - } - - - this.read_tag5 = read_tag5; - this.read_tag6 = read_tag6; - this.read_tag7 = read_tag7; - this.read_tag14 = read_tag14; - this.toString = toString; - this.read_pub_key = read_pub_key; - this.read_priv_key = read_priv_key; - this.decryptSecretMPIs = decryptSecretMPIs; - this.read_nodes = read_nodes; - this.verifyKey = verifyKey; - this.getKeyId = getKeyId; - this.getFingerprint = getFingerprint; - this.write_private_key = write_private_key; - this.write_public_key = write_public_key; } diff --git a/src/packet/signature.js b/src/packet/signature.js new file mode 100644 index 00000000..885c48c9 --- /dev/null +++ b/src/packet/signature.js @@ -0,0 +1,35 @@ +// 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 + +/** + * @class + * @classdesc Implementation of the Signature Packet (Tag 2) + * + * RFC4480 5.2: + * A Signature packet describes a binding between some public key and + * some data. The most common signatures are a signature of a file or a + * block of text, and a signature that is a certification of a User ID. + */ +function openpgp_packet_signature() { + this.tag = 2; + + this.write = function() { + + } + + this.read = function() {} +} diff --git a/src/packet/sym_encrypted_integrity_protected.js b/src/packet/sym_encrypted_integrity_protected.js index 220158f2..8086cb92 100644 --- a/src/packet/sym_encrypted_integrity_protected.js +++ b/src/packet/sym_encrypted_integrity_protected.js @@ -115,17 +115,16 @@ function openpgp_packet_sym_encrypted_integrity_protected() { util.print_debug_hexstr_dump("calc hash = ", this.hash); - - this.packets.read(decrypted.substr(0, decrypted.length - 22)); - var mdc = decrypted.substr(decrypted.length - 20, 20); if(this.hash != mdc) { - this.packets = null; + this.packets = new openpgp_packetlist(); util.print_error("Decryption stopped: discovered a " + "modification of encrypted data."); return; } + else + this.packets.read(decrypted.substr(0, decrypted.length - 22)); } this.toString = function() { diff --git a/test/general/packet.js b/test/general/packet.js index eb72c1f0..e0e15728 100644 --- a/test/general/packet.js +++ b/test/general/packet.js @@ -104,7 +104,7 @@ unittests.register("Packet testing", function() { msg = new openpgp_packetlist(), msg2 = new openpgp_packetlist(); - enc.symmetric_key = '12345678901234567890123456789012', + enc.symmetric_key = '12345678901234567890123456789012'; enc.public_key_algorithm = openpgp.publickey.rsa_encrypt; enc.symmetric_algorithm = openpgp.symmetric.aes256; enc.public_key_id.bytes = '12345678'; @@ -119,6 +119,67 @@ unittests.register("Packet testing", function() { return new test_result('Public key encrypted symmetric key packet', msg2[0].symmetric_key == enc.symmetric_key && msg2[0].symmetric_algorithm == enc.symmetric_algorithm); + }, function() { + var armored_key = + '-----BEGIN PGP PRIVATE KEY BLOCK-----\n' + + 'Version: GnuPG v2.0.19 (GNU/Linux)\n' + + '\n' + + 'lQHYBFF33iMBBAC9YfOYahJlWrVj2J1TjQiZLunWljI4G9e6ARTyD99nfOkV3swh\n' + + '0WaOse4Utj7BfTqdYcoezhCaQpuExUupKWZqmduBcwSmEBfNu1XyKcxlDQuuk0Vk\n' + + 'viGC3kFRce/cJaKVFSRU8V5zPgt6KQNv/wNz7ydEisaSoNbk51vQt5oGfwARAQAB\n' + + 'AAP5AVL8xWMuKgLj9g7/wftMH+jO7vhAxje2W3Y+8r8TnOSn0536lQvzl/eQyeLC\n' + + 'VK2k3+7+trgO7I4KuXCXZqgAbEi3niDYXDaCJ+8gdR9qvPM2gi9NM71TGXZvGE0w\n' + + 'X8gIZfqLTQWKm9TIS/3tdrth4nwhiye0ASychOboIiN6VIECAMbCQ4/noxGV6yTK\n' + + 'VezsGSz+iCMxz2lV270/Ac2C5WPk+OlxXloxUXeEkGIr6Xkmhhpceed2KL41UC8Y\n' + + 'w5ttGIECAPPsahniKGyqp9CHy6W0B83yhhcIbmLlaVG2ftKyUEDxIggzOlXuVrue\n' + + 'z9XRd6wFqwDd1QMFW0uUyHPDCIFPnv8CAJaDFSZutuWdWMt15NZXjfgRgfJuDrtv\n' + + 'E7yFY/p0el8lCihOT8WoHbTn1PbCYMzNBc0IhHaZKAtA2pjkE+wzz9ClP7QbR2Vv\n' + + 'cmdlIDxnZW9yZ2VAZXhhbXBsZS5jb20+iLkEEwECACMFAlF33iMCGwMHCwkIBwMC\n' + + 'AQYVCAIJCgsEFgIDAQIeAQIXgAAKCRBcqs36fwJCXRbvA/9LPiK6WFKcFoNBnLEJ\n' + + 'mS/CNkL8yTpkslpCP6+TwJMc8uXqwYl9/PW2+CwmzZjs6JsvTzMcR/ZbfZJuSW6Y\n' + + 'EsLNejsSpgcY9aiewGtE+53e5oKYnlmVMTWOPywciIgMvXlzdGhxcwqJ8u0hT+ug\n' + + '9CjcAfuX9yw85LwXtdGwNh7J8Q==\n' + + '=lKiS\n' + + '-----END PGP PRIVATE KEY BLOCK-----'; + + key = new openpgp_packetlist(); + key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key = key[0]; + + var enc = new openpgp_packet_public_key_encrypted_session_key(), + secret = '12345678901234567890123456789012'; + + enc.symmetric_key = secret; + enc.public_key_algorithm = openpgp.publickey.rsa_encrypt; + enc.symmetric_algorithm = openpgp.symmetric.aes256; + enc.public_key_id.bytes = '12345678'; + + enc.encrypt(key.public_key.mpi); + + enc.decrypt(key.public_key.mpi, key.mpi); + + return new test_result('Public key packet (reading, unencrpted)', + enc.symmetric_key == secret); + }, function() { + + + + + + key = new openpgp_packetlist(); + key.read(openpgp_encoding_deArmor(armored_key).openpgp); + key = key[0]; + + var msg = new openpgp_packetlist(); + msg.read(openpgp_encoding_deArmor(gpg).openpgp); + + msg[0].decrypt(key.public_key.mpi, key.mpi); + msg[1].decrypt(msg[0].symmetric_algorithm, msg[0].symmetric_key); + + + + return new test_result('Public key packet (reading, GPG encrypted)', + true); }]; tests.reverse(); diff --git a/test/index.html b/test/index.html index f6dddbd5..dcd2b9a7 100644 --- a/test/index.html +++ b/test/index.html @@ -1,4 +1,3 @@ -