// 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 /** * 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 input input string to read the packet from * @param position start position for the parser * @param len length of the packet or remaining length of input * @return openpgp_packet_keymaterial object */ 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 input input string to read the packet from * @param position start position for the parser * @param len length of the packet or remaining length of input * @return openpgp_packet_keymaterial object */ 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 input input string to read the packet from * @param position start position for the parser * @param len length of the packet or remaining length of input * @return openpgp_packet_keymaterial object */ 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 input input string to read the packet from * @param position start position for the parser * @param len length of the packet or remaining length of input * @return openpgp_packet_keymaterial object */ 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 input input string to read the packet from * @param position start position for the parser * @param len length of the packet or remaining length of input * @return 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 input input string to read the packet from * @param position start position for the parser * @param len length of the packet or remaining length of input * @return 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.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. // // 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 str_passphrase the passphrase for this private key as string * @return 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 cleartextMPIs = normal_cfb_decrypt(AESencrypt, this.IVLength, keyExpansion(key), 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.s2kUsageConcentions != 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 parent_node [openpgp_*] the parent object * @param input [String] input string to read the packet(s) from * @param position [integer] start position for the parser * @param len [integer] 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 = 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] = 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 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.subKeyRevocationSignature[i]) var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+ String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata; if (this.subKeyRevocationSignature[i].verify(hashdata, this.parentNode)) return 2; else return 0; } } 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) { var tohash= this.header+this.data; //if (this.tagType == 14) { tohash = String.fromCharCode(0x99)+this.header.substring(1)+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); } } 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; }