// 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_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.charCodeAt(mypos++); if (this.version == 3) { // A four-octet number denoting the time that the key was created. this.creationTime = new Date(((input.charCodeAt(mypos++) << 24) | (input.charCodeAt(mypos++) << 16) | (input.charCodeAt(mypos++) << 8) | (input.charCodeAt(mypos++)))*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.charCodeAt(mypos++) << 8) | input.charCodeAt(mypos++); // - A one-octet number denoting the public-key algorithm of this key. this.publicKeyAlgorithm = input.charCodeAt(mypos++); 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.charCodeAt(mypos++) << 24) | (input.charCodeAt(mypos++) << 16) | (input.charCodeAt(mypos++) << 8) | (input.charCodeAt(mypos++)))*1000); // - A one-octet number denoting the public-key algorithm of this key. this.publicKeyAlgorithm = input.charCodeAt(mypos++); 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.charCodeAt(position)+" "+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.charCodeAt(mypos++); 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.charCodeAt(mypos++); } // - [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.charCodeAt(mypos++); this.checksum[1] = input.charCodeAt(mypos++); } 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].MPI.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; }